fix(uplifts): keep a failed queue from rendering as an empty archive

This commit is contained in:
Alexandre Brandizzi 2026-09-25 16:48:46 -03:00
parent 70501693c8
commit 3ed45547db
3 changed files with 51 additions and 11 deletions

View file

@ -295,9 +295,31 @@ function UpliftApprovalsEmptyState({ tab }: { tab: UpliftApprovalTab }) {
);
}
// A failed queue is not an empty one: the page's error alert explains it, so the
// empty-state copy must not also claim the list has nothing in it.
function UpliftApprovalsEmptyRow({
tab,
colSpan,
hasError,
}: {
tab: UpliftApprovalTab;
colSpan: number;
hasError: boolean;
}) {
if (hasError) return null;
return (
<TableRow>
<TableCell colSpan={colSpan} align="center" className="py-10">
<UpliftApprovalsEmptyState tab={tab} />
</TableCell>
</TableRow>
);
}
export function UpliftApprovalsTable({
tab,
isLoading,
hasError,
rows,
canRevoke,
onOpenDetail,
@ -309,6 +331,7 @@ export function UpliftApprovalsTable({
}: {
tab: UpliftApprovalTab;
isLoading: boolean;
hasError: boolean;
rows: UpliftQueueItem[];
canRevoke: boolean;
onOpenDetail: (row: UpliftQueueItem) => void;
@ -339,11 +362,7 @@ export function UpliftApprovalsTable({
</TableCell>
</TableRow>
) : rows.length === 0 ? (
<TableRow>
<TableCell colSpan={headerCells.length} align="center" className="py-10">
<UpliftApprovalsEmptyState tab={tab} />
</TableCell>
</TableRow>
<UpliftApprovalsEmptyRow tab={tab} colSpan={headerCells.length} hasError={hasError} />
) : (
rows.map((row) => (
<UpliftApprovalRow

View file

@ -173,6 +173,7 @@ export default function UpliftQueuePage() {
approved: knownTotal(approvedQuery),
rejected: knownTotal(rejectedQuery),
};
const activeTotal = counts[tab];
const pendingExposureTotal = pendingQuery.data?.pendingExposureTotal ?? null;
const handleTabChange = (_event: unknown, value: string) => {
@ -189,6 +190,7 @@ export default function UpliftQueuePage() {
<UpliftApprovalsTable
tab={tab}
isLoading={activeQuery.isLoading}
hasError={Boolean(activeQuery.error)}
rows={rows}
canRevoke={canRevokeUplifts}
onOpenDetail={openDetail}
@ -198,11 +200,13 @@ export default function UpliftQueuePage() {
onOpenAttachment={openUpliftEvidence}
isDecisionPending={approvePending || rejectPending || revokePending}
/>
<UpliftApprovalsPagination
count={activeQuery.data?.totalCount ?? 0}
page={pages[tab]}
onPageChange={(page) => setPages((current) => ({ ...current, [tab]: page }))}
/>
{activeTotal != null && (
<UpliftApprovalsPagination
count={activeTotal}
page={pages[tab]}
onPageChange={(page) => setPages((current) => ({ ...current, [tab]: page }))}
/>
)}
{detailItem != null && (
<UpliftDetailModal
item={detailItem}

View file

@ -1,5 +1,5 @@
import { fireEvent, screen, within } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type {
UpliftQueueItem,
UpliftsQueueParams,
@ -128,6 +128,12 @@ async function openRejectedTab() {
}
describe("Uplift Approvals rejected tab", () => {
// Load the page module once up front so the first test's budget is not spent on a
// cold import of the whole route.
beforeAll(async () => {
await import("@/app/(protected)/uplifts/index");
}, 60_000);
beforeEach(() => {
queueState.requestedStatuses = [];
queueState.errorByStatus = {};
@ -164,6 +170,17 @@ describe("Uplift Approvals rejected tab", () => {
expect(screen.getByLabelText("1 pending uplifts")).toBeInTheDocument();
});
it("shows the load error instead of an empty archive when the rejected queue fails", async () => {
queueState.byStatus.Rejected = undefined;
queueState.errorByStatus.Rejected = new Error("Failed to load uplift queue");
const table = await openRejectedTab();
expect(screen.getByRole("alert")).toHaveTextContent("Failed to load uplift queue");
expect(within(table).queryByText("No rejected uplifts.")).not.toBeInTheDocument();
expect(within(table).queryByTestId("HighlightOffOutlinedIcon")).not.toBeInTheDocument();
expect(screen.queryByText(/of 0/)).not.toBeInTheDocument();
});
it("lists only rejected uplifts, in the server's most-recently-rejected order", async () => {
const table = await openRejectedTab();