|
|
"use client";
|
|
|
|
|
|
import { Truck } from "@phosphor-icons/react";
|
|
|
import type { QuoteDetail, QuotePageStatus } from "@/lib/frontend/types";
|
|
|
import { Card } from "@/components/ui/card";
|
|
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
|
import { ErrorBanner } from "@/components/ui/error-banner";
|
|
|
import { WarningBanner } from "@/components/ui/warning-banner";
|
|
|
import { SecondaryButton } from "@/components/ui/primary-button";
|
|
|
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
|
|
|
import { QuoteCard } from "@/components/quote/quote-card";
|
|
|
import { CountdownTimer } from "@/components/quote/countdown-timer";
|
|
|
|
|
|
interface QuoteResultPanelProps {
|
|
|
status: QuotePageStatus;
|
|
|
quote: QuoteDetail | null;
|
|
|
error: string | null;
|
|
|
onRetry: () => void;
|
|
|
onExpire: () => void;
|
|
|
onExpiredConfirm: () => void;
|
|
|
/** 轮询阶段提示(如 Priority1 填表较慢) */
|
|
|
processingHint?: string;
|
|
|
/** 空闲态副文案 */
|
|
|
idleHint?: string;
|
|
|
}
|
|
|
|
|
|
const STATUS_LABEL: Partial<Record<QuotePageStatus, string>> = {
|
|
|
resolving_address: "正在解析 MotherShip 地址联想…",
|
|
|
validating: "正在提交询价请求…",
|
|
|
processing: "正在查询报价,请稍候…",
|
|
|
};
|
|
|
|
|
|
function LoadingProgressCard({
|
|
|
title,
|
|
|
hint,
|
|
|
}: {
|
|
|
title: string;
|
|
|
hint?: string;
|
|
|
}) {
|
|
|
return (
|
|
|
<Card>
|
|
|
<p className="mb-3 text-sm font-medium text-text-secondary">{title}</p>
|
|
|
{hint ? (
|
|
|
<p className="mb-4 text-xs text-text-secondary">{hint}</p>
|
|
|
) : null}
|
|
|
<div className="mb-4 h-2 w-full overflow-hidden rounded-full bg-bg">
|
|
|
<div className="h-full w-full animate-pulse rounded-full bg-primary/50" />
|
|
|
</div>
|
|
|
<div className="space-y-3">
|
|
|
<Skeleton className="h-8 w-1/3" />
|
|
|
<Skeleton className="h-12 w-2/3" />
|
|
|
<Skeleton className="h-4 w-full" />
|
|
|
<Skeleton className="h-4 w-5/6" />
|
|
|
</div>
|
|
|
</Card>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
export function QuoteResultPanel({
|
|
|
status,
|
|
|
quote,
|
|
|
error,
|
|
|
onRetry,
|
|
|
onExpire,
|
|
|
onExpiredConfirm,
|
|
|
processingHint,
|
|
|
idleHint,
|
|
|
}: QuoteResultPanelProps) {
|
|
|
if (status === "resolving_address") {
|
|
|
return (
|
|
|
<LoadingProgressCard
|
|
|
title={STATUS_LABEL.resolving_address!}
|
|
|
hint="若出现多个精确坐标,将弹出地址确认窗口,请逐项选择后再报价"
|
|
|
/>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
if (status === "validating" || status === "processing") {
|
|
|
return (
|
|
|
<LoadingProgressCard
|
|
|
title={
|
|
|
STATUS_LABEL[status] ?? STATUS_LABEL.processing!
|
|
|
}
|
|
|
hint={
|
|
|
status === "processing"
|
|
|
? processingHint ??
|
|
|
"通常 10~30 秒;若超过 2 分钟仍在等待,请查看 RPA Worker 日志或重启 worker"
|
|
|
: undefined
|
|
|
}
|
|
|
/>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
if (status === "error") {
|
|
|
return (
|
|
|
<ErrorBanner
|
|
|
action={
|
|
|
<SecondaryButton onClick={onRetry}>重试</SecondaryButton>
|
|
|
}
|
|
|
>
|
|
|
{error ?? "暂时无法获取报价,请稍后重试"}
|
|
|
</ErrorBanner>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
if (status === "expired") {
|
|
|
return (
|
|
|
<>
|
|
|
<Card className="relative opacity-60">
|
|
|
{quote && <QuoteCard quote={quote} />}
|
|
|
</Card>
|
|
|
<ConfirmDialog
|
|
|
open
|
|
|
title="报价已过期"
|
|
|
message="当前报价已超过有效期,请重新询价。"
|
|
|
confirmLabel="重新询价"
|
|
|
onConfirm={onExpiredConfirm}
|
|
|
onCancel={onExpiredConfirm}
|
|
|
/>
|
|
|
</>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
if (status === "manual_followup") {
|
|
|
return (
|
|
|
<WarningBanner
|
|
|
action={
|
|
|
<SecondaryButton onClick={onRetry}>重新询价</SecondaryButton>
|
|
|
}
|
|
|
>
|
|
|
{error ?? "表单已提交,站点判定需人工确认细节后报价"}
|
|
|
</WarningBanner>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
if ((status === "success" || status === "fallback") && quote) {
|
|
|
return (
|
|
|
<div className="space-y-4">
|
|
|
{status === "fallback" && (
|
|
|
<WarningBanner>
|
|
|
非实时报价,仅供参考(已使用历史缓存降级)
|
|
|
</WarningBanner>
|
|
|
)}
|
|
|
<QuoteCard quote={quote} />
|
|
|
{quote.valid_until && (
|
|
|
<div className="flex justify-end">
|
|
|
<CountdownTimer
|
|
|
validUntil={quote.valid_until}
|
|
|
onExpire={onExpire}
|
|
|
/>
|
|
|
</div>
|
|
|
)}
|
|
|
</div>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
return (
|
|
|
<Card className="flex min-h-[280px] flex-col items-center justify-center text-center">
|
|
|
<Truck size={48} weight="thin" className="text-text-disabled" />
|
|
|
<p className="mt-3 text-base font-medium text-text-primary">
|
|
|
填写信息后获取报价
|
|
|
</p>
|
|
|
<p className="mt-1 text-sm text-text-secondary">
|
|
|
{idleHint ?? "提交后将返回 MotherShip 全部可用运价档位"}
|
|
|
</p>
|
|
|
</Card>
|
|
|
);
|
|
|
}
|