Polish edit modals across app
This commit is contained in:
462
src/app.js
462
src/app.js
@@ -395,6 +395,11 @@ const TRANSLATIONS = {
|
||||
"validation.missing_tp": "En eller flera tilldelade bilar saknar transponder-ID.",
|
||||
"validation.duplicate_tp": "Dubblett-transponder i session: {ids}.",
|
||||
"validation.invalid_date": "Datum måste vara i format YYYY-MM-DD.",
|
||||
"validation.invalid_selection": "Välj ett giltigt alternativ.",
|
||||
"validation.required_name": "Namn måste fyllas i.",
|
||||
"validation.required_transponder": "Transponder måste fyllas i.",
|
||||
"validation.required_date": "Datum måste fyllas i.",
|
||||
"validation.required_duration": "Duration måste vara minst 1 minut.",
|
||||
"edit.class_name": "Redigera klassnamn",
|
||||
"edit.driver_name": "Redigera förarnamn",
|
||||
"edit.driver_class": "Redigera förarklass",
|
||||
@@ -867,6 +872,11 @@ const TRANSLATIONS = {
|
||||
"validation.missing_tp": "One or more assigned cars are missing transponder ID.",
|
||||
"validation.duplicate_tp": "Duplicate transponder(s) in session: {ids}.",
|
||||
"validation.invalid_date": "Date must be in YYYY-MM-DD format.",
|
||||
"validation.invalid_selection": "Select a valid option.",
|
||||
"validation.required_name": "Name is required.",
|
||||
"validation.required_transponder": "Transponder is required.",
|
||||
"validation.required_date": "Date is required.",
|
||||
"validation.required_duration": "Duration must be at least 1 minute.",
|
||||
"edit.class_name": "Edit class name",
|
||||
"edit.driver_name": "Edit driver name",
|
||||
"edit.driver_class": "Edit driver class",
|
||||
@@ -974,9 +984,13 @@ let reconnectTimer = null;
|
||||
let backendSyncTimer = null;
|
||||
let appVersionPollTimer = null;
|
||||
let baselineAppVersion = "";
|
||||
let selectedClassEditId = null;
|
||||
let selectedLeaderboardKey = null;
|
||||
let selectedGridSessionId = null;
|
||||
let selectedDriverEditId = null;
|
||||
let selectedCarEditId = null;
|
||||
let selectedEventEditId = null;
|
||||
let selectedSessionEditId = null;
|
||||
let quickAddDraft = null;
|
||||
let overlaySyncTimer = null;
|
||||
let overlayRotationTimer = null;
|
||||
@@ -985,6 +999,7 @@ let overlayEvents = [];
|
||||
let lastOverlayLeaderKeyBySession = {};
|
||||
let lastOverlayTop3BySession = {};
|
||||
let lastOverlayBestLapByKey = {};
|
||||
let activeModalEscapeHandler = null;
|
||||
const backend = {
|
||||
available: false,
|
||||
lastSyncAt: null,
|
||||
@@ -1608,6 +1623,7 @@ function renderNav() {
|
||||
}
|
||||
|
||||
function renderView() {
|
||||
clearModalEscapeHandler();
|
||||
const navMeta = NAV_ITEMS.find((x) => x.id === currentView);
|
||||
dom.pageTitle.textContent = navMeta ? t(navMeta.titleKey) : "";
|
||||
dom.pageSubtitle.textContent = navMeta ? t(navMeta.subtitleKey) : "";
|
||||
@@ -1654,6 +1670,47 @@ function renderView() {
|
||||
updateHeaderState();
|
||||
}
|
||||
|
||||
function clearModalEscapeHandler() {
|
||||
if (activeModalEscapeHandler) {
|
||||
document.removeEventListener("keydown", activeModalEscapeHandler);
|
||||
activeModalEscapeHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
function bindModalShell(overlayId, onClose, focusSelector = 'input, select, textarea, button') {
|
||||
const overlay = document.getElementById(overlayId);
|
||||
if (!overlay) {
|
||||
clearModalEscapeHandler();
|
||||
return;
|
||||
}
|
||||
const focusTarget = overlay.querySelector(focusSelector);
|
||||
window.setTimeout(() => {
|
||||
if (focusTarget instanceof HTMLElement) {
|
||||
focusTarget.focus();
|
||||
if (focusTarget instanceof HTMLInputElement) {
|
||||
focusTarget.select();
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
clearModalEscapeHandler();
|
||||
activeModalEscapeHandler = (event) => {
|
||||
if (event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
document.addEventListener("keydown", activeModalEscapeHandler);
|
||||
}
|
||||
|
||||
function setFormError(errorId, message) {
|
||||
const errorNode = document.getElementById(errorId);
|
||||
if (!errorNode) {
|
||||
return;
|
||||
}
|
||||
errorNode.textContent = message || "";
|
||||
errorNode.hidden = !message;
|
||||
}
|
||||
|
||||
function updateHeaderState() {
|
||||
const session = getActiveSession();
|
||||
if (!session) {
|
||||
@@ -1978,6 +2035,7 @@ function statCard(label, value, note) {
|
||||
}
|
||||
|
||||
function renderClasses() {
|
||||
const editingClass = state.classes.find((item) => item.id === selectedClassEditId) || null;
|
||||
dom.view.innerHTML = `
|
||||
<div class="panel-row">
|
||||
<section class="panel">
|
||||
@@ -2008,6 +2066,29 @@ function renderClasses() {
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
${
|
||||
editingClass
|
||||
? `
|
||||
<div class="modal-overlay" id="classEditModalOverlay">
|
||||
<div class="modal-card">
|
||||
<div class="panel-header">
|
||||
<h3>${t("common.edit")}</h3>
|
||||
<button class="btn" id="classEditCancel">${t("common.cancel")}</button>
|
||||
</div>
|
||||
<form id="classEditForm" class="panel-body form-grid cols-2">
|
||||
<input name="name" required value="${escapeHtml(editingClass.name)}" placeholder="${t("classes.placeholder")}" />
|
||||
<p class="form-error" id="classEditError" hidden></p>
|
||||
<div class="actions-inline">
|
||||
<button class="btn btn-primary" type="submit">${t("common.save")}</button>
|
||||
<button class="btn" id="classEditCancelFooter" type="button">${t("common.cancel")}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
`;
|
||||
|
||||
document.getElementById("classForm")?.addEventListener("submit", (e) => {
|
||||
@@ -2020,16 +2101,7 @@ function renderClasses() {
|
||||
|
||||
state.classes.forEach((item) => {
|
||||
document.getElementById(`class-edit-${item.id}`)?.addEventListener("click", () => {
|
||||
const nextName = prompt(t("edit.class_name"), item.name);
|
||||
if (nextName === null) {
|
||||
return;
|
||||
}
|
||||
const cleaned = nextName.trim();
|
||||
if (!cleaned) {
|
||||
return;
|
||||
}
|
||||
item.name = cleaned;
|
||||
saveState();
|
||||
selectedClassEditId = item.id;
|
||||
renderView();
|
||||
});
|
||||
|
||||
@@ -2039,6 +2111,46 @@ function renderClasses() {
|
||||
renderView();
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById("classEditCancel")?.addEventListener("click", () => {
|
||||
selectedClassEditId = null;
|
||||
renderView();
|
||||
});
|
||||
|
||||
document.getElementById("classEditCancelFooter")?.addEventListener("click", () => {
|
||||
selectedClassEditId = null;
|
||||
renderView();
|
||||
});
|
||||
|
||||
document.getElementById("classEditModalOverlay")?.addEventListener("click", (event) => {
|
||||
if (event.target?.id === "classEditModalOverlay") {
|
||||
selectedClassEditId = null;
|
||||
renderView();
|
||||
}
|
||||
});
|
||||
|
||||
bindModalShell("classEditModalOverlay", () => {
|
||||
selectedClassEditId = null;
|
||||
renderView();
|
||||
});
|
||||
|
||||
document.getElementById("classEditForm")?.addEventListener("submit", (event) => {
|
||||
event.preventDefault();
|
||||
if (!editingClass) {
|
||||
return;
|
||||
}
|
||||
const form = new FormData(event.currentTarget);
|
||||
const cleaned = String(form.get("name") || "").trim();
|
||||
if (!cleaned) {
|
||||
setFormError("classEditError", t("validation.required_name"));
|
||||
return;
|
||||
}
|
||||
setFormError("classEditError", "");
|
||||
editingClass.name = cleaned;
|
||||
selectedClassEditId = null;
|
||||
saveState();
|
||||
renderView();
|
||||
});
|
||||
}
|
||||
|
||||
function renderDrivers() {
|
||||
@@ -2100,6 +2212,7 @@ function renderDrivers() {
|
||||
.join("")}
|
||||
</select>
|
||||
<input name="transponder" value="${escapeHtml(editingDriver.transponder || "")}" placeholder="${t("drivers.transponder_placeholder")}" />
|
||||
<p class="form-error" id="driverEditError" hidden></p>
|
||||
<div class="actions-inline">
|
||||
<button class="btn btn-primary" type="submit">${t("common.save")}</button>
|
||||
<button class="btn" id="driverEditCancelFooter" type="button">${t("common.cancel")}</button>
|
||||
@@ -2158,6 +2271,11 @@ function renderDrivers() {
|
||||
}
|
||||
});
|
||||
|
||||
bindModalShell("driverEditModalOverlay", () => {
|
||||
selectedDriverEditId = null;
|
||||
renderView();
|
||||
});
|
||||
|
||||
document.getElementById("driverEditForm")?.addEventListener("submit", (event) => {
|
||||
event.preventDefault();
|
||||
if (!editingDriver) {
|
||||
@@ -2168,11 +2286,14 @@ function renderDrivers() {
|
||||
const cleanedClassId = String(form.get("classId") || "").trim();
|
||||
const cleanedTp = String(form.get("transponder") || "").trim();
|
||||
if (!cleanedName) {
|
||||
setFormError("driverEditError", t("validation.required_name"));
|
||||
return;
|
||||
}
|
||||
if (cleanedClassId && !state.classes.some((item) => item.id === cleanedClassId)) {
|
||||
setFormError("driverEditError", t("validation.invalid_selection"));
|
||||
return;
|
||||
}
|
||||
setFormError("driverEditError", "");
|
||||
editingDriver.name = cleanedName;
|
||||
editingDriver.classId = cleanedClassId || editingDriver.classId;
|
||||
editingDriver.transponder = cleanedTp;
|
||||
@@ -2183,6 +2304,7 @@ function renderDrivers() {
|
||||
}
|
||||
|
||||
function renderCars() {
|
||||
const editingCar = state.cars.find((car) => car.id === selectedCarEditId) || null;
|
||||
dom.view.innerHTML = `
|
||||
<section class="panel">
|
||||
<div class="panel-header"><h3>${t("cars.create")}</h3></div>
|
||||
@@ -2213,6 +2335,35 @@ function renderCars() {
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
${
|
||||
editingCar
|
||||
? `
|
||||
<div class="modal-overlay" id="carEditModalOverlay">
|
||||
<div class="modal-card">
|
||||
<div class="panel-header">
|
||||
<h3>${t("common.edit")}</h3>
|
||||
<button class="btn" id="carEditCancel">${t("common.cancel")}</button>
|
||||
</div>
|
||||
<form id="carEditForm" class="panel-body form-grid cols-3">
|
||||
<input name="name" required value="${escapeHtml(editingCar.name)}" placeholder="${t("cars.name_placeholder")}" />
|
||||
<input
|
||||
name="transponder"
|
||||
required
|
||||
value="${escapeHtml(editingCar.transponder || "")}"
|
||||
placeholder="${t("cars.transponder_placeholder")}"
|
||||
/>
|
||||
<p class="form-error" id="carEditError" hidden></p>
|
||||
<div class="actions-inline">
|
||||
<button class="btn btn-primary" type="submit">${t("common.save")}</button>
|
||||
<button class="btn" id="carEditCancelFooter" type="button">${t("common.cancel")}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
`;
|
||||
|
||||
document.getElementById("carForm")?.addEventListener("submit", (e) => {
|
||||
@@ -2229,22 +2380,7 @@ function renderCars() {
|
||||
|
||||
state.cars.forEach((c) => {
|
||||
document.getElementById(`car-edit-${c.id}`)?.addEventListener("click", () => {
|
||||
const nextName = prompt(t("edit.car_name"), c.name);
|
||||
if (nextName === null) {
|
||||
return;
|
||||
}
|
||||
const nextTp = prompt(t("edit.car_transponder"), c.transponder || "");
|
||||
if (nextTp === null) {
|
||||
return;
|
||||
}
|
||||
const cleanedName = nextName.trim();
|
||||
const cleanedTp = nextTp.trim();
|
||||
if (!cleanedName || !cleanedTp) {
|
||||
return;
|
||||
}
|
||||
c.name = cleanedName;
|
||||
c.transponder = cleanedTp;
|
||||
saveState();
|
||||
selectedCarEditId = c.id;
|
||||
renderView();
|
||||
});
|
||||
|
||||
@@ -2257,6 +2393,52 @@ function renderCars() {
|
||||
renderView();
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById("carEditCancel")?.addEventListener("click", () => {
|
||||
selectedCarEditId = null;
|
||||
renderView();
|
||||
});
|
||||
|
||||
document.getElementById("carEditCancelFooter")?.addEventListener("click", () => {
|
||||
selectedCarEditId = null;
|
||||
renderView();
|
||||
});
|
||||
|
||||
document.getElementById("carEditModalOverlay")?.addEventListener("click", (event) => {
|
||||
if (event.target?.id === "carEditModalOverlay") {
|
||||
selectedCarEditId = null;
|
||||
renderView();
|
||||
}
|
||||
});
|
||||
|
||||
bindModalShell("carEditModalOverlay", () => {
|
||||
selectedCarEditId = null;
|
||||
renderView();
|
||||
});
|
||||
|
||||
document.getElementById("carEditForm")?.addEventListener("submit", (event) => {
|
||||
event.preventDefault();
|
||||
if (!editingCar) {
|
||||
return;
|
||||
}
|
||||
const form = new FormData(event.currentTarget);
|
||||
const cleanedName = String(form.get("name") || "").trim();
|
||||
const cleanedTp = String(form.get("transponder") || "").trim();
|
||||
if (!cleanedName) {
|
||||
setFormError("carEditError", t("validation.required_name"));
|
||||
return;
|
||||
}
|
||||
if (!cleanedTp) {
|
||||
setFormError("carEditError", t("validation.required_transponder"));
|
||||
return;
|
||||
}
|
||||
setFormError("carEditError", "");
|
||||
editingCar.name = cleanedName;
|
||||
editingCar.transponder = cleanedTp;
|
||||
selectedCarEditId = null;
|
||||
saveState();
|
||||
renderView();
|
||||
});
|
||||
}
|
||||
|
||||
function renderEvents() {
|
||||
@@ -2273,6 +2455,7 @@ function renderEventWorkspace(mode) {
|
||||
const classOptions = state.classes
|
||||
.map((c) => `<option value="${c.id}">${escapeHtml(c.name)}</option>`)
|
||||
.join("");
|
||||
const editingEvent = filteredEvents.find((event) => event.id === selectedEventEditId) || null;
|
||||
|
||||
dom.view.innerHTML = `
|
||||
<section class="panel">
|
||||
@@ -2315,6 +2498,38 @@ function renderEventWorkspace(mode) {
|
||||
</section>
|
||||
|
||||
<section id="eventManageArea"></section>
|
||||
|
||||
${
|
||||
editingEvent
|
||||
? `
|
||||
<div class="modal-overlay" id="eventEditModalOverlay">
|
||||
<div class="modal-card">
|
||||
<div class="panel-header">
|
||||
<h3>${t("common.edit")}</h3>
|
||||
<button class="btn" id="eventEditCancel">${t("common.cancel")}</button>
|
||||
</div>
|
||||
<form id="eventEditForm" class="panel-body form-grid cols-3">
|
||||
<input name="name" required value="${escapeHtml(editingEvent.name)}" placeholder="${t("events.name_placeholder")}" />
|
||||
<input name="date" required type="date" value="${escapeHtml(editingEvent.date || "")}" />
|
||||
<select name="classId">
|
||||
${state.classes
|
||||
.map(
|
||||
(item) =>
|
||||
`<option value="${item.id}" ${item.id === editingEvent.classId ? "selected" : ""}>${escapeHtml(item.name)}</option>`
|
||||
)
|
||||
.join("")}
|
||||
</select>
|
||||
<p class="form-error" id="eventEditError" hidden></p>
|
||||
<div class="actions-inline">
|
||||
<button class="btn btn-primary" type="submit">${t("common.save")}</button>
|
||||
<button class="btn" id="eventEditCancelFooter" type="button">${t("common.cancel")}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
`;
|
||||
|
||||
document.getElementById("eventForm")?.addEventListener("submit", (e) => {
|
||||
@@ -2334,26 +2549,7 @@ function renderEventWorkspace(mode) {
|
||||
|
||||
filteredEvents.forEach((e) => {
|
||||
document.getElementById(`event-edit-${e.id}`)?.addEventListener("click", () => {
|
||||
const nextName = prompt(t("edit.event_name"), e.name);
|
||||
if (nextName === null) {
|
||||
return;
|
||||
}
|
||||
const nextDate = prompt(t("edit.event_date"), e.date);
|
||||
if (nextDate === null) {
|
||||
return;
|
||||
}
|
||||
const cleanedName = nextName.trim();
|
||||
const cleanedDate = nextDate.trim();
|
||||
if (!cleanedName) {
|
||||
return;
|
||||
}
|
||||
if (!isValidIsoDate(cleanedDate)) {
|
||||
alert(t("validation.invalid_date"));
|
||||
return;
|
||||
}
|
||||
e.name = cleanedName;
|
||||
e.date = cleanedDate;
|
||||
saveState();
|
||||
selectedEventEditId = e.id;
|
||||
renderView();
|
||||
});
|
||||
|
||||
@@ -2373,6 +2569,62 @@ function renderEventWorkspace(mode) {
|
||||
renderEventManager(e.id);
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById("eventEditCancel")?.addEventListener("click", () => {
|
||||
selectedEventEditId = null;
|
||||
renderView();
|
||||
});
|
||||
|
||||
document.getElementById("eventEditCancelFooter")?.addEventListener("click", () => {
|
||||
selectedEventEditId = null;
|
||||
renderView();
|
||||
});
|
||||
|
||||
document.getElementById("eventEditModalOverlay")?.addEventListener("click", (event) => {
|
||||
if (event.target?.id === "eventEditModalOverlay") {
|
||||
selectedEventEditId = null;
|
||||
renderView();
|
||||
}
|
||||
});
|
||||
|
||||
bindModalShell("eventEditModalOverlay", () => {
|
||||
selectedEventEditId = null;
|
||||
renderView();
|
||||
});
|
||||
|
||||
document.getElementById("eventEditForm")?.addEventListener("submit", (event) => {
|
||||
event.preventDefault();
|
||||
if (!editingEvent) {
|
||||
return;
|
||||
}
|
||||
const form = new FormData(event.currentTarget);
|
||||
const cleanedName = String(form.get("name") || "").trim();
|
||||
const cleanedDate = String(form.get("date") || "").trim();
|
||||
const cleanedClassId = String(form.get("classId") || "").trim();
|
||||
if (!cleanedName) {
|
||||
setFormError("eventEditError", t("validation.required_name"));
|
||||
return;
|
||||
}
|
||||
if (!cleanedDate) {
|
||||
setFormError("eventEditError", t("validation.required_date"));
|
||||
return;
|
||||
}
|
||||
if (!isValidIsoDate(cleanedDate)) {
|
||||
setFormError("eventEditError", t("validation.invalid_date"));
|
||||
return;
|
||||
}
|
||||
if (cleanedClassId && !state.classes.some((item) => item.id === cleanedClassId)) {
|
||||
setFormError("eventEditError", t("validation.invalid_selection"));
|
||||
return;
|
||||
}
|
||||
setFormError("eventEditError", "");
|
||||
editingEvent.name = cleanedName;
|
||||
editingEvent.date = cleanedDate;
|
||||
editingEvent.classId = cleanedClassId || editingEvent.classId;
|
||||
selectedEventEditId = null;
|
||||
saveState();
|
||||
renderView();
|
||||
});
|
||||
}
|
||||
|
||||
function renderEventManager(eventId) {
|
||||
@@ -2400,6 +2652,7 @@ function renderEventManager(eventId) {
|
||||
.map((c) => `<option value="${c.id}">${escapeHtml(c.name)} (${escapeHtml(c.transponder)})</option>`)
|
||||
.join("");
|
||||
const branding = normalizeBrandingConfig(event.branding);
|
||||
const editingSession = sessions.find((session) => session.id === selectedSessionEditId) || null;
|
||||
const gridSessions = event.mode === "race" ? sessions.filter((session) => normalizeStartMode(session.startMode) === "position") : [];
|
||||
if (selectedGridSessionId && !gridSessions.some((session) => session.id === selectedGridSessionId)) {
|
||||
selectedGridSessionId = "";
|
||||
@@ -2722,6 +2975,42 @@ function renderEventManager(eventId) {
|
||||
`
|
||||
: ""
|
||||
}
|
||||
|
||||
${
|
||||
editingSession
|
||||
? `
|
||||
<div class="modal-overlay" id="sessionEditModalOverlay">
|
||||
<div class="modal-card">
|
||||
<div class="panel-header">
|
||||
<h3>${t("events.edit_session")}</h3>
|
||||
<button class="btn" id="sessionEditCancel">${t("common.cancel")}</button>
|
||||
</div>
|
||||
<form id="sessionEditForm" class="panel-body form-grid cols-5">
|
||||
<input name="name" required value="${escapeHtml(editingSession.name)}" placeholder="${t("events.session_name")}" />
|
||||
<select name="type">
|
||||
${SESSION_TYPES.map(
|
||||
(item) => `<option value="${item}" ${item === editingSession.type ? "selected" : ""}>${getSessionTypeLabel(item)}</option>`
|
||||
).join("")}
|
||||
</select>
|
||||
<input name="durationMin" required type="number" min="1" value="${editingSession.durationMin || 5}" />
|
||||
<select name="startMode">
|
||||
<option value="mass" ${normalizeStartMode(editingSession.startMode) === "mass" ? "selected" : ""}>${t("events.start_mode_mass")}</option>
|
||||
<option value="position" ${normalizeStartMode(editingSession.startMode) === "position" ? "selected" : ""}>${t("events.start_mode_position")}</option>
|
||||
<option value="staggered" ${normalizeStartMode(editingSession.startMode) === "staggered" ? "selected" : ""}>${t("events.start_mode_staggered")}</option>
|
||||
</select>
|
||||
<input name="seedBestLapCount" type="number" min="0" step="1" value="${editingSession.seedBestLapCount || 0}" />
|
||||
<input name="staggerGapSec" type="number" min="0" step="1" value="${editingSession.staggerGapSec || 0}" />
|
||||
<p class="form-error" id="sessionEditError" hidden></p>
|
||||
<div class="actions-inline">
|
||||
<button class="btn btn-primary" type="submit">${t("common.save")}</button>
|
||||
<button class="btn" id="sessionEditCancelFooter" type="button">${t("common.cancel")}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
`;
|
||||
|
||||
document.getElementById("eventBrandingForm")?.addEventListener("submit", (e) => {
|
||||
@@ -2792,36 +3081,7 @@ function renderEventManager(eventId) {
|
||||
|
||||
sessions.forEach((s) => {
|
||||
document.getElementById(`session-edit-${s.id}`)?.addEventListener("click", () => {
|
||||
const nextName = prompt(t("events.session_name"), s.name);
|
||||
if (nextName === null) {
|
||||
return;
|
||||
}
|
||||
const nextDuration = prompt(t("events.duration_placeholder"), String(s.durationMin || 5));
|
||||
if (nextDuration === null) {
|
||||
return;
|
||||
}
|
||||
const nextStartMode = prompt(
|
||||
`${t("events.start_mode")} (mass|position|staggered)`,
|
||||
String(s.startMode || "mass")
|
||||
);
|
||||
if (nextStartMode === null) {
|
||||
return;
|
||||
}
|
||||
const nextSeedBestLaps = prompt(t("events.seed_best_laps"), String(s.seedBestLapCount || 0));
|
||||
if (nextSeedBestLaps === null) {
|
||||
return;
|
||||
}
|
||||
const nextStaggerGapSec = prompt(t("events.stagger_gap_sec"), String(s.staggerGapSec || 0));
|
||||
if (nextStaggerGapSec === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
s.name = nextName.trim() || s.name;
|
||||
s.durationMin = Math.max(1, Number(nextDuration) || s.durationMin || 5);
|
||||
s.startMode = normalizeStartMode(nextStartMode);
|
||||
s.seedBestLapCount = Math.max(0, Number(nextSeedBestLaps) || 0);
|
||||
s.staggerGapSec = Math.max(0, Number(nextStaggerGapSec) || 0);
|
||||
saveState();
|
||||
selectedSessionEditId = s.id;
|
||||
renderEventManager(eventId);
|
||||
});
|
||||
|
||||
@@ -2863,6 +3123,56 @@ function renderEventManager(eventId) {
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById("sessionEditCancel")?.addEventListener("click", () => {
|
||||
selectedSessionEditId = null;
|
||||
renderEventManager(eventId);
|
||||
});
|
||||
|
||||
document.getElementById("sessionEditCancelFooter")?.addEventListener("click", () => {
|
||||
selectedSessionEditId = null;
|
||||
renderEventManager(eventId);
|
||||
});
|
||||
|
||||
document.getElementById("sessionEditModalOverlay")?.addEventListener("click", (event) => {
|
||||
if (event.target?.id === "sessionEditModalOverlay") {
|
||||
selectedSessionEditId = null;
|
||||
renderEventManager(eventId);
|
||||
}
|
||||
});
|
||||
|
||||
bindModalShell("sessionEditModalOverlay", () => {
|
||||
selectedSessionEditId = null;
|
||||
renderEventManager(eventId);
|
||||
});
|
||||
|
||||
document.getElementById("sessionEditForm")?.addEventListener("submit", (event) => {
|
||||
event.preventDefault();
|
||||
if (!editingSession) {
|
||||
return;
|
||||
}
|
||||
const form = new FormData(event.currentTarget);
|
||||
const cleanedName = String(form.get("name") || "").trim();
|
||||
const cleanedDuration = Number(form.get("durationMin") || editingSession.durationMin || 5) || 0;
|
||||
if (!cleanedName) {
|
||||
setFormError("sessionEditError", t("validation.required_name"));
|
||||
return;
|
||||
}
|
||||
if (cleanedDuration < 1) {
|
||||
setFormError("sessionEditError", t("validation.required_duration"));
|
||||
return;
|
||||
}
|
||||
setFormError("sessionEditError", "");
|
||||
editingSession.name = cleanedName;
|
||||
editingSession.type = String(form.get("type") || editingSession.type);
|
||||
editingSession.durationMin = Math.max(1, cleanedDuration);
|
||||
editingSession.startMode = normalizeStartMode(String(form.get("startMode") || editingSession.startMode || "mass"));
|
||||
editingSession.seedBestLapCount = Math.max(0, Number(form.get("seedBestLapCount") || 0) || 0);
|
||||
editingSession.staggerGapSec = Math.max(0, Number(form.get("staggerGapSec") || 0) || 0);
|
||||
selectedSessionEditId = null;
|
||||
saveState();
|
||||
renderEventManager(eventId);
|
||||
});
|
||||
|
||||
if (event.mode === "track") {
|
||||
document.getElementById("sponsorRoundsForm")?.addEventListener("submit", (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
Reference in New Issue
Block a user