The standalone weekly-review-dashboard React app — the internal NA and
EMEA/APAC BD and CS weekly review board. This repo is the new home for the
dashboard that previously lived under weekly-review-dashboard/ in the
jeff-os monorepo; the history was carried over with git filter-repo.
Production URL: https://na-rep-dashboard.netlify.app.
This README is the single source of truth for the app. Keep it current so a
new collaborator can orient in minutes. Deploy steps and team_admin seating
live in DEPLOY.md.
- Manager: Jeff, the only global role. Manager sees every team and region.
- Team admin: scoped by team and region through
team_admins; example in the runbook is Lara Kidd for CS. - Rep: sees their own week and read-only team summary.
- There is no npm install, package manager, or dev bundler. The app is a flat
list of browser scripts loaded by
index.html. - Serve the raw app for development:
python3 -m http.server 8000from the repo root, then open http://localhost:8000.- Edit
index.htmlorsrc/files and refresh; Babel runs in the browser viavendor/babel-standalone.min.js.
- Build the single-file deploy bundle:
python3 build/bundle.pywritesdist/index.html.python3 build/bundle.py --checkprints a build summary.
- Run the test suites:
node --test tests/*.mjs— the Node built-in test runner over the.mjssuite. Use the glob form; the bare directory form (node --test tests/) is known to flake and is not supported here.python3 -m pytest tests/test_rfc151_reps_parity.py tests/test_issue_3781_currency_toggle.py -q— the two relocated parity tests (see "Roster changes" and CI).
- Deploys happen via Netlify from
main.netlify.tomlrunspython3 build/bundle.pyand publishesdist/, and redirects all routes toindex.html. - Manual deploys publish
dist/, not the raw app directory. - Operational deploy details live in
DEPLOY.md.
db/*.sqlholds the Supabase schema, RLS, seed, and verification SQL.- Schema changes in db/*.sql do not reach Supabase until Jeff applies them.
- A SQL change merged here is a request to apply; landing the PR does not run it against the live database.
RLS is the real data boundary; the client only mirrors it. Roles:
- Manager is global bypass and sees every team and region.
- Team admin is RLS-scoped through
team_adminsrows joined to the rep's team and region. Ateam_admin(e.g. Lara for CS) can write the shared dashboard state —checks,asks,manager_notes, standup, and wins — for the reps inside her team×region scope. She cannot widen her own scope, cross teams, or touch reps outside her region rows. - Rep is owner scope plus same-team read where policy allows.
- Client gating uses
canManageRep,canManageAny,isManagerialRole, andadminScopes; RLS remains the only real backstop for data access.
-
index.html- app shell, inline CSS, ordered script list,isPreview,App,HomeView, nav, View-as, workspace and region switchers,viewrouter, andReactDOM.createRoot. -
README.md- this canonical onboarding document. -
DEPLOY.md- deploy and team_admin seating runbook. -
CURSOR_PROMPT.md- legacy pointer to this README. -
netlify.toml- Netlify build, publish, and redirect config. -
.gitignore- ignored generated and local files. -
assets/-mindtools-logo.pngand Inter font subsets. -
vendor/- pinned browser dependencies; there is no npm install flow. -
db/- Supabase schema, RLS, seed, and verification SQL. -
build/- bundler tooling, especiallybundle.py. -
tests/- Node built-in.mjstests for the browser app source plus the two relocated Python parity tests. -
dist/- generated deploy output; never hand-edit. -
src/data-model.js- static data and helpers:REPS,DELIVERABLES,WEEKS,QUARTERS,TEAMS,REGIONS,FX_RATES, visibility, RBAC scope, currency, URL state, and standup helpers. -
src/supabase-client.js- Supabase client, auth helpers, DB read/write functions, and realtime subscriptions. -
src/components.jsx- shared UI primitives. -
src/manager.jsx-APP_PAGESnav registry plusFlagQueue,ResolvedSection,ManagerNote,MarkedByStamp. -
src/rep-view.jsx-RepView, one rep's selected-week view. -
src/team-rollup.jsx-TeamRollup, the reps by deliverables scan grid, sectioned by region. -
src/standup.jsx-StandupView,StandupCell,MentionAutocomplete,MentionedYouBanner, daily async grid, Tue/Thu sync, mentions, and own realtime cycle. -
src/wins-form.jsx-WinsFormView, the four-section Weekly Wins form withWF_WEEKSand its own load/save/subscribe cycle. -
src/my-number.jsx-MyNumber, a rep's private NB deal stack or CS book deep dive. -
src/target-board.jsx-LeaderboardView, ranked attainment board with region buckets, display currency, MTD/QTD/YTD, and historical quarter-final switching. -
src/attainment-data.jsx- attainment V2 data layer:ATT_QUARTER,loadAttainmentV2,attBuildLive, quarter-final helpers, and formatters. -
src/tweaks-panel.jsx-useTweaksandTweaksPanel, a Claude Design sandbox panel, not app navigation. -
src/auth-gate.jsx-AuthGate, the magic-link login wall. -
src/don-onboarding.jsx-DonOnboarding, induction checklist view for Don Hazelwood. -
src/stuart-onboarding.jsx-StuartOnboarding, induction checklist view for Stuart Chadwick. -
src/don-induction-content.js-DON_INDUCTION_MDcontent. -
src/stuart-induction-content.js-STUART_INDUCTION_MDcontent. -
src/onboarding-links.js-ONBOARDING_LINKSdeep-link map. -
src/attainment.css- Target Board styles, inlined bybundle.py. -
src/don-onboarding.css- induction styles, inlined bybundle.py.
-
There is no module system.
-
index.htmlloads the app as a flat list of browser scripts. -
Shared code intentionally couples through
window. -
Script order in
index.htmlis the dependency graph. -
Dev runs Babel in the browser through
vendor/babel-standalone.min.js. -
Production uses
bundle.pyto generatedist/index.html; the bundle unpacks withLOADER_SCRIPT,DecompressionStream, blob URLs, and Babel transform. -
React, ReactDOM, Babel Standalone, Supabase JS.
-
src/supabase-client.jsbefore callers. -
src/data-model.jsbefore every consumer ofREPS,WEEKS, and helper functions. -
src/components.jsxbefore views. -
View scripts before callers that render them.
-
src/auth-gate.jsxaftersrc/supabase-client.js. -
src/attainment-data.jsxbeforesrc/my-number.jsxandsrc/target-board.jsx. -
Content JS before onboarding JSX.
-
The trailing inline bootstrap runs last.
-
Appowns a single string router state namedview. -
setViewis the only router mutator. -
parseUrlStatereads deep-link state once at mount. -
serializeUrlStatewrites URL state back after changes. -
viewis either anAPP_PAGESid or aREPSid. -
Current page ids are home, rollup, leaderboard, standup, wins, manager:flags, don:onboarding, and stuart:onboarding.
-
The render chain checks page ids first.
-
Any unmatched non-page string routes to
RepView. -
A page id collision with a rep id hides that rep route.
-
statecomes fromloadState, backed by Supabase or localStorage. -
Completion state is keyed by rep id, week id, and deliverable id.
-
state.checksstoresmarkedByandat. -
state.asksstorestext,at, and optionalresponse. -
state.resolvedAsksstores the soft-resolved ask mirror. -
state.managerNotesstoresnote,updated_by, andupdated_at. -
standupFillslives outsidestate. -
stateForViewsmergesstateandstandupFillsso standup realtime cannot clobber shared state. -
WinsFormViewandStandupVieweach run their own load/save/subscribe cycle. -
devViewAslets managers and preview mode impersonate another user for testing. -
effectiveUserisdevViewAswhen set, otherwiseauthedUser. -
isManager,myRepId,myTeams, andviewerScopederive fromeffectiveUser. -
isPreviewis hostname-derived and exposed asIS_PREVIEW. -
Preview with no user gets manager-parity client scope.
-
A signed-in user with empty scope stays empty; the client fails closed.
-
RLS remains the real data boundary.
-
teamsForUserderives allowed teams foreffectiveUser. -
activeTeamis clamped tomyTeams; stale localStorage cannot widen access. -
The workspace switcher appears only when
myTeamshas more than one entry. -
viewerScopeForUserderives team and region scope. -
regionPillis clamped to scope. -
activeTeam,viewerScope, andregionPillare threaded into each major view.
Core data lives in src/data-model.js and is exported with Object.assign.
-
REPSentries carry id, name, role, initials, hue, region, team, skipped deliverables, links, email, active-through visibility, and emit flag. -
DELIVERABLESentries carry id, title, short label, why text, icon, document link fields, note, automatic status flag, and active-through retirement. -
QUARTERSentries carry id, label,startMonday, and week count. -
WEEKSentries are produced bybuildWeeksand carry id, index, Monday, Friday, Sunday, quarter, andqIndex. -
REGIONS,REGION_ORDER,TEAMS,FX_RATES, andDISPLAY_CURRENCIESsupport filtering and display. -
buildWeekswalksQUARTERSin order. -
It emits gapless Monday through Sunday weeks.
-
It assigns global sequential week ids and indexes.
-
It assigns quarter id and
qIndexfor quarter UI and emails. -
Existing week ids w1 through w23 must never be renumbered or re-dated.
-
localStorage keys, URL params, Supabase rows, and manager notes depend on stable week ids.
-
Add future quarters by appending to
QUARTERS; do not edit existing Q2 or Q3 entries. -
Week/date:
weeksForQuarter,quarterForWeek,currentQuarterId,currentWeekIndex,weekForDate. -
Visibility/completion:
repVisibleInWeek,deliverablesForWeek,activeDeliverablesFor,delComplete. -
State/URL:
loadState,saveState,checkKey,parseUrlState,serializeUrlState. -
Standup/email:
standupFillsFromRows,standupStatus,buildWeekEmail,buildQuarterEmail,openMailto. -
Region/currency:
regionForRep,repsByRegion,regionCurrency,convertAmount,formatCurrencyAmount. -
RBAC:
repById,isManagerialRole,canManageRep,canManageAny,teamsForUser,defaultTeamForUser,viewerScopeForUser,regionsUnderScope,repsUnderScope.
Project: tvdizqryowracmtjdskv.supabase.co.
-
src/supabase-client.jscontains the Supabase URL and anon key. -
The anon key is public by design; RLS gates data access.
-
Auth uses magic links only.
-
Sign-in is allowlist-gated.
-
A Supabase trigger creates the user row from the allowlist on first sign-in.
-
getMyUserloads the user row by auth id; for team admins, it also loads scopes intoadminScopes. -
Auth/client:
getSupabaseClient,getSession,getMyUser,sendMagicLink,verifyEmailOtp,signOut,onAuthChange. -
Shared dashboard state:
loadStateFromSupabase,toggleCheckSupabase,setManagerNoteSupabase,setAskSupabase,reopenAskSupabase,setAskResponseSupabase,subscribeRealtime,migrateLocalToSupabase. -
Standup/wins:
loadStandupForDate,saveStandupField,subscribeStandupChanges,loadStandupFills,loadWins,loadAllWinsForWeek,saveWins,subscribeWinsChanges. -
Attainment/induction:
loadAttainment,loadAttainmentQuarterFinals,loadAttainmentForQuarter,loadClosedWonDeals,loadRenewalBook,loadCsQuarterlyTargets,deriveAttainmentPcts,loadInductionState,loadInductionStateFor,setInductionItem.
Supabase tables:
| table | key columns | notes |
|---|---|---|
| allowed_emails | email, role, rep_id | Allowlist gate; trigger populates users. |
| users | auth_id, email, role, rep_id | Role is manager, team_admin, or rep. |
| checks | rep_id, week_index, deliverable_id | Uses integer week_index; client converts with weekIdToIdx. |
| asks | rep_id, week_index, deliverable_id, response, resolved fields | Uses integer week_index; resolve is soft-resolve. |
| manager_notes | rep_id, week_id, del_id | Exception: uses string week_id. |
| standup_entries | date, rep_id, standup fields | Date-based; no week index. |
| wins | rep_id, week_index, worked_on, invisible, big_win, hype | Uses integer weekIndex directly. |
| teams | id, label | Team registry. |
| reps | rep_id, team_id, region, active | Server roster used by RLS. |
| team_admins | auth_id, team_id, region | Per-region scopes; inert unless user role is team_admin. |
-
db/migration-team-rbac-schema.sqladds teams, reps, team admins, and widened role checks. -
db/migration-team-rbac-rls.sqldefines RLS policies and carries the reps backfill rows the parity test guards. -
db/migration-wins.sqlanddb/migration-wins-rls-harden.sqldefine and harden wins storage. -
db/migration-membership-gate-checks-asks-wins.sqlgates shared tables by membership. -
db/0002_ask_responses.sqladds ask responses. -
db/migration-resolved-flags.sqladds resolved flag storage. -
db/migration-attainment.sql,db/migration-attainment-v2.sql,db/migration-attainment-detail-rls.sql, anddb/migration-attainment-quarter-final.sqlsupport attainment. -
db/verify-rls-cutover.sqlanddb/test-team-rbac-rls.sqlverify RLS behavior. -
checksandasksuse integerweek_index. -
manager_notesuses stringweek_id. -
winsreceives integerweekIndexdirectly. -
Do not pass a string week id to a function that expects an integer week index.
-
loadAttainmentV2calls loaders for snapshots, closed-won deals, renewal book, and CS quarterly targets. -
attBuildLiveassembles NB and CS live shapes. -
Historical quarter finals come from
loadQuarterFinalsand the attainment_quarter_final table. -
LeaderboardViewrenders historical quarter-final choices withattQuarterFinalOptions.
-
vendor/pins:react.development.js,react-dom.development.js,babel-standalone.min.js,supabase-js.min.js. Upgrading means replacing the pinned file; there is no lockfile. -
python3 build/bundle.pywritesdist/index.html. -
python3 build/bundle.py --checkprints a build summary. -
The bundle inlines source, vendor files, fonts, and logo assets.
-
The generated output is one self-contained HTML file.
-
netlify.tomlrunspython3 build/bundle.py, publishesdist, and redirects all routes toindex.html. -
Node suite:
node --test tests/*.mjs(glob form; the bare directory form flakes and is not supported here).tests/quarters.test.mjschecksWEEKSlength, Q2/Q3 dates,qIndex,weeksForQuarter, and quarter-scoped emails.tests/rbac-helpers.test.mjscheckscanManageRep,canManageAny,isManagerialRole,teamsForUser, and inert scope guard behavior.tests/rbac-matrix-lockstep.test.mjsencodes the SQL truth table and assertscanManageRepby persona.tests/url-state.test.mjschecksparseUrlStateandserializeUrlState.tests/viewerscope.test.mjschecksviewerScopeForUser,regionsUnderScope, andrepsUnderScope.tests/deliverables-retirement.test.mjschecksdeliverablesForWeekretirement behavior.tests/quarter-finals.test.mjschecksattBuildQuarterFinalNB and CS shapes.
-
Python parity suite:
tests/test_rfc151_reps_parity.pyguards parity betweenREPS[]insrc/data-model.jsand the serverrepsbackfill rows indb/migration-team-rbac-rls.sql. CI fails when the two drift.tests/test_issue_3781_currency_toggle.pyguards the currency-toggle infrastructure insrc/data-model.js.
Adding a rep is one PR in THIS repo that changes both halves of the roster pair together:
- Append the rep to
REPSinsrc/data-model.js— a unique id, hue, initials, region, team, and (for EMEA/APAC CS reps) skipped deliverables set to outreach and commitments. - Add the matching backfill row in
db/migration-team-rbac-rls.sql(the reps backfill rows the relocated parity test reads): the same rep id, name, team id, region, and active flag. - The relocated
tests/test_rfc151_reps_parity.pyguards the pair — it reddens CI ifREPS[]and the backfill rows drift. Run it locally before pushing:python3 -m pytest tests/test_rfc151_reps_parity.py -q.
Then Jeff applies the backfill to live Supabase and handles the
monorepo-side target/registry leg (the targets and the registry entries that
live outside this repo). Do not assume landing the PR updates the live
reps table — see "Schema and Supabase authority".
- Seat a team admin: insert an allowlist row with role
team_adminand null rep id, plus oneteam_adminsrow per covered region (auth id, team id, region). Never use rolemanagerfor a division lead. FollowDEPLOY.mdfor the operational runbook. - Add a deliverable: append it to
DELIVERABLESinsrc/data-model.js, add its id to relevant reps' skip lists, and (for a form-opener) follow theonOpenWinsoronOpenStanduppattern insrc/rep-view.jsx. - Add a view: create a new JSX file under
src/that assigns its view towindow, add its script tag inindex.htmlat the correct dependency position, append a page entry toAPP_PAGESinsrc/manager.jsx(the icon must be a case handled byIcon), add a routing case inApp, and threadauthedUser,activeTeam,viewerScope, andregionPillinto the new view as needed. - Add a quarter: append one entry to
QUARTERSinsrc/data-model.js. Do not modify existing Q2 or Q3 entries. The newstartMondaymust be the Monday immediately after the previous quarter's last week. Q3 ends at w23: Monday Sep 28 2026 through Sunday Oct 4 2026. Q4 2026 therefore starts Monday Oct 5 2026:new Date(2026, 9, 5). Do not usenew Date(2026, 8, 1).
- No module system exists;
windowcoupling is intentional and script order is the dependency graph. - The
viewnamespace is shared by page ids and rep ids. - A page id collision with a rep id hides that rep route.
standupFillslives outsidestate.WinsFormViewandStandupVieweach run their own load/save/subscribe cycle.isPreviewis hostname-derived; a production host matching the preview pattern would get manager-parity client scope.- RLS is the only real backstop for data access.
- Week id is a string like w5.
- Week index is a number like 5.
- Storage and URL state use the string week id unless a Supabase function explicitly expects integer week index.
- CS monthly has no target; renewal MTD is null and should render as an em dash, never 0 percent.
FX_RATESare display-only approximations.dist/index.htmlis generated output and must not be hand-edited.