feat(calendar): N moderator slots per day with disable-on-duplicate

This commit is contained in:
ferdi2go committed 2026-04-28 11:17:08 +00:00
1 parent 3b84fce1e6
commit fa28640c28
1 file changed
+37 -14
+37 -14
View File
@@ -140,8 +140,18 @@ function renderGrid(wrapper, exportBtn) {
wrapper.replaceChildren();
const daysInMonth = new Date(currentYear, currentMonth, 0).getDate();
const nameById = Object.fromEntries(state.employees.map(e => [e.id, e.name]));
const planByDate = Object.fromEntries(currentPlan.map(a => [a.date, a.employeeId]));
const empNameById = Object.fromEntries(state.employees.map(e => [e.id, e.name]));
const entriesByDate = {};
for (const a of currentPlan) {
if (!entriesByDate[a.date]) entriesByDate[a.date] = [];
entriesByDate[a.date].push(a);
}
// Sort each date's entries alphabetically by employee name (stable slot order)
for (const date of Object.keys(entriesByDate)) {
entriesByDate[date].sort((x, y) =>
(empNameById[x.employeeId] ?? '').localeCompare(empNameById[y.employeeId] ?? '', 'de')
);
}
// Day-of-week header row
const headerGrid = document.createElement('div');
@@ -174,7 +184,6 @@ function renderGrid(wrapper, exportBtn) {
const isWeekend = wdNum >= 6;
const isHoliday = state.calendar.holidays.includes(iso);
const isClosure = state.calendar.companyClosures.includes(iso);
const assigneeId = planByDate[iso];
const cell = document.createElement('div');
cell.className = 'cal-day';
@@ -210,8 +219,17 @@ function renderGrid(wrapper, exportBtn) {
tag.appendChild(removeBtn);
cell.appendChild(tag);
} else {
// Assignee dropdown
// N slots per day
const slotCount = state.settings.moderatorsPerDay;
const dayEntries = entriesByDate[iso] ?? []; // already alphabetically sorted
const slotValues = []; // employeeId per slot index, '' if empty
for (let s = 0; s < slotCount; s++) {
slotValues.push(dayEntries[s]?.employeeId ?? '');
}
for (let slotIdx = 0; slotIdx < slotCount; slotIdx++) {
const sel = document.createElement('select');
sel.dataset.slot = String(slotIdx);
const emptyOpt = document.createElement('option');
emptyOpt.value = '';
@@ -222,26 +240,31 @@ function renderGrid(wrapper, exportBtn) {
const opt = document.createElement('option');
opt.value = emp.id;
opt.textContent = emp.name; // textContent: safe
if (emp.id === assigneeId) opt.selected = true;
// Disable if already picked in a different slot for this day
const pickedElsewhere = slotValues.some((id, i) => i !== slotIdx && id === emp.id);
if (pickedElsewhere) opt.disabled = true;
if (emp.id === slotValues[slotIdx]) opt.selected = true;
sel.appendChild(opt);
}
sel.addEventListener('change', () => {
const empId = sel.value;
if (empId) {
const entry = { date: iso, employeeId: empId, manual: true };
const newId = sel.value;
const oldId = slotValues[slotIdx];
if (oldId) {
removeHistoryEntry(state, iso, oldId);
currentPlan = currentPlan.filter(a => !(a.date === iso && a.employeeId === oldId));
}
if (newId) {
const entry = { date: iso, employeeId: newId, manual: true };
addHistoryEntries(state, [entry]);
const idx = currentPlan.findIndex(a => a.date === iso);
if (idx >= 0) currentPlan[idx] = entry; else currentPlan.push(entry);
} else {
removeHistoryEntry(state, iso);
currentPlan = currentPlan.filter(a => a.date !== iso);
currentPlan.push(entry);
}
saveState();
exportBtn.style.display = currentPlan.length > 0 ? 'inline-block' : 'none';
renderGrid(wrapper, exportBtn);
});
cell.appendChild(sel);
}
// FT / BU mark buttons
const btnRow = document.createElement('div');