diff --git a/frontend/package.json b/frontend/package.json index f24b5ac..6b1a3b1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,6 +11,7 @@ "preview": "vite preview" }, "dependencies": { + "@hookform/resolvers": "^4.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-dropdown-menu": "^2.1.6", "@radix-ui/react-label": "^2.1.2", @@ -18,17 +19,22 @@ "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.8", "@tailwindcss/vite": "^4.0.6", + "axios": "^1.7.9", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "embla-carousel-autoplay": "^8.5.2", "embla-carousel-react": "^8.5.2", "lucide-react": "^0.475.0", + "next-themes": "^0.4.4", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-hook-form": "^7.54.2", "react-router-dom": "^7.1.5", + "sonner": "^2.0.1", "tailwind-merge": "^3.0.1", "tailwindcss": "^4.0.6", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zod": "^3.24.2" }, "devDependencies": { "@eslint/js": "^9.19.0", diff --git a/frontend/src/api/axios.ts b/frontend/src/api/axios.ts new file mode 100644 index 0000000..5312064 --- /dev/null +++ b/frontend/src/api/axios.ts @@ -0,0 +1,9 @@ +import axios from 'axios' + +export const axiosInstance = axios.create({ + baseURL: 'http://localhost:3000', + headers: { + 'Content-Type': 'application/json', + }, + withCredentials: true, +}); \ No newline at end of file diff --git a/frontend/src/components/password-input.tsx b/frontend/src/components/password-input.tsx new file mode 100644 index 0000000..7fec070 --- /dev/null +++ b/frontend/src/components/password-input.tsx @@ -0,0 +1,39 @@ +import * as React from 'react' +import { Eye, EyeOff } from 'lucide-react'; +import { cn } from '@/lib/utils' +import { Button } from './ui/button' + +type PasswordInputProps = Omit< + React.InputHTMLAttributes, + 'type' +> + +const PasswordInput = React.forwardRef( + ({ className, disabled, ...props }, ref) => { + const [showPassword, setShowPassword] = React.useState(false) + return ( +
+ + +
+ ) + } +) +PasswordInput.displayName = 'PasswordInput' + +export { PasswordInput } diff --git a/frontend/src/components/ui/form.tsx b/frontend/src/components/ui/form.tsx new file mode 100644 index 0000000..2bca54c --- /dev/null +++ b/frontend/src/components/ui/form.tsx @@ -0,0 +1,175 @@ +import * as React from 'react' +import { + Controller, + ControllerProps, + FieldPath, + FieldValues, + FormProvider, + useFormContext, +} from 'react-hook-form' +import * as LabelPrimitive from '@radix-ui/react-label' +import { Slot } from '@radix-ui/react-slot' +import { cn } from '@/lib/utils' +import { Label } from '@/components/ui/label' + +const Form = FormProvider + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +> = { + name: TName +} + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue +) + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + ...props +}: ControllerProps) => { + return ( + + + + ) +} + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext) + const itemContext = React.useContext(FormItemContext) + const { getFieldState, formState } = useFormContext() + + const fieldState = getFieldState(fieldContext.name, formState) + + if (!fieldContext) { + throw new Error('useFormField should be used within ') + } + + const { id } = itemContext + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + } +} + +type FormItemContextValue = { + id: string +} + +const FormItemContext = React.createContext( + {} as FormItemContextValue +) + +const FormItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const id = React.useId() + + return ( + +
+ + ) +}) +FormItem.displayName = 'FormItem' + +const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const { error, formItemId } = useFormField() + + return ( +