From baebb6ac9d3bbd9be8d4e7d5bb945f2907b530ca Mon Sep 17 00:00:00 2001 From: larssand Date: Mon, 16 Mar 2026 19:57:56 +0100 Subject: [PATCH] Add brand fields for drivers and cars --- src/app.js | 125 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 91 insertions(+), 34 deletions(-) diff --git a/src/app.js b/src/app.js index c8f4cc2..01274dc 100644 --- a/src/app.js +++ b/src/app.js @@ -86,11 +86,13 @@ const TRANSLATIONS = { "classes.title": "Klasser", "drivers.create": "Skapa förare", "drivers.name_placeholder": "Förarnamn", + "drivers.brand_placeholder": "Team / märke (valfritt)", "drivers.transponder_placeholder": "Personlig transponder (valfritt)", "drivers.add": "Lägg till förare", "drivers.title": "Förare", "cars.create": "Skapa bil", "cars.name_placeholder": "Bilnamn eller nummer", + "cars.brand_placeholder": "Märke / modell (valfritt)", "cars.transponder_placeholder": "Bilens transponder", "cars.add": "Lägg till bil", "cars.title": "Bilar", @@ -460,6 +462,7 @@ const TRANSLATIONS = { "settings.sync_now": "Synka nu", "settings.export_json": "Exportera JSON", "table.name": "Namn", + "table.brand": "Märke", "table.class": "Klass", "table.transponder": "Transponder", "table.delete": "Ta bort", @@ -723,11 +726,13 @@ const TRANSLATIONS = { "classes.title": "Classes", "drivers.create": "Create Driver", "drivers.name_placeholder": "Driver name", + "drivers.brand_placeholder": "Team / brand (optional)", "drivers.transponder_placeholder": "Personal transponder (optional)", "drivers.add": "Add Driver", "drivers.title": "Drivers", "cars.create": "Create Track Car", "cars.name_placeholder": "Car name or number", + "cars.brand_placeholder": "Brand / model (optional)", "cars.transponder_placeholder": "Car transponder", "cars.add": "Add Car", "cars.title": "Cars", @@ -1097,6 +1102,7 @@ const TRANSLATIONS = { "settings.sync_now": "Sync Now", "settings.export_json": "Export JSON", "table.name": "Name", + "table.brand": "Brand", "table.class": "Class", "table.transponder": "Transponder", "table.delete": "Delete", @@ -1454,6 +1460,8 @@ function seedDefaultData() { state.settings.racePresets = []; } + state.drivers = state.drivers.map((driver) => normalizeDriver(driver)).filter((driver) => driver.name); + state.cars = state.cars.map((car) => normalizeCar(car)).filter((car) => car.name); state.events = state.events.map((event) => normalizeEvent(event)); state.sessions = state.sessions.map((session) => normalizeSession(session)); @@ -1600,8 +1608,8 @@ function setupLanguageControl() { function buildPersistableState() { return { classes: state.classes, - drivers: state.drivers, - cars: state.cars, + drivers: state.drivers.map((driver) => normalizeDriver(driver)), + cars: state.cars.map((car) => normalizeCar(car)), events: state.events, sessions: state.sessions.map((session) => normalizeSession(session)), resultsBySession: state.resultsBySession, @@ -1755,8 +1763,8 @@ async function hydrateFromBackend() { function applyPersistedState(persisted) { state.classes = persisted.classes || []; - state.drivers = persisted.drivers || []; - state.cars = persisted.cars || []; + state.drivers = (persisted.drivers || []).map((driver) => normalizeDriver(driver)).filter((driver) => driver.name); + state.cars = (persisted.cars || []).map((car) => normalizeCar(car)).filter((car) => car.name); state.events = (persisted.events || []).map((event) => normalizeEvent(event)); state.sessions = (persisted.sessions || []).map((session) => normalizeSession(session)); state.resultsBySession = persisted.resultsBySession || {}; @@ -1992,6 +2000,27 @@ function buildRaceFormatConfigFromForm(form, event) { }; } +function normalizeDriver(driver) { + const item = driver && typeof driver === "object" ? driver : {}; + return { + id: item.id || uid("driver"), + name: String(item.name || "").trim(), + classId: String(item.classId || ""), + brand: String(item.brand || "").trim(), + transponder: String(item.transponder || "").trim(), + }; +} + +function normalizeCar(car) { + const item = car && typeof car === "object" ? car : {}; + return { + id: item.id || uid("car"), + name: String(item.name || "").trim(), + brand: String(item.brand || "").trim(), + transponder: String(item.transponder || "").trim(), + }; +} + function normalizeEvent(event) { return { ...event, @@ -2811,9 +2840,10 @@ function renderDrivers() { dom.view.innerHTML = `

${t("drivers.create")}

-
+ +
@@ -2823,12 +2853,13 @@ function renderDrivers() {

${t("drivers.title")}

${renderTable( - [t("table.name"), t("table.class"), t("table.transponder"), t("events.actions")], + [t("table.name"), t("table.class"), t("table.brand"), t("table.transponder"), t("events.actions")], state.drivers.map( (d) => ` ${escapeHtml(d.name)} ${escapeHtml(getClassName(d.classId))} + ${escapeHtml(d.brand || "-")} ${escapeHtml(d.transponder || "-")} @@ -2850,7 +2881,7 @@ function renderDrivers() {

${t("common.edit")}

-
+ +
@@ -2877,12 +2909,15 @@ function renderDrivers() { document.getElementById("driverForm")?.addEventListener("submit", (e) => { e.preventDefault(); const form = new FormData(e.currentTarget); - state.drivers.push({ - id: uid("driver"), - name: String(form.get("name")).trim(), - classId: String(form.get("classId")), - transponder: String(form.get("transponder") || "").trim(), - }); + state.drivers.push( + normalizeDriver({ + id: uid("driver"), + name: String(form.get("name")).trim(), + classId: String(form.get("classId")), + brand: String(form.get("brand") || "").trim(), + transponder: String(form.get("transponder") || "").trim(), + }) + ); saveState(); renderView(); }); @@ -2933,6 +2968,7 @@ function renderDrivers() { const form = new FormData(event.currentTarget); const cleanedName = String(form.get("name") || "").trim(); const cleanedClassId = String(form.get("classId") || "").trim(); + const cleanedBrand = String(form.get("brand") || "").trim(); const cleanedTp = String(form.get("transponder") || "").trim(); if (!cleanedName) { setFormError("driverEditError", t("validation.required_name")); @@ -2945,6 +2981,7 @@ function renderDrivers() { setFormError("driverEditError", ""); editingDriver.name = cleanedName; editingDriver.classId = cleanedClassId || editingDriver.classId; + editingDriver.brand = cleanedBrand; editingDriver.transponder = cleanedTp; selectedDriverEditId = null; saveState(); @@ -2957,8 +2994,9 @@ function renderCars() { dom.view.innerHTML = `

${t("cars.create")}

- + + @@ -2968,11 +3006,12 @@ function renderCars() {

${t("cars.title")}

${renderTable( - [t("table.car"), t("table.transponder"), t("events.actions")], + [t("table.car"), t("table.brand"), t("table.transponder"), t("events.actions")], state.cars.map( (c) => ` ${escapeHtml(c.name)} + ${escapeHtml(c.brand || "-")} ${escapeHtml(c.transponder)} @@ -2994,13 +3033,14 @@ function renderCars() {

${t("common.edit")}

-
+ +
@@ -3018,11 +3058,14 @@ function renderCars() { document.getElementById("carForm")?.addEventListener("submit", (e) => { e.preventDefault(); const form = new FormData(e.currentTarget); - state.cars.push({ - id: uid("car"), - name: String(form.get("name")).trim(), - transponder: String(form.get("transponder")).trim(), - }); + state.cars.push( + normalizeCar({ + id: uid("car"), + name: String(form.get("name")).trim(), + brand: String(form.get("brand") || "").trim(), + transponder: String(form.get("transponder")).trim(), + }) + ); saveState(); renderView(); }); @@ -3072,6 +3115,7 @@ function renderCars() { } const form = new FormData(event.currentTarget); const cleanedName = String(form.get("name") || "").trim(); + const cleanedBrand = String(form.get("brand") || "").trim(); const cleanedTp = String(form.get("transponder") || "").trim(); if (!cleanedName) { setFormError("carEditError", t("validation.required_name")); @@ -3083,6 +3127,7 @@ function renderCars() { } setFormError("carEditError", ""); editingCar.name = cleanedName; + editingCar.brand = cleanedBrand; editingCar.transponder = cleanedTp; selectedCarEditId = null; saveState(); @@ -4737,24 +4782,31 @@ function renderTiming() { return; } const transponder = String(form.get("transponder") || "").trim(); + const brand = String(form.get("brand") || "").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, - }); + state.drivers.push( + normalizeDriver({ + id: uid("driver"), + name, + classId: String(form.get("classId") || getPreferredClassId(active)), + brand, + transponder, + }) + ); } } else if (!state.cars.some((item) => String(item.transponder || "").trim() === transponder)) { - state.cars.push({ - id: uid("car"), - name, - transponder, - }); + state.cars.push( + normalizeCar({ + id: uid("car"), + name, + brand, + transponder, + }) + ); } quickAddDraft = null; saveState(); @@ -5148,7 +5200,7 @@ function renderQuickAddPanel(session) { return `

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

- + + ${ isDriver ? ``