PO-Lite Zero Back End — Implementation Plan
Author: nail60 Date: 2026-04-15 Status: Completed Completed: 2026-04-23 PR: Arda-cards/arda-frontend-app#754 Figma Reference: Arda MVP 2.0 Hi-Fi Mockups — node 3791-137363
Summary
Section titled “Summary”Build a frontend-only purchase order sidebar that lets users generate a PO from either a single line item or a group (multi select is a future feature), compose a purchase order in a right-side push panel, edit all fields (quantities, prices, supplier, taxes/fees/discounts), and download a PDF. Zero backend changes — all state lives client-side in Redux.
Context
Section titled “Context”- The order queue page (
src/app/order-queue/page.tsx) renders card-based supplier groups with per-card and group-level actions (Order All, Start Order, Complete). There is no multi-select today. - Existing right panels (ItemDetailsPanel, EmailPanel, ItemFormPanel) use a fixed overlay pattern. This feature uses a push pattern instead — the main content area shrinks to make room.
- Types for
Order,OrderLine,OrderStatusalready exist insrc/types/order.ts. - The existing PO V1 implementation plan covers a broader scope (AG Grid conversion, backend endpoints). This plan is a deliberately smaller slice: the sidebar form only, no AG Grid, no backend.
In Scope
Section titled “In Scope”- “Create PO” button on each line item card and on each supplier group header
- Push-style right sidebar panel (resizable, ~480px default, min 380px, max 700px)
- Order header form: order type dropdown, supplier (To), deliver by, deliver to
- Editable order sheet: item name, SKU, note, qty, unit, cost/ea, line total
- Add/remove line items while editing
- Taxes, fees, and discounts section (add/edit/remove arbitrary line items)
- Order summary: subtotal, taxes, fees, discounts, total
- Footer actions that vary by order type (Create PDF, Preview, etc.)
- Client-side PDF download (implementation details deferred to next phase)
- Redux slice for draft state; no API persistence
- MSW mock handlers for future API compatibility
Out of Scope
Section titled “Out of Scope”- AG Grid conversion of the order queue
- Backend API endpoints or database changes
- Order persistence across sessions (beyond redux-persist)
- Email send flow (existing EmailPanel continues to handle that)
- Supplier creation or item creation from within the sidebar
- Receiving workflow integration
- PDF template design (next phase)
- “Add to order” mode — when the sidebar is open, “Start order” buttons change to “Add to order” and append items to the current draft; items already in the draft are grayed out. Requires extracting card rows into memoized sub-components to avoid re-rendering the full order queue on draft changes.
- Currency selection and conversion — the sidebar currently hardcodes USD. Add a currency selector to the PO header form and convert line item costs, taxes/fees/discounts, and totals accordingly. Requires a currency data source (static rates or API) and formatting updates across the order sheet, summary, and PDF.
- Design system field atoms — the PO sidebar uses shadcn
Input/Textarea/Labelbecause the design system’sInputGroupcompound component (canary atoms) is not exported from the canary entry point, and the nominal export has no field components at all. OnceInputGroupand the field-type atoms (text, number, money, date, etc.) are exported from canary, the sidebar fields should migrate to them. - Remove drop shadows from input components in ux-prototype — shadcn textarea uses
shadow-xswhich adds an unnecessary drop shadow. Already removed inarda-frontend-app’stextarea.tsx. The design system’s Input and Textarea primitives inux-prototypeshould also be updated to useshadow-none. - TypeaheadInput background color — the design system
TypeaheadInputinherits the parent background. The PO sidebar works around this with[&_input]:bg-backgroundon the className. The component itself should accept a background prop or default tobg-backgroundon its inner input. - Browser autofill on controlled inputs — browser autocomplete fills the DOM value but doesn’t fire React
onChange, so state stays empty. Affects Payment terms, Shipping terms, Shipping via, and potentially all controlled inputs app-wide. Fix should be at theInputcomponent level (shadcn or design system) usingonInputor a MutationObserver. - Grouped combobox component — the Add Line Item modal currently uses a custom grouped list with shift-click selection. This should be replaced by a reusable
Comboboxvariant that accepts grouped options natively, for use across the app wherever grouped typeahead selection is needed. - Standardized grid cell primitives — extract reusable cell renderers (editable text, currency, quantity, typeahead) from the Items-domain-specific implementations in
columnPresets.tsxand the 9 class-based cell editors. Would enable the PO order sheet and future entity grids to share a single set of cell components withArdaGrid/DataGrid. Also reconcile the internalArdaGridwrapper with the published@arda-cards/design-systemDataGridexport. Separate project.
Design
Section titled “Design”Push Sidebar Layout
Section titled “Push Sidebar Layout”The sidebar uses a push pattern, not an overlay. When open, the order queue content area resizes from w-full to calc(100% - sidebarWidth). This differs from the existing overlay panels.
+------------------+--------------------+| | || Order Queue | Order Sidebar || (shrinks) | (push, resizable) || | || [cards...] | [form...] || | |+------------------+--------------------+Implementation approach:
- Wrap the order queue page content in a flex container
- The sidebar is a sibling div, not a portal/overlay
- Width controlled by a drag handle or CSS resize
- Slide-in animation via
transition-all duration-300
Create PO Entry Points
Section titled “Create PO Entry Points”Two ways to open the sidebar from the order queue:
- Per-card “Create PO” button — appears on each line item card (alongside existing “Start Order” etc.). Creates a draft with that single item.
- Per-group “Create PO” button — appears in the supplier group header (alongside existing “Order All”). Creates a draft pre-populated with all items in that group.
Both open the push sidebar and populate the draft. If the sidebar is already open with a draft, prompt: “You have an unsaved order. Discard and start a new one?”
Order Sidebar Sections
Section titled “Order Sidebar Sections”From the Figma reference, top to bottom:
- Header — “Purchase Order” title + close (X) button
- Order Type — Displays the vendor’s default
orderMechanism(disabled/read-only for this phase). Editable order type selection deferred to a future phase. - To (Supplier) — Typeahead dropdown, pre-filled from the selected group’s supplier. Shows all suppliers. If user picks a different supplier, prompt: “Add this supplier to all items?” (Yes updates items, No is one-off)
- Deliver By — Date picker (optional)
- Deliver To — Address textarea, pre-filled from tenant/org settings (optional)
- Order Sheet — Compact editable table:
Column Editable Notes Item Name No Display only SKU Yes Text input Note Yes Text input Qty Yes Number input Unit Yes Dropdown Cost/Ea Yes Currency input Line Total No Calculated: Qty x Cost/Ea - ”+ Add Item” button opens the Add Line Item Modal (see below)
- Remove button (X) per row
Add Line Item Modal (Figma ref — node 6268-56880):
- Title: “Add line item from the order queue”
- Combobox typeahead labeled “Select line item to add”
- Dropdown results grouped by vendor, sourced from current order queue items
- Cancel + “Add items” (primary orange) buttons + close (X)
- Future phase: extend to search all items, not just queue
- Taxes, Fees & Discounts — Repeatable rows:
Column Notes Label Text input (e.g., “Sales Tax”, “Shipping”, “10% Discount”) Type Dropdown: Tax / Fee / Discount Value Number input Mode Toggle: % or $ (percentage or flat amount) Calculated Auto-calculated from subtotal if %, or flat value - ”+ Add” button
- Remove button per row
- Summary — Subtotal, total taxes, total fees, total discounts, grand total
- Footer — Action buttons vary by order type:
- Download button (primary action — always the same regardless of order type)
- Cancel button
- Order-type-specific actions (Create PDF, Preview Email, etc.) deferred to a future phase
Frontend Changes
Section titled “Frontend Changes”New Files
Section titled “New Files”| # | File | Purpose |
|---|---|---|
| 1 | src/types/purchase-order.ts | OrderDraft, OrderDraftLine, TaxFeeDiscount, OrderDraftSummary types |
| 2 | src/store/slices/orderDraftSlice.ts | Redux slice: draft state, selection state, panel open/close |
| 3 | src/store/hooks/useOrderDraft.ts | Typed hook wrapping orderDraftSlice actions and selectors |
| 4 | src/components/orders/OrderSidebar.tsx | Push-style sidebar shell: resize handle, open/close animation, header/footer |
| 5 | src/components/orders/OrderHeaderForm.tsx | Order type, supplier, deliver by, deliver to fields |
| 6 | src/components/orders/OrderSheet.tsx | Editable line items table |
| 7 | src/components/orders/TaxFeeDiscountSection.tsx | Repeatable tax/fee/discount rows |
| 8 | src/components/orders/OrderSummary.tsx | Calculated subtotal, taxes, fees, discounts, total |
| 9 | src/components/orders/OrderFooter.tsx | Download + Cancel buttons |
| 10 | src/components/orders/AddLineItemModal.tsx | Modal with combobox typeahead to add queue items to the draft |
| 11 | src/mocks/data/mockOrders.ts | Sample draft orders for dev/testing |
| 12 | src/mocks/handlers/orders.ts | Stubbed MSW handlers for future API compat |
Modified Files
Section titled “Modified Files”| File | Change |
|---|---|
src/app/order-queue/page.tsx | Add “Create PO” buttons to cards and group headers, flex layout for push sidebar |
src/store/rootReducer.ts | Register orderDraftSlice |
src/mocks/handlers/index.ts | Register order mock handlers |
State Management
Section titled “State Management”interface OrderDraft { id: string; // client-generated UUID orderType: OrderMechanism; supplierName: string; supplierId?: string; deliverBy?: string; // ISO date deliverTo?: string; // address text lines: OrderDraftLine[]; taxesFeesDiscounts: TaxFeeDiscount[];}
interface OrderDraftLine { id: string; // client-generated UUID itemId: string; itemName: string; sku: string; note: string; quantity: number; unit: string; costPerUnit: number; lineTotal: number; // derived: quantity * costPerUnit}
interface TaxFeeDiscount { id: string; label: string; type: 'tax' | 'fee' | 'discount'; mode: 'percent' | 'flat'; value: number; calculated: number; // derived from subtotal if %, else value}
interface OrderDraftSummary { subtotal: number; totalTaxes: number; totalFees: number; totalDiscounts: number; grandTotal: number;}Redux slice state:
interface OrderDraftState { isOrderSidebarOpen: boolean; currentDraft: OrderDraft | null; sidebarWidth: number; // persisted resize preference}Task Breakdown
Section titled “Task Breakdown”| # | Task | Description | Depends On | Acceptance Criteria |
|---|---|---|---|---|
| 1 | Types | Create src/types/purchase-order.ts with draft types | — | Types compile, no circular imports |
| 2 | Redux slice | Create orderDraftSlice with actions: openSidebar, closeSidebar, createDraftFromItems, setDraftHeader, addLine, removeLine, updateLine, addTaxFeeDiscount, removeTaxFeeDiscount, updateTaxFeeDiscount, clearDraft, setSidebarWidth | 1 | Actions dispatch correctly, derived totals compute |
| 3 | Hook | Create useOrderDraft hook | 2 | Typed selectors and dispatch helpers |
| 4 | Register slice | Add to rootReducer.ts with persist config | 2 | Slice appears in Redux DevTools |
| 5 | Create PO buttons | Add “Create PO” button to each card and each group header in the order queue | 2, 3 | Per-card button creates single-item draft; group button creates multi-item draft; sidebar opens |
| 6 | Sidebar shell | OrderSidebar.tsx — push layout, resize handle, open/close animation, header with close button | 3 | Sidebar pushes content, resizes, animates |
| 7 | Header form | OrderHeaderForm.tsx — order type dropdown, supplier typeahead, deliver by date picker, deliver to textarea | 3, 6 | Fields editable, supplier pre-filled from selection |
| 8 | Order sheet | OrderSheet.tsx — editable line items table with remove rows | 3, 6 | Can edit qty/price/SKU/note, remove rows, totals recalculate |
| 9 | Add line item modal | AddLineItemModal.tsx — modal with combobox typeahead, items grouped by vendor from order queue | 3, 8 | Modal opens from ”+ Add Item”, typeahead filters queue items, selected item added to draft |
| 10 | Tax/fee/discount | TaxFeeDiscountSection.tsx — repeatable rows with type/mode/value | 3, 6 | Can add/edit/remove, percent and flat modes work |
| 11 | Summary | OrderSummary.tsx — calculated totals | 8, 10 | Subtotal, taxes, fees, discounts, grand total all correct |
| 12 | Footer | OrderFooter.tsx — Download + Cancel buttons | 6 | Download triggers PDF download, Cancel closes sidebar |
| 13 | Wire page | Integrate sidebar into page.tsx flex layout, connect “Create PO” buttons → draft creation | 5, 6, 7, 8, 9, 10, 11, 12 | End-to-end: click Create PO → sidebar opens pre-populated → edit → add items → see totals |
| 14 | Mock handlers | Stubbed MSW handlers + sample data | 1 | Handlers registered, mock mode works |
| 15 | PDF placeholder | Wire “Download” button to a placeholder (toast or empty PDF download) | 12, 13 | Button clickable, shows “PDF coming soon” or generates stub |
Worktree Strategy
Section titled “Worktree Strategy”Single directory — no worktrees needed. Single-agent implementation in arda-frontend-app.
Branch: nail60-ag/po-lite-zero-backend
Risks and Mitigations
Section titled “Risks and Mitigations”| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| page.tsx is 2500 lines; adding buttons + sidebar integration increases complexity | High | Medium | Extract card rendering into sub-components during integration (task 13) |
| Push sidebar layout conflicts with existing overlay panels (ItemDetails, Email) | Medium | Medium | Only one panel type open at a time — close overlays when sidebar opens and vice versa |
| Redux-persist draft state grows stale if items change on backend | Low | Low | Out of scope for MVP; drafts are ephemeral |
| Supplier typeahead needs supplier list not currently fetched on this page | Medium | Low | Reuse existing supplier data from items or add a simple fetch |
Open Questions and Decisions
Section titled “Open Questions and Decisions”| # | Question | Options | Recommendation | Decision |
|---|---|---|---|---|
| 1 | Should the sidebar persist across navigation? | A) Order queue page only, B) All pages via root layout | A for MVP — sidebar renders in page.tsx, draft state persists via redux-persist so it re-opens on return. Moving to root layout is a future enhancement. | A — order queue page only |
| 2 | What PDF library for client-side generation? | A) @react-pdf/renderer, B) jsPDF, C) html2pdf.js | Defer to next phase per scope | Deferred |
| 3 | Should “Save Draft” persist to localStorage beyond redux-persist? | A) Redux-persist only, B) Explicit localStorage save with name | N/A | No “Save Draft” in this phase. Draft lives in Redux state passively (redux-persist keeps it across refreshes) but no explicit save action. |
| 4 | How should the ”+ Add Item” button in the order sheet work? | A) Search/typeahead from all items, B) Pick from order queue items only | B for MVP — opens a modal with a combobox typeahead, items grouped by vendor, sourced from the current order queue. Future phase adds full item list search. | B — order queue items only |
Copyright: © Arda Systems 2025-2026, All rights reserved