Files
govoplan-tasks/webui/src/api/tasks.ts
T

147 lines
3.7 KiB
TypeScript

import { apiFetch, type ApiSettings } from "@govoplan/core-webui";
export type WorkStatus =
| "open"
| "in_progress"
| "deferred"
| "blocked"
| "completed"
| "cancelled";
export type WorkPriority = "low" | "normal" | "high" | "urgent";
export type WorkAssignment = {
kind: "account" | "group" | "role" | "function" | "function_assignment" | "anyone";
id: string;
label?: string | null;
};
export type WorkSource = {
module_id: string;
resource_type: string;
resource_id: string;
revision?: string | null;
url?: string | null;
label?: string | null;
};
export type WorkItem = {
id: string;
provider_id: string;
owner_module: string;
tenant_id: string;
title: string;
status: WorkStatus;
priority: WorkPriority;
summary?: string | null;
required_action?: string | null;
action_url?: string | null;
due_at?: string | null;
deferred_until?: string | null;
assignments: WorkAssignment[];
sources: WorkSource[];
provenance: Record<string, unknown>;
metadata: Record<string, unknown>;
revision: string;
etag?: string | null;
created_at?: string | null;
updated_at?: string | null;
};
export type WorkProviderDiagnostic = {
provider_id: string;
owner_module: string;
code: string;
message: string;
};
export type WorkListResponse = {
items: WorkItem[];
total: number;
truncated: boolean;
diagnostics: WorkProviderDiagnostic[];
};
export type WorkSummary = {
total: number;
open: number;
in_progress: number;
deferred: number;
blocked: number;
overdue: number;
urgent: number;
truncated: boolean;
diagnostics: WorkProviderDiagnostic[];
};
export type TaskCreatePayload = {
title: string;
summary?: string | null;
priority: WorkPriority;
due_at?: string | null;
required_action?: string | null;
action_url?: string | null;
assignments: WorkAssignment[];
sources?: WorkSource[];
provenance?: Record<string, unknown>;
metadata?: Record<string, unknown>;
idempotency_key: string;
};
export function listWork(
settings: ApiSettings,
filters: {
statuses?: WorkStatus[];
priorities?: WorkPriority[];
providers?: string[];
modules?: string[];
q?: string;
limit?: number;
} = {}
): Promise<WorkListResponse> {
const params = new URLSearchParams();
filters.statuses?.forEach((value) => params.append("status", value));
filters.priorities?.forEach((value) => params.append("priority", value));
filters.providers?.forEach((value) => params.append("provider", value));
filters.modules?.forEach((value) => params.append("owner_module", value));
if (filters.q) params.set("q", filters.q);
if (filters.limit) params.set("limit", String(filters.limit));
const query = params.toString();
return apiFetch<WorkListResponse>(
settings,
`/api/v1/tasks${query ? `?${query}` : ""}`
);
}
export function loadWorkSummary(settings: ApiSettings): Promise<WorkSummary> {
return apiFetch<WorkSummary>(settings, "/api/v1/tasks/summary");
}
export function createTask(
settings: ApiSettings,
payload: TaskCreatePayload
): Promise<WorkItem> {
return apiFetch<WorkItem>(settings, "/api/v1/tasks", {
method: "POST",
body: JSON.stringify(payload)
});
}
export function transitionTask(
settings: ApiSettings,
task: WorkItem,
action: "start" | "complete" | "defer" | "reopen" | "cancel",
deferredUntil?: string | null
): Promise<WorkItem> {
if (!task.etag) throw new Error("i18n:govoplan-tasks.reason.refresh_required");
return apiFetch<WorkItem>(settings, `/api/v1/tasks/${task.id}/actions`, {
method: "POST",
headers: { "If-Match": task.etag },
body: JSON.stringify({
action,
expected_revision: Number(task.revision),
deferred_until: deferredUntil || null
})
});
}