match cons export format
This commit is contained in:
+121
-50
@@ -1027,6 +1027,7 @@ let selectedShifts = new Set();
|
|||||||
|
|
||||||
let shiftColors = {};
|
let shiftColors = {};
|
||||||
let rotaExportViewEnabled = false;
|
let rotaExportViewEnabled = false;
|
||||||
|
let shiftDisplayOrder = [...oshifts];
|
||||||
|
|
||||||
oshifts.forEach((shift, i) => {
|
oshifts.forEach((shift, i) => {
|
||||||
selectedShifts.add(shift);
|
selectedShifts.add(shift);
|
||||||
@@ -1483,6 +1484,24 @@ function renderShiftTimetable() {
|
|||||||
$("#shift-timetable-div").append(gridTable);
|
$("#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() {
|
function renderRotaExportTimetable() {
|
||||||
let weeks = [];
|
let weeks = [];
|
||||||
let daysOfWeek = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
let daysOfWeek = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
||||||
@@ -1502,69 +1521,85 @@ function renderRotaExportTimetable() {
|
|||||||
|
|
||||||
weeks = weeks.sort((a, b) => parseInt(a) - parseInt(b));
|
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>`);
|
const orderedSelectedShifts = shiftDisplayOrder.filter(s => selectedShifts.has(s));
|
||||||
let header = $("<tr><th style='border:1px solid #888; min-width:100px;'>Week</th></tr>");
|
const dayShiftOrder = {};
|
||||||
daysOfWeek.forEach(day => {
|
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 => {
|
weeks.forEach(week => {
|
||||||
let row = $("<tr></tr>");
|
let row = $("<tr></tr>");
|
||||||
let weekLabel = `${week}`;
|
let startDateFormatted = formatDateForExport(weekStartDates[week]);
|
||||||
if (showWeekStart && weekStartDates[week]) {
|
row.append(`<td style='border:1px solid #888; font-weight:bold; vertical-align:top;'>${startDateFormatted}</td>`);
|
||||||
weekLabel += `<div style="font-size:0.85em; color:#444;">${weekStartDates[week]}</div>`;
|
row.append(`<td style='border:1px solid #888; text-align:center; font-weight:bold; vertical-align:top;'>${week}</td>`);
|
||||||
}
|
|
||||||
row.append(`<td style='border:1px solid #888; font-weight:bold; vertical-align:top;'>${weekLabel}</td>`);
|
|
||||||
|
|
||||||
daysOfWeek.forEach(day => {
|
daysOfWeek.forEach(day => {
|
||||||
let shiftMap = {};
|
const dayShifts = dayShiftOrder[day];
|
||||||
let dayDate = "";
|
if (dayShifts.length === 0) {
|
||||||
|
row.append(`<td style='border:1px solid #888; color:#999; text-align:center;'>-</td>`);
|
||||||
$(`table#main-table td[data-week='${week}'][data-day='${day}']`).each(function () {
|
return;
|
||||||
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>`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
table.append(row);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (weeks.length === 0) {
|
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);
|
$("#shift-timetable-div").append(table);
|
||||||
@@ -1587,7 +1622,7 @@ rerenderWorkerCheckboxesIfNeeded();
|
|||||||
|
|
||||||
// Patch the timetable button logic to use renderShiftTimetable
|
// Patch the timetable button logic to use renderShiftTimetable
|
||||||
oshifts.forEach((shift) => {
|
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({
|
button.css({
|
||||||
background: "",
|
background: "",
|
||||||
border: "1px solid #888",
|
border: "1px solid #888",
|
||||||
@@ -1612,6 +1647,42 @@ oshifts.forEach((shift) => {
|
|||||||
$("#shift-timetable-options").append(button);
|
$("#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
|
// Add help text to the shift timetable section
|
||||||
$("#shift-timetable-options").prepend(`
|
$("#shift-timetable-options").prepend(`
|
||||||
|
|||||||
Reference in New Issue
Block a user