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>
88 lines
2.6 KiB
TypeScript
88 lines
2.6 KiB
TypeScript
/**
|
|
* SentryErrorCard - Sentry Error Display
|
|
* =======================================
|
|
* 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 { Bug, ExternalLink } from 'lucide-react'
|
|
|
|
interface SentryErrorCardProps {
|
|
data: {
|
|
errorId: string
|
|
title: string
|
|
count?: number
|
|
lastSeen?: string
|
|
level?: 'error' | 'warning' | 'info'
|
|
url?: string
|
|
}
|
|
}
|
|
|
|
export const SentryErrorCard: React.FC<SentryErrorCardProps> = ({ data }) => {
|
|
const level = data.level || 'error'
|
|
|
|
const levelColors = {
|
|
error: 'border-red-500 text-red-600',
|
|
warning: 'border-yellow-500 text-yellow-600',
|
|
info: 'border-blue-500 text-blue-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">
|
|
<Bug className={levelColors[level]} size={20} />
|
|
<span className="font-['VT323'] text-xl font-bold uppercase tracking-wider text-nothing-black">
|
|
Sentry // {data.errorId}
|
|
</span>
|
|
</div>
|
|
<div className={`px-2 py-1 text-xs font-body font-bold border-2 ${levelColors[level]} uppercase`}>
|
|
{level}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="p-4 space-y-3">
|
|
<div>
|
|
<span className="text-xs uppercase font-body font-bold text-gray-500 tracking-wider">
|
|
Error
|
|
</span>
|
|
<div className="mt-1 font-['VT323'] text-xl text-nothing-black">
|
|
{data.title}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex gap-4 text-sm font-body">
|
|
{data.count !== undefined && (
|
|
<div>
|
|
<span className="text-gray-500">Count:</span>{' '}
|
|
<span className="font-bold">{data.count}</span>
|
|
</div>
|
|
)}
|
|
{data.lastSeen && (
|
|
<div>
|
|
<span className="text-gray-500">Last seen:</span>{' '}
|
|
<span className="font-bold">{data.lastSeen}</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{data.url && (
|
|
<a
|
|
href={data.url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="inline-flex items-center gap-1 text-sm font-body text-[#4A90D9] hover:underline"
|
|
>
|
|
View in Sentry <ExternalLink size={14} />
|
|
</a>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|