TradeForce migration docs

#Company shift dashboard

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

Company operators post and manage shifts, review applicants, browse contractors, approve time cards, and update company profile/settings on /company/shifts. This module owns the company dashboard page and is the sole implement owner for shared reusables Detailed job view, End job popup, and Row actions — contractor and time-tracking modules consume those components without forking them.

#Purpose

Hiring companies need one authenticated home to run the marketplace day-to-day: see open and closed shifts, publish new jobs with rates and headcount, open a shift for edit and applicant actions, start or end time tracking from a row, mark contractors Preferred / Do not Book / No show, approve logged time and mark payment send, and review dashboard/report metrics. Observable outcomes are persisted shifts / time_entries rows (and contractor-user list memberships that include the signed-in company user) under company-scoped RLS, with money and approval flags written only through server-authorized RPCs (MON-CLIENT-WRITES / LOG-CLIENT-WRITES).

#Scope boundaries

In scope Out of scope
Route /company/shifts under cabinet workspace; tab panels from nav Workspace chrome Header / SideBar (MOD-SHELL)
Filter Posted / Upcoming / Closed; Stop Hiring confirm persist; Closed filter-only Inventing a Close-the-job persist path (control unwired in export)
Shift list browse + open detail Contractor shift browse / apply (MOD-CONTRACTOR-SHIFTS)
Post a New Job form + insert shifts + edge shift notify path Full chat thread UI (MOD-CHAT); only create-chat + navigate
Shared Detailed job view, Row actions, End job popup implement trees Stripe Connect / KYC / payouts onboarding (MON-MARKETPLACE)
Contractors tab Preferred / Do not Book / No show on contractor-user lists + message start Admin verification console (MOD-ADMIN)
Time-tracking approve + payments tab company send paths Recursive tracking approve backend rewrite (MOD-TIME-TRACKING / LOG-RECURSIVE)
Dashboard KPIs, reports sections, company profile save, light settings Inventing Connect onboarding or non-evidenced money rails

#Actors

Actor Behavior in this module
Company operator Signed-in profiles.role = company. Sees tab panels, posts/edits shifts, manages applicants, adds self to contractor Preferred / Do not Book / No show lists, approves time, views payments schedule, edits company profile.
Contractor (via shared reusables) Does not own this page, but Row actions / End job popup / Detailed job view are shared; contractor hosts import the same components. Contractor-user list fields hold which company users preferred/blocked them.
Session client Anon-key Supabase client for reads/list filters; money and approval mutations go through JWT RPCs / edge — never service-role in the browser.
Scheduler / edge Edge function shift (replaces API event shift) runs after post/edit when the shift date is after “now”, and fans out follow-on schedules when ≥1 contractor already has the shift on their list.

#Capability composition

The company dashboard is one host route with mutually exclusive tab panels driven by URL nav values. Nested tabs=postjob and tabs=detail overlay the shifts list without adding sidebar items. Shared reusables mount from list rows and from other modules.

flowchart TB subgraph host["Company shifts host"] Dash["Dashboard KPIs"] List["Company shifts list"] Post["Post new job"] Detail["Detailed job view"] Contractors["Contractors browse"] Time["Time-tracking approve"] Pay["Payments tab"] Profile["Profile / settings / reports"] end subgraph shared["Shared reusables"] DV["Detailed job view"] RA["Row actions"] EJ["End job popup"] end List --> DV List --> RA RA --> EJ Post -->|"insert shifts + edge shift"| List Detail --> DV

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

#Primary journeys

Journeys are reconstructed from company dashboard workflows and shared reusable workflows (ButtonClicked → NewThing / ChangeThing / ScheduleAPIEvent / ChangePage / Show-Hide / SetCustomState). Where a step only opens another module’s full UI (chat thread body, Stripe Connect plugin), the step is marked out of scope.

#J1 — Enter company dashboard and switch tabs

  1. A signed-in company user opens /company/shifts (with or without tab).
  2. The host renders under the cabinet workspace and stamps the page root; non-company or signed-out visitors are redirected or denied.
  3. URL tab selects exactly one panel: empty/dashboard → dashboard; company_shifts → shifts list (+ Post a New Job); contractors → contractors; time_tracking → time-tracking; payments → payments; reports → reports; profile → profile; settings → settings.
  4. Nested tabs=postjob or tabs=detail opens post/detail overlays without inventing extra sidebar entries.
  5. Session/role read failure shows a clear error or redirect — never a blank authenticated shell.

#J2 — Filter and browse company shifts

  1. On tab=company_shifts, the operator sees Posted / Upcoming / Closed filters bound to shift_type (default filter custom state posted).
  2. The repeating list shows Job Title, Location, Assigned Contractor, Skillset, Required Hours, Date, and HR columns, or empty “No result found.”
  3. The list loads only shifts owned by the current company profile, filtered by shift_type, with pagination and an explicit max page size.
  4. Stop Hiring opens Popup stop hiring; confirm persists shift_type = upcoming and stop_hiring = true through server-authorized updates. Closed is filter-only (sets list custom state type_ = closed). Close the job is visible but has no ButtonClicked workflow in the export — do not invent a persist path.
  5. Opening a row / details control mounts the shared Detailed job view (and/or tabs=detail).
  6. Load/filter/mutation failures show plain-language UI errors.

#J3 — Post a new job and publish to marketplace

  1. Operator chooses Post a New Job (tab stays on company shifts; nested tabs=postjob) and fills the new-job form (title, date/time Wisconsin, hours, policies, contacts, seats needed, skill set, rates, experience).
  2. Post creates a shifts row with shift_type = posted, company ownership, and the form field map from DATA-MODEL; Discard resets/hides without insert.
  3. After insert, when the new shift’s date is after current date/time, the app invokes edge function shift (JWT; company or service scheduler roles) instead of a browser-trusted API secret.
  4. Edge shift only continues the notify chain when at least one contractor user already lists that shift; it does not invent Stripe Connect/KYC.
  5. Invalid/empty parent group blocks Post; failures show inline errors.

#J4 — View, edit, and manage applicants on a shift

  1. Operator opens Detailed job view for a bound shift and sees transcribed labels (Location, Experience, Directions, Was posted at, Start Date/Time, Wisconsin time, Hours required, pay practice, contacts, policies, assigned contractors).
  2. Edit the job PATCHes shift fields through server-authorized paths; company_rate / contractor_rate only via RPC.
  3. Stop hiring confirm sets shift_type = upcoming and stop_hiring = true, then closes the dialog.
  4. Cancel contractor removes the user from assignee lists and from the contractor’s shifts membership after the 2-hour/4-hour pay note confirm.
  5. Contractor don’t show up adds the user to the no-show list and adjusts dependability counters server-side.
  6. Empty assignees show “There you’ll see assigned contractors for this shift”; mutation failures stay inline; Discard/Confirm reset groups.

#J5 — Row actions and end job popup

  1. Shared Row actions shows Details, Start tracking, and End (visibility respects closed-shift custom state).
  2. Details opens the shared Detailed job view for the bound shift — no second detail tree.
  3. Start tracking creates a time_entries row (company_request false, contractor = current user, send false) via server insert/RPC, then navigates per source ChangePage.
  4. End shows End job popup; Cancel resets/hides without writes.
  5. Submit writes tracking notes, sets send true, and computes paid_amount from logged hours/minutes × skill rate inside a server RPC (MON-CLIENT-WRITES).
  6. Postmark/notification connectors may be stubbed behind env with timeout; no new money rails.

#J6 — Browse contractors, Preferred, and message

  1. On tab=contractors, operator searches and filters by Location and Skills over a paginated contractor list (empty “No result found.”).
  2. Preferred / Do not Book / No show up mutate the viewed contractor user’s preferred_list_user / do_not_book_list_user / no_show_up_list_user by adding or removing the signed-in company user (CurrentUser) under RLS — not writes to the company operator’s own profile lists.
  3. Send a message creates a chat row visible to both users and navigates to /chat (chat body remains MOD-CHAT).
  4. Back to all returns from contractor detail to the list; load/save errors are visible.

#J7 — Approve time cards and send payments

  1. On tab=time_tracking, operator sees company-scoped time entries with column labels and empty “Nothing was found. Try again later!”.
  2. Approve / Multipal approval set company approval and/or sent-for-payment only via transactional server RPC.
  3. Logged hours/minutes edits recompute paid_amount on the server from shift skill rates (same family as End job popup).
  4. On tab=payments, operator sees Previous payments / Upcoming payment schedule headings, Unpaid status copy, and Multipal payment using the same server send path.
  5. Existing Stripe account display may be read-only; Connect plugin actions are not implemented here (MON-MARKETPLACE).
  6. Dialogs dismiss on success; failures show visible errors.

#J8 — Dashboard metrics, reports, profile, and settings

  1. On tab=dashboard, operator sees KPI labels (Current open shifts, Percentage of open shifts, Placement rate, Total Cancellation rate, Time to fill shift, Repeat contractors, Average weekly cost) and empty “You have no shifts for today.” when appropriate.
  2. Metrics come from company-scoped queries (or honest zeros) — no fabricated Connect balances.
  3. Reports shows Cancellations/No shows, TradeForce Usage, Total budget for the month with empty “Nothing was found at the moment.” and paginated lists.
  4. Profile fields align with company onboarding where they overlap; Save PATCHes profiles under RLS.
  5. Settings mounts only evidenced chrome — no undocumented Connect onboarding.

#Vertical slices (decompose)

Item Product outcome
COSHIFT-HOST /company/shifts host, role gate, and exclusive tab panel switching (including empty → dashboard).
COSHIFT-LIST Posted/Upcoming/Closed filter list; Stop Hiring confirm persist; Closed filter-only; Close the job unwired; open detail.
COSHIFT-POST Post a New Job form, insert shifts, conditional edge shift when date > now.
COSHIFT-DETAIL Sole Detailed job view implement tree: view/edit, stop hiring, cancel / don’t-show applicants.
COSHIFT-ACTIONS Sole Row actions + End job popup trees: details, start tracking, end + paid RPC.
COSHIFT-CONTRACTORS Contractors tab search/filter; Preferred / Do not Book / No show on contractor-user lists; start chat navigate.
COSHIFT-APPROVE-PAY Time-tracking approve + payments tab send_paid RPCs.
COSHIFT-DASHBOARD-META Dashboard KPIs, reports, profile save, settings chrome.

#Key UI states

State When User sees
Empty tab / dashboard URL nav empty or dashboard Dashboard KPI panel
company_shifts tab=company_shifts Shift filters + list + Post a New Job
tabs=postjob Nested post overlay G-New job post form
tabs=detail Nested detail Detailed job view instance
Filter posted / upcoming / closed List custom state type_ Matching shift rows or empty copy
Closed-shift row actions Closed-shifts custom state Constrained Details / Start tracking / End visibility
End job popup open End control Confirm copy, earlier/later ask, notes, Cancel/Submit
Preferred on contractor detail Preferred control Button label Preferred; list membership updates on contractor user
Approve / payment dialogs Approve or Multipal actions Note popup then success dismiss or error
Label / trigger Behavior
Empty tab Show dashboard (nav empty or dashboard)
Dashboard ?tab=dashboard
Company shifts ?tab=company_shifts
Post a New Job Keep company shifts; set tabs=postjob
Open detail Set tabs=detail and/or mount Detailed job view
Contractors ?tab=contractors
Time-tracking ?tab=time_tracking
Payments ?tab=payments
Reports ?tab=reports
Profile (SideBar Settings) ?tab=profile
Settings (SideMenuCompany) ?tab=settings
Send a message Create chat → /chat

#Style and fidelity

Company dashboard uses the cabinet workspace outlet (.surface-card main). Transcribe visible copy and stamp data-bubble-id on migrated roots: page, panel groups (dashboard, company shifts, contractors, time track, payments, reports, profile, settings), Post a New Job, new-job form, Detailed job view, Row actions, End job popup, and primary list/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 date/time fields. Prefer-control button copy is Preferred (not “Prefer”).

#Failure modes

Case Expected behavior
Signed-out or non-company on host Redirect to / or role home; no company panels.
Shift list query / filter failure Plain-language error on list; no silent empty success.
Post validation (empty parent / invalid fields) Block insert; inline errors; stay on form.
Edge shift unavailable / unauthorized User-visible failure after insert; do not pretend notify succeeded.
Stop hiring confirm / edit / cancel / don’t-show write failure Inline error; leave prior state unchanged.
Close the job clicked No persist in source (unwired); UI may no-op — do not invent shift_type/stop_hiring writes.
Closed filter Filter-only custom state; never ChangeThing the shift.
Start tracking / End submit / approve / send_paid RPC failure Popup or row error; map once at boundary; no client-only money flip.
Contractor list / Preferred / Do not Book / No show save failure Visible error; contractor-user lists unchanged.
Profile save failure Visible error on profile panel.
Stripe Connect plugin path Do not implement; read-only account fields only if evidenced.

#Traceability

Product concept Target (route / component / table) Bubble evidence
Company dashboard page /company/shifts, CompanyShiftsPage bTITd
Dashboard panel CompanyDashboardPanel bTKOL0, element_condition:bTKOL0:0
Company shifts list CompanyShiftsList bTIcA, element_condition:bTIcA:0
Post a New Job control PostNewJobButton bTIbh
New job form PostJobForm bTIkQ
Post insert workflow createShift + RLS/RPC bTJer1, bTJfJ1
Schedule notify after post Edge shift bTLtQ, bTMBT, bTMBa
Schedule notify after edit Edge shift bTMBl, bTMBT
Shift type filters shift_type enum + list state shift_type, custom_state:bTIbn:type_
Closed filter (filter-only) list custom state type_=closed bTIcH, bTJfl
Stop Hiring (opens popup) ShowElement / DisplayGroupData bTIdf, bTJft
Stop hiring confirm (list popup) shift_type=upcoming, stop_hiring bTJgL
Close the job (unwired) visible control; no workflow bTIdl
Detailed job view DetailedViewJob (sole tree) bTIgC, instance bTIfM
Edit the job PATCH shift fields / rate RPC bTJmD, bTJmQ
Stop hiring confirm (detail) shift_type=upcoming, stop_hiring bTJlT, bTJlV
Cancel contractor assignee + shifts_list remove bTJkk
Contractor don’t show up no-show list + counters bTJkG, bTJkM
Row actions ShiftRowActions (sole tree) bTHTn
Details / Start tracking / End row controls bTKCl, bTLdP/bTLdV, bTLej
End job popup EndJobPopup (sole tree) bTHkx, submit bTLcX/bTLcd
Contractors panel CompanyContractorsPanel bTJph
Preferred control Preferred button copy bTJyd0
Preferred list mutate contractor user += CurrentUser bTJzT0, bTJzZ0
Do not Book list mutate contractor user lists bTJzd0, bTJzj0
No show up list mutate contractor user lists bTJzl0, bTJzr0
Send a message NewThing chat + ChangePage bTJzA0, bTJzM0, bTJzS0
Time-tracking panel CompanyTimeTrackingPanel bTKSh0
Payments panel CompanyPaymentsPanel bTKea4
Reports / profile / settings meta panels bTKky4, bTKiv4, bTKrz
Tab option values ?tab= from nav nav
Nested post/detail ?tabs= tabs (postjob, detail)
Shifts table public.shifts shifts
Time entries public.time_entries time_tracking1
Preferred / do-not-book / no-show fields user list fields on contractor profile preferred_list_user, do_not_book_list_user, no_show_up_list_user
Custom rates (skill) rates / custom_rate reads custom_rate, rates
Dead Bubble job type omit entity job (residual dead)
Residual company type ownership via profiles + shifts.company_id company

#Naming (idiomatic) — source → target

Source (Bubble) Target Layer
company_dashboard_shifts / bTITd /company/shifts route
DetailedViewJob DetailedViewJob component ts
EndJobPopup EndJobPopup ts
RGaction ShiftRowActions ts
shifts shifts sql
job_title_text job_title sql
hours_required_number hours_required sql
date_date shift_date sql
shift_type_option_shift_type shift_type sql/enum
company_rates_number company_rate sql
contractor_rates_number contractor_rate sql
amount_of_needed_contractors_number seats_needed sql
company_user company_idprofiles sql
stop_hiring_boolean stop_hiring sql
preferred_list_user contractor-user preferred companies list sql
do_not_book_list_user contractor-user do-not-book companies list sql
no_show_up_list_user contractor-user no-show companies list sql
time_tracking1 time_entries sql
paid___number paid_amount sql
send_paid_boolean sent_for_payment sql
approve_company_boolean company_approved (or DATA-MODEL name) sql
logged__hours__number / logged__minuts__number logged_hours / logged_minutes sql
API event shift (bTMBT) edge function shift api
option nav db_values tab query string doc/route

#IR evidence

#Module evidence roots

Supporting segments: ir/slices/bTIbd____root.json, ir/slices/bTIbd__elements____remaining_children.json, ir/slices/bTIbd__elements__bTHQp__elements____remaining_children.json, ir/slices/bTIbd__workflows____part_A.json, ir/slices/edef_bTIgD____root.json, ir/slices/edef_bTHky____root.json, ir/slices/edef_bTHTo____root.json, user types shifts / job / company / custom_rate, option shift_type.

#Path-key ↔ inventory id aliases

Inventory id Export path key
bTITd /pages/bTIbd
bTIgC /element_definitions/bTIgD
bTHkx /element_definitions/bTHky
bTHTn /element_definitions/bTHTo
bTMBT /api/bTMBU

#Panel visibility (URL nav)

Panel Shows when
Dashboard nav equals dashboard or nav is empty
Company shifts nav equals company_shifts
Post a New Job button nav equals company_shifts
New job form tabs equals postjob
Contractors nav equals contractors
Time-tracking nav equals time_tracking
Payments nav equals payments
Reports nav equals reports
Profile nav equals profile
Settings nav equals settings

#Shift type option set

db_value Display
posted posted
upcoming upcoming
closed closed

List filter custom state type_ defaults to posted. Closed filter workflow bTJfl (trigger bTIcH) only SetCustomState type_=closed — it does not ChangeThing the shift.

#Stop Hiring vs Close the job (list)

#Preferred / Do not Book / No show ownership

#Post / edit → edge shift

#Privacy / feature notes

#Decompose refs

tooling/cursor-impl-cycle/output/decompose/MOD-COMPANY-SHIFTS.json — items COSHIFT-HOST through COSHIFT-DASHBOARD-META.

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