feat: add StartupLogPanel component for deployment startup logs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-14 23:21:26 +02:00
parent 119cf912b8
commit e7732703a6
2 changed files with 106 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
.panel {
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 6px;
overflow: hidden;
margin-top: 8px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
border-bottom: 1px solid var(--border);
}
.headerLeft {
display: flex;
align-items: center;
gap: 8px;
}
.title {
font-size: 13px;
font-weight: 600;
color: var(--text-primary);
}
.badge {
font-size: 11px;
padding: 1px 8px;
border-radius: 10px;
}
.badgeLive {
background: var(--success-muted);
color: var(--success);
}
.badgeStopped {
background: var(--error-muted);
color: var(--error);
}
.pollingHint {
font-size: 11px;
color: var(--text-muted);
}
.lineCount {
font-size: 12px;
color: var(--text-muted);
}
.empty {
padding: 16px;
text-align: center;
font-size: 13px;
color: var(--text-muted);
}

View File

@@ -0,0 +1,46 @@
import { LogViewer } from '@cameleer/design-system';
import { useStartupLogs } from '../api/queries/logs';
import type { Deployment } from '../api/queries/admin/apps';
import styles from './StartupLogPanel.module.css';
interface StartupLogPanelProps {
deployment: Deployment;
appSlug: string;
envSlug: string;
}
export function StartupLogPanel({ deployment, appSlug, envSlug }: StartupLogPanelProps) {
const isStarting = deployment.status === 'STARTING';
const isFailed = deployment.status === 'FAILED';
const { data } = useStartupLogs(appSlug, envSlug, deployment.createdAt, isStarting);
const entries = data?.data ?? [];
if (entries.length === 0 && !isStarting) return null;
return (
<div className={styles.panel}>
<div className={styles.header}>
<div className={styles.headerLeft}>
<span className={styles.title}>Startup Logs</span>
{isStarting && (
<>
<span className={`${styles.badge} ${styles.badgeLive}`}> live</span>
<span className={styles.pollingHint}>polling every 3s</span>
</>
)}
{isFailed && (
<span className={`${styles.badge} ${styles.badgeStopped}`}>stopped</span>
)}
</div>
<span className={styles.lineCount}>{entries.length} lines</span>
</div>
{entries.length > 0 ? (
<LogViewer entries={entries} maxHeight={300} />
) : (
<div className={styles.empty}>Waiting for container output...</div>
)}
</div>
);
}