Files
govoplan-forms-runtime/webui/src/api/formsRuntime.ts
T
2026-08-01 20:57:26 +02:00

273 lines
8.0 KiB
TypeScript

import { apiFetch, apiPath, type ApiSettings } from "@govoplan/core-webui";
export type InstitutionalReference = {
kind: string;
owner_module: string;
object_id: string;
tenant_id: string;
version?: string | null;
valid_at?: string | null;
label?: string | null;
};
export type EvidenceReference = {
kind: string;
owner_module: string;
evidence_id: string;
tenant_id: string;
version?: string | null;
};
export type FormCondition =
| { kind: "predicate"; field_key: string; operator: string; value?: unknown }
| { kind: "all" | "any" | "not"; conditions: FormCondition[] };
export type FormFieldDefinition = {
key: string;
label: string;
value_type: "text" | "multiline_text" | "integer" | "number" | "boolean" | "date" | "datetime" | "email" | "choice" | "multi_choice" | "object" | "list";
required: boolean;
help_text?: string | null;
options: string[];
constraints: Record<string, unknown>;
default_value?: unknown;
visibility_condition?: FormCondition | null;
};
export type FormDefinition = {
reference: InstitutionalReference;
key: string;
temporal: { revision: string; recorded_at?: string | null };
title: string;
description?: string | null;
fields: FormFieldDefinition[];
publication_state: "draft" | "published" | "retired";
allow_drafts: boolean;
max_attachments: number;
signature_requirement: "none" | "optional" | "required";
policy_refs: string[];
handoff_kinds: string[];
pages?: Array<{
key: string;
title: string;
description?: string | null;
visibility_condition?: FormCondition | null;
sections: Array<{
key: string;
title: string;
description?: string | null;
field_keys: string[];
visibility_condition?: FormCondition | null;
}>;
}>;
fallback_locale?: string | null;
localizations?: Array<{
locale: string;
title?: string | null;
description?: string | null;
field_labels: Record<string, string>;
field_help_texts: Record<string, string>;
option_labels: Record<string, Record<string, string>>;
page_titles: Record<string, string>;
section_titles: Record<string, string>;
}>;
};
export type ValidationResult = {
field?: string | null;
severity: "warning" | "error";
code: string;
message: string;
};
export type FormInstance = {
reference: InstitutionalReference;
tenant_id: string;
instance_id: string;
revision: number;
status: string;
definition_ref: InstitutionalReference;
values: Record<string, unknown>;
validation_results: ValidationResult[];
attachment_refs: EvidenceReference[];
signature_refs: EvidenceReference[];
handoff_refs: InstitutionalReference[];
service_ref?: InstitutionalReference | null;
receipt_id?: string | null;
recorded_at: string;
change_reason: string;
created_by: string;
changed_by: string;
replayed: boolean;
};
export type FormInstanceEvent = {
event_id: string;
event_type: string;
instance_revision: number;
status: string;
occurred_at: string;
actor_id: string;
payload: Record<string, unknown>;
};
export type FormHandoff = {
effect_id: string;
instance_id: string;
instance_revision: number;
binding_kind: "case" | "workflow";
binding_reference: string;
provider_capability: string;
state: "requested" | "accepted" | "rejected" | "outcome_unknown" | "reconciled" | "compensated";
attempt_count: number;
requested_at: string;
resolved_at?: string | null;
target_ref?: InstitutionalReference | null;
href?: string | null;
evidence: EvidenceReference[];
last_error?: string | null;
metadata: Record<string, unknown>;
};
export function listFormInstances(
settings: ApiSettings,
options: { statuses?: string[]; definitionId?: string; offset?: number; limit?: number } = {},
signal?: AbortSignal
): Promise<{ instances: FormInstance[]; total: number; offset: number; limit: number }> {
return apiFetch(settings, apiPath("/api/v1/forms-runtime/instances", {
status: options.statuses,
definition_id: options.definitionId,
offset: options.offset ?? 0,
limit: options.limit ?? 100
}), { signal });
}
export function getFormInstance(
settings: ApiSettings,
instanceId: string,
signal?: AbortSignal
): Promise<FormInstance> {
return apiFetch(settings, `/api/v1/forms-runtime/instances/${encodeURIComponent(instanceId)}`, { signal });
}
export function getFormDefinition(
settings: ApiSettings,
instanceId: string,
signal?: AbortSignal
): Promise<FormDefinition> {
return apiFetch(
settings,
`/api/v1/forms-runtime/instances/${encodeURIComponent(instanceId)}/definition`,
{ signal }
);
}
export function getFormInstanceHistory(
settings: ApiSettings,
instanceId: string,
signal?: AbortSignal
): Promise<{ revisions: FormInstance[] }> {
return apiFetch(settings, `/api/v1/forms-runtime/instances/${encodeURIComponent(instanceId)}/history`, { signal });
}
export function getFormInstanceEvents(
settings: ApiSettings,
instanceId: string,
signal?: AbortSignal
): Promise<{ events: FormInstanceEvent[] }> {
return apiFetch(settings, `/api/v1/forms-runtime/instances/${encodeURIComponent(instanceId)}/events`, { signal });
}
export function saveFormDraft(
settings: ApiSettings,
instance: FormInstance,
values: Record<string, unknown>,
changeReason: string
): Promise<FormInstance> {
return apiFetch(settings, `/api/v1/forms-runtime/instances/${encodeURIComponent(instance.instance_id)}`, {
method: "PATCH",
body: JSON.stringify({
expected_revision: instance.revision,
values,
attachment_refs: instance.attachment_refs,
signature_refs: instance.signature_refs,
idempotency_key: crypto.randomUUID(),
recorded_at: new Date().toISOString(),
change_reason: changeReason
})
});
}
export function submitFormInstance(
settings: ApiSettings,
instance: FormInstance,
values: Record<string, unknown>
): Promise<FormInstance> {
return apiFetch(settings, `/api/v1/forms-runtime/instances/${encodeURIComponent(instance.instance_id)}/submit`, {
method: "POST",
body: JSON.stringify({
expected_revision: instance.revision,
values,
attachment_refs: instance.attachment_refs,
signature_refs: instance.signature_refs,
idempotency_key: crypto.randomUUID(),
recorded_at: new Date().toISOString()
})
});
}
export function listFormHandoffs(
settings: ApiSettings,
instanceId: string,
signal?: AbortSignal
): Promise<{ handoffs: FormHandoff[] }> {
return apiFetch(settings, `/api/v1/forms-runtime/instances/${encodeURIComponent(instanceId)}/handoffs`, { signal });
}
export function startFormHandoff(
settings: ApiSettings,
instance: FormInstance,
bindingKind: "case" | "workflow",
bindingReference?: string
): Promise<{ handoff: FormHandoff; instance?: FormInstance | null }> {
return apiFetch(settings, `/api/v1/forms-runtime/instances/${encodeURIComponent(instance.instance_id)}/handoffs/native`, {
method: "POST",
body: JSON.stringify({
expected_revision: instance.revision,
binding_kind: bindingKind,
binding_reference: bindingReference?.trim() || null,
idempotency_key: crypto.randomUUID(),
requested_at: new Date().toISOString()
})
});
}
export function actOnFormHandoff(
settings: ApiSettings,
instanceId: string,
effectId: string,
action: "retry" | "reconcile"
): Promise<{ handoff: FormHandoff; instance?: FormInstance | null }> {
return apiFetch(settings, `/api/v1/forms-runtime/instances/${encodeURIComponent(instanceId)}/handoffs/${encodeURIComponent(effectId)}/${action}`, {
method: "POST",
body: JSON.stringify({ recorded_at: new Date().toISOString() })
});
}
export function compensateFormHandoff(
settings: ApiSettings,
instanceId: string,
effectId: string,
changeReason: string
): Promise<{ handoff: FormHandoff }> {
return apiFetch(settings, `/api/v1/forms-runtime/instances/${encodeURIComponent(instanceId)}/handoffs/${encodeURIComponent(effectId)}/compensate`, {
method: "POST",
body: JSON.stringify({
recorded_at: new Date().toISOString(),
confirmed_absent: true,
change_reason: changeReason
})
});
}