o2o-infinith-web/src/hooks/useAnalysis.ts

77 lines
2.2 KiB
TypeScript

/**
* useAnalysis — start an analysis + poll status until terminal.
*
* Contract lives in infinith-api/docs/API_CONTRACT.md §2-3.
* Replace `any` with generated types from `src/api-types/schema.d.ts`
* once backend `openapi.json` is stable.
*/
import { useEffect, useRef, useState } from 'react'
import { apiClient } from '@/lib/apiClient'
type Status = 'pending' | 'discovering' | 'collecting' | 'generating' | 'complete' | 'partial' | 'error'
interface StatusResponse {
analysis_run_id: string
status: Status
progress: number
current_step: string
channel_errors: Record<string, string>
completed_at: string | null
}
interface ChannelHandles {
youtube?: string
instagram?: string[]
facebook?: string
naver_blog?: string
gangnam_unni?: string
}
const TERMINAL: Status[] = ['complete', 'partial', 'error']
export function useAnalysis() {
const [status, setStatus] = useState<StatusResponse | null>(null)
const [error, setError] = useState<Error | null>(null)
const pollerRef = useRef<number | null>(null)
useEffect(
() => () => {
if (pollerRef.current) window.clearInterval(pollerRef.current)
},
[],
)
async function start(input: { clinic_id?: string; url?: string; channels: ChannelHandles }) {
try {
const { data } = await apiClient.post('/api/analyses', input)
pollStatus(data.analysis_run_id)
return data.analysis_run_id as string
} catch (err) {
setError(err as Error)
throw err
}
}
function pollStatus(runId: string) {
if (pollerRef.current) window.clearInterval(pollerRef.current)
pollerRef.current = window.setInterval(async () => {
try {
const { data } = await apiClient.get<StatusResponse>(`/api/analyses/${runId}/status`)
setStatus(data)
if (TERMINAL.includes(data.status) && pollerRef.current) {
window.clearInterval(pollerRef.current)
pollerRef.current = null
}
} catch (err) {
setError(err as Error)
if (pollerRef.current) {
window.clearInterval(pollerRef.current)
pollerRef.current = null
}
}
}, 2000)
}
return { start, pollStatus, status, error }
}