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.

150 lines
4.3 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 { 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>
);
}