feat(stock-orders): wrap cells + add download PDF buttons

Two UX improvements on the /stock-orders list:
- Number + Supplier cells now wrap long text instead of
  clipping. Uses whitespace-normal + break-words + max-w-[200px]
  on the cell, plus a row-level py-3 bump so wrapped cells look
  balanced with single-line rows.
- Two new outline buttons next to 'New Stock Item':
  'Download Stocktake PDF' -> /api/v1/stock-items/stocktake-pdf
  'Download Orders PDF' -> /api/v1/stock-purchases/list-pdf
  Both open the PDF in a new tab via target=_blank; the browser
  handles the download.

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
SJCoetz 2026-08-03 16:40:24 +02:00
parent e025b340b8
commit 1b2f4a4f52
2 changed files with 49 additions and 5 deletions

View file

@ -149,6 +149,30 @@ describe("StockOrdersTable — quick draft button", () => {
});
});
describe("StockOrdersTable — download PDFs", () => {
afterEach(() => {
cleanup();
});
it("renders a 'Download Stocktake PDF' link to the Stocktake PDF endpoint", () => {
render(<StockOrdersTable rows={[]} currentStatus="" page={1} totalPages={1} />);
const link = screen.getByTestId("download-stocktake-pdf");
expect(link).toBeInTheDocument();
expect(link.tagName).toBe("A");
expect(link).toHaveAttribute("href", "/api/v1/stock-items/stocktake-pdf");
expect(link).toHaveAttribute("target", "_blank");
});
it("renders a 'Download Orders PDF' link to the Orders PDF endpoint", () => {
render(<StockOrdersTable rows={[]} currentStatus="" page={1} totalPages={1} />);
const link = screen.getByTestId("download-orders-pdf");
expect(link).toBeInTheDocument();
expect(link.tagName).toBe("A");
expect(link).toHaveAttribute("href", "/api/v1/stock-purchases/list-pdf");
expect(link).toHaveAttribute("target", "_blank");
});
});
describe("StockOrdersTable — edit draft button", () => {
afterEach(() => {
cleanup();

View file

@ -15,7 +15,7 @@ import { Badge } from "@/components/ui/badge";
import { Button, buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { Card, CardContent } from "@/components/ui/card";
import { ChevronRight, Package, Pencil } from "lucide-react";
import { ChevronRight, Download, Package, Pencil } from "lucide-react";
import { QuickDraftModal } from "@/components/stock-orders/quick-draft-modal";
import {
STOCK_PURCHASE_STATUS_VALUES,
@ -151,7 +151,27 @@ export default function StockOrdersTable({
})}
</div>
<div className="flex items-center justify-between gap-2 flex-wrap">
<div className="flex items-center gap-2 flex-wrap">
<a
href="/api/v1/stock-items/stocktake-pdf"
target="_blank"
rel="noopener"
className={cn(buttonVariants({ variant: "outline" }), "gap-1.5")}
data-testid="download-stocktake-pdf"
>
<Download className="h-4 w-4" />
Download Stocktake PDF
</a>
<a
href="/api/v1/stock-purchases/list-pdf"
target="_blank"
rel="noopener"
className={cn(buttonVariants({ variant: "outline" }), "gap-1.5")}
data-testid="download-orders-pdf"
>
<Download className="h-4 w-4" />
Download Orders PDF
</a>
<Button
type="button"
variant="outline"
@ -201,15 +221,15 @@ export default function StockOrdersTable({
return (
<TableRow
key={row.id}
className="cursor-pointer"
className="cursor-pointer [&>td]:py-3"
onClick={() => {
router.push(`/stock-orders/${row.id}`);
}}
>
<TableCell className="font-medium">
<TableCell className="font-medium whitespace-normal break-words max-w-[200px]">
{row.purchase_number}
</TableCell>
<TableCell>
<TableCell className="whitespace-normal break-words max-w-[200px]">
{supplier?.suppliername ?? (
<span className="text-muted-foreground italic">
Unknown supplier