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:
@@ -15,6 +15,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { ShareButton } from "@/components/share-button";
|
||||
import { ReminderPicker } from "@/components/reminder-picker";
|
||||
import type { CalView } from "@/modules/_core/themes";
|
||||
import {
|
||||
Select,
|
||||
@@ -25,6 +26,7 @@ import {
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import type { CalendarDto, CalendarEventDto } from "../server/queries";
|
||||
import { getEventReminderOffsets } from "../server/queries";
|
||||
import {
|
||||
createCalendar,
|
||||
createEvent,
|
||||
@@ -57,7 +59,7 @@ type EventDraft = {
|
||||
allDay: boolean;
|
||||
location: string;
|
||||
notes: string;
|
||||
remindMinutesBefore: number | null;
|
||||
reminderOffsets: number[];
|
||||
};
|
||||
|
||||
type MobileView = "day" | "week" | "agenda";
|
||||
@@ -99,10 +101,12 @@ export function CalendarShell({
|
||||
calendars,
|
||||
events,
|
||||
defaultView = "month",
|
||||
defaultReminderOffsets = [30],
|
||||
}: {
|
||||
calendars: CalendarDto[];
|
||||
events: CalendarEventDto[];
|
||||
defaultView?: CalView;
|
||||
defaultReminderOffsets?: number[];
|
||||
}) {
|
||||
const [calendarRows, setCalendarRows] = useState(calendars);
|
||||
const [eventRows, setEventRows] = useState(events);
|
||||
@@ -160,13 +164,14 @@ export function CalendarShell({
|
||||
allDay,
|
||||
location: "",
|
||||
notes: "",
|
||||
remindMinutesBefore: 30,
|
||||
reminderOffsets: [...defaultReminderOffsets],
|
||||
});
|
||||
}
|
||||
|
||||
function openExistingEvent({ event }: EventClickArg) {
|
||||
const row = eventRows.find((item) => item.id === event.id);
|
||||
if (!row) return;
|
||||
const eventId = row.id;
|
||||
setSelectedEvent({
|
||||
id: row.id,
|
||||
calendarId: row.calendarId,
|
||||
@@ -176,7 +181,12 @@ export function CalendarShell({
|
||||
allDay: row.allDay,
|
||||
location: row.location ?? "",
|
||||
notes: row.notes ?? "",
|
||||
remindMinutesBefore: null,
|
||||
reminderOffsets: [],
|
||||
});
|
||||
void getEventReminderOffsets(eventId).then((offsets) => {
|
||||
setSelectedEvent((current) =>
|
||||
current?.id === eventId ? { ...current, reminderOffsets: offsets } : current,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -199,7 +209,11 @@ export function CalendarShell({
|
||||
|
||||
startTransition(async () => {
|
||||
if (eventId) {
|
||||
await updateEvent({ id: eventId, ...payload });
|
||||
await updateEvent({
|
||||
id: eventId,
|
||||
...payload,
|
||||
reminderOffsets: selectedEvent.reminderOffsets,
|
||||
});
|
||||
setLastCalendarId(payload.calendarId);
|
||||
setEventRows((current) =>
|
||||
current.map((event) =>
|
||||
@@ -220,7 +234,7 @@ export function CalendarShell({
|
||||
} else {
|
||||
const created = await createEvent({
|
||||
...payload,
|
||||
remindMinutesBefore: selectedEvent.remindMinutesBefore,
|
||||
reminderOffsets: selectedEvent.reminderOffsets,
|
||||
});
|
||||
setLastCalendarId(payload.calendarId);
|
||||
setEventRows((current) => [...current, created]);
|
||||
@@ -538,22 +552,11 @@ export function CalendarShell({
|
||||
placeholder="Add details…"
|
||||
/>
|
||||
</div>
|
||||
{!selectedEvent.id && (
|
||||
<label className="flex items-center gap-2 text-sm">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="size-4 cursor-pointer"
|
||||
checked={selectedEvent.remindMinutesBefore !== null}
|
||||
onChange={(e) =>
|
||||
setSelectedEvent({
|
||||
...selectedEvent,
|
||||
remindMinutesBefore: e.target.checked ? 30 : null,
|
||||
})
|
||||
}
|
||||
/>
|
||||
Remind me 30 min before
|
||||
</label>
|
||||
)}
|
||||
<ReminderPicker
|
||||
offsets={selectedEvent.reminderOffsets}
|
||||
onChange={(reminderOffsets) => setSelectedEvent({ ...selectedEvent, reminderOffsets })}
|
||||
disabled={isPending}
|
||||
/>
|
||||
<div className="flex items-center justify-between gap-2 pt-2">
|
||||
<div className="flex gap-2">
|
||||
{selectedEvent.id && (
|
||||
|
||||
Reference in New Issue
Block a user