diff --git a/apps/frontend/src/assets/backgrounds/backgroundLogin.png b/apps/frontend/src/assets/backgrounds/backgroundLogin.png new file mode 100644 index 0000000..3eba5c9 Binary files /dev/null and b/apps/frontend/src/assets/backgrounds/backgroundLogin.png differ diff --git a/apps/frontend/src/assets/backgrounds/backgroundLoginMobile.png b/apps/frontend/src/assets/backgrounds/backgroundLoginMobile.png new file mode 100644 index 0000000..3032a38 Binary files /dev/null and b/apps/frontend/src/assets/backgrounds/backgroundLoginMobile.png differ diff --git a/apps/frontend/src/routes/login/index.lazy.tsx b/apps/frontend/src/routes/login/index.lazy.tsx index bc9d30d..58c16ef 100644 --- a/apps/frontend/src/routes/login/index.lazy.tsx +++ b/apps/frontend/src/routes/login/index.lazy.tsx @@ -1,21 +1,24 @@ import { createLazyFileRoute, useNavigate } from "@tanstack/react-router"; import { useMutation } from "@tanstack/react-query"; -import { - Paper, - PasswordInput, - Stack, - Text, - TextInput, - Group, - Button, - Alert, -} from "@mantine/core"; +import { Input } from '@/shadcn/components/ui/input.tsx'; +import { Button } from '@/shadcn/components/ui/button.tsx'; +import { Alert } from '@/shadcn/components/ui/alert.tsx'; +import { Card } from '@/shadcn/components/ui/card.tsx'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage +} from '@/shadcn/components/ui/form.tsx'; import client from "../../honoClient"; -import { useForm } from "@mantine/form"; +import { useForm } from "react-hook-form"; import { z } from "zod"; -import { zodResolver } from "mantine-form-zod-resolver"; +import { zodResolver } from "@hookform/resolvers/zod"; import { useEffect, useState } from "react"; import useAuth from "@/hooks/useAuth"; +import { TbArrowNarrowRight } from "react-icons/tb"; export const Route = createLazyFileRoute("/login/")({ component: LoginPage, @@ -38,11 +41,11 @@ export default function LoginPage() { const { isAuthenticated, saveAuthData } = useAuth(); const form = useForm({ - initialValues: { + resolver: zodResolver(formSchema), + defaultValues: { username: "", password: "", }, - validate: zodResolver(formSchema), }); useEffect(() => { @@ -94,63 +97,94 @@ export default function LoginPage() { }; return ( -
- - - Welcome - -
- - {errorMessage ? ( - - {errorMessage} - - ) : null} - - - - - - {/* +
+ Amati +
+
+ +

Sign In

+

+ New to this app?{' '} + - Don't have an account? Register - */} -

- - - - - + Register now + +

+
+ +
+ {errorMessage && ( + +

{errorMessage}

+
+ )} + ( + + Email/Username + + + + + + )} + /> + ( + + Password + + + + + + )} + /> +

+ + Forgot Password? + +

+
+
+ +
+
+ + +
); } diff --git a/apps/frontend/src/shadcn/components/ui/alert.tsx b/apps/frontend/src/shadcn/components/ui/alert.tsx new file mode 100644 index 0000000..41fa7e0 --- /dev/null +++ b/apps/frontend/src/shadcn/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } diff --git a/apps/frontend/src/shadcn/components/ui/card.tsx b/apps/frontend/src/shadcn/components/ui/card.tsx new file mode 100644 index 0000000..afa13ec --- /dev/null +++ b/apps/frontend/src/shadcn/components/ui/card.tsx @@ -0,0 +1,79 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/apps/frontend/src/shadcn/components/ui/form.tsx b/apps/frontend/src/shadcn/components/ui/form.tsx new file mode 100644 index 0000000..44dc758 --- /dev/null +++ b/apps/frontend/src/shadcn/components/ui/form.tsx @@ -0,0 +1,178 @@ +"use client" + +import * as React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" +import { Slot } from "@radix-ui/react-slot" +import { + Controller, + ControllerProps, + FieldPath, + FieldValues, + FormProvider, + useFormContext, +} from "react-hook-form" + +import { cn } from "@/lib/utils" +import { Label } from "@/shadcn/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 ( +