#Workspace chrome and navigation
Module id: MOD-SHELL
Doc profile: system
Shared authenticated chrome for TradeForce: the Header, contractor/company/admin SideBar menus, and the company SideMenuCompany reusable. After MOD-AUTH establishes a session, this module mounts the cabinet workspace and routes users into shift, time-tracking, payment, admin, and chat hosts via evidenced tab query params. Destination page bodies belong to later modules.
#Purpose
Signed-in contractors, companies, and admins need one consistent workspace frame—header actions plus a role-gated side menu—so they can reach My shifts, shift lists, time tracking, payments, company dashboard tabs, admin tabs, and chat without each product page reinventing chrome. This module owns layout composition, role menu visibility, header journeys (profile, logout, chat shortcut, notifications list + mark-read, presence, mobile drawers), and the navigation maps. It does not own shift lists, admin tables, chat threads, onboarding, or auth forms.
#Scope boundaries
| In scope | Out of scope |
|---|---|
| Cabinet workspace shell on authenticated host routes | Destination page bodies (shifts, admin ops, chat UI, payments content) |
| Header profile / logout / chat / notifications / presence / mobile company links | Creating or emailing notifications (MOD-CHAT) |
| SideBar contractor, company, and admin MENU regions | Onboarding wizard (MOD-ONBOARDING) |
| SideMenuCompany reusable (three-item MENU) mounted for company hosts | Inventing always-visible dual company sidebars when the dashboard instance is hidden |
tab query contracts from nav / tabs / adminnav / admintab |
Stripe Connect / KYC (MON-MARKETPLACE) |
notifications table + profiles.is_online for header journeys |
Auth index / reset-password (MOD-AUTH) |
#Actors
| Actor | System behavior |
|---|---|
| Contractor | Sees contractor SideBar MENU only; opens My shifts, Shifts, Time-tracking, Payments; uses header profile → contractors tab, chat, notifications, presence. |
| Company | Sees company SideBar MENU; optional SideMenuCompany reusable (hidden on load on company dashboard); header mobile links Dashboard / Payments / Settings; Edit profile → company profile tab. |
| Admin | Sees admin SideBar MENU; opens Open Shifts, Companies, Contractors, Payments admin tabs. |
| Session client | Anon-key Supabase client + AUTH-FOUNDATION helpers; chrome requires a session; signed-out host hits redirect to /. |
#Shell composition
Chrome is one cabinet workspace, not three apps. Host routes compose Header + SideBar (and SideMenuCompany where embedded) around a main outlet. Exactly one SideBar role region is visible from profiles.role. Mobile width toggles Header mobile_ and SideBar/SideMenuCompany mob_ custom states and the Header floating MENU group.
Composition and ChangePage chains are mapped under Traceability / IR evidence.
#Primary journeys
Journeys are reconstructed from Header and SideBar / SideMenuCompany workflows (ButtonClicked → ChangePage / ToggleElement / LogOut / MakeChangeCurrentUser / notification Search). Destination page bodies stay out of scope; this module only delivers chrome and the tab (or message) query contract.
#J1 — Enter authenticated workspace chrome
- A signed-in user opens an authenticated host (
/contractor/shifts,/contractor/time-tracking,/contractor/payments,/company/shifts,/admin, or/chat). - The app renders the cabinet shell with Header and SideBar; the main outlet stays empty or placeholder-only for destination modules.
- SideBar shows exactly one role MENU: contractor, company, or admin, matching
profiles.role. - At a narrow viewport, hamburger / mob controls toggle floating mobile chrome without breaking the outlet.
- A signed-out visit to a host redirects to
/and does not render workspace chrome.
#J2 — Header profile, logout, chat, notifications, and presence
- User opens the profile drop and sees Edit profile and Log out.
- Log out clears the session and navigates to
/; Edit profile navigates by role (contractor → contractors tab on contractor shifts; company → profile tab on company shifts). - Message control opens chat with
message=unapproved; time control opens contractor time-tracking withtab=time_tracking. - Notification bell: if the user has at least one recipient notification, the panel toggles and lists body text; unread rows are marked read for the current user. If count is zero, show “You don't have any notifications yet” and do not open the panel.
- Online/Offline control updates
profiles.is_onlinefor the signed-in user. - On mobile Header MENU, company links Dashboard / Payments / Settings set the matching company
tabvalues.
#J3 — Contractor sidebar navigation
- Contractor sees MENU plus My shifts, Shifts, Time-tracking, and Payments.
- My shifts →
/contractor/shifts?tab=myshifts(navmyshifts). - Shifts →
/contractor/shifts?tab=shiftlist(tabsshiftlist — preserve tabs vs nav difference). - Time-tracking →
/contractor/time-tracking?tab=time_tracking. - Payments →
/contractor/paymentswith no required tab. - Mobile contractor menu reaches the same four destinations.
#J4 — Company sidebar and SideMenuCompany navigation
- Company user sees SideBar MENU with Dashboard, Company shifts, Reports, Payments, Contractors, Time-tracking, and Settings.
- SideBar items navigate on
/company/shiftswithnavtab values; SideBar Settings usestab=profile(not settings). - SideMenuCompany MENU shows Dashboard, Payments, and Settings →
tab=dashboard|payments|settingsrespectively. - Mobile company menu duplicates evidenced SideBar destinations for company_shifts, contractors, time_tracking, and reports.
- SideMenuCompany on the company dashboard remains hidden on load; do not invent an always-on second sidebar.
#J5 — Admin sidebar navigation
- Admin sees MENU plus Shifts, Companies, Contractors, and Payments.
- Shifts →
/admin?tab=openshifts(admintabopenshifts). - Companies →
/admin?tab=companies(adminnavcompanies). - Contractors →
/admin?tab=contractors. - Payments →
/admin?tab=payments. - Mobile admin menu hits the same four tab targets; admin page bodies remain MOD-ADMIN.
#Navigation contracts
| Label / trigger | Behavior |
|---|---|
| Contractor My shifts | /contractor/shifts?tab=myshifts |
| Contractor Shifts | /contractor/shifts?tab=shiftlist |
| Contractor Time-tracking | /contractor/time-tracking?tab=time_tracking |
| Contractor Payments | /contractor/payments |
| Company SideBar Dashboard … Settings | /company/shifts?tab= dashboard | company_shifts | reports | payments | contractors | time_tracking | profile (Settings label) |
| SideMenuCompany Dashboard / Payments / Settings | /company/shifts?tab= dashboard | payments | settings |
| Admin Shifts / Companies / Contractors / Payments | /admin?tab= openshifts | companies | contractors | payments |
| Header Edit profile | Contractor → tab=contractors on contractor shifts; company → tab=profile on company shifts |
| Header messages | /chat?message=unapproved |
| Header time | /contractor/time-tracking?tab=time_tracking |
| Header Log out | Sign out → / |
| Header mobile Dashboard / Payments / Settings | Company tabs dashboard | payments | settings |
#Vertical slices (decompose)
| Item | Product outcome |
|---|---|
| SHELL-LAYOUT | Cabinet workspace on authenticated hosts; Header + SideBar composition; role-gated menu regions; mobile drawers; session guard. Enabler for nav and header action slices. |
| SHELL-HEADER-ACTIONS | Profile menu, logout, chat/time shortcuts, notification panel (list body + mark-read + empty toast), presence → profiles.is_online, mobile company MENU links; notifications migration + RLS. |
| SHELL-NAV-CONTRACTOR | Four contractor destinations with evidenced tab params (including shiftlist via tabs). |
| SHELL-NAV-COMPANY | Seven company SideBar tabs + SideMenuCompany three-item map (Settings→settings vs SideBar Settings→profile). |
| SHELL-NAV-ADMIN | Four admin tab destinations (openshifts vs adminnav companies/contractors/payments). |
#Side-effect contracts
| Trigger | Side effect | Owner |
|---|---|---|
| Host route without session | Redirect to /; no chrome |
SHELL-LAYOUT + AUTH-FOUNDATION |
| Log out | signOut + navigate / |
SHELL-HEADER-ACTIONS |
| Open notifications with unread | Append current user to notifications.read_by_ids |
SHELL-HEADER-ACTIONS |
| Online/Offline toggle | Update profiles.is_online |
SHELL-HEADER-ACTIONS |
| Nav / header ChangePage | Client navigation with tab or message query |
SHELL-NAV-* / SHELL-HEADER-ACTIONS |
| Notification create / email | Out of scope | MOD-CHAT |
#Shell-owned field mapping
Idiomatic target names for shell-owned persistence and chrome (source ids stay in Traceability / IR evidence):
| Target | Source concept | Layer |
|---|---|---|
profiles.role |
user role option | sql / ts |
profiles.is_online |
online/offline flag on current user | sql / ts |
notifications |
notification thing | sql |
notifications.body |
notification body text | sql / ts |
notifications.recipient_ids |
recipient user list | sql / ts |
notifications.read_by_ids |
read-by user list | sql / ts |
AppHeader / AppSidebar / SideMenuCompany |
Header / SideBar / SideMenuCompany chrome | ts |
?tab= / ?message= |
ChangePage URL parameters | doc / routes |
#Style and fidelity
Provisional IR style mapping for chrome; shared tokens are owned here — SHELL-LAYOUT delivers the app-level stylesheet from IR styles (ir/slices/styles____part_all.json — 49 styles, 8 color tokens, 1 font token), and this cycle's module map has no separate design-system module. Stamp data-bubble-id on Header, SideBar, and SideMenuCompany roots and on migrated nav group roots used in parity checks. Breakpoints follow PageData around ≤1010px for floating mob chrome and Header G-main adaptation around ≤600px—do not invent a different grid. Transcribe MENU labels and header action copy exactly (Edit profile, Log out, empty-notifications toast).
#Failure modes
| Case | Expected behavior |
|---|---|
| Signed-out host URL | Redirect to /; no workspace chrome. |
| Sign-out failure | User-visible error; stay signed in until success. |
| Zero notifications + bell click | Toast/error “You don't have any notifications yet”; panel stays closed. |
| Notifications fetch / mark-read failure | User-visible error; do not pretend unread cleared. |
| Presence update failure | User-visible error; do not flip Online/Offline UI as if persisted. |
| Wrong role menu visible | Must not happen — gate on profiles.role equals contractor | company | admin. |
| SideMenuCompany always shown beside SideBar | Invalid — company dashboard instance is hidden on load; preserve that unless a later evidenced show workflow is added. |
#Needs human clarification
None. After inventory + sanitized export chase for Header / SideBar / SideMenuCompany workflows, ChangePage tab/message params, notification Search + empty toast, presence writes, role visibility states, and the company-dashboard SideMenuCompany is_visible: false instance, shell behavior is encoded in the export. Destination bodies and notification create/email remain owned by other modules, not unanswered shell questions.
#Traceability
| Product concept | Target (route / component) | Bubble evidence |
|---|---|---|
| Header reusable | AppHeader, hosts |
bTHOe |
| SideBar reusable | AppSidebar |
bTHMl |
| SideMenuCompany reusable | SideMenuCompany |
bTMyh2 |
| Contractor / company / admin menus | Role regions in sidebar | bTHNF, bTISK, bTIRZ + element_condition role equals |
| Profile menu toggle | ProfileMenu | Workflow bTHaj / bTHap |
| Edit profile by role | headerNavigation |
Workflow bTKFj → bTHMg tab contractors / bTITd tab profile |
| Log out | Session helper + / |
bTHty1 / bTHuE1 / bTHuF1 → bTGYf |
| Open chat | /chat?message=unapproved |
bTIoe / bTIor → bTJQD0 |
| Open time-tracking | /contractor/time-tracking?tab=time_tracking |
bTMPF1 / bTMPT → bTJFi0 |
| Notifications panel + list | NotificationPanel, notifications |
bTLBr/bTLBx, bTLBZ, bTLCB, bTLBl; empty bTLNB/bTLNL |
| Mark-read | PATCH notifications | bTLNS / bTLNp / bTLNf / bTLOC |
| Presence | profiles.is_online |
bTMcr0, bTMdJ0, bTMdQ0, bTMhl |
| Contractor nav map | contractorNav |
bTIpV…bTIqF chain |
| Company SideBar map | companyNav |
bTIqK…bTIrZ (Settings→profile) |
| SideMenuCompany map | SideMenuCompany |
bTNCN2, bTNCe2, bTNCl2 |
| Admin nav map | adminNav |
bTIrl…bTIsP |
| Mobile Header MENU labels | Floating company links | bTNFR, bTNFT, bTNFZ, bTNFf |
| Option sets for tabs | Query contracts | nav, tabs, adminnav, admintab |
| Roles | profiles.role |
role, bTHAB0, bTHAC0, bTIrk |
| Presence flag | profiles.is_online |
offline_online_boolean |
| Notification body / recipients / read-by | notifications.body / recipient_ids / read_by_ids |
text_text, user_list_user, read_list_user |
| Notification type | notifications table |
notification |
#IR evidence
#Module evidence roots
bTHOe— Header (/element_definitions/bTHOf)bTHMl— SideBar (/element_definitions/bTHMm)bTMyh2— SideMenuCompany (/element_definitions/bTNEP2)
Supporting segments: ir/slices/edef_bTHOf____root.json, ir/slices/edef_bTHMm____root.json, ir/slices/edef_bTNEP2____root.json, ir/slices/styles____part_all.json.
#Path-key ↔ inventory id aliases
| Path key / pointer | Inventory id | Notes |
|---|---|---|
/element_definitions/bTHOf |
bTHOe |
Header |
/element_definitions/bTHMm |
bTHMl |
SideBar |
/element_definitions/bTNEP2 |
bTMyh2 |
SideMenuCompany |
/element_definitions/bTHOf/workflows/bTHal |
bTHaj |
Profile drop toggle |
/element_definitions/bTHOf/workflows/bTKFl |
bTKFj |
Edit profile ChangePage |
/element_definitions/bTHOf/workflows/bTHuD1 |
bTHty1 |
Log out + ChangePage index |
/element_definitions/bTHOf/workflows/bTIoj |
bTIoe |
Messages → chat |
/element_definitions/bTHOf/workflows/bTLBw |
bTLBr |
Bell toggle when count ≥1 |
/element_definitions/bTHOf/workflows/bTLNH |
bTLNB |
Empty notifications toast |
/element_definitions/bTHMm/workflows/bTIpa |
bTIpV |
My shifts |
/element_definitions/bTHMm/workflows/bTIph |
bTIpf |
Shifts / shiftlist |
/element_definitions/bTHMm/workflows/bTIrY |
bTIrZ (action) |
SideBar Settings → profile |
/pages/bTIbd/elements/bTNEt2 |
bTNEo2 |
SideMenuCompany instance is_visible: false |
#Chased navigation facts
- Contractor: myshifts (
nav), shiftlist (tabs), time_tracking (tabson time-tracking page), payments (no tab). - Company SideBar: dashboard, company_shifts, reports, payments, contractors, time_tracking, profile for Settings label.
- SideMenuCompany: dashboard, payments, settings.
- Admin: openshifts (
admintab), companies/contractors/payments (adminnav). - Edit profile: contractor → contractors on contractor shifts; company → profile on company shifts.
- Chat shortcut:
message= tabs unapproved. - Empty bell copy: “You don't have any notifications yet”.
- Presence:
offline_online_booleantrue/false via MakeChangeCurrentUser on Online/Offline control. - SideMenuCompany on company dashboard:
is_visible: false(chased); only the reusable + hidden instance are evidenced—do not invent always-on dual nav.
#Role visibility
- Contractor menu visible when CurrentUser role equals contractor.
- Company menu when role equals company.
- Admin menu when role equals admin.
#Privacy & data
- Target RLS: authenticated users select
notificationswhere they appear inrecipient_ids; update only own membership inread_by_ids. profiles.is_onlineupdate is own-row only (AUTH profiles RLS).- MOD-CHAT owns insert/send/email for notifications.
#Option sets used for shell routing
nav: myshifts, dashboard, company_shifts, reports, payments, contractors, profile, settings, …tabs: shiftlist, time_tracking, unapproved, …adminnav: companies, contractors, payments, …admintab: openshifts, …role: contractor, company, admin.