import { useEffect, useMemo, useState } from "react"; import { Save } from "lucide-react"; import { FormGrid, ContentGrid, Button, Card, DismissibleAlert, DocumentationHelpLink, FormField, ToggleSwitch, useUnsavedDraftGuard, type ApiSettings, type AuthInfo } from "@govoplan/core-webui"; import { getCalendarViewPreferences, updateCalendarViewPreferences, type CalendarViewPreferences } from "../../api/calendar"; import { CALENDAR_DOCUMENTATION, CALENDAR_I18N, } from "./interfacePatterns"; type CalendarPreferenceDraft = Pick< CalendarViewPreferences, | "dim_weekends" | "dim_off_hours" | "workday_start_hour" | "workday_end_hour" | "continuous_virtualization" | "continuous_overscan_weeks" | "alternate_continuous_months" >; const FALLBACK_DRAFT: CalendarPreferenceDraft = { dim_weekends: true, dim_off_hours: true, workday_start_hour: 6, workday_end_hour: 20, continuous_virtualization: true, continuous_overscan_weeks: 6, alternate_continuous_months: true }; export default function CalendarSettingsPanel({ settings, auth }: { settings: ApiSettings; auth: AuthInfo; }) { const [loaded, setLoaded] = useState(null); const [draft, setDraft] = useState(FALLBACK_DRAFT); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [message, setMessage] = useState(""); const [tone, setTone] = useState<"success" | "warning">("success"); const dirty = useMemo( () => loaded !== null && JSON.stringify(loaded) !== JSON.stringify(draft), [draft, loaded] ); useEffect(() => { void loadPreferences(); }, [auth.user.id, settings.accessToken, settings.apiBaseUrl, settings.apiKey]); async function loadPreferences() { setLoading(true); setMessage(""); try { const response = await getCalendarViewPreferences(settings); const next = preferenceDraft(response); setLoaded(next); setDraft(next); } catch (error) { setTone("warning"); setMessage(errorText(error)); } finally { setLoading(false); } } async function savePreferences(): Promise { if (draft.workday_end_hour <= draft.workday_start_hour) { setTone("warning"); setMessage("Workday end must be after workday start."); return false; } setSaving(true); setMessage(""); try { const response = await updateCalendarViewPreferences(settings, draft); const next = preferenceDraft(response); setLoaded(next); setDraft(next); setTone("success"); setMessage("Calendar preferences saved."); window.dispatchEvent( new CustomEvent("govoplan:calendar-preferences-changed") ); return true; } catch (error) { setTone("warning"); setMessage(errorText(error)); return false; } finally { setSaving(false); } } const disabled = loading || saving; const saveDisabledReason = loading ? CALENDAR_I18N.loading : saving ? CALENDAR_I18N.saving : !dirty ? CALENDAR_I18N.noChanges : undefined; useUnsavedDraftGuard({ dirty, onSave: savePreferences, onDiscard: () => setDraft(loaded ?? FALLBACK_DRAFT) }); return ( }> setDraft((current) => ({ ...current, dim_weekends })) } /> setDraft((current) => ({ ...current, dim_off_hours })) } /> setDraft((current) => ({ ...current, workday_start_hour: Number(event.target.value) })) } /> setDraft((current) => ({ ...current, workday_end_hour: Number(event.target.value) })) } /> setDraft((current) => ({ ...current, continuous_virtualization })) } /> setDraft((current) => ({ ...current, continuous_overscan_weeks: Number(event.target.value) })) } /> setDraft((current) => ({ ...current, alternate_continuous_months })) } />
{message && ( {message} )}
); } function preferenceDraft( preferences: CalendarViewPreferences ): CalendarPreferenceDraft { return { dim_weekends: preferences.dim_weekends, dim_off_hours: preferences.dim_off_hours, workday_start_hour: preferences.workday_start_hour, workday_end_hour: preferences.workday_end_hour, continuous_virtualization: preferences.continuous_virtualization, continuous_overscan_weeks: preferences.continuous_overscan_weeks, alternate_continuous_months: preferences.alternate_continuous_months }; } function errorText(error: unknown): string { return error instanceof Error ? error.message : "Calendar preferences could not be loaded."; }