Implement unified work inbox module
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
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
|
||||
})
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user