fixa adda drivers

This commit is contained in:
larssand
2026-03-14 20:17:28 +01:00
parent 3119d98bb6
commit da1e60fe52
2 changed files with 187 additions and 2 deletions

View File

@@ -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() {
</div>
</section>
${active ? renderQuickAddPanel(active) : ""}
<section class="panel mt-16">
<div class="panel-header"><h3>${t("timing.speaker_panel")}</h3></div>
<div class="panel-body">
@@ -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) => `<option value="${item.id}" ${item.id === (quickAddDraft.classId || getPreferredClassId(session)) ? "selected" : ""}>${escapeHtml(item.name)}</option>`
)
.join("");
const isDriver = quickAddDraft.type === "driver";
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">
<input name="transponder" value="${escapeHtml(quickAddDraft.transponder)}" readonly />
<input
name="name"
required
autofocus
placeholder="${t(isDriver ? "drivers.name_placeholder" : "cars.name_placeholder")}"
value="${escapeHtml(quickAddDraft.name || "")}"
/>
${
isDriver
? `<select name="classId">${classOptions}</select>`
: `<div class="hint quick-add-spacer">${t("timing.quick_add_hint")}</div>`
}
<div class="actions-inline">
<button class="btn btn-primary" type="submit">${t("common.save")}</button>
<button class="btn" id="quickAddCancel" type="button">${t("common.cancel")}</button>
</div>
</form>
</section>
`;
}
function renderGuide() {
dom.view.innerHTML = `
<section class="panel">
@@ -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 `
<div class="actions-inline quick-add-actions">
${!quickState.hasDriver ? `<button class="btn btn-mini" id="${idPrefix}-add-driver">${t("timing.add_driver")}</button>` : ""}
${!quickState.hasCar ? `<button class="btn btn-mini" id="${idPrefix}-add-car">${t("timing.add_car")}</button>` : ""}
</div>
`;
}
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) {
<div class="panel-body">
<p><strong>${escapeHtml(row.driverName)}</strong> • ${escapeHtml(row.carName)}</p>
<p>${t("table.transponder")}: ${escapeHtml(row.transponder)}</p>
${renderQuickAddActions(session, row.transponder, "leaderboardModal")}
<p>${t("table.laps")}: ${row.laps}</p>
<p>${t("timing.total_time")}: ${escapeHtml(row.resultDisplay)}</p>
<p>${t("table.best_lap")}: ${formatLap(row.bestLapMs)}</p>
@@ -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 `
<tr>
<td>${new Date(p.timestamp).toLocaleTimeString()}</td>
@@ -3853,6 +4027,7 @@ function renderRecentPassings(session) {
<td>${escapeHtml(p.carName || "-")}</td>
<td>${escapeHtml(p.loopId || "-")}</td>
<td>${p.strength ?? "-"}</td>
<td>${renderQuickAddActions(session, p.transponder, `recentPassing-${index}`)}</td>
</tr>
`;
})

View File

@@ -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));