"use client" import { SignJWT } from "jose" import { CheckIcon, CopyIcon } from "lucide-react" import { useEffect, useMemo, useState } from "react" import type { AgentDeskConfig } from "@/lib/sdk/config-types" import { useI18n } from "@/i18n/provider" const STORAGE_KEY = "agent-desk-web-widget-test-config" const DEFAULT_JWT_TTL_MINUTES = "30" const INITIAL_CONFIG: AgentDeskConfig = { channelId: "", baseUrl: "", apiBaseUrl: "", } type AuthMode = "guest" | "jwt" type WidgetDemoConfig = AgentDeskConfig & { authMode?: AuthMode jwtSecret?: string jwtUserId?: string jwtName?: string jwtTtlMinutes?: string } function getDefaultConfig(defaultName: string): WidgetDemoConfig { if (typeof window === "undefined") { return INITIAL_CONFIG } const savedText = window.localStorage.getItem(STORAGE_KEY) const savedConfig = savedText ? (JSON.parse(savedText) as Partial) : {} const query = new URLSearchParams(window.location.search) return { channelId: query.get("channelId") ?? savedConfig.channelId ?? "", baseUrl: "", apiBaseUrl: "", authMode: (query.get("authMode") as AuthMode | null) ?? savedConfig.authMode ?? "guest", jwtSecret: savedConfig.jwtSecret ?? "", jwtUserId: query.get("userId") ?? savedConfig.jwtUserId ?? "demo-user-001", jwtName: query.get("name") ?? savedConfig.jwtName ?? defaultName, jwtTtlMinutes: savedConfig.jwtTtlMinutes ?? DEFAULT_JWT_TTL_MINUTES, } } function removeMountedWidget() { if (typeof window === "undefined") { return } window.AgentDeskWidget?.destroy() document .querySelectorAll( '[data-agent-desk-widget="launcher"], [data-agent-desk-widget="frame"], [data-agent-desk-widget="script"]' ) .forEach((node) => node.remove()) delete window.AgentDeskConfig delete window.__CS_AI_AGENT_WIDGET_CONFIG__ delete window.__CS_AI_AGENT_WIDGET_STATE__ delete window.AgentDeskWidget } function injectWidget(config: AgentDeskConfig) { removeMountedWidget() window.AgentDeskConfig = config const script = document.createElement("script") script.async = true script.src = `${window.location.origin}/sdk/agent-desk-sdk.min.js` script.dataset.agentDeskWidget = "script" document.body.appendChild(script) } function buildWidgetConfig(config: WidgetDemoConfig): AgentDeskConfig { const nextConfig: AgentDeskConfig = { channelId: config.channelId.trim(), baseUrl: "", apiBaseUrl: "", } if (config.authMode === "jwt") { nextConfig.getUserToken = undefined } return nextConfig } async function signUserToken(config: WidgetDemoConfig, t: (key: string) => string) { const userId = (config.jwtUserId || "").trim() const name = (config.jwtName || "").trim() const secret = (config.jwtSecret || "").trim() const ttl = Number(config.jwtTtlMinutes || DEFAULT_JWT_TTL_MINUTES) if (!userId) { throw new Error(t("widgetDemo.missingUserId")) } if (!name) { throw new Error(t("widgetDemo.missingName")) } if (!secret) { throw new Error(t("widgetDemo.missingSecret")) } if (!Number.isFinite(ttl) || ttl <= 0) { throw new Error(t("widgetDemo.invalidTtl")) } return new SignJWT({ userId, name }) .setProtectedHeader({ alg: "HS256", typ: "JWT" }) .setIssuedAt() .setExpirationTime(`${ttl}m`) .sign(new TextEncoder().encode(secret)) } export function SupportWidgetDemo() { const t = useI18n() const [config, setConfig] = useState({ ...INITIAL_CONFIG, authMode: "guest", jwtSecret: "", jwtUserId: "demo-user-001", jwtName: t("widgetDemo.defaultName"), jwtTtlMinutes: DEFAULT_JWT_TTL_MINUTES, }) const [status, setStatus] = useState(t("widgetDemo.missingChannel")) const [origin, setOrigin] = useState("") const [generatedToken, setGeneratedToken] = useState("") const [latestDirectChatUrl, setLatestDirectChatUrl] = useState("") const [copied, setCopied] = useState(false) const [snippetCopied, setSnippetCopied] = useState(false) async function mountWidget(configToMount: WidgetDemoConfig) { const cleanConfig = { ...configToMount, channelId: configToMount.channelId.trim(), baseUrl: "", apiBaseUrl: "", getUserToken: undefined, } const nextConfig = buildWidgetConfig(cleanConfig) if (cleanConfig.authMode === "jwt") { nextConfig.getUserToken = () => signUserToken(cleanConfig, t) } setConfig(cleanConfig) setGeneratedToken("") setLatestDirectChatUrl("") window.localStorage.setItem(STORAGE_KEY, JSON.stringify(cleanConfig)) if (!nextConfig.channelId) { removeMountedWidget() setStatus(t("widgetDemo.missingChannel")) return } injectWidget(nextConfig) setStatus( cleanConfig.authMode === "jwt" ? t("widgetDemo.mountedJwt") : t("widgetDemo.mountedGuest") ) } useEffect(() => { const timer = window.setTimeout(() => { const initialConfig = getDefaultConfig(t("widgetDemo.defaultName")) setOrigin(window.location.origin) setConfig(initialConfig) setStatus(initialConfig.channelId ? t("widgetDemo.mounted") : t("widgetDemo.missingChannel")) if (initialConfig.channelId) { void mountWidget(initialConfig).catch((error) => { removeMountedWidget() setGeneratedToken("") setStatus(error instanceof Error ? error.message : t("widgetDemo.mountFailed")) }) } }, 0) return () => { window.clearTimeout(timer) removeMountedWidget() } }, [t]) const snippet = useMemo(() => { const scriptSrc = origin ? `${origin}/sdk/agent-desk-sdk.min.js` : "/sdk/agent-desk-sdk.min.js" const configLines = [` channelId: "${config.channelId || ""}"`] if (config.authMode === "jwt") { configLines.push(` async getUserToken() { const res = await fetch("/api/support/user-token", { credentials: "include" }); const data = await res.json(); return data.userToken; }`) } return ` ` }, [config.authMode, config.channelId, origin]) function updateField( key: K, value: WidgetDemoConfig[K] ) { setConfig((current) => ({ ...current, [key]: value })) } async function handleMount() { try { await mountWidget(config) } catch (error) { removeMountedWidget() setGeneratedToken("") setStatus(error instanceof Error ? error.message : t("widgetDemo.mountFailed")) } } async function handleCopyDirectUrl() { if (!window.AgentDeskWidget || typeof navigator === "undefined") { return } try { const url = await window.AgentDeskWidget.getChatUrl() setLatestDirectChatUrl(url) if (config.authMode === "jwt") { setGeneratedToken(new URL(url).searchParams.get("userToken") || "") } await navigator.clipboard.writeText(url) setCopied(true) window.setTimeout(() => setCopied(false), 1600) } catch (error) { setStatus(error instanceof Error ? error.message : t("widgetDemo.linkFailed")) } } async function handleOpenDirectChat() { if (!window.AgentDeskWidget) { return } try { const url = await window.AgentDeskWidget.getChatUrl() setLatestDirectChatUrl(url) if (config.authMode === "jwt") { setGeneratedToken(new URL(url).searchParams.get("userToken") || "") } window.open(url, "_blank", "noopener,noreferrer") } catch (error) { setStatus(error instanceof Error ? error.message : t("widgetDemo.linkFailed")) } } async function handleCopySnippet() { if (typeof navigator === "undefined") { return } await navigator.clipboard.writeText(snippet) setSnippetCopied(true) window.setTimeout(() => setSnippetCopied(false), 1600) } return (
{t("widgetDemo.title")}
{status}
updateField("channelId", value)} /> updateField("authMode", value)} options={[ { label: t("widgetDemo.guest"), value: "guest" }, { label: t("widgetDemo.jwtUser"), value: "jwt" }, ]} /> {config.authMode === "jwt" ? (
updateField("jwtUserId", value)} /> updateField("jwtName", value)} /> updateField("jwtSecret", value)} type="password" /> updateField("jwtTtlMinutes", value)} type="number" />
) : null}
{t("widgetDemo.snippetTitle")}
{config.authMode === "jwt" ? (
{t("widgetDemo.jwtNotice")}
) : null}
              {snippet}
            
{t("widgetDemo.directChat")}
{generatedToken ? (
{t("widgetDemo.currentToken")}