You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
chajia/components/priority1/priority1-quote-result-pane...

181 lines
5.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"use client";
import { CountdownTimer } from "@/components/quote/countdown-timer";
import { ErrorBanner } from "@/components/ui/error-banner";
import { LoadingSpinner } from "@/components/ui/loading-spinner";
import { WarningBanner } from "@/components/ui/warning-banner";
import type { QuoteDetail } from "@/lib/frontend/types";
import { PRIORITY1_MANUAL_FOLLOWUP_ERROR_CODE } from "@/modules/priority1/constants";
import { Priority1FtlScheduleCalendar } from "@/components/priority1/priority1-ftl-schedule-calendar";
import type { Priority1ShipmentSummary } from "@/components/priority1/priority1-ftl-schedule-calendar";
import { Priority1LtlQuoteOptions } from "@/components/priority1/priority1-ltl-quote-options";
type Props = {
quote: QuoteDetail | null;
loading: boolean;
error: string | null;
shipmentContext?: Priority1ShipmentSummary;
onRequestNewQuote?: () => void;
};
export function Priority1QuoteResultPanel({
quote,
loading,
error,
shipmentContext,
onRequestNewQuote,
}: Props) {
if (loading) {
return (
<div className="flex min-h-[320px] flex-col items-center justify-center gap-4 rounded-lg border border-neutral-200 bg-white p-8">
<LoadingSpinner />
<p className="text-sm text-neutral-600"> Priority1 </p>
</div>
);
}
if (error) {
return (
<ErrorBanner>
{error.trim() || "暂时无法获取报价,请稍后重试。"}
</ErrorBanner>
);
}
if (!quote) {
return (
<div className="flex min-h-[320px] items-center justify-center rounded-lg border border-dashed border-neutral-300 bg-neutral-50 p-8 text-sm text-neutral-500">
Priority1
</div>
);
}
if (quote.status === "processing") {
return (
<div className="flex min-h-[320px] flex-col items-center justify-center gap-4 rounded-lg border border-neutral-200 bg-white p-8">
<LoadingSpinner />
<p className="text-sm text-neutral-600"></p>
</div>
);
}
if (quote.status === "failed") {
const message =
quote.error_message?.trim() ||
"报价失败,请稍后重试。";
return (
<ErrorBanner
action={
onRequestNewQuote ? (
<button
type="button"
onClick={onRequestNewQuote}
className="text-sm font-medium text-error underline"
>
</button>
) : undefined
}
>
{message}
</ErrorBanner>
);
}
if (quote.error_code === PRIORITY1_MANUAL_FOLLOWUP_ERROR_CODE) {
return (
<WarningBanner
action={
onRequestNewQuote ? (
<button
type="button"
onClick={onRequestNewQuote}
className="text-sm font-medium text-amber-900 underline"
>
</button>
) : undefined
}
>
<p className="font-medium"></p>
<p className="mt-1">
{quote.error_message ??
"Priority1 未返回可展示的即时报价,已记录任务供人工处理。"}
</p>
</WarningBanner>
);
}
if (quote.status === "expired") {
return (
<WarningBanner
action={
onRequestNewQuote ? (
<button
type="button"
onClick={onRequestNewQuote}
className="text-sm font-medium text-amber-900 underline"
>
</button>
) : undefined
}
>
<p className="font-medium"></p>
<p className="mt-1"></p>
</WarningBanner>
);
}
const p1 = quote.priority1;
if (!p1 || p1.lines.length === 0) {
const legacyHint =
quote.source_type === "stale"
? "本次使用了历史缓存降级,数据格式可能不兼容,请重新询价。"
: quote.quotes && quote.quotes.length > 0
? "服务端返回了旧版报价格式,请重新部署 worker 与 API 后重试。"
: "RPA 未抓取到报价金额,请核对重量/等级是否合理后重试。";
return (
<WarningBanner
action={
onRequestNewQuote ? (
<button
type="button"
onClick={onRequestNewQuote}
className="text-sm font-medium text-amber-900 underline"
>
</button>
) : undefined
}
>
<p className="font-medium"></p>
<p className="mt-1">{legacyHint}</p>
</WarningBanner>
);
}
const validUntil = quote.valid_until;
return (
<div className="overflow-hidden rounded-lg border border-neutral-800">
{validUntil ? (
<div className="flex items-center justify-between border-b border-neutral-200 bg-neutral-50 px-4 py-2 text-xs text-neutral-600">
<span></span>
<CountdownTimer validUntil={validUntil} />
</div>
) : null}
{p1.display_mode === "ftl_calendar" && shipmentContext ? (
<Priority1FtlScheduleCalendar
lines={p1.lines}
shipment={shipmentContext}
onRequestNewQuote={onRequestNewQuote}
/>
) : (
<Priority1LtlQuoteOptions lines={p1.lines} />
)}
</div>
);
}