Skip to content

Batch A: operational lists, money and stock

2. Batch A — Operational lists (ListLayout)

Section titled “2. Batch A — Operational lists (ListLayout)”
  • Route /admin/payments · features/staff/PaymentsPanel.tsx (+ PaymentRefundDialog, SettlementDialog, RefundReceiptAmendDialog, payments/PaymentDocuments.tsx). Gate: view_finance or record_payment (forRecording mode = record-only actor).
  • Job: see what money is owed, record cash/bank receipts, waive or refund, find a receipt.
  • Reference: org2 (stats + tabs + card rows), mgr1 (“At a glance”), mgr2 (Undo).
  • Layout: ListLayout.
    • title “Payments”, subtitle “Money owed to {space}, and how it was paid.”
    • actions: primary none by default (nothing is created here). Outline secondary: “Receipts & credit notes” (opens a side drawer that hosts PaymentDocuments, instead of the inline show/hide section). More: Export CSV, Export Excel (if the export exists; today none is wired, so More is empty and hidden).
    • callouts: when due_state=overdue count > 0: “{n} charges are overdue. Oldest is {age} old.” → “Show overdue”.
    • stats: Pending (count) [+query] · Overdue [+query] · Paid this month [+query] (omit if no date filter exists; otherwise show Pending/Overdue/Waived). Hidden for forRecording actors, who see only Pending.
    • tabs (one tab per value of the existing single-valued status filter): To collect (pending) · Paid online · Paid in person · Waived · Cancelled · All. Record-only actors see To collect · Paid in person (matching today’s forRecording filter).
    • toolbar: Due (All / Due today / Upcoming / Overdue) as chips on the To collect tab only; Subject select; sort menu; saved views (existing ListViewControls). Bulk bar on select: “Mark paid in person ({n})” (primary in bar) · “Waive ({n})”.
    • list: DataTable on desktop, stacked cards under md. Columns: Subject (label + type as muted meta), Due (PaymentDueBadge as soft pill), Amount (bold, right), Refunded (muted, only if > 0), Received (settlement summary, “corrected” pill). Row inline action: on pending → “Mark paid” (opens settlement dialog). Row overflow: Waive, Correct receipt, Refund / Release deposit, Correct refund #n.
  • Wireframe
Payments [Receipts & credit notes]
Money owed to Fab Lab, and how it was paid.
┌ ! 3 charges are overdue. Oldest is 12 days old. Show overdue → ┐
┌Pending──┐┌Overdue──┐┌Waived───┐
│ 14 ││ 3 ││ 2 │
└─────────┘└─────────┘└─────────┘
To collect 14 | Paid online | Paid in person | Waived | All
[Due: All · Today · Upcoming · Overdue] [Subject v] [Sort v] [Views v]
┌──────────────────────────────────────────────────────────────────────────┐
│[ ] Laser job #A12 · Machine service Due in 2 days ₹450 Mark paid [...]│
│[ ] Monthly dues · Membership Overdue 12 d ₹800 Mark paid [...]│
└──────────────────────────────────────────────────────────────────────────┘
‹ 1 2 3 ›
  • Mobile: stats become a 2×2 grid; tabs scroll horizontally; rows stack as cards (subject, amount right, due pill under, “Mark paid” full-width).
  • Dialogs: Settlement (“Record payment” / “Record payment for 3 charges”): method, reference (optional), received date, then “Save receipt” + Cancel. Refund: amount (prefilled with refundable balance), method/reference/date for manual rail, reason, then “Refund ₹450” (the verb carries the amount). Release deposit uses the same dialog with title “Release deposit”. Amend: “Correct receipt”, then “Save correction”. All four use the Modal restyle with footer Cancel (outline) + primary.
  • States: empty To collect: “Nothing to collect. Every charge here is paid or waived.” · empty All: “No payments yet. Charges appear here when someone books, registers or borrows.” · filtered: standard · conflict (409): inline callout on the row “Someone else updated this charge. Refreshed.” · load error: standard.
  • Undo: none. Settlement writes an append-only receipt, so use the confirm dialog and do not fake an Undo.
  • Route /admin/ledger · panels/Ledger.tsx, LedgerParts.tsx. Gate view_inventory.
  • Job: answer “what is out right now, with whom, and what is late?”
  • Reference: org2 (stats + list), mgr1 (latest list).
  • Layout: ListLayout.
    • title “Ledger”, subtitle “Everything that’s out right now.”
    • actions: no primary (read-only). More: Export CSV, Export Excel. Superadmin: a toggle chip “All makerspaces” in toolbar (not a header action).
    • stats: Items out (sum of qty) [loaded] · Records [loaded] · Overdue [+query overdue=1].
    • callouts: overdue > 0: “{n} loans are overdue.” → “Show overdue” (sets the existing Overdue toggle). If accountability is allowed, a secondary link “Open accountability →”.
    • tabs: All · Reviewed · Self-checkout · Direct (the existing Source filter, promoted to tabs).
    • toolbar: search “Holder, email, item or box”, “Overdue only” chip, sortable headers stay.
    • list: card rows. Left: an identity chip (initials of the holder, stable tone). Title = item name (+ “Box: X” meta, unit tags). Meta = holder · out since {date}. Right = qty + due pill (“Due Fri” / “Overdue 3 d” red-tinted). Row click: if the record maps to a request or direct loan, open it (core request detail). Otherwise no click.
  • Wireframe
Ledger [More v]
Everything that's out right now.
┌Items out┐┌Records┐┌Overdue┐
│ 37 ││ 21 ││ 4 │
All | Reviewed | Self-checkout | Direct
[Search holder, email, item or box........] (Overdue only)
┌ (AK) Arduino Uno ×2 · Box B-04 Out since 3 Oct Due Fri ┐
┌ (RM) Soldering station Out since 1 Oct Overdue 3 d ┐
  • States: empty: “Nothing is out right now. Loans appear here the moment they’re handed over.” · load error: standard.
  • Route /admin/transfers · panels/StockTransferPanel.tsx, StockTransferTable.tsx. Gate edit_inventory or superadmin.
  • Job: move stock between containers or makerspaces, with a reason, and see the history.
  • Reference: org2 list + org3 (form in its own surface, not above the list).
  • Layout: ListLayout for history. The create form moves out of the page top into a FlowLayout dialog “New transfer” (two steps: From and to → What’s moving, with Reason on step 2).
    • title “Transfers”, subtitle “Stock moved between boxes and spaces.”
    • actions: primary “New transfer” (hidden for read-only actors).
    • callouts: read-only actor: “Transfers are managed by inventory staff. You can see the history but not move stock.” (replaces the warn box).
    • list: rows with a direction chip (→ icon) + “{qty} {product}” title, meta “{from} → {to} · {reason}”, right = date + actor initials.
    • Dialog step 1: Source container, Destination container (superadmin also gets Source/Destination makerspace, shown first). When cross-makerspace, an info callout replaces the long paragraph: “This moves stock to {space}. A matching product is created there if needed and stays private until they publish it.” with “Read the details” for the asset-tracked limitation. Step 2: line editor (product + qty, “+ Add line” dashed row, remove via row [...]), Reason. Footer: Back · “Move stock”.
  • States: empty: “No transfers yet. Move stock between boxes or spaces with New transfer.” · validation: inline per line (“Only {n} available”).
  • Route /admin/stocktake · panels/StocktakeSessionPanel.tsx, StocktakeCountSection.tsx, StocktakeDiscipline.tsx. Gate edit_inventory; policy needs canConfigure.
  • Job: start a count, count items (by scan), complete, approve, apply adjustments.
  • Reference: org2 (session list with status), mgr4 (detail with sections), mgr2 (scan + recent with Undo).
  • Split: today one page holds policy, schedules, a create form, every session and an inline count table. Split into:
    1. Sessions list — ListLayout.
    2. Session detail — DetailLayout (see B2), opened by ?session={id} (query param, no route change).
    3. Policy & schedules — a SettingsLayout section reached from More: “Count policy and schedule” (opens as a side drawer holding settings rows). Shown only to canConfigure.
  • List slots: title “Stocktake”, subtitle “Count what’s on the shelves and fix the numbers.” actions: primary “Start count” (opens a small dialog: Container (All containers), Notes, Visibility (Use space policy / Blind / Show expected)). More: “Count policy and schedule”. callouts: a schedule is due: “Monthly cycle count is due today.” → “Start it”. stats: In progress [loaded from list] · Awaiting approval [loaded] · Applied this month [+query]. tabs: Active (draft+counting) · Needs approval (completed) · Ready to apply (approved) · Done (applied, cancelled). Rows: status pill, title = notes or “Count of {container|all containers}”, meta = started {date} · {n} lines counted. Inline action by status: Counting → “Continue”, Completed → “Approve”, Approved → “Apply”.
  • States: empty: “No counts yet. Start a count to check the shelves against the system.” · Needs approval empty: “Nothing waiting for approval.”
  • Route /admin/to-buy · panels/ProcurementPanel.tsx, ProcurementPanelRows.tsx, ProcurementReceipts.tsx, ProcurementMoveModal.tsx, ReorderSuggestions.tsx. Gate canUseToBuy; kind chooser for manage_makerspace.
  • Job: keep a shopping list, track it from requested to received, then move it into inventory.
  • Reference: org2 (tabs by lifecycle, rows with inline next-step action).
  • Layout: ListLayout.
    • title “To buy”, subtitle “What the space needs, from request to shelf.”
    • actions: primary “Add item” (dialog: Name, Qty, Link, Est. unit cost, Machine type when required, Kind for space managers). More: Export CSV, Export Excel.
    • callouts: ReorderSuggestions becomes a callout: “{n} items are running low.” → “Review suggestions” (opens a drawer listing them with “Add to list”). Received but not moved: “{n} received items aren’t in inventory yet.” → “Move them”.
    • stats (all [loaded], they are computed today): Estimated total (visible) · Open budget · Spent (received actual).
    • tabs (status): Requested · Approved · Ordered · Received · Cancelled · All, with counts [loaded].
    • toolbar: group-by chip “By machine type” (on by default when grouping exists today).
    • list: replace the 14-column editable table (min-width 1180 px) with card rows: title = item × qty, meta = vendor · est. ₹ · purchaser, right = status pill + one next-step inline action by status: Requested → “Approve”, Approved → “Mark ordered”, Ordered → “Mark received”, Received → “Move to inventory”. Editing vendor, actual cost, status and receipts happens in a row drawer (“Edit” in the row [...]), which also hosts receipts (View / Delete). Delete is the last overflow item and confirms.
  • Mobile: this fixes today’s horizontal scroll; cards are naturally stacked.
  • States: empty: “Nothing on the list yet. Add the first thing your space needs.” · load error: “Couldn’t load the list. Try again.” (replaces “Unable to load list.”) · receipt errors inline in the drawer.
  • Route /admin/to-be-fixed · panels/NeedsFixShelf.tsx. Gate edit_inventory.
  • Job: repair or scrap units that came back broken.
  • Reference: org2 rows with an inline action, mgr2 Undo.
  • Layout: ListLayout.
    • title “To be fixed”, subtitle “Units returned broken. Repair them back into stock, or scrap them.”
    • actions: none (items arrive from returns).
    • stats: Units to fix [loaded sum] · Products affected [loaded].
    • list: rows with a warm “{n} to fix” pill, meta “{available} available · {total} total”. Right: a qty stepper (defaults to all), inline primary-text action “Repaired”, row [...] → “Scrap…” (confirm: “Scrap 2 × Multimeter? They’re removed from inventory for good.”).
    • After “Repaired”: toast “2 × Multimeter back in stock.” There is no Undo, because the API has no reverse action.
  • States: empty: “Nothing on the shelf. Broken returns land here for repair.” (success-tinted icon tile, not muted text).