Training stats count Open/Free practice by default; laps from
qualifying, heats and finals are kept and shown under Based on: Races
or All, marked with race, session and series. Races are listed per race
session and never split into stints. Same picker in the mobile profile.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Group any number of races in freely chosen classes into a series with a
points table (ÖRC/100-80-70/F1/custom), best-N or drop-X counting, a
club filter that re-ranks members without guests, tie-breaks, rounds
created from a date list, and per-class standings with print and PDF.
Also fixes PDF export of wide tables: they are compacted and switched
to landscape instead of truncating names and dropping columns.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds a Test pronunciation button (forms and list) that plays the
passing sound and real announcements with the spoken name through race
audio. Fixes the Drivers/Cars search losing focus after one letter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Race sessions: Timing driver details become read-only with an Open in
Judging button; penalties and lap changes happen only in Judging.
Practice: no penalties anywhere, per-lap clean-up stays on Timing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sidebar update notice now lands on System & backup at Updates, and
Race Day's decoder/backend/audio fix buttons open the matching tab,
instead of whichever tab was used last.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Practice laps under 85% of the driver's own median (a likely shortcut)
are flagged: counted, but never a best lap, record or training stat
until approved. Timing and Race Day get a one-click Approve/Remove
panel, overlays show a warning mark, and any lap can be removed or
restored from Timing and Judging with undo.
Also sends judge corrections straight to the server so its hot-results
protection can no longer hold back a correction made right after a
passing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resetting a running Open Practice deleted its results, the Timing view
recreated an empty result at once, and the next save rebuilt that
session's training laps from nothing - wiping every earlier lap from
the drivers' stats. Reset now syncs first and records resultsResetAt;
laps from before it are kept.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Race Day Start now runs a count-down (Go, 30 s - 5 min, default per
event) with calls every minute, 30 s and 10 s, grid call and 9..3
count for grid starts, hold/resume/+1 min/to 30 s/to 10 s/Go now, and
starts the heat by itself when it runs out. The transponder check runs
during the count-down, replacing the separate warm-up.
Adds a flying start mode (horn + clock is running, own clock per car)
as the qualifying default, and the latest personal clock start rules
from EFRA 9.3 / IFMAR 4.2.3 for flying and staggered starts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Race Day gets a 1-3 minute warm-up per heat with a green check per
driver when their transponder is seen, countdown announcements
(sv/en), quick transponder fix from the panel, and a start warning
that still allows starting when someone is missing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A club can run one Open Practice all season and re-lay the track now and then.
Previously a session's layout was frozen at start and every training lap inherited
it, so a season-long practice stayed on its first layout forever, and changing layout
was hidden under Settings -> Appearance.
- Each practice lap now counts toward the layout active when it was driven; a running
practice session moves onto a new layout when one is added
- "New layout" strip on Training stats, Race Day and the Dashboard, offered only around
practice; a running race session blocks layout changes everywhere
- "Stats for" selector (current / older layout / before layouts / all layouts) on
Training stats and in the driver's mobile profile (?layout= on the public API)
- A layout that has practice laps on it can no longer be deleted
- Fix: the buildDriverTrainingStats wrapper in app.js dropped extra arguments
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Announcements can use Piper, a free local neural TTS, instead of the browser's system
voice. No account, no paid service, and it keeps working with no internet at the track.
- npm run tts:install (or Settings -> Audio -> Install) downloads the standalone Piper
release for Linux/Windows/macOS plus Swedish and English voices into piper/ once
- server/tts.js generates each line once, one at a time, and caches it as WAV under
data/tts/; a new line takes ~0.3 s with the standalone build
- src/piper_speech.js plays it and falls back to the browser voice if Piper is missing,
errors, or takes over 2.5 s, so no announcement is lost and timing is never held up
- "Pre-generate phrases and driver names" renders every fixed phrase and name in both
languages before race day
Browser voice stays the default. Generation routes fall under the admin PIN.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Off by default. Once a PIN is set in Settings -> System & backup, every non-GET /api/*
request (plus GET /api/ammc/config) needs a signed 30-day HttpOnly cookie, which also
closes the unauthenticated /api/update/run restart trigger. A 401 opens a PIN dialog
and the pending save is retried after login. Reads stay open because the public overlay
hydrates through GET /api/state. Optional RC_ADMIN_ALLOWED_CIDRS restricts the same
routes by client address.
The PIN hash and signing secret live in data/, outside the app_state blob, so they
never leak through GET /api/state, exports or backups.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The page had grown to ~20 stacked panels on one very long scroll - the same problem
already fixed twice this session for Settings and Event Manager. Grouped into 5 tabs
(Getting started, Create a race, Run a race, Training & results, Video guides) using
the same activeGuideTab-as-module-state approach so the selection survives an in-place
re-render (e.g. a language switch). The always-visible overview cards at the top are
unchanged.
Also investigated a "feels slow" concern raised alongside this: measured the page's
actual render cost in a real browser (median 0.6ms, on par with or faster than Settings/
Timing) and confirmed the periodic tickClock/updateSyncStatus intervals never re-render
an idle Guide page. No performance issue found - the perceived slowness was the page's
length, which this change addresses directly.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
These three UIs (all shipped earlier this session) had only been verified with throwaway
scratch scripts during development. Adds tests/browser_settings_tabs.js,
browser_event_manager_tabs.js and browser_event_schedule.js, wired into the existing
test:browser suite.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Reported by the user: creating a team, adding the wrong driver, then
moving that driver to another team by ticking them on the new team's
edit form left the driver on both teams at once. Root cause: there
was never a "move" action - the edit form only ever added a driver to
the team being saved, never removed them from wherever they already
were. Live laps from a driver on two teams were then silently
credited to only the first-matching team, with no clear error.
reassignTeamMembers (event_race_logic.js) now strips a driver/car
being saved onto one team out of every other team in the same event,
and the team checkbox list shows which team a driver is already on
before you move them.
Also fixed two related gaps found while investigating, at the user's
request to fix everything: getTeamDriverPool had regressed to
ignoring the event's class/participant scoping entirely (showing
every driver in the club), and deleting a driver or car left them
behind in team rosters forever. Added a duplicate_team_members Race
Day preflight check as defense in depth for any already-corrupted
saved state.
Verified with new unit tests for reassignTeamMembers, getTeamDriverPool
and the new preflight check, plus a new permanent browser test
reproducing the user's exact reported scenario end to end.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Unlike Open Practice, these session types run the real countdown/light/
audio sequence before a session transitions to "running" - this was
untested beyond Open Practice's instant-start shortcut. Confirms the
Start button shows a real ticking countdown, the right audio events
fire per type (qualifying_start only for qualification), and pause/
resume/finish behave the same as they already do for Open Practice.
All audio effects/voice are silenced via raceAudio.events so the test
depends only on the sequence's own timers, not on real audio/TTS
playback completing in a headless browser with no sound hardware.
Also logs a new roadmap item: indoor clubs that reconfigure the track
layout in the same venue have no way to tell the app a layout change
happened, so PBs/track records/pace baselines silently keep comparing
lap times across incomparable layouts. No design decided yet - open
questions recorded for a future dedicated conversation.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Nytt browser-test för start/paus/fortsätt/finish och byte till
nästa heat i kön, mot en simulerad decoder-socket (ingen riktig
AMMC-hårdvara). Hittade och fixade två buggar under vägen:
- Open Practice på sponsor/track-event kunde inte startas trots
grönt ljus i förkontrollen (separat, striktare kontroll saknade
samma undantag)
- Race Day-vyn ritades aldrig om när decodern anslöt/kopplades ner
Testramverket hanterar nu även native alert()/confirm()-dialoger
automatiskt, annars fryser en headless körning helt.