match cons export format
This commit is contained in:
+121
-50
@@ -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(`
|
||||
|
||||
Reference in New Issue
Block a user