feat: implement definition-aware forms runtime
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
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 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;
|
||||
};
|
||||
|
||||
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[];
|
||||
};
|
||||
|
||||
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 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()
|
||||
})
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user