153 lines
4.0 KiB
TypeScript
153 lines
4.0 KiB
TypeScript
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()
|
|
})
|
|
}
|
|
);
|
|
}
|
|
|
|
export async function finalizeVotingBallot(
|
|
settings: ApiSettings,
|
|
record: CommitteeRecord,
|
|
input: {
|
|
approvalId: string;
|
|
changeReason: string;
|
|
}
|
|
): Promise<CommitteeRecord> {
|
|
const ballotId = String(record.attributes.voting_ballot_id ?? "").trim();
|
|
const ballot = await apiFetch<{ revision: number }>(
|
|
settings,
|
|
`/api/v1/voting/${encodeURIComponent(ballotId)}`
|
|
);
|
|
return apiFetch<CommitteeRecord>(
|
|
settings,
|
|
`/api/v1/committee/workspace/vote/${encodeURIComponent(record.object_id)}/finalize-voting`,
|
|
{
|
|
method: "POST",
|
|
body: JSON.stringify({
|
|
voting_ballot_id: ballotId,
|
|
voting_expected_revision: ballot.revision,
|
|
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()
|
|
})
|
|
}
|
|
);
|
|
}
|