Extract judging and correction logic into module
This commit is contained in:
235
src/app.js
235
src/app.js
@@ -8,6 +8,8 @@ import { normalizeRaceTeam as normalizeRaceTeamLogic, normalizeStoredRacePreset
|
||||
|
||||
import { getSessionTypeLabel as getSessionTypeLabelLogic, getStatusLabel as getStatusLabelLogic, isUntimedSession as isUntimedSessionLogic, getActiveSession as getActiveSessionLogic, getSessionTargetMs as getSessionTargetMsLogic, getSessionLapWindow as getSessionLapWindowLogic, isCountedPassing as isCountedPassingLogic, getVisiblePassings as getVisiblePassingsLogic, getPassingValidationLabel as getPassingValidationLabelLogic, getSessionTiming as getSessionTimingLogic, ensureSessionResult as ensureSessionResultLogic, buildLeaderboard as buildLeaderboardLogic, formatLapDelta as formatLapDeltaLogic, formatLeaderboardGap as formatLeaderboardGapLogic, getCompetitorElapsedMs as getCompetitorElapsedMsLogic, getCompetitorPassings as getCompetitorPassingsLogic, getCompetitorSeedMetric as getCompetitorSeedMetricLogic, getSessionEntrants as getSessionEntrantsLogic, buildPracticeStandings as buildPracticeStandingsLogic, getQualifyingPointsValue as getQualifyingPointsValueLogic, isHighPointsTable as isHighPointsTableLogic, compareNumberSet as compareNumberSetLogic, buildQualifyingTieBreakNote as buildQualifyingTieBreakNoteLogic, hasQualifyingPrimaryTie as hasQualifyingPrimaryTieLogic, buildQualifyingStandings as buildQualifyingStandingsLogic, formatTeamActiveMemberLabel as formatTeamActiveMemberLabelLogic, buildTeamRaceStandings as buildTeamRaceStandingsLogic, buildTeamStintLog as buildTeamStintLogLogic, getSessionGridEntries as getSessionGridEntriesLogic, getSessionGridOrder as getSessionGridOrderLogic, ensureSessionDriverOrder as ensureSessionDriverOrderLogic, buildFinalStandings as buildFinalStandingsLogic } from "./timing_logic.js";
|
||||
|
||||
import { getManualCorrectionSummary as getManualCorrectionSummaryLogic, applyCompetitorCorrection as applyCompetitorCorrectionLogic, recalculateCompetitorFromPassings as recalculateCompetitorFromPassingsLogic, invalidateCompetitorLastLap as invalidateCompetitorLastLapLogic, restoreCompetitorLastInvalidLap as restoreCompetitorLastInvalidLapLogic, findPassingByUndoMarker as findPassingByUndoMarkerLogic, undoJudgingAdjustment as undoJudgingAdjustmentLogic, getJudgeFilteredRows as getJudgeFilteredRowsLogic, getJudgeFilteredLog as getJudgeFilteredLogLogic } from "./judging_logic.js";
|
||||
|
||||
const renderRaceFormatFieldView = (labelKey, hintKey, controlHtml, options = {}) => renderRaceFormatField(labelKey, hintKey, controlHtml, options, { t });
|
||||
const renderRaceFormatContextCardView = (titleKey, hintKey) => renderRaceFormatContextCard(titleKey, hintKey, { t });
|
||||
const renderManageStatusBadgeView = (status) => renderManageStatusBadge(status, { t });
|
||||
@@ -77,6 +79,16 @@ const getSessionGridOrder = (session) => getSessionGridOrderLogic(session);
|
||||
const ensureSessionDriverOrder = (session) => ensureSessionDriverOrderLogic(session, { getSessionEntrants });
|
||||
const buildFinalStandings = (event) => buildFinalStandingsLogic(event, { getSessionsForEvent, buildLeaderboard });
|
||||
|
||||
const getManualCorrectionSummary = (row) => getManualCorrectionSummaryLogic(row, { formatLap });
|
||||
const applyCompetitorCorrection = (session, row, options = {}) => applyCompetitorCorrectionLogic(session, row, options, { ensureSessionResult, uid, t, formatLap, saveState });
|
||||
const recalculateCompetitorFromPassings = (session, rowKey) => recalculateCompetitorFromPassingsLogic(session, rowKey, { ensureSessionResult, getCompetitorPassings, isCountedPassing });
|
||||
const invalidateCompetitorLastLap = (session, row) => invalidateCompetitorLastLapLogic(session, row, { ensureSessionResult, getCompetitorPassings, isCountedPassing, recalculateCompetitorFromPassings, uid, t, formatLap, saveState });
|
||||
const restoreCompetitorLastInvalidLap = (session, row) => restoreCompetitorLastInvalidLapLogic(session, row, { ensureSessionResult, getCompetitorPassings, recalculateCompetitorFromPassings, uid, t, formatLap, saveState });
|
||||
const findPassingByUndoMarker = (session, rowKey, passingTimestamp) => findPassingByUndoMarkerLogic(session, rowKey, passingTimestamp, { ensureSessionResult });
|
||||
const undoJudgingAdjustment = (session, adjustmentId) => undoJudgingAdjustmentLogic(session, adjustmentId, { ensureSessionResult, findPassingByUndoMarker, recalculateCompetitorFromPassings, uid, t, saveState });
|
||||
const getJudgeFilteredRows = (rows, filterValue) => getJudgeFilteredRowsLogic(rows, filterValue);
|
||||
const getJudgeFilteredLog = (adjustments, filterValue) => getJudgeFilteredLogLogic(adjustments, filterValue);
|
||||
|
||||
const NAV_ITEMS = [
|
||||
{ id: "dashboard", titleKey: "nav.dashboard", subtitleKey: "nav.dashboard_sub" },
|
||||
{ id: "events", titleKey: "nav.events", subtitleKey: "nav.events_sub" },
|
||||
@@ -6902,229 +6914,6 @@ function getEventName(eventId) {
|
||||
return state.events.find((x) => x.id === eventId)?.name || t("common.unknown_event");
|
||||
}
|
||||
|
||||
function getManualCorrectionSummary(row) {
|
||||
const laps = Number(row?.manualLapAdjustment || 0) || 0;
|
||||
const timeMs = Number(row?.manualTimeAdjustmentMs || 0) || 0;
|
||||
const bits = [];
|
||||
if (laps) {
|
||||
bits.push(`${laps > 0 ? "+" : ""}${laps}L`);
|
||||
}
|
||||
if (timeMs) {
|
||||
bits.push(`${timeMs > 0 ? "+" : "-"}${formatLap(Math.abs(timeMs))}`);
|
||||
}
|
||||
return bits.join(" • ");
|
||||
}
|
||||
|
||||
function applyCompetitorCorrection(session, row, options = {}) {
|
||||
const result = ensureSessionResult(session.id);
|
||||
const entry = result.competitors[row.key];
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
if (options.reset) {
|
||||
const previousLapAdjustment = Number(entry.manualLapAdjustment || 0) || 0;
|
||||
const previousTimeAdjustmentMs = Number(entry.manualTimeAdjustmentMs || 0) || 0;
|
||||
entry.manualLapAdjustment = 0;
|
||||
entry.manualTimeAdjustmentMs = 0;
|
||||
result.adjustments.push({
|
||||
id: uid("judge"),
|
||||
ts: Date.now(),
|
||||
competitorKey: row.key,
|
||||
displayName: row.displayName || row.driverName || t("common.unknown_driver"),
|
||||
action: t("timing.penalty_reset"),
|
||||
detail: "-",
|
||||
category: "correction",
|
||||
undo: {
|
||||
type: "restore_correction_state",
|
||||
previousLapAdjustment,
|
||||
previousTimeAdjustmentMs,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const lapDelta = Number(options.lapDelta || 0) || 0;
|
||||
const timeMsDelta = Number(options.timeMsDelta || 0) || 0;
|
||||
const previousLapAdjustment = Number(entry.manualLapAdjustment || 0) || 0;
|
||||
const previousTimeAdjustmentMs = Number(entry.manualTimeAdjustmentMs || 0) || 0;
|
||||
entry.manualLapAdjustment = (Number(entry.manualLapAdjustment || 0) || 0) + lapDelta;
|
||||
entry.manualTimeAdjustmentMs = (Number(entry.manualTimeAdjustmentMs || 0) || 0) + timeMsDelta;
|
||||
const detailBits = [];
|
||||
if (lapDelta) {
|
||||
detailBits.push(`${lapDelta > 0 ? "+" : ""}${lapDelta}L`);
|
||||
}
|
||||
if (timeMsDelta) {
|
||||
detailBits.push(`${timeMsDelta > 0 ? "+" : "-"}${formatLap(Math.abs(timeMsDelta))}`);
|
||||
}
|
||||
result.adjustments.push({
|
||||
id: uid("judge"),
|
||||
ts: Date.now(),
|
||||
competitorKey: row.key,
|
||||
displayName: row.displayName || row.driverName || t("common.unknown_driver"),
|
||||
action: t("timing.manual_corrections"),
|
||||
detail: detailBits.join(" • ") || "-",
|
||||
category: "correction",
|
||||
undo: {
|
||||
type: "restore_correction_state",
|
||||
previousLapAdjustment,
|
||||
previousTimeAdjustmentMs,
|
||||
},
|
||||
});
|
||||
}
|
||||
saveState();
|
||||
}
|
||||
|
||||
function recalculateCompetitorFromPassings(session, rowKey) {
|
||||
const result = ensureSessionResult(session.id);
|
||||
const entry = result.competitors[rowKey];
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
const passings = getCompetitorPassings(session, entry, { includeInvalid: true });
|
||||
const validPassings = passings.filter((passing) => isCountedPassing(passing));
|
||||
entry.laps = validPassings.length;
|
||||
entry.lastLapMs = validPassings.length ? Number(validPassings[validPassings.length - 1].lapMs || 0) || null : null;
|
||||
entry.lastTimestamp = validPassings.length
|
||||
? Number(validPassings[validPassings.length - 1].timestamp || 0) || entry.startTimestamp || session.startedAt || null
|
||||
: entry.startTimestamp || session.startedAt || null;
|
||||
const bestLapCandidates = validPassings.map((passing) => Number(passing.lapMs || 0)).filter((lapMs) => lapMs > 500);
|
||||
entry.bestLapMs = bestLapCandidates.length ? Math.min(...bestLapCandidates) : null;
|
||||
}
|
||||
|
||||
function invalidateCompetitorLastLap(session, row) {
|
||||
const result = ensureSessionResult(session.id);
|
||||
const entry = result.competitors[row.key];
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const passings = getCompetitorPassings(session, row, { includeInvalid: true });
|
||||
const target = [...passings].reverse().find((passing) => isCountedPassing(passing));
|
||||
if (!target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
target.validLap = false;
|
||||
target.invalidReason = "manual_invalid";
|
||||
recalculateCompetitorFromPassings(session, row.key);
|
||||
result.adjustments.push({
|
||||
id: uid("judge"),
|
||||
ts: Date.now(),
|
||||
competitorKey: row.key,
|
||||
displayName: row.displayName || row.driverName || t("common.unknown_driver"),
|
||||
action: t("timing.invalidate_last_lap"),
|
||||
detail: formatLap(Number(target.lapMs || 0) || 0),
|
||||
category: "invalid",
|
||||
undo: {
|
||||
type: "set_passing_validity",
|
||||
passingTimestamp: Number(target.timestamp || 0) || 0,
|
||||
validLap: true,
|
||||
invalidReason: "",
|
||||
},
|
||||
});
|
||||
saveState();
|
||||
return true;
|
||||
}
|
||||
|
||||
function restoreCompetitorLastInvalidLap(session, row) {
|
||||
const result = ensureSessionResult(session.id);
|
||||
const passings = getCompetitorPassings(session, row, { includeInvalid: true });
|
||||
const target = [...passings].reverse().find((passing) => passing.validLap === false && passing.invalidReason === "manual_invalid");
|
||||
if (!target) {
|
||||
return false;
|
||||
}
|
||||
target.validLap = true;
|
||||
target.invalidReason = "";
|
||||
recalculateCompetitorFromPassings(session, row.key);
|
||||
result.adjustments.push({
|
||||
id: uid("judge"),
|
||||
ts: Date.now(),
|
||||
competitorKey: row.key,
|
||||
displayName: row.displayName || row.driverName || t("common.unknown_driver"),
|
||||
action: t("timing.restore_last_invalid"),
|
||||
detail: formatLap(Number(target.lapMs || 0) || 0),
|
||||
category: "invalid",
|
||||
undo: {
|
||||
type: "set_passing_validity",
|
||||
passingTimestamp: Number(target.timestamp || 0) || 0,
|
||||
validLap: false,
|
||||
invalidReason: "manual_invalid",
|
||||
},
|
||||
});
|
||||
saveState();
|
||||
return true;
|
||||
}
|
||||
|
||||
function findPassingByUndoMarker(session, rowKey, passingTimestamp) {
|
||||
const result = ensureSessionResult(session.id);
|
||||
return result.passings.find((passing) => passing.competitorKey === rowKey && Number(passing.timestamp || 0) === Number(passingTimestamp || 0)) || null;
|
||||
}
|
||||
|
||||
function undoJudgingAdjustment(session, adjustmentId) {
|
||||
const result = ensureSessionResult(session.id);
|
||||
const adjustment = (result.adjustments || []).find((entry) => entry.id === adjustmentId && !entry.undoneAt);
|
||||
if (!adjustment || !adjustment.undo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const entry = result.competitors[adjustment.competitorKey];
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (adjustment.undo.type === "restore_correction_state") {
|
||||
entry.manualLapAdjustment = Number(adjustment.undo.previousLapAdjustment || 0) || 0;
|
||||
entry.manualTimeAdjustmentMs = Number(adjustment.undo.previousTimeAdjustmentMs || 0) || 0;
|
||||
} else if (adjustment.undo.type === "set_passing_validity") {
|
||||
const passing = findPassingByUndoMarker(session, adjustment.competitorKey, adjustment.undo.passingTimestamp);
|
||||
if (!passing) {
|
||||
return false;
|
||||
}
|
||||
passing.validLap = adjustment.undo.validLap !== false;
|
||||
passing.invalidReason = adjustment.undo.validLap === false ? adjustment.undo.invalidReason || "manual_invalid" : "";
|
||||
recalculateCompetitorFromPassings(session, adjustment.competitorKey);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
adjustment.undoneAt = Date.now();
|
||||
result.adjustments.push({
|
||||
id: uid("judge"),
|
||||
ts: Date.now(),
|
||||
competitorKey: adjustment.competitorKey,
|
||||
displayName: adjustment.displayName || t("common.unknown_driver"),
|
||||
action: t("judging.undo_action"),
|
||||
detail: adjustment.action || "-",
|
||||
category: "undo",
|
||||
});
|
||||
saveState();
|
||||
return true;
|
||||
}
|
||||
|
||||
function getJudgeFilteredRows(rows, filterValue) {
|
||||
if (filterValue === "invalid") {
|
||||
return rows.filter((row) => row.invalidPending);
|
||||
}
|
||||
if (filterValue === "corrected") {
|
||||
return rows.filter((row) => (Number(row.manualLapAdjustment || 0) || 0) !== 0 || (Number(row.manualTimeAdjustmentMs || 0) || 0) !== 0);
|
||||
}
|
||||
if (filterValue === "team") {
|
||||
return rows.filter((row) => Boolean(row.teamId));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
function getJudgeFilteredLog(adjustments, filterValue) {
|
||||
if (filterValue === "corrections") {
|
||||
return adjustments.filter((entry) => entry.category === "correction");
|
||||
}
|
||||
if (filterValue === "invalid") {
|
||||
return adjustments.filter((entry) => entry.category === "invalid");
|
||||
}
|
||||
if (filterValue === "undo") {
|
||||
return adjustments.filter((entry) => entry.category === "undo");
|
||||
}
|
||||
return adjustments;
|
||||
}
|
||||
|
||||
function getFreePracticeSessions(eventId) {
|
||||
return getSessionsForEvent(eventId).filter((session) => session.type === "free_practice");
|
||||
}
|
||||
|
||||
222
src/judging_logic.js
Normal file
222
src/judging_logic.js
Normal file
@@ -0,0 +1,222 @@
|
||||
export function getManualCorrectionSummary(row, { formatLap }) {
|
||||
const laps = Number(row?.manualLapAdjustment || 0) || 0;
|
||||
const timeMs = Number(row?.manualTimeAdjustmentMs || 0) || 0;
|
||||
const bits = [];
|
||||
if (laps) {
|
||||
bits.push(`${laps > 0 ? "+" : ""}${laps}L`);
|
||||
}
|
||||
if (timeMs) {
|
||||
bits.push(`${timeMs > 0 ? "+" : "-"}${formatLap(Math.abs(timeMs))}`);
|
||||
}
|
||||
return bits.join(" • ");
|
||||
}
|
||||
|
||||
export function applyCompetitorCorrection(session, row, options = {}, { ensureSessionResult, uid, t, formatLap, saveState }) {
|
||||
const result = ensureSessionResult(session.id);
|
||||
const entry = result.competitors[row.key];
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
if (options.reset) {
|
||||
const previousLapAdjustment = Number(entry.manualLapAdjustment || 0) || 0;
|
||||
const previousTimeAdjustmentMs = Number(entry.manualTimeAdjustmentMs || 0) || 0;
|
||||
entry.manualLapAdjustment = 0;
|
||||
entry.manualTimeAdjustmentMs = 0;
|
||||
result.adjustments.push({
|
||||
id: uid("judge"),
|
||||
ts: Date.now(),
|
||||
competitorKey: row.key,
|
||||
displayName: row.displayName || row.driverName || t("common.unknown_driver"),
|
||||
action: t("timing.penalty_reset"),
|
||||
detail: "-",
|
||||
category: "correction",
|
||||
undo: {
|
||||
type: "restore_correction_state",
|
||||
previousLapAdjustment,
|
||||
previousTimeAdjustmentMs,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const lapDelta = Number(options.lapDelta || 0) || 0;
|
||||
const timeMsDelta = Number(options.timeMsDelta || 0) || 0;
|
||||
const previousLapAdjustment = Number(entry.manualLapAdjustment || 0) || 0;
|
||||
const previousTimeAdjustmentMs = Number(entry.manualTimeAdjustmentMs || 0) || 0;
|
||||
entry.manualLapAdjustment = (Number(entry.manualLapAdjustment || 0) || 0) + lapDelta;
|
||||
entry.manualTimeAdjustmentMs = (Number(entry.manualTimeAdjustmentMs || 0) || 0) + timeMsDelta;
|
||||
const detailBits = [];
|
||||
if (lapDelta) {
|
||||
detailBits.push(`${lapDelta > 0 ? "+" : ""}${lapDelta}L`);
|
||||
}
|
||||
if (timeMsDelta) {
|
||||
detailBits.push(`${timeMsDelta > 0 ? "+" : "-"}${formatLap(Math.abs(timeMsDelta))}`);
|
||||
}
|
||||
result.adjustments.push({
|
||||
id: uid("judge"),
|
||||
ts: Date.now(),
|
||||
competitorKey: row.key,
|
||||
displayName: row.displayName || row.driverName || t("common.unknown_driver"),
|
||||
action: t("timing.manual_corrections"),
|
||||
detail: detailBits.join(" • ") || "-",
|
||||
category: "correction",
|
||||
undo: {
|
||||
type: "restore_correction_state",
|
||||
previousLapAdjustment,
|
||||
previousTimeAdjustmentMs,
|
||||
},
|
||||
});
|
||||
}
|
||||
saveState();
|
||||
}
|
||||
|
||||
export function recalculateCompetitorFromPassings(session, rowKey, { ensureSessionResult, getCompetitorPassings, isCountedPassing }) {
|
||||
const result = ensureSessionResult(session.id);
|
||||
const entry = result.competitors[rowKey];
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
const passings = getCompetitorPassings(session, entry, { includeInvalid: true });
|
||||
const validPassings = passings.filter((passing) => isCountedPassing(passing));
|
||||
entry.laps = validPassings.length;
|
||||
entry.lastLapMs = validPassings.length ? Number(validPassings[validPassings.length - 1].lapMs || 0) || null : null;
|
||||
entry.lastTimestamp = validPassings.length
|
||||
? Number(validPassings[validPassings.length - 1].timestamp || 0) || entry.startTimestamp || session.startedAt || null
|
||||
: entry.startTimestamp || session.startedAt || null;
|
||||
const bestLapCandidates = validPassings.map((passing) => Number(passing.lapMs || 0)).filter((lapMs) => lapMs > 500);
|
||||
entry.bestLapMs = bestLapCandidates.length ? Math.min(...bestLapCandidates) : null;
|
||||
}
|
||||
|
||||
export function invalidateCompetitorLastLap(session, row, { ensureSessionResult, getCompetitorPassings, isCountedPassing, recalculateCompetitorFromPassings, uid, t, formatLap, saveState }) {
|
||||
const result = ensureSessionResult(session.id);
|
||||
const entry = result.competitors[row.key];
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const passings = getCompetitorPassings(session, row, { includeInvalid: true });
|
||||
const target = [...passings].reverse().find((passing) => isCountedPassing(passing));
|
||||
if (!target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
target.validLap = false;
|
||||
target.invalidReason = "manual_invalid";
|
||||
recalculateCompetitorFromPassings(session, row.key);
|
||||
result.adjustments.push({
|
||||
id: uid("judge"),
|
||||
ts: Date.now(),
|
||||
competitorKey: row.key,
|
||||
displayName: row.displayName || row.driverName || t("common.unknown_driver"),
|
||||
action: t("timing.invalidate_last_lap"),
|
||||
detail: formatLap(Number(target.lapMs || 0) || 0),
|
||||
category: "invalid",
|
||||
undo: {
|
||||
type: "set_passing_validity",
|
||||
passingTimestamp: Number(target.timestamp || 0) || 0,
|
||||
validLap: true,
|
||||
invalidReason: "",
|
||||
},
|
||||
});
|
||||
saveState();
|
||||
return true;
|
||||
}
|
||||
|
||||
export function restoreCompetitorLastInvalidLap(session, row, { ensureSessionResult, getCompetitorPassings, recalculateCompetitorFromPassings, uid, t, formatLap, saveState }) {
|
||||
const result = ensureSessionResult(session.id);
|
||||
const passings = getCompetitorPassings(session, row, { includeInvalid: true });
|
||||
const target = [...passings].reverse().find((passing) => passing.validLap === false && passing.invalidReason === "manual_invalid");
|
||||
if (!target) {
|
||||
return false;
|
||||
}
|
||||
target.validLap = true;
|
||||
target.invalidReason = "";
|
||||
recalculateCompetitorFromPassings(session, row.key);
|
||||
result.adjustments.push({
|
||||
id: uid("judge"),
|
||||
ts: Date.now(),
|
||||
competitorKey: row.key,
|
||||
displayName: row.displayName || row.driverName || t("common.unknown_driver"),
|
||||
action: t("timing.restore_last_invalid"),
|
||||
detail: formatLap(Number(target.lapMs || 0) || 0),
|
||||
category: "invalid",
|
||||
undo: {
|
||||
type: "set_passing_validity",
|
||||
passingTimestamp: Number(target.timestamp || 0) || 0,
|
||||
validLap: false,
|
||||
invalidReason: "manual_invalid",
|
||||
},
|
||||
});
|
||||
saveState();
|
||||
return true;
|
||||
}
|
||||
|
||||
export function findPassingByUndoMarker(session, rowKey, passingTimestamp, { ensureSessionResult }) {
|
||||
const result = ensureSessionResult(session.id);
|
||||
return result.passings.find((passing) => passing.competitorKey === rowKey && Number(passing.timestamp || 0) === Number(passingTimestamp || 0)) || null;
|
||||
}
|
||||
|
||||
export function undoJudgingAdjustment(session, adjustmentId, { ensureSessionResult, findPassingByUndoMarker, recalculateCompetitorFromPassings, uid, t, saveState }) {
|
||||
const result = ensureSessionResult(session.id);
|
||||
const adjustment = (result.adjustments || []).find((entry) => entry.id === adjustmentId && !entry.undoneAt);
|
||||
if (!adjustment || !adjustment.undo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const entry = result.competitors[adjustment.competitorKey];
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (adjustment.undo.type === "restore_correction_state") {
|
||||
entry.manualLapAdjustment = Number(adjustment.undo.previousLapAdjustment || 0) || 0;
|
||||
entry.manualTimeAdjustmentMs = Number(adjustment.undo.previousTimeAdjustmentMs || 0) || 0;
|
||||
} else if (adjustment.undo.type === "set_passing_validity") {
|
||||
const passing = findPassingByUndoMarker(session, adjustment.competitorKey, adjustment.undo.passingTimestamp);
|
||||
if (!passing) {
|
||||
return false;
|
||||
}
|
||||
passing.validLap = adjustment.undo.validLap !== false;
|
||||
passing.invalidReason = adjustment.undo.validLap === false ? adjustment.undo.invalidReason || "manual_invalid" : "";
|
||||
recalculateCompetitorFromPassings(session, adjustment.competitorKey);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
adjustment.undoneAt = Date.now();
|
||||
result.adjustments.push({
|
||||
id: uid("judge"),
|
||||
ts: Date.now(),
|
||||
competitorKey: adjustment.competitorKey,
|
||||
displayName: adjustment.displayName || t("common.unknown_driver"),
|
||||
action: t("judging.undo_action"),
|
||||
detail: adjustment.action || "-",
|
||||
category: "undo",
|
||||
});
|
||||
saveState();
|
||||
return true;
|
||||
}
|
||||
|
||||
export function getJudgeFilteredRows(rows, filterValue) {
|
||||
if (filterValue === "invalid") {
|
||||
return rows.filter((row) => row.invalidPending);
|
||||
}
|
||||
if (filterValue === "corrected") {
|
||||
return rows.filter((row) => (Number(row.manualLapAdjustment || 0) || 0) !== 0 || (Number(row.manualTimeAdjustmentMs || 0) || 0) !== 0);
|
||||
}
|
||||
if (filterValue === "team") {
|
||||
return rows.filter((row) => Boolean(row.teamId));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
export function getJudgeFilteredLog(adjustments, filterValue) {
|
||||
if (filterValue === "corrections") {
|
||||
return adjustments.filter((entry) => entry.category === "correction");
|
||||
}
|
||||
if (filterValue === "invalid") {
|
||||
return adjustments.filter((entry) => entry.category === "invalid");
|
||||
}
|
||||
if (filterValue === "undo") {
|
||||
return adjustments.filter((entry) => entry.category === "undo");
|
||||
}
|
||||
return adjustments;
|
||||
}
|
||||
Reference in New Issue
Block a user