"use client"; import { useCallback, useState } from "react"; import { FlockQuoteForm, type FlockFormSubmitPayload, } from "@/components/flock/flock-quote-form"; import { FlockLoggedInQuoteForm, mapFlockLoggedInToApiInput, type FlockLoggedInQuotePayload, } from "@/components/flock/flock-logged-in-quote-form"; import { FlockQuoteProgress } from "@/components/flock/flock-quote-progress"; import { FlockQuoteResult } from "@/components/flock/flock-quote-result"; import { WarningBanner } from "@/components/ui/warning-banner"; import { ErrorBanner } from "@/components/ui/error-banner"; import { SUBMIT_LOCK_MS } from "@/lib/frontend/constants"; import { hostCreateFlockQuote, hostGetQuote } from "@/lib/frontend/api-client"; import { uuidV4 } from "@/lib/frontend/format"; import type { QuoteDetail, QuotePageStatus } from "@/lib/frontend/types"; import { pollQuoteUntilDone } from "@/hooks/use-quote-polling"; import { getFlockPollTimeoutMs } from "@/lib/flock/poll-timeout"; import { formatQuoteErrorMessage } from "@/modules/quote/quote-error-messages"; export interface FlockQuoteWidgetProps { serviceToken?: string; customerId: string; apiBaseUrl?: string; /** 客户已绑定 Flock 账密 → 显示登录后 Quick 查价界面 */ flockLoggedInUi?: boolean; } /** Flock Freight 内嵌查价:表单 + 可视化进度 + 轮询 + 两档结果 */ export function FlockQuoteWidget({ serviceToken, customerId, apiBaseUrl = "", flockLoggedInUi = false, }: FlockQuoteWidgetProps) { const [status, setStatus] = useState("idle"); const [quote, setQuote] = useState(null); const [error, setError] = useState(null); const [submitLocked, setSubmitLocked] = useState(false); const [activeQuoteId, setActiveQuoteId] = useState(null); const [startedAtMs, setStartedAtMs] = useState(null); const [rpaStage, setRpaStage] = useState(null); const [rpaStageLabel, setRpaStageLabel] = useState(null); const reset = useCallback(() => { setStatus("idle"); setQuote(null); setError(null); setActiveQuoteId(null); setStartedAtMs(null); setRpaStage(null); setRpaStageLabel(null); }, []); const finishQuote = useCallback((detail: QuoteDetail) => { setQuote(detail); setActiveQuoteId(detail.quote_id ?? null); if (detail.rpa_stage) setRpaStage(detail.rpa_stage); if (detail.rpa_stage_label) setRpaStageLabel(detail.rpa_stage_label); if (detail.status === "failed") { setStatus("error"); setError( detail.error_message ?? formatQuoteErrorMessage(detail.error_code) ?? "报价失败", ); return; } if (detail.status === "expired") { setStatus("expired"); return; } if (!detail.flock?.lines?.length) { setStatus("error"); setError("未返回 Flock 报价档位"); return; } setStatus(detail.is_realtime === false ? "fallback" : "success"); setError(null); }, []); const handleSubmit = useCallback( async (payload: FlockFormSubmitPayload & Record) => { if (submitLocked) return; setSubmitLocked(true); setTimeout(() => setSubmitLocked(false), SUBMIT_LOCK_MS); setError(null); setQuote(null); setActiveQuoteId(null); setStartedAtMs(Date.now()); setRpaStage("queued"); setRpaStageLabel("任务已入队,等待 Worker 领取"); setStatus("processing"); const created = await hostCreateFlockQuote(apiBaseUrl, serviceToken, { request_id: uuidV4(), customer_id: customerId, flock_input: payload, }); if (created.code !== 0) { setStatus("error"); setError(created.message || "提交失败"); return; } const { quote_id } = created.data; setActiveQuoteId(quote_id); const pollResult = await pollQuoteUntilDone( async () => { try { const res = await hostGetQuote( apiBaseUrl, serviceToken, customerId, quote_id, ); if (res.code !== 0) { return { ok: false, errorMessage: res.message }; } return { ok: true, data: res.data }; } catch { return { ok: false }; } }, { timeoutMs: getFlockPollTimeoutMs(), onProcessing: (detail) => { if (detail.rpa_stage) setRpaStage(detail.rpa_stage); if (detail.rpa_stage_label) setRpaStageLabel(detail.rpa_stage_label); }, }, ); if (pollResult.type === "done") { finishQuote(pollResult.quote); return; } setStatus("error"); if (pollResult.type === "error" && pollResult.quote?.rpa_stage) { setRpaStage(pollResult.quote.rpa_stage); setRpaStageLabel(pollResult.quote.rpa_stage_label ?? null); } setError( pollResult.type === "timeout" ? "Flock 询价超时,请稍后重试(可在进度条确认停在哪一步)" : pollResult.message || "查询失败", ); }, [apiBaseUrl, customerId, finishQuote, serviceToken, submitLocked], ); const handleLoggedInSubmit = useCallback( (payload: FlockLoggedInQuotePayload) => { const mapped = mapFlockLoggedInToApiInput(payload); void handleSubmit(mapped); }, [handleSubmit], ); return (
{error && status === "error" && {error}} {status === "expired" && ( 报价已过期,请重新查询 )} {status === "success" || status === "fallback" ? ( quote?.flock ? ( ) : null ) : flockLoggedInUi ? ( ) : ( )}
); }