feat(sp6): bulk action bar wired into lanes

This commit is contained in:
Tyler
2026-06-20 18:43:51 -06:00
parent b607e4c459
commit 3157c86340
3 changed files with 224 additions and 9 deletions
+54
View File
@@ -0,0 +1,54 @@
// @vitest-environment happy-dom
import { afterEach, describe, expect, it, vi } from "vitest";
import { cleanup, render, screen, fireEvent } from "@testing-library/react";
import { BulkBar } from "./BulkBar";
afterEach(() => cleanup());
describe("BulkBar", () => {
it("renders action buttons appropriate for the lane", () => {
const { container } = render(
<BulkBar
lane="rejected"
count={3}
onResubmit={vi.fn()}
onDismiss={vi.fn()}
onExport={vi.fn()}
/>,
);
expect(container.textContent).toMatch(/re-?submit/i);
expect(container.textContent).toMatch(/export/i);
expect(container.textContent).not.toMatch(/dismiss/i);
});
it("calls onResubmit when Resubmit clicked", () => {
const onResubmit = vi.fn();
const { container } = render(
<BulkBar
lane="rejected"
count={2}
onResubmit={onResubmit}
onDismiss={() => {}}
onExport={() => {}}
/>,
);
const buttons = container.querySelectorAll("button");
const resubmitBtn = Array.from(buttons).find((b) => /re-?submit/i.test(b.textContent || ""));
expect(resubmitBtn).toBeDefined();
fireEvent.click(resubmitBtn!);
expect(onResubmit).toHaveBeenCalledOnce();
});
it("hides when count is zero", () => {
const { container } = render(
<BulkBar
lane="rejected"
count={0}
onResubmit={() => {}}
onDismiss={() => {}}
onExport={() => {}}
/>,
);
expect(container.firstChild).toBeNull();
});
});
+56
View File
@@ -0,0 +1,56 @@
export function BulkBar({
lane,
count,
onResubmit,
onDismiss,
onExport,
}: {
lane: "rejected" | "candidates" | "unmatched" | "done_today";
count: number;
onResubmit: () => void;
onDismiss: () => void;
onExport: () => void;
}) {
if (count === 0) return null;
return (
<div
className="fixed bottom-4 left-1/2 px-4 py-2 font-mono flex gap-3 z-20"
style={{
transform: "translateX(-50%)",
background: "var(--tt-bg-elev)",
border: "1px solid var(--tt-amber)",
color: "var(--tt-ink)",
fontSize: 11,
}}
>
<span className="tabular-nums" style={{ color: "var(--tt-amber)" }}>
{count} selected
</span>
{lane === "rejected" && (
<button
onClick={onResubmit}
className="hover:underline"
style={{ color: "var(--tt-ink)" }}
>
Re-submit ({count})
</button>
)}
{lane === "candidates" && (
<button
onClick={onDismiss}
className="hover:underline"
style={{ color: "var(--tt-ink)" }}
>
Dismiss ({count})
</button>
)}
<button
onClick={onExport}
className="hover:underline"
style={{ color: "var(--tt-ink)" }}
>
Export CSV ({count})
</button>
</div>
);
}