ui(deploy): HistoryDisclosure with inline log expansion

Collapsible deployment history table (sorted newest-first) with
click-to-expand StartupLogPanel for any historical deployment row.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-22 23:03:02 +02:00
parent 98a7b7819f
commit 063a4a5532

View File

@@ -0,0 +1,64 @@
import { useState } from 'react';
import { DataTable } from '@cameleer/design-system';
import type { Column } from '@cameleer/design-system';
import type { Deployment, AppVersion } from '../../../../api/queries/admin/apps';
import { timeAgo } from '../../../../utils/format-utils';
import { StartupLogPanel } from '../../../../components/StartupLogPanel';
import styles from '../AppDeploymentPage.module.css';
interface Props {
deployments: Deployment[];
versions: AppVersion[];
appSlug: string;
envSlug: string;
}
export function HistoryDisclosure({ deployments, versions, appSlug, envSlug }: Props) {
const [open, setOpen] = useState(false);
const [expanded, setExpanded] = useState<string | null>(null);
const versionMap = new Map(versions.map((v) => [v.id, v]));
const rows = deployments
.slice()
.sort((a, b) => (b.createdAt ?? '').localeCompare(a.createdAt ?? ''));
const columns: Column<Deployment>[] = [
{ key: 'createdAt', header: 'Started', render: (_, d) => timeAgo(d.createdAt) },
{
key: 'appVersionId', header: 'Version',
render: (_, d) => {
const v = versionMap.get(d.appVersionId);
return v ? `v${v.version}` : '?';
},
},
{ key: 'status', header: 'Status' },
{
key: 'deployedAt', header: 'Duration',
render: (_, d) => d.deployedAt && d.createdAt
? `${Math.round((Date.parse(d.deployedAt) - Date.parse(d.createdAt)) / 1000)}s`
: '—',
},
];
return (
<div className={styles.historyRow}>
<button type="button" className={styles.disclosureToggle} onClick={() => setOpen(!open)}>
{open ? '▼' : '▶'} History ({rows.length})
</button>
{open && (
<>
<DataTable
columns={columns}
data={rows}
onRowClick={(row) => setExpanded(expanded === row.id ? null : row.id)}
/>
{expanded && (() => {
const d = rows.find((r) => r.id === expanded);
if (!d) return null;
return <StartupLogPanel deployment={d} appSlug={appSlug} envSlug={envSlug} />;
})()}
</>
)}
</div>
);
}