add basic timeline view

This commit is contained in:
2026-02-24 10:58:47 +01:00
parent 27d3cd364e
commit ae706c9a91
10 changed files with 510 additions and 9 deletions

View File

@@ -0,0 +1,22 @@
import { db } from "@zendegi/db";
import { timelineGroup } from "@zendegi/db/schema/timeline";
import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";
import { authMiddleware } from "@/middleware/auth";
export const createTimelineGroup = createServerFn({ method: "POST" })
.middleware([authMiddleware])
.inputValidator(
z.object({ title: z.string().min(1), timelineId: z.string().uuid() })
)
.handler(async ({ data }) => {
const [newGroup] = await db
.insert(timelineGroup)
.values({
title: data.title,
timelineId: data.timelineId,
})
.returning();
return newGroup;
});

View File

@@ -0,0 +1,34 @@
import { db } from "@zendegi/db";
import { timelineItem } from "@zendegi/db/schema/timeline";
import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";
import { authMiddleware } from "@/middleware/auth";
export const createTimelineItem = createServerFn({ method: "POST" })
.middleware([authMiddleware])
.inputValidator(
z.object({
title: z.string().min(1),
description: z.string().default(""),
start: z.string().transform((s) => new Date(s)),
end: z
.string()
.optional()
.transform((s) => (s ? new Date(s) : undefined)),
timelineGroupId: z.string().uuid(),
})
)
.handler(async ({ data }) => {
const [newItem] = await db
.insert(timelineItem)
.values({
title: data.title,
description: data.description,
start: data.start,
end: data.end,
timelineGroupId: data.timelineGroupId,
})
.returning();
return newItem;
});

View File

@@ -0,0 +1,20 @@
import { db } from "@zendegi/db";
import { timeline } from "@zendegi/db/schema/timeline";
import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";
import { authMiddleware } from "@/middleware/auth";
export const createTimeline = createServerFn({ method: "POST" })
.middleware([authMiddleware])
.inputValidator(z.object({ title: z.string().min(1) }))
.handler(async ({ data, context }) => {
const [newTimeline] = await db
.insert(timeline)
.values({
title: data.title,
ownerId: context.session!.user.id,
})
.returning();
return newTimeline;
});

View File

@@ -0,0 +1,36 @@
import { eq } from "drizzle-orm";
import { db } from "@zendegi/db";
import { timeline } from "@zendegi/db/schema/timeline";
import { createServerFn } from "@tanstack/react-start";
import { queryOptions } from "@tanstack/react-query";
import { z } from "zod";
export const getTimeline = createServerFn({ method: "GET" })
.inputValidator(z.object({ id: z.string().uuid() }))
.handler(async ({ data }) => {
const result = await db.query.timeline.findFirst({
where: eq(timeline.id, data.id),
with: {
groups: {
orderBy: (g, { asc }) => [asc(g.sortOrder)],
with: {
items: {
orderBy: (i, { asc }) => [asc(i.start)],
},
},
},
},
});
if (!result) {
throw new Error("Timeline not found");
}
return result;
});
export const timelineQueryOptions = (timelineId: string) =>
queryOptions({
queryKey: ["timeline", timelineId],
queryFn: () => getTimeline({ data: { id: timelineId } }),
});