feat: add provisioning indicator for board lead in task panel and implement related tests
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
|||||||
Plus,
|
Plus,
|
||||||
Pencil,
|
Pencil,
|
||||||
Play,
|
Play,
|
||||||
|
RefreshCcw,
|
||||||
Settings,
|
Settings,
|
||||||
ShieldCheck,
|
ShieldCheck,
|
||||||
X,
|
X,
|
||||||
@@ -1794,6 +1795,12 @@ export default function BoardDetailPage() {
|
|||||||
});
|
});
|
||||||
}, [agents, workingAgentIds]);
|
}, [agents, workingAgentIds]);
|
||||||
|
|
||||||
|
const boardLead = useMemo(
|
||||||
|
() => agents.find((agent) => agent.is_board_lead) ?? null,
|
||||||
|
[agents],
|
||||||
|
);
|
||||||
|
const isBoardLeadProvisioning = boardLead?.status === "provisioning";
|
||||||
|
|
||||||
const loadComments = useCallback(
|
const loadComments = useCallback(
|
||||||
async (taskId: string) => {
|
async (taskId: string) => {
|
||||||
if (!isSignedIn || !boardId) return;
|
if (!isSignedIn || !boardId) return;
|
||||||
@@ -2347,6 +2354,12 @@ export default function BoardDetailPage() {
|
|||||||
<p className="mt-1 text-sm text-slate-500">
|
<p className="mt-1 text-sm text-slate-500">
|
||||||
Keep tasks moving through your workflow.
|
Keep tasks moving through your workflow.
|
||||||
</p>
|
</p>
|
||||||
|
{isBoardLeadProvisioning ? (
|
||||||
|
<div className="mt-3 inline-flex items-center gap-2 rounded-lg border border-amber-200 bg-amber-50 px-3 py-1.5 text-xs font-medium text-amber-800">
|
||||||
|
<RefreshCcw className="h-3.5 w-3.5 animate-spin" />
|
||||||
|
<span>Provisioning board lead…</span>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap items-center gap-3">
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
<div className="flex items-center gap-1 rounded-lg bg-slate-100 p-1">
|
<div className="flex items-center gap-1 rounded-lg bg-slate-100 p-1">
|
||||||
|
|||||||
90
frontend/src/components/BoardApprovalsPanel.test.tsx
Normal file
90
frontend/src/components/BoardApprovalsPanel.test.tsx
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
import type { ApprovalRead } from "@/api/generated/model";
|
||||||
|
import { BoardApprovalsPanel } from "./BoardApprovalsPanel";
|
||||||
|
|
||||||
|
vi.mock("@/auth/clerk", () => ({
|
||||||
|
useAuth: () => ({ isSignedIn: true }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("recharts", () => {
|
||||||
|
type BoxProps = React.PropsWithChildren<{ className?: string }>;
|
||||||
|
const Box = ({ children, className }: BoxProps) => (
|
||||||
|
<div className={className}>{children}</div>
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
ResponsiveContainer: Box,
|
||||||
|
Tooltip: Box,
|
||||||
|
Legend: Box,
|
||||||
|
PieChart: Box,
|
||||||
|
Pie: Box,
|
||||||
|
Cell: Box,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const renderWithQueryClient = (ui: React.ReactNode) => {
|
||||||
|
const queryClient = new QueryClient({
|
||||||
|
defaultOptions: {
|
||||||
|
queries: { retry: false },
|
||||||
|
mutations: { retry: false },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return render(
|
||||||
|
<QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("BoardApprovalsPanel", () => {
|
||||||
|
it("renders nested linked-approval metadata and rubric scores", () => {
|
||||||
|
const approval = {
|
||||||
|
id: "approval-1",
|
||||||
|
board_id: "board-1",
|
||||||
|
action_type: "task.create",
|
||||||
|
confidence: 62,
|
||||||
|
status: "pending",
|
||||||
|
task_id: "task-1",
|
||||||
|
created_at: "2026-02-12T10:00:00Z",
|
||||||
|
resolved_at: null,
|
||||||
|
payload: {
|
||||||
|
linked_request: {
|
||||||
|
tasks: [
|
||||||
|
{
|
||||||
|
title: "Launch onboarding checklist",
|
||||||
|
description: "Create and validate the v1 onboarding checklist.",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
task_ids: ["task-1", "task-2"],
|
||||||
|
},
|
||||||
|
decision: { reason: "Needs explicit sign-off before rollout." },
|
||||||
|
analytics: {
|
||||||
|
rubric_scores: {
|
||||||
|
clarity: 25,
|
||||||
|
risk: 20,
|
||||||
|
dependencies: 15,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
task_ids: ["task-1", "task-2"],
|
||||||
|
rubric_scores: null,
|
||||||
|
} as ApprovalRead;
|
||||||
|
|
||||||
|
renderWithQueryClient(
|
||||||
|
<BoardApprovalsPanel boardId="board-1" approvals={[approval]} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.getAllByText("Launch onboarding checklist").length,
|
||||||
|
).toBeGreaterThan(0);
|
||||||
|
expect(
|
||||||
|
screen.getByText("Create and validate the v1 onboarding checklist."),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByText("Needs explicit sign-off before rollout."),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(/rubric scores/i)).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("Clarity")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user