diff --git a/apps/web/src/app/[locale]/errors/page.tsx b/apps/web/src/app/[locale]/errors/page.tsx index f13dffff..31236225 100644 --- a/apps/web/src/app/[locale]/errors/page.tsx +++ b/apps/web/src/app/[locale]/errors/page.tsx @@ -1,164 +1,16 @@ 'use client' /** - * Errors Page - #44 錯誤追蹤頁面 - * ============================== - * Phase 10: Sentry + OpenClaw + UI 整合 - * - * Nothing.tech 視覺規範: - * - 純白底色 (bg-white) - * - 極細淺灰邊框 (border border-gray-200) - * - 無圓角或微圓角 (rounded-sm) - * - 嚴禁陰影 (shadow-none) - * - * 佈局: - * - 左側: 統計卡片 + 趨勢圖 - * - 右側: 問題列表 - * - * i18n: 100% next-intl,零硬編碼 - * - * 建立: 2026-03-26 (台北時區) - * 建立者: Claude Code (#44 Error UI) + * Errors Page — Sprint 5: 內容抽取到 ErrorsPanel */ -import { useTranslations } from 'next-intl' import { AppLayout } from '@/components/layout' -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' - -// ============================================================================= -// Page Component -// ============================================================================= - -export default function ErrorsPage({ - params, -}: { - params: { locale: string } -}) { - const t = useTranslations('errors') - const { - stats, - issues, - trends, - loading, - error, - activePeriod, - refetch, - setPeriod, - } = useErrors() - - // #126: UX Audit / Session Replay 數據 - const { - data: uxAuditData, - loading: uxAuditLoading, - error: uxAuditError, - } = useUXAudit() - - const handleIssueClick = (issue: SentryIssue) => { - // Open in new tab if permalink available - if (issue.permalink) { - window.open(issue.permalink, '_blank', 'noopener,noreferrer') - } - } +import { ErrorsPanel } from '@/components/panels/ErrorsPanel' +export default function ErrorsPage({ params }: { params: { locale: string } }) { return ( -
- {/* Header */} -
-
- -
-

- {t('title')} -

-

- {t('subtitle')} -

-
-
- - {/* Actions */} -
- - -{/* - Sentry Dashboard 連結已移除 - 原因: 內網 IP 會觸發瀏覽器「存取區域網路」權限對話框 - 2026-03-30 ogt: 參考 feedback_sentry_local_network.md - 未來: 可透過 VPN 或反向代理公開 Sentry UI - */} -
-
- - {/* Error State */} - {error && ( -
-

{error}

-
- )} - - {/* Main Grid */} -
- {/* Left Column - Stats & Trends */} -
- {/* Overview Card */} - - - {/* Trend Chart */} - - - {/* #126: UX Audit / Session Replay */} - -
- - {/* Right Column - Issues List */} -
- -
-
- - {/* Footer Info */} -
-

- {t('footerInfo')} -

-
-
+
) } diff --git a/apps/web/src/app/[locale]/observability/page.tsx b/apps/web/src/app/[locale]/observability/page.tsx index 59e40119..b12efdcb 100644 --- a/apps/web/src/app/[locale]/observability/page.tsx +++ b/apps/web/src/app/[locale]/observability/page.tsx @@ -20,9 +20,10 @@ import { AppLayout } from '@/components/layout' import { PageTabs, type TabConfig } from '@/components/layout/page-tabs' import { MonitoringPanel } from '@/components/panels/MonitoringPanel' import { ApmPanel } from '@/components/panels/ApmPanel' +import { ErrorsPanel } from '@/components/panels/ErrorsPanel' import { LobsterLoading } from '@/components/shared/lobster-loading' -// Tab 3-5: 暫時 lazy import (未來抽取 Panel) +// Tab 4-5: 暫時 lazy import (未來抽取 Panel) const ErrorsContent = lazy(() => import('@/app/[locale]/errors/page')) const AppsContent = lazy(() => import('@/app/[locale]/apps/page')) const ServicesContent = lazy(() => import('@/app/[locale]/services/page')) @@ -44,7 +45,7 @@ export default function ObservabilityPage({ params }: { params: { locale: string { id: 'errors', label: t('errors'), - content: }>, + content: , }, { id: 'apps', diff --git a/apps/web/src/components/panels/ErrorsPanel.tsx b/apps/web/src/components/panels/ErrorsPanel.tsx new file mode 100644 index 00000000..798eb46e --- /dev/null +++ b/apps/web/src/components/panels/ErrorsPanel.tsx @@ -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 ( +
+
+
+ +
+

{t('title')}

+

{t('subtitle')}

+
+
+ +
+ {error &&

{error}

} +
+
+ + + +
+
+ +
+
+

{t('footerInfo')}

+
+ ) +} diff --git a/apps/web/src/components/panels/index.ts b/apps/web/src/components/panels/index.ts index 6e0e43cb..81b1f9dc 100644 --- a/apps/web/src/components/panels/index.ts +++ b/apps/web/src/components/panels/index.ts @@ -14,3 +14,4 @@ export { MonitoringPanel } from './MonitoringPanel' export { ApmPanel } from './ApmPanel' +export { ErrorsPanel } from './ErrorsPanel'