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
18 changes: 18 additions & 0 deletions src/components/sponsors/reports/LinesManifestView.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,20 @@ export const Destination = ({ name, booth }) => {
);
};

// Values arrive display-ready from the API; reformatting here would desync the
// screen from the CSV.
const AdditionalFields = ({ fields }) => (
<>
{(fields ?? []).map((field, idx) => (
<Box
// Answers carry no id and labels are not unique — as with the row key below.
// eslint-disable-next-line react/no-array-index-key
key={`${field.label}-${idx}`}
>{`${field.label}: ${field.value}`}</Box>
))}
</>
);

// Buckets flat per-line rows into sponsor groups, preserving first-seen order.
//
// Do NOT rely on row adjacency: the backend orders lines by sponsor NAME
Expand Down Expand Up @@ -119,6 +133,7 @@ const HEADERS = [
{ key: "col_destination" },
{ key: "col_checkout_at" },
{ key: "col_notes" },
{ key: "col_additional_fields" },
{ key: "col_quantity", align: "right" },
{ key: "col_used_rate" },
{ key: "col_status" },
Expand Down Expand Up @@ -229,6 +244,9 @@ const LinesManifestView = ({
{formatCheckoutTime(line.purchase?.checkout_at)}
</TableCell>
<TableCell>{line.notes}</TableCell>
<TableCell>
<AdditionalFields fields={line.additional_fields} />
</TableCell>
<TableCell align="right">
{line.is_partially_canceled
? `${liveQuantity(line)} / ${line.quantity}`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,11 @@ describe("LinesManifestView", () => {
// HEADER row, which is a separate array (HEADERS). If the two desync by
// one, every column right of the break silently misaligns and stays
// green. Assert exact header/cell cardinality directly.
it("has exactly 13 column headers matching 13 cells per row", () => {
it("has exactly 14 column headers matching 14 cells per row", () => {
renderView();
expect(screen.getAllByRole("columnheader")).toHaveLength(13);
expect(screen.getAllByRole("columnheader")).toHaveLength(14);
const row = screen.getByText("AV1").closest("tr");
expect(within(row).getAllByRole("cell")).toHaveLength(13);
expect(within(row).getAllByRole("cell")).toHaveLength(14);
});

// Sponsor bucketing (formerly bucketLinesBySponsor, now a private helper).
Expand Down Expand Up @@ -207,6 +207,55 @@ describe("Destination booth fallback", () => {
});
});

describe("Additional Fields column", () => {
it("renders every answered field as 'Label: value', one per line, in the order received", () => {
renderView({
rows: [
line({
additional_fields: [
{ label: "Chair Color", value: "Blue" },
{ label: "Start Time", value: "08:30" }
]
})
]
});
const row = screen.getByText("AV1").closest("tr");
const cells = within(row).getAllByRole("cell");
// Immediately after Notes (index 6), so index 7.
const cell = cells[7];
expect(cell).toHaveTextContent("Chair Color: Blue");
expect(cell).toHaveTextContent("Start Time: 08:30");
// Order received, not sorted: the backend preserves the source snapshot's order
// and the logistics manifest is read in that order.
expect(cell.textContent.indexOf("Chair Color")).toBeLessThan(
cell.textContent.indexOf("Start Time")
);
// One element per entry, so each renders on its own line rather than as one run-on string.
expect(cell.querySelectorAll("div")).toHaveLength(2);
});

it("renders an empty cell for a null, missing or empty additional_fields", () => {
// Three shapes the API can legitimately send. A crash here takes down the whole
// report page, so all three are pinned in one test rather than left to chance.
[
line({ additional_fields: null }),
line({ additional_fields: [] }),
(() => {
const l = line();
delete l.additional_fields;
return l;
})()
].forEach((row) => {
const { unmount } = renderView({ rows: [row] });
const cells = within(screen.getByText("AV1").closest("tr")).getAllByRole(
"cell"
);
expect(cells[7]).toBeEmptyDOMElement();
unmount();
});
});
});

describe("lines_count copy", () => {
it("says the count is of LIVE lines, not all rendered lines", () => {
// The chip is fed liveLineCount, but canceled lines still RENDER, so a group
Expand Down
1 change: 1 addition & 0 deletions src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -4397,6 +4397,7 @@
"col_destination": "Destination",
"col_checkout_at": "Checked Out At",
"col_notes": "Notes",
"col_additional_fields": "Additional Fields",
"col_quantity": "Qty",
"col_sponsor": "Sponsor",
"col_sponsor_note": "Sponsor Note",
Expand Down
Loading