import { apiFetch, type ApiSettings, type PeoplePickerSearchGroup } from "@govoplan/core-webui"; export type SchedulingStatus = "draft" | "collecting" | "closed" | "decided" | "handed_off" | "cancelled" | "archived"; export type SchedulingParticipantVisibility = "aggregates_only" | "names_and_statuses"; export type SchedulingCandidateSlot = { id: string; poll_option_id?: string | null; label: string; description?: string | null; start_at: string; end_at: string; timezone: string; location?: string | null; position: number; revision: string; freebusy_checked_at?: string | null; freebusy_status?: string | null; freebusy_conflicts: Record[]; tentative_hold_event_id?: string | null; metadata?: Record; }; export type SchedulingParticipant = { id: string; revision?: string | null; is_current_participant: boolean; respondent_id?: string | null; display_name?: string | null; email?: string | null; participant_type: string | null; required: boolean | null; status: string; poll_invitation_id?: string | null; invitation_token?: string | null; last_invited_at?: string | null; responded_at?: string | null; metadata?: Record; }; export type SchedulingParticipantAggregate = { total: number; status_counts: Record; }; export type SchedulingParticipantVisibilityDecision = { requested_visibility: SchedulingParticipantVisibility; effective_visibility: SchedulingParticipantVisibility; policy_applied: boolean; reason?: string | null; source_path: Record[]; details: Record; }; export type SchedulingRequest = { id: string; tenant_id: string | null; title: string; description?: string | null; location?: string | null; timezone: string; status: SchedulingStatus; poll_id?: string | null; selected_slot_id?: string | null; organizer_user_id?: string | null; deadline_at?: string | null; allow_external_participants: boolean; allow_participant_updates: boolean; result_visibility: string; participant_visibility: SchedulingParticipantVisibility; effective_participant_visibility: SchedulingParticipantVisibility; participant_aggregate: SchedulingParticipantAggregate; participant_visibility_decision: SchedulingParticipantVisibilityDecision; notify_on_answers: boolean; single_choice: boolean; max_participants_per_option: number | null; allow_maybe: boolean; allow_comments: boolean; participant_email_required: boolean; anonymous_password_protection_enabled: boolean; public_participation_policy_enforcement_available: boolean | null; public_participation_policy_enforcement_reason?: string | null; participant_invitation_delivery_available: boolean | null; calendar_integration_enabled: boolean | null; calendar_id?: string | null; calendar_freebusy_enabled: boolean | null; calendar_hold_enabled: boolean | null; create_calendar_event_on_decision: boolean | null; calendar_event_id?: string | null; handed_off_at?: string | null; cancelled_at?: string | null; cancellation_notice_until?: string | null; created_at: string; updated_at: string; metadata?: Record; slots: SchedulingCandidateSlot[]; participants: SchedulingParticipant[]; }; export type SchedulingRequestListResponse = { requests: SchedulingRequest[] }; export type SchedulingStatusResponse = { request: SchedulingRequest }; export type SchedulingCandidateSlotPayload = { label?: string | null; description?: string | null; start_at: string; end_at: string; timezone?: string | null; location?: string | null; metadata?: Record; }; export type SchedulingParticipantPayload = { respondent_id?: string | null; display_name?: string | null; email?: string | null; participant_type?: "internal" | "external" | "resource"; required?: boolean; metadata?: Record; }; export type SchedulingRequestCreatePayload = { title: string; description?: string | null; location?: string | null; timezone?: string; status?: "draft" | "collecting"; deadline_at?: string | null; allow_external_participants?: boolean; allow_participant_updates?: boolean; result_visibility?: "organizer" | "after_response" | "after_close" | "public"; participant_visibility?: SchedulingParticipantVisibility; notify_on_answers?: boolean; single_choice?: boolean; max_participants_per_option?: number | null; allow_maybe?: boolean; allow_comments?: boolean; participant_email_required?: boolean; anonymous_password_protection_enabled?: boolean; anonymous_password?: string; calendar?: { enabled?: boolean; calendar_id?: string | null; freebusy_enabled?: boolean; tentative_holds_enabled?: boolean; create_event_on_decision?: boolean; }; slots: SchedulingCandidateSlotPayload[]; participants?: SchedulingParticipantPayload[]; create_participant_invitations?: boolean; metadata?: Record; }; export type SchedulingRequestUpdatePayload = Omit< SchedulingRequestCreatePayload, "timezone" | "status" | "slots" | "participants" > & { slots?: SchedulingCandidateSlotReconcilePayload[]; participants?: SchedulingParticipantReconcilePayload[]; }; export type SchedulingCandidateSlotReconcilePayload = SchedulingCandidateSlotPayload & { id?: string; revision?: string; }; export type SchedulingParticipantReconcilePayload = SchedulingParticipantPayload & { id?: string; revision?: string; }; export type SchedulingCandidateSlotUpdatePayload = { label?: string | null; description?: string | null; start_at?: string | null; end_at?: string | null; timezone?: string | null; location?: string | null; metadata?: Record | null; }; export type SchedulingDecisionPayload = { slot_id?: string | null; poll_option_id?: string | null; handoff_to_calendar?: boolean | null; }; export type SchedulingAvailabilityValue = "available" | "maybe" | "unavailable"; export type SchedulingAvailabilityPayload = { answers: Array<{ slot_id: string; value: SchedulingAvailabilityValue; option_revision: string; }>; comment?: string | null; }; export type SchedulingAvailabilityResponse = { request_id: string; participant_id: string; has_response: boolean; submitted_at?: string | null; comment?: string | null; answers: Array<{ slot_id: string; value: SchedulingAvailabilityValue; }>; }; export type SchedulingPublicCandidateSlot = { id: string; label: string; description?: string | null; start_at: string; end_at: string; timezone: string; location?: string | null; position: number; revision: string; }; export type SchedulingPublicParticipationResponse = { request_id: string; title: string; description?: string | null; location?: string | null; timezone: string; status: SchedulingStatus; deadline_at?: string | null; cancelled_at?: string | null; cancellation_notice_until?: string | null; cancellation_notice_only: boolean; participant_email_required: boolean; anonymous_password_required: boolean; single_choice: boolean; max_participants_per_option: number | null; allow_maybe: boolean; allow_comments: boolean; allow_participant_updates: boolean; has_response: boolean; submitted_at?: string | null; answers: Array<{ slot_id: string; value: SchedulingAvailabilityValue; }>; comment?: string | null; replayed: boolean; slots: SchedulingPublicCandidateSlot[]; }; export type SchedulingPublicParticipationAccessPayload = { participant_email?: string | null; password?: string | null; }; export type SchedulingPublicParticipationSubmitPayload = SchedulingPublicParticipationAccessPayload & { answers: SchedulingAvailabilityPayload["answers"]; comment?: string | null; idempotency_key?: string; }; export type SchedulingCalendarActionResponse = { request: SchedulingRequest; created_event_ids: string[]; updated_slot_ids: string[]; warnings: string[]; }; export type SchedulingNotification = { id: string; request_id: string; participant_id?: string | null; event_kind: string; channel: string; recipient?: string | null; status: string; payload: Record; error?: string | null; sent_at?: string | null; created_at: string; updated_at: string; metadata?: Record; }; export type SchedulingNotificationListResponse = { notifications: SchedulingNotification[] }; export type SchedulingInvitationAction = "copy" | "send" | "revoke"; export type SchedulingInvitationActionResponse = { participant_id: string; action: SchedulingInvitationAction; status: string; action_url?: string | null; issued_at?: string | null; replayed: boolean; notification?: SchedulingNotification | null; }; export type SchedulingPollOptionResult = { option_id: string; option_key: string; label: string; count: number; score: number; values: Record; }; export type SchedulingSummaryResponse = { request: SchedulingRequest; poll_summary: { poll_id: string; kind: string; status: string; response_count: number; option_results: SchedulingPollOptionResult[]; leading_option_ids: string[]; }; }; export type SchedulingPeopleSearchResponse = { groups: PeoplePickerSearchGroup[]; }; const json = (payload: unknown) => ({ method: "POST", body: JSON.stringify(payload ?? {}) }); export async function searchSchedulingPeople( settings: ApiSettings, query: string, limit = 25, signal?: AbortSignal ): Promise { const params = new URLSearchParams({ query, limit: String(limit) }); const response = await apiFetch( settings, `/api/v1/scheduling/people?${params.toString()}`, { signal } ); return response.groups; } export function listSchedulingRequests(settings: ApiSettings, status?: string): Promise { const query = status ? `?status=${encodeURIComponent(status)}` : ""; return apiFetch(settings, `/api/v1/scheduling/requests${query}`); } export function createSchedulingRequest(settings: ApiSettings, payload: SchedulingRequestCreatePayload): Promise { return apiFetch(settings, "/api/v1/scheduling/requests", json(payload)); } export function updateSchedulingRequest( settings: ApiSettings, requestId: string, payload: SchedulingRequestUpdatePayload ): Promise { return apiFetch(settings, `/api/v1/scheduling/requests/${requestId}`, { method: "PATCH", body: JSON.stringify(payload) }); } export function updateSchedulingCandidateSlot( settings: ApiSettings, requestId: string, slotId: string, payload: SchedulingCandidateSlotUpdatePayload ): Promise { return apiFetch(settings, `/api/v1/scheduling/requests/${requestId}/slots/${slotId}`, { method: "PATCH", body: JSON.stringify(payload) }); } export function submitSchedulingAvailability( settings: ApiSettings, requestId: string, payload: SchedulingAvailabilityPayload ): Promise { return apiFetch( settings, `/api/v1/scheduling/requests/${requestId}/responses`, json(payload) ); } export function getSchedulingAvailabilityResponse( settings: ApiSettings, requestId: string ): Promise { return apiFetch( settings, `/api/v1/scheduling/requests/${requestId}/responses/me` ); } export function getPublicSchedulingParticipation( settings: ApiSettings, requestId: string, token: string, payload: SchedulingPublicParticipationAccessPayload ): Promise { return apiFetch( settings, `/api/v1/scheduling/public/${encodeURIComponent(requestId)}/${encodeURIComponent(token)}`, json(payload) ); } export function submitPublicSchedulingParticipation( settings: ApiSettings, requestId: string, token: string, payload: SchedulingPublicParticipationSubmitPayload ): Promise { return apiFetch( settings, `/api/v1/scheduling/public/${encodeURIComponent(requestId)}/${encodeURIComponent(token)}/responses`, json(payload) ); } export function openSchedulingRequest(settings: ApiSettings, requestId: string): Promise { return apiFetch(settings, `/api/v1/scheduling/requests/${requestId}/open`, json({})); } export function closeSchedulingRequest(settings: ApiSettings, requestId: string): Promise { return apiFetch(settings, `/api/v1/scheduling/requests/${requestId}/close`, json({})); } export function decideSchedulingRequest(settings: ApiSettings, requestId: string, payload: SchedulingDecisionPayload): Promise { return apiFetch(settings, `/api/v1/scheduling/requests/${requestId}/decide`, json(payload)); } export function evaluateSchedulingFreeBusy(settings: ApiSettings, requestId: string): Promise { return apiFetch(settings, `/api/v1/scheduling/requests/${requestId}/calendar/freebusy`, json({})); } export function createSchedulingHolds(settings: ApiSettings, requestId: string): Promise { return apiFetch(settings, `/api/v1/scheduling/requests/${requestId}/calendar/holds`, json({})); } export function createSchedulingCalendarEvent(settings: ApiSettings, requestId: string): Promise { return apiFetch(settings, `/api/v1/scheduling/requests/${requestId}/calendar/event`, json({})); } export function schedulingSummary(settings: ApiSettings, requestId: string): Promise { return apiFetch(settings, `/api/v1/scheduling/requests/${requestId}/summary`); } export function createSchedulingNotifications(settings: ApiSettings, requestId: string, eventKind: string): Promise { return apiFetch(settings, `/api/v1/scheduling/requests/${requestId}/notifications`, json({ event_kind: eventKind })); } export function listSchedulingNotifications(settings: ApiSettings, requestId?: string): Promise { const query = requestId ? `?request_id=${encodeURIComponent(requestId)}` : ""; return apiFetch(settings, `/api/v1/scheduling/notifications${query}`); } export function issueSchedulingParticipantInvitation( settings: ApiSettings, requestId: string, participantId: string, participantRevision: string, action: Exclude ): Promise { return apiFetch( settings, `/api/v1/scheduling/requests/${encodeURIComponent(requestId)}/participants/${encodeURIComponent(participantId)}/invitation`, json({ action, participant_revision: participantRevision }) ); } export function revokeSchedulingParticipantInvitation( settings: ApiSettings, requestId: string, participantId: string, participantRevision: string ): Promise { return apiFetch( settings, `/api/v1/scheduling/requests/${encodeURIComponent(requestId)}/participants/${encodeURIComponent(participantId)}/invitation`, { method: "DELETE", body: JSON.stringify({ participant_revision: participantRevision }) } ); }