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>
This commit is contained in:
@@ -1,9 +1,21 @@
|
||||
function formatDateTimeLocalValue(ms) {
|
||||
const date = new Date(ms);
|
||||
date.setSeconds(0, 0);
|
||||
date.setMinutes(date.getMinutes() - date.getTimezoneOffset());
|
||||
return date.toISOString().slice(0, 16);
|
||||
}
|
||||
|
||||
function formatClockTime(ms) {
|
||||
return new Date(ms).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: false });
|
||||
}
|
||||
|
||||
export function renderEventManagerMarkup(context) {
|
||||
const {
|
||||
event,
|
||||
t,
|
||||
escapeHtml,
|
||||
sessions,
|
||||
eventSchedule,
|
||||
sessionTypeChoices,
|
||||
sessionTypeHintKey,
|
||||
raceTeams,
|
||||
@@ -742,6 +754,39 @@ export function renderEventManagerMarkup(context) {
|
||||
</section>
|
||||
` : ""}
|
||||
|
||||
<section class="panel mt-16">
|
||||
<div class="panel-header"><h3>${t("events.schedule_title")}</h3></div>
|
||||
<div class="panel-body">
|
||||
<p class="hint">${t("events.schedule_hint")}</p>
|
||||
<form id="scheduleForm" class="form-grid cols-3">
|
||||
<label>${t("events.schedule_start_time")}<input type="datetime-local" name="scheduleStartAt" value="${event.raceConfig?.scheduleStartAt ? escapeHtml(formatDateTimeLocalValue(event.raceConfig.scheduleStartAt)) : ""}" /></label>
|
||||
<label>${t("events.schedule_gap")}<input type="number" min="0" step="1" name="scheduleGapMin" value="${Number(event.raceConfig?.scheduleGapMin ?? 3)}" /></label>
|
||||
<div class="actions">
|
||||
<button class="btn btn-primary" type="submit">${t("events.schedule_save")}</button>
|
||||
${event.raceConfig?.scheduleStartAt ? `<button id="scheduleClear" class="btn btn-danger" type="button">${t("events.schedule_clear")}</button>` : ""}
|
||||
</div>
|
||||
</form>
|
||||
${eventSchedule && eventSchedule.length
|
||||
? renderTable(
|
||||
[t("table.session"), t("table.type"), t("events.schedule_planned"), t("events.schedule_actual"), ""],
|
||||
eventSchedule.map((row) => {
|
||||
const session = sessions.find((item) => item.id === row.sessionId);
|
||||
if (!session) return "";
|
||||
return `
|
||||
<tr>
|
||||
<td>${escapeHtml(session.name)}</td>
|
||||
<td>${escapeHtml(getSessionTypeLabel(session.type))}</td>
|
||||
<td>${escapeHtml(formatClockTime(row.plannedStart))} - ${escapeHtml(formatClockTime(row.plannedEnd))}</td>
|
||||
<td>${row.actualStart ? escapeHtml(formatClockTime(row.actualStart)) + (row.actualEnd ? ` - ${escapeHtml(formatClockTime(row.actualEnd))}` : "") : "-"}</td>
|
||||
<td>${row.delayed ? `<span class="pill pill-warn">${t("events.schedule_delayed")}</span>` : ""}</td>
|
||||
</tr>
|
||||
`;
|
||||
})
|
||||
)
|
||||
: `<p class="hint">${t("events.schedule_not_set")}</p>`}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel mt-16">
|
||||
<div class="panel-header"><h3>${t("events.final_matrix")}</h3></div>
|
||||
<div class="panel-body">
|
||||
|
||||
Reference in New Issue
Block a user