Skip to main content

Architecture

Analysis tab architecture and calculation flow

Analysis tab architecture and calculation flow

Section Architecture
Updated 03.06.2026

Overview#

The Analysis tab is the developer-facing inspection layer for a selected model. It combines:

  • model summary information
  • related model tables (Inner Models, Previous Models, Cluster Models)
  • the Archive Parameters panel with time-depth, approach, direct targets, overcome state, reverse targets, and cancellation state
  • legacy forecast toggles that still live outside the React state tree

The current implementation is not a pure PHP template and not a pure standalone SPA. It is a hybrid runtime where React renders the visible UI, while the legacy page still provides the surrounding accordion shell, hidden bridge elements, chart integration, and toggle bindings.

Primary source files#

  • resources/react/init.jsx - React host, Analysis accordion item, JSON fetch, related-model tables, archive parameter cards
  • resources/css/react-ui.css - visual styling for the React Analysis section
  • resources/js/main.js - legacy DOM bridge that writes the selected model_id into the hidden host inside #collapseAnalysis
  • templates/private/analysis_ajax.php - JSON/HTML backend endpoint used by the tab
  • inc_calc_controls.php - legacy business logic and constants that define the original control/aim semantics

Runtime architecture#

React host#

The visible tab content is rendered by AnalysisAccordionItem in resources/react/init.jsx.

It does not own model selection directly. Instead, it observes a hidden DOM node:

<span id="modelId" class="visually-hidden">0</span>

When that node changes, React starts a new request to:

templates/private/analysis_ajax.php?model_id=...&format=json

Legacy to React bridge#

The legacy layer in resources/js/main.js remains responsible for pushing the selected model id into the hidden bridge element inside #collapseAnalysis.

The key helper is:

mpSetAnalysisModelIdState(modelId)

Important detail: the bridge payload is intentionally numeric only. The React host treats the node as a pure model_id state carrier and no longer expects formatted text there.

Server endpoint#

templates/private/analysis_ajax.php is the data backend for the tab.

In modern runtime it mainly serves JSON, but it still keeps compatibility with legacy HTML output paths. When working on the tab, the JSON path is the primary production path.

The endpoint now has a stricter split of responsibilities:

  • the JSON branch keeps raw API payload semantics for React
  • the legacy HTML branch applies local escaping for DB-backed text output without altering the JSON contract

Request flow#

  1. The user selects or opens a model in the legacy UI.
  2. main.js calls mpSetAnalysisModelIdState(modelId).
  3. The hidden #collapseAnalysis #modelId node changes.
  4. The React MutationObserver in AnalysisAccordionItem detects the change.
  5. React aborts any stale in-flight request, keeps the latest pending model_id, and fetches a fresh JSON payload from analysis_ajax.php.
  6. The payload is rendered into model summary, related-model tables, and archive parameter cards.
  7. After render, React re-runs legacy forecast toggle synchronizers so the old checkbox logic keeps working on the new host nodes.

JSON contract returned to React#

The endpoint returns a payload with these top-level parts:

  • model_summary - selected model metadata shown above the tables
  • sections.inner - inner models
  • sections.previous - previous models
  • sections.cluster - cluster-related models
  • archive - normalized archive parameter structure consumed by AnalysisArchiveSection

The archive block is already normalized for the UI: each item carries a display name, description, threshold, numeric progress, and status label. React only renders it and does not recompute archive math on the client.

Archive Parameters pipeline#

Entry point#

The backend entry point is:

analyzeArchiveParameters($modelId, $db, $db_prefix, $model, $branches, $paramsTableExists)

This function prepares the visible parameter set according to the model type (AM, DBM, EM, EAM, AM/DBM) and then calculates the progress for each parameter.

Visible item groups#

All models start from a shared base set:

  • TIME_DEPTH
  • APPROACH_LEVEL
  • AIM1 to AIM4

Additional items depend on the model family:

  • EM/EAM add OVERCOME_LEVEL, REVSTOP_LEVEL, and optionally AIM5 to AIM8
  • AM/AM/DBM add LOST_AM
  • DBM adds REACHED

Unified history engine#

The current target logic is centered around:

buildUnifiedAnalysisAimHistory(array $model, array $params, array $controls, array $levels, $db, $db_prefix)

This helper scans chart history in model-relative phases and acts as the single visible source of truth for:

  • whether approach was reached
  • how much direct progress AIM1-AIM4 accumulated
  • whether breakdown/overcome was reached
  • how much reverse progress AIM5-AIM8 accumulated
  • whether reverse stop invalidated further breakdown-target rendering

This is the key reason the tab now behaves consistently: the visible cards and visible aim bars no longer depend on separate ad-hoc formulas.

CP and size resolution#

Before the history scan can work, the server must resolve CP_level, CP_bar, and model size.

The main helper is:

resolveAnalysisCpContext($model, $params, $controls, $levels, $db, $db_prefix)

Resolution order is intentionally aligned with real stored data:

  1. try calcP6/calcP6t
  2. if unavailable, restore CP/breakdown context from stored levels such as lvl_am, lvl_brkd, P6, or SL

This is an example of a justified storage-based fallback. It exists because different generations of models persist equivalent control data in different places.

Time Depth alignment#

Time Depth is no longer calculated from an isolated legacy-only path.

The current implementation of calculateTimeDepthProgress() first tries to reuse the unified history snapshot and, if needed, restores the same core inputs through the shared helpers:

  • resolveAnalysisCpContext()
  • resolveAnalysisLifecycleEndAbs()
  • archived size_time or reconstructed model time points

This keeps TIME_DEPTH aligned with the same recovered CP_bar, lifecycle-end boundary, and model-size semantics that now drive Approach, Overcome, and visible target progress.

Parameter calculators#

calculateParameterProgress() dispatches the visible archive items to dedicated calculators:

  • calculateTimeDepthProgress()
  • calculateApproachProgress()
  • calculateOvercomeProgress()
  • calculateRevStopProgress()
  • calculateLostAMProgress()
  • calculateReachedProgress()
  • calculateAimProgress()

The important design rule is that visible target bars are fed from the unified history engine, while type-specific scalar items may still use narrower calculators where that remains semantically correct.

AIM normalization rules#

After raw calculation, the server normalizes target groups so that a farther target cannot be more complete than an earlier one.

Examples:

  • AIM3 cannot exceed AIM2
  • AIM4 cannot exceed AIM3
  • AIM6 cannot exceed AIM5, and so on

Archived models may also force the final archived aim to 100% when the final persisted state clearly proves that exact target was reached.

How React renders the archive panel#

AnalysisArchiveSection does not know business rules. It only renders normalized server data.

  • archive.items - generic parameter cards
  • archive.approach + archive.direct_targets - direct phase card and AIM1-AIM4
  • archive.overcome + archive.breakdown_targets - reverse phase card and AIM5-AIM8
  • archive.revstop - substatus inside the overcome group

The progress bar component AnalysisProgressBar is deliberately dumb: it receives a numeric value and only clamps/render-labels it.

The temporary runtime debug block that used to expose internal snapshots has been removed. The production panel now renders only the normalized working payload returned by the backend.

Forecast toggle host#

The block with forecast toggles is intentionally mounted into a stable DOM host:

<div id="analysisForecastTogglesHome">...</div>

This is important because main.js still binds legacy change listeners directly to these checkbox nodes. If the host is remounted during normal Analysis refreshes, the legacy listeners become stale.

Safe extension rules#

  • Do not change only the legacy HTML branch in analysis_ajax.php and assume the visible tab changed. The production UI uses the React JSON path.
  • Do not apply HTML escaping to the JSON branch. Keep output hardening local to the legacy HTML rendering path.
  • When changing visible labels or panel structure, keep all interface text in English.
  • When changing archive math, start from the server side and keep React presentational.
  • Preserve the hidden #collapseAnalysis #modelId bridge unless you are intentionally redesigning the whole legacy/React integration.
  • Preserve the stable forecast toggle host unless you also rewrite the legacy checkbox binding model in main.js.
  • After editing React or CSS, rebuild the bundle with npm run build so the changes reach public/app.min.js and public/app.css.

Debugging checklist#

  1. If the wrong model appears in Analysis, inspect the hidden bridge flow in main.js and the React observer in AnalysisAccordionItem.
  2. If related-model tables are wrong, inspect branch loading in fetchAnalysisBranches() and id extraction for IDinners, IDprevs, and clusterParams.
  3. If archive progress is wrong, inspect resolveAnalysisCpContext(), buildUnifiedAnalysisAimHistory(), resolveAnalysisLifecycleEndAbs(), and the final normalization inside analyzeArchiveParameters().
  4. If specifically Time Depth diverges from Approach/AIM, check whether the runtime reached the unified-history path or the shared CP/lifecycle recovery path inside calculateTimeDepthProgress().
  5. If browser and CLI disagree, verify broker/database context first. The project uses a multibroker runtime and the same model_id can point to different data in another context.
  6. If React changes appear not to work, confirm the bundle was rebuilt and the browser is not serving an old public/app.min.js.

Practical local commands#

Typical local actions while working on the tab:

npm run build
php -l templates/private/analysis_ajax.php

Continue Reading

Related Articles