feat(xworkmate): align web home with desktop shell

This commit is contained in:
Haitao Pan 2026-03-21 13:11:19 +08:00
parent ed5a628fa9
commit 4031e47cb4
3 changed files with 888 additions and 604 deletions

View File

@ -55,6 +55,7 @@ type OpenClawAssistantPaneProps = {
variant?: "page" | "sidebar";
showConversation?: boolean;
emptyConversationHint?: string;
integrationsHref?: string;
onStateChange?: (state: OpenClawAssistantViewState) => void;
};
@ -401,6 +402,7 @@ export function OpenClawAssistantPane({
variant = "page",
showConversation = true,
emptyConversationHint,
integrationsHref = "/panel/api",
onStateChange,
}: OpenClawAssistantPaneProps) {
const router = useRouter();
@ -1292,7 +1294,7 @@ export function OpenClawAssistantPane({
<button
type="button"
onClick={() => router.push("/panel/api")}
onClick={() => router.push(integrationsHref)}
className="tactile-button tactile-button-primary border border-[color:var(--color-primary-border)] px-3 text-xs text-[var(--color-primary-foreground)]"
title={copy.integrations}
>
@ -1378,7 +1380,7 @@ export function OpenClawAssistantPane({
</div>
<button
type="button"
onClick={() => router.push("/panel/api")}
onClick={() => router.push(integrationsHref)}
className="tactile-button tactile-button-primary px-4 text-sm"
>
{copy.openIntegrations}

View File

@ -0,0 +1,120 @@
import { fireEvent, render, screen } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { IntegrationDefaults } from "@/lib/openclaw/types";
import { XWorkmateWorkspacePage } from "@/components/xworkmate/XWorkmateWorkspacePage";
const pushMock = vi.fn();
const assistantPaneMock = vi.fn();
const mockStore = {
setScope: vi.fn(),
applyDefaults: vi.fn(),
setSelectedSessionKey: vi.fn(),
selectedSessionKey: "",
openclawUrl: "",
vaultUrl: "",
apisixUrl: "",
};
vi.mock("next/navigation", () => ({
useRouter: () => ({
push: pushMock,
}),
}));
vi.mock("@/i18n/LanguageProvider", () => ({
useLanguage: () => ({
language: "zh",
}),
}));
vi.mock("@/state/openclawConsoleStore", () => ({
useOpenClawConsoleStore: (selector: (state: typeof mockStore) => unknown) =>
selector(mockStore),
}));
vi.mock("@/components/openclaw/OpenClawAssistantPane", () => ({
OpenClawAssistantPane: (props: { integrationsHref?: string }) => {
assistantPaneMock(props);
return (
<div data-testid="assistant-pane">
assistant-pane:{props.integrationsHref ?? "missing"}
</div>
);
},
}));
const emptyDefaults: IntegrationDefaults = {
openclawUrl: "",
openclawOrigin: "",
openclawTokenConfigured: false,
vaultUrl: "",
vaultNamespace: "",
vaultTokenConfigured: false,
vaultSecretPath: "",
vaultSecretKey: "",
apisixUrl: "",
apisixTokenConfigured: false,
};
describe("XWorkmateWorkspacePage", () => {
beforeEach(() => {
pushMock.mockReset();
assistantPaneMock.mockReset();
mockStore.setScope.mockReset();
mockStore.applyDefaults.mockReset();
mockStore.setSelectedSessionKey.mockReset();
mockStore.selectedSessionKey = "";
mockStore.openclawUrl = "";
mockStore.vaultUrl = "";
mockStore.apisixUrl = "";
});
it("renders the desktop-style AI Gateway empty state and routes to xworkmate integrations", () => {
render(
<XWorkmateWorkspacePage
defaults={emptyDefaults}
profile={null}
scopeKey="test-scope"
/>,
);
expect(screen.getByText("先配置 AI Gateway")).toBeInTheDocument();
expect(
screen.getByText(
/请先在 Settings -> AI Gateway 中配置地址、API Key 和默认模型/,
),
).toBeInTheDocument();
fireEvent.click(
screen.getAllByRole("button", { name: "配置 AI Gateway" })[0],
);
expect(pushMock).toHaveBeenCalledWith("/xworkmate/integrations");
});
it("renders the assistant pane when a gateway target is available", () => {
const connectedDefaults: IntegrationDefaults = {
...emptyDefaults,
openclawUrl: "wss://gateway.example.com",
openclawTokenConfigured: true,
};
mockStore.openclawUrl = "wss://gateway.example.com";
render(
<XWorkmateWorkspacePage
defaults={connectedDefaults}
profile={null}
scopeKey="test-scope"
/>,
);
expect(screen.getByTestId("assistant-pane")).toBeInTheDocument();
expect(assistantPaneMock).toHaveBeenCalledWith(
expect.objectContaining({
integrationsHref: "/xworkmate/integrations",
}),
);
});
});

File diff suppressed because it is too large Load Diff