🛂 Migrate frontend to Shadcn (#2010)

* 🔧 Add Tailwind, update dependencies and config files

* ✨ Introduce new Shadcn components and remove old ones

* 🔧 Update dependencies

* Add new components.json file

* 🔥 Remove Chakra UI files

* 🔧 Add ThemeProvider component and integrate it into main

* 🔥 Remove common components

* Update primary color

* ✨ Add new components

* ✨ Add AuthLayout component

* 🔧 Add utility function cn

* 🔧 Refactor devtools integration and update dependencies

* ✨ Add Footer and Error components

* ♻️ Update Footer

* 🔥 Remove utils

* ♻️ Refactor error handling in useAuth

* ♻️ Refactor useCustomToast

* ♻️ Refactor Login component and form handling

* ♻️ Refactor SignUp component and form handling

* 🔧 Update dependencies

* ♻️ Refactor RecoverPassword component and form handling

* ♻️ Refactor ResetPassword and form handling

* ♻️ Add error component to root route

* ♻️ Refactor error handling in utils

* ♻️ Update buttons

* 🍱 Add icons and logos assets

* ♻️ Refactor Sidebar component

* 🎨 Format

* ♻️ Refactor ThemeProvider

* ♻️ Refactor Common components

* 🔥 Remove old Appearance component

* ✨ Add Sidebar components

* ♻️ Refactor DeleteAccount components

* ♻️ Refactor ChangePassword component

* ♻️ Refactor UserSettings

* ✨ Add TanStack table

* ♻️ Update SignUp

* ✨ Add Select component

* 🎨 Format

* ♻️ Update Footer

* ✨ Add useCopyToClipboard hook

* 🎨 Tweak table styles

* 🎨 Tweak styling

* ♻️ Refactor AddUser and AddItem components

* ♻️ Update DeleteConfirmation

* ✅ Update tests

* ✅ Update tests

* ✅ Fix tests

* ✨ Add DataTable for item and admin management

* ♻️ Refactor DeleteUser and DeleteItem components

* ✅ Fix tests

* ♻️ Refactor EditUser and EditItem components

* ♻️ Refactor UserInformation component

* 🎨 Format

* ♻️ Refactor pending components

* 🎨 Format

* ✅ Update tests

* ✅ Update tests

* ✅ Fix test

* ♻️ Minor tweaks

* ♻️ Update social media links
This commit is contained in:
Alejandra
2025-12-07 13:21:13 +01:00
committed by GitHub
parent 61b7cd673a
commit 8c2532a5c3
104 changed files with 8891 additions and 3287 deletions
+97 -96
View File
@@ -1,41 +1,49 @@
import {
Button,
DialogActionTrigger,
DialogTitle,
Input,
Text,
VStack,
} from "@chakra-ui/react"
import { zodResolver } from "@hookform/resolvers/zod"
import { useMutation, useQueryClient } from "@tanstack/react-query"
import { Plus } from "lucide-react"
import { useState } from "react"
import { type SubmitHandler, useForm } from "react-hook-form"
import { FaPlus } from "react-icons/fa"
import { useForm } from "react-hook-form"
import { z } from "zod"
import { type ItemCreate, ItemsService } from "@/client"
import type { ApiError } from "@/client/core/ApiError"
import useCustomToast from "@/hooks/useCustomToast"
import { handleError } from "@/utils"
import { Button } from "@/components/ui/button"
import {
DialogBody,
DialogCloseTrigger,
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogRoot,
DialogTitle,
DialogTrigger,
} from "../ui/dialog"
import { Field } from "../ui/field"
} from "@/components/ui/dialog"
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import { LoadingButton } from "@/components/ui/loading-button"
import useCustomToast from "@/hooks/useCustomToast"
import { handleError } from "@/utils"
const formSchema = z.object({
title: z.string().min(1, { message: "Title is required" }),
description: z.string().optional(),
})
type FormData = z.infer<typeof formSchema>
const AddItem = () => {
const [isOpen, setIsOpen] = useState(false)
const queryClient = useQueryClient()
const { showSuccessToast } = useCustomToast()
const {
register,
handleSubmit,
reset,
formState: { errors, isValid, isSubmitting },
} = useForm<ItemCreate>({
const { showSuccessToast, showErrorToast } = useCustomToast()
const form = useForm<FormData>({
resolver: zodResolver(formSchema),
mode: "onBlur",
criteriaMode: "all",
defaultValues: {
@@ -48,95 +56,88 @@ const AddItem = () => {
mutationFn: (data: ItemCreate) =>
ItemsService.createItem({ requestBody: data }),
onSuccess: () => {
showSuccessToast("Item created successfully.")
reset()
showSuccessToast("Item created successfully")
form.reset()
setIsOpen(false)
},
onError: (err: ApiError) => {
handleError(err)
},
onError: handleError.bind(showErrorToast),
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ["items"] })
},
})
const onSubmit: SubmitHandler<ItemCreate> = (data) => {
const onSubmit = (data: FormData) => {
mutation.mutate(data)
}
return (
<DialogRoot
size={{ base: "xs", md: "md" }}
placement="center"
open={isOpen}
onOpenChange={({ open }) => setIsOpen(open)}
>
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button value="add-item" my={4}>
<FaPlus fontSize="16px" />
<Button className="my-4">
<Plus className="mr-2" />
Add Item
</Button>
</DialogTrigger>
<DialogContent>
<form onSubmit={handleSubmit(onSubmit)}>
<DialogHeader>
<DialogTitle>Add Item</DialogTitle>
</DialogHeader>
<DialogBody>
<Text mb={4}>Fill in the details to add a new item.</Text>
<VStack gap={4}>
<Field
required
invalid={!!errors.title}
errorText={errors.title?.message}
label="Title"
>
<Input
{...register("title", {
required: "Title is required.",
})}
placeholder="Title"
type="text"
/>
</Field>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Add Item</DialogTitle>
<DialogDescription>
Fill in the details to add a new item.
</DialogDescription>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div className="grid gap-4 py-4">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>
Title <span className="text-destructive">*</span>
</FormLabel>
<FormControl>
<Input
placeholder="Title"
type="text"
{...field}
required
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Field
invalid={!!errors.description}
errorText={errors.description?.message}
label="Description"
>
<Input
{...register("description")}
placeholder="Description"
type="text"
/>
</Field>
</VStack>
</DialogBody>
<FormField
control={form.control}
name="description"
render={({ field }) => (
<FormItem>
<FormLabel>Description</FormLabel>
<FormControl>
<Input placeholder="Description" type="text" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<DialogFooter gap={2}>
<DialogActionTrigger asChild>
<Button
variant="subtle"
colorPalette="gray"
disabled={isSubmitting}
>
Cancel
</Button>
</DialogActionTrigger>
<Button
variant="solid"
type="submit"
disabled={!isValid}
loading={isSubmitting}
>
Save
</Button>
</DialogFooter>
</form>
<DialogCloseTrigger />
<DialogFooter>
<DialogClose asChild>
<Button variant="outline" disabled={mutation.isPending}>
Cancel
</Button>
</DialogClose>
<LoadingButton type="submit" loading={mutation.isPending}>
Save
</LoadingButton>
</DialogFooter>
</form>
</Form>
</DialogContent>
</DialogRoot>
</Dialog>
)
}