jbwui — JBW360 Web Portal (React)
Module path:
st-integration/jbwui/· packagejbw-clientv0.1.0 · Create React App
Purpose
Section titled “Purpose”React portal for JBW staff and dealers, backed by Cognito and jbw-sam.
The web front-end for the JB Warranties ServiceTitan integration (“JB360”, hosted at jbw360.jbwarranties.com). It is an internal/partner portal — login only, no public signup. Two roles via Cognito groups:
jbw-admin— JB Warranties staff: dashboards, dealer onboarding, user management, admin tools.jbw-dealer— HVAC dealers/contractors: their own dashboard, agreement sync, claims.
Core flows: Cognito auth (incl. forced new-password, forgot/reset, idle timeout), a Terms-of-Use acceptance gate between sign-in and any navigation, a 4–5 step dealer onboarding wizard (ServiceTitan or Workiz), agreement sync/matching/approval against ServiceTitan data, claims management (with embedded JB Warranties iframes), and admin/dealer KPI dashboards.
Tech Stack
Section titled “Tech Stack”| Concern | Choice |
|---|---|
| Framework | React 18.3 (CRA / react-scripts 5), JavaScript (no TypeScript) |
| UI | MUI v5 + MUI X (DataGrid, Charts, DatePickers), Emotion, styled-components |
| State | React Context (src/AuthContext.jsx) + local state + localStorage; no Redux |
| Forms | Formik + Yup |
| Routing | react-router-dom v6 (createBrowserRouter in src/App.js), guard in src/RouteGuard.jsx |
| Auth | AWS Cognito via amazon-cognito-identity-js (src/services/jbwAuthServices.js) |
| HTTP | axios shared instance with interceptors (src/services/configs/axiosConfig.js) |
| Misc | crypto-js (AES), lodash, date-fns/dayjs/moment, export-from-json, mammoth (client-side DOCX→HTML for the Terms panel) |
The Terms-of-Use Gate
Section titled “The Terms-of-Use Gate”Added by the July 2026 release. It is not a route — it is an in-page state swap on the sign-in page, which is why the route tables below are unaffected. It sits in front of every login.
Flow. After Cognito auth succeeds, a useEffect on src/pages/SignIn.js:277-300 runs checkTermsAndNavigate():
getUserDetails(user.email)→GET user?email=→ thetblDealerRegistrationrow- If
isTermsAccepted === 1→handleUserNavigation()(the normal admin/dealer branch) - Otherwise →
setShowTermsDialog(true), and the login form is replaced in place by<TermsOfUsePanel>inside the sameGrid/Paper(SignIn.js:329-333)
The panel (src/components/TermsOfUsePanel.jsx) fetches the .docx named by envConfig.TermsUrl (:25), converts it with mammoth.convertToHtml (:30), and injects it via dangerouslySetInnerHTML (:105). Accept stays disabled until the user scrolls to the bottom (:41-47, gate at :130).
- Accept →
updateUserTrems(loginUser.id, 1)→PUT user/updateterms→handleUserNavigation()—SignIn.js:248-266 - Decline →
signOut(); the user is bounced
Consent is persisted per dealer-registration row, not per Cognito user. A second entry point — a “Download Terms of Use” Fab — is on every authenticated page via src/components/TopBar.jsx:134-143,323-334.
Backing Lambdas (both untemplated, added the same release):
| Call | Lambda | SQL |
|---|---|---|
GET user?email= | jbw-sam/src/jbwgetactivedealerregistrationdetails/ | SELECT * FROM tblDealerRegistration WHERE registeredEmail = ? AND isactive = 1 — 404s if no row |
PUT user/updateterms | jbw-sam/src/jbwsetusertermsagreed/ | UPDATE tblDealerRegistration SET isTermsAccepted = ?, termsAcceptedOn = NOW() WHERE id = ? |
Routes
Section titled “Routes”Public auth routes live under RootLayout; all business flows sit under /home behind Cognito (RouteGuard).
Public (under RootLayout):
| Route | Page | Purpose |
|---|---|---|
/, /signin, /signin/:id | pages/SignIn.js | Cognito login; :id carries encrypted email+code for invited users. Post-login: admins → /home/overview, dealers → onboarding check → dashboard or wizard |
/signup | pages/Register.js | Dealer/user registration (verify-then-register) |
/forgotpassword | pages/VerifyUser.js | Initiate forgot-password |
/resetpassword/:id | pages/ResetPassword.js | Reset with code |
/changepassword/:id | pages/ChangePassword.js | Forced new-password challenge |
Authenticated (/home, HomeLayout = TopBar + Sidebar):
| Route | Page | Purpose |
|---|---|---|
/home, /home/overview | pages/Overview.js | Admin dashboard: KPI cards, latest onboardings, Admin Tools console |
/home/dealerdetails[/:id] | pages/DealerDetails.jsx | Dealer dashboard: info, KPIs, tenants, agreements/claims/invoices tabs |
/home/onboardinghome | pages/Onboarding.js | All dealers grid + “Onboard New Dealer” + per-dealer sync |
/home/agreementssynchome | pages/AgreementsSyncHome.jsx | Agreement sync: Map Location / Pending Approval / Approved tabs, Sync Data button |
/home/userslist | pages/UsersList.js | User management |
/home/transactionlog | pages/TransactionLogs.jsx | Transmission/sync logs (from tblTransmissionDetails) |
/home/jbwclaims | pages/JBWarrantiesFrame.jsx | Embedded JB Warranties claim360.aspx iframe |
/home/todohome[/:id] | pages/ToDoHome.jsx | Email-notification inbox (sidebar link currently commented out) |
/home/settings | — | Stub (route renders an MUI icon; pages/settings.js exists but is unused) |
/home/onboarding/* | pages/onboarding/ | Wizard: ClientName → ClientLocation → ClientPlatform → (ST: ClientCustom → ClientId) / (Workiz: ClientAccountId) → CLientConnect |
Auth & Session Behavior
Section titled “Auth & Session Behavior”- Cognito ID token (JWT) stored in
localStorage["token"]; role read fromcognito:groups[0](any group ≠jbw-dealeris treated as admin). - Axios request interceptor attaches
Authorization: Bearer <token>and drives a global loader viaapi-loadingevents. - Response interceptor: on 401, refreshes the Cognito session once (queueing concurrent requests) and retries; on failure fires
session-expired→ re-auth dialog. AuthContextproactively refreshes tokens when <5 min to expiry (60 s check loop) and enforces a 30-minute idle timeout (src/hooks/useIdleTimer.js).
Backend Integration
Section titled “Backend Integration”Everything business-related goes to one place — the jbw-sam gateway; the only non-gateway traffic is deep links and embedded JB Warranties iframes.
All API calls go through the shared axios instance with baseURL = REACT_APP_AWS_GATEWAY_BASE_URL (the jbw-sam API Gateway). Service modules live in src/services/ — the complete endpoint-per-module list is in the code reference.
External (non-gateway) integrations:
- ServiceTitan deep links:
REACT_APP_ST_URL; Workiz job links:REACT_APP_WZ_URL. - JB Warranties member portal iframes (hardcoded
jbandassociates.bizURLs): claims console (claim360.aspx), claim dialog (jb360claim.aspx, with postMessage origin check), agreement PDF (customeragpdf.aspx), payment link. - Release-notes PDF from
REACT_APP_RELEASE_NOTE_URL.
Quirk: several callers branch on APP_ENV === "Development" to read res.data.body vs res.data (API Gateway proxy wrapping differs between stages).
Environment Configuration
Section titled “Environment Configuration”CRA REACT_APP_* vars, baked into the bundle at build time. Files: .env (default = Production values), prod.env, stage.env; selected via env-cmd in npm scripts.
| Variable | Purpose |
|---|---|
REACT_APP_MYVARNAME | Env label (“Production”/“Staging”) — drives staging banner + dev response unwrapping |
REACT_APP_USER_POOL_ID / REACT_APP_CLIENT_ID | Cognito user pool / app client |
REACT_APP_AWS_GATEWAY_BASE_URL | Backend API Gateway base URL |
REACT_APP_ST_URL / REACT_APP_WZ_URL | ServiceTitan / Workiz deep-link bases |
REACT_APP_RELEASE_NOTE_URL / REACT_APP_RELEASE_NOTE_MONTH | Release-notes PDF + label |
REACT_APP_ISSTAGING | Staging flag (YES/NO) |
REACT_APP_TERMS_DOC_URL | Site-root path to the Terms-of-Use .docx fetched by the sign-in consent panel and the TopBar download. Identical value in all three env files (line 10 each); surfaced as envConfig.TermsUrl (src/services/configs/envConfig.js:6) |
Prod points at go.servicetitan.com and the prod API Gateway stage; stage points at integration.servicetitan.com and the dev stage.
Run / Build / Deploy
Section titled “Run / Build / Deploy”Local dev — ⚠ the default .env holds production values:
npm installnpm start # dev server :3000 — Production config!Staging dev — the safe option for day-to-day work:
npm run start:stage # staging config (stage.env), :3006Production build:
npm run build # default .envnpm run build:prod # explicit prod.env bundleStaging build:
npm run build:stage # stage.env bundleDeployment note: there is no deploy config in the repo — the CRA build/ output is synced to the S3 bucket and served via the CloudFront distribution defined in jbw-sam/template.yaml (manual upload + invalidation). npm scripts use Windows set PORT= syntax, so they assume a Windows workstation.
Caveats / Known Issues
Section titled “Caveats / Known Issues”See Also
Section titled “See Also”- Code reference — jbwui (components & services)
- System overview