diff --git a/ui/src/pages/AppsTab/AppDeploymentPage/AppDeploymentPage.module.css b/ui/src/pages/AppsTab/AppDeploymentPage/AppDeploymentPage.module.css index f7869b64..f35d780e 100644 --- a/ui/src/pages/AppsTab/AppDeploymentPage/AppDeploymentPage.module.css +++ b/ui/src/pages/AppsTab/AppDeploymentPage/AppDeploymentPage.module.css @@ -53,3 +53,44 @@ white-space: nowrap; border: 0; } + +.checkpointsRow { + margin-top: 8px; +} + +.disclosureToggle { + background: none; + border: none; + color: var(--text-muted); + cursor: pointer; + font-size: 13px; + padding: 4px 0; +} + +.checkpointList { + display: flex; + flex-direction: column; + gap: 4px; + padding: 6px 0 0 12px; +} + +.checkpointRow { + display: flex; + align-items: center; + gap: 10px; + font-size: 13px; +} + +.checkpointMeta { + color: var(--text-muted); +} + +.checkpointArchived { + color: var(--warning); + font-size: 12px; +} + +.checkpointEmpty { + color: var(--text-muted); + font-size: 13px; +} diff --git a/ui/src/pages/AppsTab/AppDeploymentPage/Checkpoints.tsx b/ui/src/pages/AppsTab/AppDeploymentPage/Checkpoints.tsx new file mode 100644 index 00000000..3b447ce1 --- /dev/null +++ b/ui/src/pages/AppsTab/AppDeploymentPage/Checkpoints.tsx @@ -0,0 +1,65 @@ +import { useState } from 'react'; +import { Button, Badge } from '@cameleer/design-system'; +import type { Deployment, AppVersion } from '../../../api/queries/admin/apps'; +import { timeAgo } from '../../../utils/format-utils'; +import styles from './AppDeploymentPage.module.css'; + +interface CheckpointsProps { + deployments: Deployment[]; + versions: AppVersion[]; + currentDeploymentId: string | null; + onRestore: (deploymentId: string) => void; +} + +export function Checkpoints({ deployments, versions, currentDeploymentId, onRestore }: CheckpointsProps) { + const [open, setOpen] = useState(false); + const versionMap = new Map(versions.map((v) => [v.id, v])); + + // Only successful deployments (RUNNING with a deployedAt). Exclude the currently-running one. + const checkpoints = deployments + .filter((d) => d.deployedAt && d.status === 'RUNNING' && d.id !== currentDeploymentId) + .sort((a, b) => (b.deployedAt ?? '').localeCompare(a.deployedAt ?? '')); + + return ( +