From da1e60fe523094de2dbf0dea25813d6a22dfbc70 Mon Sep 17 00:00:00 2001 From: larssand Date: Sat, 14 Mar 2026 20:17:28 +0100 Subject: [PATCH] fixa adda drivers --- src/app.js | 179 ++++++++++++++++++++++++++++++++++++++++++++++++- src/styles.css | 10 +++ 2 files changed, 187 insertions(+), 2 deletions(-) diff --git a/src/app.js b/src/app.js index 9283a00..27be086 100644 --- a/src/app.js +++ b/src/app.js @@ -251,6 +251,12 @@ const TRANSLATIONS = { "timing.no_session_selected": "Ingen session vald.", "timing.no_passings": "Inga passeringar registrerade.", "timing.details": "Detaljer", + "timing.add_driver": "Lägg till förare", + "timing.add_car": "Lägg till bil", + "timing.quick_add_hint": "Snabbregistrera transponder", + "timing.quick_add_title": "Snabbregistrering", + "timing.quick_add_driver_title": "Lägg till förare från transponder", + "timing.quick_add_car_title": "Lägg till bil från transponder", "timing.open_overlay": "Öppna overlay", "timing.open_speaker_overlay": "Speaker overlay", "timing.open_results_overlay": "Result overlay", @@ -361,6 +367,8 @@ const TRANSLATIONS = { "table.ahead_gap": "Gap fram", "table.own_delta": "Eget delta", "common.delete": "Ta bort", + "common.cancel": "Avbryt", + "common.save": "Spara", "common.edit": "Redigera", "common.unknown_driver": "Okänd förare", "common.unknown_car": "Okänd bil", @@ -389,8 +397,10 @@ const TRANSLATIONS = { "validation.invalid_date": "Datum måste vara i format YYYY-MM-DD.", "edit.class_name": "Redigera klassnamn", "edit.driver_name": "Redigera förarnamn", + "edit.new_driver_name": "Namn på ny förare", "edit.driver_transponder": "Redigera personlig transponder (kan vara tom)", "edit.car_name": "Redigera bilnamn", + "edit.new_car_name": "Namn på ny bil", "edit.car_transponder": "Redigera bilens transponder", "edit.event_name": "Redigera eventnamn", "edit.event_date": "Redigera eventdatum (YYYY-MM-DD)", @@ -712,6 +722,12 @@ const TRANSLATIONS = { "timing.no_session_selected": "No session selected.", "timing.no_passings": "No passings recorded.", "timing.details": "Details", + "timing.add_driver": "Add driver", + "timing.add_car": "Add car", + "timing.quick_add_hint": "Quick-register transponder", + "timing.quick_add_title": "Quick add", + "timing.quick_add_driver_title": "Add driver from transponder", + "timing.quick_add_car_title": "Add car from transponder", "timing.open_overlay": "Open overlay", "timing.open_speaker_overlay": "Speaker overlay", "timing.open_results_overlay": "Results overlay", @@ -822,6 +838,8 @@ const TRANSLATIONS = { "table.ahead_gap": "Gap ahead", "table.own_delta": "Own delta", "common.delete": "Delete", + "common.cancel": "Cancel", + "common.save": "Save", "common.edit": "Edit", "common.unknown_driver": "Unknown Driver", "common.unknown_car": "Unknown Car", @@ -850,8 +868,10 @@ const TRANSLATIONS = { "validation.invalid_date": "Date must be in YYYY-MM-DD format.", "edit.class_name": "Edit class name", "edit.driver_name": "Edit driver name", + "edit.new_driver_name": "New driver name", "edit.driver_transponder": "Edit personal transponder (can be empty)", "edit.car_name": "Edit car name", + "edit.new_car_name": "New car name", "edit.car_transponder": "Edit car transponder", "edit.event_name": "Edit event name", "edit.event_date": "Edit event date (YYYY-MM-DD)", @@ -954,6 +974,7 @@ let appVersionPollTimer = null; let baselineAppVersion = ""; let selectedLeaderboardKey = null; let selectedGridSessionId = null; +let quickAddDraft = null; let overlaySyncTimer = null; let overlayRotationTimer = null; let overlayRotationIndex = 0; @@ -3174,6 +3195,8 @@ function renderTiming() { + ${active ? renderQuickAddPanel(active) : ""} +

${t("timing.speaker_panel")}

@@ -3224,6 +3247,59 @@ function renderTiming() { }); }); + if (active && selectedRow) { + bindQuickAddActions(active, selectedRow.transponder, "leaderboardModal"); + } + + if (active) { + ensureSessionResult(active.id) + .passings.slice(-20) + .reverse() + .forEach((passing, index) => { + bindQuickAddActions(active, passing.transponder, `recentPassing-${index}`); + }); + } + + document.getElementById("quickAddCancel")?.addEventListener("click", () => { + quickAddDraft = null; + renderView(); + }); + + document.getElementById("quickAddForm")?.addEventListener("submit", (event) => { + event.preventDefault(); + if (!active || !quickAddDraft) { + return; + } + const form = new FormData(event.currentTarget); + const name = String(form.get("name") || "").trim(); + if (!name) { + return; + } + const transponder = String(form.get("transponder") || "").trim(); + if (!transponder) { + return; + } + if (quickAddDraft.type === "driver") { + if (!state.drivers.some((item) => String(item.transponder || "").trim() === transponder)) { + state.drivers.push({ + id: uid("driver"), + name, + classId: String(form.get("classId") || getPreferredClassId(active)), + transponder, + }); + } + } else if (!state.cars.some((item) => String(item.transponder || "").trim() === transponder)) { + state.cars.push({ + id: uid("car"), + name, + transponder, + }); + } + quickAddDraft = null; + saveState(); + renderView(); + }); + document.getElementById("leaderboardModalClose")?.addEventListener("click", () => { selectedLeaderboardKey = null; renderView(); @@ -3346,6 +3422,42 @@ function renderSpeakerToggle(settingKey, labelKey) { `; } +function renderQuickAddPanel(session) { + if (!quickAddDraft || !quickAddDraft.transponder) { + return ""; + } + const classOptions = state.classes + .map( + (item) => `` + ) + .join(""); + const isDriver = quickAddDraft.type === "driver"; + return ` +
+

${t(isDriver ? "timing.quick_add_driver_title" : "timing.quick_add_car_title")}

+
+ + + ${ + isDriver + ? `` + : `
${t("timing.quick_add_hint")}
` + } +
+ + +
+
+
+ `; +} + function renderGuide() { dom.view.innerHTML = `
@@ -3696,6 +3808,67 @@ function renderOverlaySidePanel(panel) { `; } +function getQuickAddState(transponder) { + const normalized = String(transponder || "").trim(); + const driver = state.drivers.find((item) => String(item.transponder || "").trim() === normalized) || null; + const car = state.cars.find((item) => String(item.transponder || "").trim() === normalized) || null; + return { + transponder: normalized, + hasDriver: Boolean(driver), + hasCar: Boolean(car), + }; +} + +function getPreferredClassId(session) { + const event = state.events.find((item) => item.id === session?.eventId); + if (event?.classId) { + return event.classId; + } + return state.classes[0]?.id || ""; +} + +function beginQuickAddDraft(session, type, transponder) { + const normalized = String(transponder || "").trim(); + if (!normalized) { + return; + } + if (type === "driver" && state.drivers.some((item) => String(item.transponder || "").trim() === normalized)) { + return; + } + if (type === "car" && state.cars.some((item) => String(item.transponder || "").trim() === normalized)) { + return; + } + quickAddDraft = { + type, + transponder: normalized, + classId: getPreferredClassId(session), + name: type === "driver" ? normalized : `Car ${normalized}`, + }; + renderView(); +} + +function renderQuickAddActions(session, transponder, idPrefix) { + const quickState = getQuickAddState(transponder); + if (!quickState.transponder || (quickState.hasDriver && quickState.hasCar)) { + return ""; + } + return ` +
+ ${!quickState.hasDriver ? `` : ""} + ${!quickState.hasCar ? `` : ""} +
+ `; +} + +function bindQuickAddActions(session, transponder, idPrefix) { + document.getElementById(`${idPrefix}-add-driver`)?.addEventListener("click", () => { + beginQuickAddDraft(session, "driver", transponder); + }); + document.getElementById(`${idPrefix}-add-car`)?.addEventListener("click", () => { + beginQuickAddDraft(session, "car", transponder); + }); +} + function renderLeaderboardModal(session, row) { const passings = getCompetitorPassings(session, row); return ` @@ -3708,6 +3881,7 @@ function renderLeaderboardModal(session, row) {

${escapeHtml(row.driverName)} • ${escapeHtml(row.carName)}

${t("table.transponder")}: ${escapeHtml(row.transponder)}

+ ${renderQuickAddActions(session, row.transponder, "leaderboardModal")}

${t("table.laps")}: ${row.laps}

${t("timing.total_time")}: ${escapeHtml(row.resultDisplay)}

${t("table.best_lap")}: ${formatLap(row.bestLapMs)}

@@ -3843,8 +4017,8 @@ function renderRecentPassings(session) { } return renderTable( - [t("table.time"), t("table.transponder"), t("table.driver"), t("table.car"), t("table.loop"), t("table.strength")], - items.map((p) => { + [t("table.time"), t("table.transponder"), t("table.driver"), t("table.car"), t("table.loop"), t("table.strength"), ""], + items.map((p, index) => { return ` ${new Date(p.timestamp).toLocaleTimeString()} @@ -3853,6 +4027,7 @@ function renderRecentPassings(session) { ${escapeHtml(p.carName || "-")} ${escapeHtml(p.loopId || "-")} ${p.strength ?? "-"} + ${renderQuickAddActions(session, p.transponder, `recentPassing-${index}`)} `; }) diff --git a/src/styles.css b/src/styles.css index cc7b042..8c28819 100644 --- a/src/styles.css +++ b/src/styles.css @@ -439,6 +439,16 @@ select:focus { flex-wrap: wrap; } +.quick-add-actions { + justify-content: flex-end; +} + +.quick-add-spacer { + display: flex; + align-items: center; + min-height: 42px; +} + .check-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));