feat(stock): persist pricing draft to localStorage
This commit is contained in:
parent
759e05048c
commit
7aaed60860
2 changed files with 50 additions and 0 deletions
|
|
@ -379,6 +379,30 @@ describe("StockListPage", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Stock list — localStorage draft persistence", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
_resetState();
|
||||||
|
_state.items = [
|
||||||
|
{ id: "aaaaaaaaaaaaaaa", sku: "B-001", name: "M10 Hex Bolt", selling_price: 0, cost_price: 0, markup: 0, current_qty: 5, min_qty: 0, is_active: true, supplier: "" },
|
||||||
|
];
|
||||||
|
window.localStorage.clear();
|
||||||
|
});
|
||||||
|
afterEach(() => cleanup());
|
||||||
|
|
||||||
|
it("survives a page remount via localStorage", async () => {
|
||||||
|
const { unmount } = render(<Page />);
|
||||||
|
await screen.findByText("M10 Hex Bolt");
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: /Edit pricing/i }));
|
||||||
|
const costInput = await screen.findByLabelText(/Cost for B-001/i);
|
||||||
|
fireEvent.change(costInput, { target: { value: "1.23" } });
|
||||||
|
unmount();
|
||||||
|
render(<Page />);
|
||||||
|
await screen.findByText("M10 Hex Bolt");
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: /Edit pricing/i }));
|
||||||
|
expect(await screen.findByText(/Apply changes/i)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("Stock list — missing-pricing-only filter", () => {
|
describe("Stock list — missing-pricing-only filter", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
_resetState();
|
_resetState();
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,27 @@ export default function StockListPage() {
|
||||||
window.localStorage.setItem("wfdash.stock.showPricing", String(showPricingColumns));
|
window.localStorage.setItem("wfdash.stock.showPricing", String(showPricingColumns));
|
||||||
}, [showPricingColumns]);
|
}, [showPricingColumns]);
|
||||||
|
|
||||||
|
// Rehydrate draft from localStorage on mount.
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof window === "undefined") return;
|
||||||
|
try {
|
||||||
|
const raw = window.localStorage.getItem("wfdash.stock.pricingDraft");
|
||||||
|
if (raw) setPricingDraft(JSON.parse(raw));
|
||||||
|
} catch {
|
||||||
|
// ignore corrupt draft
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Persist draft every change.
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof window === "undefined") return;
|
||||||
|
try {
|
||||||
|
window.localStorage.setItem("wfdash.stock.pricingDraft", JSON.stringify(pricingDraft));
|
||||||
|
} catch {
|
||||||
|
// localStorage quota — best-effort
|
||||||
|
}
|
||||||
|
}, [pricingDraft]);
|
||||||
|
|
||||||
// Debounce the search input → 250ms after the last keystroke. Same
|
// Debounce the search input → 250ms after the last keystroke. Same
|
||||||
// pattern as /orders and /quotes — the operator is hunting for a
|
// pattern as /orders and /quotes — the operator is hunting for a
|
||||||
// match, not paging through.
|
// match, not paging through.
|
||||||
|
|
@ -250,6 +271,11 @@ export default function StockListPage() {
|
||||||
Object.entries(pricingDraft).filter(([id]) => failedIds.has(id)),
|
Object.entries(pricingDraft).filter(([id]) => failedIds.has(id)),
|
||||||
);
|
);
|
||||||
setPricingDraft(failedDraftEntries);
|
setPricingDraft(failedDraftEntries);
|
||||||
|
if (failedIds.size === 0) {
|
||||||
|
window.localStorage.removeItem("wfdash.stock.pricingDraft");
|
||||||
|
} else {
|
||||||
|
window.localStorage.setItem("wfdash.stock.pricingDraft", JSON.stringify(failedDraftEntries));
|
||||||
|
}
|
||||||
setPricingApplyState(failedIds.size > 0 ? "partial" : "applied");
|
setPricingApplyState(failedIds.size > 0 ? "partial" : "applied");
|
||||||
toast.success(
|
toast.success(
|
||||||
failedIds.size > 0
|
failedIds.size > 0
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue