mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-08 05:39:13 +00:00
Align Content, Client & Billing settings sections with new designs
This commit is contained in:
@@ -10,6 +10,8 @@ import { useUserConfig, ModelOptions, UserConfig } from "../common/auth";
|
|||||||
import { isValidPhoneNumber } from 'libphonenumber-js';
|
import { isValidPhoneNumber } from 'libphonenumber-js';
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/ui/input-otp";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
@@ -17,20 +19,18 @@ import {
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
} from "@/components/ui/card";
|
} from "@/components/ui/card";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuRadioGroup,
|
DropdownMenuRadioGroup,
|
||||||
DropdownMenuRadioItem,
|
DropdownMenuRadioItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu"
|
} from "@/components/ui/dropdown-menu"
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
TableCell,
|
TableCell,
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
TableRow,
|
||||||
} from "@/components/ui/table"
|
} from "@/components/ui/table"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
@@ -42,22 +42,25 @@ import {
|
|||||||
FileMagnifyingGlass,
|
FileMagnifyingGlass,
|
||||||
Trash,
|
Trash,
|
||||||
Copy,
|
Copy,
|
||||||
PlusCircle,
|
|
||||||
CreditCard,
|
CreditCard,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
NotionLogo,
|
NotionLogo,
|
||||||
GithubLogo,
|
GithubLogo,
|
||||||
Files,
|
Files,
|
||||||
WhatsappLogo,
|
WhatsappLogo,
|
||||||
ExclamationMark
|
ExclamationMark,
|
||||||
|
Plugs,
|
||||||
|
CloudSlash,
|
||||||
|
Laptop,
|
||||||
|
Plus,
|
||||||
|
FloppyDisk,
|
||||||
|
PlugsConnected,
|
||||||
|
ArrowCircleUp,
|
||||||
} from "@phosphor-icons/react";
|
} from "@phosphor-icons/react";
|
||||||
|
|
||||||
import NavMenu from "../components/navMenu/navMenu";
|
import NavMenu from "../components/navMenu/navMenu";
|
||||||
import SidePanel from "../components/sidePanel/chatHistorySidePanel";
|
import SidePanel from "../components/sidePanel/chatHistorySidePanel";
|
||||||
import Loading from "../components/loading/loading";
|
import Loading from "../components/loading/loading";
|
||||||
import { ExternalLinkIcon } from "lucide-react";
|
|
||||||
import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/ui/input-otp";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
|
|
||||||
|
|
||||||
interface DropdownComponentProps {
|
interface DropdownComponentProps {
|
||||||
@@ -73,7 +76,7 @@ const DropdownComponent: React.FC<DropdownComponentProps> = ({ items, selected,
|
|||||||
<div className="overflow-hidden">
|
<div className="overflow-hidden">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild className="w-full">
|
<DropdownMenuTrigger asChild className="w-full">
|
||||||
<Button variant="outline" className="justify-start">
|
<Button variant="outline" className="justify-start py-6">
|
||||||
{items.find(item => item.id === Number(position))?.name}
|
{items.find(item => item.id === Number(position))?.name}
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
@@ -323,128 +326,197 @@ export default function SettingsView() {
|
|||||||
<Suspense fallback={<Loading />}>
|
<Suspense fallback={<Loading />}>
|
||||||
<div id="content" className="grid grid-flow-column sm:grid-flow-row gap-16 m-8">
|
<div id="content" className="grid grid-flow-column sm:grid-flow-row gap-16 m-8">
|
||||||
<div className="section grid gap-8">
|
<div className="section grid gap-8">
|
||||||
<div className="text-4xl">Profile</div>
|
<div className="text-2xl">Profile</div>
|
||||||
<div className="cards flex flex-wrap gap-16">
|
<div className="cards flex flex-wrap gap-16">
|
||||||
<Card className={cardClassName}>
|
<Card className={cardClassName}>
|
||||||
<CardHeader className="text-xl flex flex-row"><UserCircle className="h-7 w-7 mr-2"/>Name</CardHeader>
|
<CardHeader className="text-xl flex flex-row"><UserCircle className="h-7 w-7 mr-2"/>Name</CardHeader>
|
||||||
<CardContent className="overflow-hidden">
|
<CardContent className="overflow-hidden">
|
||||||
<input type="text" className="w-full border border-gray-300 rounded-lg p-4" defaultValue={userConfig.given_name} />
|
<p className="pb-4 text-gray-400">What should Khoj refer to you as?</p>
|
||||||
|
<Input type="text" className="w-full border border-gray-300 rounded-lg p-4 py-6" defaultValue={userConfig.given_name} />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter className="flex flex-wrap gap-4">
|
<CardFooter className="flex flex-wrap gap-4">
|
||||||
<Button variant="outline" size="sm" className="border-green-400">Save</Button>
|
<Button variant="outline" size="sm"><FloppyDisk className="h-5 w-5 inline mr-2" />Save</Button>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="section grid gap-8">
|
<div className="section grid gap-8">
|
||||||
<div className="text-4xl">Content</div>
|
<div className="text-2xl">Content</div>
|
||||||
<div className="cards flex flex-wrap gap-16">
|
<div className="cards flex flex-wrap gap-16">
|
||||||
<Card className={cardClassName}>
|
<Card className={cardClassName}>
|
||||||
<CardHeader className="text-xl flex flex-row"><Files className="h-7 w-7 mr-2" />Files</CardHeader>
|
<CardHeader className="text-xl flex flex-row text-2xl"><Laptop className="h-8 w-8 mr-2" />Files</CardHeader>
|
||||||
<CardContent className="overflow-hidden">
|
<CardContent className="overflow-hidden pb-12 text-gray-400">
|
||||||
Manage your synced files
|
Manage your synced files
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter className="flex flex-wrap gap-4">
|
<CardFooter className="flex flex-wrap gap-4">
|
||||||
<Button variant="outline" size="sm" className="border-green-400">{userConfig.enabled_content_source.computer ? "Update" : "Setup"} <ArrowRight className="inline ml-2" weight="bold"/></Button>
|
<Button variant="outline" size="sm">
|
||||||
<Button variant="outline" size="sm" className={`${userConfig.enabled_content_source.computer ? "border-red-400" : "hidden"}`}>Disable</Button>
|
{userConfig.enabled_content_source.computer && (
|
||||||
|
<>
|
||||||
|
<Files className="h-5 w-5 inline mr-1" />Manage
|
||||||
|
</>
|
||||||
|
) || (
|
||||||
|
<>
|
||||||
|
<Plugs className="h-5 w-5 inline mr-1" />Connect
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
<Button variant="outline" size="sm" className={`${userConfig.enabled_content_source.computer || "hidden"}`}>
|
||||||
|
<CloudSlash className="h-5 w-5 inline mr-1" />Disable
|
||||||
|
</Button>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
<Card className={cardClassName}>
|
<Card className={cardClassName}>
|
||||||
<CardHeader className="text-xl flex flex-row"><GithubLogo className="h-7 w-7 mr-2" />Github</CardHeader>
|
<CardHeader className="text-xl flex flex-row text-2xl"><GithubLogo className="h-8 w-8 mr-2" />Github</CardHeader>
|
||||||
<CardContent className="overflow-hidden">
|
<CardContent className="overflow-hidden pb-12 text-gray-400">
|
||||||
Set repositories to index
|
Set Github repositories to index
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter className="flex flex-wrap gap-4">
|
<CardFooter className="flex flex-wrap gap-4">
|
||||||
<Button variant="outline" size="sm" className="border-green-400">{userConfig.enabled_content_source.github ? "Update" : "Setup"} <ArrowRight className="inline ml-2" weight="bold"/></Button>
|
<Button variant="outline" size="sm">
|
||||||
<Button variant="outline" size="sm" className={`${userConfig.enabled_content_source.github ? "border-red-400" : "hidden"}`}>Disable</Button>
|
{userConfig.enabled_content_source.github && (
|
||||||
|
<>
|
||||||
|
<Files className="h-5 w-5 inline mr-1" />Manage
|
||||||
|
</>
|
||||||
|
) || (
|
||||||
|
<>
|
||||||
|
<Plugs className="h-5 w-5 inline mr-1" />Connect
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
<Button variant="outline" size="sm" className={`${userConfig.enabled_content_source.github || "hidden"}`}>
|
||||||
|
<CloudSlash className="h-5 w-5 inline mr-1" />Disable
|
||||||
|
</Button>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
<Card className={cardClassName}>
|
<Card className={cardClassName}>
|
||||||
<CardHeader className="text-xl flex flex-row"><NotionLogo className="h-7 w-7 mr-2" />Notion</CardHeader>
|
<CardHeader className="text-xl flex flex-row"><NotionLogo className="h-7 w-7 mr-2" />Notion</CardHeader>
|
||||||
<CardContent className="overflow-hidden">
|
<CardContent className="overflow-hidden pb-12 text-gray-400">
|
||||||
Sync your Notion pages
|
Sync your Notion pages
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter className="flex flex-wrap gap-4">
|
<CardFooter className="flex flex-wrap gap-4">
|
||||||
<Button variant="outline" size="sm" className="border-green-400">{userConfig.enabled_content_source.notion ? "Update" : "Setup"} <ArrowRight className="inline ml-2" weight="bold"/></Button>
|
<Button variant="outline" size="sm">
|
||||||
<Button variant="outline" size="sm" className={`${userConfig.enabled_content_source.notion ? "border-red-400" : "hidden"}`}>Disable</Button>
|
{userConfig.enabled_content_source.notion && (
|
||||||
|
<>
|
||||||
|
<Files className="h-5 w-5 inline mr-1" />Manage
|
||||||
|
</>
|
||||||
|
) || (
|
||||||
|
<>
|
||||||
|
<Plugs className="h-5 w-5 inline mr-1" />Connect
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
<Button variant="outline" size="sm" className={`${userConfig.enabled_content_source.notion || "hidden"}`}>
|
||||||
|
<CloudSlash className="h-5 w-5 inline mr-1" />Disable
|
||||||
|
</Button>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="section grid gap-8">
|
<div className="section grid gap-8">
|
||||||
<div className="text-4xl">Features</div>
|
<div className="text-2xl">Models</div>
|
||||||
<div className="cards flex flex-wrap gap-16">
|
<div className="cards flex flex-wrap gap-16">
|
||||||
|
{userConfig.chat_model_options.length > 0 && (
|
||||||
<Card className={cardClassName}>
|
<Card className={cardClassName}>
|
||||||
<CardHeader className="text-xl flex flex-row"><ChatCircleText className="h-7 w-7 mr-2"/>Chat</CardHeader>
|
<CardHeader className="text-xl flex flex-row"><ChatCircleText className="h-7 w-7 mr-2"/>Chat</CardHeader>
|
||||||
<CardContent className="overflow-hidden">
|
<CardContent className="overflow-hidden pb-12 grid gap-8">
|
||||||
|
<p className="text-gray-400">Pick the chat model to generate text responses</p>
|
||||||
<DropdownComponent
|
<DropdownComponent
|
||||||
items={userConfig.chat_model_options}
|
items={userConfig.chat_model_options}
|
||||||
selected={userConfig.selected_chat_model_config}
|
selected={userConfig.selected_chat_model_config}
|
||||||
callbackFunc={updateModel("chat")}
|
callbackFunc={updateModel("chat")}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
<CardFooter className="flex flex-wrap gap-4">
|
||||||
|
{!userConfig.is_active && (
|
||||||
|
<p className="text-gray-400">Subscribe to switch model</p>
|
||||||
|
)}
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
{userConfig.search_model_options.length > 0 && (
|
||||||
<Card className={cardClassName}>
|
<Card className={cardClassName}>
|
||||||
<CardHeader className="text-xl flex flex-row"><FileMagnifyingGlass className="h-7 w-7 mr-2"/>Search</CardHeader>
|
<CardHeader className="text-xl flex flex-row"><FileMagnifyingGlass className="h-7 w-7 mr-2"/>Search</CardHeader>
|
||||||
<CardContent className="overflow-hidden">
|
<CardContent className="overflow-hidden pb-12 grid gap-8">
|
||||||
|
<p className="text-gray-400">Pick the search model to find your documents</p>
|
||||||
<DropdownComponent
|
<DropdownComponent
|
||||||
items={userConfig.search_model_options}
|
items={userConfig.search_model_options}
|
||||||
selected={userConfig.selected_search_model_config}
|
selected={userConfig.selected_search_model_config}
|
||||||
callbackFunc={updateModel("search")}
|
callbackFunc={updateModel("search")}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
<CardFooter className="flex flex-wrap gap-4">
|
||||||
|
{!userConfig.is_active && (
|
||||||
|
<p className="text-gray-400">Subscribe to switch model</p>
|
||||||
|
)}
|
||||||
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
|
)}
|
||||||
|
{userConfig.paint_model_options.length > 0 && (
|
||||||
<Card className={cardClassName}>
|
<Card className={cardClassName}>
|
||||||
<CardHeader className="text-xl flex flex-row"><Palette className="h-7 w-7 mr-2"/>Paint</CardHeader>
|
<CardHeader className="text-xl flex flex-row"><Palette className="h-7 w-7 mr-2"/>Paint</CardHeader>
|
||||||
<CardContent className="overflow-hidden">
|
<CardContent className="overflow-hidden pb-12 grid gap-8">
|
||||||
|
<p className="text-gray-400">Pick the paint model to generate image responses</p>
|
||||||
<DropdownComponent
|
<DropdownComponent
|
||||||
items={userConfig.paint_model_options}
|
items={userConfig.paint_model_options}
|
||||||
selected={userConfig.selected_paint_model_config}
|
selected={userConfig.selected_paint_model_config}
|
||||||
callbackFunc={updateModel("paint")}
|
callbackFunc={updateModel("paint")}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
<CardFooter className="flex flex-wrap gap-4">
|
||||||
|
{!userConfig.is_active && (
|
||||||
|
<p className="text-gray-400">Subscribe to switch model</p>
|
||||||
|
)}
|
||||||
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
|
)}
|
||||||
|
{userConfig.voice_model_options.length > 0 && (
|
||||||
<Card className={cardClassName}>
|
<Card className={cardClassName}>
|
||||||
<CardHeader className="text-xl flex flex-row"><SpeakerHigh className="h-7 w-7 mr-2"/>Voice</CardHeader>
|
<CardHeader className="text-xl flex flex-row"><SpeakerHigh className="h-7 w-7 mr-2"/>Voice</CardHeader>
|
||||||
<CardContent className="overflow-hidden">
|
<CardContent className="overflow-hidden pb-12 grid gap-8">
|
||||||
|
<p className="text-gray-400">Pick the voice model to generate speech responses</p>
|
||||||
<DropdownComponent
|
<DropdownComponent
|
||||||
items={userConfig.voice_model_options}
|
items={userConfig.voice_model_options}
|
||||||
selected={userConfig.selected_voice_model_config}
|
selected={userConfig.selected_voice_model_config}
|
||||||
callbackFunc={updateModel("voice")}
|
callbackFunc={updateModel("voice")}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
<CardFooter className="flex flex-wrap gap-4">
|
||||||
|
{!userConfig.is_active && (
|
||||||
|
<p className="text-gray-400">Subscribe to switch model</p>
|
||||||
|
)}
|
||||||
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="section grid gap-8">
|
<div className="section grid gap-8">
|
||||||
<div className="text-4xl">Clients</div>
|
<div className="text-2xl">Clients</div>
|
||||||
<div className="cards flex flex-wrap gap-16">
|
<div className="cards flex flex-wrap gap-8">
|
||||||
<Card className="grid grid-flow-column border border-gray-300 shadow-md rounded-lg">
|
<Card className="grid grid-flow-column border border-gray-300 shadow-md rounded-lg">
|
||||||
<CardHeader className="text-xl flex flex-row"><Key className="h-7 w-7 mr-2"/>API Keys</CardHeader>
|
<CardHeader className="text-xl grid grid-flow-col grid-cols-[1fr_auto] pb-0">
|
||||||
<CardContent className="overflow-hidden">
|
<span className="flex flex-wrap">
|
||||||
|
<Key className="h-7 w-7 mr-2" />API Keys
|
||||||
|
</span>
|
||||||
|
<Button variant="secondary" className="!mt-0" onClick={generateAPIKey}>
|
||||||
|
<Plus weight="bold" className='h-5 w-5 mr-2' />Generate Key
|
||||||
|
</Button>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="overflow-hidden grid gap-6">
|
||||||
<p className="text-md text-gray-400">
|
<p className="text-md text-gray-400">
|
||||||
Access Khoj from your Desktop App, Obsidian plugin, and more.
|
Access Khoj from the <a href="https://docs.khoj.dev/clients/Desktop" target="_blank">Desktop</a>, <a href="https://docs.khoj.dev/clients/Obsidian">Obsidian</a>, <a href="https://docs.khoj.dev/clients/Emacs">Emacs</a> apps and more.
|
||||||
</p>
|
</p>
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
|
||||||
<TableRow>
|
|
||||||
<TableHead className="pl-0">Name</TableHead>
|
|
||||||
<TableHead>Token</TableHead>
|
|
||||||
<TableHead>Actions</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{apiKeys.map((key) => (
|
{apiKeys.map((key) => (
|
||||||
<TableRow key={key.token}>
|
<TableRow key={key.token}>
|
||||||
<TableCell className="pl-0"><b>{key.name}</b></TableCell>
|
<TableCell className="pl-0 py-3">{key.name}</TableCell>
|
||||||
<TableCell>{`${key.token.slice(0, 4)}...${key.token.slice(-4)}`}</TableCell>
|
<TableCell className="grid grid-flow-col grid-cols-[1fr_auto] bg-secondary rounded-xl p-3">
|
||||||
<TableCell>
|
<span>
|
||||||
<Button variant="outline" className="border border-green-400" onClick={() => copyAPIKey(key.token)}>
|
{`${key.token.slice(0, 6)}...${key.token.slice(-4)}`}
|
||||||
<Copy className="h-4 w-4 mr-2" /><span className="hidden md:inline">Copy</span>
|
</span>
|
||||||
</Button>
|
<div className="grid grid-flow-col">
|
||||||
<Button variant="outline" className="md:ml-4 border border-red-400" onClick={() => deleteAPIKey(key.token)}>
|
<Copy weight="bold" className="h-4 w-4 mr-2 hover:bg-primary/40" onClick={() => copyAPIKey(key.token)}/>
|
||||||
<Trash className='h-4 w-4 mr-2' /><span className="hidden md:inline">Delete</span>
|
<Trash weight="bold" className='h-4 w-4 mr-2 md:ml-4 text-red-400 hover:bg-primary/40' onClick={() => deleteAPIKey(key.token)}/>
|
||||||
</Button>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
@@ -452,9 +524,6 @@ export default function SettingsView() {
|
|||||||
</Table>
|
</Table>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter className="flex flex-wrap gap-4">
|
<CardFooter className="flex flex-wrap gap-4">
|
||||||
<Button variant="outline" className="border border-green-300" onClick={generateAPIKey}>
|
|
||||||
<PlusCircle className='h-4 w-4 mr-2' />Generate Key
|
|
||||||
</Button>
|
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
<Card className={cardClassName}>
|
<Card className={cardClassName}>
|
||||||
@@ -468,17 +537,17 @@ export default function SettingsView() {
|
|||||||
)}
|
)}
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="grid gap-4">
|
<CardContent className="grid gap-4">
|
||||||
<p>
|
<p className="text-gray-400">
|
||||||
Connect your number to chat with Khoj on WhatsApp. Learn more about the integration <a href="https://docs.khoj.dev/clients/whatsapp">here</a>.
|
Connect your number to chat with Khoj on WhatsApp. Learn more about the integration <a href="https://docs.khoj.dev/clients/whatsapp">here</a>.
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
{numberValidationState === PhoneNumberValidationState.VerifyOTP && (
|
{numberValidationState === PhoneNumberValidationState.VerifyOTP && (
|
||||||
<div>
|
<>
|
||||||
<p>{`Enter the OTP sent to your WhatsApp number: ${number}`}</p>
|
<p>{`Enter the OTP sent to your WhatsApp number: ${number}`}</p>
|
||||||
<InputOTP
|
<InputOTP
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
maxLength={6}
|
maxLength={6}
|
||||||
value={otp}
|
value={otp || ""}
|
||||||
onChange={setOTP}
|
onChange={setOTP}
|
||||||
onComplete={() => setNumberValidationState(PhoneNumberValidationState.Verified)}
|
onComplete={() => setNumberValidationState(PhoneNumberValidationState.Verified)}
|
||||||
>
|
>
|
||||||
@@ -491,16 +560,17 @@ export default function SettingsView() {
|
|||||||
<InputOTPSlot index={5} />
|
<InputOTPSlot index={5} />
|
||||||
</InputOTPGroup>
|
</InputOTPGroup>
|
||||||
</InputOTP>
|
</InputOTP>
|
||||||
</div>
|
</>
|
||||||
) || (
|
) || (
|
||||||
<div>
|
<>
|
||||||
<Input
|
<Input
|
||||||
|
type="tel"
|
||||||
onChange={(e) => setNumber(e.target.value)}
|
onChange={(e) => setNumber(e.target.value)}
|
||||||
value={number}
|
value={number || ""}
|
||||||
placeholder="Enter your WhatsApp number"
|
placeholder="Enter your WhatsApp number"
|
||||||
className="w-full border border-gray-300 rounded-lg p-4"
|
className="w-full border border-gray-300 rounded-lg px-4 py-6"
|
||||||
/>
|
/>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
@@ -508,7 +578,6 @@ export default function SettingsView() {
|
|||||||
{numberValidationState === PhoneNumberValidationState.VerifyOTP && (
|
{numberValidationState === PhoneNumberValidationState.VerifyOTP && (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="border border-green-400"
|
|
||||||
onClick={verifyOTP}
|
onClick={verifyOTP}
|
||||||
>
|
>
|
||||||
Verify
|
Verify
|
||||||
@@ -516,15 +585,15 @@ export default function SettingsView() {
|
|||||||
) || (
|
) || (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="border border-green-400"
|
|
||||||
disabled={!number || number === userConfig.phone_number || !isValidPhoneNumber(number)}
|
disabled={!number || number === userConfig.phone_number || !isValidPhoneNumber(number)}
|
||||||
onClick={sendOTP}
|
onClick={sendOTP}
|
||||||
>
|
>
|
||||||
{!number || number === userConfig.phone_number || !isValidPhoneNumber(number)
|
{!userConfig.phone_number
|
||||||
? "Update"
|
? (<><Plugs className="inline mr-2" />Setup Whatsapp</>)
|
||||||
: (
|
: !number || number === userConfig.phone_number || !isValidPhoneNumber(number)
|
||||||
<>Send OTP to Whatsapp <ArrowRight className="inline ml-2" weight="bold"/></>
|
? (<><PlugsConnected className="inline mr-2 text-green-400" />Switch Number</>)
|
||||||
)}
|
: (<>Send OTP to Whatsapp <ArrowRight className="inline ml-2" weight="bold"/></>)
|
||||||
|
}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
@@ -532,68 +601,66 @@ export default function SettingsView() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="section grid gap-8">
|
<div className="section grid gap-8">
|
||||||
<div className="text-4xl">Billing</div>
|
<div className="text-2xl">Billing</div>
|
||||||
<div className="cards flex flex-wrap gap-16">
|
<div className="cards flex flex-wrap gap-16">
|
||||||
<Card className={cardClassName}>
|
<Card className={cardClassName}>
|
||||||
<CardHeader className="text-xl flex flex-row">
|
<CardHeader className="text-xl flex flex-row">
|
||||||
<CreditCard className="h-7 w-7 mr-2"/>
|
<CreditCard className="h-7 w-7 mr-2"/>
|
||||||
Subscription
|
Subscription
|
||||||
{(userConfig.subscription_state === "subscribed" || userConfig.subscription_state === "unsubscribed") && (
|
|
||||||
<CheckCircle weight="bold" className="h-4 w-4 ml-1 text-green-400" />
|
|
||||||
)}
|
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="overflow-hidden">
|
<CardContent className="grid gap-2 overflow-hidden">
|
||||||
|
<p className="text-gray-400">Current Plan</p>
|
||||||
{userConfig.subscription_state === "trial" && (
|
{userConfig.subscription_state === "trial" && (
|
||||||
<div>
|
<>
|
||||||
<p>You are on a 14 day trial of the Khoj <i>Futurist</i> plan</p>
|
<p className="text-xl">Futurist Trial</p>
|
||||||
<p>See <a href="https://khoj.dev/pricing">pricing</a> for details</p>
|
<p className="text-gray-400">You are on a 14 day trial of the Khoj <i>Futurist</i> plan</p>
|
||||||
</div>
|
<p className="text-gray-400">See <a href="https://khoj.dev/pricing">pricing</a> for details</p>
|
||||||
|
</>
|
||||||
) || userConfig.subscription_state === "subscribed" && (
|
) || userConfig.subscription_state === "subscribed" && (
|
||||||
<div>
|
<>
|
||||||
<p>You are <b>subscribed</b> to Khoj <i>Futurist</i></p>
|
<p className="text-xl">Futurist</p>
|
||||||
<p>Subscription will <b>renew</b> on <b>{ userConfig.subscription_renewal_date }</b></p>
|
<p className="text-gray-400">Subscription <b>renews</b> on <b>{ userConfig.subscription_renewal_date }</b></p>
|
||||||
</div>
|
</>
|
||||||
) || userConfig.subscription_state === "unsubscribed" && (
|
) || userConfig.subscription_state === "unsubscribed" && (
|
||||||
<div>
|
<>
|
||||||
<p>You are <b>subscribed</b> to Khoj <i>Futurist</i></p>
|
<p className="text-xl">Futurist</p>
|
||||||
<p>Subscription will <b>expire</b> on <b>{ userConfig.subscription_renewal_date }</b></p>
|
<p className="text-gray-400">Subscription <b>ends</b> on <b>{ userConfig.subscription_renewal_date }</b></p>
|
||||||
</div>
|
</>
|
||||||
) || userConfig.subscription_state === "expired" && (
|
) || userConfig.subscription_state === "expired" && (
|
||||||
<div>
|
<>
|
||||||
<p>Subscribe to the Khoj <i>Futurist</i> plan</p>
|
<p className="text-xl">Trial</p>
|
||||||
{userConfig.subscription_renewal_date && (
|
{userConfig.subscription_renewal_date && (
|
||||||
<p>Your subscription <b>expired</b> on <b>{ userConfig.subscription_renewal_date }</b></p>
|
<p className="text-gray-400">Your subscription <b>expired</b> on <b>{ userConfig.subscription_renewal_date }</b></p>
|
||||||
) || (
|
) || (
|
||||||
<p>See <a href="https://khoj.dev/pricing">pricing</a> for details</p>
|
<p className="text-gray-400">See <a href="https://khoj.dev/pricing">pricing</a> for details</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter className="flex flex-wrap gap-4">
|
<CardFooter className="flex flex-wrap gap-4">
|
||||||
{(userConfig.subscription_state == "subscribed") && (
|
{(userConfig.subscription_state == "subscribed") && (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="border border-red-400"
|
className="hover:text-red-400"
|
||||||
onClick={() => setSubscription("cancel")}
|
onClick={() => setSubscription("cancel")}
|
||||||
>
|
>
|
||||||
Unsubscribe
|
<CloudSlash className="h-5 w-5 mr-2" />Unsubscribe
|
||||||
</Button>
|
</Button>
|
||||||
) || (userConfig.subscription_state == "unsubscribed") && (
|
) || (userConfig.subscription_state == "unsubscribed") && (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="border border-green-400"
|
className="text-primary/80 hover:text-primary"
|
||||||
onClick={() => setSubscription("resubscribe")}
|
onClick={() => setSubscription("resubscribe")}
|
||||||
>
|
>
|
||||||
Resubscribe
|
<ArrowCircleUp weight="bold" className="h-5 w-5 mr-2" />Resubscribe
|
||||||
</Button>
|
</Button>
|
||||||
) || (
|
) || (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="border border-green-400"
|
className="text-primary/80 hover:text-primary"
|
||||||
onClick={() => window.open(`${userConfig.khoj_cloud_subscription_url}?prefilled_email=${userConfig.username}`, '_blank', 'noopener,noreferrer')}
|
onClick={() => window.open(`${userConfig.khoj_cloud_subscription_url}?prefilled_email=${userConfig.username}`, '_blank', 'noopener,noreferrer')}
|
||||||
>
|
>
|
||||||
Subscribe
|
<ArrowCircleUp weight="bold" className="h-5 w-5 mr-2" />Subscribe
|
||||||
<ExternalLinkIcon className="h-4 w-4 ml-1" />
|
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
|
|||||||
Reference in New Issue
Block a user