match cons export format

This commit is contained in:
Ross
2026-06-03 20:29:33 +01:00
parent ea68885ff3
commit 2949327d39
+121 -50
View File
@@ -1027,6 +1027,7 @@ let selectedShifts = new Set();
let shiftColors = {};
let rotaExportViewEnabled = false;
let shiftDisplayOrder = [...oshifts];
oshifts.forEach((shift, i) => {
selectedShifts.add(shift);
@@ -1483,6 +1484,24 @@ function renderShiftTimetable() {
$("#shift-timetable-div").append(gridTable);
}
function formatDateForExport(dateStr) {
if (!dateStr) return "";
let d = new Date(dateStr);
if (Number.isNaN(d.getTime())) return dateStr;
let dd = String(d.getDate()).padStart(2, "0");
let mm = String(d.getMonth() + 1).padStart(2, "0");
let yyyy = d.getFullYear();
return `${dd}/${mm}/${yyyy}`;
}
function getShiftHeaderLabel(shift) {
let s = (shift || "").toLowerCase();
if (s.includes("twil")) return "TWIL";
if (s.includes("night") || s.includes("oncall")) return "NIGHT";
if (s.includes("weekend")) return "WEEKEND";
return (shift || "").toUpperCase();
}
function renderRotaExportTimetable() {
let weeks = [];
let daysOfWeek = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
@@ -1502,69 +1521,85 @@ function renderRotaExportTimetable() {
weeks = weeks.sort((a, b) => parseInt(a) - parseInt(b));
let table = $(`<table id="rota-export-table" class="shift-grid-table" style="margin-bottom:20px; border-collapse:collapse; width:auto; table-layout:fixed;"><caption>Rota Export (Shifts by Day)</caption></table>`);
let header = $("<tr><th style='border:1px solid #888; min-width:100px;'>Week</th></tr>");
const orderedSelectedShifts = shiftDisplayOrder.filter(s => selectedShifts.has(s));
const dayShiftOrder = {};
daysOfWeek.forEach(day => {
header.append(`<th style='border:1px solid #888; min-width:160px;'>${day}</th>`);
dayShiftOrder[day] = orderedSelectedShifts.filter(shift => {
let found = false;
$(`table#main-table td[data-day='${day}']`).each(function () {
let shifts = ($(this).attr("data-shift") || "").split(",").map(x => x.trim()).filter(Boolean);
if (shifts.includes(shift)) {
found = true;
return false;
}
});
return found;
});
});
table.append(header);
let table = $(`<table id="rota-export-table" class="shift-grid-table" style="margin-bottom:20px; border-collapse:collapse; width:auto; table-layout:fixed;"><caption>Rota Export (Shifts by Day)</caption></table>`);
let headerTop = $("<tr></tr>");
headerTop.append("<th style='border:1px solid #888; min-width:130px;'>Week Start</th>");
headerTop.append("<th style='border:1px solid #888; min-width:70px;'>Week</th>");
daysOfWeek.forEach(day => {
let span = Math.max(1, dayShiftOrder[day].length);
headerTop.append(`<th style='border:1px solid #888; min-width:120px;' colspan='${span}'>${day.toUpperCase()}</th>`);
});
table.append(headerTop);
let headerBottom = $("<tr></tr>");
headerBottom.append("<th style='border:1px solid #888; min-width:130px;'></th>");
headerBottom.append("<th style='border:1px solid #888; min-width:70px;'>No</th>");
daysOfWeek.forEach(day => {
if (dayShiftOrder[day].length === 0) {
headerBottom.append("<th style='border:1px solid #888; min-width:120px;'></th>");
return;
}
dayShiftOrder[day].forEach(shift => {
headerBottom.append(`<th style='border:1px solid #888; min-width:120px;'>${getShiftHeaderLabel(shift)}</th>`);
});
});
table.append(headerBottom);
weeks.forEach(week => {
let row = $("<tr></tr>");
let weekLabel = `${week}`;
if (showWeekStart && weekStartDates[week]) {
weekLabel += `<div style="font-size:0.85em; color:#444;">${weekStartDates[week]}</div>`;
}
row.append(`<td style='border:1px solid #888; font-weight:bold; vertical-align:top;'>${weekLabel}</td>`);
let startDateFormatted = formatDateForExport(weekStartDates[week]);
row.append(`<td style='border:1px solid #888; font-weight:bold; vertical-align:top;'>${startDateFormatted}</td>`);
row.append(`<td style='border:1px solid #888; text-align:center; font-weight:bold; vertical-align:top;'>${week}</td>`);
daysOfWeek.forEach(day => {
let shiftMap = {};
let dayDate = "";
$(`table#main-table td[data-week='${week}'][data-day='${day}']`).each(function () {
let shifts = ($(this).attr("data-shift") || "").split(",").map(s => s.trim()).filter(Boolean);
let date = $(this).attr("data-date") || "";
if (!dayDate && date) dayDate = date;
let workerName = $(this).closest("tr").find("td.worker span.name").text().trim();
shifts.forEach(shift => {
if (!selectedShifts.has(shift)) return;
if (!shiftMap[shift]) {
shiftMap[shift] = new Set();
}
if (workerName) {
shiftMap[shift].add(workerName);
}
});
});
let shiftNames = Object.keys(shiftMap).sort();
if (shiftNames.length === 0) {
let emptyText = dayDate ? `<div style='font-size:0.85em;color:#666;'>${dayDate}</div>-` : "-";
row.append(`<td style='border:1px solid #888; color:#999; text-align:center;'>${emptyText}</td>`);
} else {
let content = [];
if (dayDate) {
content.push(`<div style='font-size:0.8em; color:#555; margin-bottom:4px;'>${dayDate}</div>`);
}
shiftNames.forEach(shift => {
let workerList = Array.from(shiftMap[shift]).sort();
let workerSuffix = "";
if (showWorkers && workerList.length > 0) {
workerSuffix = `: ${workerList.join(" / ")}`;
}
let color = shiftColors[shift] || "#f3f3f3";
content.push(`<div style='background:${color}; margin:2px 0; padding:2px 6px; border-radius:4px; text-align:left;'>${shift}${workerSuffix}</div>`);
});
row.append(`<td style='border:1px solid #888; vertical-align:top;'>${content.join("")}</td>`);
const dayShifts = dayShiftOrder[day];
if (dayShifts.length === 0) {
row.append(`<td style='border:1px solid #888; color:#999; text-align:center;'>-</td>`);
return;
}
dayShifts.forEach(shift => {
let workersForShift = new Set();
$(`table#main-table td[data-week='${week}'][data-day='${day}']`).each(function () {
let shifts = ($(this).attr("data-shift") || "").split(",").map(s => s.trim()).filter(Boolean);
if (!shifts.includes(shift)) return;
let workerName = $(this).closest("tr").find("td.worker span.name").text().trim();
if (workerName) workersForShift.add(workerName);
});
let workerList = Array.from(workersForShift).sort();
let text = "";
if (showWorkers) {
text = workerList.join("/");
} else {
text = workerList.length > 0 ? "X" : "";
}
row.append(`<td style='border:1px solid #888; vertical-align:top; text-align:center;'>${text}</td>`);
});
});
table.append(row);
});
if (weeks.length === 0) {
table.append(`<tr><td colspan="8" style="border:1px solid #888; text-align:center; color:#666;">No rota data found.</td></tr>`);
table.append(`<tr><td colspan="16" style="border:1px solid #888; text-align:center; color:#666;">No rota data found.</td></tr>`);
}
$("#shift-timetable-div").append(table);
@@ -1587,7 +1622,7 @@ rerenderWorkerCheckboxesIfNeeded();
// Patch the timetable button logic to use renderShiftTimetable
oshifts.forEach((shift) => {
let button = $(`<button data-shift='${shift}'>${shift}</button>`);
let button = $(`<button data-shift='${shift}' draggable='true' title='Drag to reorder shifts'>${shift}</button>`);
button.css({
background: "",
border: "1px solid #888",
@@ -1612,6 +1647,42 @@ oshifts.forEach((shift) => {
$("#shift-timetable-options").append(button);
});
let draggedShiftButton = null;
$(document).on("dragstart", "#shift-timetable-options button[data-shift]", function (e) {
draggedShiftButton = this;
if (e.originalEvent && e.originalEvent.dataTransfer) {
e.originalEvent.dataTransfer.effectAllowed = "move";
}
});
$(document).on("dragover", "#shift-timetable-options button[data-shift]", function (e) {
e.preventDefault();
});
$(document).on("drop", "#shift-timetable-options button[data-shift]", function (e) {
e.preventDefault();
if (!draggedShiftButton || draggedShiftButton === this) return;
const $dragged = $(draggedShiftButton);
const $target = $(this);
const draggedIndex = $dragged.index();
const targetIndex = $target.index();
if (draggedIndex < targetIndex) {
$target.after($dragged);
} else {
$target.before($dragged);
}
shiftDisplayOrder = $("#shift-timetable-options button[data-shift]").map(function () {
return $(this).attr("data-shift");
}).get();
renderShiftTimetable();
draggedShiftButton = null;
});
// Add help text to the shift timetable section
$("#shift-timetable-options").prepend(`