A very performant and light (2mb in memory) link shortener and tracker. Written in Rust and React and uses Postgres/SQLite.
1import { useState, useEffect } from 'react'
2import { useForm } from 'react-hook-form'
3import { z } from 'zod'
4import { zodResolver } from '@hookform/resolvers/zod'
5import { useAuth } from '../context/AuthContext'
6import { Button } from '@/components/ui/button'
7import { Input } from '@/components/ui/input'
8import { Card } from '@/components/ui/card'
9import {
10 Form,
11 FormControl,
12 FormField,
13 FormItem,
14 FormLabel,
15 FormMessage,
16} from '@/components/ui/form'
17import { useToast } from '@/hooks/use-toast'
18import { checkFirstUser } from '../api/client'
19
20const formSchema = z.object({
21 email: z.string().email('Invalid email address'),
22 password: z.string().min(6, 'Password must be at least 6 characters long'),
23 adminToken: z.string().optional(),
24})
25
26type FormValues = z.infer<typeof formSchema>
27
28export function AuthForms() {
29 const [isFirstUser, setIsFirstUser] = useState<boolean | null>(null)
30 const { login, register } = useAuth()
31 const { toast } = useToast()
32
33 const form = useForm<FormValues>({
34 resolver: zodResolver(formSchema),
35 defaultValues: {
36 email: '',
37 password: '',
38 adminToken: '',
39 },
40 })
41
42 useEffect(() => {
43 const init = async () => {
44 try {
45 const isFirst = await checkFirstUser()
46 setIsFirstUser(isFirst)
47 } catch (err) {
48 console.error('Error checking first user:', err)
49 setIsFirstUser(false)
50 }
51 }
52
53 init()
54 }, [])
55
56 const onSubmit = async (values: FormValues) => {
57 try {
58 if (isFirstUser) {
59 await register(values.email, values.password, values.adminToken || '')
60 } else {
61 await login(values.email, values.password)
62 }
63 form.reset()
64 } catch (err: any) {
65 toast({
66 variant: 'destructive',
67 title: 'Error',
68 description: err.response?.data || 'An error occurred',
69 })
70 }
71 }
72
73 if (isFirstUser === null) {
74 return <div>Loading...</div>
75 }
76
77 return (
78 <Card className="w-full max-w-md mx-auto p-6">
79 <div className="mb-6 text-center">
80 <h2 className="text-2xl font-bold">
81 {isFirstUser ? 'Create Admin Account' : 'Login'}
82 </h2>
83 <p className="text-sm text-muted-foreground mt-1">
84 {isFirstUser
85 ? 'Set up your admin account to get started'
86 : 'Welcome back! Please login to your account'}
87 </p>
88 </div>
89
90 <Form {...form}>
91 <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
92 <FormField
93 control={form.control}
94 name="email"
95 render={({ field }) => (
96 <FormItem>
97 <FormLabel>Email</FormLabel>
98 <FormControl>
99 <Input type="email" {...field} />
100 </FormControl>
101 <FormMessage />
102 </FormItem>
103 )}
104 />
105
106 <FormField
107 control={form.control}
108 name="password"
109 render={({ field }) => (
110 <FormItem>
111 <FormLabel>Password</FormLabel>
112 <FormControl>
113 <Input type="password" {...field} />
114 </FormControl>
115 <FormMessage />
116 </FormItem>
117 )}
118 />
119
120 {isFirstUser && (
121 <FormField
122 control={form.control}
123 name="adminToken"
124 render={({ field }) => (
125 <FormItem>
126 <FormLabel>Admin Setup Token</FormLabel>
127 <FormControl>
128 <Input type="text" {...field} />
129 </FormControl>
130 <FormMessage />
131 </FormItem>
132 )}
133 />
134 )}
135
136 <Button type="submit" className="w-full">
137 {isFirstUser ? 'Create Account' : 'Sign in'}
138 </Button>
139 </form>
140 </Form>
141 </Card>
142 )
143}