Commit Graph

85 Commits

Author SHA1 Message Date
larssand
581ec2c781 fix(series): clearer step layout, sorted class list and autosave
The series page is split into numbered steps; classes are a sorted list
with the tick on the left and All/None, new series start with none, and
every change is saved straight away so ticks are no longer lost. Also
gives primary/danger buttons white text in the light theme.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:28:05 +02:00
larssand
af79b610db feat(training): base training stats on practice, keep race laps separate
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>
2026-10-10 20:08:17 +02:00
larssand
9a9379aa83 feat(series): add series/cup championships with points tables and standings
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>
2026-10-10 19:49:23 +02:00
larssand
9d79bcb4d3 feat(layouts): optional min/max lap time per track layout for practice
Each layout can carry its own lap window (set on creation, editable in
Settings, saved from Race Day during practice). Open/Free practice on
that layout uses it; races always keep the event's values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:33:22 +02:00
larssand
8601954ea2 feat(practice): flag suspiciously short laps and remove any lap in one click
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>
2026-10-07 19:53:23 +02:00
larssand
ebd0f74f40 feat(audio): add downloadable extra Piper voices with accents
Adds Alma (sv), Alan, Northern English, Cori, Alba (British/Scottish)
and Joe, John, Norman (American) as optional voices downloaded one at
a time from Settings, with gender (measured pitch) and accent shown in
the voice lists. The standard install is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 21:58:08 +02:00
larssand
5bfb67ea02 feat(race-day): start heats with an EFRA/IFMAR count-down and flying start
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>
2026-09-30 21:27:20 +02:00
larssand
1bc75f8ec1 feat(race-day): add warm-up transponder check before a heat
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>
2026-09-30 20:47:15 +02:00
larssand
627179b95a feat(layouts): switch track layout mid-practice and view stats per layout
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>
2026-09-27 21:24:44 +02:00
larssand
196437d25b feat(audio): choose a male or female local voice per language
The installer now fetches one male and one female Piper voice per language (Swedish
NST/Lisa, English Ryan/Lessac), with genders confirmed by measuring each voice's pitch
rather than guessed from the name. Settings -> Audio gets a voice selector per language
with its own Test button that always plays the Piper voice. Male is preselected for both.

The integration test now runs its server with an empty home directory, so voices
installed on the developer's own machine can't leak into it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 20:29:59 +02:00
larssand
9016ce02e4 feat(auth): add optional admin PIN for all API writes
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>
2026-09-24 22:14:58 +02:00
larssand
922bd704eb Split the Guide page into tabs, matching the Settings/Event Manager pattern
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>
2026-09-12 19:59:15 +02:00
larssand
ba4ed30b11 Split Event Manager's race-mode page into tabs
The race-mode management page (participants, teams, race format,
live grid/standings, and now the schedule/print tools) had grown into
a long scroll. Grouped into four tabs - Participants & teams, Format,
Live & results, Schedule & print - matching the tab pattern already
used for Settings. Only wrapper divs were added; no section's markup
or event bindings moved. The existing step-card overview and warning
links now switch to the right tab before jumping to it, and Open Grid
switches to the Live tab so its target is actually visible. Track/
sponsor events, which never had this length problem, are unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 13:47:35 +02:00
larssand
cf89086dff Add a time-of-day race day schedule to Event Manager
Events can now be given a start time and a gap between sessions on a
new "Schedule" panel; every session's planned clock time cascades
automatically in running order (practice, qualifying, finals), and
once a session actually starts or finishes, everything after it
re-anchors to that real time - a late start or a session that ran
long (including any pause) pushes the rest of the day forward with
no manual reschedule step. The scheduled time now also shows on the
printed/PDF start lists.

Printable start lists and manually moving a driver to start last in
their own heat (for a mechanic-time request) already existed, so
this only builds the missing piece: the schedule itself.

Also fixes a real pre-existing bug in the same session-ordering logic
this feature relies on: getSessionSortWeight used `order[type] || 99`,
which silently treated open_practice's own weight of 0 as falsy and
sorted practice sessions dead last instead of first in print/PDF
output.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 22:33:57 +02:00
larssand
cc9f5c380b Group Settings into tabs instead of one long scroll
Splits the eleven stacked panel sections into four tabs (Connection,
Audio, Appearance & overlay, System & backup) so a specific setting
is a click away rather than a long scroll, per user feedback. Purely
a layout change - no section's markup, ids, or event bindings moved.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 19:19:34 +02:00
larssand
15b574a37d Fix Team Race duplicate-membership bug and two related team-roster gaps
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>
2026-09-10 17:07:45 +02:00
larssand
71b3451b13 Show update-available link in the sidebar, not only in Settings
The update card lived inside Settings, easy to miss on an app people
mostly leave on the Dashboard or Timing view. A small link now sits
next to the version number in the sidebar footer, on every view, and
appears automatically from the same startup update check Settings
already used - no new endpoint. Clicking it jumps straight into
Settings with the update ready to run.

Verified with a real headless-browser run against a throwaway git
clone: the link appears on its own without ever opening Settings,
names the real new version, survives navigating to another view, and
clicking it lands on Settings with the update already available there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 14:51:46 +02:00
larssand
1c4a06b69e Record completed training sessions, credit them, and add session-count goals
A driver completing the practice timer, "Jämna varv", or "Stegträning"
now gets a permanent history entry instead of the outcome only existing
as a one-off notification - visible on both their mobile profile and
the admin Training page, so progress over past sessions is actually
reviewable. Each completion earns a prominent emoji-badged credit.

The completion is verified server-side against the driver's real
recorded laps before being saved, reusing the same progress builders
live tracking already uses - the client can only ask for a completion
to be checked and recorded, never supply the outcome itself, so a
driver can't fabricate a completed session (and the reward tied to it)
any more than they could fabricate a lap time.

Both per-driver and club-wide goals can now target "complete N
sessions of one exercise kind" as an alternative to "N laps", sharing
the existing reward/claim mechanism and forms.

Fixes a real bug found while building this: a pre-existing two-argument
wrapper around getTrainingGoalProgress in app.js silently dropped the
new third argument, so every sessions-metric goal displayed 0 progress
regardless of how many sessions were actually completed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 20:43:47 +02:00
larssand
94794350e5 Add a results archive and purge orphaned passing rows on delete
Until now there was no way to find a completed session without already
knowing which event it belonged to - navigation was strictly Events ->
one event -> its sessions. A new "Resultatarkiv" view lists every
finished session across all events, searchable by event/session name,
class, driver and date range.

Opening a session reuses the existing single-session leaderboard
builder for standings and the existing per-competitor lap lookup for
lap history - no new results logic - plus a session-wide penalties/
corrections list (resultsBySession[id].adjustments), previously only
visible in the live judging action log. Export buttons reuse the
existing per-event PDF and race-package JSON export functions rather
than adding a new format.

Investigated what should happen to raw timing data when a race is
deleted. Training statistics and session replay already read from
their own independent, deliberately-retained copies that survive
deletion by existing design - unaffected either way. The one thing
that outlived deletion with no purpose was the raw `passings` SQLite
rows (write-only; nothing in the app reads them), growing forever with
driver IDs never purged. A new POST /api/passings/purge endpoint now
removes exactly the deleted session(s)' rows, called as a best-effort
side call from both delete-event and delete-session.

Also fixes a real bug found while building this: ensureSessionResult
guarded adjustments/splits/pitEvents/pitStops against being missing on
an already-existing result, but not passings itself, so viewing lap
history for such a result (e.g. an older/imported race package)
crashed outright.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 15:49:10 +02:00
larssand
be9a2de31a Explain a Backend URL/page-origin mismatch instead of a bare fetch error
A real Windows install had sync and backups fail everywhere with
"Failed to fetch" while the server itself was fully healthy - confirmed
via Invoke-WebRequest returning 200 for every endpoint the browser
couldn't reach. The page was opened via a LAN IP while Settings still
had the Backend URL set to 127.0.0.1 from an earlier session; browsers
treat those as different origins, so the server's CORS check correctly
rejected the requests, but a CORS rejection surfaces to fetch() as an
unexplained generic error with no indication of the real cause.

Settings now detects this mismatch on every render and shows the page's
actual address with a one-click button to fix it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 12:06:58 +02:00
larssand
01e1165748 lägg till stegträning-övning + v1.3.0
Andra guidade träningsövningen i Open Practice: konfigurera en
stegserie (antal varv + lugnt/kontrollerat/snabbt per steg),
förvalt till klassiska pyramiden. Training-sidan visar aktuellt
steg, kvarvarande varv och en progress-pill per steg. När
sekvensen är klar jämförs de två kontrollerade stegens snitt
neutralt (t.ex. "+0.90s"), utan att kalla en försämring för
trötthet.

syncTrainingLedger sprider nu resten av det normaliserade
training-objektet istället för att lista fält manuellt, så
samma klass av bugg som drabbade Jämna varv inte kan upprepas
för framtida fält.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 09:02:44 +02:00
larssand
9122e91f2e lägg till jämna varv-övning + v1.2.0
Ny guidad träningsövning i Open Practice: sätt måltid, tolerans
och antal varv, se aktuell serie och dagens bästa live på
Training-sidan. Ett missat varv startar en ny serie utan att
radera bästa resultatet.

Hittade och fixade två buggar under vägen:
- syncTrainingLedger byggde om training-state vid varje sparning
  utan att föra vidare det nya evenLaps-fältet, så sparade
  konfigurationer försvann direkt
- Training-sidan kraschade för alla förare med färre än 3 varv i
  varje stint (t.ex. de första varven i ett pass) eftersom en
  dedupe-funktion läste en egenskap på ett null-värde

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 08:51:50 +02:00
larssand
a0c3d3039e Separat status och mottagningsräknare för varje kanal.
Senaste råmeddelande, fel och ignoreringsorsaker.
Pausa visning, rensa och kopiera logg.
Uppdateras utan att störa inställningsfält eller tidtagning.
2026-09-08 18:37:47 +02:00
larssand
8aedf83826 fix all overlays 2026-09-08 18:26:30 +02:00
larssand
d0889736d0 fix overlay list fields 2026-09-08 17:36:49 +02:00
larssand
443434d569 fix status and roadmap 2026-09-08 17:26:55 +02:00
larssand
9e6fe2386d fix sort and obs panels 2026-09-08 13:21:29 +02:00
larssand
6e91f921c2 split obs overlay i vänster/höger
Race-namn, logga och klocka till vänster, förarstats-tabellen
för sig till höger. Samma inställningar (rows/showClock/
showFastest/showLaps/showResult/showBest/showGap) styr
fortfarande innehållet, bara positioneringen är ändrad
2026-09-07 17:56:26 +02:00
larssand
a2e29b89dd add sorting 2026-09-05 13:50:11 +02:00
larssand
9c74c1f256 add version 2026-09-05 13:39:50 +02:00
larssand
78c650bde7 raceday kontroller 2026-09-05 09:59:55 +02:00
larssand
4068cc7c19 add more stats to mobile profile 2026-09-04 16:17:45 +02:00
larssand
142c45be04 add replay session 2026-09-04 15:58:24 +02:00
larssand
2d56b74731 Add local and public QR sharing for training profiles 2026-09-03 20:15:13 +02:00
larssand
483a9dbd5a Fix lap limits and five-minute stint statistics 2026-09-02 19:36:25 +02:00
larssand
f561dd8feb new raceday and backup 2026-09-01 07:07:23 +02:00
larssand
5ed193cdeb fix import av csv 2026-08-30 16:14:45 +02:00
larssand
fdc99e64bb fix style color 2026-08-30 15:10:00 +02:00
larssand
694aba5f3d Add motivational training challenges and shared club goals 2026-08-30 09:16:11 +02:00
larssand
e5e1754185 add more train stats per driver 2026-08-29 09:56:45 +02:00
larssand
375bfb2416 förbättreing tränings 2026-08-28 21:44:23 +02:00
larssand
3cf68cf34a branding och namechange 2026-08-28 20:56:58 +02:00
larssand
5f79228941 Add configurable manifest-driven race audio system 2026-08-28 13:29:27 +02:00
larssand
a87c64a47c Improve decoder setup UI and add configurable training timers 2026-08-27 17:29:44 +02:00
larssand
8ea8b3a84e Add multi-decoder split and pit timing support 2026-08-25 19:45:40 +02:00
larssand
3aeb77a042 add stat 2026-08-24 21:28:38 +02:00
larssand
355a1ad3b0 Förbättrat ljudsystemet 2026-08-24 19:40:39 +02:00
larssand
2e6866a013 Add simplified Open Practice flow and reorganize 2026-08-24 19:25:21 +02:00
larssand
40d0c3fa90 förenklat träningsdelen 2026-08-24 17:16:07 +02:00
larssand
4a74785791 Add secure public driver training profiles 2026-08-24 16:54:35 +02:00