import { useCallback } from "react"; import { CalendarClock } from "lucide-react"; import { Link } from "react-router-dom"; import { DashboardWidgetList, DismissibleAlert, LoadingFrame, StatusBadge, useDashboardWidgetData, type ApiSettings, type DashboardWidgetConfiguration } from "@govoplan/core-webui"; import { listSchedulingRequests, type SchedulingRequest } from "../../api/scheduling"; export default function SchedulingRequestsWidget({ settings, refreshKey, configuration }: { settings: ApiSettings; refreshKey: number; configuration: DashboardWidgetConfiguration; }) { const maxItems = numberSetting(configuration.maxItems, 5, 1, 12); const includeDrafts = configuration.includeDrafts === true; const load = useCallback(async () => { const response = await listSchedulingRequests(settings); return response.requests .filter( (request) => request.status === "collecting" || (includeDrafts && request.status === "draft") ) .sort(compareRequests) .slice(0, maxItems); }, [includeDrafts, maxItems, settings]); const { data: requests, loading, error } = useDashboardWidgetData( load, refreshKey ); return ( {error && ( {error} )} ({ id: request.id, title: request.title, detail: responseLabel(request), meta: deadlineLabel(request), leading: ); } function compareRequests( left: SchedulingRequest, right: SchedulingRequest ): number { if (left.status !== right.status) { return left.status === "collecting" ? -1 : 1; } const leftDeadline = left.deadline_at ? new Date(left.deadline_at).getTime() : Number.POSITIVE_INFINITY; const rightDeadline = right.deadline_at ? new Date(right.deadline_at).getTime() : Number.POSITIVE_INFINITY; return ( leftDeadline - rightDeadline || new Date(right.updated_at).getTime() - new Date(left.updated_at).getTime() ); } function responseLabel(request: SchedulingRequest): string { const responded = request.participant_aggregate.status_counts.responded ?? 0; return `${responded} of ${request.participant_aggregate.total} responded`; } function deadlineLabel(request: SchedulingRequest): string { if (!request.deadline_at) return `${request.slots.length} options`; return `Due ${new Intl.DateTimeFormat(undefined, { day: "2-digit", month: "short" }).format(new Date(request.deadline_at))}`; } function numberSetting( value: unknown, fallback: number, minimum: number, maximum: number ): number { const numeric = typeof value === "number" ? value : Number(value); return Number.isFinite(numeric) ? Math.max(minimum, Math.min(maximum, Math.floor(numeric))) : fallback; }