Skip to content

Batches H and I: member area and stations

9. Batch H — Member area tab bodies (member AppShell)

Section titled “9. Batch H — Member area tab bodies (member AppShell)”

The member shell, its nav and the Overview tab are in ux-spec.md. These are the bodies of the other memberTabs() (gating unchanged). Each tab body uses ListLayout slots without the shell header repeating the space name.

  • Component MemberActivity.tsx (section loans), MemberDashboardSections.tsx (MemberHistory), MemberLoanRenew.tsx. Reference: org2 tabs + rows.
  • title “Loans” · tabs Out now · History · rows: item, “Due Fri” / “Overdue” pill, inline “Renew” when allowed, history rows with returned date.
  • Empty: “Nothing out right now. Browse the catalogue to borrow something.” + “Browse catalogue”.
  • Component bookings/MyBookings.tsx, BookingReschedule.tsx. Reference: org2 date-chip rows.
  • title “Bookings” · actions primary “Book a space” (→ public bookings) · tabs Upcoming · Past (replaces the two aria-pressed buttons) · date-chip rows (space, time range, status pill) · inline “Move” · [...] “Cancel booking…” (inline confirm stays, restyled).
  • Empty: “No upcoming bookings. Book a bench or room when you need one.”

H3. Events — /member/events (+ partner events)

Section titled “H3. Events — /member/events (+ partner events)”
  • Component MemberActivity (events), MemberCalendarActions, MemberEventCancel, MemberEventFeedback, PartnerEvents.tsx. Reference: att9 ticket, org2 rows.
  • title “Events” · tabs Going · Past · Partner events (only when PartnerEvents has rows).
  • Going rows: date chip · title · time · place · inline “Ticket” (opens an att9 ticket card dialog with QR, “Add to calendar”, “Can’t make it? Cancel”).
  • Past rows: inline “Give feedback” / “Certificate”.
  • Partner events: info line “Your space partners with these hosts, so you can register even when the event isn’t public.” · rows grouped by series · inline “Register” (expands the registration fields in a dialog rather than inline).
  • Empty Going: “You’re not registered for anything yet. See what’s on.” + “See events”.
  • Component MemberPayments.tsx, MemberDashboardSections (MemberDues), payments/PaymentDocuments (member audience). Reference: org2 stats + rows.
  • title “Payments” · stats: Outstanding (per currency) [loaded] · Membership fee [loaded] · rows: what, due, amount, status pill, inline “Pay now” (online rail) or muted “Pay at the desk” · secondary outline header action “Receipts” (drawer with documents).
  • Empty: “Nothing to pay. You’re all settled.”
  • Component notifications/MemberNotifications.tsx, AnnouncementFeed.tsx, PreferenceCentre.tsx, member notices. Reference: activity rail.
  • title “Inbox” · tabs Notices · Announcements · Preferences (Preferences uses SettingsLayout rows: channel switches, digest frequency, Save).
  • Empty: “No notices need your attention.” / “No announcements yet.”

H6. Account portal — /member/account-portal

Section titled “H6. Account portal — /member/account-portal”
  • Component MemberProfilePanel.tsx (+ memberProfile/*), MemberCardPanel.tsx (+ memberCard/*), MemberDirectory.tsx, MemberReferrals.tsx, MemberCertifications.tsx, MemberClaimCodes. Today these stack in one tall column.
  • Layout: SettingsLayout with sub-tabs Profile · Card · Certifications · Community · Invite a friend, plus a link chip “Account & security →” (C6). Tab contents depend on modules, as today.
    • Profile (auth6): rows Avatar (upload), Headline, Institution, Bio, Interests, Languages, GitHub; “Projects” list with dashed “+ Add project”. Save.
    • Card (att9 ticket style): a card visual (space colour band, printed name, number, QR when active) · “Name printed on the card” row · photo upload with consent · actions “Save name”, “Preview PDF”, “Save for offline use” / “Forget offline card”, “Keep my loans on this device”. Revoked: danger callout “This card was revoked. Ask staff for a new one.”
    • Certifications: rows (machine, level, granted date, expiry pill). Empty: “No training records yet. Ask staff about a machine induction.”
    • Community (member directory): ListLayout body: search + member cards (avatar, name, headline, interests chips) → profile drawer (projects, links, recently attended). Empty: “No members have shared a profile yet.”
    • Invite a friend (MemberReferrals): org4 “Invite someone” card (email + “Send invite”) + “Pending invitations” list + claim-code field. Empty: “No pending invitations.”
  • Split note: Community is a directory, not account content. It belongs in its own member nav item. That requires adding a value to MEMBER_TABS/memberTabs (a gating change, which PLAN §5 says to avoid), so it stays a sub-tab here and the decision goes to the owner.

I1. Event check-in station — /event-check-in/:stationToken, /m/:slug/event-check-in/:stationToken

Section titled “I1. Event check-in station — /event-check-in/:stationToken, /m/:slug/event-check-in/:stationToken”
  • Component events/EventCheckInStationPage.tsx + staff/OfflineCheckInOperator.tsx (eventCheckInOfflineStore).
  • Reference: att10 (PIN pad) + att11 (door scanner) directly.
  • Layout: KioskLayout, two states:
    1. Locked: stage left = space avatar tile, “{event} · Door check-in”, big title “Enter the station PIN”, body “Ask an organiser for the 8-digit PIN. It unlocks check-in on this device only.”, 8 PIN boxes. side right = numeric keypad (1–9, Clear, 0, ⌫) + full-width “Open check-in” (disabled until 8 digits). A physical keyboard still works. Keep the Station PIN label as the input’s accessible name. Error: “That PIN didn’t work, or check-in isn’t open right now.”
    2. Unlocked: station bar = avatar, event name, “Door check-in · Station”, counter “{n} / {total} checked in”, Online / Offline pill (queued count when offline: “Offline · 3 waiting to sync”), outline “Lock station” (= today’s “Wipe data and lock station”; confirm “Lock and wipe this device? Unsynced check-ins will be lost.” only when the queue is non-empty). stage = camera frame “Hold the ticket QR inside the frame” + search “Name, email or ticket code” + Find. side = result panel (green “Checked in · {name} · just now” with “Next guest” + “Undo” if the operator supports undo; amber “Already checked in at {time}”; red “Not on the list”), then “Recent” list.
  • States: roster downloading: “Getting the guest list ready…” with progress · roster unavailable (400/403/410 wipes): danger panel “This station was turned off. Ask an organiser for a new link.”

I2. Public desk kiosk — /kiosk/:slug (central)

Section titled “I2. Public desk kiosk — /kiosk/:slug (central)”
  • Component kiosk/PublicDeskKioskPage.tsx (wraps PublicInventoryPage).
  • Layout: KioskLayout whose stage hosts the public catalogue body (core spec’s catalogue, unchanged). station bar = space avatar · “Public desk” · outline “Open full catalogue”. There are no sign-in or theme controls in the bar (the theme toggle moves to a small footer control). Larger touch targets (min-h-12), larger base type.
  • Note: this is the only kiosk that embeds a core screen. Coordinate with the catalogue owner so the catalogue body renders without its own PublicShell header when hosted here.
  • Component kiosk/ScannerPage.tsx.
  • Job: a staff device that only resolves QR codes.
  • Reference: att11.
  • Layout: KioskLayout: station bar “Scanner” + signed-in staff chip · stage = ScanWorkbench (§11.2: camera + paste field + Resolve) · side = resolved-object card (the same card as the staff Scanner tab: “Box B-04 · 12 items”, “Asset · Multimeter #3 · Out”, allowed actions as labelled buttons/pills) · Recent list.
  • Fix: today the result is a raw JSON.stringify <pre>. Replace it with the shared result card. Add camera scanning (the staff QrScanner already exists).
  • States: idle: “Scan a label or paste its code.” · unknown QR: “That code isn’t one of ours.” · revoked: “This label was revoked.” (from the server message).

I4. Member PWA offline page — public/member-pwa/offline.html (+ offline.css, offline.js)

Section titled “I4. Member PWA offline page — public/member-pwa/offline.html (+ offline.css, offline.js)”
  • Layout: static HTML that mirrors KioskLayout’s look in plain CSS. It cannot use React primitives, so PLAN Phase 1 must update offline.css with the new tokens by hand.
    • Top bar: space name (from the saved card) · “Offline” pill.
    • Card visual (as H6 Card) with the saved QR, name, number, and “Saved {date} · last-known”.
    • “My loans” list (saved) with due dates.
    • Footer: primary “Try again online”, red text button “Forget saved card and loans”.
  • Copy: “You’re offline. This is your last-saved card. It may not show recent changes.” · no card: “No card saved on this device. Next time you’re online, save it from your account.”

Staff Scanner tab — tab scanner (listed here because it shares I3’s parts)

Section titled “Staff Scanner tab — tab scanner (listed here because it shares I3’s parts)”
  • Route /admin/scanner · panels/ScannerPanel.tsx, ScannerPanelForms.tsx. Gate manage_qr; module scanner.
  • Layout: AppShell + ListLayout frame (title “Scanner”, subtitle “Scan or paste a label to see what it is and act on it.”) whose list slot holds the ScanWorkbench + resolved-object card. Actions on the card: primary depends on target (Box → “View contents”); [...] “Rename & rebind…”, “Move to another space…” (each opens a dialog instead of an inline form), red “Revoke label…” (confirm). The checkout/return hint becomes an info callout with a link: “To lend this, use Direct handout →”.