export function renderTeamStintLog(session, rows, { t, escapeHtml, buildTeamStintLog, formatTeamActiveMemberLabel, renderTable, formatRaceClock }) { if (!rows.length) { return `

${t("events.no_team_results")}

`; } return `
${rows .map((row) => { const stints = buildTeamStintLog(session, row); return `
${escapeHtml(row.displayName || row.driverName)}
${t("overlay.active_member")}: ${escapeHtml(formatTeamActiveMemberLabel(row))}
${ stints.length ? renderTable( [t("events.slot"), t("table.driver"), t("table.car"), t("table.time"), t("table.duration"), t("table.laps")], stints.map( (stint) => ` ${stint.index} ${escapeHtml(stint.driverName || "-")} ${escapeHtml(stint.carName || "-")} ${new Date(stint.startTs).toLocaleTimeString()} ${formatRaceClock(stint.durationMs)} ${stint.laps} ` ) ) : `

${t("timing.no_passings")}

` }
`; }) .join("")}
`; } export function renderTeamRaceStandings(event, { t, escapeHtml, buildTeamRaceStandings, getSessionTypeLabel, renderTable, formatTeamActiveMemberLabel, formatLap, renderTeamStintLog }) { const groups = buildTeamRaceStandings(event); if (!groups.length) { return `

${t("events.no_team_results")}

`; } return groups .map( ({ session, rows }) => `

${escapeHtml(session.name)} • ${escapeHtml(getSessionTypeLabel(session.type))}

${ rows.length ? renderTable( [t("table.pos"), t("events.team_name"), t("table.laps"), t("table.result"), t("table.best_lap")], rows.map( (row, index) => ` ${index + 1}
${escapeHtml(row.displayName || row.driverName)}
${t("overlay.active_member")}: ${escapeHtml(formatTeamActiveMemberLabel(row))}
${row.laps} ${escapeHtml(row.resultDisplay)} ${formatLap(row.bestLapMs)} ` ) ) : `

${t("events.no_team_results")}

` }
${t("events.team_stint_log")}
${rows.length ? renderTeamStintLog(session, rows) : `

${t("events.no_team_results")}

`}
` ) .join(""); } export function getSessionSortWeight(session) { const order = { open_practice: 0, free_practice: 1, practice: 2, qualification: 3, heat: 4, final: 5, team_race: 6, }; return order[String(session?.type || "").toLowerCase()] || 99; } export function getDriverDisplayById(driverId, { state, t }) { const driver = state.drivers.find((item) => item.id === driverId); if (!driver) { return t("common.unknown_driver"); } return driver.transponder ? `${driver.name} (${driver.transponder})` : driver.name; } export function renderPositionGrid(session, { t, escapeHtml, getSessionGridEntries }) { const entries = getSessionGridEntries(session); if (!entries.length) { return ""; } return `

${t("events.position_grid")}

${t("timing.position_grid_hint")}

${entries .map( (entry) => `
${entry.slot}
${escapeHtml(entry.name)} ${entry.meta ? `
${escapeHtml(entry.meta)}
` : ""}
` ) .join("")}
`; } export function renderGridEditor(session, { t, escapeHtml, ensureSessionDriverOrder, state }) { if (!session) { return `

${t("events.grid_empty")}

`; } const driverIds = ensureSessionDriverOrder(session); return `
${escapeHtml(session.name)}
${t("events.grid_editor_hint")}
${t(session.gridCustomized ? "events.grid_locked" : "events.grid_unlocked")}
${driverIds .map((driverId, index) => { const driver = state.drivers.find((item) => item.id === driverId); return `
${index + 1}
${escapeHtml(driver?.name || t("common.unknown_driver"))}
${escapeHtml(driver?.transponder || "-")}
`; }) .join("")}
`; } export function getFinalMainLayouts(event, { getSessionsForEvent, getSessionGridOrder, getDriverDisplayById, t }) { const finals = getSessionsForEvent(event.id) .filter((session) => session.type === "final") .sort((left, right) => { const leftMain = String(left.name || "").match(/^([A-Z])/i)?.[1]?.toUpperCase() || "Z"; const rightMain = String(right.name || "").match(/^([A-Z])/i)?.[1]?.toUpperCase() || "Z"; if (leftMain !== rightMain) { return leftMain.localeCompare(rightMain); } return String(left.name || "").localeCompare(String(right.name || "")); }); const grouped = new Map(); finals.forEach((session) => { const mainKey = String(session.name || "").match(/^([A-Z])/i)?.[1]?.toUpperCase() || "A"; if (!grouped.has(mainKey)) { grouped.set(mainKey, []); } grouped.get(mainKey).push(session); }); return [...grouped.entries()].map(([mainKey, sessions]) => { const sortedSessions = [...sessions].sort((left, right) => String(left.name || "").localeCompare(String(right.name || ""))); const baseSession = sortedSessions[0]; const baseDriverIds = [...getSessionGridOrder(baseSession)]; const reservedSlots = Math.max(0, Number(baseSession?.reservedBumpSlots || 0) || 0); const capacity = Math.max( Number(baseSession?.maxCars || event.raceConfig?.carsPerFinal || 0) || 0, baseDriverIds.length + reservedSlots ); const slots = []; for (let index = 0; index < capacity; index += 1) { const driverId = baseDriverIds[index]; if (driverId) { slots.push({ slot: index + 1, label: getDriverDisplayById(driverId), reserved: false, }); } else if (index < baseDriverIds.length + reservedSlots) { slots.push({ slot: index + 1, label: t("events.reserved_slot"), reserved: true, }); } else { slots.push({ slot: index + 1, label: "-", reserved: false, }); } } return { mainKey, sessions: sortedSessions, slots, }; }); } export function renderFinalMatrix(event, { t, escapeHtml, getFinalMainLayouts, getStatusLabel }) { const mains = getFinalMainLayouts(event); if (!mains.length) { return `

${t("events.no_final_matrix")}

`; } return `
${mains .map( (main) => `

${t("events.main")} ${escapeHtml(main.mainKey)}

${main.sessions.length} ${escapeHtml(t("events.leg_status").toLowerCase())}
${main.slots .map( (slot) => `
${t("events.slot")} ${slot.slot} ${escapeHtml(slot.label)}
` ) .join("")}
${main.sessions .map( (session) => `
${escapeHtml(session.name)} ${escapeHtml(getStatusLabel(session.status))}
` ) .join("")}
` ) .join("")}
`; } export function buildPrintBrandBlock(branding, { escapeHtml }) { return ` `; } export function buildRaceStartListsHtml(event, { t, state, escapeHtml, resolveEventBranding, getSessionsForEvent, getSessionSortWeight, getClassName, buildPrintBrandBlock, getSessionGridEntries, getSessionTypeLabel, getStartModeLabel, renderTable }) { const branding = resolveEventBranding(event); const sessions = getSessionsForEvent(event.id) .filter((session) => session.mode === "race") .sort((left, right) => { const weightDiff = getSessionSortWeight(left) - getSessionSortWeight(right); if (weightDiff !== 0) { return weightDiff; } return String(left.name || "").localeCompare(String(right.name || "")); }); return `

${t("events.start_lists")}

${sessions .map((session) => { const entries = getSessionGridEntries(session); return ` `; }) .join("")} `; } export function buildRaceResultsHtml(event, { t, escapeHtml, resolveEventBranding, getClassName, buildPrintBrandBlock, renderRaceStandingsTableView, buildPracticeStandings, buildQualifyingStandings, buildFinalStandings, renderTeamRaceStandings }) { const branding = resolveEventBranding(event); return ` `; } export function buildTeamRaceResultsHtml(event, { t, escapeHtml, resolveEventBranding, getClassName, buildPrintBrandBlock, buildTeamRaceStandings, renderTable, formatLap, renderTeamStintLog }) { const branding = resolveEventBranding(event); const groups = buildTeamRaceStandings(event); return ` ${groups .map( ({ session, rows }) => ` ` ) .join("")} `; }