Add broader search, brand exports, and denser overlay

This commit is contained in:
larssand
2026-03-19 20:38:58 +01:00
parent e37aeff43a
commit fb9dcf1b9c
4 changed files with 86 additions and 61 deletions

View File

@@ -87,14 +87,14 @@ const TRANSLATIONS = {
"drivers.create": "Skapa förare",
"drivers.name_placeholder": "Förarnamn",
"drivers.brand_placeholder": "Team / märke (valfritt)",
"drivers.brand_filter_placeholder": "Filtrera på team / märke",
"drivers.brand_filter_placeholder": "Sök namn / transponder / brand",
"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.brand_filter_placeholder": "Filtrera på märke / modell",
"cars.brand_filter_placeholder": "Sök namn / transponder / brand",
"cars.transponder_placeholder": "Bilens transponder",
"cars.add": "Lägg till bil",
"cars.title": "Bilar",
@@ -729,14 +729,14 @@ const TRANSLATIONS = {
"drivers.create": "Create Driver",
"drivers.name_placeholder": "Driver name",
"drivers.brand_placeholder": "Team / brand (optional)",
"drivers.brand_filter_placeholder": "Filter by team / brand",
"drivers.brand_filter_placeholder": "Search name / transponder / brand",
"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.brand_filter_placeholder": "Filter by brand / model",
"cars.brand_filter_placeholder": "Search name / transponder / brand",
"cars.transponder_placeholder": "Car transponder",
"cars.add": "Add Car",
"cars.title": "Cars",
@@ -2841,10 +2841,12 @@ function renderDrivers() {
const classOptions = state.classes
.map((c) => `<option value="${c.id}">${escapeHtml(c.name)}</option>`)
.join("");
const driverSearch = driverBrandFilter.trim().toLowerCase();
const filteredDrivers = state.drivers.filter((driver) =>
String(driver.brand || "")
.toLowerCase()
.includes(driverBrandFilter.trim().toLowerCase())
!driverSearch ||
[driver.name, driver.transponder, driver.brand]
.map((value) => String(value || "").toLowerCase())
.some((value) => value.includes(driverSearch))
);
const editingDriver = state.drivers.find((driver) => driver.id === selectedDriverEditId) || null;
@@ -3013,10 +3015,12 @@ function renderDrivers() {
}
function renderCars() {
const carSearch = carBrandFilter.trim().toLowerCase();
const filteredCars = state.cars.filter((car) =>
String(car.brand || "")
.toLowerCase()
.includes(carBrandFilter.trim().toLowerCase())
!carSearch ||
[car.name, car.transponder, car.brand]
.map((value) => String(value || "").toLowerCase())
.some((value) => value.includes(carSearch))
);
const editingCar = state.cars.find((car) => car.id === selectedCarEditId) || null;
dom.view.innerHTML = `
@@ -5910,10 +5914,18 @@ function renderOverlayLeaderboard(rows) {
</div>
</div>
</div>
<div class="overlay-race-metric">
<label>${t("table.laps")}</label>
<strong>${row.laps ?? 0}</strong>
</div>
<div class="overlay-race-metric">
<label>${t("table.result")}</label>
<strong>${escapeHtml(row.resultDisplay)}</strong>
</div>
<div class="overlay-race-metric">
<label>${t("table.gap")}</label>
<strong>${escapeHtml(row.gapDisplay || row.gapAhead || "-")}</strong>
</div>
<div class="overlay-race-metric">
<label>${t("table.ahead_gap")}</label>
<strong>${escapeHtml(row.gapAhead || "-")}</strong>
@@ -8463,16 +8475,18 @@ function buildRaceStartListsHtml(event) {
${
entries.length
? renderTable(
[t("events.slot"), t("table.driver"), t("table.transponder")],
entries.map(
(entry) => `
[t("events.slot"), t("table.driver"), t("table.brand"), t("table.transponder")],
entries.map((entry) => {
const driver = state.drivers.find((item) => item.id === entry.id);
return `
<tr>
<td>${entry.slot}</td>
<td>${escapeHtml(entry.name)}</td>
<td>${escapeHtml(driver?.brand || "-")}</td>
<td>${escapeHtml(entry.meta || "-")}</td>
</tr>
`
)
`;
})
)
: `<p>${t("common.no_entries")}</p>`
}
@@ -8684,8 +8698,11 @@ async function exportRaceStartListsPdf(event) {
const sections = sessions.map((session) =>
buildPdfSection(
`${session.name}${getSessionTypeLabel(session.type)}`,
[t("events.slot"), t("table.driver"), t("table.transponder")],
getSessionGridEntries(session).map((entry) => [String(entry.slot), entry.name, entry.meta || "-"])
[t("events.slot"), t("table.driver"), t("table.brand"), t("table.transponder")],
getSessionGridEntries(session).map((entry) => {
const driver = state.drivers.find((item) => item.id === entry.id);
return [String(entry.slot), entry.name, driver?.brand || "-", entry.meta || "-"];
})
)
);
@@ -8815,8 +8832,11 @@ async function exportSessionHeatSheetPdf(session) {
sections: [
buildPdfSection(
`${session.name}${getSessionTypeLabel(session.type)}`,
[t("events.slot"), t("table.driver"), t("table.transponder")],
getSessionGridEntries(session).map((entry) => [String(entry.slot), entry.name, entry.meta || "-"])
[t("events.slot"), t("table.driver"), t("table.brand"), t("table.transponder")],
getSessionGridEntries(session).map((entry) => {
const driver = state.drivers.find((item) => item.id === entry.id);
return [String(entry.slot), entry.name, driver?.brand || "-", entry.meta || "-"];
})
),
],
});
@@ -8848,16 +8868,18 @@ function buildSessionHeatSheetHtml(session) {
${
entries.length
? renderTable(
[t("events.slot"), t("table.driver"), t("table.transponder")],
entries.map(
(entry) => `
[t("events.slot"), t("table.driver"), t("table.brand"), t("table.transponder")],
entries.map((entry) => {
const driver = state.drivers.find((item) => item.id === entry.id);
return `
<tr>
<td>${entry.slot}</td>
<td>${escapeHtml(entry.name)}</td>
<td>${escapeHtml(driver?.brand || "-")}</td>
<td>${escapeHtml(entry.meta || "-")}</td>
</tr>
`
)
`;
})
)
: `<p>${t("common.no_entries")}</p>`
}
@@ -8868,7 +8890,7 @@ function exportSessionHeatSheet(session) {
const event = state.events.find((item) => item.id === session.eventId);
const entries = getSessionGridEntries(session);
const rows = [
["event", "class", "session", "type", "start_mode", "duration_min", "slot", "driver", "transponder"],
["event", "class", "session", "type", "start_mode", "duration_min", "slot", "driver", "brand", "transponder"],
...entries.map((entry) => [
event?.name || "",
getClassName(event?.classId || ""),
@@ -8878,6 +8900,7 @@ function exportSessionHeatSheet(session) {
String(session.durationMin || ""),
String(entry.slot),
entry.name,
state.drivers.find((item) => item.id === entry.id)?.brand || "",
entry.meta || "",
]),
];