Skip to content

Commit 1da4ea8

Browse files
authored
Merge pull request #24 from d4vidsha/d4vidsha/login-screen
feat: create login screen
2 parents 1fa9303 + ee12c00 commit 1da4ea8

File tree

8 files changed

+440
-124
lines changed

8 files changed

+440
-124
lines changed

frontend/package-lock.json

Lines changed: 55 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
"@emotion/styled": "^11.11.0",
2020
"@headlessui/react": "^2.1.1",
2121
"@heroicons/react": "^2.1.4",
22+
"@hookform/resolvers": "^3.9.0",
2223
"@radix-ui/react-avatar": "^1.1.0",
2324
"@radix-ui/react-dropdown-menu": "^2.1.1",
25+
"@radix-ui/react-label": "^2.1.0",
2426
"@radix-ui/react-slot": "^1.1.0",
2527
"@tanstack/react-query": "^5.49.0",
2628
"@tanstack/react-query-devtools": "^5.49.1",
@@ -36,10 +38,11 @@
3638
"react": "^18.2.0",
3739
"react-dom": "^18.2.0",
3840
"react-error-boundary": "^4.0.13",
39-
"react-hook-form": "7.49.3",
41+
"react-hook-form": "^7.49.3",
4042
"react-icons": "5.0.1",
4143
"tailwind-merge": "^2.3.0",
42-
"tailwindcss-animate": "^1.0.7"
44+
"tailwindcss-animate": "^1.0.7",
45+
"zod": "^3.23.8"
4346
},
4447
"devDependencies": {
4548
"@biomejs/biome": "1.6.1",
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import type * as LabelPrimitive from "@radix-ui/react-label"
2+
import { Slot } from "@radix-ui/react-slot"
3+
import * as React from "react"
4+
import {
5+
Controller,
6+
type ControllerProps,
7+
type FieldPath,
8+
type FieldValues,
9+
FormProvider,
10+
useFormContext,
11+
} from "react-hook-form"
12+
13+
import { Label } from "@/components/ui/label"
14+
import { cn } from "@/lib/utils"
15+
16+
const Form = FormProvider
17+
18+
type FormFieldContextValue<
19+
TFieldValues extends FieldValues = FieldValues,
20+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
21+
> = {
22+
name: TName
23+
}
24+
25+
const FormFieldContext = React.createContext<FormFieldContextValue>(
26+
{} as FormFieldContextValue,
27+
)
28+
29+
const FormField = <
30+
TFieldValues extends FieldValues = FieldValues,
31+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
32+
>({
33+
...props
34+
}: ControllerProps<TFieldValues, TName>) => {
35+
return (
36+
<FormFieldContext.Provider value={{ name: props.name }}>
37+
<Controller {...props} />
38+
</FormFieldContext.Provider>
39+
)
40+
}
41+
42+
const useFormField = () => {
43+
const fieldContext = React.useContext(FormFieldContext)
44+
const itemContext = React.useContext(FormItemContext)
45+
const { getFieldState, formState } = useFormContext()
46+
47+
const fieldState = getFieldState(fieldContext.name, formState)
48+
49+
if (!fieldContext) {
50+
throw new Error("useFormField should be used within <FormField>")
51+
}
52+
53+
const { id } = itemContext
54+
55+
return {
56+
id,
57+
name: fieldContext.name,
58+
formItemId: `${id}-form-item`,
59+
formDescriptionId: `${id}-form-item-description`,
60+
formMessageId: `${id}-form-item-message`,
61+
...fieldState,
62+
}
63+
}
64+
65+
type FormItemContextValue = {
66+
id: string
67+
}
68+
69+
const FormItemContext = React.createContext<FormItemContextValue>(
70+
{} as FormItemContextValue,
71+
)
72+
73+
const FormItem = React.forwardRef<
74+
HTMLDivElement,
75+
React.HTMLAttributes<HTMLDivElement>
76+
>(({ className, ...props }, ref) => {
77+
const id = React.useId()
78+
79+
return (
80+
<FormItemContext.Provider value={{ id }}>
81+
<div ref={ref} className={cn("space-y-2", className)} {...props} />
82+
</FormItemContext.Provider>
83+
)
84+
})
85+
FormItem.displayName = "FormItem"
86+
87+
const FormLabel = React.forwardRef<
88+
React.ElementRef<typeof LabelPrimitive.Root>,
89+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
90+
>(({ className, ...props }, ref) => {
91+
const { error, formItemId } = useFormField()
92+
93+
return (
94+
<Label
95+
ref={ref}
96+
className={cn(error && "text-destructive", className)}
97+
htmlFor={formItemId}
98+
{...props}
99+
/>
100+
)
101+
})
102+
FormLabel.displayName = "FormLabel"
103+
104+
const FormControl = React.forwardRef<
105+
React.ElementRef<typeof Slot>,
106+
React.ComponentPropsWithoutRef<typeof Slot>
107+
>(({ ...props }, ref) => {
108+
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
109+
110+
return (
111+
<Slot
112+
ref={ref}
113+
id={formItemId}
114+
aria-describedby={
115+
!error
116+
? `${formDescriptionId}`
117+
: `${formDescriptionId} ${formMessageId}`
118+
}
119+
aria-invalid={!!error}
120+
{...props}
121+
/>
122+
)
123+
})
124+
FormControl.displayName = "FormControl"
125+
126+
const FormDescription = React.forwardRef<
127+
HTMLParagraphElement,
128+
React.HTMLAttributes<HTMLParagraphElement>
129+
>(({ className, ...props }, ref) => {
130+
const { formDescriptionId } = useFormField()
131+
132+
return (
133+
<p
134+
ref={ref}
135+
id={formDescriptionId}
136+
className={cn("text-sm text-muted-foreground", className)}
137+
{...props}
138+
/>
139+
)
140+
})
141+
FormDescription.displayName = "FormDescription"
142+
143+
const FormMessage = React.forwardRef<
144+
HTMLParagraphElement,
145+
React.HTMLAttributes<HTMLParagraphElement>
146+
>(({ className, children, ...props }, ref) => {
147+
const { error, formMessageId } = useFormField()
148+
const body = error ? String(error?.message) : children
149+
150+
if (!body) {
151+
return null
152+
}
153+
154+
return (
155+
<p
156+
ref={ref}
157+
id={formMessageId}
158+
className={cn("text-sm font-medium text-destructive", className)}
159+
{...props}
160+
>
161+
{body}
162+
</p>
163+
)
164+
})
165+
FormMessage.displayName = "FormMessage"
166+
167+
export {
168+
useFormField,
169+
Form,
170+
FormItem,
171+
FormLabel,
172+
FormControl,
173+
FormDescription,
174+
FormMessage,
175+
FormField,
176+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import * as React from "react"
2+
3+
import { cn } from "@/lib/utils"
4+
5+
export interface InputProps
6+
extends React.InputHTMLAttributes<HTMLInputElement> {}
7+
8+
const Input = React.forwardRef<HTMLInputElement, InputProps>(
9+
({ className, type, ...props }, ref) => {
10+
return (
11+
<input
12+
type={type}
13+
className={cn(
14+
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
15+
className,
16+
)}
17+
ref={ref}
18+
{...props}
19+
/>
20+
)
21+
},
22+
)
23+
Input.displayName = "Input"
24+
25+
export { Input }
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as LabelPrimitive from "@radix-ui/react-label"
2+
import { type VariantProps, cva } from "class-variance-authority"
3+
import * as React from "react"
4+
5+
import { cn } from "@/lib/utils"
6+
7+
const labelVariants = cva(
8+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
9+
)
10+
11+
const Label = React.forwardRef<
12+
React.ElementRef<typeof LabelPrimitive.Root>,
13+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
14+
VariantProps<typeof labelVariants>
15+
>(({ className, ...props }, ref) => (
16+
<LabelPrimitive.Root
17+
ref={ref}
18+
className={cn(labelVariants(), className)}
19+
{...props}
20+
/>
21+
))
22+
Label.displayName = LabelPrimitive.Root.displayName
23+
24+
export { Label }

0 commit comments

Comments
 (0)