- 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>
111 lines
2.9 KiB
TypeScript
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,
|
|
}
|
|
}
|