-
Notifications
You must be signed in to change notification settings - Fork 104
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
44c5c9a
commit 6106068
Showing
56 changed files
with
8,647 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
'use server'; | ||
|
||
import { z } from 'zod'; | ||
|
||
import { createUser, getUser } from '@/lib/db/queries'; | ||
|
||
import { signIn } from './auth'; | ||
|
||
const authFormSchema = z.object({ | ||
email: z.string().email(), | ||
password: z.string().min(6), | ||
}); | ||
|
||
export interface LoginActionState { | ||
status: 'idle' | 'in_progress' | 'success' | 'failed' | 'invalid_data'; | ||
} | ||
|
||
export const login = async ( | ||
_: LoginActionState, | ||
formData: FormData, | ||
): Promise<LoginActionState> => { | ||
try { | ||
const validatedData = authFormSchema.parse({ | ||
email: formData.get('email'), | ||
password: formData.get('password'), | ||
}); | ||
|
||
await signIn('credentials', { | ||
email: validatedData.email, | ||
password: validatedData.password, | ||
redirect: false, | ||
}); | ||
|
||
return { status: 'success' }; | ||
} catch (error) { | ||
if (error instanceof z.ZodError) { | ||
return { status: 'invalid_data' }; | ||
} | ||
|
||
return { status: 'failed' }; | ||
} | ||
}; | ||
|
||
export interface RegisterActionState { | ||
status: | ||
| 'idle' | ||
| 'in_progress' | ||
| 'success' | ||
| 'failed' | ||
| 'user_exists' | ||
| 'invalid_data'; | ||
} | ||
|
||
export const register = async ( | ||
_: RegisterActionState, | ||
formData: FormData, | ||
): Promise<RegisterActionState> => { | ||
try { | ||
const validatedData = authFormSchema.parse({ | ||
email: formData.get('email'), | ||
password: formData.get('password'), | ||
}); | ||
|
||
const [user] = await getUser(validatedData.email); | ||
|
||
if (user) { | ||
return { status: 'user_exists' } as RegisterActionState; | ||
} | ||
await createUser(validatedData.email, validatedData.password); | ||
await signIn('credentials', { | ||
email: validatedData.email, | ||
password: validatedData.password, | ||
redirect: false, | ||
}); | ||
|
||
return { status: 'success' }; | ||
} catch (error) { | ||
if (error instanceof z.ZodError) { | ||
return { status: 'invalid_data' }; | ||
} | ||
|
||
return { status: 'failed' }; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
'use client'; | ||
|
||
import type { User } from 'next-auth'; | ||
import { useRouter } from 'next/navigation'; | ||
|
||
import { PlusIcon } from '@/components/icons'; | ||
import { SidebarHistory } from '@/components/sidebar-history'; | ||
import { SidebarUserNav } from '@/components/sidebar-user-nav'; | ||
import { Button } from '@/components/ui/button'; | ||
import { | ||
Sidebar, | ||
SidebarContent, | ||
SidebarFooter, | ||
SidebarHeader, | ||
SidebarMenu, | ||
useSidebar, | ||
} from '@/components/ui/sidebar'; | ||
import Link from 'next/link'; | ||
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip'; | ||
|
||
export function AppSidebar({ user }: { user: User | undefined }) { | ||
const router = useRouter(); | ||
const { setOpenMobile } = useSidebar(); | ||
|
||
return ( | ||
<Sidebar className="group-data-[side=left]:border-r-0"> | ||
<SidebarHeader> | ||
<SidebarMenu> | ||
<div className="flex flex-row justify-between items-center"> | ||
<Link | ||
href="/" | ||
onClick={() => { | ||
setOpenMobile(false); | ||
}} | ||
className="flex flex-row gap-3 items-center" | ||
> | ||
<span className="text-lg font-semibold px-2 hover:bg-muted rounded-md cursor-pointer"> | ||
Chatbot | ||
</span> | ||
</Link> | ||
<Tooltip> | ||
<TooltipTrigger asChild> | ||
<Button | ||
variant="ghost" | ||
type="button" | ||
className="p-2 h-fit" | ||
onClick={() => { | ||
setOpenMobile(false); | ||
router.push('/'); | ||
router.refresh(); | ||
}} | ||
> | ||
<PlusIcon /> | ||
</Button> | ||
</TooltipTrigger> | ||
<TooltipContent align="end">New Chat</TooltipContent> | ||
</Tooltip> | ||
</div> | ||
</SidebarMenu> | ||
</SidebarHeader> | ||
<SidebarContent> | ||
<SidebarHistory user={user} /> | ||
</SidebarContent> | ||
<SidebarFooter>{user && <SidebarUserNav user={user} />}</SidebarFooter> | ||
</Sidebar> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import Form from 'next/form'; | ||
|
||
import { Input } from './ui/input'; | ||
import { Label } from './ui/label'; | ||
|
||
export function AuthForm({ | ||
action, | ||
children, | ||
defaultEmail = '', | ||
}: { | ||
action: NonNullable< | ||
string | ((formData: FormData) => void | Promise<void>) | undefined | ||
>; | ||
children: React.ReactNode; | ||
defaultEmail?: string; | ||
}) { | ||
return ( | ||
<Form action={action} className="flex flex-col gap-4 px-4 sm:px-16"> | ||
<div className="flex flex-col gap-2"> | ||
<Label | ||
htmlFor="email" | ||
className="text-zinc-600 font-normal dark:text-zinc-400" | ||
> | ||
Email Address | ||
</Label> | ||
|
||
<Input | ||
id="email" | ||
name="email" | ||
className="bg-muted text-md md:text-sm" | ||
type="email" | ||
placeholder="[email protected]" | ||
autoComplete="email" | ||
required | ||
autoFocus | ||
defaultValue={defaultEmail} | ||
/> | ||
</div> | ||
|
||
<div className="flex flex-col gap-2"> | ||
<Label | ||
htmlFor="password" | ||
className="text-zinc-600 font-normal dark:text-zinc-400" | ||
> | ||
Password | ||
</Label> | ||
|
||
<Input | ||
id="password" | ||
name="password" | ||
className="bg-muted text-md md:text-sm" | ||
type="password" | ||
required | ||
/> | ||
</div> | ||
|
||
{children} | ||
</Form> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import { cn } from '@/lib/utils'; | ||
import { ClockRewind, CopyIcon, RedoIcon, UndoIcon } from './icons'; | ||
import { Button } from './ui/button'; | ||
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip'; | ||
import { toast } from 'sonner'; | ||
import { ConsoleOutput, UIBlock } from './block'; | ||
import { Dispatch, memo, SetStateAction } from 'react'; | ||
import { RunCodeButton } from './run-code-button'; | ||
import { useMultimodalCopyToClipboard } from '@/hooks/use-multimodal-copy-to-clipboard'; | ||
|
||
interface BlockActionsProps { | ||
block: UIBlock; | ||
handleVersionChange: (type: 'next' | 'prev' | 'toggle' | 'latest') => void; | ||
currentVersionIndex: number; | ||
isCurrentVersion: boolean; | ||
mode: 'read-only' | 'edit' | 'diff'; | ||
setConsoleOutputs: Dispatch<SetStateAction<Array<ConsoleOutput>>>; | ||
} | ||
|
||
function PureBlockActions({ | ||
block, | ||
handleVersionChange, | ||
currentVersionIndex, | ||
isCurrentVersion, | ||
mode, | ||
setConsoleOutputs, | ||
}: BlockActionsProps) { | ||
const { copyTextToClipboard, copyImageToClipboard } = | ||
useMultimodalCopyToClipboard(); | ||
|
||
return ( | ||
<div className="flex flex-row gap-1"> | ||
{block.kind === 'code' && ( | ||
<RunCodeButton block={block} setConsoleOutputs={setConsoleOutputs} /> | ||
)} | ||
|
||
{block.kind === 'text' && ( | ||
<Tooltip> | ||
<TooltipTrigger asChild> | ||
<Button | ||
variant="outline" | ||
className={cn( | ||
'p-2 h-fit !pointer-events-auto dark:hover:bg-zinc-700', | ||
{ | ||
'bg-muted': mode === 'diff', | ||
}, | ||
)} | ||
onClick={() => { | ||
handleVersionChange('toggle'); | ||
}} | ||
disabled={ | ||
block.status === 'streaming' || currentVersionIndex === 0 | ||
} | ||
> | ||
<ClockRewind size={18} /> | ||
</Button> | ||
</TooltipTrigger> | ||
<TooltipContent>View changes</TooltipContent> | ||
</Tooltip> | ||
)} | ||
|
||
<Tooltip> | ||
<TooltipTrigger asChild> | ||
<Button | ||
variant="outline" | ||
className="p-2 h-fit dark:hover:bg-zinc-700 !pointer-events-auto" | ||
onClick={() => { | ||
handleVersionChange('prev'); | ||
}} | ||
disabled={currentVersionIndex === 0 || block.status === 'streaming'} | ||
> | ||
<UndoIcon size={18} /> | ||
</Button> | ||
</TooltipTrigger> | ||
<TooltipContent>View Previous version</TooltipContent> | ||
</Tooltip> | ||
|
||
<Tooltip> | ||
<TooltipTrigger asChild> | ||
<Button | ||
variant="outline" | ||
className="p-2 h-fit dark:hover:bg-zinc-700 !pointer-events-auto" | ||
onClick={() => { | ||
handleVersionChange('next'); | ||
}} | ||
disabled={isCurrentVersion || block.status === 'streaming'} | ||
> | ||
<RedoIcon size={18} /> | ||
</Button> | ||
</TooltipTrigger> | ||
<TooltipContent>View Next version</TooltipContent> | ||
</Tooltip> | ||
|
||
<Tooltip> | ||
<TooltipTrigger asChild> | ||
<Button | ||
variant="outline" | ||
className="p-2 h-fit dark:hover:bg-zinc-700" | ||
onClick={() => { | ||
if (block.kind === 'image') { | ||
copyImageToClipboard(block.content); | ||
} else { | ||
copyTextToClipboard(block.content); | ||
} | ||
|
||
toast.success('Copied to clipboard!'); | ||
}} | ||
disabled={block.status === 'streaming'} | ||
> | ||
<CopyIcon size={18} /> | ||
</Button> | ||
</TooltipTrigger> | ||
<TooltipContent>Copy to clipboard</TooltipContent> | ||
</Tooltip> | ||
</div> | ||
); | ||
} | ||
|
||
export const BlockActions = memo(PureBlockActions, (prevProps, nextProps) => { | ||
if (prevProps.block.status !== nextProps.block.status) return false; | ||
if (prevProps.currentVersionIndex !== nextProps.currentVersionIndex) | ||
return false; | ||
if (prevProps.isCurrentVersion !== nextProps.isCurrentVersion) return false; | ||
|
||
return true; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { memo } from 'react'; | ||
import { CrossIcon } from './icons'; | ||
import { Button } from './ui/button'; | ||
import { initialBlockData, useBlock } from '@/hooks/use-block'; | ||
|
||
function PureBlockCloseButton() { | ||
const { setBlock } = useBlock(); | ||
|
||
return ( | ||
<Button | ||
variant="outline" | ||
className="h-fit p-2 dark:hover:bg-zinc-700" | ||
onClick={() => { | ||
setBlock((currentBlock) => | ||
currentBlock.status === 'streaming' | ||
? { | ||
...currentBlock, | ||
isVisible: false, | ||
} | ||
: { ...initialBlockData, status: 'idle' }, | ||
); | ||
}} | ||
> | ||
<CrossIcon size={18} /> | ||
</Button> | ||
); | ||
} | ||
|
||
export const BlockCloseButton = memo(PureBlockCloseButton, () => true); |
Oops, something went wrong.