Merge branch 'master' of github.com:khoj-ai/khoj into features/migrate-to-spring-ui

This commit is contained in:
sabaimran 2024-08-05 16:12:46 +05:30
commit b7ed32f455
18 changed files with 335 additions and 200 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 170 KiB

View file

@ -186,7 +186,7 @@ In whichever clients you're using for testing, you'll need to update the server
### Before Making Changes ### Before Making Changes
1. Install Git Hooks for Validation 1. Install Git Hooks for Validation
```shell ```shell
pre-commit install -t pre-push -t pre-commit ./scripts/dev_setup.sh
``` ```
- This ensures standard code formatting fixes and other checks run automatically on every commit and push - This ensures standard code formatting fixes and other checks run automatically on every commit and push
- Note 1: If [pre-commit](https://pre-commit.com/#intro) didn't already get installed, [install it](https://pre-commit.com/#install) via `pip install pre-commit` - Note 1: If [pre-commit](https://pre-commit.com/#intro) didn't already get installed, [install it](https://pre-commit.com/#install) via `pip install pre-commit`

119
scripts/dev_setup.sh Executable file
View file

@ -0,0 +1,119 @@
# Initialize the development environment for the project
# ---
PROJECT_ROOT=$(git rev-parse --show-toplevel)
# Install Web App
# ---
echo "Installing Web App..."
cd $PROJECT_ROOT/src/interface/web
yarn install
# Install Obsidian App
# ---
echo "Installing Obsidian App..."
cd $PROJECT_ROOT/src/interface/obsidian
yarn install
# Install Desktop App
# ---
echo "Installing Desktop App..."
cd $PROJECT_ROOT/src/interface/desktop
yarn install
# Install Server App
# ---
echo "Installing Server App..."
cd $PROJECT_ROOT
# pip install --user pipenv && pipenv install -e '.[dev]' --skip-lock && pipenv shell
python3 -m venv .venv && pip install -e '.[dev]' && . .venv/bin/activate
# Install pre-commit hooks
# ----
echo "Installing pre-commit hooks..."
# Setup pre-commit hooks using the pre-commit package
pre-commit install -t pre-push -t pre-commit
# Run Prettier on web app
cat << 'EOF' > temp_pre_commit
# Run Prettier for Web App
# -------------------------
# Function to check if color output is possible
can_use_color() {
if [ -t 1 ] && command -v tput >/dev/null 2>&1 && tput colors >/dev/null 2>&1; then
return 0
else
return 1
fi
}
# Function to print colored text if possible
print_color() {
if can_use_color; then
tput setab "$1"
printf "%s" "$2"
tput sgr0
else
printf "%s" "$2"
fi
}
print_status() {
local status="$1"
local color="$2"
printf "prettier%-64s" "..."
print_color "$color" "$status"
printf "\n"
}
PROJECT_ROOT=$(git rev-parse --show-toplevel)
# Get the list of staged files
FILES=$(git diff --cached --name-only --diff-filter=ACMR | grep '^src/interface/web/' | sed 's| |\\ |g')
if [ -z "$FILES" ]; then
if [ -t 1 ]; then
print_status "Skipped" 6
else
echo "prettier.....................................................Skipped"
fi
else
# Run prettier on staged files
echo "$FILES" | xargs $PROJECT_ROOT/src/interface/web/node_modules/.bin/prettier --ignore-unknown --write
# Check if any files were modified by prettier
MODIFIED=$(git diff --name-only -- $FILES)
if [ -n "$MODIFIED" ]; then
if [ -t 1 ]; then
print_status "Modified" 1
else
echo "prettier.....................................................Modified"
fi
exit 1
fi
# Add back the modified/prettified files to staging
# echo "$FILES" | xargs git add
# Show the user if changes were made
if [ -t 1 ]; then
print_status "Passed" 2
else
echo "prettier.....................................................Passed"
fi
fi
EOF
# Prepend the new content to the existing pre-commit file
cat temp_pre_commit "$(git rev-parse --git-dir)/hooks/pre-commit" > temp_combined_pre_commit
# Replace the old pre-commit file with the new combined one
mv temp_combined_pre_commit "$(git rev-parse --git-dir)/hooks/pre-commit"
# Clean up
# ---
# Remove the temporary pre-commit file
rm temp_pre_commit
# Make sure the pre-commit hook is executable
chmod +x "$(git rev-parse --git-dir)/hooks/pre-commit"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 651 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

View file

@ -16,7 +16,7 @@
"start": "yarn electron ." "start": "yarn electron ."
}, },
"dependencies": { "dependencies": {
"@todesktop/runtime": "^1.3.0", "@todesktop/runtime": "^1.6.4",
"axios": "^1.6.4", "axios": "^1.6.4",
"cron": "^2.4.3", "cron": "^2.4.3",
"electron-store": "^8.1.0" "electron-store": "^8.1.0"

View file

@ -1,6 +1,9 @@
{ {
"id": "", "id": "",
"icon": "./assets/icons/favicon-128x128.png", "icon": "./assets/icons/favicon.icns",
"appPath": ".", "appPath": ".",
"schemaVersion": 1 "schemaVersion": 1,
"windows": {
"icon": "./assets/icons/favicon-128x128.png"
}
} }

View file

@ -50,10 +50,10 @@
dependencies: dependencies:
defer-to-connect "^2.0.0" defer-to-connect "^2.0.0"
"@todesktop/runtime@^1.3.0": "@todesktop/runtime@^1.6.4":
version "1.6.3" version "1.6.4"
resolved "https://registry.yarnpkg.com/@todesktop/runtime/-/runtime-1.6.3.tgz#152dda0ad49e0f0ab0f33493e185b594b8871964" resolved "https://registry.yarnpkg.com/@todesktop/runtime/-/runtime-1.6.4.tgz#a9d62a021cf2647c51371c892bfb1d4c5a29ed7e"
integrity sha512-1PNBSuTSZxlIS5/3lVRRD76/z0i4iqD1H9GItV2arXwWbfwBKwV9h51aLQR0SQ7BgAvzQcHvJTQaiA8WL0ku4g== integrity sha512-n6dOxhrKKsXMM+i2u9iRvoJSR2KCWw0orYK+FT9RbWNPykhuFIYd0yy8dYgYy/OuClKGyGl4SJFi2757FLhWDA==
dependencies: dependencies:
del "^6.0.0" del "^6.0.0"
electron-updater "^4.6.1" electron-updater "^4.6.1"

View file

@ -171,7 +171,7 @@ export default function NavMenu() {
</DropdownMenuItem> </DropdownMenuItem>
) : ( ) : (
<DropdownMenuItem> <DropdownMenuItem>
<Link href="/auth/login" className="no-underline w-full"> <Link href="/login" className="no-underline w-full">
<div className="flex flex-rows"> <div className="flex flex-rows">
<ArrowRight className="w-6 h-6" /> <ArrowRight className="w-6 h-6" />
<p className="ml-3 font-semibold">Login</p> <p className="ml-3 font-semibold">Login</p>
@ -275,7 +275,7 @@ export default function NavMenu() {
</MenubarItem> </MenubarItem>
) : ( ) : (
<MenubarItem> <MenubarItem>
<Link href="/auth/login" className="no-underline w-full"> <Link href="/login" className="no-underline w-full">
<div className="flex flex-rows"> <div className="flex flex-rows">
<ArrowRight className="w-6 h-6" /> <ArrowRight className="w-6 h-6" />
<p className="ml-3 font-semibold">Login</p> <p className="ml-3 font-semibold">Login</p>

View file

@ -5,7 +5,6 @@ import styles from "./suggestions.module.css";
import { converColorToBgGradient } from "@/app/common/colorUtils"; import { converColorToBgGradient } from "@/app/common/colorUtils";
import { convertSuggestionTitleToIconClass } from "./suggestionsData"; import { convertSuggestionTitleToIconClass } from "./suggestionsData";
interface SuggestionCardProps { interface SuggestionCardProps {
title: string; title: string;
body: string; body: string;
@ -23,10 +22,7 @@ export default function SuggestionCard(data: SuggestionCardProps) {
<Card className={cardClassName}> <Card className={cardClassName}>
<CardHeader className="m-0 p-2 pb-1 relative"> <CardHeader className="m-0 p-2 pb-1 relative">
<div className="flex flex-row md:flex-col"> <div className="flex flex-row md:flex-col">
{convertSuggestionTitleToIconClass( {convertSuggestionTitleToIconClass(data.title, data.color.toLowerCase())}
data.title,
data.color.toLowerCase(),
)}
<CardTitle className={titleClassName}>{data.title}</CardTitle> <CardTitle className={titleClassName}>{data.title}</CardTitle>
</div> </div>
</CardHeader> </CardHeader>

View file

@ -41,29 +41,32 @@ addSuggestionColorMap(SuggestionType.Interviewing, "purple");
addSuggestionColorMap(SuggestionType.Home, "green"); addSuggestionColorMap(SuggestionType.Home, "green");
addSuggestionColorMap(SuggestionType.Fun, "fuchsia"); addSuggestionColorMap(SuggestionType.Fun, "fuchsia");
addSuggestionColorMap(SuggestionType.Code, "purple"); addSuggestionColorMap(SuggestionType.Code, "purple");
addSuggestionColorMap(SuggestionType.Finance, "green") addSuggestionColorMap(SuggestionType.Finance, "green");
const DEFAULT_COLOR = "orange"; const DEFAULT_COLOR = "orange";
export function convertSuggestionTitleToIconClass(title: string, color: string) { export function convertSuggestionTitleToIconClass(title: string, color: string) {
if (title === SuggestionType.Automation) return getIconFromIconName("Robot", color, "w-8", "h-8"); if (title === SuggestionType.Automation)
return getIconFromIconName("Robot", color, "w-8", "h-8");
if (title === SuggestionType.Paint) return getIconFromIconName("Palette", color, "w-8", "h-8"); if (title === SuggestionType.Paint) return getIconFromIconName("Palette", color, "w-8", "h-8");
if (title === SuggestionType.PopCulture) return getIconFromIconName("Confetti", color, "w-8", "h-8"); if (title === SuggestionType.PopCulture)
return getIconFromIconName("Confetti", color, "w-8", "h-8");
if (title === SuggestionType.Travel) return getIconFromIconName("Jeep", color, "w-8", "h-8"); if (title === SuggestionType.Travel) return getIconFromIconName("Jeep", color, "w-8", "h-8");
if (title === SuggestionType.Learning) return getIconFromIconName("Book", color, "w-8", "h-8"); if (title === SuggestionType.Learning) return getIconFromIconName("Book", color, "w-8", "h-8");
if (title === SuggestionType.Health) return getIconFromIconName("Asclepius", color, "w-8", "h-8"); if (title === SuggestionType.Health)
return getIconFromIconName("Asclepius", color, "w-8", "h-8");
if (title === SuggestionType.Fun) return getIconFromIconName("Island", color, "w-8", "h-8"); if (title === SuggestionType.Fun) return getIconFromIconName("Island", color, "w-8", "h-8");
if (title === SuggestionType.Home) return getIconFromIconName("House", color, "w-8", "h-8"); if (title === SuggestionType.Home) return getIconFromIconName("House", color, "w-8", "h-8");
if (title === SuggestionType.Language) return getIconFromIconName("Translate", color, "w-8", "h-8"); if (title === SuggestionType.Language)
return getIconFromIconName("Translate", color, "w-8", "h-8");
if (title === SuggestionType.Code) return getIconFromIconName("Code", color, "w-8", "h-8"); if (title === SuggestionType.Code) return getIconFromIconName("Code", color, "w-8", "h-8");
if (title === SuggestionType.Food) return getIconFromIconName("BowlFood", color, "w-8", "h-8"); if (title === SuggestionType.Food) return getIconFromIconName("BowlFood", color, "w-8", "h-8");
if (title === SuggestionType.Interviewing) return getIconFromIconName("Lectern", color, "w-8", "h-8"); if (title === SuggestionType.Interviewing)
return getIconFromIconName("Lectern", color, "w-8", "h-8");
if (title === SuggestionType.Finance) return getIconFromIconName("Wallet", color, "w-8", "h-8"); if (title === SuggestionType.Finance) return getIconFromIconName("Wallet", color, "w-8", "h-8");
else return getIconFromIconName("Lightbulb", color, "w-8", "h-8"); else return getIconFromIconName("Lightbulb", color, "w-8", "h-8");
} }
export const suggestionsData: Suggestion[] = [ export const suggestionsData: Suggestion[] = [
{ {
type: SuggestionType.Automation, type: SuggestionType.Automation,
@ -516,19 +519,22 @@ export const suggestionsData: Suggestion[] = [
{ {
type: SuggestionType.Learning, type: SuggestionType.Learning,
color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR,
description: "Guide a high school student through solving a quadratic equation step-by-step.", description:
"Guide a high school student through solving a quadratic equation step-by-step.",
link: "", link: "",
}, },
{ {
type: SuggestionType.Learning, type: SuggestionType.Learning,
color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR,
description: "Create a series of questions to help a student discover the principles of basic economics.", description:
"Create a series of questions to help a student discover the principles of basic economics.",
link: "", link: "",
}, },
{ {
type: SuggestionType.Learning, type: SuggestionType.Learning,
color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR,
description: "Develop a hands-on experiment to demonstrate the concept of density to middle schoolers.", description:
"Develop a hands-on experiment to demonstrate the concept of density to middle schoolers.",
link: "", link: "",
}, },
{ {
@ -540,19 +546,22 @@ export const suggestionsData: Suggestion[] = [
{ {
type: SuggestionType.Learning, type: SuggestionType.Learning,
color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR,
description: "Create a personalized learning plan for a student struggling with grammar concepts.", description:
"Create a personalized learning plan for a student struggling with grammar concepts.",
link: "", link: "",
}, },
{ {
type: SuggestionType.Learning, type: SuggestionType.Learning,
color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR,
description: "Design a series of questions to encourage critical thinking about climate change.", description:
"Design a series of questions to encourage critical thinking about climate change.",
link: "", link: "",
}, },
{ {
type: SuggestionType.Learning, type: SuggestionType.Learning,
color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR,
description: "Develop a step-by-step guide for conducting a basic science experiment on plant growth.", description:
"Develop a step-by-step guide for conducting a basic science experiment on plant growth.",
link: "", link: "",
}, },
{ {
@ -576,7 +585,8 @@ export const suggestionsData: Suggestion[] = [
{ {
type: SuggestionType.Health, type: SuggestionType.Health,
color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR,
description: "Explain the differences between various types of headaches and their treatments.", description:
"Explain the differences between various types of headaches and their treatments.",
link: "", link: "",
}, },
{ {
@ -606,7 +616,8 @@ export const suggestionsData: Suggestion[] = [
{ {
type: SuggestionType.Health, type: SuggestionType.Health,
color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR,
description: "Provide an overview of the different types of cancer screenings and their importance.", description:
"Provide an overview of the different types of cancer screenings and their importance.",
link: "", link: "",
}, },
{ {
@ -624,13 +635,15 @@ export const suggestionsData: Suggestion[] = [
{ {
type: SuggestionType.Finance, type: SuggestionType.Finance,
color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR, color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR,
description: "Explain the concept of compound interest and its importance in long-term savings.", description:
"Explain the concept of compound interest and its importance in long-term savings.",
link: "", link: "",
}, },
{ {
type: SuggestionType.Finance, type: SuggestionType.Finance,
color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR, color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR,
description: "Provide an overview of different types of retirement accounts (e.g., 401(k), IRA, Roth IRA).", description:
"Provide an overview of different types of retirement accounts (e.g., 401(k), IRA, Roth IRA).",
link: "", link: "",
}, },
{ {
@ -654,7 +667,8 @@ export const suggestionsData: Suggestion[] = [
{ {
type: SuggestionType.Finance, type: SuggestionType.Finance,
color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR, color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR,
description: "Describe different methods for paying off debt, such as the snowball and avalanche methods.", description:
"Describe different methods for paying off debt, such as the snowball and avalanche methods.",
link: "", link: "",
}, },
{ {
@ -666,7 +680,8 @@ export const suggestionsData: Suggestion[] = [
{ {
type: SuggestionType.Finance, type: SuggestionType.Finance,
color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR, color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR,
description: "Provide an overview of different types of insurance and their importance in financial planning.", description:
"Provide an overview of different types of insurance and their importance in financial planning.",
link: "", link: "",
}, },
{ {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 170 KiB

View file

@ -378,8 +378,8 @@ export default function SettingsView() {
initialUserConfig?.is_phone_number_verified initialUserConfig?.is_phone_number_verified
? PhoneNumberValidationState.Verified ? PhoneNumberValidationState.Verified
: initialUserConfig?.phone_number : initialUserConfig?.phone_number
? PhoneNumberValidationState.SendOTP ? PhoneNumberValidationState.SendOTP
: PhoneNumberValidationState.Setup, : PhoneNumberValidationState.Setup,
); );
setName(initialUserConfig?.given_name); setName(initialUserConfig?.given_name);
setNotionToken(initialUserConfig?.notion_token ?? null); setNotionToken(initialUserConfig?.notion_token ?? null);
@ -748,18 +748,18 @@ export default function SettingsView() {
)) || )) ||
(userConfig.subscription_state === (userConfig.subscription_state ===
"unsubscribed" && ( "unsubscribed" && (
<> <>
<p className="text-xl">Futurist</p> <p className="text-xl">Futurist</p>
<p className="text-gray-400"> <p className="text-gray-400">
Subscription <b>ends</b> on{" "} Subscription <b>ends</b> on{" "}
<b> <b>
{ {
userConfig.subscription_renewal_date userConfig.subscription_renewal_date
} }
</b> </b>
</p> </p>
</> </>
)) || )) ||
(userConfig.subscription_state === "expired" && ( (userConfig.subscription_state === "expired" && (
<> <>
<p className="text-xl">Free Plan</p> <p className="text-xl">Free Plan</p>
@ -773,17 +773,17 @@ export default function SettingsView() {
</b> </b>
</p> </p>
)) || ( )) || (
<p className="text-gray-400"> <p className="text-gray-400">
Check{" "} Check{" "}
<a <a
href="https://khoj.dev/pricing" href="https://khoj.dev/pricing"
target="_blank" target="_blank"
> >
pricing page pricing page
</a>{" "} </a>{" "}
to compare plans. to compare plans.
</p> </p>
)} )}
</> </>
))} ))}
</CardContent> </CardContent>
@ -800,20 +800,20 @@ export default function SettingsView() {
)) || )) ||
(userConfig.subscription_state == (userConfig.subscription_state ==
"unsubscribed" && ( "unsubscribed" && (
<Button <Button
variant="outline" variant="outline"
className="text-primary/80 hover:text-primary" className="text-primary/80 hover:text-primary"
onClick={() => onClick={() =>
setSubscription("resubscribe") setSubscription("resubscribe")
} }
> >
<ArrowCircleUp <ArrowCircleUp
weight="bold" weight="bold"
className="h-5 w-5 mr-2" className="h-5 w-5 mr-2"
/> />
Resubscribe Resubscribe
</Button> </Button>
)) || ( )) || (
<Button <Button
variant="outline" variant="outline"
className="text-primary/80 hover:text-primary" className="text-primary/80 hover:text-primary"
@ -848,11 +848,12 @@ export default function SettingsView() {
<CardHeader className="flex flex-row text-2xl"> <CardHeader className="flex flex-row text-2xl">
<Laptop className="h-8 w-8 mr-2" /> <Laptop className="h-8 w-8 mr-2" />
Files Files
{ {userConfig.enabled_content_source.computer && (
userConfig.enabled_content_source.computer && ( <CheckCircle
<CheckCircle className="h-6 w-6 ml-auto text-green-500" weight="fill" /> className="h-6 w-6 ml-auto text-green-500"
) weight="fill"
} />
)}
</CardHeader> </CardHeader>
<CardContent className="overflow-hidden pb-12 text-gray-400"> <CardContent className="overflow-hidden pb-12 text-gray-400">
Manage your synced files Manage your synced files
@ -895,11 +896,11 @@ export default function SettingsView() {
Manage Manage
</> </>
)) || ( )) || (
<> <>
<Plugs className="h-5 w-5 inline mr-1" /> <Plugs className="h-5 w-5 inline mr-1" />
Connect Connect
</> </>
)} )}
</Button> </Button>
<Button <Button
variant="outline" variant="outline"
@ -915,11 +916,12 @@ export default function SettingsView() {
<CardHeader className="text-xl flex flex-row"> <CardHeader className="text-xl flex flex-row">
<NotionLogo className="h-7 w-7 mr-2" /> <NotionLogo className="h-7 w-7 mr-2" />
Notion Notion
{ {userConfig.enabled_content_source.notion && (
userConfig.enabled_content_source.notion && ( <CheckCircle
<CheckCircle className="h-6 w-6 ml-auto text-green-500" weight="fill" /> className="h-6 w-6 ml-auto text-green-500"
) weight="fill"
} />
)}
</CardHeader> </CardHeader>
<CardContent className="grid gap-4"> <CardContent className="grid gap-4">
<p className="text-gray-400"> <p className="text-gray-400">
@ -938,7 +940,7 @@ export default function SettingsView() {
{ {
/* Show connect to notion button if notion oauth url setup and user disconnected*/ /* Show connect to notion button if notion oauth url setup and user disconnected*/
userConfig.notion_oauth_url && userConfig.notion_oauth_url &&
!userConfig.enabled_content_source.notion ? ( !userConfig.enabled_content_source.notion ? (
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
@ -952,35 +954,35 @@ export default function SettingsView() {
Connect Connect
</Button> </Button>
) : /* Show sync button if user connected to notion and API key unchanged */ ) : /* Show sync button if user connected to notion and API key unchanged */
userConfig.enabled_content_source.notion && userConfig.enabled_content_source.notion &&
notionToken === userConfig.notion_token ? ( notionToken === userConfig.notion_token ? (
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={() => syncContent("notion")} onClick={() => syncContent("notion")}
> >
<ArrowsClockwise className="h-5 w-5 inline mr-1" /> <ArrowsClockwise className="h-5 w-5 inline mr-1" />
Sync Sync
</Button> </Button>
) : /* Show set API key button notion oauth url not set setup */ ) : /* Show set API key button notion oauth url not set setup */
!userConfig.notion_oauth_url ? ( !userConfig.notion_oauth_url ? (
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={saveNotionToken} onClick={saveNotionToken}
disabled={ disabled={
notionToken === userConfig.notion_token notionToken === userConfig.notion_token
} }
> >
<FloppyDisk className="h-5 w-5 inline mr-1" /> <FloppyDisk className="h-5 w-5 inline mr-1" />
{(userConfig.enabled_content_source {(userConfig.enabled_content_source
.notion && .notion &&
"Update API Key") || "Update API Key") ||
"Set API Key"} "Set API Key"}
</Button> </Button>
) : ( ) : (
<></> <></>
) )
} }
<Button <Button
variant="outline" variant="outline"
@ -1195,18 +1197,18 @@ export default function SettingsView() {
Chat on Whatsapp Chat on Whatsapp
{(numberValidationState === {(numberValidationState ===
PhoneNumberValidationState.Verified && ( PhoneNumberValidationState.Verified && (
<CheckCircle <CheckCircle
weight="bold" weight="bold"
className="h-4 w-4 ml-1 text-green-400" className="h-4 w-4 ml-1 text-green-400"
/> />
)) || )) ||
(numberValidationState !== (numberValidationState !==
PhoneNumberValidationState.Setup && ( PhoneNumberValidationState.Setup && (
<ExclamationMark <ExclamationMark
weight="bold" weight="bold"
className="h-4 w-4 ml-1 text-yellow-400" className="h-4 w-4 ml-1 text-yellow-400"
/> />
))} ))}
</CardHeader> </CardHeader>
<CardContent className="grid gap-4"> <CardContent className="grid gap-4">
<p className="text-gray-400"> <p className="text-gray-400">
@ -1235,85 +1237,85 @@ export default function SettingsView() {
/> />
{numberValidationState === {numberValidationState ===
PhoneNumberValidationState.VerifyOTP && ( PhoneNumberValidationState.VerifyOTP && (
<> <>
<p>{`Enter the OTP sent to your number: ${phoneNumber}`}</p> <p>{`Enter the OTP sent to your number: ${phoneNumber}`}</p>
<InputOTP <InputOTP
autoFocus={true} autoFocus={true}
maxLength={6} maxLength={6}
value={otp || ""} value={otp || ""}
onChange={setOTP} onChange={setOTP}
onComplete={() => onComplete={() =>
setNumberValidationState( setNumberValidationState(
PhoneNumberValidationState.VerifyOTP, PhoneNumberValidationState.VerifyOTP,
) )
} }
> >
<InputOTPGroup> <InputOTPGroup>
<InputOTPSlot index={0} /> <InputOTPSlot index={0} />
<InputOTPSlot index={1} /> <InputOTPSlot index={1} />
<InputOTPSlot index={2} /> <InputOTPSlot index={2} />
<InputOTPSlot index={3} /> <InputOTPSlot index={3} />
<InputOTPSlot index={4} /> <InputOTPSlot index={4} />
<InputOTPSlot index={5} /> <InputOTPSlot index={5} />
</InputOTPGroup> </InputOTPGroup>
</InputOTP> </InputOTP>
</> </>
)} )}
</div> </div>
</CardContent> </CardContent>
<CardFooter className="flex flex-wrap gap-4"> <CardFooter className="flex flex-wrap gap-4">
{(numberValidationState === {(numberValidationState ===
PhoneNumberValidationState.VerifyOTP && ( PhoneNumberValidationState.VerifyOTP && (
<Button variant="outline" onClick={verifyOTP}> <Button variant="outline" onClick={verifyOTP}>
Verify Verify
</Button> </Button>
)) || ( )) || (
<Button <Button
variant="outline" variant="outline"
disabled={ disabled={
!phoneNumber || !phoneNumber ||
(phoneNumber === userConfig.phone_number && (phoneNumber === userConfig.phone_number &&
numberValidationState === numberValidationState ===
PhoneNumberValidationState.Verified) || PhoneNumberValidationState.Verified) ||
!isValidPhoneNumber(phoneNumber) !isValidPhoneNumber(phoneNumber)
} }
onClick={sendOTP} onClick={sendOTP}
> >
{!userConfig.phone_number ? ( {!userConfig.phone_number ? (
<> <>
<Plugs className="inline mr-2" /> <Plugs className="inline mr-2" />
Setup Whatsapp Setup Whatsapp
</> </>
) : !phoneNumber || ) : !phoneNumber ||
(phoneNumber === userConfig.phone_number && (phoneNumber === userConfig.phone_number &&
numberValidationState === numberValidationState ===
PhoneNumberValidationState.Verified) || PhoneNumberValidationState.Verified) ||
!isValidPhoneNumber(phoneNumber) ? ( !isValidPhoneNumber(phoneNumber) ? (
<> <>
<PlugsConnected className="inline mr-2 text-green-400" /> <PlugsConnected className="inline mr-2 text-green-400" />
Switch Number Switch Number
</> </>
) : ( ) : (
<> <>
Send OTP{" "} Send OTP{" "}
<ArrowRight <ArrowRight
className="inline ml-2" className="inline ml-2"
weight="bold" weight="bold"
/> />
</> </>
)} )}
</Button> </Button>
)} )}
{numberValidationState === {numberValidationState ===
PhoneNumberValidationState.Verified && ( PhoneNumberValidationState.Verified && (
<Button <Button
variant="outline" variant="outline"
onClick={() => disconnectNumber()} onClick={() => disconnectNumber()}
> >
<CloudSlash className="h-5 w-5 mr-2" /> <CloudSlash className="h-5 w-5 mr-2" />
Disconnect Disconnect
</Button> </Button>
)} )}
</CardFooter> </CardFooter>
</Card> </Card>
</div> </div>