198 lines
5.7 KiB
TypeScript
198 lines
5.7 KiB
TypeScript
import type { ZinoClient } from "./client";
|
|
import type {
|
|
ActivityLogEntry,
|
|
Column,
|
|
DetailViewResponse,
|
|
InstanceReport,
|
|
ReportSection,
|
|
StateTransition,
|
|
TabularViewParams,
|
|
TabularViewResponse,
|
|
} from "./types";
|
|
import {
|
|
mockDelay,
|
|
mockTabularResponse,
|
|
mockInstanceReport,
|
|
mockAuditLog,
|
|
} from "./mock";
|
|
|
|
/**
|
|
* View and reporting methods — tabular record views, detail views,
|
|
* and audit trails. Maps to view-service endpoints.
|
|
*/
|
|
export class ViewService {
|
|
private client: ZinoClient;
|
|
constructor(client: ZinoClient) {
|
|
this.client = client;
|
|
}
|
|
|
|
/**
|
|
* Fetch a paginated, sortable, filterable record view.
|
|
* Maps to `GET /recordview?rv_id=…`.
|
|
*/
|
|
async getTabularView(
|
|
viewId: string,
|
|
params: TabularViewParams = {},
|
|
): Promise<TabularViewResponse> {
|
|
if (this.client.isMock) {
|
|
await mockDelay();
|
|
return mockTabularResponse(params);
|
|
}
|
|
|
|
const qs = new URLSearchParams();
|
|
qs.set("rv_id", viewId);
|
|
if (params.page) qs.set("page", String(params.page));
|
|
if (params.pageSize) qs.set("limit", String(params.pageSize));
|
|
if (params.sortBy) qs.set("sort_by", params.sortBy);
|
|
if (params.sortDir) qs.set("sort_dir", params.sortDir);
|
|
if (params.search) qs.set("search", params.search);
|
|
if (params.filters) {
|
|
for (const [k, v] of Object.entries(params.filters)) {
|
|
qs.set(`filter.${k}`, v);
|
|
}
|
|
}
|
|
|
|
const raw = await this.client.request<{
|
|
config: { fields: Array<{ field_key: string; output_label: string; data_type: string; is_filter: boolean; is_search: boolean }> };
|
|
data: Record<string, unknown>[];
|
|
pagination?: { page: number; limit: number; total_count: number; total_pages: number };
|
|
}>("GET", `/recordview?${qs.toString()}`);
|
|
|
|
const columns: Column[] = raw.config.fields
|
|
.filter((f) => f.field_key !== "")
|
|
.map((f) => ({
|
|
key: f.field_key,
|
|
label: f.output_label,
|
|
data_type: f.data_type,
|
|
sortable: true,
|
|
filterable: f.is_filter,
|
|
searchable: f.is_search,
|
|
}));
|
|
|
|
const rows = Array.isArray(raw.data) ? raw.data : [];
|
|
|
|
return {
|
|
rows,
|
|
total: raw.pagination?.total_count ?? rows.length,
|
|
totalPages: raw.pagination?.total_pages,
|
|
page: raw.pagination?.page ?? params.page ?? 1,
|
|
columns,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Fetch a detail view for a specific workflow instance.
|
|
* Maps to `GET /detailview?dv_id=…&instance_id=…`.
|
|
*/
|
|
async getDetailView(viewId: string, instanceId: string): Promise<DetailViewResponse> {
|
|
if (this.client.isMock) {
|
|
await mockDelay();
|
|
const data: Record<string, unknown> = {
|
|
instance_id: instanceId,
|
|
view_id: viewId,
|
|
status: "Open",
|
|
created_at: new Date().toISOString(),
|
|
};
|
|
return {
|
|
data,
|
|
sections: [
|
|
{
|
|
title: "Details",
|
|
fields: Object.entries(data).map(([k, v]) => ({ label: k, value: v })),
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
const raw = await this.client.request<{
|
|
config: { fields: Array<{ field_key: string; output_label: string; data_type: string }> };
|
|
data: Record<string, unknown>;
|
|
}>("GET", `/detailview?dv_id=${encodeURIComponent(viewId)}&instance_id=${encodeURIComponent(instanceId)}`);
|
|
|
|
const data = raw.data ?? {};
|
|
const fields = (raw.config?.fields ?? []).filter((f) => f.field_key !== "");
|
|
|
|
const sections: ReportSection[] = [
|
|
{
|
|
title: "Details",
|
|
fields: fields.length > 0
|
|
? fields.map((f) => ({ label: f.output_label, value: data[f.field_key] }))
|
|
: Object.entries(data).map(([k, v]) => ({ label: k, value: v })),
|
|
},
|
|
];
|
|
|
|
return { data, sections };
|
|
}
|
|
|
|
/**
|
|
* Fetch a full instance report — global data, activity history,
|
|
* and state transitions.
|
|
*/
|
|
async getInstanceReport(
|
|
workflowId: string,
|
|
instanceId: string,
|
|
): Promise<{ report: InstanceReport; sections: ReportSection[]; history: StateTransition[] }> {
|
|
if (this.client.isMock) {
|
|
await mockDelay();
|
|
return mockInstanceReport(instanceId);
|
|
}
|
|
|
|
const [instance, audit] = await Promise.all([
|
|
this.client.request<InstanceReport>("POST", "/instance", {
|
|
workflow_id: workflowId,
|
|
instance_id: instanceId,
|
|
}),
|
|
this.client.request<ActivityLogEntry[]>(
|
|
"GET",
|
|
`/audit?instance_id=${encodeURIComponent(instanceId)}`,
|
|
),
|
|
]);
|
|
|
|
const sections: ReportSection[] = [
|
|
{
|
|
title: "Instance Details",
|
|
fields: [
|
|
{ label: "Instance ID", value: instance.instance_id },
|
|
{ label: "Workflow", value: instance.workflow_id },
|
|
{ label: "Current State", value: instance.current_state_name },
|
|
{ label: "Created", value: instance.created_at },
|
|
{ label: "Updated", value: instance.updated_at },
|
|
],
|
|
},
|
|
{
|
|
title: "Data",
|
|
fields: Object.entries((instance as any).data ?? instance.global_data ?? {}).map(([k, v]) => ({
|
|
label: k,
|
|
value: v,
|
|
})),
|
|
},
|
|
];
|
|
|
|
const history: StateTransition[] = audit.map((entry) => ({
|
|
from_state: "",
|
|
to_state: entry.execution_state,
|
|
activity_name: entry.activity_id,
|
|
performed_by: entry.user_id,
|
|
timestamp: entry.created_at,
|
|
data: entry.data,
|
|
}));
|
|
|
|
return { report: instance, sections, history };
|
|
}
|
|
|
|
/**
|
|
* Fetch the raw audit log for an instance.
|
|
*/
|
|
async getAuditLog(instanceId: string): Promise<ActivityLogEntry[]> {
|
|
if (this.client.isMock) {
|
|
await mockDelay();
|
|
return mockAuditLog();
|
|
}
|
|
|
|
return this.client.request<ActivityLogEntry[]>(
|
|
"GET",
|
|
`/audit?instance_id=${encodeURIComponent(instanceId)}`,
|
|
);
|
|
}
|
|
}
|