update all deps

This commit is contained in:
2026-03-10 19:17:15 +01:00
parent 4b1084d826
commit 7a8736d9c5
17 changed files with 173 additions and 201 deletions

View File

@@ -9,41 +9,38 @@ export default function TopNav() {
<div className="fixed lg:w-full right-0 z-50 lg:bg-background">
<nav className="flex flex-col-reverse lg:flex-row flex-wrap w-20 lg:w-full outline-1 lg:h-10 h-full">
<div className="flex flex-wrap lg:h-full w-20 lg:w-fit lg:flex-row">
<Button className="flex h-fit lg:h-full w-full lg:w-20" asChild variant="outline">
<Button className="flex h-10 lg:h-full w-full lg:w-20" asChild variant="outline">
<Link href={"/blog"}> Blog </Link>
</Button>
<Button asChild className="flex h-full w-full lg:w-20" variant="outline">
<Button asChild className="flex h-10 lg:h-full w-full lg:w-20" variant="outline">
<Link href={"/cv"}> CV </Link>
</Button>
<Button asChild className="flex h-full w-full lg:w-20" variant="outline">
<Button asChild className="flex h-10 lg:h-full w-full lg:w-20" variant="outline">
<Link href={"/projects"}> Projects </Link>
</Button>
<Button asChild className="flex h-full w-full lg:w-20" variant="outline">
<Link href={"/fun"}> Fun </Link>
</Button>
</div>
<div className="flex flex-col-reverse flex-wrap lg:h-full w-20 lg:w-fit lg:flex-row lg:ml-auto">
<AdminWrap>
<Button className="flex h-full w-full lg:w-20" variant="outline">
<Button className="flex h-10 lg:h-full w-full lg:w-20" variant="outline">
<Link className="" href={"/admin"}> Admin </Link>
</Button>
</AdminWrap>
<Show when="signed-in">
<Button asChild className="flex h-full w-full lg:w-20" variant={"outline"}>
<Button asChild className="flex h-10 lg:h-full w-full lg:w-20" variant={"outline"}>
<SignOutButton />
</Button>
</Show>
<Show when="signed-out">
<Button asChild className="flex h-full cursor-pointer lg:w-20" variant={"outline"}>
<Button asChild className="flex h-10 lg:h-full cursor-pointer lg:w-20" variant={"outline"}>
<SignInButton mode="modal" />
</Button>
<Button asChild className="flex h-full cursor-pointer lg:w-20" variant={"outline"}>
<Button asChild className="flex h-10 lg:h-full cursor-pointer lg:w-20" variant={"outline"}>
<SignUpButton mode="modal" />
</Button>
</Show>
<ThemeSwitch />
<Show when="signed-in">
<Button asChild className="flex h-full cursor-pointer lg:w-20 content-center" variant={"outline"}>
<Button asChild className="flex h-10 lg:h-full cursor-pointer lg:w-20 content-center" variant={"outline"}>
<div>
<UserButton />
</div>

View File

@@ -7,7 +7,7 @@ import CreateUpdateCvCategoryForm from "../_components/CreateUpdateForm";
export default function Page() {
const {id} = useParams<{id: string}>();
const {data} = trpc.category.select.useQuery({id: id})
const {data} = trpc.category.getById.useQuery(id)
if (data !== undefined && data.length > 0) {
return (
<CreateUpdateCvCategoryForm entity={data[0]}/>

View File

@@ -6,7 +6,7 @@ import { trpc } from "~/app/_trpc/Client";
import type { IterableElement } from 'type-fest'
import { entitySchemas, makeOnSuccess } from "~/lib/utils";
import type { RouterOutputs } from "~/server/routers/_app";
import { CollapsibleForm, FormScaffold } from '~/app/_components/Form/Components';
import { FormScaffold } from '~/app/_components/Form/Components';
import { useState } from 'react';
import { SelectFormField, TextInputFormField } from '~/app/_components/Form/Fields';
import { usePathname, useRouter } from 'next/navigation';

View File

@@ -6,24 +6,24 @@ import type { inferProcedureOutput } from "@trpc/server"
import type { RouterOutputs } from "~/server/routers/_app"
import { cn } from "~/lib/utils"
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"
import type { ArrayElement } from "type-fest"
type CvCategoryProps = {
initialData: RouterOutputs['cv']['category']['get'],
initialData: ArrayElement<RouterOutputs['categoryv2']['listByLayoutPosition']>,
layout: "row"|"col",
children?: React.ReactElement<Parameters<typeof CvEntry>>
}
export default function CvCategory(props:CvCategoryProps) {
const query = trpc.category.select.useQuery({id: props.initialData? props.initialData.id : ""});
if (query.data !== undefined) {
const category = trpc.categoryv2.getById.useQuery(props.initialData? props.initialData.id : "");
return (
<Card className={cn(props.layout == "row" ? "w-full" : "","gsapan")}>
<CardHeader>
<CardTitle>
{query.data[0].name}
{category.data?.name}
</CardTitle>
</CardHeader>
{(query.data?.at(0)?.cvEntry.length ? query.data?.cvEntry.length : 0 ) > 0 ?
<CardContent className={cn(props.layout == "row" ? "flex flex-row flex-wrap justify-center lg:justify-between" : "flex flex-col","gap-[1rem]","overflow-scroll")}>
{query.data?.cvEntry.map((entry) => (
{(category.data?.cvEntry.length ? category.data?.cvEntry.length : 0 ) > 0 ?
<CardContent className={cn(props.layout == "row" ? "flex flex-row flex-wrap justify-center lg:justify-between" : "flex flex-col","gap-4","overflow-scroll")}>
{category.data?.cvEntry.map((entry) => (
<CvEntry className={props.layout == "row" ? "w-full lg:w-fit" : undefined} key={entry.id} initialData={entry}/>
))}
</CardContent>
@@ -32,15 +32,4 @@ export default function CvCategory(props:CvCategoryProps) {
}
</Card>
)
} else {
return (
<Card className="gsapan">
<CardHeader>
<CardTitle>
Loading ...
</CardTitle>
</CardHeader>
</Card>
);
}
}

View File

@@ -1,25 +1,25 @@
import { trpc } from "~/app/_trpc/Client"
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "~/components/ui/card"
import { Skeleton } from "~/components/ui/skeleton"
import { cn, type Element } from "~/lib/utils"
import type { CategoryRouterOutputs } from "~/server/routers/cv/category"
import type { EntryRouterOutputs } from "~/server/routers/cv/entry"
import { cn, type Defined } from "~/lib/utils"
import Markdown from 'react-markdown'
import { format } from 'date-fns'
import rehypeHighlight from 'rehype-highlight'
import rehypeRaw from 'rehype-raw'
import type { RouterOutputs } from "~/server/routers/_app"
import type { ArrayElement } from "type-fest"
export default function CvEntry(params: {
initialData: EntryRouterOutputs['get'] | Element<Element<CategoryRouterOutputs['list']>['cvEntry']>
className?: string
initialData: ArrayElement<Defined<RouterOutputs['categoryv2']['getById']>['cvEntry']>,
className?: string
}) {
const query = trpc.cv.entry.get.useQuery({ id: params.initialData?.id ? params.initialData.id : "" })
const { data } = query
const query = trpc.entryv2.getById.useQuery(params.initialData.id);
const { data, isError, error } = query
return (
<>
{
data ?
<>
<Card className={params.className ? cn("w-fit",params.className) : "w-fit"}>
<Card className={params.className ? cn("w-fit", params.className) : "w-fit"}>
{
data.title ?
<CardHeader>
@@ -31,17 +31,17 @@ export default function CvEntry(params: {
data.description ?
<CardContent className="text-sm lg:text-base">
<div>
<Markdown rehypePlugins={[rehypeHighlight,rehypeRaw]}>{data.description}</Markdown>
<Markdown rehypePlugins={[rehypeHighlight, rehypeRaw]}>{data.description}</Markdown>
</div>
</CardContent> :
<></>
}
{
!data.hideDates ?
<CardFooter className="text-sm">
{`von ${format((new Date()).setTime(Date.parse(data.fromTime)), 'M. yyyy')} bis zum ${format((new Date()).setTime(Date.parse(data.toTime)), 'M. yyyy')}`}
</CardFooter> :
<></>
<CardFooter className="text-sm">
{`von ${format((new Date()).setTime(Date.parse(data.fromTime)), 'M. yyyy')} bis zum ${format((new Date()).setTime(Date.parse(data.toTime)), 'M. yyyy')}`}
</CardFooter> :
<></>
}
</Card>
</> :
@@ -55,13 +55,12 @@ export default function CvEntry(params: {
</CardHeader>
<CardContent>
<div>
<Skeleton className="h-4 w-[250px]" />
<Skeleton className="h-4 w-[200px]" />
<Skeleton className="h-4 w-[200px]" />
<Skeleton className="h-4 w-60" />
<Skeleton className="h-4 w-50" />
<Skeleton className="h-4 w-50" />
</div>
</CardContent>
</Card>
</>
}
</>

View File

@@ -2,25 +2,15 @@
import { useGSAP } from "@gsap/react";
import { useGsapContext } from "../_providers/GsapProvicer";
import { trpc } from "../_trpc/Client";
import { useEffect, useRef, useState } from "react";
import { SidebarContent, SidebarProvider, Sidebar } from "~/components/ui/sidebar";
import { useRef } from "react";
import { SidebarContent, SidebarProvider, Sidebar } from "~/components/ui/sidebar";
import SidebarTriggerDisappearsOnMobile from "./_components/SidebarTriggerDisappearsOnMobile";
import CvCategory from "./_components/CvCategory";
import type { CategoryRouterOutputs } from "~/server/routers/cv/category";
export default function CvPage() {
const [sidebarCategories,setSidebarCategories] = useState<CategoryRouterOutputs['list']>([]);
const [headerCategories,setHeaderCategories] = useState<CategoryRouterOutputs['list']>([]);
const [col1Categories,setCol1Categories] = useState<CategoryRouterOutputs['list']>([]);
const [col2Categories,setCol2Categories] = useState<CategoryRouterOutputs['list']>([]);
const categories = trpc.cv.category.list.useQuery();
useEffect(() => {
if (categories.data !== undefined) {
setSidebarCategories(categories.data.filter((cat) => cat.layoutPosition == "sidebar"))
setHeaderCategories(categories.data.filter((cat) => cat.layoutPosition == "header"))
setCol1Categories(categories.data.filter((cat) => cat.layoutPosition == "col1"))
setCol2Categories(categories.data.filter((cat) => cat.layoutPosition == "col2"))
}
},[categories.data])
const sidebarCategories = trpc.categoryv2.listByLayoutPosition.useQuery("sidebar");
const col1Categories = trpc.categoryv2.listByLayoutPosition.useQuery("col1");
const headerCategories = trpc.categoryv2.listByLayoutPosition.useQuery("header");
const col2Categories = trpc.categoryv2.listByLayoutPosition.useQuery("col2");
const gsap = useGsapContext()
const container = useRef<HTMLDivElement>(null)
enum Direction {
@@ -53,66 +43,55 @@ export default function CvPage() {
dir = dir + 1
}
})
}, { scope: container, dependencies: [col1Categories,col2Categories,headerCategories,sidebarCategories], revertOnUpdate: true })
if (categories.data !== undefined) {
return (
<>
<SidebarProvider ref={container}>
{categories.data.filter((cat) => cat.layoutPosition == 'sidebar').length > 0 ?
<>
<SidebarTriggerDisappearsOnMobile />
<Sidebar className="gsapan ">
<SidebarContent className="p-2 lg:pt-[3.2rem]">
{sidebarCategories.map((cat) => {
}, { scope: container, dependencies: [headerCategories.data, sidebarCategories.data], revertOnUpdate: true })
return (
<>
<SidebarProvider ref={container}>
{(sidebarCategories.data?.length ? sidebarCategories.data?.length : 0) > 0 ?
<>
<SidebarTriggerDisappearsOnMobile />
<Sidebar className="gsapan ">
<SidebarContent className="p-2 lg:pt-[3.2rem]">
{sidebarCategories.data?.map((cat) => {
if (cat !== undefined) {
return (
<CvCategory layout="col" initialData={cat} key={cat.id} />
)
})}
</SidebarContent>
</Sidebar>
</> :
<></>
}
<div className="h-full w-full flex flex-wrap flex-row p-[1rem] pt-[2rem] ">
<div id="mainwrap" className="flex w-full flex-col gap-[1rem] lg:px-[15vw]">
<div id="header" className="flex w-full h-fit flex-row gap-[1rem] flex-wrap">
{headerCategories.map((cat) => {
}
})}
</SidebarContent>
</Sidebar>
</> :
<></>
}
<div className="h-full w-full flex flex-wrap flex-row p-4 pt-8 ">
<div id="mainwrap" className="flex w-full flex-col gap-4 lg:px-[15vw]">
<div id="header" className="flex w-full h-fit flex-row gap-4 flex-wrap">
{headerCategories.data?.map((cat) => {
return (
<CvCategory layout="row" initialData={cat} key={cat.id} />
)
})}
</div>
<div id="colwrapper" className="flex flex-col lg:flex-row w-full h-3/4 gap-4">
<div id="col1" className={`flex flex-col w-full ${col1Categories.data?.length ? col1Categories.data?.length : 0 > 0 ? "lg:w-1/2" : ""} h-full gap-4`}>
{col1Categories.data?.map((cat) => {
return (
<CvCategory layout="row" initialData={cat} key={cat.id} />
<CvCategory layout="col" initialData={cat} key={cat.id} />
)
})}
</div>
<div id="colwrapper" className="flex flex-col lg:flex-row w-full h-3/4 gap-[1rem]">
{col1Categories.length > 0 ?
<div id="col1" className={`flex flex-col w-full ${col2Categories.length > 0 ? "lg:w-1/2" : ""} h-full gap-[1rem]`}>
{col1Categories.map((cat) => {
return (
<CvCategory layout="col" initialData={cat} key={cat.id} />
)
})}
</div> :
<></>
}
{col2Categories.length > 0 ?
<div id="col2" className={`flex flex-col w-full ${col1Categories.length > 0 ? "lg:w-1/2" : ""} h-full gap-[1rem]`}>
{col2Categories.map((cat) => {
return (
<CvCategory layout="col" initialData={cat} key={cat.id} />
)
})}
</div> :
<></>
}
<div id="col2" className={`flex flex-col w-full ${col2Categories.data?.length ? col2Categories.data?.length : 0 > 0 ? "lg:w-1/2" : ""} h-full gap-4`}>
{col2Categories.data?.map((cat) => {
return (
<CvCategory layout="col" initialData={cat} key={cat.id} />
)
})}
</div>
</div>
</div>
</SidebarProvider>
</>
)
} else {
return (
<>
</>
)
}
</div>
</SidebarProvider>
</>
)
}

View File

@@ -1,30 +0,0 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faChessBishop,faChessPawn,faChessKing,faChessQueen,faChessKnight,faChessRook } from "@fortawesome/free-solid-svg-icons"
type piece = "pawn" | "bishop" | "king" | "queen" | "knight" | "rook"
type color = "black" | "white"
type ChessPieceProps = {
piece: piece,
pieceColor: color,
color: color
}
export default function ChessField(props:ChessPieceProps) {
let fontSize = "2em";
let className = props.color == "black" ? "bg-slate-600" : "bg-slate-200"
className += " h-10 w-10 flex place-content-center items-center text-center p-auto"
const pieceColor = props.pieceColor == "black" ? "#3d3d3d" : "#b5b5b5"
switch(props.piece) {
case "pawn":
return <div className={className}> <FontAwesomeIcon icon={faChessPawn} style={{color:pieceColor, fontSize:fontSize}}/> </div>
case "bishop":
return <div className={className}> <FontAwesomeIcon icon={faChessBishop} style={{color:pieceColor,fontSize:fontSize}}/> </div>
case "king":
return <div className={className}> <FontAwesomeIcon icon={faChessKing} style={{color:pieceColor,fontSize:fontSize}}/> </div>
case "queen":
return <div className={className}> <FontAwesomeIcon icon={faChessQueen} style={{color:pieceColor,fontSize:fontSize}}/> </div>
case "knight":
return <div className={className}> <FontAwesomeIcon icon={faChessKnight} style={{color:pieceColor,fontSize:fontSize}}/> </div>
case "rook":
return <div className={className}> <FontAwesomeIcon icon={faChessRook} style={{color:pieceColor,fontSize:fontSize}}/> </div>
}
}

View File

@@ -1,10 +0,0 @@
'use client'
export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode}>) {
return (
<>
{children}
</>
)
}

View File

@@ -1,12 +0,0 @@
'use client'
import { usePathname } from "next/navigation"
import ChessField from "./_components/ChessPiece"
export default function Page() {
const pathName = usePathname()
return (
<div>
<ChessField color="black" piece="pawn" pieceColor="white"/>
<ChessField color="white" piece="king" pieceColor="black"/>
</div>
)
}

View File

@@ -13,7 +13,7 @@ import GsapProvider from "./_providers/GsapProvicer";
import { CodeHighlightStyle } from "./_components/CodeHighlightSyle";
import { cn } from "~/lib/utils";
const inter = Inter({subsets:['latin'],variable:'--font-sans'});
const inter = Inter({ subsets: ['latin'], variable: '--font-sans' });
config.autoAddCss = false;
@@ -37,22 +37,22 @@ export default async function RootLayout({
return (
<ClerkProvider>
<TrpcProvider>
<ThemeProvider>
<GsapProvider>
<html lang="en" className={cn(geist.variable, "font-sans", inter.variable)} suppressHydrationWarning>
<head>
<CodeHighlightStyle/>
</head>
<body className="flex flex-col bg-background text-foreground">
<GsapProvider>
<html lang="en" className={cn(geist.variable, "font-sans", inter.variable)} suppressHydrationWarning>
<head>
<CodeHighlightStyle />
</head>
<body className="flex flex-col bg-background text-foreground">
<ThemeProvider>
<TopNav />
<main className="absolute lg:top-10 h-[100vh] w-[100vw]">
{children}
<main className="absolute lg:top-10 h-screen w-screen">
{children}
</main>
{modal}
</body>
</html>
</GsapProvider>
</ThemeProvider>
</ThemeProvider>
</body>
</html>
</GsapProvider>
</TrpcProvider>
</ClerkProvider>
);

View File

@@ -9,6 +9,8 @@ export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
export type Defined<T> = T extends undefined ? never : T;
export type Schema = typeof schema;
export type SchemaKeys<S> = {
@@ -68,12 +70,7 @@ export function makeOnSuccess<T extends FieldValues>(uc: 'update' | 'create' | '
}
}
export function entitySchemas<T extends SchemaKeys<Schema>>(table: T)
: {
insert: Pretty<BuildSchema<'insert', Schema[T]['_']['columns'], undefined>>
update: Pretty<BuildSchema<'update', Schema[T]['_']['columns'], undefined>>
select: Pretty<BuildSchema<'select', Schema[T]['_']['columns'], undefined>>
} {
export function entitySchemas<T extends SchemaKeys<Schema>>(table: T) {
const insertSchema = createInsertSchema<Schema[T]>(schema[table]);
const updateSchema = createUpdateSchema<Schema[T]>(schema[table]);
const selectSchema = createSelectSchema<Schema[T]>(schema[table]);

View File

@@ -1,20 +1,24 @@
import type { inferRouterOutputs } from "@trpc/server";
import { router } from "../trpc";
import type { inferReactQueryProcedureOptions } from "@trpc/react-query";
import { projectRouter } from "./project";
import { techStackRouter } from "./techStack";
import { cvCategoryRouter } from "./cvCategory";
import { cvEntryRouter } from "./cvEntry";
import { trpcCrudRouterFromDrizzleEntity } from "../lib";
import type { inferRouterMeta } from "@trpc/server/unstable-core-do-not-import";
import { cvCategory } from "../dbschema/schema";
const { router : project } = trpcCrudRouterFromDrizzleEntity('project')
const { router : techStack } = trpcCrudRouterFromDrizzleEntity('techStack')
const { router : category } = trpcCrudRouterFromDrizzleEntity('cvCategory')
const { router : entry } = trpcCrudRouterFromDrizzleEntity('cvEntry')
export const trpcRouter = router({
project: project,
techStack: techStack,
category: category,
entry: entry
})
project: trpcCrudRouterFromDrizzleEntity('project').router,
projectv2: projectRouter,
techStack: trpcCrudRouterFromDrizzleEntity('techStack').router,
techStackv2: techStackRouter,
category: trpcCrudRouterFromDrizzleEntity('cvCategory').router,
categoryv2: cvCategoryRouter,
entry: trpcCrudRouterFromDrizzleEntity('cvEntry').router,
entryv2: cvEntryRouter,
});
export type TrpcRouter = typeof trpcRouter
export type RouterOutputs = inferRouterOutputs<TrpcRouter>
export type ReactQueryOptions = inferReactQueryProcedureOptions<TrpcRouter>
export type TrpcRouter = typeof trpcRouter;
export type RouterOutputs = inferRouterOutputs<TrpcRouter>;
export type ReactQueryOptions = inferReactQueryProcedureOptions<TrpcRouter>;

View File

@@ -0,0 +1,34 @@
import { publicProcedure, router } from "~/server/trpc";
import { db } from "~/server/db";
import { cvCategory } from "~/server/dbschema/schema";
import { eq } from "drizzle-orm";
import { z } from 'zod';
import { createInsertSchema, createUpdateSchema } from "drizzle-zod";
import { TRPCError } from "@trpc/server";
export const cvCategoryRouter = router({
listByLayoutPosition: publicProcedure.input(z.enum(cvCategory.layoutPosition.enumValues)).query(async ({input}) => {
console.log(input);
const res = await db.select().from(cvCategory).where(eq(cvCategory.layoutPosition,input));
console.log(res);
return res;
}),
getById: publicProcedure.input(z.string()).query(async ({input}) => {
const res = await db.query.cvCategory.findFirst({
with: {
cvEntry: true
},
where(fields, operators) {
return operators.eq(fields.id, input)
},
})
return res;
}),
insert: publicProcedure.input(createInsertSchema(cvCategory)).mutation(async ({input}) => {
let res = await db.insert(cvCategory).values(input).returning().execute();
return res.at(0)
}),
updateById: publicProcedure.input(z.object({id: z.string(),data: createUpdateSchema(cvCategory)})).mutation(async ({input}) => {
let res = await db.update(cvCategory).set(input.data).where(eq(cvCategory.id,input.id)).execute();
})
});

View File

@@ -0,0 +1,16 @@
import { publicProcedure, router } from "~/server/trpc";
import { db } from "~/server/db";
import { z } from 'zod';
export const cvEntryRouter = router({
getById: publicProcedure.input(z.string()).query(async ({input}) => {
const res = await db.query.cvEntry.findFirst({
with: {
category: true
},
where(fields, operators) {
return operators.eq(fields.id, input)
},
})
return res;
}),
});

View File

@@ -0,0 +1,4 @@
import { router } from "~/server/trpc";
export const projectRouter = router({
});

View File

@@ -0,0 +1,5 @@
import { router } from "~/server/trpc";
export const techStackRouter = router({
});