Skip to content

Onboarding

Visual architecture guide for new developers.

flowchart TB
    subgraph External["External Services"]
        API["Data-Gov API<br/>(Rails Backend)"]
        BioLoupe["BioLoupe Platform<br/>(Login)"]
    end

    subgraph App["React App"]
        Main["main.tsx"]
        Auth["AuthProvider"]
        Query["QueryClientProvider"]
        AppComp["App.tsx"]
        Forecast["Forecasting.tsx"]

        subgraph Sections["Feature Sections"]
            Config["Configuration"]
            Model["Model"]
            Sales["SalesChart"]
            Monte["MonteCarlo"]
            Tornado["Tornado"]
        end
    end

    subgraph State["State Layer"]
        RQ["React Query<br/>(Server State)"]
        Jotai["Jotai Atoms<br/>(Client State)"]
    end

    Main --> Query --> Auth --> AppComp --> Forecast
    Forecast --> Sections

    API <--> RQ
    BioLoupe <-.->|JWT Cookie| Auth
    RQ -->|Sync Data| Jotai
    Jotai <-->|Read/Write| Sections

Simple explanation:

  • User visits app → Auth checks if logged in → Loads main Forecasting page
  • React Query fetches data from API → Syncs to Jotai atoms → Sections read/write atoms

sequenceDiagram
    participant User
    participant App
    participant AuthProvider
    participant API as Data-Gov API
    participant BioLoupe as BioLoupe Login

    User->>App: Opens app
    App->>AuthProvider: Mount
    AuthProvider->>API: GET /api/check_session<br/>(with JWT cookie)

    alt Session Valid
        API-->>AuthProvider: { logged_in: true, user: {...} }
        AuthProvider->>App: isLoggedIn = true
        App->>User: Show Forecasting
    else Session Invalid
        API-->>AuthProvider: { logged_in: false }
        AuthProvider->>App: isLoggedIn = false
        App->>BioLoupe: Redirect to /users/login
        User->>BioLoupe: Enters credentials
        BioLoupe->>BioLoupe: Sets JWT cookie
        BioLoupe->>App: Redirect back
        Note over App: Cycle repeats, session now valid
    end

Simple explanation:

  1. App loads → checks if you’re logged in via API
  2. If yes → show the app
  3. If no → send you to BioLoupe login page
  4. After login → cookie is set → you come back authenticated

flowchart LR
    subgraph API["Backend"]
        Endpoint["/api/statistics"]
    end

    subgraph ReactQuery["React Query Layer"]
        Hook["useStatistics()"]
        Cache["Query Cache"]
    end

    subgraph Jotai["Jotai Layer"]
        RefRows["referenceRowsAtom<br/>(API data - read only)"]
        InstRows["instanceRowsAtom<br/>(User edits)"]
        Derived["Derived Atoms<br/>geographies, indications"]
    end

    subgraph Components["Components"]
        Dropdown["Dropdowns"]
        Sections["Sections"]
    end

    Endpoint -->|fetch| Hook
    Hook -->|cache| Cache
    Hook -->|setReferenceRows| RefRows
    RefRows -->|auto-compute| Derived
    Derived -->|useAtomValue| Dropdown
    RefRows -->|useAtomValue| Sections
    InstRows -->|useAtomValue| Sections

Simple explanation:

  1. useStatistics() fetches data from API
  2. Data syncs to referenceRowsAtom (read-only reference data)
  3. Derived atoms auto-compute lists (geographies, indications)
  4. Components read from atoms using useAtomValue

flowchart TB
    subgraph Stores["Two Main Stores"]
        Ref["Reference Store<br/>(referenceRowsAtom)<br/>━━━━━━━━━━━━━<br/>• From API<br/>• Read-only<br/>• Disease configs"]
        Inst["Instance Store<br/>(instanceRowsAtom)<br/>━━━━━━━━━━━━━<br/>• User edits<br/>• Editable<br/>• Form values"]
    end

    subgraph Context["Context Atoms"]
        Geo["geoAtom<br/>(active geography)"]
        Ind["indicationAtom<br/>(active indication)"]
        Year["selectedYearAtom"]
    end

    subgraph Sections["Section Atoms (via Factories)"]
        ConfigAtoms["config.assumptions<br/>config.lines<br/>config.population"]
        IncAtoms["incidence.evolution<br/>incidence.erosion"]
        MonteAtoms["monteCarlo.variables"]
    end

    Ref --> Context
    Inst --> Context
    Context --> Sections

Simple explanation:

  • Reference rows = Data from API (disease info, defaults) - never changes
  • Instance rows = Your edits (form values) - you can change these
  • Context atoms = What you currently have selected (geography, indication, year)
  • Section atoms = Computed values for each section (derived from above)

5. How Data is Stored (Normalized Pattern)

Section titled “5. How Data is Stored (Normalized Pattern)”
flowchart TB
    subgraph Row["Each Row in Store"]
        direction LR
        G["geo: 'USA'"]
        I["indication: 'Breast Cancer'"]
        L["line: '1L'"]
        Y["year: 2024"]
        N["name: 'marketShare'"]
        V["value: 0.25"]
    end

    subgraph Query["Query System"]
        Q["getValue(rows, {<br/>  geo: 'USA',<br/>  indication: 'Breast Cancer',<br/>  line: '1L'<br/>}, 'marketShare')"]
        R["→ Returns: 0.25"]
    end

    Row --> Query

Simple explanation:

  • All data stored as flat rows with keys: geo + indication + line + year + name
  • To get a value: query with those keys + the field name
  • Fast O(1) lookups instead of nested objects

flowchart TB
    subgraph Main["atoms.ts (Orchestrator)"]
        Shared["Shared Atoms<br/>━━━━━━━━━━━━━<br/>geoAtom<br/>indicationAtom<br/>instanceRowsAtom"]

        Factory["createForecastingAtoms()"]
    end

    subgraph Factories["Section Factories"]
        CF["createConfigAtoms(deps)"]
        IF["createIncidenceAtoms(deps)"]
        MF["createModelAtoms(deps)"]
        MCF["createMonteCarloAtoms(deps)"]
    end

    subgraph Returns["Returned Atoms"]
        CA["config.assumptions<br/>config.lines<br/>config.updateLine"]
        IA["incidence.evolution<br/>incidence.setEvolution"]
        MA["model.yearLabels<br/>model.diseaseConfig"]
        MCA["monteCarlo.variables"]
    end

    Shared -->|pass as deps| Factory
    Factory -->|call| CF
    Factory -->|call| IF
    Factory -->|call| MF
    Factory -->|call| MCF

    CF --> CA
    IF --> IA
    MF --> MA
    MCF --> MCA

Simple explanation:

  1. Main atoms.ts creates shared atoms (geo, indication, rows)
  2. Calls each section’s factory function, passing atoms as parameters
  3. Sections NEVER import from atoms.ts directly (prevents circular dependencies)
  4. Factories return section-specific atoms for components to use

sequenceDiagram
    participant User
    participant Component
    participant SetAtom as useSetAtom
    participant Jotai as Jotai Store
    participant Derived as Derived Atoms
    participant OtherComp as Other Components

    User->>Component: Changes input value
    Component->>SetAtom: updateAssumptions({ treatmentRate: 80 })
    SetAtom->>Jotai: set(instanceRowsAtom, newRow)

    Note over Jotai: Row updated:<br/>{ geo: 'USA', indication: 'X',<br/>name: 'treatmentRate', value: 80 }

    Jotai->>Derived: Triggers recomputation
    Derived->>OtherComp: Components using this data re-render
    OtherComp->>User: Updated UI

Simple explanation:

  1. User changes a value in a form
  2. Component calls a setter atom (e.g., updateAssumptions)
  3. Setter atom updates the instance store
  4. Any derived atoms that depend on this data auto-recompute
  5. Components using those atoms re-render with new values

flowchart LR
    subgraph Components["Components"]
        Config["Configuration.tsx"]
        Model["Model.tsx"]
        Monte["MonteCarlo.tsx"]
    end

    subgraph Atoms["forecastingAtoms"]
        CA["config.assumptions"]
        CL["config.lines"]
        ML["model.yearLabels"]
        MV["monteCarlo.variables"]
    end

    subgraph Hooks["Jotai Hooks"]
        Read["useAtomValue<br/>(read only)"]
        Write["useSetAtom<br/>(write only)"]
    end

    Config -->|useAtomValue| CA
    Config -->|useAtomValue| CL
    Config -->|useSetAtom| CA

    Model -->|useAtomValue| CA
    Model -->|useAtomValue| ML

    Monte -->|useAtomValue| MV
    Monte -->|useSetAtom| MV

Hook selection rules:

HookWhen to UseRe-renders?
useAtomValueOnly reading dataYes, when atom changes
useSetAtomOnly writing dataNever
useAtomBoth read & writeYes, when atom changes

flowchart TB
    subgraph Step1["1. App Loads"]
        Load["Forecasting.tsx mounts"]
    end

    subgraph Step2["2. Fetch Data"]
        RQ["useStatistics()<br/>fetches /api/statistics"]
        Sync["setReferenceRows(data)"]
    end

    subgraph Step3["3. Derive Lists"]
        Geos["geographiesAtom<br/>['USA', 'EU5', 'Japan']"]
        Inds["indicationsAtom<br/>['Breast Cancer', 'Lung Cancer']"]
    end

    subgraph Step4["4. User Selects"]
        Select["User picks:<br/>USA + Breast Cancer"]
        Init["initIndicationAtom()<br/>creates 50+ instance rows"]
    end

    subgraph Step5["5. Sections Render"]
        Sections["Configuration, Model, etc.<br/>read from instance + reference rows"]
    end

    subgraph Step6["6. User Edits"]
        Edit["User changes treatment rate"]
        Update["updateAssumptions({ treatmentRate: 80 })"]
        Rerender["Dependent components re-render"]
    end

    Step1 --> Step2 --> Step3 --> Step4 --> Step5 --> Step6

flowchart TB
    subgraph EnvFiles[".env Files"]
        ENV["VITE_API_URL<br/>VITE_BIOLOUPE_URL<br/>VITE_ENABLE_PRICING"]
    end

    subgraph Config["config.ts"]
        GetAPI["getApiUrl()<br/>reads VITE_API_URL"]
        GetAuth["getBioloupeUrl()<br/>reads VITE_BIOLOUPE_URL"]
    end

    subgraph Usage["Usage"]
        API["API calls"]
        Auth["Auth redirects"]
    end

    ENV --> GetAPI
    ENV --> GetAuth
    GetAPI --> API
    GetAuth --> Auth

Variables:

VariablePurposeDev Default
VITE_API_URLData-gov API endpointhttp://localhost:3010
VITE_BIOLOUPE_URLBioLoupe auth redirectshttp://localhost:8080
VITE_ENABLE_PRICINGFeature flagfalse

Copy .env.example to .env and customize as needed.


TermDefinition
LoELoss of Exclusivity - when patent protection ends and generics/biosimilars enter market
1L/2L/3L+Therapy lines (first-line, second-line, third-line and beyond)
Peak ShareMaximum market share within a therapy class (always computed, never stored)

See ARCHITECTURE.md - Key Domain Concepts for full glossary.

LayerTechnologyPurpose
UIReact 19 + Radix UIComponents & accessibility
StateJotaiClient-side reactive state
Server StateTanStack QueryAPI data fetching + caching
StylingTailwind CSSUtility-first styling
ChartsChart.jsData visualization
AuthJWT CookiesShared auth with BioLoupe
APIRails BackendData persistence
FilePurpose
src/features/reports/forecasting/atoms.tsMain Jotai state orchestrator
src/features/reports/forecasting/Forecasting.tsxRoot component
src/contexts/auth-context.tsxAuthentication
src/hooks/useStatistics.tsData fetching
src/lib/api.tsAPI client
src/lib/config.tsEnvironment config