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>
This commit is contained in:
larssand
2026-09-10 17:07:45 +02:00
parent 71b3451b13
commit 15b574a37d
16 changed files with 261 additions and 15 deletions

View File

@@ -47,6 +47,13 @@ export function renderEventManagerMarkup(context) {
getModeLabel,
renderTable,
} = context;
// Checking a driver/car here moves them off whichever other team already has them (see
// reassignTeamMembers in event_race_logic.js) - name that team here so the admin sees
// the move coming rather than discovering it after saving.
const driverTeamElsewhere = (driverId, excludeTeamId = "") =>
raceTeams.find((team) => team.id !== excludeTeamId && team.driverIds.includes(driverId))?.name || "";
const carTeamElsewhere = (carId, excludeTeamId = "") =>
raceTeams.find((team) => team.id !== excludeTeamId && team.carIds.includes(carId))?.name || "";
const sessionChannelMarkup = sessions
.map((session) => ({ session, markup: renderSessionChannelAnalysis(session) }))
.filter((entry) => entry.markup)
@@ -324,7 +331,7 @@ export function renderEventManagerMarkup(context) {
(driver) => `
<label class="check-card">
<input type="checkbox" name="teamDriverIds" form="teamForm" value="${driver.id}" />
<span>${escapeHtml(driver.name)}${driver.transponder ? ` (${escapeHtml(driver.transponder)})` : ""}</span>
<span>${escapeHtml(driver.name)}${driver.transponder ? ` (${escapeHtml(driver.transponder)})` : ""}${driverTeamElsewhere(driver.id) ? `<small>${escapeHtml(t("events.team_member_elsewhere", { team: driverTeamElsewhere(driver.id) }))}</small>` : ""}</span>
</label>
`
)
@@ -344,7 +351,7 @@ export function renderEventManagerMarkup(context) {
(car) => `
<label class="check-card">
<input type="checkbox" name="teamCarIds" form="teamForm" value="${car.id}" />
<span>${escapeHtml(car.name)} (${escapeHtml(car.transponder || "-")})</span>
<span>${escapeHtml(car.name)} (${escapeHtml(car.transponder || "-")})${carTeamElsewhere(car.id) ? `<small>${escapeHtml(t("events.team_member_elsewhere", { team: carTeamElsewhere(car.id) }))}</small>` : ""}</span>
</label>
`
)
@@ -757,7 +764,7 @@ export function renderEventManagerMarkup(context) {
(driver) => `
<label class="check-card">
<input type="checkbox" name="teamDriverIds" value="${driver.id}" ${editingTeam.driverIds.includes(driver.id) ? "checked" : ""} />
<span>${escapeHtml(driver.name)}${driver.transponder ? ` (${escapeHtml(driver.transponder)})` : ""}</span>
<span>${escapeHtml(driver.name)}${driver.transponder ? ` (${escapeHtml(driver.transponder)})` : ""}${driverTeamElsewhere(driver.id, editingTeam.id) ? `<small>${escapeHtml(t("events.team_member_elsewhere", { team: driverTeamElsewhere(driver.id, editingTeam.id) }))}</small>` : ""}</span>
</label>
`
)
@@ -772,7 +779,7 @@ export function renderEventManagerMarkup(context) {
(car) => `
<label class="check-card">
<input type="checkbox" name="teamCarIds" value="${car.id}" ${editingTeam.carIds.includes(car.id) ? "checked" : ""} />
<span>${escapeHtml(car.name)} (${escapeHtml(car.transponder || "-")})</span>
<span>${escapeHtml(car.name)} (${escapeHtml(car.transponder || "-")})${carTeamElsewhere(car.id, editingTeam.id) ? `<small>${escapeHtml(t("events.team_member_elsewhere", { team: carTeamElsewhere(car.id, editingTeam.id) }))}</small>` : ""}</span>
</label>
`
)