Skip to content

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

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.

  • 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, OrderStatus already exist in src/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.
  • “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
  • 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/Label because the design system’s InputGroup compound component (canary atoms) is not exported from the canary entry point, and the nominal export has no field components at all. Once InputGroup and 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-xs which adds an unnecessary drop shadow. Already removed in arda-frontend-app’s textarea.tsx. The design system’s Input and Textarea primitives in ux-prototype should also be updated to use shadow-none.
  • TypeaheadInput background color — the design system TypeaheadInput inherits the parent background. The PO sidebar works around this with [&_input]:bg-background on the className. The component itself should accept a background prop or default to bg-background on 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 the Input component level (shadcn or design system) using onInput or 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 Combobox variant 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.tsx and the 9 class-based cell editors. Would enable the PO order sheet and future entity grids to share a single set of cell components with ArdaGrid/DataGrid. Also reconcile the internal ArdaGrid wrapper with the published @arda-cards/design-system DataGrid export. Separate project.

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

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?”

From the Figma reference, top to bottom:

  1. Header — “Purchase Order” title + close (X) button
  2. Order Type — Displays the vendor’s default orderMechanism (disabled/read-only for this phase). Editable order type selection deferred to a future phase.
  3. 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)
  4. Deliver By — Date picker (optional)
  5. Deliver To — Address textarea, pre-filled from tenant/org settings (optional)
  6. Order Sheet — Compact editable table:
    ColumnEditableNotes
    Item NameNoDisplay only
    SKUYesText input
    NoteYesText input
    QtyYesNumber input
    UnitYesDropdown
    Cost/EaYesCurrency input
    Line TotalNoCalculated: 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
  1. Taxes, Fees & Discounts — Repeatable rows:
    ColumnNotes
    LabelText input (e.g., “Sales Tax”, “Shipping”, “10% Discount”)
    TypeDropdown: Tax / Fee / Discount
    ValueNumber input
    ModeToggle: % or $ (percentage or flat amount)
    CalculatedAuto-calculated from subtotal if %, or flat value
    • ”+ Add” button
    • Remove button per row
  2. Summary — Subtotal, total taxes, total fees, total discounts, grand total
  3. 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
#FilePurpose
1src/types/purchase-order.tsOrderDraft, OrderDraftLine, TaxFeeDiscount, OrderDraftSummary types
2src/store/slices/orderDraftSlice.tsRedux slice: draft state, selection state, panel open/close
3src/store/hooks/useOrderDraft.tsTyped hook wrapping orderDraftSlice actions and selectors
4src/components/orders/OrderSidebar.tsxPush-style sidebar shell: resize handle, open/close animation, header/footer
5src/components/orders/OrderHeaderForm.tsxOrder type, supplier, deliver by, deliver to fields
6src/components/orders/OrderSheet.tsxEditable line items table
7src/components/orders/TaxFeeDiscountSection.tsxRepeatable tax/fee/discount rows
8src/components/orders/OrderSummary.tsxCalculated subtotal, taxes, fees, discounts, total
9src/components/orders/OrderFooter.tsxDownload + Cancel buttons
10src/components/orders/AddLineItemModal.tsxModal with combobox typeahead to add queue items to the draft
11src/mocks/data/mockOrders.tsSample draft orders for dev/testing
12src/mocks/handlers/orders.tsStubbed MSW handlers for future API compat
FileChange
src/app/order-queue/page.tsxAdd “Create PO” buttons to cards and group headers, flex layout for push sidebar
src/store/rootReducer.tsRegister orderDraftSlice
src/mocks/handlers/index.tsRegister order mock handlers
src/types/purchase-order.ts
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
}
#TaskDescriptionDepends OnAcceptance Criteria
1TypesCreate src/types/purchase-order.ts with draft typesTypes compile, no circular imports
2Redux sliceCreate orderDraftSlice with actions: openSidebar, closeSidebar, createDraftFromItems, setDraftHeader, addLine, removeLine, updateLine, addTaxFeeDiscount, removeTaxFeeDiscount, updateTaxFeeDiscount, clearDraft, setSidebarWidth1Actions dispatch correctly, derived totals compute
3HookCreate useOrderDraft hook2Typed selectors and dispatch helpers
4Register sliceAdd to rootReducer.ts with persist config2Slice appears in Redux DevTools
5Create PO buttonsAdd “Create PO” button to each card and each group header in the order queue2, 3Per-card button creates single-item draft; group button creates multi-item draft; sidebar opens
6Sidebar shellOrderSidebar.tsx — push layout, resize handle, open/close animation, header with close button3Sidebar pushes content, resizes, animates
7Header formOrderHeaderForm.tsx — order type dropdown, supplier typeahead, deliver by date picker, deliver to textarea3, 6Fields editable, supplier pre-filled from selection
8Order sheetOrderSheet.tsx — editable line items table with remove rows3, 6Can edit qty/price/SKU/note, remove rows, totals recalculate
9Add line item modalAddLineItemModal.tsx — modal with combobox typeahead, items grouped by vendor from order queue3, 8Modal opens from ”+ Add Item”, typeahead filters queue items, selected item added to draft
10Tax/fee/discountTaxFeeDiscountSection.tsx — repeatable rows with type/mode/value3, 6Can add/edit/remove, percent and flat modes work
11SummaryOrderSummary.tsx — calculated totals8, 10Subtotal, taxes, fees, discounts, grand total all correct
12FooterOrderFooter.tsx — Download + Cancel buttons6Download triggers PDF download, Cancel closes sidebar
13Wire pageIntegrate sidebar into page.tsx flex layout, connect “Create PO” buttons → draft creation5, 6, 7, 8, 9, 10, 11, 12End-to-end: click Create PO → sidebar opens pre-populated → edit → add items → see totals
14Mock handlersStubbed MSW handlers + sample data1Handlers registered, mock mode works
15PDF placeholderWire “Download” button to a placeholder (toast or empty PDF download)12, 13Button clickable, shows “PDF coming soon” or generates stub

Single directory — no worktrees needed. Single-agent implementation in arda-frontend-app.

Branch: nail60-ag/po-lite-zero-backend

RiskLikelihoodImpactMitigation
page.tsx is 2500 lines; adding buttons + sidebar integration increases complexityHighMediumExtract card rendering into sub-components during integration (task 13)
Push sidebar layout conflicts with existing overlay panels (ItemDetails, Email)MediumMediumOnly 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 backendLowLowOut of scope for MVP; drafts are ephemeral
Supplier typeahead needs supplier list not currently fetched on this pageMediumLowReuse existing supplier data from items or add a simple fetch
#QuestionOptionsRecommendationDecision
1Should the sidebar persist across navigation?A) Order queue page only, B) All pages via root layoutA 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
2What PDF library for client-side generation?A) @react-pdf/renderer, B) jsPDF, C) html2pdf.jsDefer to next phase per scopeDeferred
3Should “Save Draft” persist to localStorage beyond redux-persist?A) Redux-persist only, B) Explicit localStorage save with nameN/ANo “Save Draft” in this phase. Draft lives in Redux state passively (redux-persist keeps it across refreshes) but no explicit save action.
4How should the ”+ Add Item” button in the order sheet work?A) Search/typeahead from all items, B) Pick from order queue items onlyB 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