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>
90 lines
3.1 KiB
TypeScript
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>
|
|
)
|
|
}
|