Files
awoooi/apps/web/src/hooks/useErrors.ts
OG T 5ee139749a chore(lint): 清理 7 項 ESLint 警告
- useApprovalSSE.ts: 標記未使用的 fallbackToPolling
- useErrors.ts: 移除未使用的 ErrorListResponse import
- dashboard.store.ts: 標記 SSE event 參數
- agent.store.ts: 加註 SSE 串流迴圈說明
- approval.store.ts: 改用正規 type import
- terminal.store.ts: 改用 inline type import
- OmniTerminal.tsx: 改用 type import

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-29 16:40:19 +08:00

111 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 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,
}
}