import { useCallback, useRef, useState } from "react"; import type { JobResponse } from "../api/types"; import type { OperationRecord, OperationStatus } from "../contracts/workspace"; export interface BeginOperationInput { intent: string; targetKind: OperationRecord["targetKind"]; targetId: string; requester?: string; } export interface OperationUpdate { status?: OperationStatus; job?: JobResponse; message?: string; errorReason?: string; diagnosticId?: string; } export interface OperationTracker { operations: OperationRecord[]; begin: (input: BeginOperationInput) => string; update: (operationId: string, update: OperationUpdate) => void; succeed: (operationId: string, message?: string, job?: JobResponse) => void; fail: (operationId: string, errorReason: string, diagnosticId?: string) => void; isPending: (targetId: string, intent?: string) => boolean; } export function useOperationTracker(): OperationTracker { const [operations, setOperations] = useState([]); const sequence = useRef(0); const begin = useCallback((input: BeginOperationInput): string => { sequence.current += 1; const now = new Date().toISOString(); const record: OperationRecord = { id: `op-${Date.now()}-${sequence.current}`, intent: input.intent, targetKind: input.targetKind, targetId: input.targetId, requester: input.requester ?? "当前用户", status: "pending", createdAt: now, updatedAt: now }; setOperations((current) => [record, ...current]); return record.id; }, []); const update = useCallback((operationId: string, patch: OperationUpdate) => { setOperations((current) => current.map((operation) => operation.id === operationId ? { ...operation, status: patch.status ?? operation.status, jobId: patch.job?.id ?? operation.jobId, jobState: patch.job?.state ?? operation.jobState, message: patch.message ?? operation.message, errorReason: patch.errorReason ?? operation.errorReason, diagnosticId: patch.diagnosticId ?? operation.diagnosticId, updatedAt: new Date().toISOString() } : operation ) ); }, []); const succeed = useCallback( (operationId: string, message?: string, job?: JobResponse) => { update(operationId, { status: "succeeded", message, job }); }, [update] ); const fail = useCallback( (operationId: string, errorReason: string, diagnosticId?: string) => { update(operationId, { status: "failed", errorReason, diagnosticId: diagnosticId ?? operationId }); }, [update] ); const isPending = useCallback( (targetId: string, intent?: string) => operations.some( (operation) => operation.status === "pending" && operation.targetId === targetId && (intent === undefined || operation.intent === intent) ), [operations] ); return { operations, begin, update, succeed, fail, isPending }; }