feat: sync calendar entries to a Microsoft 365 group calendar
Adds a one-way push (ApiCore -> M365) of Aufgaben/Durchsichten/ Behandlungen/Fütterungen/Ernten/Königinnen-Zuordnungen into the calendar of a Microsoft 365 group, via the Graph API. - CalendarSyncModule (app-only client-credentials Graph auth, separate from the interactive SSO app registration; new env vars M365_CALENDAR_TENANT_ID/_CLIENT_ID/_CLIENT_SECRET). - Outbox + minute-interval cron worker (CalendarSyncOutboxEntry) instead of a synchronous push, since Graph calls are too slow/flaky to block a domain create request on; failed syncs retry up to 5 times. - Per-source-type toggle plus a group-ID field, configurable in Verwaltung > Automatisierung > Kalender-Sync (Verwaltung > Backup, an env var still wins on those fields where one applies to related config). - Hooked into Tasks/Inspections/Treatments/Feedings/Harvests/Queens at their existing create (and Task update/complete) points; entries render as all-day Graph events to avoid timezone ambiguity. - Documents the design decision in ARCHITECTURE.md (Annahme 9), since this is a net-new capability the lastenheft doesn't scope. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
28ae0a2723
commit
e7b157ec35
35 files changed
+1760
-7
No files matched your search
@@ -0,0 +1,29 @@
|
||||
import { apiFetch } from "@/lib/auth/api-client";
|
||||
import {
|
||||
CalendarSyncSettings,
|
||||
CalendarSyncStatus,
|
||||
UpdateCalendarSyncSettingsInput,
|
||||
} from "./types";
|
||||
|
||||
export function getCalendarSyncSettings(): Promise<CalendarSyncSettings> {
|
||||
return apiFetch<CalendarSyncSettings>("/calendar/sync/settings");
|
||||
}
|
||||
|
||||
export function updateCalendarSyncSettings(
|
||||
input: UpdateCalendarSyncSettingsInput,
|
||||
): Promise<CalendarSyncSettings> {
|
||||
return apiFetch<CalendarSyncSettings>("/calendar/sync/settings", {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
}
|
||||
|
||||
export function getCalendarSyncStatus(): Promise<CalendarSyncStatus> {
|
||||
return apiFetch<CalendarSyncStatus>("/calendar/sync/status");
|
||||
}
|
||||
|
||||
export function runCalendarSyncNow(): Promise<CalendarSyncStatus> {
|
||||
return apiFetch<CalendarSyncStatus>("/calendar/sync/run-now", {
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
export interface CalendarSyncSettings {
|
||||
id: string;
|
||||
enabled: boolean;
|
||||
groupId: string | null;
|
||||
syncTask: boolean;
|
||||
syncInspection: boolean;
|
||||
syncTreatment: boolean;
|
||||
syncFeeding: boolean;
|
||||
syncHarvest: boolean;
|
||||
syncQueenAssignment: boolean;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface UpdateCalendarSyncSettingsInput {
|
||||
enabled?: boolean;
|
||||
groupId?: string;
|
||||
syncTask?: boolean;
|
||||
syncInspection?: boolean;
|
||||
syncTreatment?: boolean;
|
||||
syncFeeding?: boolean;
|
||||
syncHarvest?: boolean;
|
||||
syncQueenAssignment?: boolean;
|
||||
}
|
||||
|
||||
export interface CalendarSyncStatus {
|
||||
configured: boolean;
|
||||
enabled: boolean;
|
||||
groupId: string | null;
|
||||
pendingCount: number;
|
||||
failedCount: number;
|
||||
syncedCount: number;
|
||||
}
|
||||
Reference in new issue
Block a user