diff --git a/src/app/(app)/stock-orders/page.test.tsx b/src/app/(app)/stock-orders/page.test.tsx
new file mode 100644
index 0000000..3582438
--- /dev/null
+++ b/src/app/(app)/stock-orders/page.test.tsx
@@ -0,0 +1,138 @@
+import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
+import { cleanup, render, screen, fireEvent } from "@testing-library/react";
+
+// ── Mocks ────────────────────────────────────────────────────────────────
+//
+// The page is a client component that reads PocketBase via
+// `@/lib/pocketbase`. We mock both the `pocketbase` npm package
+// (to keep EventSource out of jsdom) and the local helper module.
+
+// Shared hoisted state so both mocks agree on what getList returns.
+const { _state, _collectionMock, _resetState } = vi.hoisted(() => {
+ const state = {
+ items: [] as unknown[],
+ error: undefined as string | undefined,
+ };
+ const collectionMock = {
+ getList: vi.fn().mockImplementation(async () => ({
+ items: state.items,
+ totalItems: state.items.length,
+ page: 1,
+ perPage: 50,
+ totalPages: 1,
+ })),
+ getFullList: vi.fn().mockResolvedValue([]),
+ };
+ return {
+ _state: state,
+ _collectionMock: collectionMock,
+ _resetState: () => {
+ state.items.length = 0;
+ state.error = undefined;
+ collectionMock.getList.mockClear();
+ collectionMock.getList.mockImplementation(async () => ({
+ items: state.items,
+ totalItems: state.items.length,
+ page: 1,
+ perPage: 50,
+ totalPages: 1,
+ }));
+ },
+ };
+});
+
+vi.mock("pocketbase", () => ({
+ default: class MockPocketBase {
+ autoCancellation() {}
+ collection(_name: string) {
+ return _collectionMock;
+ }
+ },
+}));
+
+vi.mock("@/lib/pocketbase", () => ({
+ __esModule: true,
+ default: () => ({
+ collection: () => _collectionMock,
+ }),
+ getPocketBase: () => ({
+ collection: () => _collectionMock,
+ }),
+}));
+
+vi.mock("next/navigation", () => ({
+ useRouter: () => ({
+ push: vi.fn(),
+ replace: vi.fn(),
+ back: vi.fn(),
+ }),
+}));
+
+// Stub the QuickDraftModal — it's already tested in
+// `src/components/stock-orders/quick-draft-modal.test.tsx`. This test
+// only cares that the page mounts the modal and toggles its `open`
+// prop when the button is clicked.
+vi.mock("@/components/stock-orders/quick-draft-modal", () => ({
+ QuickDraftModal: ({
+ open,
+ onOpenChange,
+ }: {
+ open: boolean;
+ onOpenChange: (b: boolean) => void;
+ }) => (
+
+
+
+ ),
+}));
+
+import StockOrdersPage from "./page";
+
+// ── Tests ────────────────────────────────────────────────────────────────
+
+describe("StockOrdersPage — quick draft button", () => {
+ beforeEach(() => {
+ _resetState();
+ });
+
+ afterEach(() => {
+ cleanup();
+ });
+
+ it("renders a New Stock Item button in the page header", async () => {
+ render();
+ const button = await screen.findByTestId("new-stock-item-button");
+ expect(button).toBeInTheDocument();
+ expect(button).toHaveTextContent(/new stock item/i);
+ });
+
+ it("mounts the QuickDraftModal (closed by default)", async () => {
+ render();
+ const modal = await screen.findByTestId("quick-draft-modal");
+ expect(modal).toBeInTheDocument();
+ expect(modal.dataset.open).toBe("false");
+ });
+
+ it("opens the QuickDraftModal when the button is clicked", async () => {
+ render();
+ const modal = await screen.findByTestId("quick-draft-modal");
+ expect(modal.dataset.open).toBe("false");
+ fireEvent.click(screen.getByTestId("new-stock-item-button"));
+ expect(modal.dataset.open).toBe("true");
+ });
+
+ it("closes the QuickDraftModal when the modal calls onOpenChange(false)", async () => {
+ render();
+ const modal = await screen.findByTestId("quick-draft-modal");
+ fireEvent.click(screen.getByTestId("new-stock-item-button"));
+ expect(modal.dataset.open).toBe("true");
+ fireEvent.click(screen.getByTestId("quick-draft-modal-close"));
+ expect(modal.dataset.open).toBe("false");
+ });
+});
\ No newline at end of file
diff --git a/src/app/(app)/stock-orders/page.tsx b/src/app/(app)/stock-orders/page.tsx
index 240fde4..a1cb840 100644
--- a/src/app/(app)/stock-orders/page.tsx
+++ b/src/app/(app)/stock-orders/page.tsx
@@ -16,6 +16,7 @@ import { Card, CardContent } from "@/components/ui/card";
import { Loader2, Plus, AlertCircle, Package, ChevronRight, Boxes } from "lucide-react";
import { toast } from "sonner";
import getPocketBase from "@/lib/pocketbase";
+import { QuickDraftModal } from "@/components/stock-orders/quick-draft-modal";
import {
STOCK_PURCHASE_STATUS_VALUES,
type StockPurchase,
@@ -93,6 +94,7 @@ export default function StockOrdersPage() {
const [status, setStatus] = useState("");
const [page, setPage] = useState(1);
const [totalPages, setTotalPages] = useState(1);
+ const [quickDraftOpen, setQuickDraftOpen] = useState(false);
const fetchRows = useCallback(async () => {
setLoading(true);
@@ -160,6 +162,14 @@ export default function StockOrdersPage() {
Reorder from low stock
+
)}
+
+
);
}