Add moving a driver between qualifying heats
The user asked whether a lone driver left in the last heat (e.g. 7
drivers at 3 per heat) could be moved into another heat manually.
Investigated first: neither "Reseed" (redistributes every not-yet-run
heat by current standings, not a targeted move) nor the grid editor
(reorders start position within one heat, never moves a driver
between heats) could do this - there was genuinely no path to it.
moveDriversIntoQualifyingHeat (src/event_race_logic.js) adds the same
"checking them elsewhere is the move" pattern already used for team
rosters: a new driver checklist on the qualification session edit
form pulls a checked driver out of whichever other heat they're
currently on, scoped to the same round only ("Q2 H2" never touches
"Q1"/"Q3", since heat composition can legitimately differ round to
round after a reseed). The checklist names the driver's current heat
before saving, mirroring the team-roster warning.
Also confirmed, per the user's earlier questions, already correct
with no changes needed: practice's per-driver "best 3 laps"/"best
5-minute result" stats, and that heats are already seeded fastest-
first from practice results. Flagged one real, deliberately unbuilt
gap while investigating: a true per-driver rolling "5 minutes from
your own first crossing" qualifying *heat* clock isn't supported -
only the practice stats work that way today.
Verified with new unit tests for moveDriversIntoQualifyingHeat and
the extracted parseQualifyingHeatName helper, and a real headless-
browser run reproducing the exact reported scenario end to end
through the actual UI.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,7 @@ export function renderEventManagerMarkup(context) {
|
||||
raceDrivers,
|
||||
teamDriverPool,
|
||||
showTeamsSection,
|
||||
parseQualifyingHeatName,
|
||||
state,
|
||||
getDriverDisplayById,
|
||||
raceFormatAdvanced,
|
||||
@@ -54,6 +55,21 @@ export function renderEventManagerMarkup(context) {
|
||||
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 || "";
|
||||
// Same idea as driverTeamElsewhere above, but for qualifying heats: checking a driver
|
||||
// onto this heat moves them out of whichever other heat in the SAME round they're
|
||||
// currently on (see moveDriversIntoQualifyingHeat) - name that heat so the admin sees the
|
||||
// move coming before saving, not after.
|
||||
const driverHeatElsewhere = (driverId, targetSession) => {
|
||||
const targetParsed = parseQualifyingHeatName(targetSession?.name);
|
||||
if (!targetParsed) return "";
|
||||
const other = sessions.find((session) =>
|
||||
session.id !== targetSession.id
|
||||
&& session.type === "qualification"
|
||||
&& (session.driverIds || []).includes(driverId)
|
||||
&& parseQualifyingHeatName(session.name)?.round === targetParsed.round
|
||||
);
|
||||
return other?.name || "";
|
||||
};
|
||||
const sessionChannelMarkup = sessions
|
||||
.map((session) => ({ session, markup: renderSessionChannelAnalysis(session) }))
|
||||
.filter((entry) => entry.markup)
|
||||
@@ -864,6 +880,29 @@ export function renderEventManagerMarkup(context) {
|
||||
<input name="maxCars" type="number" min="1" value="${editingSession.maxCars || ""}" />
|
||||
<small>${t("events.session_max_cars_hint")}</small>
|
||||
</label>
|
||||
${
|
||||
event.mode === "race" && editingSession.type === "qualification"
|
||||
? `
|
||||
<div class="span-5">
|
||||
<h4>${t("events.heat_drivers")}</h4>
|
||||
<p class="hint">${t("events.heat_drivers_hint")}</p>
|
||||
<div class="check-grid">
|
||||
${raceDrivers
|
||||
.map((driver) => {
|
||||
const elsewhere = driverHeatElsewhere(driver.id, editingSession);
|
||||
return `
|
||||
<label class="check-card">
|
||||
<input type="checkbox" name="heatDriverIds" value="${driver.id}" ${(editingSession.driverIds || []).includes(driver.id) ? "checked" : ""} />
|
||||
<span>${escapeHtml(driver.name)}${driver.transponder ? ` (${escapeHtml(driver.transponder)})` : ""}${elsewhere ? `<small>${escapeHtml(t("events.heat_member_elsewhere", { heat: elsewhere }))}</small>` : ""}</span>
|
||||
</label>
|
||||
`;
|
||||
})
|
||||
.join("")}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
<p class="form-error" id="sessionEditError" hidden></p>
|
||||
<div class="actions-inline">
|
||||
<button class="btn btn-primary" type="submit">${t("common.save")}</button>
|
||||
|
||||
Reference in New Issue
Block a user