TradeForce migration docs

#Contractor shifts and profile

Module id: MOD-CONTRACTOR-SHIFTS
Doc profile: capability

Contractors browse and claim open marketplace shifts, manage their My shifts membership (including cancel), and maintain profile fields, documents, and preferred-company visibility on /contractor/shifts. Shared Detailed job view and Row actions stay owned by MOD-COMPANY-SHIFTS — this module consumes Row actions by slot and uses a local detail group, not a forked company reusable.

#Purpose

Signed-in contractors need one authenticated home to find claimable work, open shift detail, claim or cancel membership, and keep identity documents and skills current. Observable outcomes are membership writes on shifts / profiles (contractor lists and shift lists), optional company notification on cancel (in-app and/or Postmark after the grace window), profile and documents persistence under owner RLS, and a 15-minute grace schedule hook at claim time (worker body owned by MOD-TIME-TRACKING).

#Scope boundaries

In scope Out of scope
Route /contractor/shifts under cabinet workspace; panels for My shifts, Shift list, local detail, profile Workspace chrome Header / SideBar (MOD-SHELL)
Filters (region, company, skill, date, rate) + search by job title; claim confirm Company post/edit/approve dashboard (MOD-COMPANY-SHIFTS)
Local G-Detailed view of the shift for tab=detail Forking DetailedViewJob reusable (sole owner MOD-COMPANY-SHIFTS)
My shifts Upcoming / Completed, cancel with reason + dependability deduction Time-tracking UI and grace-delay worker recursion (MOD-TIME-TRACKING)
Profile save, seven document types, preferred-companies popup Stripe Connect / KYC / payouts (MON-MARKETPLACE / MOD-PAYMENTS)
Create chat row + navigate to /chat from detail Message Chat thread body (MOD-CHAT)
Slot shared Row actions instance Copying Row actions element definition into a contractor-only tree

#Actors

Actor Behavior in this module
Contractor Signed-in profiles.role = contractor. Browses claimable shifts, claims/cancels, edits profile and documents. Unapproved contractors (profiles.approved false) cannot claim.
Company (indirect) Receives cancel notification when scheduled paths fire; does not use this page as primary host.
Session client Anon-key Supabase client for reads; claim/cancel membership mutations via JWT RPC / edge — never service-role in the browser.
Scheduler / edge Claim schedules the 15-minute delay API equivalent; cancel may cancel that schedule and notify the company. Delay worker body is MOD-TIME-TRACKING.

#Capability composition

One host route with mutually exclusive panels driven by URL tab. Empty tab or myshifts shows My shifts; shiftlist shows the marketplace list; detail shows local shift detail; contractors shows profile. Nested query shift={id} binds detail. Shared Row actions mounts inside My shifts rows; company Detailed job view is not instantiated here.

flowchart TB subgraph host["Contractor shifts host"] My["My shifts"] List["Open shift list + filters"] Detail["Local shift detail"] Profile["Contractor profile"] end subgraph shared["Shared from company module"] RA["Row actions slot"] end List -->|"open row"| Detail My -->|"details"| Detail Detail -->|"claim / cancel"| My My --> RA Profile -->|"preferred companies popup"| Profile

Bubble evidence for panels and workflows is under Traceability / IR evidence.

#Primary journeys

Journeys are reconstructed from contractor_shifts_profile PageLoaded / ButtonClicked workflows (ChangePage, Show-Hide, MakeChangeCurrentUser, ChangeThing, ScheduleAPIEvent, NewThing notification, connectors). Chat thread UI and grace-delay approve recursion stay out of scope where noted.

#J1 — Enter contractor shifts and switch panels

  1. A signed-in contractor opens /contractor/shifts (with or without tab).
  2. Signed-out visitors redirect to /; admin visitors redirect to /admin; company visitors redirect to /company/shifts.
  3. Empty tab or tab=myshifts shows My shifts; tab=shiftlist shows the open-shift list; tab=detail shows local detail; tab=contractors shows profile.
  4. URL message=welcome shows Welcome popup; message=unapproved shows the unapproved claim-pending popup.
  5. Session/role read failure shows a clear error or redirect — never a blank authenticated shell.

#J2 — Browse, filter, and open shift detail

  1. On tab=shiftlist, the contractor sees search placeholder “Search for job” and filter labels Region, Company, Skill set, Select shift date, Select rate.
  2. The open-shifts list loads claimable marketplace shifts not already on the contractor’s membership, with columns Job Title, Location, Company (plus Shift Time / Shift Date / HR / Action per list chrome).
  3. Empty list shows “No result found.”; load/filter failures are visible.
  4. Opening a row navigates to /contractor/shifts?tab=detail&shift={id} and shows the local detailed view (Skillset, Location, Start Date/Time, Wisconsin time, Hours required, Hourly Rate, Description, Claim / Cancel / Message).
  5. Detail does not mount or fork the company Detailed job view reusable.

#J3 — Claim an open shift

  1. On detail, Claim opens Popup Claim Shift with confirm copy, Confirm / Discard, and the 15-minute grace warning.
  2. If profiles.approved is false, claim stays blocked (unapproved popup / disabled claim) — no membership writes.
  3. Confirm adds the contractor to the shift’s contractor list, adds the shift to the contractor’s shift membership, schedules the 15-minute grace delay job, stores the schedule id on the user, and hides the popup.
  4. Discard closes without membership writes.
  5. Message creates a chat row for contractor + shift company and navigates to /chat (thread UI remains MOD-CHAT).
  6. Mutation failures show inline/page errors; grace worker body beyond enqueue is MOD-TIME-TRACKING.

#J4 — Manage My shifts and cancel

  1. On My shifts, Upcoming vs Completed shifts toggles list mode (active_shifts vs closed_shifts) — only one mode active.
  2. The list shows the contractor’s membership rows (Shift Name, Location, HR, Company, Skillset, Worked Hours, Date) or “No result found.”
  3. Details reuses the same local detail panel (tab=detail&shift={id}) — no second detail tree.
  4. My shifts rows slot shared Row actions (company-owned) behind the existing instance marker — no contractor-only fork of the definition.
  5. Cancel opens Popup Cancel Shift; Confirm removes membership lists, records cancellation reason, adjusts dependability from the negatives option, cancels any pending grace schedule, may create an in-app company notification when the grace schedule id is empty, may send company Postmark/connector email when Current Date/Time is after shift start + 15 minutes, and removes the shift from Upcoming immediately on success.
  6. Cancel/notification failures map once and stay visible.

#J5 — Update contractor profile and documents

  1. On tab=contractors, profile shows General Information (legal names, email, skills summary, four skill-set options, state, city, dependability score) and document uploaders.
  2. Seven uploaders map 1:1 to document types: driver’s license, resume, mask fit test, diplomas, hearing examination OSHA, references from two foreman/managers, specialty credentials.
  3. Picture uploader updates preview; Save persists profile image plus name, skills summary, skill set, state, and city.
  4. State/City use onboarding CSC helpers (no private CSC headers in the SPA); changing state clears city.
  5. Optional password fields run UpdateCredentials only when filled and matching.
  6. “See which companies prefer me” opens Popup Companies listing preferred companies (Company Name, Location, Important Contacts) — do not read the empty current_contractors_info type.
  7. Save/upload failures show visible errors; lists/docs stay unchanged on failure.

#Vertical slices (decompose)

Item Product outcome
CSHIFT-BROWSE-APPLY Host route, role gates, welcome/unapproved messages, shift list + filters, local detail, claim, message→chat, shifts migration + claim enqueue.
CSHIFT-MYSHIFTS My shifts panel, Upcoming/Completed, cancel popup + membership/dependability writes, shared Row actions slot.
CSHIFT-PROFILE Profile panel, documents, skills/preferences, preferred-companies popup, profile/docs migration.

#Key UI states

State When User sees
Empty tab / myshifts URL tab empty or myshifts My shifts list
shiftlist tab=shiftlist Filters + open shifts RG
detail tab=detail + shift id Local detailed view
contractors tab=contractors Profile + documents
message=welcome query message welcome Welcome popup
message=unapproved query message unapproved Unapproved popup
Filters open filter chrome toggled Region / Company / Skill / date / rate controls
Upcoming vs Completed My shifts sort custom state Matching membership rows or empty copy
Claim popup open Claim control Confirm / Discard + grace warning
Cancel popup open Cancel control Reason picker + Confirm / Discard
Unapproved contractor profiles.approved false Claim blocked
Label / trigger Behavior
Empty tab Show My shifts (nav myshifts or empty)
My shifts (sidebar) ?tab=myshifts (nav)
Shifts / Shift list (sidebar) ?tab=shiftlist (tabs — preserve tabs vs nav split)
Open detail ?tab=detail&shift={id} (tabs detail)
Profile (header / welcome CTA intent) ?tab=contractors (nav) for profile panel
Welcome dismiss (export-wired) Hides welcome; ChangePage stays on host with Shift list tab value — see HQ-1
Unapproved dismiss Hides popup; ChangePage sets host tab from option tabs value upcomingshifts (display MyShifts) — distinct from nav myshifts used by My shifts panel visibility
Message on detail Create chat → /chat with company user param
Preferred companies Open / close Popup Companies only

#Style and fidelity

Contractor shifts uses the cabinet workspace outlet. Transcribe visible copy and stamp data-bubble-id on migrated roots: page, Shift list, My shifts, local detail, profile, claim/cancel/welcome/unapproved/companies popups, filter group, and primary RG roots. Shared tokens come from the app-level stylesheet that SHELL-LAYOUT (MOD-SHELL) delivers out of IR styles (ir/slices/styles____part_all.json — 49 styles, 8 color tokens, 1 font token); this cycle's module map has no separate design-system module, so do not defer a token to one. Preserve Wisconsin time copy on detail date/time fields.

#Failure modes

Case Expected behavior
Signed-out on host Redirect to /; no contractor panels.
Admin / company on host Redirect to /admin or /company/shifts.
Unapproved claim attempt Block membership writes; show unapproved messaging.
Open-shift list / filter query failure Plain-language error; no silent empty success.
Claim confirm failure Inline/page error; leave membership unchanged; keep or dismiss popup honestly.
Cancel confirm / notification failure Visible error; Upcoming membership unchanged on hard failure. In-app company notification (when grace schedule id empty) and Postmark/connector email (when Current Date/Time is after shift start + 15 minutes) are separate paths — do not invert the Postmark gate into a within-15-minutes-of-start window.
Profile / document / image save failure Visible error; prior profile/docs unchanged.
Password mismatch on Save Focus confirm; do not UpdateCredentials.
Chat create failure Visible error; do not pretend navigate succeeded.
Grace-delay edge unavailable at claim User-visible failure after or instead of incomplete schedule persist — do not invent approve recursion here.

#Needs human clarification

Id Question Why unclear (plain + IR) Impact
HQ-1 Should Welcome's "Go to My Profile Page" navigate to the profile panel (tab=contractors) as decompose CSHIFT-BROWSE-APPLY states, or to the Shift list panel as the export ButtonClicked workflow writes? Plain: The button label says profile, but the wired ChangePage sets the host tab from option tabs value shiftlist (display ShiftList), not nav contractors. Implement needs one destination. IR: Welcome control workflow bTIoX ChangePage sets tab = display of option.tabs / shiftlist. Profile panel visibility is option.nav / contractors (element_condition:bTJWU0:0); Shift list visibility is option.tabs / shiftlist (element_condition:bTHaf:0). Decompose CSHIFT-BROWSE-APPLY expects profile (tab=contractors) — that conflicts with the export-wired destination. blocks_implement

#Traceability

Product concept Target (route / component / table) Bubble evidence
Contractor shifts page /contractor/shifts, ContractorShiftsPage bTHMg
Shifts host group page chrome bTHQn
Shift list panel ShiftListPanel bTHaf, element_condition:bTHaf:0
Filters group ShiftFilters bTHbT, bTHax, custom_state:bTHbT:*
Search box job title filter bTHbH
Open shifts RG open marketplace list bTHeC
Open detail from row tab=detail&shift= bTKDt, bTHeD
Local shift detail ShiftDetailPanel bTHUv, element_condition:bTHUv:0
Claim button / popup ClaimShiftDialog bTHWW, bTHme, bTKEE, bTKFR
Message → chat create + /chat bTHXG, bTMkF
Welcome / unapproved message popups bTHkC, bTHoJ, bTIoR, bTIoX, bTIpB
PageLoaded gates redirect signed-out / roles bTKuP, bTKnt
My shifts panel MyShiftsPanel bTHRM, element_condition:bTHRM:0
Upcoming / Completed sort custom state bTHRZ, bTHRq, bTKEV0, bTKEc0, sort
My shifts RG membership list bTHTF
Cancel button / popup CancelShiftDialog bTHWc, bTHnb, bTKVI, bTKVg
Cancel in-app notify NewThing notification when schedule id empty bTNLS
Cancel Postmark/connector notify when (start + 15m) < now bTNMm
Row actions instance slot company component bTHUp, bTHTn
Profile panel ContractorProfilePanel bTJWU0, element_condition:bTJWU0:0
Profile save / picture PATCH profiles bTJYG0, bTJWm0, bTKGb, bTKFq
Preferred companies popup read preferred list bTJcT0, bTJdn0, preferred_list_user
Documents documents + docs_type documents, docs_type
Shifts table public.shifts shifts
Approval gate field profiles.approved approve1_boolean
Tab option values ?tab= from tabs / nav tabs, nav
Rate filter options filter attrs min/max rate_filter
Cancel reasons reason enum reason_of_cancellation
Skill set options profile multi-select skill_set (option values)
Dead preferred-info type omit current_contractors_info
Shared detail reusable (not forked) company sole owner bTIgC
Grace delay API enqueue only here bTKPV (via claim ScheduleAPIEvent)

#Source → target naming (under Traceability)

Source (Bubble) Target Layer
contractor_shifts_profile / bTHMg /contractor/shifts route
G-Detailed view of the shift local ShiftDetailPanel ts
DetailedViewJob do not fork; company DetailedViewJob ts
RGaction shared ShiftRowActions slot ts
shifts shifts sql
job_title_text job_title sql
date_date shift_date sql
contractor_list_user shift contractor membership sql
shifts_list_custom_shifts profiles shift membership sql
contractor_rates_number contractor_rate sql
approve1_boolean profiles.approved sql
short_summary_about_your_experience_text skills_summary sql
profileimage_image profile_image sql
dependability_scoup_number dependability_score sql
preferred_list_user preferred_company_ids / preferred companies sql
docs_list_custom_documents documents sql
schedule_15_min_delay_text grace schedule id column sql
option nav / tabs db_values tab query string doc/route

#IR evidence

#Module evidence roots

Supporting segments: ir/slices/bTHMh____root.json, ir/slices/bTHMh__elements____remaining_children.json, ir/slices/bTHMh__elements__bTHQp__elements____remaining_children.json, ir/slices/bTHMh__workflows____part_A.json, ir/slices/edef_bTIgD____root.json, ir/slices/edef_bTHTo____root.json, options tabs / nav / sort / rate_filter / docs_type / reason_of_cancellation / skill_set.

#Path-key ↔ inventory id aliases

Inventory id Export path key
bTHMg /pages/bTHMh
bTIgC /element_definitions/bTIgD
bTHTn /element_definitions/bTHTo
Claim confirm workflow bTKFR /pages/bTHMh/workflows/bTKFT
Cancel confirm workflow bTKVg /pages/bTHMh/workflows/bTKVl

#Panel visibility (URL tab)

Panel Shows when
My shifts nav equals myshifts or tab is empty
Shift list tabs equals shiftlist
Local detail tabs equals detail
Profile nav equals contractors

Preserve SHELL’s split: sidebar My shifts uses nav myshifts; sidebar Shifts uses tabs shiftlist.

#Claim confirm chain (bTKFR)

  1. MakeChangeCurrentUser — add shift to shifts_list_custom_shifts when not already present
  2. ChangeThing — add CurrentUser to shift contractor_list_user
  3. ScheduleAPIEvent — API bTKPV at Current Date/Time + 15 minutes (params user + shift)
  4. ChangeThing — store schedule id on user schedule_15_min_delay_text
  5. Hide claim popup

#Cancel confirm chain (bTKVg)

  1. Optional NewThing notification to company when grace schedule id empty (bTNLS) — separate in-app path
  2. CancelScheduledAPIEvent when schedule id present
  3. ChangeThing — remove from contractor/show lists; set reason; add to cancel list
  4. MakeChangeCurrentUser — clear schedule id; remove shift membership; subtract dependability via negatives cancel-within-4-hours option
  5. Conditional Postmark/connector notify (bTNMm) when ElementParent.data_start_date plus 15 minutes is less than Current Date/Time — i.e. after the 15-minute grace window past shift start (fires when (start + 15m) < now, not within 15 minutes of start)
  6. Reset inputs + hide popup

#Option sets (db_value → display)

Set Values used here
nav myshifts → MyShifts; contractors → Contractors
tabs shiftlist → ShiftList; detail → DetailShift; upcomingshifts → MyShifts; welcome / unapproved for message param
sort active_shifts → ActiveShifts; closed_shifts → ClosedShifts
docs_type seven document db_values listed in J5
reason_of_cancellation sick, family_emergencies, unexpected_personal_issues, other
skill_set concrete_finisher, concrete_laborer, concrete_walls, concrete_carpenter
rate_filter display bands $1–20 … $80–100; wire db_values include typo _40_600 for $60–80 — persist wire values; use attrs min/max for filtering

#Privacy / residuals

#Decompose refs

Generated from the repository at 2026-09-22 15:55 UTC · source of truth is docs/ in the workspace, not this site.