Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,19 @@ type TeamActionRailProps = {
readonly archiveTeamDisabled?: boolean;
readonly archiveTeamHint?: string;
readonly conversationActionLabel: string;
readonly deploymentsActionLabel?: string;
readonly deploymentsDisabled?: boolean;
readonly deploymentsHint?: string;
readonly editTeamDisabled?: boolean;
readonly editTeamLabel: string;
readonly editTeamHint?: string;
readonly governanceActionLabel?: string;
readonly governanceDisabled?: boolean;
readonly governanceHint?: string;
readonly onArchiveTeam?: () => void;
readonly onOpenConversation: () => void;
readonly onOpenDeployments?: () => void;
readonly onOpenGovernance?: () => void;
readonly onOpenServiceMapping: () => void;
readonly onOpenTeamEditor: () => void;
readonly onOpenTeamBuilder: () => void;
Expand Down Expand Up @@ -72,11 +80,19 @@ export const TeamActionRail: React.FC<TeamActionRailProps> = ({
archiveTeamDisabled = false,
archiveTeamHint,
conversationActionLabel,
deploymentsActionLabel,
deploymentsDisabled = false,
deploymentsHint,
editTeamDisabled = false,
editTeamLabel,
editTeamHint,
governanceActionLabel,
governanceDisabled = false,
governanceHint,
onArchiveTeam,
onOpenConversation,
onOpenDeployments,
onOpenGovernance,
onOpenServiceMapping,
onOpenTeamEditor,
onOpenTeamBuilder,
Expand Down Expand Up @@ -106,6 +122,26 @@ export const TeamActionRail: React.FC<TeamActionRailProps> = ({
<Button onClick={onOpenConversation} style={topActionButtonStyle}>
{conversationActionLabel}
</Button>
{governanceActionLabel && onOpenGovernance ? (
<Button
disabled={governanceDisabled}
onClick={onOpenGovernance}
style={topActionButtonStyle}
title={governanceDisabled ? governanceHint : undefined}
>
{governanceActionLabel}
</Button>
) : null}
{deploymentsActionLabel && onOpenDeployments ? (
<Button
disabled={deploymentsDisabled}
onClick={onOpenDeployments}
style={topActionButtonStyle}
title={deploymentsDisabled ? deploymentsHint : undefined}
>
{deploymentsActionLabel}
</Button>
) : null}
<Button onClick={onOpenTeamBuilder} style={topActionButtonStyle}>
{teamBuilderActionLabel}
</Button>
Expand Down
51 changes: 48 additions & 3 deletions apps/aevatar-console-web/src/pages/teams/detail.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -822,8 +822,10 @@ describe("TeamDetailPage", () => {
expect(screen.getByText("Runtime deltas")).toBeTruthy();
expect(await screen.findByText("Step deltas")).toBeTruthy();
expect(await screen.findByText("Handoff deltas")).toBeTruthy();
expect(screen.getByRole("button", { name: "本次成员对话" })).toBeTruthy();
expect(screen.getByRole("button", { name: "处理等待 Run" })).toBeTruthy();
expect(screen.getByRole("button", { name: "服务映射" })).toBeTruthy();
expect(screen.getByRole("button", { name: "治理绑定" })).toBeTruthy();
expect(screen.getByRole("button", { name: "部署记录" })).toBeTruthy();
expect(screen.getByRole("button", { name: "高级编辑" })).toBeTruthy();
expect(studioApi.getTeam).toHaveBeenCalledWith("scope-1", "t-alpha");
});
Expand Down Expand Up @@ -1358,12 +1360,16 @@ describe("TeamDetailPage", () => {
await screen.findByRole("button", { name: "服务映射" });
fireEvent.click(screen.getByRole("button", { name: "事件流" }));
await screen.findAllByText(/risk_review/);
fireEvent.click(screen.getAllByRole("button", { name: "本次成员对话" })[0]);
fireEvent.click(screen.getAllByRole("button", { name: "处理等待 Run" })[0]);

await waitFor(() => {
expect(window.location.pathname).toBe("/runtime/runs");
});
const draftKey = new URLSearchParams(window.location.search).get("draftKey");
const runsParams = new URLSearchParams(window.location.search);
expect(runsParams.get("runId")).toBe("run-current");
expect(runsParams.get("scopeId")).toBe("scope-1");
expect(runsParams.get("serviceOverrideId")).toBe("default");
const draftKey = runsParams.get("draftKey");
expect(draftKey).toBeTruthy();
expect(loadDraftRunPayload(draftKey)).toMatchObject({
kind: "observed_run_session",
Expand Down Expand Up @@ -1394,6 +1400,45 @@ describe("TeamDetailPage", () => {
expect(params.get("serviceId")).toBe("default");
});

it("opens Platform governance and deployments from top attention actions", async () => {
renderWithQueryClient(React.createElement(TeamDetailPage));

await screen.findByRole("button", { name: "服务映射" });
await waitFor(() => {
expect(screen.getByRole("button", { name: "治理绑定" })).toBeEnabled();
expect(screen.getByRole("button", { name: "部署记录" })).toBeEnabled();
});

fireEvent.click(screen.getByRole("button", { name: "治理绑定" }));

await waitFor(() => {
expect(window.location.pathname).toBe("/governance");
});
let params = new URLSearchParams(window.location.search);
expect(params.get("tenantId")).toBe("scope-1");
expect(params.get("appId")).toBe("default");
expect(params.get("namespace")).toBe("default");
expect(params.get("serviceId")).toBe("default");
expect(params.get("revisionId")).toBe("rev-2");
expect(params.get("view")).toBe("bindings");

act(() => {
history.push("/teams/scope-1/t-alpha");
});
await screen.findByRole("button", { name: "部署记录" });
fireEvent.click(screen.getByRole("button", { name: "部署记录" }));

await waitFor(() => {
expect(window.location.pathname).toBe("/deployments");
});
params = new URLSearchParams(window.location.search);
expect(params.get("tenantId")).toBe("scope-1");
expect(params.get("appId")).toBe("default");
expect(params.get("namespace")).toBe("default");
expect(params.get("serviceId")).toBe("default");
expect(params.get("deploymentId")).toBe("dep-2");
});

it("opens Mission Control from the team event stream with run context", async () => {
renderWithQueryClient(React.createElement(TeamDetailPage));

Expand Down
32 changes: 26 additions & 6 deletions apps/aevatar-console-web/src/pages/teams/detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ import {
AGUIEventType,
CustomEventName,
} from "@aevatar-react-sdk/types";
import {
DeploymentUnitOutlined,
} from "@ant-design/icons";
import type { Edge, Node } from "@xyflow/react";
import { Input, Modal, Space, Typography, message, theme } from "antd";
import { useQuery, useQueryClient } from "@tanstack/react-query";
Expand Down Expand Up @@ -1607,6 +1604,7 @@ const TeamDetailPage: React.FC = () => {
endpointId: "chat",
endpointKind: "chat",
prompt: lens.playback.launchPrompt || undefined,
runId,
route: lens.playback.workflowName || undefined,
scopeId,
serviceId: runtimeServiceId,
Expand Down Expand Up @@ -1776,6 +1774,11 @@ const TeamDetailPage: React.FC = () => {
focusedOperationalUnit?.latestRun?.runId ||
"";
const currentRevisionId = trimText(lens.activeRevision?.revisionId) || "--";
const currentGovernanceRevisionId =
trimText(lens.activeRevision?.revisionId) ||
trimText(lens.currentService?.activeServingRevisionId) ||
trimText(lens.currentService?.defaultServingRevisionId) ||
"";
const currentRevisionStatus =
trimText(lens.activeRevision?.servingState) ||
trimText(lens.activeRevision?.status) ||
Expand Down Expand Up @@ -3140,6 +3143,7 @@ const TeamDetailPage: React.FC = () => {
scopeId,
serviceId: runtimeServiceId,
actorId: lens.currentRun?.actorId || undefined,
runId: lens.currentRun?.runId || undefined,
}),
);
}, [
Expand All @@ -3149,8 +3153,16 @@ const TeamDetailPage: React.FC = () => {
runtimeServiceId,
scopeId,
]);
const conversationActionLabel = lens.playback.currentRunId ? "本次成员对话" : "成员运行记录";
const conversationActionLabel = lens.playback.currentRunId
? lens.healthStatus === "blocked"
? "处理等待 Run"
: lens.healthStatus === "degraded"
? "排查失败 Run"
: "本次成员对话"
: "成员运行记录";
const serviceMappingActionLabel = "服务映射";
const governanceActionLabel = "治理绑定";
const deploymentsActionLabel = "部署记录";
const teamBuilderActionLabel = "高级编辑";
const editTeamActionLabel = "Edit Team";
const canEditSelectedTeam = Boolean(teamSummaryQuery.data && selectedTeamId);
Expand Down Expand Up @@ -3293,11 +3305,11 @@ const TeamDetailPage: React.FC = () => {
history.push(
buildPlatformGovernanceHref({
...platformRouteIdentity,
revisionId: currentRevisionId !== "--" ? currentRevisionId : undefined,
revisionId: currentGovernanceRevisionId || undefined,
view: "bindings",
}),
);
}, [currentRevisionId, platformRouteIdentity]);
}, [currentGovernanceRevisionId, platformRouteIdentity]);
const handleOpenDeployments = React.useCallback(() => {
history.push(
buildPlatformDeploymentsHref({
Expand Down Expand Up @@ -3503,11 +3515,19 @@ const TeamDetailPage: React.FC = () => {
archiveTeamDisabled={!teamSummaryQuery.data || teamArchiving}
archiveTeamHint={archiveTeamHint}
conversationActionLabel={conversationActionLabel}
deploymentsActionLabel={deploymentsActionLabel}
deploymentsDisabled={currentDeploymentId === "--"}
deploymentsHint="当前还没有可定位的部署记录。"
editTeamDisabled={!canEditSelectedTeam}
editTeamHint={editTeamHint}
editTeamLabel={editTeamActionLabel}
governanceActionLabel={governanceActionLabel}
governanceDisabled={!runtimeServiceId}
governanceHint="当前还没有可定位的服务绑定。"
onArchiveTeam={openTeamArchive}
onOpenConversation={handleOpenConversation}
onOpenDeployments={handleOpenDeployments}
onOpenGovernance={handleOpenGovernance}
onOpenServiceMapping={handleOpenServiceMapping}
onOpenTeamEditor={openTeamEditor}
onOpenTeamBuilder={() => history.push(teamBuilderRoute)}
Expand Down
17 changes: 17 additions & 0 deletions apps/aevatar-console-web/src/pages/teams/home.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,23 @@ describe("TeamsHomePage", () => {
expect(screen.queryByText("查看运行")).toBeNull();
});

it("opens the default member run shortcut with current run context", async () => {
renderWithQueryClient(React.createElement(TeamsHomePage));

fireEvent.click(await screen.findByRole("button", { name: "更多" }));
fireEvent.click(await screen.findByText("查看默认成员运行"));

await waitFor(() => {
expect(window.location.pathname).toBe("/runtime/runs");
});

const params = new URLSearchParams(window.location.search);
expect(params.get("actorId")).toBe("actor://workflow-alpha");
expect(params.get("runId")).toBe("run-latest");
expect(params.get("scopeId")).toBe("scope-a");
expect(params.get("serviceOverrideId")).toBe("service-alpha");
});

it("routes Create Team to the real create-team page", async () => {
renderWithQueryClient(React.createElement(TeamsHomePage));

Expand Down
2 changes: 2 additions & 0 deletions apps/aevatar-console-web/src/pages/teams/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,7 @@ function buildMemberRosterPreview(input: {
latestRun?.actorId ||
matchedService?.primaryActorId ||
undefined,
runId: latestRun?.runId || undefined,
scopeId: input.scopeId,
serviceId,
})
Expand Down Expand Up @@ -650,6 +651,7 @@ function buildTeamRosterPreview(input: {
primaryMemberPreview?.serviceId && primaryMemberPreview.serviceId.length > 0
? buildRuntimeRunsHref({
actorId: latestRun?.actorId || undefined,
runId: latestRun?.runId || undefined,
scopeId: input.scopeId,
serviceId: primaryMemberPreview.serviceId,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,21 @@ describe('runtimeRoutes', () => {
});

it('lets runs return back to topology detail routes', () => {
expect(
buildRuntimeRunsHref({
const href = buildRuntimeRunsHref({
actorId: 'actor://selected',
runId: 'run-1',
returnTo: buildRuntimeExplorerHref({
actorId: 'actor://selected',
returnTo: buildRuntimeExplorerHref({
actorId: 'actor://selected',
runId: 'run-1',
}),
runId: 'run-1',
}),
).toContain(
'returnTo=%2Fruntime%2Fexplorer%2Fdetail%3FactorId%3Dactor%253A%252F%252Fselected%26runId%3Drun-1',
});
const url = new URL(href, 'https://console.aevatar.test');

expect(url.pathname).toBe('/runtime/runs');
expect(url.searchParams.get('actorId')).toBe('actor://selected');
expect(url.searchParams.get('runId')).toBe('run-1');
expect(url.searchParams.get('returnTo')).toBe(
'/runtime/explorer/detail?actorId=actor%3A%2F%2Fselected&runId=run-1',
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export function buildRuntimeRunsHref(options?: {
route?: string;
workflow?: string;
prompt?: string;
runId?: string;
scopeId?: string;
serviceOverrideId?: string;
serviceId?: string;
Expand All @@ -64,6 +65,7 @@ export function buildRuntimeRunsHref(options?: {
return buildHref(runtimePaths.runs, {
route: options?.route ?? options?.workflow,
prompt: options?.prompt,
runId: options?.runId,
scopeId: options?.scopeId,
serviceOverrideId: options?.serviceOverrideId ?? options?.serviceId,
endpointId: options?.endpointId,
Expand Down
Loading