/** * Admin Shifts Management Module * Handles shift CRUD operations, volunteer management, and email functionality */ // Shift state let editingShiftId = null; let currentShiftData = null; let allUsers = []; // Add shift management functions async function loadAdminShifts() { const list = document.getElementById('admin-shifts-list'); if (list) { list.innerHTML = '

Loading shifts...

'; } try { console.log('Loading admin shifts...'); const response = await fetch('/api/shifts/admin'); const data = await response.json(); if (data.success) { console.log('Successfully loaded', data.shifts.length, 'shifts'); displayAdminShifts(data.shifts); } else { console.error('Failed to load shifts:', data.error); if (list) { list.innerHTML = '

Failed to load shifts

'; } window.adminCore.showStatus('Failed to load shifts', 'error'); } } catch (error) { console.error('Error loading admin shifts:', error); if (list) { list.innerHTML = '

Error loading shifts

'; } window.adminCore.showStatus('Failed to load shifts', 'error'); } } function displayAdminShifts(shifts) { const list = document.getElementById('admin-shifts-list'); if (!list) { console.error('Admin shifts list element not found'); return; } if (shifts.length === 0) { list.innerHTML = '

No shifts created yet.

'; return; } list.innerHTML = shifts.map(shift => { const shiftDate = window.adminCore.createLocalDate(shift.Date); const signupCount = shift.signups ? shift.signups.length : 0; const isPublic = shift['Is Public'] !== false; console.log(`Shift "${shift.Title}" (ID: ${shift.ID}) has ${signupCount} volunteers:`, shift.signups?.map(s => s['User Email']) || []); return `

${window.adminCore.escapeHtml(shift.Title)}

📅 ${shiftDate.toLocaleDateString()} | ⏰ ${shift['Start Time']} - ${shift['End Time']}

📍 ${window.adminCore.escapeHtml(shift.Location || 'TBD')}

👥 ${signupCount}/${shift['Max Volunteers']} volunteers

${shift.Status || 'Open'}

${isPublic ? '🌐 Public' : '🔒 Private'}

${isPublic ? ` ` : ''}
`; }).join(''); // Add event listeners using delegation setupShiftActionListeners(); } // Setup shift action listeners function setupShiftActionListeners() { const list = document.getElementById('admin-shifts-list'); if (!list) return; // Remove any existing listeners to avoid duplicates const newList = list.cloneNode(true); list.parentNode.replaceChild(newList, list); // Get the updated reference const updatedList = document.getElementById('admin-shifts-list'); updatedList.addEventListener('click', function(e) { if (e.target.classList.contains('delete-shift-btn')) { const shiftId = e.target.getAttribute('data-shift-id'); console.log('Delete button clicked for shift:', shiftId); deleteShift(shiftId); } else if (e.target.classList.contains('edit-shift-btn')) { const shiftId = e.target.getAttribute('data-shift-id'); console.log('Edit button clicked for shift:', shiftId); editShift(shiftId); } else if (e.target.classList.contains('manage-volunteers-btn')) { const shiftId = e.target.getAttribute('data-shift-id'); const shiftData = JSON.parse(e.target.getAttribute('data-shift').replace(/'/g, "'")); console.log('Manage volunteers clicked for shift:', shiftId); showShiftUserModal(shiftId, shiftData); } else if (e.target.classList.contains('copy-shift-link-btn')) { const shiftId = e.target.getAttribute('data-shift-id'); console.log('Copy link button clicked for shift:', shiftId); copyShiftLink(shiftId); } else if (e.target.classList.contains('open-shift-link-btn')) { const shiftId = e.target.getAttribute('data-shift-id'); console.log('Open link button clicked for shift:', shiftId); openShiftLink(shiftId); } }); } // Delete shift async function deleteShift(shiftId) { if (!confirm('Are you sure you want to delete this shift? All signups will be cancelled.')) { return; } try { const response = await fetch(`/api/shifts/admin/${shiftId}`, { method: 'DELETE' }); const data = await response.json(); if (data.success) { window.adminCore.showStatus('Shift deleted successfully', 'success'); await loadAdminShifts(); console.log('Refreshed shifts list after deleting shift'); } else { window.adminCore.showStatus(data.error || 'Failed to delete shift', 'error'); } } catch (error) { console.error('Error deleting shift:', error); window.adminCore.showStatus('Failed to delete shift', 'error'); } } // Edit shift async function editShift(shiftId) { try { // Find the shift in the current data const response = await fetch('/api/shifts/admin'); const data = await response.json(); if (!data.success) { window.adminCore.showStatus('Failed to load shift data', 'error'); return; } const shift = data.shifts.find(s => s.ID === parseInt(shiftId)); if (!shift) { window.adminCore.showStatus('Shift not found', 'error'); return; } // Set editing mode editingShiftId = shiftId; // Populate the form const titleInput = document.getElementById('shift-title'); const descInput = document.getElementById('shift-description'); const dateInput = document.getElementById('shift-date'); const startInput = document.getElementById('shift-start'); const endInput = document.getElementById('shift-end'); const locationInput = document.getElementById('shift-location'); const maxVolInput = document.getElementById('shift-max-volunteers'); if (titleInput) titleInput.value = shift.Title || ''; if (descInput) descInput.value = shift.Description || ''; if (dateInput) dateInput.value = shift.Date || ''; if (startInput) startInput.value = shift['Start Time'] || ''; if (endInput) endInput.value = shift['End Time'] || ''; if (locationInput) locationInput.value = shift.Location || ''; if (maxVolInput) maxVolInput.value = shift['Max Volunteers'] || ''; // Update public checkbox if it exists const publicCheckbox = document.getElementById('shift-is-public'); if (publicCheckbox) { publicCheckbox.checked = shift['Is Public'] !== false; } // Change submit button text const submitBtn = document.querySelector('#shift-form button[type="submit"]'); if (submitBtn) { submitBtn.textContent = 'Update Shift'; } // Remove editing class from any previous item document.querySelectorAll('.shift-admin-item.editing').forEach(el => { el.classList.remove('editing'); }); // Add editing class to current item const shiftElement = document.querySelector(`[data-shift-id="${shiftId}"]`); if (shiftElement) { const shiftItem = shiftElement.closest('.shift-admin-item'); if (shiftItem) { shiftItem.classList.add('editing'); } } // Scroll to form const form = document.getElementById('shift-form'); if (form) { form.scrollIntoView({ behavior: 'smooth' }); } window.adminCore.showStatus('Editing shift: ' + shift.Title, 'info'); } catch (error) { console.error('Error loading shift for edit:', error); window.adminCore.showStatus('Failed to load shift for editing', 'error'); } } // Create or update shift async function createShift(e) { e.preventDefault(); const titleInput = document.getElementById('shift-title'); const descInput = document.getElementById('shift-description'); const dateInput = document.getElementById('shift-date'); const startInput = document.getElementById('shift-start'); const endInput = document.getElementById('shift-end'); const locationInput = document.getElementById('shift-location'); const maxVolInput = document.getElementById('shift-max-volunteers'); const title = titleInput?.value; const description = descInput?.value; const date = dateInput?.value; const startTime = startInput?.value; const endTime = endInput?.value; const location = locationInput?.value; const maxVolunteers = maxVolInput?.value; // Get public checkbox value const publicCheckbox = document.getElementById('shift-is-public'); const isPublic = publicCheckbox?.checked ?? true; const shiftData = { title, description, date, startTime, endTime, location, maxVolunteers: parseInt(maxVolunteers), isPublic }; try { let response; if (editingShiftId) { // Update existing shift response = await fetch(`/api/shifts/admin/${editingShiftId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(shiftData) }); } else { // Create new shift response = await fetch('/api/shifts/admin', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(shiftData) }); } const data = await response.json(); if (data.success) { window.adminCore.showStatus(editingShiftId ? 'Shift updated successfully' : 'Shift created successfully', 'success'); clearShiftForm(); await loadAdminShifts(); console.log('Refreshed shifts list after saving shift'); } else { window.adminCore.showStatus(data.error || 'Failed to save shift', 'error'); } } catch (error) { console.error('Error saving shift:', error); window.adminCore.showStatus('Failed to save shift', 'error'); } } function clearShiftForm() { const form = document.getElementById('shift-form'); if (form) { form.reset(); // Reset editing state editingShiftId = null; // Reset submit button text const submitBtn = document.querySelector('#shift-form button[type="submit"]'); if (submitBtn) { submitBtn.textContent = 'Create Shift'; } // Remove editing class from any shift items document.querySelectorAll('.shift-admin-item.editing').forEach(el => { el.classList.remove('editing'); }); window.adminCore.showStatus('Form cleared', 'info'); } } // Public Shifts Functions function generateShiftPublicLink(shiftId) { const baseUrl = window.location.origin; return `${baseUrl}/public-shifts.html#shift-${shiftId}`; } function copyShiftLink(shiftId) { const link = generateShiftPublicLink(shiftId); navigator.clipboard.writeText(link).then(() => { window.adminCore.showStatus('Public shift link copied to clipboard!', 'success'); }).catch(() => { // Fallback for older browsers const textArea = document.createElement('textarea'); textArea.value = link; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); window.adminCore.showStatus('Public shift link copied to clipboard!', 'success'); }); } function openShiftLink(shiftId) { const link = generateShiftPublicLink(shiftId); window.open(link, '_blank'); } // Update the shift form to include Is Public checkbox function updateShiftFormWithPublicOption() { const form = document.getElementById('shift-form'); if (!form) return; // Check if public checkbox already exists if (document.getElementById('shift-is-public')) return; const maxVolunteersGroup = form.querySelector('.form-group:has(#shift-max-volunteers)'); if (maxVolunteersGroup) { const publicGroup = document.createElement('div'); publicGroup.className = 'form-group'; publicGroup.innerHTML = ` `; maxVolunteersGroup.insertAdjacentElement('afterend', publicGroup); } } // Setup shift event listeners function setupShiftEventListeners() { // Shift form submission const shiftForm = document.getElementById('shift-form'); if (shiftForm) { shiftForm.addEventListener('submit', createShift); } // Clear shift form button const clearShiftBtn = document.getElementById('clear-shift-form'); if (clearShiftBtn) { clearShiftBtn.addEventListener('click', function() { const wasEditing = editingShiftId !== null; clearShiftForm(); if (wasEditing) { window.adminCore.showStatus('Edit cancelled', 'info'); } }); } } // Export shift management functions window.adminShifts = { loadAdminShifts, displayAdminShifts, deleteShift, editShift, createShift, clearShiftForm, generateShiftPublicLink, copyShiftLink, openShiftLink, updateShiftFormWithPublicOption, setupShiftEventListeners, getEditingShiftId: () => editingShiftId, getCurrentShiftData: () => currentShiftData, getAllUsers: () => allUsers };