project forms

This commit is contained in:
2025-07-03 04:10:38 +02:00
parent 3195aaae81
commit b58024b66a
8 changed files with 982 additions and 763 deletions

View File

@@ -45,7 +45,7 @@ export default async function AdminSideBar() {
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton asChild>
<Link href={"/"}> Some Project Action </Link>
<Link href={"/admin/project/create"}> Create Project </Link>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>

View File

@@ -0,0 +1,27 @@
import { ChevronsUpDown, Plus } from "lucide-react"
import { Button } from "~/components/ui/button";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "~/components/ui/collapsible";
import { type Element } from "~/lib/utils";
import CreateUpdateProjectForm from "./CreateForm";
import type { ProjectRouterOutputs } from "~/server/routers/project";
export default function CollapsibleForm(params:{project:Element<ProjectRouterOutputs['list']>|undefined}) {
return (
<Collapsible >
<CollapsibleTrigger asChild>
<Button variant={"ghost"}>
{ params.project ?
<>{params.project.title} <ChevronsUpDown/></> : <>New <Plus/></>
}
</Button>
</CollapsibleTrigger>
<CollapsibleContent className="autoAlpha">
{
params.project ?
<CreateUpdateProjectForm className="w-full" project={params.project}/> :
<CreateUpdateProjectForm className="w-full" project={params.project}/>
}
</CollapsibleContent>
</Collapsible>
)
}

View File

@@ -0,0 +1,153 @@
"use client"
import { insertSchema } from "~/lib/schema/project/project"
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import { z } from "zod";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "~/components/ui/form";
import { Input } from "~/components/ui/input";
import { Select, SelectContent, SelectTrigger, SelectItem, SelectValue } from "~/components/ui/select";
import { trpc } from "~/app/_trpc/Client";
import { Button } from "~/components/ui/button";
import * as Card from '~/components/ui/card'
import type { Entries } from 'type-fest'
import { type Element } from "~/lib/utils";
import type { ProjectRouterOutputs } from "~/server/routers/project";
import { Suspense } from "react";
export default function CreateUpdateProjectForm(params:{className:string|undefined, project:Element<ProjectRouterOutputs['list']>|undefined}) {
const [techStacks,] = trpc.project.stack.list.useSuspenseQuery()
const form = useForm<z.infer<typeof insertSchema>>({
resolver: zodResolver(insertSchema),
defaultValues: {
id: params.project ? params.project.id : crypto.randomUUID(),
}
})
const createMutation = trpc.project.create.useMutation({
onSuccess: (data) => { form.setValue("id", data[0] ? data[0].id : "") }
})
const updateMutation = trpc.project.update.useMutation({
onSuccess: (data) => {
if (null !== data) {
let entries = Object.entries(data) as Entries<typeof data>
entries.forEach( (entry) => {
form.setValue(entry[0],entry[1])
})
}
}
})
function onSubmit(values: z.infer<typeof insertSchema>) {
params.project ?
updateMutation.mutate({by: {id: values.id}, update: { ...values}}) :
createMutation.mutate(values)
}
return (
<Card.Card className={params.className ? params.className : "w-5/6 lg:w-1/2"}>
<Card.CardHeader>
<Card.CardTitle>
Create Entry
</Card.CardTitle>
</Card.CardHeader>
<Card.CardContent>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-8"
>
<FormField
control={form.control}
name="stackId"
render={({ field }) => (
<FormItem>
<FormLabel>
Stack
</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value ? field.value : ""}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder={field.value ? field.value : ""} />
</SelectTrigger>
</FormControl>
<SelectContent>
<Suspense fallback={(<> </>)}>
{techStacks.map((stack) => {
return (<SelectItem key={stack.id} value={stack.id}> { stack.stackItems ? stack.stackItems.join("-") : "Empty Stack" } </SelectItem>)
})}
</Suspense>
</SelectContent>
</Select>
</FormItem>
)}
/>
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>
Title
</FormLabel>
<FormControl>
<Input placeholder="title" onChange={field.onChange} value={field.value == null ? undefined : field.value} />
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="sourceType"
render={({ field }) => (
<FormItem>
<FormLabel>
Source Type
</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value ? field.value : "open"}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder={field.value} />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="open"> open </SelectItem>
<SelectItem value="closed"> closed </SelectItem>
</SelectContent>
</Select>
</FormItem>
)}
/>
<FormField
control={form.control}
name="releaseStatus"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Release Status</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value ? field.value : "released"}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder={field.value} />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="released"> released </SelectItem>
<SelectItem value="unreleased"> unreleased </SelectItem>
</SelectContent>
</Select>
</FormItem>
)}
/>
<Button type="submit"> Create </Button>
<FormMessage className={updateMutation.status == "success" || createMutation.status == "success" ? "text-green-500" : "text-red-500"}>
{ params.project ?
(
<>{updateMutation.error ? updateMutation.error.message : updateMutation.status}</>
) :
(
<>{createMutation.error ? createMutation.error.message : createMutation.status}</>
)
}
</FormMessage>
</form>
</Form>
</Card.CardContent>
</Card.Card>
)
}

View File

@@ -0,0 +1,7 @@
import CreateUpdateProjectForm from "../_components/CreateForm";
export default function Page() {
return (
<CreateUpdateProjectForm className="" project={undefined}/>
)
}