feat: p2 batch 28-31 reminders lists comments bang stats
Calendar events support multiple reminder offsets with presets and per-user defaults. Lists index adds inline task entry and list property editing. Generic entity comments on list detail. New bangs.stats dashboard widget. Closes Gitea #28, #29, #30, #31. Migrations 0022 and 0023.
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
import { CalendarShell } from "@/modules/calendar/components/calendar-shell";
|
||||
import { listCalendars, listEvents } from "@/modules/calendar/server/queries";
|
||||
import {
|
||||
getDefaultEventReminderOffsets,
|
||||
listCalendars,
|
||||
listEvents,
|
||||
} from "@/modules/calendar/server/queries";
|
||||
import { getCurrentSession } from "@/lib/session";
|
||||
import type { CalView } from "@/modules/_core/themes";
|
||||
|
||||
@@ -10,10 +14,11 @@ export default async function CalendarPage() {
|
||||
const to = new Date(now);
|
||||
to.setMonth(to.getMonth() + 10);
|
||||
|
||||
const [{ user }, calendars, events] = await Promise.all([
|
||||
const [{ user }, calendars, events, defaultReminderOffsets] = await Promise.all([
|
||||
getCurrentSession(),
|
||||
listCalendars(),
|
||||
listEvents({ from, to, calendarIds: "all" }),
|
||||
getDefaultEventReminderOffsets(),
|
||||
]);
|
||||
|
||||
return (
|
||||
@@ -21,6 +26,7 @@ export default async function CalendarPage() {
|
||||
calendars={calendars}
|
||||
events={events}
|
||||
defaultView={user.themeCalView as CalView}
|
||||
defaultReminderOffsets={defaultReminderOffsets}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { notFound } from "next/navigation";
|
||||
import { getCurrentSession } from "@/lib/session";
|
||||
import { ListDetail } from "@/modules/lists/components/list-detail";
|
||||
import { getList } from "@/modules/lists/server/queries";
|
||||
|
||||
export default async function ListPage({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = await params;
|
||||
const list = await getList(id).catch(() => null);
|
||||
const [{ user }, list] = await Promise.all([getCurrentSession(), getList(id).catch(() => null)]);
|
||||
if (!list) notFound();
|
||||
|
||||
return <ListDetail initialList={list} />;
|
||||
return <ListDetail initialList={list} currentUserId={user.id} />;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { revokeShareLinkAction } from "./actions";
|
||||
import { getHouseholdApiTokenStatus } from "@/modules/_core/api-token";
|
||||
import { ApiTokenSettings } from "@/components/api-token-settings";
|
||||
import { AssistantOptIn } from "@/components/assistant-opt-in";
|
||||
import { DefaultEventRemindersSetting } from "@/components/default-event-reminders-setting";
|
||||
import { listCalendars } from "@/modules/calendar/server/queries";
|
||||
import { listLists } from "@/modules/lists/server/queries";
|
||||
import Link from "next/link";
|
||||
@@ -61,7 +62,12 @@ export default async function SettingsPage({
|
||||
{section === "household" && <HouseholdSection household={household} user={user} />}
|
||||
{section === "sharing" && <SharingSection />}
|
||||
{section === "notifications" && (
|
||||
<NotificationsSection user={user} vapidKey={vapidKey} ntfyConfigured={ntfyConfigured} />
|
||||
<NotificationsSection
|
||||
user={user}
|
||||
defaultEventReminderOffsets={user.defaultEventReminderOffsets ?? [30]}
|
||||
vapidKey={vapidKey}
|
||||
ntfyConfigured={ntfyConfigured}
|
||||
/>
|
||||
)}
|
||||
{section === "calendars" && <CalendarsAndListsSection />}
|
||||
{section === "appearance" && <AppearanceSection user={user} />}
|
||||
@@ -185,10 +191,12 @@ async function SharingSection() {
|
||||
|
||||
function NotificationsSection({
|
||||
user,
|
||||
defaultEventReminderOffsets,
|
||||
vapidKey,
|
||||
ntfyConfigured,
|
||||
}: {
|
||||
user: { notifPush: boolean; notifInApp: boolean; notifNtfy: boolean };
|
||||
defaultEventReminderOffsets: number[];
|
||||
vapidKey: string;
|
||||
ntfyConfigured: boolean;
|
||||
}) {
|
||||
@@ -216,6 +224,18 @@ function NotificationsSection({
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Default event reminders</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<DefaultEventRemindersSetting
|
||||
key={defaultEventReminderOffsets.join(",")}
|
||||
initialOffsets={defaultEventReminderOffsets}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
"use server";
|
||||
|
||||
import { eq } from "drizzle-orm";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { z } from "zod";
|
||||
import { db } from "@/lib/db";
|
||||
import { getCurrentSession } from "@/lib/session";
|
||||
import { normalizeReminderOffsets } from "@/lib/reminder-offsets";
|
||||
import { users } from "@/modules/_core/schema";
|
||||
|
||||
const offsetsSchema = z.array(z.number().int().min(0)).max(20);
|
||||
|
||||
export async function setDefaultEventReminderOffsets(offsets: number[]) {
|
||||
const { user } = await getCurrentSession();
|
||||
const parsed = normalizeReminderOffsets(offsetsSchema.parse(offsets));
|
||||
|
||||
await db.update(users).set({ defaultEventReminderOffsets: parsed }).where(eq(users.id, user.id));
|
||||
|
||||
revalidatePath("/calendar");
|
||||
revalidatePath("/settings");
|
||||
}
|
||||
Reference in New Issue
Block a user