From 40cc485be4944f339a28952e00195de12af6ad7f Mon Sep 17 00:00:00 2001 From: Vincent Date: Sat, 25 Jul 2026 22:54:03 -0400 Subject: [PATCH 1/3] create subject page --- src/pages/subjects/create.tsx | 252 ++++++++++++++++++++++++++++++++-- 1 file changed, 244 insertions(+), 8 deletions(-) diff --git a/src/pages/subjects/create.tsx b/src/pages/subjects/create.tsx index ee75db8..e5341a4 100644 --- a/src/pages/subjects/create.tsx +++ b/src/pages/subjects/create.tsx @@ -1,16 +1,252 @@ -import React from 'react' -import { RequireRole } from '@/components/require-role'; +import React from "react"; +import { CreateView } from "@/components/refine-ui/views/create-view"; +import { Breadcrumb } from "@/components/refine-ui/layout/breadcrumb"; +import { Button } from "@/components/ui/button"; +import { useBack, type HttpError } from "@refinedev/core"; +import { useForm } from "@refinedev/react-hook-form"; +import { Separator } from "@/components/ui/separator"; +import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; +import { Controller } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { subjectSchema } from "@/lib/schema"; +import * as z from "zod"; +import { toast } from "sonner"; +import { + FieldGroup, + FieldLabel, + FieldError, + FieldDescription, + Field, +} from "@/components/ui/field"; +import { + InputGroup, + InputGroupTextarea, + InputGroupAddon, + InputGroupText, +} from "@/components/ui/input-group"; +import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { RequireRole } from "@/components/require-role"; +import { DEPARTMENT_OPTIONS } from "@/constants"; + +type CreateSubjectFormOutput = z.infer; + +const Create = () => { + const back = useBack(); + const { + refineCore: { onFinish, formLoading }, + ...form + } = useForm({ + resolver: zodResolver(subjectSchema), + defaultValues: { + name: "", + code: "", + description: "", + department: "", + }, + refineCoreProps: { + resource: "subjects", + action: "create", + onMutationSuccess: () => { + toast.success("Subject created successfully"); + back(); + }, + onMutationError: (error) => { + toast.error(error.message ?? "Failed to create subject"); + }, + }, + }); + + function onSubmit(data: CreateSubjectFormOutput) { + onFinish(data); + } -const SubjectsCreate =()=>{ return ( -
create
- ) -} + + +

Create Subject

+
+

Provide the details of the subject to be created.

+
+ +
+ + + + Fill in the details to create a new subject + + + +
+ + ( + + + Subject name + + {fieldState.error ? "*" : null} + + + + {fieldState.invalid && ( + + )} + + )} + /> + +
+ ( + + + Code + + {fieldState.error ? "*" : null} + + + + {fieldState.invalid && ( + + )} + + )} + /> + + ( + + + Department + + {fieldState.error ? "*" : null} + + + + {fieldState.invalid && ( + + )} + + )} + /> +
+ + ( + + + Description + + {fieldState.error ? "*" : null} + + + + + + + {field.value.length}/255 characters + + + + + Include topics, level, and how this subject is used in + the curriculum. + + {fieldState.invalid && ( + + )} + + )} + /> +
+
+ + +
+
+
+
+ ); +}; const SubjectsCreatePage = () => ( - + ); -export default SubjectsCreatePage \ No newline at end of file +export default SubjectsCreatePage; From cc5dc716f74cf961257b64b409dcf52b1d2438b7 Mon Sep 17 00:00:00 2001 From: Vincent Date: Sat, 25 Jul 2026 23:59:41 -0400 Subject: [PATCH 2/3] pass department id to backend --- src/App.tsx | 4 ++++ src/lib/schema.ts | 12 ++++++++---- src/pages/subjects/create.tsx | 37 +++++++++++++++++++++++++---------- src/types/index.ts | 16 ++++++++------- 4 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index ccd76fa..6b94855 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -72,6 +72,10 @@ function App() { create:"/subjects/create", meta:{label:"Subjects", icon:} }, + { + name: "departments", + meta: { hide: true }, + }, { name: "classes", list:"/classes", diff --git a/src/lib/schema.ts b/src/lib/schema.ts index 0be7ca7..acd6df8 100644 --- a/src/lib/schema.ts +++ b/src/lib/schema.ts @@ -16,10 +16,14 @@ export const subjectSchema = z.object({ code: z.string().min(5, "Subject code must be at least 5 characters"), description: z .string() - .min(5, "Subject description must be at least 5 characters"), - department: z - .string() - .min(2, "Subject department must be at least 2 characters"), + .min(5, "Subject description must be at least 5 characters") + .max(255, "Subject description must be at most 255 characters"), + departmentId: z.coerce + .number({ + required_error: "Department is required", + invalid_type_error: "Department is required", + }) + .min(1, "Department is required"), }); const scheduleSchema = z.object({ diff --git a/src/pages/subjects/create.tsx b/src/pages/subjects/create.tsx index e5341a4..6578e23 100644 --- a/src/pages/subjects/create.tsx +++ b/src/pages/subjects/create.tsx @@ -2,7 +2,7 @@ import React from "react"; import { CreateView } from "@/components/refine-ui/views/create-view"; import { Breadcrumb } from "@/components/refine-ui/layout/breadcrumb"; import { Button } from "@/components/ui/button"; -import { useBack, type HttpError } from "@refinedev/core"; +import { useBack, useList, type HttpError } from "@refinedev/core"; import { useForm } from "@refinedev/react-hook-form"; import { Separator } from "@/components/ui/separator"; import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; @@ -33,7 +33,7 @@ import { SelectValue, } from "@/components/ui/select"; import { RequireRole } from "@/components/require-role"; -import { DEPARTMENT_OPTIONS } from "@/constants"; +import type { Department } from "@/types"; type CreateSubjectFormOutput = z.infer; @@ -48,7 +48,7 @@ const Create = () => { name: "", code: "", description: "", - department: "", + departmentId: undefined, }, refineCoreProps: { resource: "subjects", @@ -63,6 +63,13 @@ const Create = () => { }, }); + const { query: DepartmentsQuery } = useList({ + resource: "departments", + pagination: { pageSize: 100 }, + }); + const departments = (DepartmentsQuery.data?.data ?? []) as Department[]; + const departmentIsLoading = DepartmentsQuery.isLoading; + function onSubmit(data: CreateSubjectFormOutput) { onFinish(data); } @@ -143,8 +150,9 @@ const Create = () => { /> ( {