nice category list, markdown

This commit is contained in:
2025-06-12 23:37:05 +02:00
parent 4f24ef7db0
commit 128269de1f
21 changed files with 664 additions and 509 deletions

View File

@@ -1,11 +1,12 @@
'use server'
import UpdateCvCategoryForm from "./UpdateForm";
import UpdateCvCategoryForm from "../_components/UpdateForm";
export default async function Page({params}:{params: Promise<{id:string}>}) {
console.log(params)
const {id} = await params;
return (
<UpdateCvCategoryForm params={{id:id}}/>
<UpdateCvCategoryForm id={id} className={undefined}/>
)
}

View File

@@ -0,0 +1,29 @@
import { ChevronsUpDown, Plus } from "lucide-react"
import { Button } from "~/components/ui/button";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "~/components/ui/collapsible";
import { type EntryRouterOutputs } from "~/server/routers/cv/entry";
import type { CategoryRouterOutputs } from "~/server/routers/cv/category";
import { type Element } from "~/lib/utils";
import UpdateCvCategoryForm from "./UpdateForm";
import CreateCvCategoryForm from "./CreateForm";
export default function CollapsibleCvCategoryForm(params:{category:Element<CategoryRouterOutputs['list']>|undefined}) {
return (
<Collapsible >
<CollapsibleTrigger asChild>
<Button variant={"ghost"}>
{ params.category ?
<>{params.category.name} <ChevronsUpDown/></> : <>New <Plus/></>
}
</Button>
</CollapsibleTrigger>
<CollapsibleContent className="autoAlpha">
{
params.category ?
<UpdateCvCategoryForm className="w-full" id={params.category.id} key={params.category.id} /> :
<CreateCvCategoryForm className="w-full"/>
}
</CollapsibleContent>
</Collapsible>
)
}

View File

@@ -0,0 +1,87 @@
'use client'
import { insertSchema } from "~/lib/schema/cv/category"
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'
export default function CreateCvCategoryForm(params:{className:string|undefined}) {
const form = useForm<z.infer<typeof insertSchema>>({
resolver: zodResolver(insertSchema),
defaultValues: {
id: crypto.randomUUID(),
layoutPosition: "col1"
}
})
const mutation = trpc.cv.category.create.useMutation()
function onSubmit(values: z.infer<typeof insertSchema>) {
mutation.mutate(values)
form.setValue("id",crypto.randomUUID())
}
return (
<Card.Card className={params.className ? params.className : "w-5/6 lg:w-1/2"}>
<Card.CardHeader>
<Card.CardTitle>
Create Category
</Card.CardTitle>
</Card.CardHeader>
<Card.CardContent>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-8"
>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>
Name
</FormLabel>
<FormControl>
<Input placeholder="name" onChange={field.onChange} value={field.value == null ? undefined : field.value} />
</FormControl>
</FormItem>
)}
>
</FormField>
<FormField
control={form.control}
name="layoutPosition"
render={({ field }) => (
<FormItem>
<FormLabel>
Layout Position
</FormLabel>
<FormControl>
<Select onValueChange={field.onChange} defaultValue={field.value == null ? undefined : field.value}>
<SelectTrigger>
<SelectValue placeholder={form.getValues().layoutPosition} />
</SelectTrigger>
<SelectContent>
{insertSchema.shape.layoutPosition.unwrap().unwrap().options.map((o) => (
<SelectItem key={o} value={o}> {o} </SelectItem>
))}
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
>
</FormField>
<Button type="submit"> Create </Button>
<FormMessage className={mutation.status == "success" ? "text-green-500" : "text-red-500"}>
{mutation.error ? mutation.error.message : mutation.status}
</FormMessage>
</form>
</Form>
</Card.CardContent>
</Card.Card>
)
}

View File

@@ -1,5 +1,5 @@
'use client'
import { insertSchema, updateRouteSchema, updateSchema } from "~/lib/schema/cv/category"
import { updateRouteSchema } from "~/lib/schema/cv/category"
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import { z } from "zod";
@@ -9,17 +9,19 @@ import { Select, SelectContent, SelectTrigger, SelectItem, SelectValue } from "~
import { trpc } from "~/app/_trpc/Client";
import { Button } from "~/components/ui/button";
import * as Card from '~/components/ui/card'
import { useRouter } from "next/navigation";
import { use } from "react";
export default function UpdateCvCategoryForm({params}:{params:{id:string}}) {
const id = params.id
import { useRouter, usePathname } from "next/navigation";
import { Delete } from "lucide-react";
import { cn } from "~/lib/utils";
export default function UpdateCvCategoryForm(params: { id: string, className: string | undefined }) {
const router = useRouter();
const pathname = usePathname();
const id = params.id;
console.log(id)
const category = trpc.cv.category.get.useQuery({id:id})
const category = trpc.cv.category.get.useQuery({ id: id })
const form = useForm<z.infer<typeof updateRouteSchema>>({
resolver: zodResolver(updateRouteSchema),
defaultValues: {
by: {id: id},
by: { id: id },
update: {
layoutPosition: category.data?.layoutPosition,
name: category.data?.layoutPosition
@@ -27,15 +29,30 @@ export default function UpdateCvCategoryForm({params}:{params:{id:string}}) {
}
})
category.promise.then((data) => {
form.setValue("update.layoutPosition",data?.layoutPosition)
form.setValue("update.name",data?.name)
form.setValue("update.layoutPosition", data?.layoutPosition)
form.setValue("update.name", data?.name)
})
const mutation = trpc.cv.category.update.useMutation({onSuccess: () => {
category.refetch()
}})
const updateMutation = trpc.cv.category.update.useMutation({
onSuccess: () => {
category.refetch()
}
})
const deleteMutation = trpc.cv.category.delete.useMutation({
onSuccess: () => {
if (pathname.includes('list')) {
router.refresh()
} else {
router.back()
}
}
})
const deleteCategory = (id: string) => {
console.log(`deleting ${id}`)
deleteMutation.mutate(id)
}
function onSubmit(values: z.infer<typeof updateRouteSchema>) {
mutation.mutate({
by: {id: id},
updateMutation.mutate({
by: { id: id },
update: {
layoutPosition: values.update.layoutPosition,
name: values.update.name
@@ -44,7 +61,7 @@ export default function UpdateCvCategoryForm({params}:{params:{id:string}}) {
}
if (category.data !== undefined) {
return (
<Card.Card className="w-5/6 lg:w-1/2">
<Card.Card className={params.className ? params.className : "w-5/6 lg:w-1/2"}>
<Card.CardHeader>
<Card.CardTitle>
Update Category
@@ -74,12 +91,12 @@ export default function UpdateCvCategoryForm({params}:{params:{id:string}}) {
<FormField
control={form.control}
name="by.id"
render={({field}) => (
render={({ field }) => (
<FormItem>
<FormControl>
<Input hidden onChange={field.onChange} value={field.value}/>
<Input hidden onChange={field.onChange} value={field.value} />
</FormControl>
</FormItem>
</FormItem>
)}
>
</FormField>
@@ -108,8 +125,13 @@ export default function UpdateCvCategoryForm({params}:{params:{id:string}}) {
>
</FormField>
<Button type="submit"> Update </Button>
<FormMessage className={mutation.status == "success" ? "text-green-500" : "text-red-500"}>
{mutation.error ? mutation.error.message : mutation.status}
<FormMessage className={cn(updateMutation.status == "success" ? "text-green-500" : "text-red-500", "flex flex-row justify-between")}>
{updateMutation.error ? updateMutation.error.message : updateMutation.status}
<Button
className="ml-auto cursor-pointer" variant="destructive" onClick={() => { deleteCategory(id) }}>
Delete
<Delete />
</Button>
</FormMessage>
</form>
</Form>

View File

@@ -1,87 +1,8 @@
'use client'
import { insertSchema } from "~/lib/schema/cv/category"
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 CreateCvCategoryForm from "../_components/CreateForm";
export default function CreateCvCategoryForm() {
const form = useForm<z.infer<typeof insertSchema>>({
resolver: zodResolver(insertSchema),
defaultValues: {
id: crypto.randomUUID(),
layoutPosition: "col1"
}
})
const mutation = trpc.cv.category.create.useMutation()
function onSubmit(values: z.infer<typeof insertSchema>) {
mutation.mutate(values)
form.setValue("id",crypto.randomUUID())
}
export default function Page() {
return (
<Card.Card className="w-5/6 lg:w-1/2">
<Card.CardHeader>
<Card.CardTitle>
Create Category
</Card.CardTitle>
</Card.CardHeader>
<Card.CardContent>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-8"
>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>
Name
</FormLabel>
<FormControl>
<Input placeholder="name" onChange={field.onChange} value={field.value == null ? undefined : field.value} />
</FormControl>
</FormItem>
)}
>
</FormField>
<FormField
control={form.control}
name="layoutPosition"
render={({ field }) => (
<FormItem>
<FormLabel>
Layout Position
</FormLabel>
<FormControl>
<Select onValueChange={field.onChange} defaultValue={field.value == null ? undefined : field.value}>
<SelectTrigger>
<SelectValue placeholder={form.getValues().layoutPosition} />
</SelectTrigger>
<SelectContent>
{insertSchema.shape.layoutPosition.unwrap().unwrap().options.map((o) => (
<SelectItem key={o} value={o}> {o} </SelectItem>
))}
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
>
</FormField>
<Button type="submit"> Create </Button>
<FormMessage className={mutation.status == "success" ? "text-green-500" : "text-red-500"}>
{mutation.error ? mutation.error.message : mutation.status}
</FormMessage>
</form>
</Form>
</Card.CardContent>
</Card.Card>
<CreateCvCategoryForm/>
)
}

View File

@@ -2,29 +2,18 @@
import Link from "next/link";
import { trpc } from "~/app/_trpc/Client";
import { useGSAP } from '@gsap/react'
import { useEffect, useRef } from "react";
import { useRef } from "react";
import * as Card from '~/components/ui/card'
import { useGsapContext } from "~/app/_providers/GsapProvicer";
import { Button } from "~/components/ui/button";
import { ChevronsUpDown, Delete } from 'lucide-react'
import UpdateCvEntryForm from "../../entry/[id]/UpdateForm";
import { CollapsibleContent, CollapsibleTrigger, Collapsible } from "~/components/ui/collapsible";
import CollapsibleCvEntryForm from "../../entry/_components/CollapsibleForm";
import UpdateCvCategoryForm from "../_components/UpdateForm";
import CollapsibleCvCategoryForm from "../_components/CollapsibleForm";
export default function CvPage() {
const cvCategories = trpc.cv.category.list.useQuery();
const cvCategories = trpc.cv.category.list.useQuery(undefined,{refetchInterval:1000});
const gsap = useGsapContext()
const container = useRef<HTMLDivElement>(null);
const mut = trpc.cv.category.delete.useMutation({
onSuccess: () => {
cvCategories.refetch()
}
})
useEffect(() => { console.log(cvCategories.data) }, [cvCategories.data])
const deleteCategory = (id: string) => {
console.log(`deleting ${id}`)
mut.mutate(id)
}
useGSAP(() => {
gsap?.from('.gsapan', { x: -100, opacity: 0, duration: 0.5, stagger: { each: 0.3 } })
gsap?.from('.gsapan', { x: -100, opacity: 0, duration: 0.5, stagger: { each: 0.3 } });
}, { scope: container, dependencies: [cvCategories.status], revertOnUpdate: true });
return (
<div ref={container} className="w-5/6 lg:w-1/2 flex flex-col gap-3">
@@ -44,8 +33,7 @@ export default function CvPage() {
</Link>
<Card.CardContent className="flex flex-row">
<div className="flex flex-col w-full">
<span>Category id : {cat.id}</span>
<span>Category Position : {cat.layoutPosition}</span>
<UpdateCvCategoryForm id={cat.id} className="w-full" />
<br />
<span>Entries:</span>
<div className="w-full">
@@ -53,30 +41,21 @@ export default function CvPage() {
cat.cvEntry.length > 0 ? (
<>
{cat.cvEntry.map((entry) => (
<Collapsible>
<CollapsibleTrigger>
<Button variant={"ghost"}>
{entry.title} <ChevronsUpDown/>
</Button>
</CollapsibleTrigger>
<CollapsibleContent>
<UpdateCvEntryForm params={{ id: entry.id, className: "w-full" }} key={entry.id} />
</CollapsibleContent>
</Collapsible>
<CollapsibleCvEntryForm key={entry.id} entry={entry} />
))}
</>
) : (<></>)
}
</div>
<div className="flex flex-col w-full">
<CollapsibleCvEntryForm entry={undefined} />
</div>
</div>
<Button
className="ml-auto cursor-pointer" variant="destructive" onClick={() => { deleteCategory(cat.id) }}>
<Delete />
</Button>
</Card.CardContent>
</Card.Card>
)
})}
<CollapsibleCvCategoryForm category={undefined} />
</>
}
</div>

View File

@@ -1,11 +1,12 @@
'use server'
import UpdateCvEntryForm from "./UpdateForm";
import UpdateCvEntryForm from "../_components/UpdateForm";
export default async function Page({params}:{params: Promise<{id:string}>}) {
console.log(params)
const {id} = await params;
return (
<UpdateCvEntryForm params={{id:id}}/>
<UpdateCvEntryForm params={{id:id, className: undefined}}/>
)
}

View File

@@ -0,0 +1,29 @@
import { ChevronsUpDown, Plus } from "lucide-react"
import { Button } from "~/components/ui/button";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "~/components/ui/collapsible";
import { type EntryRouterOutputs } from "~/server/routers/cv/entry";
import type { CategoryRouterOutputs } from "~/server/routers/cv/category";
import { type Element } from "~/lib/utils";
import UpdateCvEntryForm from "./UpdateForm";
import CreateCvEntryForm from "./CreateForm";
export default function CollapsibleCvEntryForm(params:{entry:Element<Element<CategoryRouterOutputs['list']>['cvEntry']>|EntryRouterOutputs['get']|Element<EntryRouterOutputs['list']>|undefined}) {
return (
<Collapsible>
<CollapsibleTrigger asChild>
<Button variant={"ghost"}>
{ params.entry ?
<>{params.entry.title} <ChevronsUpDown/></> : <>New <Plus/></>
}
</Button>
</CollapsibleTrigger>
<CollapsibleContent className="autoAlpha">
{
params.entry ?
<UpdateCvEntryForm params={{ id: params.entry.id, className: "w-full" }} key={params.entry.id} /> :
<CreateCvEntryForm className="w-full"/>
}
</CollapsibleContent>
</Collapsible>
)
}

View File

@@ -1,64 +1,51 @@
'use client'
import { updateRouteSchema, updateRouteSchemaCliennt } from "~/lib/schema/cv/entry"
import { insertSchema, insertSchemaForm } from "~/lib/schema/cv/entry"
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import { format } from 'date-fns'
import { z } from "zod";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "~/components/ui/form";
import { Form, FormControl, FormDescription, 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 { format } from 'date-fns'
import { Popover, PopoverContent, PopoverTrigger } from "~/components/ui/popover";
import { cn } from "~/lib/utils";
import { CalendarIcon } from "lucide-react";
import { Calendar } from "~/components/ui/calendar";
import { Textarea } from "~/components/ui/textarea";
export default function UpdateCvEntryForm({ params }: { params: { id: string, className: string|undefined } }) {
console.log(params)
const id = params.id
import { cn } from "~/lib/utils";
import { useState } from "react"
export default function CreateCvEntryForm(params:{className:string|undefined}) {
const categories = trpc.cv.category.list.useQuery()
const entry = trpc.cv.entry.get.useQuery({ id: id })
const form = useForm<z.infer<typeof updateRouteSchemaCliennt>>({
resolver: zodResolver(updateRouteSchemaCliennt),
const form = useForm<z.infer<typeof insertSchemaForm>>({
resolver: zodResolver(insertSchemaForm),
defaultValues: {
by: { id: id },
update: {
categoryId: entry.data?.categoryId,
title: entry.data?.title,
description: entry.data?.description,
}
id: crypto.randomUUID(),
title: "",
description: "",
categoryId: ""
}
})
entry.promise.then((v) => {
form.setValue('update.title',v?.title)
form.setValue('update.description',v?.description);
form.setValue('update.fromTime',new Date(Date.parse(v ? v.fromTime: "")))
form.setValue('update.toTime',new Date(Date.parse(v ? v.toTime: "")))
form.setValue('update.categoryId',v?.categoryId)
const mutation = trpc.cv.entry.create.useMutation({
onSuccess: (data) => { form.setValue("id", data[0] ? data[0].id : "") }
})
const mutation = trpc.cv.entry.update.useMutation({
onSuccess: () => {
entry.refetch()
}
})
function onSubmit(values: z.infer<typeof updateRouteSchemaCliennt>) {
let { title, categoryId, description } = values.update;
mutation.mutate({
by: { id: id },
update: {
fromTime: format(values.update.fromTime, 'yyyy-MM-dd'),
toTime: format(values.update.toTime, 'yyyy-MM-dd'),
title: title,
categoryId: categoryId,
description: description
}
})
function onSubmit(values: z.infer<typeof insertSchemaForm>) {
let { id, title, categoryId, description } = values
let v: z.infer<typeof insertSchema> = {
id: id,
categoryId: categoryId,
description: description,
title: title,
fromTime: format(values.fromTime,'yyyy-MM-dd'),
toTime: format(values.toTime,'yyyy-MM-dd'),
}
mutation.mutate(v)
form.setValue("id", crypto.randomUUID())
}
if (entry.data !== undefined) {
return (
<Card.Card className={params.className ? params.className : "w-5/6 lg:w-1/2"}>
return (
<Card.Card className={params.className ? params.className : "w-5/6 lg:w-1/2"}>
<Card.CardHeader>
<Card.CardTitle>
Create Entry
@@ -72,7 +59,7 @@ export default function UpdateCvEntryForm({ params }: { params: { id: string, cl
>
<FormField
control={form.control}
name="update.categoryId"
name="categoryId"
render={({ field }) => (
<FormItem>
<FormLabel>
@@ -83,11 +70,11 @@ export default function UpdateCvEntryForm({ params }: { params: { id: string, cl
<Select onValueChange={field.onChange} defaultValue={categories.data[0]?.id}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder={categories.data[0]?.name ? categories.data[0]?.name : "Select category"} />
<SelectValue placeholder={categories.data[0]?.name ? categories.data[0]?.name : "Select category" } />
</SelectTrigger>
</FormControl>
<SelectContent>
{categories.data?.map((c) => {
{ categories.data?.map((c) => {
return (<SelectItem value={c.id}> {c.name} </SelectItem>)
})}
</SelectContent>
@@ -99,7 +86,7 @@ export default function UpdateCvEntryForm({ params }: { params: { id: string, cl
/>
<FormField
control={form.control}
name="update.title"
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>
@@ -113,21 +100,21 @@ export default function UpdateCvEntryForm({ params }: { params: { id: string, cl
/>
<FormField
control={form.control}
name="update.description"
name="description"
render={({ field }) => (
<FormItem>
<FormLabel>
Description
</FormLabel>
<FormControl>
<Textarea placeholder="description" onChange={field.onChange} value={field.value == null ? undefined : field.value} />
<Input placeholder="description" onChange={field.onChange} value={field.value == null ? undefined : field.value} />
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="update.fromTime"
name="fromTime"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>From Date</FormLabel>
@@ -168,7 +155,7 @@ export default function UpdateCvEntryForm({ params }: { params: { id: string, cl
/>
<FormField
control={form.control}
name="update.toTime"
name="toTime"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>To Date</FormLabel>
@@ -207,7 +194,7 @@ export default function UpdateCvEntryForm({ params }: { params: { id: string, cl
</FormItem>
)}
/>
<Button type="submit"> Update </Button>
<Button type="submit"> Create </Button>
<FormMessage className={mutation.status == "success" ? "text-green-500" : "text-red-500"}>
{mutation.error ? mutation.error.message : mutation.status}
</FormMessage>
@@ -215,11 +202,5 @@ export default function UpdateCvEntryForm({ params }: { params: { id: string, cl
</Form>
</Card.CardContent>
</Card.Card>
)
} else {
return (
<>
</>
)
}
)
}

View File

@@ -0,0 +1,245 @@
'use client'
import { updateRouteSchema, updateRouteSchemaCliennt } from "~/lib/schema/cv/entry"
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 { format } from 'date-fns'
import { Popover, PopoverContent, PopoverTrigger } from "~/components/ui/popover";
import { cn } from "~/lib/utils";
import { CalendarIcon, Delete } from "lucide-react";
import { Calendar } from "~/components/ui/calendar";
import { Textarea } from "~/components/ui/textarea";
import { usePathname, useRouter } from "next/navigation";
export default function UpdateCvEntryForm({ params }: { params: { id: string, className: string | undefined } }) {
console.log(params)
const id = params.id
const categories = trpc.cv.category.list.useQuery()
const entry = trpc.cv.entry.get.useQuery({ id: id })
const router = useRouter();
const pathname = usePathname();
const form = useForm<z.infer<typeof updateRouteSchemaCliennt>>({
resolver: zodResolver(updateRouteSchemaCliennt),
defaultValues: {
by: { id: id },
update: {
categoryId: entry.data?.categoryId,
title: entry.data?.title,
description: entry.data?.description,
}
}
})
entry.promise.then((v) => {
form.setValue('update.title', v?.title)
form.setValue('update.description', v?.description);
form.setValue('update.fromTime', new Date(Date.parse(v ? v.fromTime : "")))
form.setValue('update.toTime', new Date(Date.parse(v ? v.toTime : "")))
form.setValue('update.categoryId', v?.categoryId)
})
const updateMutation = trpc.cv.entry.update.useMutation({
onSuccess: () => {
if (pathname.includes('list')) {
router.refresh()
} else {
router.back()
}
}
})
const deleteMutation = trpc.cv.entry.delete.useMutation({
onSuccess: () => {
}
})
const deleteEntry = (id: string) => {
deleteMutation.mutate(id)
}
function onSubmit(values: z.infer<typeof updateRouteSchemaCliennt>) {
let { title, categoryId, description } = values.update;
updateMutation.mutate({
by: { id: id },
update: {
fromTime: format(values.update.fromTime, 'yyyy-MM-dd'),
toTime: format(values.update.toTime, 'yyyy-MM-dd'),
title: title,
categoryId: categoryId,
description: description
}
})
}
if (entry.data !== undefined) {
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="update.categoryId"
render={({ field }) => (
<FormItem>
<FormLabel>
Category
</FormLabel>
{
categories.data ? (
<Select onValueChange={field.onChange} defaultValue={categories.data[0]?.id}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder={categories.data[0]?.name ? categories.data[0]?.name : "Select category"} />
</SelectTrigger>
</FormControl>
<SelectContent>
{categories.data?.map((c) => {
return (<SelectItem key={c.id} value={c.id}> {c.name} </SelectItem>)
})}
</SelectContent>
</Select>
) : <Select></Select>
}
</FormItem>
)}
/>
<FormField
control={form.control}
name="update.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="update.description"
render={({ field }) => (
<FormItem>
<FormLabel>
Description
</FormLabel>
<FormControl>
<Textarea placeholder="description" onChange={field.onChange} value={field.value == null ? undefined : field.value} />
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="update.fromTime"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>From Date</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant={"outline"}
className={cn(
"w-[240px] pl-3 text-left font-normal",
!field.value && "text-muted-foreground"
)}
>
{field.value ? (
format(field.value, "PPP")
) : (
<span>Pick a date</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={field.value}
onSelect={field.onChange}
disabled={(date) =>
date > new Date() || date < new Date("1900-01-01")
}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="update.toTime"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>To Date</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant={"outline"}
className={cn(
"w-[240px] pl-3 text-left font-normal",
!field.value && "text-muted-foreground"
)}
>
{field.value ? (
format(field.value, "PPP")
) : (
<span>Pick a date</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={field.value}
onSelect={field.onChange}
disabled={(date) =>
date > new Date() || date < new Date("1900-01-01")
}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit"> Update </Button>
<FormMessage className={cn(updateMutation.status == "success" ? "text-green-500" : "text-red-500", "flex flex-row justify-between")}>
{updateMutation.error ? updateMutation.error.message : updateMutation.status}
<Button
className="ml-auto cursor-pointer" variant="destructive" onClick={() => { deleteEntry(id) }}>
Delete
<Delete />
</Button>
</FormMessage>
</form>
</Form>
</Card.CardContent>
</Card.Card>
)
} else {
return (
<>
</>
)
}
}

View File

@@ -1,206 +1,8 @@
'use client'
import { insertSchema, insertSchemaForm } from "~/lib/schema/cv/entry"
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import { format } from 'date-fns'
import { z } from "zod";
import { Form, FormControl, FormDescription, 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 { Popover, PopoverContent, PopoverTrigger } from "~/components/ui/popover";
import { CalendarIcon } from "lucide-react";
import { Calendar } from "~/components/ui/calendar";
import { cn } from "~/lib/utils";
import { useState } from "react"
import CreateCvEntryForm from "../_components/CreateForm"
export default function CreateCvEntryForm() {
const categories = trpc.cv.category.list.useQuery()
const form = useForm<z.infer<typeof insertSchemaForm>>({
resolver: zodResolver(insertSchemaForm),
defaultValues: {
id: crypto.randomUUID(),
title: "",
description: "",
categoryId: ""
}
})
const mutation = trpc.cv.entry.create.useMutation({
onSuccess: (data) => { form.setValue("id", data[0] ? data[0].id : "") }
})
function onSubmit(values: z.infer<typeof insertSchemaForm>) {
let { id, title, categoryId, description } = values
let v: z.infer<typeof insertSchema> = {
id: id,
categoryId: categoryId,
description: description,
title: title,
fromTime: format(values.fromTime,'yyyy-MM-dd'),
toTime: format(values.toTime,'yyyy-MM-dd'),
}
mutation.mutate(v)
form.setValue("id", crypto.randomUUID())
}
export default function Page() {
return (
<Card.Card 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="categoryId"
render={({ field }) => (
<FormItem>
<FormLabel>
Category
</FormLabel>
{
categories.data ? (
<Select onValueChange={field.onChange} defaultValue={categories.data[0]?.id}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder={categories.data[0]?.name ? categories.data[0]?.name : "Select category" } />
</SelectTrigger>
</FormControl>
<SelectContent>
{ categories.data?.map((c) => {
return (<SelectItem value={c.id}> {c.name} </SelectItem>)
})}
</SelectContent>
</Select>
) : <Select></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="description"
render={({ field }) => (
<FormItem>
<FormLabel>
Description
</FormLabel>
<FormControl>
<Input placeholder="description" onChange={field.onChange} value={field.value == null ? undefined : field.value} />
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="fromTime"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>From Date</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant={"outline"}
className={cn(
"w-[240px] pl-3 text-left font-normal",
!field.value && "text-muted-foreground"
)}
>
{field.value ? (
format(field.value, "PPP")
) : (
<span>Pick a date</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={field.value}
onSelect={field.onChange}
disabled={(date) =>
date > new Date() || date < new Date("1900-01-01")
}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="toTime"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>To Date</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant={"outline"}
className={cn(
"w-[240px] pl-3 text-left font-normal",
!field.value && "text-muted-foreground"
)}
>
{field.value ? (
format(field.value, "PPP")
) : (
<span>Pick a date</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={field.value}
onSelect={field.onChange}
disabled={(date) =>
date > new Date() || date < new Date("1900-01-01")
}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit"> Create </Button>
<FormMessage className={mutation.status == "success" ? "text-green-500" : "text-red-500"}>
{mutation.error ? mutation.error.message : mutation.status}
</FormMessage>
</form>
</Form>
</Card.CardContent>
</Card.Card>
<CreateCvEntryForm/>
)
}

View File

@@ -5,20 +5,11 @@ import { useGSAP } from '@gsap/react'
import { useRef } from "react";
import * as Card from '~/components/ui/card'
import { useGsapContext } from "~/app/_providers/GsapProvicer";
import { Button } from "~/components/ui/button";
import { Delete } from 'lucide-react'
export default function CvPage() {
const cvEntries = trpc.cv.entry.list.useQuery();
const gsap = useGsapContext()
const container = useRef<HTMLDivElement>(null);
const mut = trpc.cv.entry.delete.useMutation({onSuccess: () => {
console.log('success')
cvEntries.refetch()
}})
const deleteEntry = (id: string) => {
console.log(`deleting ${id}`)
mut.mutate(id)
}
useGSAP(() => {
gsap?.from('.gsapan', { x: -100, opacity: 0, duration: 0.5, stagger: { each: 0.3 } })
}, { scope: container, dependencies: [cvEntries.status], revertOnUpdate: true });
@@ -34,20 +25,16 @@ export default function CvPage() {
<Link href={`/admin/cv/entry/${ent.id}`}>
<Card.CardHeader>
<Card.CardTitle>
Entry Name: { ent.title }
Entry Name: {ent.title}
</Card.CardTitle>
</Card.CardHeader>
</Link>
<Card.CardContent className="flex flex-row">
<div>
Category id : {ent.id} <br />
Category Name :
<Link href={`/admin/cv/entry/${ent.categoryId}`}> {ent.category?.name} </Link>
Category id : {ent.id} <br />
Category Name :
<Link href={`/admin/cv/entry/${ent.categoryId}`}> {ent.category?.name} </Link>
</div>
<Button
className="ml-auto cursor-pointer" variant="destructive" onClick={() => { deleteEntry(ent.id) }}>
<Delete />
</Button>
</Card.CardContent>
</Card.Card>
)