Add scheduling calendar integration and WebUI
This commit is contained in:
210
webui/src/api/scheduling.ts
Normal file
210
webui/src/api/scheduling.ts
Normal file
@@ -0,0 +1,210 @@
|
||||
import { apiFetch, type ApiSettings } from "@govoplan/core-webui";
|
||||
|
||||
export type SchedulingStatus = "draft" | "collecting" | "closed" | "decided" | "handed_off" | "cancelled" | "archived";
|
||||
|
||||
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;
|
||||
freebusy_checked_at?: string | null;
|
||||
freebusy_status?: string | null;
|
||||
freebusy_conflicts: Record<string, unknown>[];
|
||||
tentative_hold_event_id?: string | null;
|
||||
metadata?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type SchedulingParticipant = {
|
||||
id: string;
|
||||
respondent_id?: string | null;
|
||||
display_name?: string | null;
|
||||
email?: string | null;
|
||||
participant_type: string;
|
||||
required: boolean;
|
||||
status: string;
|
||||
poll_invitation_id?: string | null;
|
||||
invitation_token?: string | null;
|
||||
last_invited_at?: string | null;
|
||||
responded_at?: string | null;
|
||||
metadata?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type SchedulingRequest = {
|
||||
id: string;
|
||||
tenant_id: string;
|
||||
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;
|
||||
calendar_integration_enabled: boolean;
|
||||
calendar_id?: string | null;
|
||||
calendar_freebusy_enabled: boolean;
|
||||
calendar_hold_enabled: boolean;
|
||||
create_calendar_event_on_decision: boolean;
|
||||
calendar_event_id?: string | null;
|
||||
handed_off_at?: string | null;
|
||||
cancelled_at?: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
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<string, unknown>;
|
||||
};
|
||||
|
||||
export type SchedulingParticipantPayload = {
|
||||
respondent_id?: string | null;
|
||||
display_name?: string | null;
|
||||
email?: string | null;
|
||||
participant_type?: "internal" | "external" | "resource";
|
||||
required?: boolean;
|
||||
metadata?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
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";
|
||||
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<string, unknown>;
|
||||
};
|
||||
|
||||
export type SchedulingDecisionPayload = {
|
||||
slot_id?: string | null;
|
||||
poll_option_id?: string | null;
|
||||
handoff_to_calendar?: boolean | null;
|
||||
};
|
||||
|
||||
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<string, unknown>;
|
||||
error?: string | null;
|
||||
sent_at?: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type SchedulingNotificationListResponse = { notifications: SchedulingNotification[] };
|
||||
|
||||
export type SchedulingPollOptionResult = {
|
||||
option_id: string;
|
||||
option_key: string;
|
||||
label: string;
|
||||
count: number;
|
||||
score: number;
|
||||
values: Record<string, number>;
|
||||
};
|
||||
|
||||
export type SchedulingSummaryResponse = {
|
||||
request: SchedulingRequest;
|
||||
poll_summary: {
|
||||
poll_id: string;
|
||||
kind: string;
|
||||
status: string;
|
||||
response_count: number;
|
||||
option_results: SchedulingPollOptionResult[];
|
||||
leading_option_ids: string[];
|
||||
};
|
||||
};
|
||||
|
||||
const json = (payload: unknown) => ({ method: "POST", body: JSON.stringify(payload ?? {}) });
|
||||
|
||||
export function listSchedulingRequests(settings: ApiSettings, status?: string): Promise<SchedulingRequestListResponse> {
|
||||
const query = status ? `?status=${encodeURIComponent(status)}` : "";
|
||||
return apiFetch<SchedulingRequestListResponse>(settings, `/api/v1/scheduling/requests${query}`);
|
||||
}
|
||||
|
||||
export function createSchedulingRequest(settings: ApiSettings, payload: SchedulingRequestCreatePayload): Promise<SchedulingRequest> {
|
||||
return apiFetch<SchedulingRequest>(settings, "/api/v1/scheduling/requests", json(payload));
|
||||
}
|
||||
|
||||
export function openSchedulingRequest(settings: ApiSettings, requestId: string): Promise<SchedulingStatusResponse> {
|
||||
return apiFetch<SchedulingStatusResponse>(settings, `/api/v1/scheduling/requests/${requestId}/open`, json({}));
|
||||
}
|
||||
|
||||
export function closeSchedulingRequest(settings: ApiSettings, requestId: string): Promise<SchedulingStatusResponse> {
|
||||
return apiFetch<SchedulingStatusResponse>(settings, `/api/v1/scheduling/requests/${requestId}/close`, json({}));
|
||||
}
|
||||
|
||||
export function decideSchedulingRequest(settings: ApiSettings, requestId: string, payload: SchedulingDecisionPayload): Promise<SchedulingStatusResponse> {
|
||||
return apiFetch<SchedulingStatusResponse>(settings, `/api/v1/scheduling/requests/${requestId}/decide`, json(payload));
|
||||
}
|
||||
|
||||
export function evaluateSchedulingFreeBusy(settings: ApiSettings, requestId: string): Promise<SchedulingCalendarActionResponse> {
|
||||
return apiFetch<SchedulingCalendarActionResponse>(settings, `/api/v1/scheduling/requests/${requestId}/calendar/freebusy`, json({}));
|
||||
}
|
||||
|
||||
export function createSchedulingHolds(settings: ApiSettings, requestId: string): Promise<SchedulingCalendarActionResponse> {
|
||||
return apiFetch<SchedulingCalendarActionResponse>(settings, `/api/v1/scheduling/requests/${requestId}/calendar/holds`, json({}));
|
||||
}
|
||||
|
||||
export function createSchedulingCalendarEvent(settings: ApiSettings, requestId: string): Promise<SchedulingCalendarActionResponse> {
|
||||
return apiFetch<SchedulingCalendarActionResponse>(settings, `/api/v1/scheduling/requests/${requestId}/calendar/event`, json({}));
|
||||
}
|
||||
|
||||
export function schedulingSummary(settings: ApiSettings, requestId: string): Promise<SchedulingSummaryResponse> {
|
||||
return apiFetch<SchedulingSummaryResponse>(settings, `/api/v1/scheduling/requests/${requestId}/summary`);
|
||||
}
|
||||
|
||||
export function createSchedulingNotifications(settings: ApiSettings, requestId: string, eventKind: string): Promise<SchedulingNotificationListResponse> {
|
||||
return apiFetch<SchedulingNotificationListResponse>(settings, `/api/v1/scheduling/requests/${requestId}/notifications`, json({ event_kind: eventKind }));
|
||||
}
|
||||
|
||||
export function listSchedulingNotifications(settings: ApiSettings, requestId?: string): Promise<SchedulingNotificationListResponse> {
|
||||
const query = requestId ? `?request_id=${encodeURIComponent(requestId)}` : "";
|
||||
return apiFetch<SchedulingNotificationListResponse>(settings, `/api/v1/scheduling/notifications${query}`);
|
||||
}
|
||||
Reference in New Issue
Block a user