Skip to content

The Talent Accelerator — Frontend Developer Documentation

Audience: Frontend developers only. Goal: A complete onboarding + reference guide so a new frontend developer can understand the project, navigate the code, and start contributing without a live walkthrough.

Ground rule: Everything here is derived from the actual source code. Where something cannot be determined from the code, it says "Unable to determine from the available code." Nothing is invented.

ℹ️ Scope note. The frontend is not one app — it is five separate React applications that share one template, one backend, and one login. They live under Frontend Prod/: sso-cicd (sign-in & admin portal), tom-cicd (TOM — offers & pay data), taref-cicd (JE — job evaluation), live-bm-cicd (LBT — benchmarking), dashboard-cicd (analytics). Unless a section says otherwise, code examples are from tom-cicd (the richest app); the same patterns apply to all five because they were forked from the same template.


Table of Contents

# Section
1 Introduction
2 Project Structure
3 Frontend Architecture
4 Routing
5 Module Documentation
6 Pages
7 Components
8 State Management
9 API Layer
10 Forms & Validation
11 Authentication & Authorization
12 Shared Utilities
13 Styling System
14 Assets
15 Environment Configuration
16 Performance Optimizations
17 Error Handling
18 Development Workflow
19 Coding Standards
20 Debugging Guide
21 Common Pitfalls
22 Best Practices
23 Developer Onboarding Checklist

1. Introduction

Purpose of the frontend

The frontend is the user interface for a compensation-management platform ("The Talent Accelerator", internally "TOM"). Companies use it to manage pay data, build candidate offers, evaluate jobs, benchmark pay, and view analytics. (For the product/business view, see the Product Handbook.)

High-level overview

Five single-page applications (SPAs), one per business area, all talking to the same backend REST API:

App folder Business area REACT_APP_APPLICATION
sso-cicd Login + company/user administration SSO
tom-cicd Pay data + offer modelling + AI insights TOM (also builds as ADMIN_PANEL)
taref-cicd Job evaluation + job libraries JE
live-bm-cicd Live Pay Benchmarking Tool LBT
dashboard-cicd Compensation analytics LBT default (serves the dashboard)

They share login through a browser cookie on a common domain (see §11).

Technology stack

Concern Choice Version (from package.json)
Language TypeScript ^4.1.2
Framework React ^17.0.2 (taref-cicd uses React 18)
Build tooling Create React App + CRACO react-scripts 4.0.3, @craco/craco ^6.2.0
State Redux Toolkit + RTK Query @reduxjs/toolkit ^1.6.1
State persistence redux-persist ^6.0.0
Routing React Router react-router-dom ^5.2.0 (v5)
UI library (primary) Ant Design 4 antd ^4.16.6
UI library (secondary) MUI 5 + Emotion @mui/material ^5.15.14
Utility CSS Tailwind CSS (postcss7-compat) via craco.config.js
Styling language LESS less ^4.1.1, craco-less
Charts Recharts ^2.7.2 (TOM/JE/Dashboard)
Rich text react-quill ^2.0.0
HTTP native fetch (wrapped)
JWT decode jwt-decode ^4.0.0
Cookies universal-cookie ^4.0.4
Dates moment + date-fns
Icons @ant-design/icons + react-icons
Git hooks husky + lint-staged ^7.0.1, ^11.0.0
Formatting Prettier .prettierrc present

Design principles (as observed in the code)

  1. One template, five apps. Every app has an identical src/ layout so a developer who learns one learns all five.
  2. API-first via RTK Query. All backend calls are RTK Query createApi "services"; components use the generated hooks.
  3. Path aliases everywhere. Imports use @components, @services, @hooks, etc. (never long relative paths).
  4. Permission-driven UI. Buttons, tabs, routes, and menu items are gated by checkPermission(...).
  5. Colocation. A page owns its subcomponents in its own folder.
  6. Shared cookie SSO. No app has a real login except sso-cicd; the rest read a shared cookie and verify.

2. Project Structure

Every app has the same src/ layout. Understand it once, apply it five times.

text <app>/ ├── public/ # index.html, favicon, color.less (runtime theme), /samples/*.csv ├── src/ │ ├── App.tsx # Root component: bootstraps auth from cookie, picks route config, renders Layout │ ├── index.tsx # ReactDOM entry: Provider + PersistGate + HashRouter │ ├── assets/ # Images, SVGs, fonts │ ├── components/ # Reusable UI components (see §7) │ ├── constants/ # roles, env-derived constants, theme JSON, static data │ ├── hooks/ # Custom hooks (see §12) │ ├── pages/ # Screens, split into admin/ and client/ (see §6) │ ├── router/ # Route configs, paths, permissions, guards (see §4) │ ├── services/ # RTK Query APIs + REST wrapper + cookies/storage (see §9) │ ├── store/ # Redux store + per-domain slices (see §8) │ ├── styles/ # LESS theme + global styles (see §13) │ ├── types/ # Shared TypeScript types │ └── utils/ # Pure helper functions (see §12) ├── craco.config.js # Path aliases, LESS, Ant theme plugin, Tailwind ├── tsconfig.json / tsconfig.paths.json ├── tailwind.config.js ├── .env.example # Required env vars (values injected per environment) ├── .prettierrc └── buildspec.yml / firebase.json # CI/deploy

Folder-by-folder (why each exists)

Folder Purpose Interacts with
App.tsx / index.tsx App bootstrap & providers. index.tsx wires Redux + persistence + router; App.tsx runs the cookie-based login and renders the shell. store, router, services, components/Layout
components/ Reusable UI (tables, modals, buttons, layout shell, stepper, upload). Split between generic and feature-specific. hooks, services, utils, store
constants/ Roles list, env-derived constants (ssoUrl, cookiesDomain), theme JSON (admin.json/client.json), static data (world.json). Imported widely
hooks/ Cross-cutting hooks: typed Redux hooks, useDebounce, useFormValidation, useBreadcrumbs. store, services
pages/ The screens. admin/ = internal-staff screens, client/ = company screens. Each page folder colocates its subcomponents. components, services, store, router
router/ routeConfig.ts (route → component maps), paths.ts (URL strings), permissions.ts (permission constants), Routes.tsx + RouteWithSubRoutes.tsx (guards). pages, hooks, utils
services/ The API layer: one file per backend domain (auth.ts, offers.ts…), restService.ts (the fetch wrapper), cookies.ts, storage.ts. store, constants
store/ Redux store config + one folder per domain slice (auth, offers, companies…). services, types
styles/ theme.less (design tokens), global.less, fonts.less, utils.less. craco.config.js
types/ Shared TS interfaces (globals.ts, index.ts, types.ts). Everywhere
utils/ Pure functions: compensation math (calculateStiTotal…), checkPermission, isAdmin, formatters, validateFile. Pages, components

💡 Why admin/ vs client/ under pages/? The same app template serves both internal staff ("admin") and company users ("client"). The split keeps the two audiences' screens separate. In tom-cicd today, the app renders the client route config for everyone (see §4 — the admin/client branch is currently collapsed).


3. Frontend Architecture

Overall architecture

flowchart TB
    subgraph Browser
        subgraph "React App (one of 5)"
            IDX[index.tsx<br/>Provider + PersistGate + HashRouter]
            APP[App.tsx<br/>cookie login → verify → Layout]
            RT[Router<br/>route config + guards]
            PG[Pages]
            CM[Components]
            HK[Hooks]
        end
        RTKQ[RTK Query services<br/>generated hooks]
        REST[restService.ts<br/>fetch wrapper + token + refresh]
        STORE[(Redux store<br/>+ redux-persist)]
        COOKIE[(Shared cookie<br/>user_data / selected_company)]
    end
    API[(Backend REST API)]

    IDX --> APP --> RT --> PG
    PG --> CM
    PG --> HK
    PG --> RTKQ
    CM --> RTKQ
    RTKQ --> REST --> API
    RTKQ --> STORE
    APP --> COOKIE
    REST --> COOKIE

Application lifecycle (from load to rendered page)

sequenceDiagram
    participant B as Browser
    participant IDX as index.tsx
    participant PG as PersistGate
    participant APP as App.tsx
    participant CK as Shared cookie
    participant API as Backend

    B->>IDX: Load app
    IDX->>IDX: Wrap in Provider(store) + HashRouter
    IDX->>PG: Rehydrate persisted state (auth)
    PG->>APP: Render App
    APP->>CK: getTokenFromSharedStorage()
    alt Cookie present
        APP->>API: verifyUser(application, companyId)
        API-->>APP: user + token + claims (role, permissions)
        APP->>APP: Decode JWT → set auth slice
        APP->>B: Render Layout + Routes
    else No cookie
        APP->>B: Redirect to SSO login
    end

Component architecture

  • Presentational + container blended. Pages are containers (fetch data via RTK Query hooks, hold local state). Components in components/ are mostly presentational but several read Redux directly (Layout, AISuggestionBot, customTabs).
  • Colocation. A page folder (e.g. pages/client/Offers/) contains the page plus its private subcomponents (AddOffer/, OfferModeller/, etc.).
  • Two UI eras coexist. Older Ant Design–wrapped components (Button, Modal, Tabs) and newer Tailwind-based custom* components (customButton, customModel, customTabs) both exist. See §7 and §21.

Design patterns used

Pattern Where
Provider pattern Redux Provider, PersistGate in index.tsx
Config-driven routing routeConfig.ts arrays consumed by a generic RouteWithSubRoutes
Higher-order guard RouteWithSubRoutes wraps each route with auth + permission checks
Generated-hooks / service layer RTK Query createApiuseXQuery/useXMutation
Typed selector/dispatch hooks useTypedSelector, useTypedDispatch
Slice pattern One Redux slice per domain in store/
Path aliasing @components, @services… via CRACO + tsconfig paths

4. Routing

Library & mode

  • React Router v5 with HashRouter (index.tsx). URLs are hash-based: https://app/#/client/offers. This is deliberate — hash routing avoids server-side route configuration on static hosting (S3/Firebase).

Route configuration structure

Routing is config-driven. Four files in src/router/:

File Role
paths.ts The URL strings (nested objects, e.g. paths.client.offers.listing). Params like :company_id.
permissions.ts Permission constant strings (e.g. VIEW_OFFER).
routeConfig.ts Arrays of IRoute objects mapping a path → a component, with flags. Two arrays: admin_routeConfig and client_routeConfig.
Routes.tsx + RouteWithSubRoutes.tsx The renderer and the per-route guard.

An IRoute (router/types.ts):

ts export interface IRoute { path: string; component: any; isPrivate: boolean; // requires auth key: string; routes?: IRoute[]; // nested routes exact?: boolean; breadcrumb?: string; permission?: string; // required permission isCompany?: boolean; }

Route guard logic (RouteWithSubRoutes.tsx)

flowchart TB
    R([Route requested]) --> AUTH{isPrivate &&<br/>not authenticated?}
    AUTH -->|Yes| SSO[Redirect to SSO login]
    AUTH -->|No| PERM{Has required<br/>permission?}
    PERM -->|No| BACK[Redirect to prev path<br/>or default]
    PERM -->|Yes| ROLE{admin path vs<br/>client path matches role?}
    ROLE -->|Mismatch| BACK
    ROLE -->|OK| RENDER[Render the page component]

The guard: 1. Reads state.auth.user; if the route isPrivate and there's no user → window.open(ssoUrl, "_self"). 2. If authenticated but the route's permission fails checkPermission(...), or the admin/client path doesn't match the user's role (isAdmin), it Redirects to the previous path (stored in localStorage.prevPath) or a default. 3. Otherwise it renders route.component, passing nested routes.

Public vs protected routes

Type isPrivate Examples
Public false ResetPassword (/client/reset-password). In sso-cicd: /login, /forgot-password, /reset-password.
Protected true Everything else — all data screens, offers, evaluations, dashboards.

⚠️ Login routes are commented out in the non-SSO apps (routeConfig.ts). Only sso-cicd renders a real login page. The others send unauthenticated users to ssoUrl.

Fallback route (Routes.tsx)

The last Route (no path) is the catch-all. In tom-cicd it decides the landing page by role/permission:

tsx {user?.role === "TOM_MASTER_USER" ? <Route component={Dashboard} /> : hasTOMDashboardPermission ? <Route component={Dashboard} /> : <Route component={GradeSetup} />}

Lazy-loaded routes

  • None found. Route components are imported eagerly at the top of routeConfig.ts (static import). There is no React.lazy/Suspense code-splitting per route. See §16.

5. Module Documentation

Each app is a "module." Below is what each contains. (Business-level detail: Product Handbook §3; backend endpoints: Backend Documentation §7.)

5.1 tom-cicd — TOM (Total Offer Management)

Aspect Detail
Purpose Maintain company pay data; build/send candidate offers; view analytics.
Pages (client) Dashboard, GradeSetup, SalaryRange, CashAllowances, BenefitsPlan, ShortTermIP, LongTermIP, InternalPayrollData, MarketData, Offers (+ AddOffer wizard), EmailTemplate, SubAdmins, CompanyProfile, CreateCompany, Auth/ResetPassword.
Pages (admin) Companies, GradeMapTable, JobFunction, Sectors, User/SubAdmins.
Key services offers, gradeSetup, salaryRange, cashAllowances, benefitsPlan, shortTermIP, longTermIP, internalPayrollData, marketData, dashboard, aiSuggestion, companies, companySubAdmins.
State auth, offers, companies, businessUnit, sectors, grade, countries, subAdmin, email-template, dashboard.
Feature highlights 3-step Offer Builder wizard; AISuggestionBot; CSV upload/download per dataset; email/PDF preview; offer revisions.

5.2 taref-cicd — JE (Job Evaluation)

Aspect Detail
Purpose Create & evaluate jobs (factor questionnaire → grade); job libraries.
Pages Dashboard, JobEvaluation (list + create), CreateJobEvaluation (questionnaire), JobGradeDefination, JobLibrary, PayNet, PayNetJobFunctions (custom library), SubAdmins, Company profile.
Distinctive React 18; deployed on Firebase; extra deps xlsx (mass upload), react-slick. Persists auth and paynet.
Key state auth, jobEvaluation (full working evaluation), paynet, plus shared slices.

5.3 live-bm-cicd — LBT (Live Pay Benchmarking)

Aspect Detail
Purpose Generate market-pay benchmarking reports.
Pages DynamicReport (list + 5-step report wizard), BenchmarkingByTitle (instant view).
Distinctive Trimmed fork of TOM; most legacy routes commented out; custom SVG percentile-bar charts (no chart lib).
Key service livePayBenchmarking.

5.4 dashboard-cicd — Dashboard (ADMIN_PANEL)

Aspect Detail
Purpose Compensation analytics (compa-ratio, pay gap, hiring trends).
Pages Dashboard (charts + cascading filters), plus the benchmarking pages (bundled).
Distinctive Adds recharts + react-select; dashboard service (dashboard-ci endpoints).

5.5 sso-cicd — SSO / Admin Portal

Aspect Detail
Purpose The entry point: login, app picker, company picker, and company/user administration.
Pages Auth (Login/Forgot/Reset), Apps (launcher), CompanyList, CompanyProfile, CompanyUsers, CreateCompany, admin taxonomy pages.
Distinctive Only app with a real login (Formik + Yup). Adds formik, yup, react-slick. Persists auth and selected_app. Writes the shared cookies the other apps consume.

Inter-module relationships

flowchart LR
    SSO[sso-cicd<br/>login + admin] -->|writes shared cookie<br/>+ launches| TOM[tom-cicd]
    SSO --> JE[taref-cicd]
    SSO --> LBT[live-bm-cicd]
    SSO --> DASH[dashboard-cicd]
    TOM -. same backend<br/>+ company context .- JE
    TOM -. .- LBT
    TOM -. .- DASH

They do not import each other's code — they are separate builds. They share data (same backend) and session (shared cookie), not modules.


6. Pages

Pages live in src/pages/{admin,client}/. A page folder colocates its subcomponents. Below is the anatomy every page follows, then the headline pages.

The standard page pattern

Almost every list page follows this shape:

flowchart TB
    A[Page mounts] --> B[Read company_id from cookie / URL]
    B --> C[useXQuery hook fetches list<br/>page, page_size, sort_by, order, search]
    C --> D[Render Table component]
    D --> E[Toolbar: SearchBox + UploadButton + Download + Versions]
    E -->|search change| F[useDebounce 500ms → refetch]
    E -->|Upload click| G[Upload modal → onSubmit mutation → refetch]
    D -->|Create click| H[Modal form → create mutation → refetch]

Headline pages (from tom-cicd)

Page Route (hash) Components APIs State Business logic
Offers list /client/offers customTabs, Table, SearchBox, Button offersApi (fetchOffers, updateStatus, deleteDraft, revise) offers, auth Three tabs (Active / Past / Drafts); revise only when Placed; delete only Draft
Add Offer /client/offers/offer/:offer_id stepper, offer subforms, AISuggestionBot, EmailModal offersApi (create/update details, candidate, modeller, auto-populate, comparator, currency, email) offers (working offer + stage) 3-step wizard; auto-populate; AI insights; email/PDF
Grade Setup /client/grade-setup Table, UploadButton, Upload, Modal gradeSetupApi grade CRUD + CSV + versions; permission VIEW_JOB_GRADE
Salary Range /client/salary-range Table (+ column picker), Upload salaryRangeApi CRUD + CSV + versions; show/hide columns
Dashboard /client or /client/dashboard Recharts charts, MultiFilter, date range dashboardApi dashboard Analytics; permission VIEW_TOM_DASHBOARD
Company Profile /client/companies/:company_id Tabs, CollapsibleCard, nested BU/Region/Country/Legal-Entity pages companiesApi, businessUnitApi companies Company + org structure management
Sub Admins /client/sub-admins Table, forms companySubAdmins subAdmin Company user CRUD; gated by VIEW/CREATE/UPDATE_COMPANY_USER

For JE's evaluation questionnaire, LBT's report wizard, and SSO's user-provisioning tabs, see the Product Handbook §4–§5 for the user-facing flow.


7. Components

Location: src/components/. Full prop-level catalog below. Legend: Generic = reused across pages; Specific = tied to one feature.

Layout/ — the app shell (Generic, singleton)

Wraps every authenticated page. - Props: children only — everything else from Redux/services. - Renders: collapsible left Sider (nav), top Header (notifications bell, Apps launcher, profile dropdown), breadcrumb bar, scrollable content. - Key behaviors: - Sidebar is data-driven from sidebar-config (client_config(jobGrades, company_id)); each item gated by checkPermission. - Company switcher (only TOM_MASTER_USER/TOM_SUPER_USER): lists all companies, writes selected_company cookie, re-runs verifyUser to re-scope the session. - Profile dropdown: "Apps" → window.open(appsPageUrl); "Log out" → logout mutation → redirect to ssoUrl. - Breadcrumbs via useBreadcrumbs. - ⚠️ The admin-vs-client sidebar branch is currently collapsed — both branches use client_config.

Table/ — generic data table (Generic)

Wraps Ant Design Table with a split layout (main columns scrollable; last column rendered as a pinned table for a fixed actions column). - Key props: data, columns ({title, dataIndex, key, width?}), onRowClick?, pagination?, page?, count?, onChangePage?, onPageSizeChange?, isLoading? (renders Skeleton), scrollY?, onTableChange?(pagination, filters, sorter) (how sorting reaches the parent). - Notes: rowKey="id"; page sizes 10/20/50/100; no built-in search — the parent owns search/sort/filter.

stepper/ — multi-step wizard header (Generic; used by Offers)

Wraps MUI Stepper. Props: steps ({title, icon}[]), currentStep, onStepClick(index). Steps ≤ current are "completed."

Upload/ + UploadButton/ — CSV import (Generic)

  • UploadButton is the permission gate: renders only if role !== "COMPANY_ADMIN" && (checkPermission(create) || checkPermission(update)), else null. Props: role, createPermission, updatePermission, onClick, isLoading.
  • Upload is the modal (built on customModel): validates CSV via validateFile, has an "Active" checkbox and a per-type "Download Sample File" link (SAMPLE_FILES_MAP). Props: isVisible, setIsVisible, onSubmit, title, file, setFile.

searchInput/ (SearchBox) — (Generic)

Controlled search box. Props: placeholder?, value, onChange(value: string) (passes the string, not the event). No debounce built in — pair with useDebounce.

7.2 Modals

Component Base Notes
Modal/ Ant Modal centered, destroyOnClose, mode: "versions" | "standard", width default 1092. Props: title, isVisible, footer, setIsVisible.
customModel/ (CustomModal) Tailwind divs width is a Tailwind class string; backdrop-click closes. Used by Upload.

⚠️ There are effectively four modal implementations (Modal, customModel, plus inline overlays in Layout and EmailModal). Prefer Modal for new work unless you need the Tailwind variant. See §21.

7.3 Form primitives

Component Base Key props
customButton/ (CustomButton) Tailwind <button> btnName, onClick, children (icon), iconPosition, isLoading, disabledProp. The de-facto standard button.
Button/ Ant Button variant: download\|add\|upload\|versions\|… (icon chosen from an SVG map), isLoading. Toolbar-action button.
customInput/ (CustomFormInput) HTML input/textarea title, value, onChange(event), type: text\|textarea, disabled. Default lg:w-[48%] (2-per-row).
customSelect/ (CustomSelect) Ant Select title, options: {label,value}[], + all Ant Select props.
FormError/ show: boolean, message. Renders red text only when show.
CustomErrorMessage/ (customErrorMessage) Ant message A function, not a component. Opens a persistent error toast. Used app-wide.

7.4 Tabs

Component Notes
Tabs/ Generic, permission-filtered; route mode (NavLink) or controlled mode.
customTabs/ Offers-specific (hardcoded Active/Past/Draft; hides Draft for some roles).

7.5 Feature-specific (not for reuse)

Component Purpose
AISuggestionBot/ Floating AI-insights bot for the open offer. Self-driven from Redux + ~8 RTK Query mutations, debounced 700ms. No props.
EmailModal/ Offer email preview + send. Renders HTML via dangerouslySetInnerHTML.
CollapsibleCard/ Expandable card with Update/Save buttons.
AuthLandingImg/ Marketing panel on auth pages.

Type-safety status

  • Typed (.tsx + interfaces): Layout, Table, Modal, customModel, searchInput, Button, customButton, Tabs, FormError, AuthLandingImg, Upload, stepper.
  • Untyped (.js/.jsx): AISuggestionBot, CollapsibleCard, CustomErrorMessage, EmailModal, customInput, customSelect, customTabs, UploadButton.

8. State Management

Approach

Redux Toolkit with two kinds of state: 1. Server stateRTK Query API slices (one per backend domain). These cache responses and generate hooks. 2. Client/UI state → hand-written slices (auth, offers, companies, …).

Only auth is persisted (via redux-persist) — see the whitelist. In taref-cicd, paynet is also persisted; in sso-cicd, selected_app is also persisted.

Store structure (store/index.ts)

flowchart TB
    subgraph "Redux Store"
        subgraph "RTK Query API reducers (server state, cached)"
            A1[authApi]:::api
            A2[offersApi]:::api
            A3[companiesApi]:::api
            A4[dashboardApi]:::api
            A5[...15+ more...]:::api
        end
        subgraph "Hand-written slices (UI state)"
            S1[auth ✱ persisted]:::slice
            S2[offers]:::slice
            S3[companies]:::slice
            S4[businessUnit]:::slice
            S5[sectors / grade / countries / subAdmin / dashboard]:::slice
        end
    end
    classDef api fill:#e3f2fd,stroke:#1565c0;
    classDef slice fill:#f3e5f5,stroke:#6a1b9a;

Data flow

sequenceDiagram
    participant C as Component
    participant H as RTK Query hook
    participant RS as restService
    participant API as Backend
    participant ST as Redux store
    participant SL as Slice (extraReducers)

    C->>H: useFetchOffersQuery(args)
    H->>ST: check cache
    alt cache miss
        H->>RS: fetch
        RS->>API: HTTPS + Bearer token
        API-->>RS: JSON
        RS-->>H: normalized data
        H->>ST: cache result
        H->>SL: matchFulfilled → update slice (e.g. auth)
    end
    ST-->>C: data + isLoading + error (re-render)

Global vs local state

State kind Where Example
Global — server RTK Query cache Offers list, companies, dashboard stats
Global — UI Slices auth (user/token/permissions), offers (working offer + wizard stage)
Local useState in a page/component Modal open/close, form field values, search text

The auth slice (the most important slice)

  • State: { user, token: { access, refresh }, permissions[], applications[], is_one_time_password }.
  • How it fills: authApi.endpoints.verify.matchFulfilled decodes the JWT claims (via jwt-decode) to extract role and permissions, merges role into user, and stores applications from user.company.applications. If permissions is empty it defaults to ["all"].
  • Logout reset: the root reducer wipes the entire store and clears localStorage when the logout mutation fulfills.
  • window.store = store is exposed so non-React code (restService, checkPermission) can read/dispatch.

Best practices (this codebase)

  • Use RTK Query hooks for all server data — don't hand-roll fetches in components.
  • Use useTypedSelector / useTypedDispatch (never the untyped useSelector/useDispatch).
  • Keep ephemeral UI state local (useState); only lift to a slice if multiple screens need it.

9. API Layer

Architecture

flowchart TB
    C[Component] -->|useXQuery / useXMutation| SVC[Service: createApi]
    SVC -->|baseQuery| REST[tomService<br/>restService.ts]
    REST -->|Bearer token| API[(Backend REST API)]
    REST -->|401 / invalid token| REF[Refresh token → retry once]
    REF -->|refresh fails| SSO[Clear storage → redirect to SSO]
    REST -->|text/csv| DL[Auto-download file]

Service layer (src/services/)

  • One file per backend domain, each an RTK Query createApi:

ts export const offersApi = createApi({ reducerPath: "offersApi", baseQuery: tomService({ baseUrl: `${process.env.REACT_APP_BASE_URL}/company` }), tagTypes: ["Offers"], endpoints: builder => ({ fetchOffers: builder.query({ query: ({ company_id, ... }) => ({ url: `/${company_id}/offers/list/?...`, method: "GET" }), providesTags: ["Offers"] }), createOfferDetails: builder.mutation({ query: ({ company_id, body }) => ({ url: `/${company_id}/offer/`, method: "POST", body }), invalidatesTags: ["Offers"] }), // ... }), }); export const { useFetchOffersQuery, useCreateOfferDetailsMutation, ... } = offersApi;

  • tagTypes + providesTags/invalidatesTags give automatic cache invalidation (a mutation refetches affected queries).
  • Company-scoped services use baseUrl = REACT_APP_BASE_URL + "/company"; auth uses the bare base URL.

The REST wrapper (services/restService.tstomService)

The single fetch wrapper all services use. It:

  1. Sets Accept/Content-Type: application/json (unless formData).
  2. Reads the access token from localStorage.tokens (loadToken) and attaches Authorization: Bearer <token> on all non-login, non-third-party calls.
  3. Handles special responses:
  4. text/csv → downloads a blob automatically (this is how "Download CSV" works).
  5. 204 No Content → returns a success shape.
  6. Non-JSON → returns a structured error.
  7. Token refresh: on code 5000 or 1002 "Invalid Token", it calls /auth/refresh-token/ with the stored refresh token, re-saves tokens, and retries the original request once. If refresh fails → dispatch logout, clear localStorage, redirect to ssoUrl.
  8. Normalizes success to { data: { data, pagination, message } } so components get a consistent shape.

Request lifecycle

sequenceDiagram
    participant C as Component
    participant HK as useXQuery
    participant TS as tomService
    participant LS as localStorage
    participant API as Backend

    C->>HK: call hook
    HK->>TS: baseQuery(url, method, body)
    TS->>LS: loadToken()
    TS->>API: fetch(url, {Authorization: Bearer})
    API-->>TS: response
    alt token expired (5000/1002)
        TS->>API: POST /auth/refresh-token/
        API-->>TS: new access token
        TS->>LS: saveTokens()
        TS->>API: retry original request
    end
    TS-->>HK: { data } or { error }
    HK-->>C: data / isLoading / error

Authentication in the API layer

  • Tokens live in localStorage.tokens (storage.ts), and also in the shared cookie user_data for cross-app SSO (cookies.ts).
  • saveTokens, loadToken, loadRefreshToken in storage.ts.

Response handling & errors

See §17. In short: tomService returns either { data } or { error: { success, message, code, error, ... } }; components read error from the hook.


10. Forms & Validation

Form handling approaches (there are three)

Approach Where Notes
Ant Design Form Most create/edit modals (compensation data, company, users) Uses Ant's Form/Form.Item rules for validation + useForm.
Controlled useState + useFormValidation Simpler custom forms useFormValidation(values, validate) returns errors, isValid, touch, shouldShowError. Pair with FormError.
Formik + Yup sso-cicd login only The only app using Formik/Yup.

useFormValidation hook

ts const { isValid, errors, touch, shouldShowError } = useFormValidation(values, validate); // validate: (values) => Record<string, boolean> // true = has error // shouldShowError(field): show only after the field was touched

Render an error with the FormError component:

tsx <FormError show={shouldShowError("email")} message="Email is required" />

Validation rules (observed)

Rule Where
Required fields Ant Form.Item rules={[{ required: true }]} and validate functions
File type = CSV validateFile(file.type) before upload (Upload component)
Login email/password Yup schema (sso-cicd)
Business rules (e.g. grade required in user scope) Enforced by the backend; the UI surfaces the returned validation error

Most business validation is server-side (see Backend Documentation §10). The frontend does field-level checks and then displays the backend's is_validation_error messages.

User feedback

  • Success: Ant message.success(...).
  • Errors: customErrorMessage(...) (persistent toast) or inline FormError.
  • Loading: button isLoading spinners; Table Skeleton.

11. Authentication & Authorization

The SSO model (how one login serves five apps)

flowchart TB
    subgraph "sso-cicd"
        LOGIN[Login form Formik+Yup] --> POST[POST /v2/auth/login/]
        POST --> COOKIE[Write tokens to shared cookie<br/>user_data on cookiesDomain]
        COOKIE --> PICK[App picker + Company picker]
        PICK --> SETC[Write selected_application<br/>+ selected_company cookies]
    end
    SETC -->|window.open app URL| APP[TOM / JE / LBT / Dashboard]
    subgraph "Any other app (App.tsx)"
        APP --> READ[Read shared cookie]
        READ --> VER[verifyUser application, companyId]
        VER --> DEC[Decode JWT → role + permissions]
        DEC --> RENDER[Render app, scoped to this user + app]
    end

Login flow (per app, App.tsx::loginUserFromCookies)

  1. getTokenFromSharedStorage() reads the user_data cookie.
  2. If present: saveTokens(...), read selected_company cookie, call verifyUser({ selected_application, companyId }).
  3. If absent: window.open(ssoUrl, "_self") → user logs in on SSO.
  4. On verify rejection: redirect to ssoUrl.

Token management

Token Stored where Lifetime
Access localStorage.tokens + shared cookie 1 day (backend)
Refresh localStorage.tokens + shared cookie 5 days (backend)
  • Refresh is automatic in tomService (see §9).
  • Logout blacklists the refresh token (backend) and the root reducer wipes local state.

Session handling

  • redux-persist keeps auth across reloads.
  • PersistGate shows a loading state until rehydration completes.

Protected pages & permission-based rendering

Two levels:

  1. Route levelRouteWithSubRoutes blocks unauthenticated/unauthorized access (see §4).
  2. UI element levelcheckPermission(permission):

ts export const checkPermission = (permission?: string | string[]) => { const permissions = store?.getState()?.auth?.permissions; if (permissions?.includes("all")) return true; // fallback super-permission if (!permission) return true; if (Array.isArray(permission)) return permission.some(p => permissions?.includes(p)); return permissions?.includes(permission); };

Used to gate buttons (UploadButton), tabs (Tabs), and menu items (Layout). Roles come from constants/roles.ts (admin, company, and special groups like rolesThatCantAccessAiInsights). isAdmin(role) distinguishes internal vs company users.


12. Shared Utilities

Custom hooks (src/hooks/)

Hook Purpose When to use
useTypedSelector Typed useSelector bound to RootState Always instead of raw useSelector
useTypedDispatch Typed useDispatch Always instead of raw useDispatch
useDebounce(value, delay=500) Debounced value Search inputs before triggering a query
useFormValidation(values, validate) Lightweight validation state Custom (non-Ant) forms
useBreadcrumbs(...) Route-driven breadcrumbs In Layout

Utility functions (src/utils/)

Group Files Purpose
Compensation math calculateFixedTotal, calculateStiTotal, getTotalLti, getTotalForBeneFit, calculateDifference, percentageDifference, calculatePercentageAmount, calculatePropose, getGradeRows Offer/pay calculations mirrored from backend logic
Formatting formatNumberWithCommas, formatFirstLetterUpperRestLower, round, getCurrencyTotal, currenciesConverter Display helpers
Dates disabledDates, generateYears, getDaysOfMonth Date-picker helpers
Auth/permission checkPermission, isAdmin Access checks (see §11)
Files validateFile, sanitizeFileName Upload validation
Feedback showSuccessPopup Success UI
Misc sortingFunctions, getDifference, constant Sorting + shared constants

When to use utils: import pure helpers from @utils. Don't duplicate compensation math in components — reuse the utils versions (they mirror the backend formula).

Constants (src/constants/)

File Contents
roles.ts Role groupings (admin, company, rolesThatCantAccessAiInsights, …)
index.ts ssoUrl, appsPageUrl, cookiesDomain, livePayTool, dateFormat, theme vars
admin.json / client.json Ant theme variable sets (runtime theming)
world.json Static country data

13. Styling System

Approach — three layers working together

flowchart LR
    A[Ant Design 4<br/>component styles] --> D[Final UI]
    B[Tailwind CSS<br/>utility classes] --> D
    C[LESS theme.less<br/>design tokens] --> A
    C --> E[Runtime theming<br/>window.less.modifyVars]
    E --> D
  1. Ant Design 4 — the base component look, themed via LESS variables (the antd-theme-webpack-plugin in craco.config.js).
  2. Tailwind — utility classes (bg-themeBlue, text-white, lg:w-[48%]) for layout and custom components.
  3. LESS — design tokens in src/styles/theme.less.

Design tokens (styles/theme.less)

Token Value
@primary-color #2f49d1 (brand blue)
@primary-black #0b2540
@error-color #fa1818
@green-light / @blue-light #23dbaf / #23a7d0
Fonts Montserrat (primary), HK Grotesk (secondary)
Font sizes @text-xxl 40px@text-xs 12px

Runtime theming (admin vs client)

App.tsx calls window.less.modifyVars(adminVars | clientVars) at runtime to switch the theme depending on whether the user is on an admin or client path (adminVars/clientVars come from constants/admin.json/client.json). This lets the same build render two visual themes.

Style files (src/styles/)

File Purpose
theme.less Design tokens (colors, fonts, sizes, borders, transitions)
global.less Global element styles
fonts.less Font-face declarations
utils.less Utility classes
index.less Aggregator

Responsive design

  • Tailwind responsive prefixes (lg:, etc.) — e.g. form fields default to lg:w-[48%] (two per row on large screens).
  • Ant Design's Row/Col grid for page layout.

UI consistency

  • Reuse the shared components (Table, Modal, customButton, form primitives) rather than restyling per page.
  • Use theme tokens/Tailwind theme classes, not hardcoded colors.

14. Assets

Asset type Location Notes
Images / SVGs src/assets/ Illustrations (auth landing), logos, icons; button icons defined in components/Button/config.tsx
Icons @ant-design/icons + react-icons No custom icon font
Fonts Declared in styles/fonts.less Montserrat, HK Grotesk
Static data src/constants/world.json, JE job-library JSON Bundled reference data
Sample CSVs public/samples/*.csv Downloadable upload templates (mapped in Upload's SAMPLE_FILES_MAP)
Runtime theme public/color.less Used by the Ant theme plugin for live theming

Exact asset inventory: the specific image filenames are in src/assets/browse that folder for the current set. (Full listing: Unable to determine from the available code without enumerating the folder.)


15. Environment Configuration

Environment variables (.env.example)

All apps read these (values injected per environment, not committed):

Variable Purpose
REACT_APP_BASE_URL Backend API base URL (all service baseUrls build on this)
REACT_APP_SSO_URL The SSO login app URL (redirect target when unauthenticated)
REACT_APP_APPS_PAGE_URL The app-launcher page URL ("Apps" menu)
REACT_APP_COOKIES_DOMAIN Shared cookie domain (enables cross-app SSO)
REACT_APP_APPLICATION Which app this build is (TOM/JE/LBT/SSO/ADMIN_PANEL) — set by npm scripts

sso-cicd and the app launcher also reference per-app target URLs (tomAppUrl, jeAppUrl, rbAppUrl, ciAppUrl) and a REACT_APP_LIST_LIVE_PAY_BENCHMARKING flag. Exact values are environment-injected — Unable to determine from the available code.

Build configuration

File Role
craco.config.js Path aliases (@components…), LESS loader, Ant theme plugin, Tailwind/PostCSS
tsconfig.json + tsconfig.paths.json TS compiler + path mapping (mirrors the CRACO aliases)
tailwind.config.js Tailwind theme (custom colors like themeBlue)
package.json scripts start, start:tom, build:tom, build, test (via craco)

npm scripts (from tom-cicd)

jsonc "start": "craco start", "start:tom": "set PORT=3002 && cross-env REACT_APP_APPLICATION=TOM craco start", "build:tom": "cross-env REACT_APP_APPLICATION=TOM craco build", "build": "craco build", "test": "craco test"

Deployment

App CI/Deploy
Most (tom, live-bm, dashboard) AWS CodeBuild (buildspec.yml); env pulled from Secrets Manager (prod/tta/*); artifacts = build/
taref (JE), sso Firebase Hosting (firebase.json, .firebaserc) — SPA rewrite all routes to /index.html

Build note: buildspec.yml sets NODE_OPTIONS=--openssl-legacy-provider (needed for Node 17+ with react-scripts 4).


16. Performance Optimizations

What exists

Technique Status
RTK Query caching ✅ Server responses cached; tag-based invalidation avoids redundant refetches
Debounced search useDebounce (500ms) before firing list queries
Server-side pagination Table + backend page/page_size (default 10)
Skeleton loading Table renders skeletons while loading
Debounced AI calls AISuggestionBot debounces 700ms before posting analysis

What is NOT present (opportunities)

Technique Status
Route-level code splitting (React.lazy/Suspense) ❌ Not used — all route components imported eagerly
React.memo / useMemo / useCallback ⚠️ Sparse — not a systematic pattern
Bundle analysis / manual chunking ❌ Not configured (CRA defaults only)

For new work: consider React.lazy for heavy pages (Offer wizard, Dashboard charts) and memoizing expensive derived values. See §22.


17. Error Handling

Layers

flowchart TB
    A[API error] --> B[tomService returns error object]
    B --> C[RTK Query hook exposes error]
    C --> D{Handle in component}
    D -->|show toast| E[customErrorMessage / message.error]
    D -->|inline| F[FormError]
    B -->|token expired| G[Auto-refresh or redirect to SSO]
    H[Render error] --> I[react-error-boundary in JE app]

API errors

  • tomService returns { error: { success, message, code, error, is_validation_error, data } }.
  • Components read the hook's error and show customErrorMessage(error.message) or map error.error to field-level FormErrors.
  • Token/auth errors are handled centrally (refresh → retry → redirect to SSO).

UI / render errors

  • taref-cicd uses react-error-boundary. Other apps: Unable to determine from the available code whether a global error boundary is mounted (none found in the files reviewed).

Fallback UI

  • Table shows Ant Empty when there's no data.
  • PersistGate shows "loading..." during rehydration.

Logging

  • Client-side logging is via console.log/console.error (e.g., JWT decode errors in the auth slice). No dedicated error-tracking SDK found — Unable to determine from the available code whether Sentry/etc. is wired.

18. Development Workflow

Setting up the project (per app)

```bash cd "Frontend Prod/tom-cicd" # pick the app you're working on cp .env.example .env # fill in REACT_APP_* values (ask for the dev values) npm install # installs deps + husky hooks (postinstall) npm start # runs on the CRACO dev server

or an app-specific script:

npm run start:tom # sets REACT_APP_APPLICATION=TOM, PORT=3002 ```

⚠️ You typically run one app at a time. To exercise the full SSO flow locally you'd need SSO + the target app running with a shared cookie domain — check with the team for the local SSO setup. (Local multi-app cookie sharing config: Unable to determine from the available code.)

Running the application

  • npm start → dev server with hot reload.
  • npm run build → production bundle in build/.
  • npm test → CRA/CRACO test runner.

Add a new page

  1. Create a folder under src/pages/client/MyPage/ with index.tsx (colocate subcomponents here).
  2. Add the URL to src/router/paths.ts.
  3. If it needs a permission, add it to src/router/permissions.ts.
  4. Register it in src/router/routeConfig.ts as an IRoute (set isPrivate, permission, key).
  5. Add a sidebar entry in components/Layout's sidebar-config if it should appear in the nav (gate with permission).

Add a new component

  1. Create src/components/MyComponent/index.tsx.
  2. Define a props interface; prefer .tsx (typed) over .js.
  3. Reuse existing primitives (customButton, Table, Modal) rather than re-implementing.
  4. Import via the alias: import MyComponent from "@components/MyComponent".

Add a new route

See "Add a new page" (routing is config-driven — steps 2–4).

Connect to an API

  1. Create src/services/myThing.ts with createApi (copy an existing service like offers.ts).
  2. Set baseUrl (add /company if company-scoped) and tagTypes.
  3. Register the API's reducer and middleware in store/index.ts.
  4. Export it from the services barrel (@services).
  5. Use the generated hook in your page: const { data, isLoading } = useMyThingQuery(args).

Create a reusable component

  • Put it in src/components/, type its props, keep it presentational (take data via props), and avoid reading Redux inside it unless it's genuinely global (like Layout).

19. Coding Standards

Naming conventions (as observed)

Thing Convention Example
Component folders PascalCase (or custom* for the Tailwind variants) Table/, customButton/
Page folders PascalCase Offers/, GradeSetup/
Component files index.tsx (main), colocated subfiles PascalCase AddOffer/index.tsx
Service files camelCase, one per domain offers.ts, cashAllowances.ts
Slice folders kebab-case business-unit/, email-template/
Hooks useXxx camelCase useDebounce.ts, useTypedSelector.ts
Utils camelCase, verb-first calculateStiTotal.ts, formatNumberWithCommas.ts
Interfaces/types I-prefixed IRoute, IAuthState, ITable
RTK Query hooks use<Name>Query / use<Name>Mutation useFetchOffersQuery

Import organization

  • Use path aliases, never deep relative paths: import { useTypedSelector } from "@hooks" — not ../../hooks.
  • Aliases: @ (src), @pages, @assets, @components, @store, @types, @utils, @router, @hooks, @services, @styles, @constants (defined in both craco.config.js and tsconfig.paths.json).
  • Barrel exports: folders expose an index.ts (e.g. @services, @hooks, @store).

Code formatting (.prettierrc)

jsonc { "arrowParens": "avoid", "bracketSpacing": true, "semi": true, "singleQuote": false, "tabWidth": 2, "trailingComma": "es5", "useTabs": false }

  • Double quotes, semicolons, 2-space indent, es5 trailing commas, x => x (no parens on single arrow arg).
  • husky + lint-staged run on commit (postinstall installs husky). Keep commits passing the hooks.

Best-practice conventions

  • Prefer .tsx + typed props for new components (some legacy files are untyped .js).
  • Prefer RTK Query hooks over manual fetches.
  • Prefer the shared components over new one-off UI.

20. Debugging Guide

Tools

Need Tool
State inspection Redux DevTools (enabled in non-production — store/index.ts). Inspect auth, RTK Query caches, slices.
Network / API Browser DevTools → Network. Watch the Authorization: Bearer header, the ?statuses=&page= query strings, and the response envelope (success, code, data).
Component tree React DevTools.
Global store from console window.store.getState() (exposed intentionally).

Common issues & where to look

Symptom Likely cause / where
Redirected to SSO unexpectedly No/invalid token in the shared cookie; check cookies.ts + verifyUser result; token refresh failed in restService.
"You don't have permission" / hidden buttons checkPermission — inspect state.auth.permissions. Empty perms default to ["all"].
Data not refreshing after a create/edit Missing invalidatesTags/providesTags on the RTK Query endpoint.
CSV "download" does nothing The text/csv branch in restService handles this; check the response Content-Type.
Wrong company's data selected_company cookie / company switcher in Layout.
Charts/diagrams blank (Docs only) view the published docs site, not raw markdown.
Styling looks wrong for admin vs client window.less.modifyVars(adminVars/clientVars) in App.tsx.

API debugging tips

  • The standard response envelope is { success, message, code, data, error, is_validation_error, pagination }. A success: false with a code tells you which handler fired (see Backend Documentation §13).
  • code 5000 / 1002 = token issue → the app should auto-refresh; if it loops, the refresh token is bad.

21. Common Pitfalls

Pitfall Why it happens Do this instead
Picking the wrong Button/Modal/Tabs Two eras coexist: Ant-based (Button, Modal, Tabs) vs Tailwind custom*. Four modal implementations exist. For new work, standardize on one set (prefer the typed Ant Modal/generic Tabs and customButton). Don't invent a fifth modal.
Editing untyped .js components Some components (customInput, customSelect, customTabs, UploadButton, AISuggestionBot, EmailModal) have no types. Add types when you touch them; don't propagate any.
Assuming the admin config renders In tom-cicd, App.tsx and Layout currently force the client config for all roles. Don't rely on admin_routeConfig/admin_config being active — verify before building admin-only UI.
Forgetting cache invalidation RTK Query won't refetch without invalidatesTags. Tag queries with providesTags and mutations with invalidatesTags.
Manual fetch in a component Bypasses caching, token handling, refresh. Always add an RTK Query endpoint and use its hook.
Deep relative imports ../../../components/... breaks on moves. Use @components, @services, etc.
Search without debounce Fires a query per keystroke. Wrap the search value in useDebounce.
Trusting client validation only Real rules are server-side. Do field checks for UX, but handle is_validation_error responses.
Reading raw useSelector/useDispatch Loses typing. Use useTypedSelector/useTypedDispatch.
dangerouslySetInnerHTML EmailModal renders server HTML. Only render trusted server HTML; never user input.

22. Best Practices

Components

  • Type props with an I-prefixed interface; prefer .tsx.
  • Keep components presentational; take data via props. Reserve Redux reads for genuinely global components (Layout).
  • Reuse Table, Modal, customButton, and the form primitives.

State management

  • RTK Query for server state; slices for UI state. Persist only what must survive reload (auth).
  • Use providesTags/invalidatesTags for correct cache behavior.
  • Use typed hooks (useTypedSelector/useTypedDispatch).

API integration

  • One createApi service per backend domain; company-scoped services use the /company base.
  • Let tomService handle tokens, refresh, CSV downloads — don't reimplement.

Performance

  • Debounce search; paginate server-side.
  • Consider React.lazy for heavy pages and useMemo/React.memo for expensive renders (not yet standard here — an improvement area).

Maintainability

  • Follow the aliases and folder conventions.
  • Converge the duplicate component sets over time.
  • Mirror compensation math from @utils instead of re-deriving it.

Accessibility

  • Current state: no systematic a11y patterns found (Unable to determine from the available code beyond Ant Design's built-in accessibility).
  • For new work: rely on Ant components' semantics, add aria-*/labels on custom Tailwind controls (customButton, customInput), and ensure keyboard navigation for modals/wizards.

Code reuse

  • Put shared logic in @utils/@hooks; shared UI in @components; shared constants in @constants.

23. Developer Onboarding Checklist

Work through this in order. Check each box.

Environment setup

  • [ ] Clone the repo; open Frontend Prod/.
  • [ ] Read this document's §1§4.
  • [ ] Pick an app (start with tom-cicd). Copy .env.example.env and get dev values from the team.
  • [ ] npm install (installs husky hooks too).
  • [ ] npm start (or npm run start:tom) and load the app.

Understand the project structure

  • [ ] Skim src/ and match each folder to §2.
  • [ ] Read App.tsx and index.tsx — understand the bootstrap + SSO login (§3, §11).
  • [ ] Read store/index.ts and the auth slice (§8).

Run & explore

  • [ ] Log in via the SSO flow (or the team's local shortcut) and reach a page.
  • [ ] Open Redux DevTools; inspect auth.permissions and an RTK Query cache.
  • [ ] Trigger a list page; watch the Network tab for the request/response envelope (§9).

Learn routing & permissions

  • [ ] Read router/routeConfig.ts, paths.ts, permissions.ts, RouteWithSubRoutes.tsx (§4).
  • [ ] Find where a button is gated by checkPermission (§11).

Learn shared components

  • [ ] Read components/Table, components/Modal, components/customButton, components/Upload+UploadButton, components/stepper (§7).
  • [ ] Note the duplicate component sets (§21).

Make your first change

  • [ ] Add a harmless UI tweak to a client page (e.g., a column to a Table, using an existing service field).
  • [ ] Use an alias import and a typed prop.
  • [ ] Verify hot reload; check Prettier/lint pass on commit (husky).

Add something new (stretch)

  • [ ] Add a new list page: paths.tspermissions.tsrouteConfig.ts → sidebar entry → a createApi service (§18).

Submit a pull request

  • [ ] Ensure .prettierrc formatting and lint-staged hooks pass.
  • [ ] Reference the affected app + page in the PR description.
  • [ ] Request review per team convention. (Exact PR/branch policy: Unable to determine from the available code — confirm with the team.)

Appendix — "Unable to determine from the available code"

  • Exact deployed URLs, cookie domain, and per-app target URLs (env-injected).
  • Whether a global React error boundary is mounted in apps other than taref-cicd.
  • Whether a client-side error-tracking SDK (e.g., Sentry) is configured.
  • The exact local multi-app SSO setup for running several apps together.
  • The exact asset filenames in src/assets/ (browse the folder).
  • The team's branch/PR policy.

This document is derived from static analysis of the five apps under Frontend Prod/. Line references reflect the code at analysis time and may drift. For the product/business view see the Product Handbook; for backend/API detail see the Backend Documentation.