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>
This commit is contained in:
@@ -67,6 +67,7 @@ export function renderSettings(deps) {
|
||||
nextTrackLayoutLabel,
|
||||
addTrackLayout,
|
||||
deleteTrackLayout,
|
||||
updateTrackLayoutLapWindow = () => ({ ok: false }),
|
||||
updateStatus,
|
||||
updateStatusLoaded,
|
||||
updateCheckPending,
|
||||
@@ -306,14 +307,21 @@ export function renderSettings(deps) {
|
||||
now.setMinutes(now.getMinutes() - now.getTimezoneOffset());
|
||||
return now.toISOString().slice(0, 16);
|
||||
})())}" /></label>
|
||||
<label><span>${t("layouts.min_lap")}</span><input name="minLapSec" type="number" min="0" step="0.1" placeholder="${escapeHtml(t("layouts.lap_from_event"))}" /></label>
|
||||
<label><span>${t("layouts.max_lap")}</span><input name="maxLapSec" type="number" min="0" step="0.1" placeholder="${escapeHtml(t("layouts.lap_from_event"))}" /></label>
|
||||
<div class="actions"><button class="btn btn-primary" type="submit">${t("settings.track_layout_add")}</button></div>
|
||||
</form>
|
||||
<p class="hint">${t("layouts.lap_window_hint")}</p>
|
||||
<div class="backup-list mt-16">
|
||||
${(trackLayouts || []).length
|
||||
? trackLayouts.slice().reverse().map((layout) => `
|
||||
<article class="backup-row">
|
||||
<article class="backup-row track-layout-row">
|
||||
<div><strong>${escapeHtml(layout.label || t("settings.track_layout_unnamed"))}</strong><small>${escapeHtml(new Date(layout.startedAt).toLocaleString())}</small></div>
|
||||
<span></span>
|
||||
<form class="layout-lap-window" data-layout-lap-window="${escapeHtml(layout.id)}">
|
||||
<label><span>${t("layouts.min_lap")}</span><input name="minLapSec" type="number" min="0" step="0.1" value="${layout.minLapMs ? (layout.minLapMs / 1000).toFixed(1) : ""}" placeholder="${escapeHtml(t("layouts.lap_from_event"))}" /></label>
|
||||
<label><span>${t("layouts.max_lap")}</span><input name="maxLapSec" type="number" min="0" step="0.1" value="${layout.maxLapMs ? (layout.maxLapMs / 1000).toFixed(1) : ""}" placeholder="${escapeHtml(t("layouts.lap_from_event"))}" /></label>
|
||||
<button class="btn btn-mini" type="submit">${t("common.save")}</button>
|
||||
</form>
|
||||
<button class="btn btn-mini" type="button" data-delete-track-layout="${escapeHtml(layout.id)}">${t("common.delete")}</button>
|
||||
</article>`).join("")
|
||||
: `<p class="hint">${t("settings.track_layout_none")}</p>`}
|
||||
@@ -760,13 +768,26 @@ Erik Eriksson;654321;4WD;Associated;;</code></pre>
|
||||
alert(t("settings.track_layout_invalid_date"));
|
||||
return;
|
||||
}
|
||||
const result = addTrackLayout(label, startedAtMs);
|
||||
const seconds = (name) => Math.round((Number(form.elements.namedItem(name)?.value) || 0) * 1000);
|
||||
const result = addTrackLayout(label, startedAtMs, { minLapMs: seconds("minLapSec"), maxLapMs: seconds("maxLapSec") });
|
||||
if (!result.ok) {
|
||||
alert(result.message);
|
||||
return;
|
||||
}
|
||||
renderView();
|
||||
});
|
||||
document.querySelectorAll("[data-layout-lap-window]").forEach((form) => {
|
||||
form.addEventListener("submit", (event) => {
|
||||
event.preventDefault();
|
||||
const seconds = (name) => Math.round((Number(form.elements.namedItem(name)?.value) || 0) * 1000);
|
||||
const result = updateTrackLayoutLapWindow(form.dataset.layoutLapWindow, seconds("minLapSec"), seconds("maxLapSec"));
|
||||
if (!result.ok) {
|
||||
alert(result.message);
|
||||
return;
|
||||
}
|
||||
renderView();
|
||||
});
|
||||
});
|
||||
document.querySelectorAll("[data-delete-track-layout]").forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
const id = button.getAttribute("data-delete-track-layout");
|
||||
|
||||
Reference in New Issue
Block a user