This commit is contained in:
2025-06-06 03:12:19 +02:00
parent be9a9af137
commit 0aab9f55d7
49 changed files with 3255 additions and 93 deletions

View File

@@ -0,0 +1,125 @@
'use client'
import { insertSchema, updateRouteSchema, updateSchema } 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 { useRouter } from "next/navigation";
import { use } from "react";
export default function UpdateCvCategoryForm({params}:{params:{id:string}}) {
const id = params.id
console.log(id)
const category = trpc.cv.category.get.useQuery({id:id})
const form = useForm<z.infer<typeof updateRouteSchema>>({
resolver: zodResolver(updateRouteSchema),
defaultValues: {
by: {id: id},
update: {
layoutPosition: category.data?.layoutPosition,
name: category.data?.layoutPosition
}
}
})
category.promise.then((data) => {
form.setValue("update.layoutPosition",data?.layoutPosition)
form.setValue("update.name",data?.name)
})
const mutation = trpc.cv.category.update.useMutation({onSuccess: () => {
category.refetch()
}})
function onSubmit(values: z.infer<typeof updateRouteSchema>) {
mutation.mutate({
by: {id: id},
update: {
layoutPosition: values.update.layoutPosition,
name: values.update.name
}
})
}
if (category.data !== undefined) {
return (
<Card.Card className="w-5/6 lg:w-1/2">
<Card.CardHeader>
<Card.CardTitle>
Update Category
</Card.CardTitle>
</Card.CardHeader>
<Card.CardContent>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-8"
>
<FormField
control={form.control}
name="update.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="by.id"
render={({field}) => (
<FormItem>
<FormControl>
<Input hidden onChange={field.onChange} value={field.value}/>
</FormControl>
</FormItem>
)}
>
</FormField>
<FormField
control={form.control}
name="update.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().update.layoutPosition} />
</SelectTrigger>
<SelectContent>
{updateRouteSchema.shape.update.shape.layoutPosition.unwrap().unwrap().options.map((o) => (
<SelectItem key={o} value={o}> {o} </SelectItem>
))}
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
>
</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>
</form>
</Form>
</Card.CardContent>
</Card.Card>
)
} else {
return (
<>
</>
)
}
}

View File

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

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() {
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="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

@@ -0,0 +1,65 @@
"use client"
import Link from "next/link";
import { trpc } from "~/app/_trpc/Client";
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 cvCategories = trpc.cv.category.list.useQuery();
const gsap = useGsapContext()
const container = useRef<HTMLDivElement>(null);
const mut = trpc.cv.category.delete.useMutation({onSuccess: () => {
console.log('success')
cvCategories.refetch()
}})
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 } })
}, { scope: container, dependencies: [cvCategories.status], revertOnUpdate: true });
return (
<div ref={container} className="w-5/6 lg:w-1/2 flex flex-col gap-3">
{cvCategories.data == undefined ?
<div className="gsapan"></div>
:
<>
{cvCategories.data.map((cat) => {
return (
<Card.Card className="gsapan" key={cat.id}>
<Link href={`/admin/cv/category/${cat.id}`}>
<Card.CardHeader>
<Card.CardTitle>
Category Name : {cat.name}
</Card.CardTitle>
</Card.CardHeader>
</Link>
<Card.CardContent className="flex flex-row">
Category id : {cat.id} <br />
Category Position : {cat.layoutPosition} <br />
{
cat.cvEntry.length > 0 ? (
<>
{cat.cvEntry.map((entry) => {
<Link href={`/admin/cv/entry/${entry.id}`}> {entry.title} </Link>
})}
</>
) : (<></>)
}
<Button
className="ml-auto cursor-pointer" variant="destructive" onClick={() => { deleteCategory(cat.id) }}>
<Delete />
</Button>
</Card.CardContent>
</Card.Card>
)
})}
</>
}
</div>
)
}

View File

@@ -0,0 +1,125 @@
'use client'
import { insertSchema, updateRouteSchema, updateSchema } 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 { useRouter } from "next/navigation";
import { use } from "react";
export default function UpdateCvCategoryForm({params}:{params:{id:string}}) {
const id = params.id
console.log(id)
const category = trpc.cv.category.get.useQuery({id:id})
const form = useForm<z.infer<typeof updateRouteSchema>>({
resolver: zodResolver(updateRouteSchema),
defaultValues: {
by: {id: id},
update: {
layoutPosition: category.data?.layoutPosition,
name: category.data?.layoutPosition
}
}
})
category.promise.then((data) => {
form.setValue("update.layoutPosition",data?.layoutPosition)
form.setValue("update.name",data?.name)
})
const mutation = trpc.cv.category.update.useMutation({onSuccess: () => {
category.refetch()
}})
function onSubmit(values: z.infer<typeof updateRouteSchema>) {
mutation.mutate({
by: {id: id},
update: {
layoutPosition: values.update.layoutPosition,
name: values.update.name
}
})
}
if (category.data !== undefined) {
return (
<Card.Card className="w-5/6 lg:w-1/2">
<Card.CardHeader>
<Card.CardTitle>
Update Category
</Card.CardTitle>
</Card.CardHeader>
<Card.CardContent>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-8"
>
<FormField
control={form.control}
name="update.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="by.id"
render={({field}) => (
<FormItem>
<FormControl>
<Input hidden onChange={field.onChange} value={field.value}/>
</FormControl>
</FormItem>
)}
>
</FormField>
<FormField
control={form.control}
name="update.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().update.layoutPosition} />
</SelectTrigger>
<SelectContent>
{updateRouteSchema.shape.update.shape.layoutPosition.unwrap().unwrap().options.map((o) => (
<SelectItem key={o} value={o}> {o} </SelectItem>
))}
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
>
</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>
</form>
</Form>
</Card.CardContent>
</Card.Card>
)
} else {
return (
<>
</>
)
}
}

View File

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

View File

@@ -0,0 +1,98 @@
'use client'
import { insertSchema } 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"
export default function CreateCvEntryForm() {
const categories = trpc.cv.category.list.useQuery()
const [categoyIds, setCategoryIds] = useState<string[]>([])
const form = useForm<z.infer<typeof insertSchema>>({
resolver: zodResolver(insertSchema),
defaultValues: {
id: crypto.randomUUID(),
title: "",
description: "",
categoryId: ""
}
})
categories.promise.then((data) => {
form.setValue("categoryId", data[0]?.id)
setCategoryIds(data.map((cat) => cat.id))
})
const mutation = trpc.cv.entry.create.useMutation({
onSuccess: (data) => { form.setValue("id", data[0] ? data[0].id : "") }
})
function onSubmit(values: z.infer<typeof insertSchema>) {
mutation.mutate(values)
form.setValue("id", crypto.randomUUID())
}
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="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>
Title
</FormLabel>
<FormControl>
<Input placeholder="description" onChange={field.onChange} value={field.value == null ? undefined : field.value} />
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="categoryId"
>
</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

@@ -0,0 +1,65 @@
"use client"
import Link from "next/link";
import { trpc } from "~/app/_trpc/Client";
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 cvCategories = trpc.cv.category.list.useQuery();
const gsap = useGsapContext()
const container = useRef<HTMLDivElement>(null);
const mut = trpc.cv.category.delete.useMutation({onSuccess: () => {
console.log('success')
cvCategories.refetch()
}})
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 } })
}, { scope: container, dependencies: [cvCategories.status], revertOnUpdate: true });
return (
<div ref={container} className="w-5/6 lg:w-1/2 flex flex-col gap-3">
{cvCategories.data == undefined ?
<div className="gsapan"></div>
:
<>
{cvCategories.data.map((cat) => {
return (
<Card.Card className="gsapan" key={cat.id}>
<Link href={`/admin/cv/category/${cat.id}`}>
<Card.CardHeader>
<Card.CardTitle>
Category Name : {cat.name}
</Card.CardTitle>
</Card.CardHeader>
</Link>
<Card.CardContent className="flex flex-row">
Category id : {cat.id} <br />
Category Position : {cat.layoutPosition} <br />
{
cat.cvEntry.length > 0 ? (
<>
{cat.cvEntry.map((entry) => {
<Link href={`/admin/cv/entry/${entry.id}`}> {entry.title} </Link>
})}
</>
) : (<></>)
}
<Button
className="ml-auto cursor-pointer" variant="destructive" onClick={() => { deleteCategory(cat.id) }}>
<Delete />
</Button>
</Card.CardContent>
</Card.Card>
)
})}
</>
}
</div>
)
}