diff --git a/src/components/features/dashboard/admin/problemset/new/components/description-form.tsx b/src/components/features/dashboard/admin/problemset/new/components/description-form.tsx new file mode 100644 index 0000000..1de34b0 --- /dev/null +++ b/src/components/features/dashboard/admin/problemset/new/components/description-form.tsx @@ -0,0 +1,142 @@ +"use client"; + +import { z } from "zod"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { useRouter } from "next/navigation"; +import DockView from "@/components/dockview"; +import { ArrowRightIcon } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import MdxPreview from "@/components/mdx-preview"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import MarkdownEditor from "@/components/markdown-editor"; +import { useNewProblemStore } from "@/app/(app)/dashboard/@admin/problemset/new/store"; +import { problemSchema } from "@/components/features/dashboard/admin/problemset/new/schema"; +import { newProblemMetadataSchema } from "@/components/features/dashboard/admin/problemset/new/components/metadata-form"; + +export const newProblemDescriptionSchema = problemSchema.pick({ + description: true, +}); + +type NewProblemDescriptionSchema = z.infer< + typeof newProblemDescriptionSchema +>; + +export default function NewProblemDescriptionForm() { + const { + hydrated, + displayId, + title, + difficulty, + published, + description, + setData, + } = useNewProblemStore(); + const router = useRouter(); + + const form = useForm({ + resolver: zodResolver(newProblemDescriptionSchema), + defaultValues: { + description: description || "", + }, + }); + + const onSubmit = (data: NewProblemDescriptionSchema) => { + setData(data); + router.push("/dashboard/problemset/new/solution"); + }; + + useEffect(() => { + if (!hydrated) return; + + try { + newProblemMetadataSchema.parse({ + displayId, + title, + difficulty, + published, + }); + } catch { + router.push("/dashboard/problemset/new/metadata"); + } + }, [difficulty, displayId, hydrated, published, router, title]); + + return ( +
+ + ( + + + Description + + + + +
+ + + +
+ + ), + }, + { + id: "MarkdownEditor", + title: "Markdown Editor", + component: "MarkdownEditor", + tabComponent: "MarkdownEditor", + icon: "FileTextIcon", + node: ( + + ), + position: { referencePanel: "MdxPreview", direction: "right" }, + }, + ]} + /> +
+ + This is your problem description. + + +
+ )} + /> + + + ); +} diff --git a/src/components/features/dashboard/admin/problemset/new/components/metadata-form.tsx b/src/components/features/dashboard/admin/problemset/new/components/metadata-form.tsx new file mode 100644 index 0000000..cda3656 --- /dev/null +++ b/src/components/features/dashboard/admin/problemset/new/components/metadata-form.tsx @@ -0,0 +1,156 @@ +"use client"; + +import { z } from "zod"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { useForm } from "react-hook-form"; +import { useRouter } from "next/navigation"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { Difficulty } from "@/generated/client"; +import { Switch } from "@/components/ui/switch"; +import { getDifficultyColorClass } from "@/lib/utils"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useNewProblemStore } from "@/app/(app)/dashboard/@admin/problemset/new/store"; +import { problemSchema } from "@/components/features/dashboard/admin/problemset/new/schema"; + +export const newProblemMetadataSchema = problemSchema.pick({ + displayId: true, + title: true, + difficulty: true, + published: true, +}); + +type NewProblemMetadataSchema = z.infer; + +export default function NewProblemMetadataForm() { + const router = useRouter(); + const { displayId, title, difficulty, published, setData } = useNewProblemStore(); + + const form = useForm({ + resolver: zodResolver(newProblemMetadataSchema), + defaultValues: { + // displayId must be a number and cannot be an empty string ("") + // so set it to undefined here and convert it to "" in the Input component. + displayId: displayId || undefined, + title: title || "", + difficulty: difficulty || "EASY", + published: published || false, + }, + }); + + const onSubmit = (data: NewProblemMetadataSchema) => { + setData(data); + router.push("/dashboard/problemset/new/description"); + }; + + return ( +
+ + ( + + Display ID + + + + + Unique numeric identifier visible to users + + + + )} + /> + + ( + + Problem Title + + + + + Descriptive title summarizing the problem + + + + )} + /> + + ( + + Difficulty Level + + + Categorize problem complexity for better filtering + + + + )} + /> + + ( + +
+ Publish Status + + Make problem visible in public listings + +
+ + + +
+ )} + /> + + + + + ); +} diff --git a/src/components/features/dashboard/admin/problemset/new/components/solution-form.tsx b/src/components/features/dashboard/admin/problemset/new/components/solution-form.tsx new file mode 100644 index 0000000..eadc428 --- /dev/null +++ b/src/components/features/dashboard/admin/problemset/new/components/solution-form.tsx @@ -0,0 +1,107 @@ +"use client"; + +import { z } from "zod"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { useRouter } from "next/navigation"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useNewProblemStore } from "@/app/(app)/dashboard/@admin/problemset/new/store"; +import { problemSchema } from "@/components/features/dashboard/admin/problemset/new/schema"; +import { newProblemMetadataSchema } from "@/components/features/dashboard/admin/problemset/new/components/metadata-form"; +import { newProblemDescriptionSchema } from "@/components/features/dashboard/admin/problemset/new/components/description-form"; + +const newProblemSolutionSchema = problemSchema.pick({ + solution: true, +}); + +type NewProblemSolutionSchema = z.infer; + +export default function NewProblemSolutionForm() { + const { + hydrated, + displayId, + title, + difficulty, + published, + description, + solution, + } = useNewProblemStore(); + const router = useRouter(); + + const form = useForm({ + resolver: zodResolver(newProblemSolutionSchema), + defaultValues: { + solution: solution || "", + }, + }); + + const onSubmit = (data: NewProblemSolutionSchema) => { + console.log({ + ...data, + displayId, + title, + difficulty, + published, + description, + }); + }; + + useEffect(() => { + if (!hydrated) return; + + try { + newProblemMetadataSchema.parse({ + displayId, + title, + difficulty, + published, + }); + } catch { + router.push("/dashboard/problemset/new/metadata"); + } + + try { + newProblemDescriptionSchema.parse({ description }); + } catch { + router.push("/dashboard/problemset/new/description"); + } + }, [hydrated, displayId, title, difficulty, published, description, router]); + + return ( +
+ + ( + + Solution + + + + + This is your problem solution. + + + + )} + /> + + + + ); +}