T08b: Frontend Calendar UI (month view, kanban, ICS, resources, sharing)

- 8 calendar components (MonthView, KanbanBoard, AppointmentModal, TaskDetailPanel, IcsControls, ResourceBooking, SharingSettings + API client)

- 2 pages (/calendar, /calendar/kanban) + zustand store

- 17 vitest tests (MonthView 5, KanbanBoard 6, AppointmentModal 6) all passing

- i18n: calendar namespace in en/de (104 lines each, +exportSuccess key)

- TS strict mode pass, npm run build pass
This commit is contained in:
A0-Orchestrator
2026-06-30 11:35:08 +02:00
parent f9b19bb777
commit 7350739554
17 changed files with 2837 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
/**
* Calendar plugin UI store (zustand).
*
* Holds the currently selected month/week offset, the list of calendars the
* user owns / can write to, and the active calendar selection used by the
* Month view, Kanban view and ICS controls.
*
* Server state (entries, kanban board) lives in the API hooks layer — this
* store only carries view/UI selection state.
*/
import { create } from 'zustand';
import type { Calendar } from '@/api/calendar';
export interface CalendarState {
/** First day of the visible month (UTC midnight). */
visibleMonth: Date;
/** Selected calendar id (null = first available). */
activeCalendarId: string | null;
/** Cached calendar list (filled by fetchCalendars()). */
calendars: Calendar[];
setVisibleMonth: (date: Date) => void;
goToNextMonth: () => void;
goToPrevMonth: () => void;
goToToday: () => void;
setActiveCalendarId: (id: string | null) => void;
setCalendars: (calendars: Calendar[]) => void;
}
function startOfMonthUtc(d: Date): Date {
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1));
}
export const useCalendarStore = create<CalendarState>((set) => ({
visibleMonth: startOfMonthUtc(new Date()),
activeCalendarId: null,
calendars: [],
setVisibleMonth: (date) => set({ visibleMonth: startOfMonthUtc(date) }),
goToNextMonth: () =>
set((s) => {
const next = new Date(s.visibleMonth);
next.setUTCMonth(next.getUTCMonth() + 1);
return { visibleMonth: next };
}),
goToPrevMonth: () =>
set((s) => {
const next = new Date(s.visibleMonth);
next.setUTCMonth(next.getUTCMonth() - 1);
return { visibleMonth: next };
}),
goToToday: () => set({ visibleMonth: startOfMonthUtc(new Date()) }),
setActiveCalendarId: (id) => set({ activeCalendarId: id }),
setCalendars: (calendars) => set({ calendars }),
}));