feat: implement committee decision workspace

This commit is contained in:
2026-08-01 17:48:25 +02:00
parent 232329ac52
commit 758b59ca03
28 changed files with 4909 additions and 32 deletions
+115
View File
@@ -0,0 +1,115 @@
import { apiFetch, apiPath, type ApiSettings } from "@govoplan/core-webui";
export type CommitteeObjectKind = "body" | "meeting" | "agenda_item" | "vote" | "minute";
export type CommitteeRecord = {
tenant_id: string;
object_kind: CommitteeObjectKind;
object_id: string;
revision: number;
state: string;
title: string;
parent_id?: string | null;
recorded_at: string;
change_reason: string;
attributes: Record<string, unknown>;
context?: Record<string, unknown> | null;
evidence: Array<Record<string, unknown>>;
record_refs: Array<Record<string, unknown>>;
};
export type CommitteeRecordList = {
records: CommitteeRecord[];
total: number;
offset: number;
limit: number;
};
export function listCommitteeRecords(
settings: ApiSettings,
kind: CommitteeObjectKind,
options: {
parentId?: string;
query?: string;
states?: string[];
limit?: number;
} = {},
signal?: AbortSignal
): Promise<CommitteeRecordList> {
return apiFetch<CommitteeRecordList>(
settings,
apiPath(`/api/v1/committee/workspace/${kind}`, {
parent_id: options.parentId,
query: options.query,
state: options.states,
limit: options.limit ?? 200
}),
{ signal }
);
}
export function saveCommitteeRecord(
settings: ApiSettings,
record: CommitteeRecord,
expectedRevision?: number
): Promise<CommitteeRecord> {
return apiFetch<CommitteeRecord>(
settings,
`/api/v1/committee/workspace/${record.object_kind}`,
{
method: "POST",
body: JSON.stringify({
record,
idempotency_key: crypto.randomUUID(),
expected_revision: expectedRevision
})
}
);
}
export function committeeRecordHistory(
settings: ApiSettings,
record: CommitteeRecord,
signal?: AbortSignal
): Promise<{ revisions: CommitteeRecord[] }> {
return apiFetch(
settings,
`/api/v1/committee/workspace/${record.object_kind}/${encodeURIComponent(record.object_id)}/history`,
{ signal }
);
}
export function finalizeProviderBallot(
settings: ApiSettings,
record: CommitteeRecord,
input: {
providerBallotRef: string;
approvalId: string;
changeReason: string;
}
): Promise<CommitteeRecord> {
const providerId = String(record.attributes.provider_id ?? "").trim();
return apiFetch<CommitteeRecord>(
settings,
`/api/v1/committee/workspace/vote/${encodeURIComponent(record.object_id)}/finalize-provider`,
{
method: "POST",
body: JSON.stringify({
provider_id: providerId,
provider_ballot_ref: input.providerBallotRef.trim(),
approval_ref: {
kind: "approval",
owner_module: "approvals",
object_id: input.approvalId.trim(),
tenant_id: record.tenant_id,
version: "1"
},
expected_revision: record.revision,
recorded_at: new Date().toISOString(),
change_reason: input.changeReason.trim(),
idempotency_key: crypto.randomUUID()
})
}
);
}