361 lines
10 KiB
TypeScript
361 lines
10 KiB
TypeScript
import { APP_ID } from "../config";
|
|
|
|
const TOKEN_KEY = "zino_token";
|
|
|
|
function baseUrl(): string {
|
|
return (import.meta.env.VITE_ZINO_API_URL || "").replace(/\/+$/, "");
|
|
}
|
|
|
|
function headers(): Record<string, string> {
|
|
const h: Record<string, string> = { "Content-Type": "application/json" };
|
|
const token = localStorage.getItem(TOKEN_KEY);
|
|
if (token) h["Authorization"] = `Bearer ${token}`;
|
|
return h;
|
|
}
|
|
|
|
async function request<T>(method: string, path: string, body?: unknown): Promise<T> {
|
|
const res = await fetch(`${baseUrl()}${path}`, {
|
|
method,
|
|
headers: headers(),
|
|
body: body ? JSON.stringify(body) : undefined,
|
|
});
|
|
if (res.status === 401) {
|
|
localStorage.removeItem(TOKEN_KEY);
|
|
localStorage.removeItem("zino_user");
|
|
window.location.href = "/login";
|
|
throw new Error("Unauthorized");
|
|
}
|
|
if (!res.ok) {
|
|
let msg = res.statusText;
|
|
try { const e = await res.json(); if (e.error) msg = e.error; } catch {}
|
|
throw new Error(msg);
|
|
}
|
|
if (res.status === 204) return undefined as T;
|
|
return res.json() as Promise<T>;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Header
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface HeaderConfig {
|
|
slots: { left: string[]; right: string[]; center: string[] };
|
|
layout: { type: string; height: string; position: string; full_width: boolean };
|
|
components: {
|
|
nav?: { items: NavItem[] };
|
|
logo?: { text?: string; image_url?: string; display_type: string };
|
|
profile?: { shape: string; show_name: boolean };
|
|
};
|
|
}
|
|
|
|
export interface NavItem {
|
|
id: string;
|
|
icon: string;
|
|
type: string;
|
|
label: string;
|
|
screen_uuid: string;
|
|
}
|
|
|
|
export async function getHeaderConfig(deviceType = "desktop"): Promise<HeaderConfig> {
|
|
const raw = await request<any>("GET", `/app/${APP_ID}/view/header/${deviceType}`);
|
|
return raw.config ?? raw;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Screens
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface ScreenListItem {
|
|
id: number;
|
|
source_screen_id: number;
|
|
screen_name: string;
|
|
device: string;
|
|
description: string;
|
|
}
|
|
|
|
export interface ScreenLayout {
|
|
id: number;
|
|
source_screen_id: number;
|
|
screen_name: string;
|
|
layout: LayoutElement[];
|
|
}
|
|
|
|
export interface LayoutElement {
|
|
type: string;
|
|
style?: Record<string, string>;
|
|
config: Record<string, any>;
|
|
classes?: string[];
|
|
}
|
|
|
|
export function getScreens(deviceType = "desktop"): Promise<ScreenListItem[]> {
|
|
return request("GET", `/app/${APP_ID}/view/screens?device_type=${deviceType}`);
|
|
}
|
|
|
|
export function getScreen(idOrUuid: number | string): Promise<ScreenLayout> {
|
|
return request("GET", `/app/${APP_ID}/view/screens/${idOrUuid}`);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// RV Screens
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface RVScreen {
|
|
id: number;
|
|
source_screen_id: number;
|
|
screen_name: string;
|
|
rv_template_uid: string;
|
|
layout?: any;
|
|
}
|
|
|
|
// Accepts either the numeric source_screen_id OR the source_uuid — both
|
|
// are accepted by the view-service's rv-screens endpoint.
|
|
export function getRVScreen(idOrUuid: number | string): Promise<RVScreen> {
|
|
return request("GET", `/app/${APP_ID}/view/rv-screens/${idOrUuid}`);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Record View (POST-based)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface SearchQuery {
|
|
page?: number;
|
|
limit?: number;
|
|
sort_by?: string;
|
|
sort_dir?: "asc" | "desc";
|
|
search?: string;
|
|
filters?: Array<{ field_key: string; value: string; data_type?: string }>;
|
|
}
|
|
|
|
export interface RecordViewField {
|
|
field_key: string;
|
|
output_label: string;
|
|
data_type: string;
|
|
is_filter: boolean;
|
|
is_search: boolean;
|
|
type?: string;
|
|
activity_id?: string;
|
|
}
|
|
|
|
export interface RecordViewResponse {
|
|
config: { fields: RecordViewField[] };
|
|
data: Record<string, unknown>[];
|
|
pagination?: {
|
|
page: number;
|
|
limit: number;
|
|
total_count: number;
|
|
total_pages: number;
|
|
};
|
|
}
|
|
|
|
export function getRecordView(
|
|
rvTemplateUid: string,
|
|
rvScreenId: string | number,
|
|
query: SearchQuery = {},
|
|
): Promise<RecordViewResponse> {
|
|
return request("POST", `/app/${APP_ID}/view/recordview`, {
|
|
rv_template_uid: rvTemplateUid,
|
|
rv_screen_id: String(rvScreenId),
|
|
search_query: {
|
|
page: query.page || 1,
|
|
limit: query.limit || 50,
|
|
sort_by: query.sort_by || "",
|
|
sort_dir: query.sort_dir || "desc",
|
|
search: query.search || "",
|
|
filters: query.filters || [],
|
|
},
|
|
});
|
|
}
|
|
|
|
export function getRecordViewLegacy(
|
|
rvUid: string,
|
|
query: SearchQuery = {},
|
|
): Promise<RecordViewResponse> {
|
|
const qs = new URLSearchParams();
|
|
qs.set("rv_id", rvUid);
|
|
if (query.page) qs.set("page", String(query.page));
|
|
if (query.limit) qs.set("limit", String(query.limit));
|
|
if (query.sort_by) qs.set("sort_by", query.sort_by);
|
|
if (query.sort_dir) qs.set("sort_dir", query.sort_dir);
|
|
if (query.search) qs.set("search", query.search);
|
|
if (query.filters) {
|
|
for (const f of query.filters) {
|
|
qs.set(`filter.${f.field_key}`, f.value);
|
|
}
|
|
}
|
|
return request("GET", `/recordview?${qs.toString()}`);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// DV Screens
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface DVScreen {
|
|
id: number;
|
|
source_screen_id: number;
|
|
screen_name: string;
|
|
dv_template_uid: string;
|
|
layout?: any;
|
|
}
|
|
|
|
export function getDVScreen(idOrUuid: number | string): Promise<DVScreen> {
|
|
return request("GET", `/app/${APP_ID}/view/dv-screens/${idOrUuid}`);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Detail View
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface DetailViewResponse {
|
|
config: { fields: Array<{ field_key: string; output_label: string; data_type: string }> };
|
|
data: Record<string, unknown>;
|
|
}
|
|
|
|
export function getDetailView(dvSourceIdOrUid: number | string, instanceId: string): Promise<DetailViewResponse> {
|
|
return request(
|
|
"GET",
|
|
`/app/${APP_ID}/view/detailview/${encodeURIComponent(String(dvSourceIdOrUid))}?instance_id=${encodeURIComponent(instanceId)}`,
|
|
);
|
|
}
|
|
|
|
export function getDetailViewLegacy(dvUid: string, instanceId: string): Promise<DetailViewResponse> {
|
|
return request(
|
|
"GET",
|
|
`/detailview?dv_id=${encodeURIComponent(dvUid)}&instance_id=${encodeURIComponent(instanceId)}`,
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Form Screens
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface FormScreenField {
|
|
id: string;
|
|
uid: string;
|
|
name: string;
|
|
type: string;
|
|
data_type: string;
|
|
mandatory: boolean;
|
|
value?: any;
|
|
properties?: {
|
|
options?: Array<{ label: string; value: string }>;
|
|
country_code?: string;
|
|
};
|
|
}
|
|
|
|
export interface FormScreenResponse {
|
|
id: number;
|
|
activity_uid: string;
|
|
activity_name: string;
|
|
device_type: string;
|
|
fields: FormScreenField[];
|
|
grid_config: Array<{ i: string; x: number; y: number; w: number; h: number }>;
|
|
layout: any[];
|
|
}
|
|
|
|
export function getFormScreen(
|
|
activityId: string,
|
|
instanceId?: string,
|
|
deviceType = "desktop",
|
|
): Promise<FormScreenResponse> {
|
|
return request("POST", `/app/${APP_ID}/view/form-screens`, {
|
|
activity_id: activityId,
|
|
device_type: deviceType,
|
|
...(instanceId ? { instance_id: instanceId } : {}),
|
|
});
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Workflow Actions
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface WorkflowResponse {
|
|
success: boolean;
|
|
status_code?: number;
|
|
message?: string;
|
|
data?: Record<string, unknown>;
|
|
instance_id?: string;
|
|
}
|
|
|
|
// Core-service expects the workflow UUID as `workflow_uuid` — the numeric
|
|
// `workflow_id` field was removed when the clone-portable identifier rolled out.
|
|
export function startWorkflow(
|
|
workflowUuid: string,
|
|
activityId: string,
|
|
data?: Record<string, unknown>,
|
|
): Promise<WorkflowResponse> {
|
|
return request("POST", `/app/${APP_ID}/start`, {
|
|
workflow_uuid: workflowUuid,
|
|
activity_id: activityId,
|
|
data,
|
|
});
|
|
}
|
|
|
|
export function performActivity(
|
|
workflowUuid: string,
|
|
instanceId: string,
|
|
activityId: string,
|
|
data?: Record<string, unknown>,
|
|
): Promise<WorkflowResponse> {
|
|
return request("POST", `/app/${APP_ID}/activity`, {
|
|
workflow_uuid: workflowUuid,
|
|
instance_id: instanceId,
|
|
activity_id: activityId,
|
|
data,
|
|
});
|
|
}
|
|
|
|
export function submitForm(
|
|
workflowUuid: string,
|
|
activityId: string,
|
|
formData: Record<string, unknown>,
|
|
instanceId?: string,
|
|
deviceType = "desktop",
|
|
): Promise<WorkflowResponse> {
|
|
return request("POST", `/app/${APP_ID}/form/submit`, {
|
|
workflow_uuid: workflowUuid,
|
|
activity_id: activityId,
|
|
device_type: deviceType,
|
|
form_data: formData,
|
|
...(instanceId ? { instance_id: instanceId } : {}),
|
|
});
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Instance
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface InstanceResponse {
|
|
instance_id: string;
|
|
workflow_id: string;
|
|
current_state_id: string;
|
|
current_state_name: string;
|
|
data: Record<string, unknown>;
|
|
created_at: string;
|
|
updated_at: string;
|
|
}
|
|
|
|
export function getInstance(workflowUuid: string, instanceId: string): Promise<InstanceResponse> {
|
|
return request("POST", `/app/${APP_ID}/instance`, {
|
|
workflow_uuid: workflowUuid,
|
|
instance_id: instanceId,
|
|
});
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Audit
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface AuditEntry {
|
|
id: number;
|
|
user_id: string;
|
|
user_roles: string[];
|
|
activity_id: string;
|
|
data: Record<string, unknown>;
|
|
execution_state: string;
|
|
created_at: string;
|
|
}
|
|
|
|
export function getAuditLog(instanceId: string): Promise<AuditEntry[]> {
|
|
return request("GET", `/app/${APP_ID}/view/audit?instance_id=${encodeURIComponent(instanceId)}`);
|
|
}
|