refactor(web): ErrorsPanel 抽取 — /observability 3 個 Tab 已無雙重 Layout
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
This commit is contained in:
58
apps/web/src/components/panels/ErrorsPanel.tsx
Normal file
58
apps/web/src/components/panels/ErrorsPanel.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
'use client'
|
||||
|
||||
/**
|
||||
* ErrorsPanel — 錯誤追蹤面板 (不含 AppLayout)
|
||||
* Sprint 5: 從 /errors/page.tsx 抽取
|
||||
*/
|
||||
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { useErrors } from '@/hooks/useErrors'
|
||||
import { useUXAudit } from '@/hooks/useUXAudit'
|
||||
import {
|
||||
ErrorOverviewCard,
|
||||
RecentIssuesList,
|
||||
ErrorTrendChart,
|
||||
UXAuditCard,
|
||||
} from '@/components/errors'
|
||||
import { Bug, RefreshCw } from 'lucide-react'
|
||||
import type { SentryIssue } from '@/lib/api-client'
|
||||
|
||||
export function ErrorsPanel() {
|
||||
const t = useTranslations('errors')
|
||||
const { stats, issues, trends, loading, error, activePeriod, refetch, setPeriod } = useErrors()
|
||||
const { data: uxAuditData, loading: uxAuditLoading, error: uxAuditError } = useUXAudit()
|
||||
|
||||
const handleIssueClick = (issue: SentryIssue) => {
|
||||
if (issue.permalink) window.open(issue.permalink, '_blank', 'noopener,noreferrer')
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-6 max-w-7xl mx-auto">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<Bug className="h-6 w-6 text-gray-700" />
|
||||
<div>
|
||||
<h1 className="text-xl font-semibold text-gray-900">{t('title')}</h1>
|
||||
<p className="text-sm text-gray-500">{t('subtitle')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button onClick={refetch} disabled={loading} className="flex items-center gap-1.5 px-3 py-1.5 text-sm bg-gray-100 hover:bg-gray-200 rounded transition-colors disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
<RefreshCw className={`h-4 w-4 ${loading ? 'animate-spin' : ''}`} />
|
||||
{loading ? t('loading') : t('refresh')}
|
||||
</button>
|
||||
</div>
|
||||
{error && <div className="mb-6 p-4 bg-red-50 border border-red-200 rounded-sm"><p className="text-sm text-red-700">{error}</p></div>}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<div className="lg:col-span-1 space-y-6">
|
||||
<ErrorOverviewCard stats={stats} loading={loading} changePercent={trends?.change_percent} />
|
||||
<ErrorTrendChart data={trends} loading={loading} activePeriod={activePeriod} onPeriodChange={setPeriod} />
|
||||
<UXAuditCard data={uxAuditData} loading={uxAuditLoading} error={uxAuditError} />
|
||||
</div>
|
||||
<div className="lg:col-span-2">
|
||||
<RecentIssuesList issues={issues} loading={loading} onIssueClick={handleIssueClick} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-6 pt-4 border-t border-gray-100"><p className="text-xs text-gray-400 text-center">{t('footerInfo')}</p></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -14,3 +14,4 @@
|
||||
|
||||
export { MonitoringPanel } from './MonitoringPanel'
|
||||
export { ApmPanel } from './ApmPanel'
|
||||
export { ErrorsPanel } from './ErrorsPanel'
|
||||
|
||||
Reference in New Issue
Block a user