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 && (
-
- )}
-
- {/* 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 &&
}
+
+
+
+ )
+}
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'