Refactor timetable and shifts generation to ensure proper HTML structure with sticky headers

This commit is contained in:
Ross
2026-01-07 20:26:18 +00:00
parent f8c6e38841
commit 628114ad91
2 changed files with 32 additions and 6 deletions
+22
View File
@@ -1729,6 +1729,16 @@ function syntaxHighlight(json) {
const mainTable = document.getElementById("main-table");
if (!mainTable) return;
// Ensure the header row is inside a proper <thead> element.
// Some exports use a bare <tr> as the first row; move it into a thead so CSS/sticky headers work.
if (!mainTable.tHead) {
const firstRow = mainTable.querySelector('tr');
if (firstRow) {
const thead = mainTable.createTHead();
thead.appendChild(firstRow);
}
}
// Helper to remove highlight from all headers
function clearColHover() {
const ths = mainTable.querySelectorAll("th.col-hover");
@@ -1763,6 +1773,18 @@ $("#main-table").before(
`<button id="show-display-settings" style="margin:8px 0 8px 0; float:right;">Display Settings</button>`
);
// Ensure main table header is sticky and compact
if ($('#main-table-style').length === 0) {
$('head').append(`
<style id='main-table-style'>
/* Make main timetable header sticky */
#main-table thead th, #main-table thead td { position: sticky; top: 0; z-index: 60; background: #fff; font-size: 12px; }
/* Slight shadow to separate header from body */
#main-table thead th { box-shadow: 0 2px 6px rgba(0,0,0,0.06); }
</style>
`);
}
// --- Modal show/hide logic ---
$("#show-display-settings").on("click", function() {
$("#display-settings-modal").show();
+10 -6
View File
@@ -5222,8 +5222,10 @@ class RotaBuilder(object):
timetable = []
# Run times and heading go outside the table; store for later insertion
run_times_html = ""
if self.run_start_time is not None and self.run_end_time is not None:
timetable.append(
run_times_html = (
"<div id='run_times_row' style='display: flex; gap: 2em; margin-bottom: 1em;'>"
f"<div id='run_start_time_cell'>Start time: <span id='run_start_time'>{self.run_start_time:%Y-%m-%d %H:%M:%S%z}</span></div>"
f"<div id='run_end_time_cell'>End time: <span id='run_end_time'>{self.run_end_time:%Y-%m-%d %H:%M:%S%z}</span></div>"
@@ -5231,9 +5233,7 @@ class RotaBuilder(object):
"</div>"
)
timetable.append(
f"<h2>Rota start date: {self.start_date.isoformat()} ({self.weeks[-1]} weeks)</h2>"
)
heading_html = f"<h2>Rota start date: {self.start_date.isoformat()} ({self.weeks[-1]} weeks)</h2>"
date_row = ["<th class='worker'></th>"]
@@ -5249,7 +5249,8 @@ class RotaBuilder(object):
f"<th title='{d}' class='{th_class}' data-date='{d}'>Week {week}: {day}</th>"
)
n = n + 1
timetable.append(f"<tr class='data-row'>{''.join(date_row)}</tr>")
# Header row (dates) should be placed in a proper thead
header_row_html = f"<thead><tr class='data-row'>{''.join(date_row)}</tr></thead>"
current_site = ""
@@ -5675,7 +5676,8 @@ class RotaBuilder(object):
else:
result_string = "Rota not run"
joined_timetable = "\n".join(timetable)
# Wrap the remaining rows in a tbody and prepend the thead
joined_timetable = header_row_html + "\n<tbody>\n" + "\n".join(timetable) + "\n</tbody>"
hard_constrain_shift_info = []
for worker in self.workers:
@@ -5781,6 +5783,8 @@ class RotaBuilder(object):
html = f"""
<body>
<div class="table-div" id="{table_name}">
{run_times_html}
{heading_html}
<table id="main-table">{joined_timetable}</table>
</div>
{warnings_html}