2026-09-14 09:57:12 +07:00

135 lines
3.2 KiB
Svelte

<script lang="ts">
import type { ScoringRunStatus } from '../api/types';
interface Props {
status: ScoringRunStatus | string;
degraded?: boolean | null;
}
let { status, degraded = false }: Props = $props();
const statusConfig: Record<string, { bg: string; text: string; border: string; label: string; pulse?: boolean }> = {
pending: {
bg: 'var(--status-warning-bg)',
text: 'var(--status-warning-text)',
border: 'var(--status-warning-border)',
label: 'ĐANG CHỜ',
pulse: true,
},
running: {
bg: 'var(--status-info-bg)',
text: 'var(--status-info)',
border: 'var(--status-info-border)',
label: 'ĐANG XỬ LÝ',
pulse: true,
},
completed: {
bg: 'var(--status-good-bg)',
text: 'var(--status-good-text)',
border: 'var(--status-good-border)',
label: 'HOÀN THÀNH',
},
review_required: {
bg: 'var(--status-warning-bg)',
text: 'var(--status-warning-text)',
border: 'var(--status-warning-border)',
label: 'CẦN KIỂM TRA',
},
failed: {
bg: 'var(--status-critical-bg)',
text: 'var(--status-critical-text)',
border: 'var(--status-critical-border)',
label: 'THẤT BẠI',
},
superseded: {
bg: 'var(--bg-surface-raised)',
text: 'var(--text-muted)',
border: 'var(--border-subtle)',
label: 'ĐÃ THAY THẾ',
},
};
let badge = $derived(
statusConfig[status] ?? {
bg: 'var(--bg-surface-raised)',
text: 'var(--text-secondary)',
border: 'var(--border-subtle)',
label: status.toUpperCase(),
}
);
</script>
<div class="status-wrapper">
<span
class="badge"
style="background: {badge.bg}; color: {badge.text}; border: 1px solid {badge.border};"
>
<span class="dot {badge.pulse ? 'pulsing' : ''}" style="background: {badge.text};"></span>
<span>{badge.label}</span>
</span>
{#if degraded}
<span class="degraded-badge" title="Chế độ dự phòng: AIHub gặp sự cố, hệ thống chuyển về tính điểm quy tắc">
DỰ PHÒNG
</span>
{/if}
</div>
<style>
.status-wrapper {
display: inline-flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 3px 9px;
border-radius: var(--radius-full);
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.05em;
font-family: var(--font-mono);
line-height: 1;
}
.dot {
width: 6px;
height: 6px;
border-radius: var(--radius-full);
flex-shrink: 0;
}
.dot.pulsing {
animation: pulse-glow 1.5s ease-in-out infinite;
}
@keyframes pulse-glow {
0%, 100% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.3);
opacity: 0.6;
}
}
.degraded-badge {
display: inline-flex;
align-items: center;
padding: 3px 8px;
border-radius: var(--radius-xs);
background: var(--status-warning-bg);
color: var(--status-warning-text);
border: 1px solid var(--status-warning-border);
font-size: 0.66rem;
font-weight: 700;
font-family: var(--font-mono);
letter-spacing: 0.04em;
}
</style>