`;
list.appendChild(empEl);
});
};
// --- MODAL & FORM LOGIC ---
app.showShiftModal = (dateStr, shiftId = null) => {
const modal = document.getElementById('shiftModal');
const title = document.getElementById('shiftModalTitle');
const date = new Date(dateStr + 'T00:00:00');
title.textContent = shiftId ? `Edit Shift for ${date.toLocaleDateString()}` : `Add Shift for ${date.toLocaleDateString()}`;
document.getElementById('shiftDateInput').value = dateStr;
document.getElementById('shiftIdInput').value = shiftId || '';
const employeeSelect = document.getElementById('employeeSelect');
employeeSelect.innerHTML = '
';
employees.forEach(e => {
const isUnavailable = e.unavailable.includes(DAYS[date.getDay()]);
employeeSelect.innerHTML += `
`;
});
const deleteBtn = document.getElementById('deleteShiftButton');
if (shiftId) {
const shift = shifts[dateStr].find(s => s.id === shiftId);
employeeSelect.value = shift.employeeId;
document.getElementById('startTimeInput').value = shift.start;
document.getElementById('endTimeInput').value = shift.end;
document.getElementById('shiftNotesInput').value = shift.notes;
deleteBtn.classList.remove('hidden');
} else {
document.getElementById('shiftModal').querySelector('form') ? document.getElementById('shiftModal').querySelector('form').reset() : null;
employeeSelect.value = '';
document.getElementById('startTimeInput').value = '09:00';
document.getElementById('endTimeInput').value = '17:00';
document.getElementById('shiftNotesInput').value = '';
deleteBtn.classList.add('hidden');
}
modal.classList.remove('hidden');
};
app.showEmployeeModal = (employeeId = null) => {
const modal = document.getElementById('employeeModal');
const title = document.getElementById('employeeModalTitle');
const idInput = document.getElementById('employeeIdInput');
const nameInput = document.getElementById('employeeNameInput');
const roleInput = document.getElementById('employeeRoleInput');
const availabilityDiv = document.getElementById('availabilityCheckboxes');
availabilityDiv.innerHTML = '';
DAYS.forEach(day => {
availabilityDiv.innerHTML += `
`;
});
if (employeeId) {
const emp = employees.find(e => e.id === employeeId);
title.textContent = 'Edit Employee';
idInput.value = emp.id;
nameInput.value = emp.name;
roleInput.value = emp.role;
emp.unavailable.forEach(day => {
const checkbox = availabilityDiv.querySelector(`input[value="${day}"]`);
if (checkbox) checkbox.checked = true;
});
} else {
title.textContent = 'Add New Employee';
idInput.value = '';
nameInput.value = '';
roleInput.value = '';
}
modal.classList.remove('hidden');
};
app.hideModal = (modalId) => document.getElementById(modalId).classList.add('hidden');
// --- CORE LOGIC ---
app.saveShift = () => {
const date = document.getElementById('shiftDateInput').value;
const id = document.getElementById('shiftIdInput').value;
const employeeId = parseInt(document.getElementById('employeeSelect').value);
if (!employeeId) { alert('Please select an employee.'); return; }
const newShift = {
id: id ? parseInt(id) : Date.now(),
employeeId,
start: document.getElementById('startTimeInput').value,
end: document.getElementById('endTimeInput').value,
notes: document.getElementById('shiftNotesInput').value
};
if (!shifts[date]) shifts[date] = [];
if (id) {
const index = shifts[date].findIndex(s => s.id === newShift.id);
shifts[date][index] = newShift;
} else {
shifts[date].push(newShift);
}
saveShifts();
renderSchedule();
app.hideModal('shiftModal');
};
app.deleteShift = () => {
const date = document.getElementById('shiftDateInput').value;
const id = parseInt(document.getElementById('shiftIdInput').value);
if (confirm('Are you sure you want to delete this shift?')) {
shifts[date] = shifts[date].filter(s => s.id !== id);
if (shifts[date].length === 0) delete shifts[date];
saveShifts();
renderSchedule();
app.hideModal('shiftModal');
}
};
app.saveEmployee = () => {
const id = document.getElementById('employeeIdInput').value;
const name = document.getElementById('employeeNameInput').value;
if (!name.trim()) { alert('Employee name is required.'); return; }
const unavailable = Array.from(document.getElementById('availabilityCheckboxes').querySelectorAll('input:checked')).map(cb => cb.value);
const employeeData = {
name,
role: document.getElementById('employeeRoleInput').value,
unavailable
};
if (id) {
const emp = employees.find(e => e.id === parseInt(id));
Object.assign(emp, employeeData);
} else {
employees.push({ id: Date.now(), ...employeeData });
}
saveEmployees();
renderEmployees();
app.hideModal('employeeModal');
};
app.deleteEmployee = (id) => {
if (confirm('Are you sure you want to delete this employee? This will not remove their assigned shifts.')) {
employees = employees.filter(e => e.id !== id);
saveEmployees();
renderEmployees();
}
};
app.changeWeek = (delta) => {
currentWeekStartDate.setDate(currentWeekStartDate.getDate() + (delta * 7));
renderSchedule();
};
// --- TAB NAVIGATION ---
app.changeTab = (tabIndex) => {
document.getElementById(`tabContent-${currentTab}`).classList.add('hidden');
document.getElementById(`tab-${currentTab}`).classList.replace('tab-active', 'tab-inactive');
document.getElementById(`tabContent-${tabIndex}`).classList.remove('hidden');
document.getElementById(`tab-${tabIndex}`).classList.replace('tab-inactive', 'tab-active');
currentTab = tabIndex;
};
// --- PDF DOWNLOAD ---
app.downloadSchedulePDF = () => {
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
const startDate = new Date(currentWeekStartDate);
doc.setFontSize(20);
doc.text(`Weekly Schedule: ${startDate.toLocaleDateString()}`, 105, 15, { align: 'center' });
const head = [['Day', 'Employee', 'Shift Time', 'Role', 'Notes']];
const body = [];
for (let i = 0; i < 7; i++) {
const dayDate = new Date(startDate);
dayDate.setDate(startDate.getDate() + i);
const dateStr = formatDate(dayDate);
const dayShifts = shifts[dateStr] || [];
if (dayShifts.length > 0) {
dayShifts.forEach(shift => {
const emp = employees.find(e => e.id === shift.employeeId);
body.push([
`${DAYS[i]}, ${dayDate.toLocaleDateString()}`,
emp ? emp.name : 'N/A',
`${shift.start} - ${shift.end}`,
emp ? emp.role : 'N/A',
shift.notes
]);
});
} else {
body.push([`${DAYS[i]}, ${dayDate.toLocaleDateString()}`, '(No Shifts)', '', '', '']);
}
}
doc.autoTable({ head, body, startY: 25 });
doc.save('weekly_schedule.pdf');
};
// --- INITIALIZATION ---
loadData();
renderSchedule();
renderEmployees();
});