Add broader search, brand exports, and denser overlay
This commit is contained in:
73
src/app.js
73
src/app.js
@@ -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 || "",
|
||||
]),
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user