From 07cf870aa575477189718c67965c5b24cb174373 Mon Sep 17 00:00:00 2001 From: romashka-dev Date: Tue, 31 Dec 2024 11:45:21 +0200 Subject: [PATCH] [feat] Add spinner for pending pages + create basic structure of job form --- app/(dashboard)/add-job/page.tsx | 8 +- app/(dashboard)/jobs/page.tsx | 9 +- app/(dashboard)/stats/page.tsx | 9 +- components/ui/CreateJobForm/CreateJobForm.tsx | 62 ++++++ components/ui/CreateJobForm/index.tsx | 1 + components/ui/Spinner/Spinner.tsx | 14 ++ components/ui/Spinner/index.tsx | 1 + components/ui/form.tsx | 178 ++++++++++++++++++ components/ui/input.tsx | 22 +++ components/ui/label.tsx | 26 +++ package-lock.json | 63 ++++++- package.json | 6 +- utils/types.ts | 41 ++++ 13 files changed, 435 insertions(+), 5 deletions(-) create mode 100644 components/ui/CreateJobForm/CreateJobForm.tsx create mode 100644 components/ui/CreateJobForm/index.tsx create mode 100644 components/ui/Spinner/Spinner.tsx create mode 100644 components/ui/Spinner/index.tsx create mode 100644 components/ui/form.tsx create mode 100644 components/ui/input.tsx create mode 100644 components/ui/label.tsx create mode 100644 utils/types.ts diff --git a/app/(dashboard)/add-job/page.tsx b/app/(dashboard)/add-job/page.tsx index d3e94bc..5e90522 100644 --- a/app/(dashboard)/add-job/page.tsx +++ b/app/(dashboard)/add-job/page.tsx @@ -1,4 +1,10 @@ +import CreateJobForm from '@/components/ui/CreateJobForm' + const AddJobPage = () => { - return

Add Job Page

+ return ( +

+ +

+ ) } export default AddJobPage diff --git a/app/(dashboard)/jobs/page.tsx b/app/(dashboard)/jobs/page.tsx index 0d01e5f..bec4085 100644 --- a/app/(dashboard)/jobs/page.tsx +++ b/app/(dashboard)/jobs/page.tsx @@ -1,4 +1,11 @@ +import Spinner from '@/components/ui/Spinner' + const JobPage = () => { - return

Jobs Page

+ return ( +
+ +

Work in progress!

+
+ ) } export default JobPage diff --git a/app/(dashboard)/stats/page.tsx b/app/(dashboard)/stats/page.tsx index ff50a2d..f872a20 100644 --- a/app/(dashboard)/stats/page.tsx +++ b/app/(dashboard)/stats/page.tsx @@ -1,4 +1,11 @@ +import Spinner from '@/components/ui/Spinner' + const StatsPage = () => { - return

Stats Page

+ return ( +
+ +

Work in progress!

+
+ ) } export default StatsPage diff --git a/components/ui/CreateJobForm/CreateJobForm.tsx b/components/ui/CreateJobForm/CreateJobForm.tsx new file mode 100644 index 0000000..342ffd4 --- /dev/null +++ b/components/ui/CreateJobForm/CreateJobForm.tsx @@ -0,0 +1,62 @@ +'use client' + +import { zodResolver } from '@hookform/resolvers/zod' +import { useForm } from 'react-hook-form' +import * as z from 'zod' + +import { Button } from '@/components/ui/button' +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form' +import { Input } from '@/components/ui/input' + +const formSchema = z.object({ + username: z.string().min(2, { + message: 'Username must be at least 2 characters.', + }), +}) + +const CreateJobForm = () => { + // 1. Define your form. + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + username: '', + }, + }) + + // 2. Define a submit handler. + function onSubmit(values: z.infer) { + // Do something with the form values. + // ✅ This will be type-safe and validated. + console.log(values) + } + + return ( +
+ + ( + + Username + + + + + + )} + /> + + + + ) +} + +export default CreateJobForm diff --git a/components/ui/CreateJobForm/index.tsx b/components/ui/CreateJobForm/index.tsx new file mode 100644 index 0000000..99a7da2 --- /dev/null +++ b/components/ui/CreateJobForm/index.tsx @@ -0,0 +1 @@ +export { default } from './CreateJobForm' diff --git a/components/ui/Spinner/Spinner.tsx b/components/ui/Spinner/Spinner.tsx new file mode 100644 index 0000000..26a4ee7 --- /dev/null +++ b/components/ui/Spinner/Spinner.tsx @@ -0,0 +1,14 @@ +const Spinner = () => { + return ( +
+ + Loading... + +
+ ) +} + +export default Spinner diff --git a/components/ui/Spinner/index.tsx b/components/ui/Spinner/index.tsx new file mode 100644 index 0000000..0be0188 --- /dev/null +++ b/components/ui/Spinner/index.tsx @@ -0,0 +1 @@ +export { default } from './Spinner' diff --git a/components/ui/form.tsx b/components/ui/form.tsx new file mode 100644 index 0000000..ce264ae --- /dev/null +++ b/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 "@/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 ( +