|
|
"use client";
|
|
|
|
|
|
import { useEffect, useState } from "react";
|
|
|
import { useSearchParams } from "next/navigation";
|
|
|
import { AppLayout } from "@/components/layout/app-layout";
|
|
|
import { QuoteRouteHub } from "@/components/embed/quote-route-hub";
|
|
|
import { EmbedDemoLogin } from "@/components/embed/embed-demo-login";
|
|
|
import { Card } from "@/components/ui/card";
|
|
|
import { SecondaryButton } from "@/components/ui/primary-button";
|
|
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
|
import { ErrorBanner } from "@/components/ui/error-banner";
|
|
|
import {
|
|
|
isEmbedHostedShell,
|
|
|
parseEmbedSsoParams,
|
|
|
stripEmbedSsoQueryFromUrl,
|
|
|
} from "@/lib/embed/sso-params";
|
|
|
import {
|
|
|
embedDemoLogin,
|
|
|
embedDemoLoginWithKey,
|
|
|
embedDemoLogout,
|
|
|
embedDemoMe,
|
|
|
type EmbedDemoSession,
|
|
|
} from "@/lib/frontend/api-client";
|
|
|
|
|
|
export function EmbedDemoClient() {
|
|
|
const searchParams = useSearchParams();
|
|
|
const [booting, setBooting] = useState(true);
|
|
|
const [session, setSession] = useState<EmbedDemoSession | null>(null);
|
|
|
const [bootError, setBootError] = useState<string | null>(null);
|
|
|
const [hostedShell, setHostedShell] = useState(false);
|
|
|
|
|
|
useEffect(() => {
|
|
|
let cancelled = false;
|
|
|
|
|
|
void (async () => {
|
|
|
const params = new URLSearchParams(searchParams.toString());
|
|
|
const sso = parseEmbedSsoParams(params);
|
|
|
const hosted = isEmbedHostedShell(params);
|
|
|
if (!cancelled) setHostedShell(hosted);
|
|
|
|
|
|
// 宿主直传凭证:自动登录,跳过登录页(勿先 logout)
|
|
|
if (sso.mode === "api_key") {
|
|
|
const res = await embedDemoLoginWithKey("", sso.apiKey);
|
|
|
if (cancelled) return;
|
|
|
if (res.code === 0) {
|
|
|
stripEmbedSsoQueryFromUrl();
|
|
|
setSession(res.data);
|
|
|
setBootError(null);
|
|
|
setBooting(false);
|
|
|
return;
|
|
|
}
|
|
|
setBootError(res.message || "API Key 无效,无法自动登录");
|
|
|
setBooting(false);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (sso.mode === "password") {
|
|
|
const res = await embedDemoLogin("", sso.customerId, sso.password);
|
|
|
if (cancelled) return;
|
|
|
if (res.code === 0) {
|
|
|
stripEmbedSsoQueryFromUrl();
|
|
|
setSession(res.data);
|
|
|
setBootError(null);
|
|
|
setBooting(false);
|
|
|
return;
|
|
|
}
|
|
|
setBootError(res.message || "客户编号或密码错误,无法自动登录");
|
|
|
setBooting(false);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 无 SSO:尝试恢复已有 cookie 会话(刷新不掉登录)
|
|
|
const me = await embedDemoMe("");
|
|
|
if (cancelled) return;
|
|
|
if (me.code === 0) {
|
|
|
setSession(me.data);
|
|
|
setBootError(null);
|
|
|
setBooting(false);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
setSession(null);
|
|
|
setBooting(false);
|
|
|
})();
|
|
|
|
|
|
return () => {
|
|
|
cancelled = true;
|
|
|
};
|
|
|
}, [searchParams]);
|
|
|
|
|
|
const isKeyLogin = session?.login_type === "api_key";
|
|
|
|
|
|
const handleLogout = async () => {
|
|
|
await embedDemoLogout("");
|
|
|
setSession(null);
|
|
|
setBootError(null);
|
|
|
setHostedShell(false);
|
|
|
};
|
|
|
|
|
|
if (booting) {
|
|
|
return (
|
|
|
<AppLayout title="宿主嵌入演示">
|
|
|
<Skeleton className="h-64 w-full" />
|
|
|
</AppLayout>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
if (!session) {
|
|
|
return (
|
|
|
<AppLayout title="宿主嵌入演示">
|
|
|
{bootError ? (
|
|
|
<div className="mb-4">
|
|
|
<ErrorBanner>{bootError}</ErrorBanner>
|
|
|
</div>
|
|
|
) : null}
|
|
|
<EmbedDemoLogin
|
|
|
onSuccess={(data) => {
|
|
|
setSession(data);
|
|
|
setBootError(null);
|
|
|
}}
|
|
|
/>
|
|
|
</AppLayout>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
return (
|
|
|
<AppLayout title="宿主嵌入演示">
|
|
|
<Card className="mb-4 flex flex-wrap items-center justify-between gap-3">
|
|
|
<div>
|
|
|
<p className="text-sm text-text-secondary">
|
|
|
当前登录客户 · {isKeyLogin ? "API Key 登录" : "账号密码登录"}
|
|
|
{hostedShell ? " · 宿主直连" : ""}
|
|
|
</p>
|
|
|
<p className="text-lg font-semibold">{session.customer_id}</p>
|
|
|
<p className="mt-1 text-xs text-text-secondary">
|
|
|
请选择 MotherShip 或 Flock Freight 模块开始查价
|
|
|
</p>
|
|
|
</div>
|
|
|
{!hostedShell ? (
|
|
|
<SecondaryButton onClick={() => void handleLogout()}>
|
|
|
退出登录
|
|
|
</SecondaryButton>
|
|
|
) : null}
|
|
|
</Card>
|
|
|
|
|
|
<QuoteRouteHub customerId={session.customer_id} apiBaseUrl="" />
|
|
|
</AppLayout>
|
|
|
);
|
|
|
}
|