Skip to content

Batch A: notifications, logs, devices and catalogues

A7. Notifications (personal inbox + broadcast) — tab notifications

Section titled “A7. Notifications (personal inbox + broadcast) — tab notifications”
  • Route /admin/notifications · panels/NotificationInbox.tsx → notifications/PersonalInbox.tsx, staff/BroadcastComposer.tsx. Gate any staff action.
  • Job: catch up on what happened that needs me; managers can also send a member announcement.
  • Reference: org1/org2 Recent activity rail (this page is its “See all →” destination).
  • Layout: ListLayout.
    • title “Notifications”, subtitle “{n} unread” (live; keep the unread count in an aria-live region and in the sidebar item’s accessible name, as pinned today).
    • actions: primary “Mark all read” (disabled at 0). Outline secondary, managers with membership.broadcasts: “Send announcement”. It opens a dialog with the existing composer fields (Title, Body, Audience) and the button “Send announcement”. The composer card is no longer pinned above the inbox.
    • tabs: Unread {n} · All.
    • toolbar: Family chips (Hardware, Printing, Events, Bookings, Maintenance, Members, Payments, Stock, Certifications, Devices, API access). Raw keys become labels.
    • list: activity-style rows (activity rail, full width). Avatar/icon tile by family · bold title + summary (2 lines, clamp) · relative time (“2 min ago”) · unread dot. Inline action “Open” when there is a safe link. Row click marks it read. Unread rows get a teal left rule, not a teal border box.
    • Date group headers: Today · Yesterday · Earlier this week · Older.
  • States: empty Unread: “You’re all caught up.” · empty All: “No notifications yet. Activity from your space shows up here.” · load error: standard.

A8. Delivery logs (+ suppressions) — tab email-logs

Section titled “A8. Delivery logs (+ suppressions) — tab email-logs”
  • Route /admin/email-log · panels/DeliveryLogPanel.tsx → notifications/DeliveryLogScreen.tsx, notifications/SuppressionAdmin.tsx. Gate manage_makerspace. (panels/EmailLogPanel.tsx is still exported from StaffPanels.tsx but no route mounts it. Flag it for deletion as a separate cleanup, not in this revamp.)
  • Job: check that alerts and emails went out, retry failures, send a test, manage suppressed addresses.
  • Reference: org2 (tabs + rows + inline action), org6 (developer-ish surface kept calm).
  • Layout: ListLayout with two tabs that split today’s stacked sections.
    • title “Delivery logs”, subtitle “Every email and alert this space sent, and whether it arrived.”
    • actions: primary “Send test”. It opens a small dialog “Send a test to…” (Email to my account / each active destination) and the result shows as a toast with “View in log”.
    • callouts: failed in the current filter > 0: “{n} deliveries failed.” → “Show failed”.
    • tabs: Deliveries · Suppressed addresses.
    • Deliveries toolbar: Status chips (All, Sent, Failed, Pending, Skipped), Channel select, Destination select, Family select, Event text in More filters. list rows: channel icon tile · title “{Family} · {event}” humanised · meta “{destination or Personal devices} · {n} attempts · {time}” · status pill (Sent / Failed / Skipped “blocked by Email module” / Sending) · inline “Retry” on failed rows. The error text shows under the row in muted red.
    • Suppressed tab: actions changes to primary “Suppress an address” (dialog). Rows: address, reason, date, inline “Remove”.
  • States: empty Deliveries: “Nothing sent yet. Send a test to check your setup.” · filtered: standard · empty Suppressed: “No suppressed addresses. Bounced or blocked addresses appear here.”
  • Route /admin/accountability · panels/AccountabilityPanel.tsx. Gate view_audit.
  • Job: follow up on overdue loans and reported problems, and restrict or restore repeat offenders.
  • Reference: org2 (stats + tabs), mgr1 (“Latest …” lists).
  • Layout: ListLayout with tabs that replace the four stacked Panels and the “Show all accountability sections” button. The existing section list filter becomes the tab key.
    • title “Accountability”, subtitle “Late, lost or damaged — and who it’s tied to.”
    • stats (each a tab shortcut): Overdue loans [loaded count] · Open problems [loaded] · Repeat offenders [loaded] · Restricted [loaded].
    • tabs: Overdue · Problems · Repeat offenders · Restricted.
    • Overdue rows: identity chip (holder) · item · “Overdue {n} d” pill · inline “Open loan”.
    • Problems rows: item · reporter · reported {date} · problem text (2-line clamp).
    • Repeat offenders rows: person · damage/loss counts · status pill · inline “Restrict…” (dialog with required reason; button “Restrict {name}”) or “Restore access” if restricted.
    • Restricted rows: person · reason · since · inline “Restore access”.
  • States: Overdue empty: “No overdue loans. Nice.” · Problems empty: “No open problem reports.” · Offenders empty (named): “No damage or loss on record against a named person.” · Restricted empty: “No one is restricted here.”
  • Route /admin/warranty · panels/WarrantyPanel.tsx (+ WarrantySection, WarrantyStatusBadge). Gate edit_inventory or printing/machines.
  • Job: see what’s covered, what’s expiring, and what’s missing documents.
  • Reference: org2.
  • Layout: ListLayout.
    • title “Warranties”, subtitle “Coverage for {scope} in {space}.”
    • stats: Active · Expiring soon · Expired · Missing docs. Each is [+query] filtered count. If that is too costly, keep only the total [loaded] in the subtitle.
    • tabs: All · Active · Expiring soon · Expired · Not covered (the existing status filter).
    • toolbar: “Expires before” date, “Missing docs” chip.
    • list: rows. Identity chip (machine or asset) · host name · vendor · purchased → expires · status pill · docs count. Inline “Manage” opens the existing WarrantySection in a side drawer instead of an inline expanded table row.
  • States: empty: “No warranties recorded yet. Add one from a machine or asset.” · filtered: standard.
  • Route /admin/organized-events · panels/OrganizedEventsPanel.tsx (+ EventDrawer). Gate: non-superadmin, not single-tenant.
  • Job: an organization organizer sees the events they run across venues.
  • Reference: org2 exactly (date-chip rows, going/capacity progress).
  • Layout: ListLayout.
    • title “Organized events”, subtitle “Events you run with your organization, across spaces.”
    • tabs: Upcoming · Past (client split by date on the loaded page, or a query param if the API supports it; otherwise a single list in chronological order with a “Past” divider).
    • list: date chip (MON/DD, tone by host space) · title · meta “{location or Online} · {host space}” · right: “{going} / {capacity} going” + thin progress bar, status pill. Row click opens the existing EventDrawer.
  • States: empty: “No organized events. Events your organization runs will appear here.” · load error with “Try again”.
  • Route /admin/member-cards · panels/MemberCardsPanel.tsx, memberCards/*. Gate member-card actions; module membership.
  • Job: issue, print, reissue and revoke member cards; scan a card at the desk.
  • Reference: org4 (invite card on top + table), mgr2 (scan block).
  • Split: today one long page holds the cards table, a scan box, an issue roster and the card layout form. Use tabs: Cards · Issue · Scan · Card layout.
  • Layout: ListLayout.
    • title “Member cards”, subtitle “Printable cards that open the member area at the desk.”
    • actions: primary “Print sheet” (label “Print sheet (3 selected)” when there is a selection, else “Print all active”). Secondary outline: “Scan a card” (jumps to the Scan tab).
    • stats: Active cards [loaded] · Members without a card [loaded from roster].
    • Cards tab: status segmented control Active / Revoked (existing), DataTable with Card number, Name, Status pill, Prints, Photo (✓ / —). Row inline “Print”. Row [...] “Reissue…” and “Revoke…” (existing dialogs, restyled).
    • Issue tab: org4-style list “Members without a card”, each row with inline “Issue card”.
    • Scan tab: the shared ScanWorkbench section (§11.2) with the card result panel.
    • Card layout tab: a SettingsLayout body (settings rows) with Save.
  • States: Active empty: “No cards yet. Issue one from the Issue tab.” · Issue empty: “Every active member has a card.” · Revoked empty: “No revoked cards.”
  • Route /admin/devices · panels/DevicesPanel.tsx, DeviceIdentitySection.tsx, DeviceEnrollmentModal.tsx. Gate manage_devices; module devices.
  • Job: register printers and label printers, see which are offline, enrol or revoke them.
  • Reference: org4 (people table → device table), org6 (one-time secret handling).
  • Layout: ListLayout + detail drawer.
    • title “Devices”, subtitle “Printers and other hardware that talk to Space Works.”
    • actions: primary “Add device” (dialog: Name, Kind (Generic / Printer / Label printer), then “Create device”; on success it opens the enrollment dialog straight away).
    • stats: Online · Offline · Revoked ([loaded] from the current page only, or [+query]).
    • list: rows. Kind icon tile · name · meta “Last seen 4 min ago” / “No heartbeat yet” · status pill (Online / Offline since {time} / Revoked). Row click opens a drawer (replaces the aria-pressed button + section below the table) with settings rows: Name, Offline threshold, then Save; actions “New enrollment token”; danger zone “Revoke device”.
    • Enrollment dialog (org6 pattern): yellow strip “Copy this now. It won’t be shown again.” · dark code block with the token and Copy · purpose and expiry as meta · footer primary “I’ve copied it” (replaces “Close and discard token”).
  • States: empty: “No devices yet. Add a printer to send jobs straight to it.” · load error with “Try again”.
  • Route /admin/data-export · features/staff/DataExportsPanel.tsx. Gate manage_makerspace.
  • Job: get a readable archive of this space’s records.
  • Reference: org6 (warning strip + clients list), auth7 (callout with action).
  • Layout: ListLayout.
    • title “Data export”, subtitle “A readable copy of your space’s records. Not a backup.”
    • actions: primary “Request export” (disabled while one is running; tooltip “An export is already being built”).
    • callouts: one yellow callout replaces the three-paragraph disclosure: “Exports include member names, emails and phone numbers. Handle the file like any personal data.” → “Read what’s included” (expands the full existing text, unchanged).
    • list: job rows. Icon tile · “Export · {date}” · meta “{rows} rows” · status pill (Queued / Building / Ready / Failed) · inline “Download” when ready. Failure detail as a muted red line.
    • Running jobs also show in the Job tray.
  • States: empty: “No exports yet. Request one when you need a copy of your records.” · deadline-exceeded failure: “This export took too long. Try again at a quieter time.”
  • Route /admin/categories · panels/Categories.tsx. Gate edit_inventory.
  • Job: organise products into categories.
  • Reference: org4 (simple table) + org3 (create in a focused surface).
  • Layout: ListLayout.
    • title “Categories”, subtitle “How items are grouped in the catalogue.”
    • actions: primary “Add category”. A dialog replaces the always-visible 5-field inline row: Name, Slug (“Leave blank to use the name”), Icon, Order.
    • list: DataTable (Name, Products, Order, Icon) with inline “Edit” (same dialog) and row [...] “Delete…” (existing confirm: “Delete {name}? {n} items lose this category.”).
    • Reorder: keep the numeric order field. Drag reordering is out of scope.
  • States: empty: “No categories yet. Add one to group your catalogue.” · load error: standard.
  • Route /admin/containers · panels/ContainersPanel.tsx. Gate manage_qr; module containers.
  • Job: find a box, see what’s in it and its scan history, edit its label or location.
  • Reference: org2 list → mgr1 detail with tabs.
  • Layout: ListLayout; each container opens a detail drawer with tabs (B3), replacing the inline Edit / Contents / History toggles inside each card.
    • title “Containers”, subtitle “Boxes, bins and shelves with QR labels.”
    • actions: primary “New container”, which links to QR tools (today: “Create one in QR tools”), labelled “Create in QR tools”. More: none.
    • toolbar: search, Active / Inactive chips.
    • list: rows. Code chip (the container code in the identity chip) · label · meta location · Inactive pill when inactive · right: “{n} items” if loaded.
  • States: empty: “No containers yet. Create labelled boxes in QR tools.” with the button.

A17. Job handover (machine service) — tab handover

Section titled “A17. Job handover (machine service) — tab handover”
  • Route /admin/handover · panels/machine/HandoverConsole.tsx. Gate collect_service_request or manage_machines; module machine_service.
  • Job: at the desk, hand finished jobs to the people who requested them.
  • Reference: mgr2 “Recent check-ins” rows with an inline action; org2 rows.
  • Layout: ListLayout.
    • title “Ready to collect” (today the panel title is “Awaiting collection”; the tab label “Job handover” stays in the nav), subtitle “Finished jobs waiting for their owners.”
    • stats: Waiting [loaded] · Unpaid [loaded].
    • toolbar: search by requester (client-side filter on the loaded list).
    • list: rows. Identity chip (requester initials) · job title · meta “{machine} · finished {relative}” · payment pill (Paid / Unpaid ₹x) · inline primary-text “Hand over”. When unpaid, the inline action becomes “Collect payment” and links to the payment, and “Hand over” moves to the row [...], but only if the API allows handover while unpaid (today it does, so keep “Hand over” inline and show the unpaid pill as a warning).
    • After handover: the row collapses with toast “Handed to {name}.” No Undo, because there is no API reverse.
  • States: empty: “Nothing waiting. Finished jobs show up here for handover.” · error (action): “Couldn’t mark that job collected. It may have been handed over already.”

A18. Email templates (index) — tab emailtemplates

Section titled “A18. Email templates (index) — tab emailtemplates”
  • Route /admin/email-templates · panels/EmailTemplatesPanel.tsx. Gate inventory, printing or devices.
  • Job: find the email to customise.
  • Reference: org2 list → mgr1/mgr3 detail.
  • Layout: ListLayout index, then DetailLayout editor (B1). This replaces the 18rem left button column plus an always-mounted editor.
    • title “Email templates”, subtitle “What people receive from {space}, in your words.”
    • tabs: one per stream (Hardware, Printing, …) [loaded].
    • list: grouped by audience (header “To members” / “To staff”). Rows: template name · meta “Sent when {humanised key}” · “Edited” pill if overridden (space default or any machine type).
    • Row click goes to ?template={stream}:{audience}:{key} → editor.
  • States: empty: “No editable templates here.” (rare).