feat(calendar): N moderator slots per day with disable-on-duplicate
This commit is contained in:
1 parent
3b84fce1e6
commit
fa28640c28
1 file changed
+55
-32
+55
-32
@@ -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,38 +219,52 @@ function renderGrid(wrapper, exportBtn) {
|
||||
tag.appendChild(removeBtn);
|
||||
cell.appendChild(tag);
|
||||
} else {
|
||||
// Assignee dropdown
|
||||
const sel = document.createElement('select');
|
||||
|
||||
const emptyOpt = document.createElement('option');
|
||||
emptyOpt.value = '';
|
||||
emptyOpt.textContent = '— Leer —';
|
||||
sel.appendChild(emptyOpt);
|
||||
|
||||
for (const emp of state.employees) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = emp.id;
|
||||
opt.textContent = emp.name; // textContent: safe
|
||||
if (emp.id === assigneeId) opt.selected = true;
|
||||
sel.appendChild(opt);
|
||||
// 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 ?? '');
|
||||
}
|
||||
|
||||
sel.addEventListener('change', () => {
|
||||
const empId = sel.value;
|
||||
if (empId) {
|
||||
const entry = { date: iso, employeeId: empId, 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);
|
||||
}
|
||||
saveState();
|
||||
exportBtn.style.display = currentPlan.length > 0 ? 'inline-block' : 'none';
|
||||
});
|
||||
for (let slotIdx = 0; slotIdx < slotCount; slotIdx++) {
|
||||
const sel = document.createElement('select');
|
||||
sel.dataset.slot = String(slotIdx);
|
||||
|
||||
cell.appendChild(sel);
|
||||
const emptyOpt = document.createElement('option');
|
||||
emptyOpt.value = '';
|
||||
emptyOpt.textContent = '— Leer —';
|
||||
sel.appendChild(emptyOpt);
|
||||
|
||||
for (const emp of state.employees) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = emp.id;
|
||||
opt.textContent = emp.name; // textContent: safe
|
||||
// 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 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]);
|
||||
currentPlan.push(entry);
|
||||
}
|
||||
saveState();
|
||||
renderGrid(wrapper, exportBtn);
|
||||
});
|
||||
|
||||
cell.appendChild(sel);
|
||||
}
|
||||
|
||||
// FT / BU mark buttons
|
||||
const btnRow = document.createElement('div');
|
||||
|
||||
Reference in new issue
Block a user