Files
awoooi/apps/web/src/components/genui/IncidentTimelineCard.tsx
OG T 4d46e6b9a7
Some checks failed
E2E Health Check / e2e-health (push) Successful in 17s
CD Pipeline / build-and-deploy (push) Has been cancelled
style(web): 全站 font-mono → font-body (DM Mono 設計系統套用)
45 個 component + 6 個 page 統一從舊 font-mono 遷移到
font-body (DM Mono),確保設計系統一致性。

font-body = DM Mono (等寬),視覺效果相同但走新設計 token。
保留: font-heading (Syne)、font-dot-matrix (VT323/DSEG7)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 09:37:03 +08:00

90 lines
3.1 KiB
TypeScript

/**
* IncidentTimelineCard - Incident Timeline
* =========================================
* Phase 19.4a - GenUI 事件時間軸卡
*
* @see ADR-032 GenUI Dynamic Rendering
* @author Claude Code (首席架構師)
* @version 1.0.0
* @date 2026-03-28 (台北時間)
*/
import React from 'react'
import { AlertCircle, CheckCircle, Clock, XCircle } from 'lucide-react'
interface TimelineEvent {
timestamp: string
type: 'created' | 'updated' | 'resolved' | 'escalated'
message: string
}
interface IncidentTimelineCardProps {
data: {
incidentId: string
status: 'open' | 'investigating' | 'resolved' | 'closed'
events: TimelineEvent[]
severity?: 'P0' | 'P1' | 'P2' | 'P3'
}
}
export const IncidentTimelineCard: React.FC<IncidentTimelineCardProps> = ({ data }) => {
const statusIcon = {
open: <AlertCircle className="text-red-500" size={20} />,
investigating: <Clock className="text-yellow-500" size={20} />,
resolved: <CheckCircle className="text-green-500" size={20} />,
closed: <XCircle className="text-gray-500" size={20} />,
}
const statusColors = {
open: 'border-red-500 text-red-600',
investigating: 'border-yellow-500 text-yellow-600',
resolved: 'border-green-500 text-green-600',
closed: 'border-gray-500 text-gray-600',
}
return (
<div className="w-full max-w-2xl bg-white border-2 border-nothing-black shadow-lg rounded-sm overflow-hidden mt-4 mb-2">
<div className="flex border-b-2 border-nothing-black bg-nothing-gray-50 p-3 items-center justify-between">
<div className="flex items-center gap-2">
{statusIcon[data.status]}
<span className="font-['VT323'] text-xl font-bold uppercase tracking-wider text-nothing-black">
Incident // {data.incidentId}
</span>
</div>
<div className="flex items-center gap-2">
{data.severity && (
<span className="px-2 py-1 text-xs font-body font-bold border-2 border-nothing-black">
{data.severity}
</span>
)}
<span className={`px-2 py-1 text-xs font-body font-bold border-2 ${statusColors[data.status]} uppercase`}>
{data.status}
</span>
</div>
</div>
<div className="p-4">
<div className="space-y-3">
{data.events.map((event, i) => (
<div key={i} className="flex gap-3 text-sm">
<div className="text-xs font-body text-gray-400 w-20 shrink-0">
{event.timestamp}
</div>
<div className="flex-1">
<span className={`text-xs font-body uppercase px-1 ${
event.type === 'resolved' ? 'bg-green-100 text-green-700' :
event.type === 'escalated' ? 'bg-red-100 text-red-700' :
'bg-gray-100 text-gray-700'
}`}>
{event.type}
</span>
<span className="ml-2 font-body text-nothing-black">{event.message}</span>
</div>
</div>
))}
</div>
</div>
</div>
)
}