feat: add problem create page and components

This commit is contained in:
ngc2207 2024-11-03 20:33:58 +08:00
parent 6b71635ded
commit b41dd6be26
9 changed files with 295 additions and 7 deletions

View File

@ -0,0 +1,119 @@
"use client";
import { z } from "zod";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { useForm } from "react-hook-form";
import { toast } from "@/hooks/use-toast";
import { Input } from "@/components/ui/input";
import { zodResolver } from "@hookform/resolvers/zod";
const basicFormSchema = z.object({
title: z.string().min(1, { message: "题目标题不能为空" }),
difficulty: z.enum(["简单", "中等", "困难"], {
required_error: "请选择题目难度",
}),
});
type BasicFormValues = z.infer<typeof basicFormSchema>;
export function BasicForm() {
const form = useForm<BasicFormValues>({
resolver: zodResolver(basicFormSchema),
defaultValues: {
title: "",
difficulty: undefined,
},
});
function onSubmit(data: BasicFormValues) {
toast({
title: "题目基本信息创建成功",
description: (
<div>
<p>{data.title}</p>
<p>{data.difficulty}</p>
</div>
),
});
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel></FormLabel>
<FormControl>
<Input {...field} placeholder="例如: 两数之和" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="difficulty"
render={({ field }) => (
<FormItem>
<FormLabel></FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="点击此处选择" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="简单"></SelectItem>
<SelectItem value="中等"></SelectItem>
<SelectItem value="困难"></SelectItem>
</SelectContent>
</Select>
<FormMessage />
<Accordion type="single" collapsible>
<AccordionItem value="difficulty">
<AccordionTrigger></AccordionTrigger>
<AccordionContent>
<div className="flex flex-col">
<p>
</p>
<p>
</p>
<p>
</p>
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</FormItem>
)}
/>
</form>
</Form>
);
}

View File

@ -0,0 +1,61 @@
"use client";
import { z } from "zod";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { toast } from "@/hooks/use-toast";
import { useForm } from "react-hook-form";
import { Textarea } from "@/components/ui/textarea";
import { zodResolver } from "@hookform/resolvers/zod";
const detailsFormSchema = z.object({
description: z.string().min(1, { message: "题目描述不能为空" }),
});
type DetailsFormValues = z.infer<typeof detailsFormSchema>;
export function DeatilsForm() {
const form = useForm<DetailsFormValues>({
resolver: zodResolver(detailsFormSchema),
defaultValues: {
description: "",
},
});
function onSubmit(data: DetailsFormValues) {
toast({
title: "题目详细内容创建成功",
description: (
<div>
<p>{data.description}</p>
</div>
),
});
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="description"
render={({ field }) => (
<FormItem>
<FormLabel></FormLabel>
<FormControl>
<Textarea placeholder="描述题目要求即可" className="h-72"{...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</form>
</Form>
);
}

View File

@ -0,0 +1,43 @@
"use client";
import Link from "next/link";
import { cn } from "@/lib/utils";
import { usePathname } from "next/navigation";
import { buttonVariants } from "@/components/ui/button";
interface SidebarNavProps extends React.HTMLAttributes<HTMLElement> {
items: {
href: string;
title: string;
}[];
}
export function SidebarNav({ className, items, ...props }: SidebarNavProps) {
const pathname = usePathname();
return (
<nav
className={cn(
"flex space-x-2 lg:flex-col lg:space-x-0 lg:space-y-1",
className
)}
{...props}
>
{items.map((item) => (
<Link
key={item.href}
href={item.href}
className={cn(
buttonVariants({ variant: "ghost" }),
pathname === item.href
? "bg-muted hover:bg-muted"
: "hover:bg-transparent hover:underline",
"justify-start"
)}
>
{item.title}
</Link>
))}
</nav>
);
}

View File

@ -0,0 +1,17 @@
import { Separator } from "@/components/ui/separator";
import { DeatilsForm } from "../components/details-form";
export default function DetailsFormPage() {
return (
<div className="space-y-6">
<div>
<h3 className="text-lg font-medium"></h3>
<p className="text-sm text-muted-foreground">
</p>
</div>
<Separator />
<DeatilsForm />
</div>
);
}

View File

@ -0,0 +1,35 @@
import { Separator } from "@/components/ui/separator";
import { SidebarNav } from "./components/sidebar-nav";
const sidebarNavItems = [
{
title: "基本信息",
href: "/problems/new",
},
{
title: "详细内容",
href: "/problems/new/details",
},
];
interface SettingsLayoutProps {
children: React.ReactNode;
}
export default function ProblemCreateLayout({ children }: SettingsLayoutProps) {
return (
<div className="space-y-6 px-10 pt-5 pb-16">
<div className="space-y-0.5">
<h2 className="text-2xl font-bold tracking-tight"></h2>
<p className="text-muted-foreground"></p>
</div>
<Separator className="my-6" />
<div className="flex flex-col space-y-8 lg:flex-row lg:space-x-12 lg:space-y-0">
<aside className="-mx-4 lg:w-1/5">
<SidebarNav items={sidebarNavItems} />
</aside>
<div className="flex-1 lg:max-w-2xl">{children}</div>
</div>
</div>
);
}

View File

@ -0,0 +1,17 @@
import { BasicForm } from "./components/basic-form";
import { Separator } from "@/components/ui/separator";
export default function BasicFormPage() {
return (
<div className="space-y-6">
<div>
<h3 className="text-lg font-medium"></h3>
<p className="text-sm text-muted-foreground">
</p>
</div>
<Separator />
<BasicForm />
</div>
);
}

View File

@ -3,6 +3,7 @@ import { cn } from "@/lib/utils";
import type { Metadata } from "next"; import type { Metadata } from "next";
import { Inter } from "next/font/google"; import { Inter } from "next/font/google";
import Header from "@/components/header"; import Header from "@/components/header";
import { Toaster } from "@/components/ui/toaster";
import { AppSidebar } from "@/components/app-sidebar"; import { AppSidebar } from "@/components/app-sidebar";
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"; import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
@ -26,7 +27,8 @@ export default function RootLayout({
<AppSidebar /> <AppSidebar />
<SidebarInset> <SidebarInset>
<Header /> <Header />
{children} <main>{children}</main>
<Toaster />
</SidebarInset> </SidebarInset>
</SidebarProvider> </SidebarProvider>
</body> </body>

View File

@ -1,3 +0,0 @@
export default function ProblemCreatePage() {
return <div>Problem Create Page</div>;
}

View File

@ -1,3 +0,0 @@
export default function ProblemsetPage() {
return <div>Problemset Page</div>;
}