Implement lists module and dev login setup

This commit is contained in:
ginnoir
2026-05-06 03:49:03 -05:00
parent 744c1119a9
commit 7e3ae6eb04
31 changed files with 1525 additions and 63 deletions
@@ -4,11 +4,7 @@ import FullCalendar from "@fullcalendar/react";
import dayGridPlugin from "@fullcalendar/daygrid";
import interactionPlugin from "@fullcalendar/interaction";
import timeGridPlugin from "@fullcalendar/timegrid";
import type {
DateSelectArg,
EventClickArg,
EventDropArg,
} from "@fullcalendar/core";
import type { DateSelectArg, EventClickArg, EventDropArg } from "@fullcalendar/core";
import type { EventResizeDoneArg } from "@fullcalendar/interaction";
import { CalendarPlus, Check, Eye, EyeOff, Plus, Trash2 } from "lucide-react";
import { useMemo, useState, useTransition } from "react";
@@ -221,9 +217,7 @@ export function CalendarShell({
function updateCalendar(calendar: CalendarDto, values: Partial<CalendarDto>) {
const next = { ...calendar, ...values };
setCalendarRows((current) =>
current.map((item) => (item.id === calendar.id ? next : item)),
);
setCalendarRows((current) => current.map((item) => (item.id === calendar.id ? next : item)));
startTransition(async () => {
if (values.name !== undefined) {
await renameCalendar({ id: calendar.id, name: values.name });
@@ -367,7 +361,7 @@ export function CalendarShell({
</SelectContent>
</Select>
</div>
<Button className="w-full" onClick={addCalendar} disabled={!calendarName || isPending}>
<Button className="w-full" onClick={addCalendar} disabled={!calendarName}>
<Check />
Create calendar
</Button>
+25 -19
View File
@@ -14,20 +14,31 @@ const calendarInput = z.object({
visibility: z.enum(["private", "household"]).default("household"),
});
const eventInput = z
.object({
calendarId: z.string().uuid(),
title: z.string().trim().min(1).max(200),
startAt: z.coerce.date(),
endAt: z.coerce.date(),
allDay: z.boolean().default(false),
location: z.string().trim().max(300).nullable().optional(),
notes: z.string().trim().max(3000).nullable().optional(),
})
.refine((value) => value.endAt >= value.startAt, {
const eventBaseInput = z.object({
calendarId: z.string().uuid(),
title: z.string().trim().min(1).max(200),
startAt: z.coerce.date(),
endAt: z.coerce.date(),
allDay: z.boolean().default(false),
location: z.string().trim().max(300).nullable().optional(),
notes: z.string().trim().max(3000).nullable().optional(),
});
const eventInput = eventBaseInput.refine((value) => value.endAt >= value.startAt, {
path: ["endAt"],
message: "End must be after start",
});
const eventUpdateInput = eventBaseInput.partial().refine(
(value) => {
if (!value.startAt || !value.endAt) return true;
return value.endAt >= value.startAt;
},
{
path: ["endAt"],
message: "End must be after start",
});
},
);
export async function createCalendar(input: z.input<typeof calendarInput>) {
const parsed = calendarInput.parse(input);
@@ -79,9 +90,7 @@ export async function setCalendarVisibility(input: {
export async function setCalendarColor(input: { id: string; color: string | null }) {
const { user } = await getCurrentSession();
const parsed = z
.object({ id: z.string().uuid(), color: calendarInput.shape.color })
.parse(input);
const parsed = z.object({ id: z.string().uuid(), color: calendarInput.shape.color }).parse(input);
await assertOwnsCalendar(user.id, parsed.id);
await db
.update(calendars)
@@ -124,10 +133,7 @@ export async function createEvent(input: z.input<typeof eventInput>) {
}
export async function updateEvent(input: { id: string } & Partial<z.input<typeof eventInput>>) {
const parsed = z
.object({ id: z.string().uuid() })
.and(eventInput.partial())
.parse(input);
const parsed = z.object({ id: z.string().uuid() }).and(eventUpdateInput).parse(input);
const { user } = await getCurrentSession();
const [existing] = await db
.select({ calendarId: calendarEvents.calendarId })