used detailview api instead of audit
This commit is contained in:
parent
edddb1b696
commit
3492cdbf9e
@ -6,7 +6,7 @@
|
|||||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||||
<meta name="theme-color" content="#0B1B3B" />
|
<meta name="theme-color" content="#0B1B3B" />
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Krishna Sales" />
|
<meta name="apple-mobile-web-app-title" content="Krishna Sales" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon-180x180.png" />
|
<link rel="apple-touch-icon" href="/apple-touch-icon-180x180.png" />
|
||||||
|
|||||||
@ -2,8 +2,7 @@ import { useEffect, useState } from 'react';
|
|||||||
import { cn } from '../../lib/cn';
|
import { cn } from '../../lib/cn';
|
||||||
import { formatValue } from '../../lib/format';
|
import { formatValue } from '../../lib/format';
|
||||||
import type { ZinoClient } from '../../api/client';
|
import type { ZinoClient } from '../../api/client';
|
||||||
import type { AuditEntry } from '../../api/types';
|
import { APP_ID } from '../../api/config';
|
||||||
import { WORKFLOWS, APP_ID } from '../../api/config';
|
|
||||||
import { Card } from '../reusable/Card';
|
import { Card } from '../reusable/Card';
|
||||||
import { Spinner } from '../reusable/Spinner';
|
import { Spinner } from '../reusable/Spinner';
|
||||||
import { EmptyState } from '../reusable/EmptyState';
|
import { EmptyState } from '../reusable/EmptyState';
|
||||||
@ -11,7 +10,7 @@ import { EmptyState } from '../reusable/EmptyState';
|
|||||||
export interface DetailViewProps {
|
export interface DetailViewProps {
|
||||||
/** Workflow-bound client (see api/clients.ts). */
|
/** Workflow-bound client (see api/clients.ts). */
|
||||||
client: ZinoClient;
|
client: ZinoClient;
|
||||||
/** detailview template uid (deprecated, using audit endpoint now). */
|
/** detailview template uid. */
|
||||||
dvUid?: string;
|
dvUid?: string;
|
||||||
/** Instance to render. */
|
/** Instance to render. */
|
||||||
instanceId: number | string;
|
instanceId: number | string;
|
||||||
@ -23,29 +22,13 @@ export interface DetailViewProps {
|
|||||||
columns?: 1 | 2 | 3;
|
columns?: 1 | 2 | 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to resolve a human-readable activity name from its UID.
|
|
||||||
*/
|
|
||||||
function getActivityName(uid: string): string | undefined {
|
|
||||||
for (const wf of Object.values(WORKFLOWS)) {
|
|
||||||
for (const [actName, actDef] of Object.entries(wf.activities)) {
|
|
||||||
if (actDef.uid === uid) {
|
|
||||||
return actName
|
|
||||||
.split('_')
|
|
||||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
||||||
.join(' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic Zino detail view. Fetches `GET /app/{id}/view/audit` and
|
* Generic Zino detail view. Fetches `GET /app/{id}/view/detailview/{dvUid}` and
|
||||||
* renders the audit trail as a labeled definition grid.
|
* renders the data as a labeled definition grid.
|
||||||
*/
|
*/
|
||||||
export function DetailView({ client, instanceId, title, columns = 2 }: DetailViewProps) {
|
export function DetailView({ client, dvUid, instanceId, title, fields, columns = 2 }: DetailViewProps) {
|
||||||
const [entries, setEntries] = useState<AuditEntry[]>([]);
|
const [data, setData] = useState<Record<string, unknown> | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
@ -55,9 +38,10 @@ export function DetailView({ client, instanceId, title, columns = 2 }: DetailVie
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
const r = await client.audit(instanceId);
|
if (!dvUid) throw new Error("dvUid is required to fetch detail view data.");
|
||||||
|
const r = await client.detailView(dvUid, instanceId);
|
||||||
if (live) {
|
if (live) {
|
||||||
setEntries(r || []);
|
setData(r.data || {});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (live) setError((e as { message?: string })?.message ?? 'Failed to load');
|
if (live) setError((e as { message?: string })?.message ?? 'Failed to load');
|
||||||
@ -69,7 +53,7 @@ export function DetailView({ client, instanceId, title, columns = 2 }: DetailVie
|
|||||||
return () => {
|
return () => {
|
||||||
live = false;
|
live = false;
|
||||||
};
|
};
|
||||||
}, [client, instanceId]);
|
}, [client, dvUid, instanceId]);
|
||||||
|
|
||||||
const gridCols = { 1: 'grid-cols-1', 2: 'grid-cols-1 sm:grid-cols-2', 3: 'grid-cols-1 sm:grid-cols-3' }[columns];
|
const gridCols = { 1: 'grid-cols-1', 2: 'grid-cols-1 sm:grid-cols-2', 3: 'grid-cols-1 sm:grid-cols-3' }[columns];
|
||||||
|
|
||||||
@ -91,29 +75,20 @@ export function DetailView({ client, instanceId, title, columns = 2 }: DetailVie
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entries.length === 0) {
|
if (!data || Object.keys(data).length === 0) {
|
||||||
return (
|
return (
|
||||||
<Card title={title}>
|
<Card title={title}>
|
||||||
<EmptyState title="No history found" />
|
<EmptyState title="No details found" />
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
{entries.map((entry, i) => {
|
<Card title={title || 'Details'}>
|
||||||
const actName = getActivityName(entry.activity_id);
|
<dl className={cn('grid gap-x-6 gap-y-4', gridCols)}>
|
||||||
const headerTitle = actName || (i === 0 && title ? title : 'Update');
|
{Object.entries(data).map(([key, value]) => {
|
||||||
const updatedAt = new Date(entry.created_at).toLocaleString();
|
if (fields && !fields.includes(key)) return null;
|
||||||
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
key={entry.id}
|
|
||||||
title={headerTitle}
|
|
||||||
action={<span className="text-xs font-medium text-faint">{updatedAt}</span>}
|
|
||||||
>
|
|
||||||
<dl className={cn('grid gap-x-6 gap-y-4', gridCols)}>
|
|
||||||
{Object.entries(entry.data).map(([key, value]) => {
|
|
||||||
const isGridArray =
|
const isGridArray =
|
||||||
Array.isArray(value) &&
|
Array.isArray(value) &&
|
||||||
value.length > 0 &&
|
value.length > 0 &&
|
||||||
@ -213,9 +188,7 @@ export function DetailView({ client, instanceId, title, columns = 2 }: DetailVie
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</dl>
|
</dl>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user