Files
awoooi/apps/web/src/hooks/useErrors.ts
OG T 5172c4c925 feat(web): Error Dashboard + GenUI + OmniTerminal 組件
新增功能:
- errors/: Sentry 錯誤儀表板 (overview-card, trend-chart, issues-list)
- genui/ApprovalCard: GenUI 風格簽核卡片
- terminal/OmniTerminal: AI 終端機組件
- useErrors.ts: 錯誤數據 hooks
- terminal.store.ts: 終端機狀態管理

更新:
- conversational-view.tsx: 改進對話式 UI
- providers.tsx: 新增 provider
- sidebar.tsx: 新增 Errors 導航
- api-client.ts: 錯誤 API 整合
- i18n messages: 新增錯誤相關翻譯

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-26 19:09:36 +08:00

112 lines
2.9 KiB
TypeScript

/**
* useErrors - Sentry Error Data Hook
* ===================================
* Phase 10: Sentry + OpenClaw + UI 整合 (#41-44)
*
* 提供錯誤數據的 React Hook:
* - 統計概覽
* - 問題列表
* - 趨勢數據
*
* 建立: 2026-03-26 (台北時區)
* 建立者: Claude Code (#44 Error UI)
*/
import { useState, useEffect, useCallback } from 'react'
import {
apiClient,
type ErrorStatsResponse,
type ErrorListResponse,
type ErrorTrendResponse,
type SentryIssue,
} from '@/lib/api-client'
// =============================================================================
// Types
// =============================================================================
interface UseErrorsState {
stats: ErrorStatsResponse | null
issues: SentryIssue[]
trends: ErrorTrendResponse | null
loading: boolean
error: string | null
activePeriod: '24h' | '7d' | '30d'
}
interface UseErrorsReturn extends UseErrorsState {
refetch: () => Promise<void>
setPeriod: (period: '24h' | '7d' | '30d') => void
}
// =============================================================================
// Hook
// =============================================================================
export function useErrors(): UseErrorsReturn {
const [state, setState] = useState<UseErrorsState>({
stats: null,
issues: [],
trends: null,
loading: true,
error: null,
activePeriod: '24h',
})
const fetchData = useCallback(async (period: '24h' | '7d' | '30d' = '24h') => {
setState((prev) => ({ ...prev, loading: true, error: null }))
try {
const [statsResult, issuesResult, trendsResult] = await Promise.allSettled([
apiClient.getErrorStats(),
apiClient.listErrors({ limit: 10 }),
apiClient.getErrorTrends(period),
])
setState((prev) => ({
...prev,
stats: statsResult.status === 'fulfilled' ? statsResult.value : null,
issues: issuesResult.status === 'fulfilled' ? issuesResult.value.issues : [],
trends: trendsResult.status === 'fulfilled' ? trendsResult.value : null,
loading: false,
error: null,
activePeriod: period,
}))
} catch (err) {
setState((prev) => ({
...prev,
loading: false,
error: err instanceof Error ? err.message : 'Failed to fetch error data',
}))
}
}, [])
const setPeriod = useCallback((period: '24h' | '7d' | '30d') => {
fetchData(period)
}, [fetchData])
const refetch = useCallback(() => {
return fetchData(state.activePeriod)
}, [fetchData, state.activePeriod])
// Initial fetch
useEffect(() => {
fetchData('24h')
}, [fetchData])
// Auto-refresh every 60 seconds
useEffect(() => {
const interval = setInterval(() => {
fetchData(state.activePeriod)
}, 60000)
return () => clearInterval(interval)
}, [fetchData, state.activePeriod])
return {
...state,
refetch,
setPeriod,
}
}