Extract event and race workspace markup into module
This commit is contained in:
144
src/app.js
144
src/app.js
@@ -12,6 +12,7 @@ import { renderDashboardView, renderClassesView, renderDriversView, renderCarsVi
|
|||||||
|
|
||||||
import { renderGuideView, renderOverlayPageView } from "./misc_views.js";
|
import { renderGuideView, renderOverlayPageView } from "./misc_views.js";
|
||||||
import { getSessionsForEventHelper, getModeLabelHelper, normalizeStartModeHelper, getStartModeLabelHelper, getClassNameHelper, getEventNameHelper, renderAssignmentListView, renderSessionsTableView } from "./event_common.js";
|
import { getSessionsForEventHelper, getModeLabelHelper, normalizeStartModeHelper, getStartModeLabelHelper, getClassNameHelper, getEventNameHelper, renderAssignmentListView, renderSessionsTableView } from "./event_common.js";
|
||||||
|
import { renderEventWorkspaceMarkup } from "./event_views.js";
|
||||||
import { createDefaultAmmcConfigHelper, getManagedWsUrlHelper, loadAmmcConfigFromBackendHelper, saveAmmcConfigToBackendHelper, refreshAmmcStatusHelper, startManagedAmmcHelper, stopManagedAmmcHelper, applyPersistedStateHelper, hydrateFromBackendHelper, scheduleBackendSyncHelper, syncStateToBackendHelper, pingBackendHelper, checkAppVersionHelper, startAppVersionPollingHelper, startOverlaySyncHelper, startOverlayRotationHelper, startOverlayLiveRefreshHelper, ensureAudioContextHelper, playPassingBeepHelper, playFinishSirenHelper, playLeaderCueHelper, playStartCueHelper, playBestLapCueHelper, pushOverlayEventHelper, speakTextHelper, announcePassingHelper, announceRaceFinishedHelper, handleSessionTimerTickHelper, tickClockHelper } from "./runtime_services.js";
|
import { createDefaultAmmcConfigHelper, getManagedWsUrlHelper, loadAmmcConfigFromBackendHelper, saveAmmcConfigToBackendHelper, refreshAmmcStatusHelper, startManagedAmmcHelper, stopManagedAmmcHelper, applyPersistedStateHelper, hydrateFromBackendHelper, scheduleBackendSyncHelper, syncStateToBackendHelper, pingBackendHelper, checkAppVersionHelper, startAppVersionPollingHelper, startOverlaySyncHelper, startOverlayRotationHelper, startOverlayLiveRefreshHelper, ensureAudioContextHelper, playPassingBeepHelper, playFinishSirenHelper, playLeaderCueHelper, playStartCueHelper, playBestLapCueHelper, pushOverlayEventHelper, speakTextHelper, announcePassingHelper, announceRaceFinishedHelper, handleSessionTimerTickHelper, tickClockHelper } from "./runtime_services.js";
|
||||||
import { connectDecoderHelper, disconnectDecoderHelper, processDecoderMessageHelper } from "./decoder_runtime.js";
|
import { connectDecoderHelper, disconnectDecoderHelper, processDecoderMessageHelper } from "./decoder_runtime.js";
|
||||||
|
|
||||||
@@ -2815,135 +2816,24 @@ function renderEventWorkspace(mode) {
|
|||||||
ensureRaceWizardDraft();
|
ensureRaceWizardDraft();
|
||||||
}
|
}
|
||||||
const filteredEvents = state.events.filter((event) => event.mode === mode);
|
const filteredEvents = state.events.filter((event) => event.mode === mode);
|
||||||
const classOptions = state.classes
|
|
||||||
.map((c) => `<option value="${c.id}">${escapeHtml(c.name)}</option>`)
|
|
||||||
.join("");
|
|
||||||
const wizardClassOptions = state.classes
|
|
||||||
.map((c) => `<option value="${c.id}" ${c.id === raceWizardDraft?.classId ? "selected" : ""}>${escapeHtml(c.name)}</option>`)
|
|
||||||
.join("");
|
|
||||||
const editingEvent = filteredEvents.find((event) => event.id === selectedEventEditId) || null;
|
const editingEvent = filteredEvents.find((event) => event.id === selectedEventEditId) || null;
|
||||||
|
|
||||||
dom.view.innerHTML = `
|
dom.view.innerHTML = renderEventWorkspaceMarkup(mode, {
|
||||||
<section class="panel">
|
state,
|
||||||
<div class="panel-header"><h3>${t(isRaceMode ? "events.create_race" : "events.create")}</h3></div>
|
t,
|
||||||
<div class="panel-body">
|
escapeHtml,
|
||||||
<p>${t(isRaceMode ? "events.race_only_intro" : "events.track_only_intro")}</p>
|
renderTable,
|
||||||
${isRaceMode ? `<p class="hint">${t("events.wizard_hint")}</p>` : ""}
|
renderRaceWizardStepsView,
|
||||||
</div>
|
renderRaceWizardContentView,
|
||||||
${
|
raceWizardDraft,
|
||||||
isRaceMode
|
raceWizardStep,
|
||||||
? `
|
getDriversForClass,
|
||||||
<div class="panel-body race-wizard-steps">
|
getRaceWizardPreset,
|
||||||
${renderRaceWizardStepsView()}
|
getSessionsForEvent,
|
||||||
</div>
|
getClassName,
|
||||||
<div class="panel-body">
|
getModeLabel,
|
||||||
${renderRaceWizardContentView(raceWizardDraft, wizardClassOptions, getDriversForClass(raceWizardDraft.classId), getRaceWizardPreset(raceWizardDraft.presetId))}
|
editingEvent,
|
||||||
</div>
|
});
|
||||||
<div class="panel-body race-wizard-footer">
|
|
||||||
<button id="raceWizardReset" class="btn" type="button">${t("common.reset")}</button>
|
|
||||||
<div class="actions-inline">
|
|
||||||
${raceWizardStep > 1 ? `<button id="raceWizardPrev" class="btn" type="button">${t("common.previous")}</button>` : ""}
|
|
||||||
${raceWizardStep < 4 ? `<button id="raceWizardNext" class="btn btn-primary" type="button">${t("common.next")}</button>` : `<button id="raceWizardCreate" class="btn btn-primary" type="button">${t("events.wizard_create")}</button>`}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: `
|
|
||||||
<form id="eventForm" class="panel-body form-grid cols-4">
|
|
||||||
<label>
|
|
||||||
${t("events.field_name")}
|
|
||||||
<input required name="name" placeholder="${t("events.name_placeholder")}" />
|
|
||||||
<small>${t("events.field_name_hint")}</small>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
${t("events.field_date")}
|
|
||||||
<input required type="date" name="date" />
|
|
||||||
<small>${t("events.field_date_hint")}</small>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
${t("events.field_class")}
|
|
||||||
<select name="classId">${classOptions}</select>
|
|
||||||
<small>${t("events.field_class_hint")}</small>
|
|
||||||
</label>
|
|
||||||
<div class="actions-inline align-end">
|
|
||||||
<button class="btn btn-primary" type="submit">${t("events.add")}</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="panel">
|
|
||||||
<div class="panel-header"><h3>${t(isRaceMode ? "events.race_title" : "events.title")}</h3></div>
|
|
||||||
<div class="panel-body">
|
|
||||||
${renderTable(
|
|
||||||
[t("table.name"), t("table.date"), t("table.class"), t("table.mode"), t("events.sessions"), t("events.actions")],
|
|
||||||
filteredEvents.map((e) => {
|
|
||||||
const sessions = getSessionsForEvent(e.id);
|
|
||||||
return `
|
|
||||||
<tr>
|
|
||||||
<td>${escapeHtml(e.name)}</td>
|
|
||||||
<td>${escapeHtml(e.date)}</td>
|
|
||||||
<td>${escapeHtml(getClassName(e.classId))}</td>
|
|
||||||
<td>${getModeLabel(e.mode)}</td>
|
|
||||||
<td>${sessions.length}</td>
|
|
||||||
<td class="actions-inline">
|
|
||||||
<button id="event-edit-${e.id}" class="btn">${t("events.edit")}</button>
|
|
||||||
<button id="event-manage-${e.id}" class="btn">${t("events.manage")}</button>
|
|
||||||
<button id="event-delete-${e.id}" class="btn btn-danger">${t("common.delete")}</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
`;
|
|
||||||
})
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="eventManageArea"></section>
|
|
||||||
|
|
||||||
${
|
|
||||||
editingEvent
|
|
||||||
? `
|
|
||||||
<div class="modal-overlay" id="eventEditModalOverlay">
|
|
||||||
<div class="modal-card">
|
|
||||||
<div class="panel-header">
|
|
||||||
<h3>${t("common.edit")}</h3>
|
|
||||||
<button class="btn" id="eventEditCancel">${t("common.cancel")}</button>
|
|
||||||
</div>
|
|
||||||
<form id="eventEditForm" class="panel-body form-grid cols-3">
|
|
||||||
<label>
|
|
||||||
${t("events.field_name")}
|
|
||||||
<input name="name" required value="${escapeHtml(editingEvent.name)}" placeholder="${t("events.name_placeholder")}" />
|
|
||||||
<small>${t("events.field_name_hint")}</small>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
${t("events.field_date")}
|
|
||||||
<input name="date" required type="date" value="${escapeHtml(editingEvent.date || "")}" />
|
|
||||||
<small>${t("events.field_date_hint")}</small>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
${t("events.field_class")}
|
|
||||||
<select name="classId">
|
|
||||||
${state.classes
|
|
||||||
.map(
|
|
||||||
(item) =>
|
|
||||||
`<option value="${item.id}" ${item.id === editingEvent.classId ? "selected" : ""}>${escapeHtml(item.name)}</option>`
|
|
||||||
)
|
|
||||||
.join("")}
|
|
||||||
</select>
|
|
||||||
<small>${t("events.field_class_hint")}</small>
|
|
||||||
</label>
|
|
||||||
<p class="form-error" id="eventEditError" hidden></p>
|
|
||||||
<div class="actions-inline">
|
|
||||||
<button class="btn btn-primary" type="submit">${t("common.save")}</button>
|
|
||||||
<button class="btn" id="eventEditCancelFooter" type="button">${t("common.cancel")}</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
if (isRaceMode) {
|
if (isRaceMode) {
|
||||||
const persistWizardStepOne = () => {
|
const persistWizardStepOne = () => {
|
||||||
|
|||||||
143
src/event_views.js
Normal file
143
src/event_views.js
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
export function renderEventWorkspaceMarkup(mode, deps) {
|
||||||
|
const {
|
||||||
|
state,
|
||||||
|
t,
|
||||||
|
escapeHtml,
|
||||||
|
renderTable,
|
||||||
|
renderRaceWizardStepsView,
|
||||||
|
renderRaceWizardContentView,
|
||||||
|
raceWizardDraft,
|
||||||
|
raceWizardStep,
|
||||||
|
getDriversForClass,
|
||||||
|
getRaceWizardPreset,
|
||||||
|
getSessionsForEvent,
|
||||||
|
getClassName,
|
||||||
|
getModeLabel,
|
||||||
|
editingEvent,
|
||||||
|
} = deps;
|
||||||
|
const isRaceMode = mode === "race";
|
||||||
|
const filteredEvents = state.events.filter((event) => event.mode === mode);
|
||||||
|
const classOptions = state.classes.map((c) => `<option value="${c.id}">${escapeHtml(c.name)}</option>`).join("");
|
||||||
|
const wizardClassOptions = state.classes
|
||||||
|
.map((c) => `<option value="${c.id}" ${c.id === raceWizardDraft?.classId ? "selected" : ""}>${escapeHtml(c.name)}</option>`)
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
return `
|
||||||
|
<section class="panel">
|
||||||
|
<div class="panel-header"><h3>${t(isRaceMode ? "events.create_race" : "events.create")}</h3></div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<p>${t(isRaceMode ? "events.race_only_intro" : "events.track_only_intro")}</p>
|
||||||
|
${isRaceMode ? `<p class="hint">${t("events.wizard_hint")}</p>` : ""}
|
||||||
|
</div>
|
||||||
|
${
|
||||||
|
isRaceMode
|
||||||
|
? `
|
||||||
|
<div class="panel-body race-wizard-steps">
|
||||||
|
${renderRaceWizardStepsView()}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
${renderRaceWizardContentView(raceWizardDraft, wizardClassOptions, getDriversForClass(raceWizardDraft.classId), getRaceWizardPreset(raceWizardDraft.presetId))}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body race-wizard-footer">
|
||||||
|
<button id="raceWizardReset" class="btn" type="button">${t("common.reset")}</button>
|
||||||
|
<div class="actions-inline">
|
||||||
|
${raceWizardStep > 1 ? `<button id="raceWizardPrev" class="btn" type="button">${t("common.previous")}</button>` : ""}
|
||||||
|
${raceWizardStep < 4 ? `<button id="raceWizardNext" class="btn btn-primary" type="button">${t("common.next")}</button>` : `<button id="raceWizardCreate" class="btn btn-primary" type="button">${t("events.wizard_create")}</button>`}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: `
|
||||||
|
<form id="eventForm" class="panel-body form-grid cols-4">
|
||||||
|
<label>
|
||||||
|
${t("events.field_name")}
|
||||||
|
<input required name="name" placeholder="${t("events.name_placeholder")}" />
|
||||||
|
<small>${t("events.field_name_hint")}</small>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
${t("events.field_date")}
|
||||||
|
<input required type="date" name="date" />
|
||||||
|
<small>${t("events.field_date_hint")}</small>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
${t("events.field_class")}
|
||||||
|
<select name="classId">${classOptions}</select>
|
||||||
|
<small>${t("events.field_class_hint")}</small>
|
||||||
|
</label>
|
||||||
|
<div class="actions-inline align-end">
|
||||||
|
<button class="btn btn-primary" type="submit">${t("events.add")}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel">
|
||||||
|
<div class="panel-header"><h3>${t(isRaceMode ? "events.race_title" : "events.title")}</h3></div>
|
||||||
|
<div class="panel-body">
|
||||||
|
${renderTable(
|
||||||
|
[t("table.name"), t("table.date"), t("table.class"), t("table.mode"), t("events.sessions"), t("events.actions")],
|
||||||
|
filteredEvents.map((e) => {
|
||||||
|
const sessions = getSessionsForEvent(e.id);
|
||||||
|
return `
|
||||||
|
<tr>
|
||||||
|
<td>${escapeHtml(e.name)}</td>
|
||||||
|
<td>${escapeHtml(e.date)}</td>
|
||||||
|
<td>${escapeHtml(getClassName(e.classId))}</td>
|
||||||
|
<td>${getModeLabel(e.mode)}</td>
|
||||||
|
<td>${sessions.length}</td>
|
||||||
|
<td class="actions-inline">
|
||||||
|
<button id="event-edit-${e.id}" class="btn">${t("events.edit")}</button>
|
||||||
|
<button id="event-manage-${e.id}" class="btn">${t("events.manage")}</button>
|
||||||
|
<button id="event-delete-${e.id}" class="btn btn-danger">${t("common.delete")}</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="eventManageArea"></section>
|
||||||
|
|
||||||
|
${
|
||||||
|
editingEvent
|
||||||
|
? `
|
||||||
|
<div class="modal-overlay" id="eventEditModalOverlay">
|
||||||
|
<div class="modal-card">
|
||||||
|
<div class="panel-header">
|
||||||
|
<h3>${t("common.edit")}</h3>
|
||||||
|
<button class="btn" id="eventEditCancel">${t("common.cancel")}</button>
|
||||||
|
</div>
|
||||||
|
<form id="eventEditForm" class="panel-body form-grid cols-3">
|
||||||
|
<label>
|
||||||
|
${t("events.field_name")}
|
||||||
|
<input name="name" required value="${escapeHtml(editingEvent.name)}" placeholder="${t("events.name_placeholder")}" />
|
||||||
|
<small>${t("events.field_name_hint")}</small>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
${t("events.field_date")}
|
||||||
|
<input name="date" required type="date" value="${escapeHtml(editingEvent.date || "")}" />
|
||||||
|
<small>${t("events.field_date_hint")}</small>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
${t("events.field_class")}
|
||||||
|
<select name="classId">
|
||||||
|
${state.classes
|
||||||
|
.map((item) => `<option value="${item.id}" ${item.id === editingEvent.classId ? "selected" : ""}>${escapeHtml(item.name)}</option>`)
|
||||||
|
.join("")}
|
||||||
|
</select>
|
||||||
|
<small>${t("events.field_class_hint")}</small>
|
||||||
|
</label>
|
||||||
|
<p class="form-error" id="eventEditError" hidden></p>
|
||||||
|
<div class="actions-inline">
|
||||||
|
<button class="btn btn-primary" type="submit">${t("common.save")}</button>
|
||||||
|
<button class="btn" id="eventEditCancelFooter" type="button">${t("common.cancel")}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user