Skip to content

Student chaos page

The student chaos page (chaos-admin/public/index.html) is the interface students use to activate chaos themselves and track their pedagogical journey. It is served by the perfshop-chaos-admin container but exposed at a dedicated URL (PUBLIC_CHAOS_URL, by default port 3003). Unlike the instructor panel, it requires no authentication: a student accesses it freely from their workstation.

Sources

chaos-admin/public/index.html, chaos-admin/public/student/student-main.js, student-tabs.js, student-performance.js, student-pedagogique.js, student-scenarios.js

Architecture

The student page is a vanilla HTML/CSS/JS application — no framework, no bundler. Scripts are loaded via ordered <script> tags in index.html and communicate through protected global variables (typeof X === 'undefined').

public/
├── index.html               ← main page, 6 tabs
├── js/i18n.js               ← async i18n loader (shared admin + student)
├── i18n/{fr,en}.json        ← dictionaries
└── student/
    ├── student.css
    ├── student-tabs.css
    ├── student-performance.css
    ├── student-main.js       ← bootstrap, polling, classic chaos
    ├── student-tabs.js       ← standalone `Tabs` module (init, switchTo)
    ├── student-performance.js ← Performance tab (20 scenarios)
    ├── student-scenarios.js  ← weather scenario metadata
    └── student-pedagogique.js ← Pedagogical tab (BAC1-BAC5 slider)

Scripts are loaded in this strict order from index.html:

<script src="/config.js"></script>
<script src="/js/i18n.js"></script>
<script src="/student/student-scenarios.js"></script>
<script src="/student/student-tabs.js"></script>
<script src="/student/student-performance.js"></script>
<script src="/student/student-pedagogique.js"></script>
<script src="/student/student-main.js"></script>

config.js exposes window.__CONFIG__ (API_URL, LANG, etc.) injected by the container at startup. i18n.js exposes window._i18nReady (a Promise resolved when the JSON dictionary is loaded). student-main.js waits for window._i18nReady.then(...) before initializing anything.

The six tabs

The header of the student page displays six tab buttons, managed by the Tabs module (in student-tabs.js). Only one tab is active at a time; the selection is purely client-side, without reload.

flowchart LR
  subgraph Tabs[Tab bar]
    T1[⚡ Performance]
    T2[📜 Scripting]
    T3[🏪 Business]
    T4[⚙️ Functional]
    T5[🛡️ Security]
    T6[🎓 Pedagogical]
  end
  T1 -.-> S1[section-performance]
  T2 -.-> S2[section-scripting]
  T3 -.-> S3[section-business]
  T4 -.-> S4[section-functional]
  T5 -.-> S5[section-security]
  T6 -.-> S6[section-pedagogy]

Performance tab

This is the only tab with rich content displayed directly. It presents up to 20 weather scenarios (N1-01 to N4-05) dynamically generated from the backend API. The scenarios are rendered as clickable cards in a grid (perf-grid). A single perf-active-bar banner shows the currently active scenario (at most one at a time — this is a functional requirement of student mode).

The student-performance.js module retrieves the list via the /api/chaos/student/performance/scenarios endpoint, builds the cards while respecting the license level (N3-N4 scenarios are locked without a license), and displays them with their translated name (taken from the backend i18n dictionary via the scenario.<id>.name property).

When the student clicks on a card, the module sends POST /api/chaos/student/performance/scenario with the scenario id. The backend applies the preset (combination of several simultaneous chaos types) then responds with a new status that the 15-second polling will update on screen.

Scripting, Business, Functional, Security tabs

These four tabs share the same visual component — the classic-card — which presents:

  • A family label and a title
  • A state badge (Disabled, Junior, Confirmed, Expert, Maestro or N1N4)
  • A 5-position <input type="range"> slider (0 to 4)
  • A text status and a "license lock" message (.lock-msg)

Chaos Scripting is the only one that has a freemium level 1 accessible without a license. The other three (Business, Functional, Security) are entirely locked as long as no valid license is active: their slider is disabled and the card carries the CSS class locked. See Freemium vs Pro for the details of the limits.

Pedagogical tab

The Pedagogical tab displays a dedicated card with a 6-position slider: Disabled, Bac+1, Bac+2, Bac+3, Bac+4, Bac+5. The student-pedagogique.js module (exposed via the PedagogyTab namespace) handles all the logic:

  • PedagogyTab.initSlider() — initializes the slider events
  • PedagogyTab.applyStatus(ped, licensed, studentMode) — updates the display from the /status payload

The access rule is as follows: BAC1 and BAC2 levels are free (freemium), BAC3 to BAC5 levels require a license. The slider is also entirely locked when studentMode === false — i.e., when the instructor has disabled the student page from their admin panel.

Choosing a level does not start the journey for the student themselves: the slider configures the default level that the e-commerce frontend will see if the student navigates to it afterwards. The student then switches to the shop and starts the adventure via the welcome overlay.

Polling cycle and global state

Every 15 seconds, student-main.js calls GET /api/chaos/student/status with the X-Student-Token header if it is present in localStorage:

async function loadStatus() {
  const headers = {};
  const pedToken = localStorage.getItem('ped_student_token');
  if (pedToken) headers['X-Student-Token'] = pedToken;
  const r = await fetch(`${API}/api/chaos/student/status`, { headers });
  if (!r.ok) return;
  applyStatus(await r.json());
}

The reason for the presence of X-Student-Token in this call is subtle: without this header, the server responds needsJoin:true on every poll, which would cause a flood of /join calls. With the token, the server recognizes the session and directly returns the state.

The status payload contains the following keys:

Key Content
licensed Boolean — is a valid license active?
studentModeEnabled Boolean — has the instructor enabled the student page?
license License object: valid, plan, planLabel, holder, etc.
performance State of the active scenario + list of available scenarios
scripting, business, functional, security { level, maxLevel }
pedagogique State of the pedagogical journey (level, active session)

The applyStatus() function updates:

  • The "blocked" banner (#blocked-banner) if studentModeEnabled === false
  • The license status bar (#lic-status-bar)
  • The Performance tab via PerfTab.applyStatus(data.performance, _licensed)
  • The 4 classic cards via _updateClassicCard(key, level, maxLevel)
  • The Pedagogical tab via PedagogyTab.applyStatus(data.pedagogique, _licensed, studentMode)

User actions

When the student moves a classic slider, a 400 ms debounce delays the send:

_debounce[key] = setTimeout(() => _applyClassic(key, level), 400);

Then _applyClassic(key, level) sends POST /api/chaos/student/{scripting|business|functional|security} with the body { level }. The following HTTP codes are explicitly handled:

Code Meaning UI action
200 Success Confirmation toast, reload the status
402 Missing license Toast 🔒 License required, reload
403 Student mode disabled Toast 🔒 Session not started, reload
other Server error Toast ❌ {error}, reload

An identical mechanism exists for the Performance and Pedagogical cards, in their respective modules.

License activation from the student page

A "🔑 Activate a license" button in the header opens a banner that contains a <textarea> and an "Activate" button. The activateLicence() function sends POST /api/license/activate with the entered key:

  1. Format validation: the key must start with PFSH-
  2. fetch to the public API (this endpoint is intentionally public, see License system)
  3. On success: confirmation toast, reload the status after 1.8 seconds, hide the banner
  4. On failure: inline error message

This activation entry from the student page is practical in a classroom: the instructor gives a key to a test student, who activates it from their workstation, and all other students immediately benefit from the unlocked levels (the license is shared at the server level).

Blocked student mode

The instructor can at any time block the student page from their panel via POST /api/chaos/student/admin/mode { enabled: false }. When this is the case, all subsequent polls return studentModeEnabled: false, the student page hides the license bar and displays instead the #blocked-banner:

🔒 Session not started — waiting for the instructor The instructor has not yet opened the session. Controls will be available as soon as the session starts.

No slider is usable in this state. See Chaos admin (instructor) for the reverse trigger on the instructor side.