Add brand fields for drivers and cars
This commit is contained in:
125
src/app.js
125
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 = `
|
||||
<section class="panel">
|
||||
<div class="panel-header"><h3>${t("drivers.create")}</h3></div>
|
||||
<form id="driverForm" class="panel-body form-grid cols-4">
|
||||
<form id="driverForm" class="panel-body form-grid cols-5">
|
||||
<input required name="name" placeholder="${t("drivers.name_placeholder")}" />
|
||||
<select name="classId">${classOptions}</select>
|
||||
<input name="brand" placeholder="${t("drivers.brand_placeholder")}" />
|
||||
<input name="transponder" placeholder="${t("drivers.transponder_placeholder")}" />
|
||||
<button class="btn btn-primary" type="submit">${t("drivers.add")}</button>
|
||||
</form>
|
||||
@@ -2823,12 +2853,13 @@ function renderDrivers() {
|
||||
<div class="panel-header"><h3>${t("drivers.title")}</h3></div>
|
||||
<div class="panel-body">
|
||||
${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) => `
|
||||
<tr>
|
||||
<td>${escapeHtml(d.name)}</td>
|
||||
<td>${escapeHtml(getClassName(d.classId))}</td>
|
||||
<td>${escapeHtml(d.brand || "-")}</td>
|
||||
<td>${escapeHtml(d.transponder || "-")}</td>
|
||||
<td class="actions-inline">
|
||||
<button id="driver-edit-${d.id}" class="btn">${t("common.edit")}</button>
|
||||
@@ -2850,7 +2881,7 @@ function renderDrivers() {
|
||||
<h3>${t("common.edit")}</h3>
|
||||
<button class="btn" id="driverEditCancel">${t("common.cancel")}</button>
|
||||
</div>
|
||||
<form id="driverEditForm" class="panel-body form-grid cols-3">
|
||||
<form id="driverEditForm" class="panel-body form-grid cols-4">
|
||||
<input name="name" required value="${escapeHtml(editingDriver.name)}" placeholder="${t("drivers.name_placeholder")}" />
|
||||
<select name="classId">
|
||||
${state.classes
|
||||
@@ -2860,6 +2891,7 @@ function renderDrivers() {
|
||||
)
|
||||
.join("")}
|
||||
</select>
|
||||
<input name="brand" value="${escapeHtml(editingDriver.brand || "")}" placeholder="${t("drivers.brand_placeholder")}" />
|
||||
<input name="transponder" value="${escapeHtml(editingDriver.transponder || "")}" placeholder="${t("drivers.transponder_placeholder")}" />
|
||||
<p class="form-error" id="driverEditError" hidden></p>
|
||||
<div class="actions-inline">
|
||||
@@ -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 = `
|
||||
<section class="panel">
|
||||
<div class="panel-header"><h3>${t("cars.create")}</h3></div>
|
||||
<form id="carForm" class="panel-body form-grid cols-3">
|
||||
<form id="carForm" class="panel-body form-grid cols-4">
|
||||
<input required name="name" placeholder="${t("cars.name_placeholder")}" />
|
||||
<input name="brand" placeholder="${t("cars.brand_placeholder")}" />
|
||||
<input required name="transponder" placeholder="${t("cars.transponder_placeholder")}" />
|
||||
<button class="btn btn-primary" type="submit">${t("cars.add")}</button>
|
||||
</form>
|
||||
@@ -2968,11 +3006,12 @@ function renderCars() {
|
||||
<div class="panel-header"><h3>${t("cars.title")}</h3></div>
|
||||
<div class="panel-body">
|
||||
${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) => `
|
||||
<tr>
|
||||
<td>${escapeHtml(c.name)}</td>
|
||||
<td>${escapeHtml(c.brand || "-")}</td>
|
||||
<td>${escapeHtml(c.transponder)}</td>
|
||||
<td class="actions-inline">
|
||||
<button id="car-edit-${c.id}" class="btn">${t("common.edit")}</button>
|
||||
@@ -2994,13 +3033,14 @@ function renderCars() {
|
||||
<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">
|
||||
<form id="carEditForm" class="panel-body form-grid cols-4">
|
||||
<input name="name" required value="${escapeHtml(editingCar.name)}" placeholder="${t("cars.name_placeholder")}" />
|
||||
<input name="brand" value="${escapeHtml(editingCar.brand || "")}" placeholder="${t("cars.brand_placeholder")}" />
|
||||
<input
|
||||
name="transponder"
|
||||
required
|
||||
value="${escapeHtml(editingCar.transponder || "")}"
|
||||
placeholder="${t("cars.transponder_placeholder")}"
|
||||
placeholder="${t("cars.transponder_placeholder")}"
|
||||
/>
|
||||
<p class="form-error" id="carEditError" hidden></p>
|
||||
<div class="actions-inline">
|
||||
@@ -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 `
|
||||
<section class="panel mt-16">
|
||||
<div class="panel-header"><h3>${t(isDriver ? "timing.quick_add_driver_title" : "timing.quick_add_car_title")}</h3></div>
|
||||
<form id="quickAddForm" class="panel-body form-grid cols-4">
|
||||
<form id="quickAddForm" class="panel-body form-grid cols-5">
|
||||
<input name="transponder" value="${escapeHtml(quickAddDraft.transponder)}" readonly />
|
||||
<input
|
||||
name="name"
|
||||
@@ -5157,6 +5209,11 @@ function renderQuickAddPanel(session) {
|
||||
placeholder="${t(isDriver ? "drivers.name_placeholder" : "cars.name_placeholder")}"
|
||||
value="${escapeHtml(quickAddDraft.name || "")}"
|
||||
/>
|
||||
<input
|
||||
name="brand"
|
||||
placeholder="${t(isDriver ? "drivers.brand_placeholder" : "cars.brand_placeholder")}"
|
||||
value="${escapeHtml(quickAddDraft.brand || "")}"
|
||||
/>
|
||||
${
|
||||
isDriver
|
||||
? `<select name="classId">${classOptions}</select>`
|
||||
|
||||
Reference in New Issue
Block a user