From 128269de1f024e93c2563c0783cdf5557b59d82f Mon Sep 17 00:00:00 2001 From: Gregor Lohaus Date: Thu, 12 Jun 2025 23:37:05 +0200 Subject: [PATCH] nice category list, markdown --- components.json | 2 +- package.json | 1 + pnpm-lock.yaml | 20 ++ src/app/admin/cv/category/[id]/page.tsx | 5 +- .../category/_components/CollapsibleForm.tsx | 29 +++ .../cv/category/_components/CreateForm.tsx | 87 +++++++ .../{[id] => _components}/UpdateForm.tsx | 64 +++-- src/app/admin/cv/category/create/page.tsx | 85 +----- src/app/admin/cv/category/list/page.tsx | 45 +--- src/app/admin/cv/entry/[id]/page.tsx | 5 +- .../cv/entry/_components/CollapsibleForm.tsx | 29 +++ .../CreateForm.tsx} | 99 +++---- .../admin/cv/entry/_components/UpdateForm.tsx | 245 ++++++++++++++++++ src/app/admin/cv/entry/create/page.tsx | 204 +-------------- src/app/admin/cv/entry/list/page.tsx | 23 +- src/app/cv/_components/CvCategory.tsx | 3 + src/app/cv/_components/CvEntry.tsx | 56 +++- src/app/cv/page.tsx | 39 ++- src/server/routers/cv/category.ts | 3 +- src/server/routers/cv/entry.ts | 3 +- src/styles/globals.css | 126 ++++----- 21 files changed, 664 insertions(+), 509 deletions(-) create mode 100644 src/app/admin/cv/category/_components/CollapsibleForm.tsx create mode 100644 src/app/admin/cv/category/_components/CreateForm.tsx rename src/app/admin/cv/category/{[id] => _components}/UpdateForm.tsx (67%) create mode 100644 src/app/admin/cv/entry/_components/CollapsibleForm.tsx rename src/app/admin/cv/entry/{[id]/UpdateForm.tsx => _components/CreateForm.tsx} (73%) create mode 100644 src/app/admin/cv/entry/_components/UpdateForm.tsx diff --git a/components.json b/components.json index 3938922..63ce401 100644 --- a/components.json +++ b/components.json @@ -6,7 +6,7 @@ "tailwind": { "config": "", "css": "src/styles/globals.css", - "baseColor": "slate", + "baseColor": "zinc", "cssVariables": true, "prefix": "" }, diff --git a/package.json b/package.json index 47ce23f..bfa2008 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "gsap": "^3.13.0", "input-otp": "^1.4.2", "lucide-react": "^0.503.0", + "marked-react": "^3.0.0", "next": "15.4.0-canary.17", "next-themes": "^0.4.6", "postgres": "^3.4.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d7f3c91..9615619 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -161,6 +161,9 @@ importers: lucide-react: specifier: ^0.503.0 version: 0.503.0(react@19.1.0) + marked-react: + specifier: ^3.0.0 + version: 3.0.0(react@19.1.0) next: specifier: 15.4.0-canary.17 version: 15.4.0-canary.17(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -2100,6 +2103,16 @@ packages: resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} engines: {node: '>=8'} + marked-react@3.0.0: + resolution: {integrity: sha512-We5IPtdfarVIqypy4LmQ3O8xe9Hk8EBNs4MRUYg5077mbD7GJ+rmV9XAPJ0XK8LfQkCOwGv4I7niXWLXWywyoQ==} + peerDependencies: + react: ^16.8.0 || >=17.0.0 + + marked@15.0.12: + resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} + engines: {node: '>= 18'} + hasBin: true + minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} @@ -4026,6 +4039,13 @@ snapshots: map-obj@4.3.0: {} + marked-react@3.0.0(react@19.1.0): + dependencies: + marked: 15.0.12 + react: 19.1.0 + + marked@15.0.12: {} + minipass@7.1.2: {} minizlib@3.0.2: diff --git a/src/app/admin/cv/category/[id]/page.tsx b/src/app/admin/cv/category/[id]/page.tsx index f7eb1e7..f3238b9 100644 --- a/src/app/admin/cv/category/[id]/page.tsx +++ b/src/app/admin/cv/category/[id]/page.tsx @@ -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 ( - + ) } diff --git a/src/app/admin/cv/category/_components/CollapsibleForm.tsx b/src/app/admin/cv/category/_components/CollapsibleForm.tsx new file mode 100644 index 0000000..9fe30a1 --- /dev/null +++ b/src/app/admin/cv/category/_components/CollapsibleForm.tsx @@ -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|undefined}) { + return ( + + + + + + { + params.category ? + : + + + } + + + ) +} diff --git a/src/app/admin/cv/category/_components/CreateForm.tsx b/src/app/admin/cv/category/_components/CreateForm.tsx new file mode 100644 index 0000000..84f7191 --- /dev/null +++ b/src/app/admin/cv/category/_components/CreateForm.tsx @@ -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>({ + resolver: zodResolver(insertSchema), + defaultValues: { + id: crypto.randomUUID(), + layoutPosition: "col1" + } + }) + const mutation = trpc.cv.category.create.useMutation() + function onSubmit(values: z.infer) { + mutation.mutate(values) + form.setValue("id",crypto.randomUUID()) + } + return ( + + + + Create Category + + + +
+ + ( + + + Name + + + + + + )} + > + + ( + + + Layout Position + + + + + + )} + > + + + + {mutation.error ? mutation.error.message : mutation.status} + +
+ +
+
+ ) +} diff --git a/src/app/admin/cv/category/[id]/UpdateForm.tsx b/src/app/admin/cv/category/_components/UpdateForm.tsx similarity index 67% rename from src/app/admin/cv/category/[id]/UpdateForm.tsx rename to src/app/admin/cv/category/_components/UpdateForm.tsx index d9e38eb..a629534 100644 --- a/src/app/admin/cv/category/[id]/UpdateForm.tsx +++ b/src/app/admin/cv/category/_components/UpdateForm.tsx @@ -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>({ 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) { - 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 ( - + Update Category @@ -74,12 +91,12 @@ export default function UpdateCvCategoryForm({params}:{params:{id:string}}) { ( + render={({ field }) => ( - + - + )} > @@ -108,8 +125,13 @@ export default function UpdateCvCategoryForm({params}:{params:{id:string}}) { > - - {mutation.error ? mutation.error.message : mutation.status} + + {updateMutation.error ? updateMutation.error.message : updateMutation.status} + diff --git a/src/app/admin/cv/category/create/page.tsx b/src/app/admin/cv/category/create/page.tsx index 1b572ea..2445baf 100644 --- a/src/app/admin/cv/category/create/page.tsx +++ b/src/app/admin/cv/category/create/page.tsx @@ -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>({ - resolver: zodResolver(insertSchema), - defaultValues: { - id: crypto.randomUUID(), - layoutPosition: "col1" - } - }) - const mutation = trpc.cv.category.create.useMutation() - function onSubmit(values: z.infer) { - mutation.mutate(values) - form.setValue("id",crypto.randomUUID()) - } +export default function Page() { return ( - - - - Create Category - - - -
- - ( - - - Name - - - - - - )} - > - - ( - - - Layout Position - - - - - - )} - > - - - - {mutation.error ? mutation.error.message : mutation.status} - -
- -
-
+ ) } diff --git a/src/app/admin/cv/category/list/page.tsx b/src/app/admin/cv/category/list/page.tsx index d48b886..64e5cf7 100644 --- a/src/app/admin/cv/category/list/page.tsx +++ b/src/app/admin/cv/category/list/page.tsx @@ -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(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 (
@@ -44,8 +33,7 @@ export default function CvPage() {
- Category id : {cat.id} - Category Position : {cat.layoutPosition} +
Entries:
@@ -53,30 +41,21 @@ export default function CvPage() { cat.cvEntry.length > 0 ? ( <> {cat.cvEntry.map((entry) => ( - - - - - - - - + ))} ) : (<>) }
+
+ +
-
) })} + }
diff --git a/src/app/admin/cv/entry/[id]/page.tsx b/src/app/admin/cv/entry/[id]/page.tsx index 395eeb4..9b99abd 100644 --- a/src/app/admin/cv/entry/[id]/page.tsx +++ b/src/app/admin/cv/entry/[id]/page.tsx @@ -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 ( - + ) } diff --git a/src/app/admin/cv/entry/_components/CollapsibleForm.tsx b/src/app/admin/cv/entry/_components/CollapsibleForm.tsx new file mode 100644 index 0000000..3e7ea34 --- /dev/null +++ b/src/app/admin/cv/entry/_components/CollapsibleForm.tsx @@ -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['cvEntry']>|EntryRouterOutputs['get']|Element|undefined}) { + return ( + + + + + + { + params.entry ? + : + + + } + + + ) +} diff --git a/src/app/admin/cv/entry/[id]/UpdateForm.tsx b/src/app/admin/cv/entry/_components/CreateForm.tsx similarity index 73% rename from src/app/admin/cv/entry/[id]/UpdateForm.tsx rename to src/app/admin/cv/entry/_components/CreateForm.tsx index 5805359..ee136d5 100644 --- a/src/app/admin/cv/entry/[id]/UpdateForm.tsx +++ b/src/app/admin/cv/entry/_components/CreateForm.tsx @@ -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>({ - resolver: zodResolver(updateRouteSchemaCliennt), + const form = useForm>({ + 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) { - 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) { + let { id, title, categoryId, description } = values + let v: z.infer = { + 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 ( - + return ( + Create Entry @@ -72,7 +59,7 @@ export default function UpdateCvEntryForm({ params }: { params: { id: string, cl > ( @@ -83,11 +70,11 @@ export default function UpdateCvEntryForm({ params }: { params: { id: string, cl