785 lines
31 KiB
JavaScript
785 lines
31 KiB
JavaScript
/**
|
|
* Admin Shift Volunteers Module
|
|
* Handles volunteer management modals and shift email functionality
|
|
*/
|
|
|
|
// Prevent double loading
|
|
if (window.adminShiftVolunteersLoading) {
|
|
console.warn('admin-shift-volunteers.js already loading, skipping...');
|
|
} else {
|
|
window.adminShiftVolunteersLoading = true;
|
|
|
|
// Check if adminCore module is available
|
|
console.log('🔄 Loading admin-shift-volunteers.js...');
|
|
console.log('📦 adminCore available:', !!window.adminCore);
|
|
|
|
if (!window.adminCore) {
|
|
console.error('adminCore module not found - admin-shift-volunteers.js depends on admin-core.js');
|
|
// Don't stop loading, just note the dependency issue
|
|
}
|
|
|
|
// Volunteer management state
|
|
let currentShiftData = null;
|
|
let allUsers = [];
|
|
|
|
// Safe wrapper for adminCore functions
|
|
function safeAdminCore(funcName, ...args) {
|
|
if (window.adminCore && typeof window.adminCore[funcName] === 'function') {
|
|
return window.adminCore[funcName](...args);
|
|
} else {
|
|
console.error(`adminCore.${funcName} not available`);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Load all users for the dropdown
|
|
async function loadAllUsers() {
|
|
try {
|
|
const response = await fetch('/api/users');
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
allUsers = data.users;
|
|
populateUserSelect();
|
|
} else {
|
|
console.error('Failed to load users:', data.error);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading users:', error);
|
|
}
|
|
}
|
|
|
|
// Populate user select dropdown
|
|
function populateUserSelect() {
|
|
const select = document.getElementById('user-select');
|
|
if (!select) return;
|
|
|
|
// Clear existing options except the first one
|
|
select.innerHTML = '<option value="">Select a user...</option>';
|
|
|
|
allUsers.forEach(user => {
|
|
const option = document.createElement('option');
|
|
option.value = user.email || user.Email;
|
|
option.textContent = `${user.name || user.Name || ''} (${user.email || user.Email})`;
|
|
select.appendChild(option);
|
|
});
|
|
}
|
|
|
|
// Show the shift user management modal
|
|
async function showShiftUserModal(shiftId, shiftData) {
|
|
currentShiftData = { ...shiftData, ID: shiftId };
|
|
|
|
// Update modal title and info using the new function
|
|
updateModalTitle();
|
|
|
|
// Load users if not already loaded
|
|
if (allUsers.length === 0) {
|
|
await loadAllUsers();
|
|
populateUserSelect();
|
|
}
|
|
|
|
// Display current volunteers
|
|
displayCurrentVolunteers(shiftData.signups || []);
|
|
|
|
// Show modal
|
|
const modal = document.getElementById('shift-user-modal');
|
|
if (modal) {
|
|
modal.style.display = 'flex';
|
|
}
|
|
}
|
|
|
|
// Display current volunteers in the modal
|
|
function displayCurrentVolunteers(volunteers) {
|
|
const container = document.getElementById('current-volunteers-list');
|
|
|
|
if (!container) return;
|
|
|
|
if (!volunteers || volunteers.length === 0) {
|
|
container.innerHTML = '<div class="no-volunteers">No volunteers signed up yet.</div>';
|
|
return;
|
|
}
|
|
|
|
container.innerHTML = volunteers.map(volunteer => `
|
|
<div class="volunteer-item">
|
|
<div class="volunteer-info">
|
|
<div class="volunteer-name">${safeAdminCore('escapeHtml', volunteer['User Name'] || volunteer['User Email'] || 'Unknown') || 'Unknown'}</div>
|
|
<div class="volunteer-email">${safeAdminCore('escapeHtml', volunteer['User Email']) || volunteer['User Email'] || ''}</div>
|
|
</div>
|
|
<div class="volunteer-actions">
|
|
<div class="volunteer-communication-actions">
|
|
<a href="mailto:${safeAdminCore('escapeHtml', volunteer['User Email']) || volunteer['User Email'] || ''}"
|
|
class="btn btn-sm btn-outline-primary"
|
|
title="Email ${safeAdminCore('escapeHtml', volunteer['User Name'] || volunteer['User Email'] || 'Volunteer') || 'Volunteer'}">
|
|
📧
|
|
</a>
|
|
<button class="btn btn-sm btn-outline-secondary sms-volunteer-btn"
|
|
data-volunteer-email="${volunteer['User Email']}"
|
|
data-volunteer-name="${safeAdminCore('escapeHtml', volunteer['User Name'] || volunteer['User Email'] || 'Volunteer') || 'Volunteer'}"
|
|
title="Text ${safeAdminCore('escapeHtml', volunteer['User Name'] || volunteer['User Email'] || 'Volunteer') || 'Volunteer'} (Phone lookup required)">
|
|
💬
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-success call-volunteer-btn"
|
|
data-volunteer-email="${volunteer['User Email']}"
|
|
data-volunteer-name="${safeAdminCore('escapeHtml', volunteer['User Name'] || volunteer['User Email'] || 'Volunteer') || 'Volunteer'}"
|
|
title="Call ${safeAdminCore('escapeHtml', volunteer['User Name'] || volunteer['User Email'] || 'Volunteer') || 'Volunteer'} (Phone lookup required)">
|
|
📞
|
|
</button>
|
|
</div>
|
|
<div class="volunteer-admin-actions">
|
|
<button class="btn btn-danger btn-sm remove-volunteer-btn"
|
|
data-volunteer-id="${volunteer.ID || volunteer.id}"
|
|
data-volunteer-email="${volunteer['User Email']}">
|
|
Remove
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
|
|
// Add event listeners for remove buttons
|
|
setupVolunteerActionListeners();
|
|
}
|
|
|
|
// Setup event listeners for volunteer actions
|
|
function setupVolunteerActionListeners() {
|
|
const container = document.getElementById('current-volunteers-list');
|
|
|
|
if (container) {
|
|
container.addEventListener('click', function(e) {
|
|
if (e.target.classList.contains('remove-volunteer-btn')) {
|
|
const volunteerId = e.target.getAttribute('data-volunteer-id');
|
|
const volunteerEmail = e.target.getAttribute('data-volunteer-email');
|
|
removeVolunteerFromShift(volunteerId, volunteerEmail);
|
|
} else if (e.target.classList.contains('sms-volunteer-btn')) {
|
|
const volunteerEmail = e.target.getAttribute('data-volunteer-email');
|
|
const volunteerName = e.target.getAttribute('data-volunteer-name');
|
|
openVolunteerSMS(volunteerEmail, volunteerName);
|
|
} else if (e.target.classList.contains('call-volunteer-btn')) {
|
|
const volunteerEmail = e.target.getAttribute('data-volunteer-email');
|
|
const volunteerName = e.target.getAttribute('data-volunteer-name');
|
|
callVolunteer(volunteerEmail, volunteerName);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Add user to shift
|
|
async function addUserToShift() {
|
|
const userSelect = document.getElementById('user-select');
|
|
const userEmail = userSelect?.value;
|
|
|
|
if (!userEmail) {
|
|
safeAdminCore('showStatus', 'Please select a user to add', 'error');
|
|
return;
|
|
}
|
|
|
|
if (!currentShiftData || !currentShiftData.ID) {
|
|
safeAdminCore('showStatus', 'No shift selected or invalid shift data', 'error');
|
|
console.error('Invalid currentShiftData:', currentShiftData);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`/api/shifts/admin/${currentShiftData.ID}/add-user`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({ userEmail })
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
window.adminCore.showStatus('User successfully added to shift', 'success');
|
|
if (userSelect) userSelect.value = ''; // Clear selection
|
|
|
|
// Refresh the shift data and reload volunteers with better error handling
|
|
try {
|
|
await refreshCurrentShiftData();
|
|
console.log('Refreshed shift data after adding user');
|
|
|
|
// Also update the modal title to reflect new volunteer count
|
|
updateModalTitle();
|
|
} catch (refreshError) {
|
|
console.error('Error during refresh after adding user:', refreshError);
|
|
// Still show success since the add operation worked
|
|
}
|
|
} else {
|
|
window.adminCore.showStatus(data.error || 'Failed to add user to shift', 'error');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error adding user to shift:', error);
|
|
window.adminCore.showStatus('Failed to add user to shift', 'error');
|
|
}
|
|
}
|
|
|
|
// Remove volunteer from shift
|
|
async function removeVolunteerFromShift(volunteerId, volunteerEmail) {
|
|
if (!confirm(`Are you sure you want to remove ${volunteerEmail} from this shift?`)) {
|
|
return;
|
|
}
|
|
|
|
if (!currentShiftData || !currentShiftData.ID) {
|
|
window.adminCore.showStatus('No shift selected or invalid shift data', 'error');
|
|
console.error('Invalid currentShiftData:', currentShiftData);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`/api/shifts/admin/${currentShiftData.ID}/remove-user/${volunteerId}`, {
|
|
method: 'DELETE'
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
window.adminCore.showStatus('Volunteer successfully removed from shift', 'success');
|
|
|
|
// Refresh the shift data and reload volunteers with better error handling
|
|
try {
|
|
await refreshCurrentShiftData();
|
|
console.log('Refreshed shift data after removing volunteer');
|
|
|
|
// Also update the modal title to reflect new volunteer count
|
|
updateModalTitle();
|
|
} catch (refreshError) {
|
|
console.error('Error during refresh after removing volunteer:', refreshError);
|
|
// Still show success since the remove operation worked
|
|
}
|
|
} else {
|
|
window.adminCore.showStatus(data.error || 'Failed to remove volunteer from shift', 'error');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error removing volunteer from shift:', error);
|
|
window.adminCore.showStatus('Failed to remove volunteer from shift', 'error');
|
|
}
|
|
}
|
|
|
|
// Refresh current shift data
|
|
async function refreshCurrentShiftData() {
|
|
if (!currentShiftData || !currentShiftData.ID) {
|
|
console.warn('No current shift data or missing ID, skipping refresh');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
console.log('Refreshing shift data for shift ID:', currentShiftData.ID);
|
|
|
|
// Instead of reloading ALL admin shifts, just get this specific shift's signups
|
|
// This prevents the expensive backend call and reduces the refresh cascade
|
|
const response = await fetch(`/api/shifts/admin`);
|
|
const data = await response.json();
|
|
|
|
if (data.success && data.shifts && Array.isArray(data.shifts)) {
|
|
const updatedShift = data.shifts.find(s => s && s.ID === currentShiftData.ID);
|
|
if (updatedShift) {
|
|
console.log('Found updated shift with', updatedShift.signups?.length || 0, 'volunteers');
|
|
currentShiftData = updatedShift;
|
|
displayCurrentVolunteers(updatedShift.signups || []);
|
|
|
|
// Only update the specific shift in the main list, don't refresh everything
|
|
updateShiftInList(updatedShift);
|
|
} else {
|
|
console.warn('Could not find updated shift with ID:', currentShiftData.ID);
|
|
}
|
|
} else {
|
|
console.error('Failed to refresh shift data:', data.error || 'Invalid response format');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error refreshing shift data:', error);
|
|
}
|
|
}
|
|
|
|
// Update a single shift in the list without full refresh
|
|
function updateShiftInList(updatedShift) {
|
|
const shiftElement = document.querySelector(`[data-shift-id="${updatedShift.ID}"]`);
|
|
if (shiftElement) {
|
|
const shiftItem = shiftElement.closest('.shift-admin-item');
|
|
if (shiftItem) {
|
|
const signupCount = updatedShift.signups ? updatedShift.signups.length : 0;
|
|
|
|
// Generate list of first names for volunteers (same logic as displayAdminShifts)
|
|
const firstNames = updatedShift.signups ? updatedShift.signups.map(volunteer => {
|
|
const fullName = volunteer['User Name'] || volunteer['User Email'] || 'Unknown';
|
|
// Extract first name (everything before first space, or email username if no space)
|
|
const firstName = fullName.includes(' ') ? fullName.split(' ')[0] :
|
|
fullName.includes('@') ? fullName.split('@')[0] : fullName;
|
|
return safeAdminCore('escapeHtml', firstName) || firstName;
|
|
}).slice(0, 8) : []; // Limit to first 8 names to avoid overflow
|
|
|
|
const namesDisplay = firstNames.length > 0 ?
|
|
`<span class="volunteer-names">(${firstNames.join(', ')}${firstNames.length === 8 && signupCount > 8 ? '...' : ''})</span>` :
|
|
'';
|
|
|
|
// Find the volunteer count paragraph (contains 👥)
|
|
const volunteerCountElement = Array.from(shiftItem.querySelectorAll('p')).find(p =>
|
|
p.textContent.includes('👥') || p.classList.contains('volunteer-count')
|
|
);
|
|
|
|
if (volunteerCountElement) {
|
|
volunteerCountElement.innerHTML = `👥 ${signupCount}/${updatedShift['Max Volunteers']} volunteers ${namesDisplay}`;
|
|
volunteerCountElement.className = 'volunteer-count'; // Ensure class is set
|
|
}
|
|
|
|
// Update the data attribute with new shift data
|
|
const manageBtn = shiftItem.querySelector('.manage-volunteers-btn');
|
|
if (manageBtn) {
|
|
manageBtn.setAttribute('data-shift', JSON.stringify(updatedShift).replace(/'/g, "'"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update modal title with current volunteer count
|
|
function updateModalTitle() {
|
|
if (!currentShiftData) return;
|
|
|
|
const modalTitle = document.getElementById('modal-shift-title');
|
|
const modalDetails = document.getElementById('modal-shift-details');
|
|
|
|
if (modalTitle) {
|
|
const signupCount = currentShiftData.signups ? currentShiftData.signups.length : 0;
|
|
modalTitle.textContent = `Manage Volunteers - ${currentShiftData.Title} (${signupCount}/${currentShiftData['Max Volunteers']})`;
|
|
}
|
|
|
|
if (modalDetails) {
|
|
const shiftDate = safeAdminCore('createLocalDate', currentShiftData.Date);
|
|
const dateStr = shiftDate ? shiftDate.toLocaleDateString() : currentShiftData.Date;
|
|
const signupCount = currentShiftData.signups ? currentShiftData.signups.length : 0;
|
|
|
|
modalDetails.innerHTML = `
|
|
<p><strong>Date:</strong> ${dateStr}</p>
|
|
<p><strong>Time:</strong> ${currentShiftData['Start Time']} - ${currentShiftData['End Time']}</p>
|
|
<p><strong>Location:</strong> ${safeAdminCore('escapeHtml', currentShiftData.Location || 'TBD') || currentShiftData.Location || 'TBD'}</p>
|
|
<p><strong>Current Signups:</strong> ${signupCount} / ${currentShiftData['Max Volunteers']}</p>
|
|
`;
|
|
}
|
|
}
|
|
|
|
// Close modal
|
|
function closeShiftUserModal() {
|
|
const modal = document.getElementById('shift-user-modal');
|
|
if (modal) {
|
|
modal.style.display = 'none';
|
|
}
|
|
currentShiftData = null;
|
|
|
|
// Don't refresh the entire shifts list when closing modal
|
|
// The shifts list should already be up to date from the individual updates
|
|
console.log('Modal closed - shifts list should already be current');
|
|
}
|
|
|
|
// Communication functions for individual volunteers
|
|
async function openVolunteerSMS(volunteerEmail, volunteerName) {
|
|
try {
|
|
// Look up the volunteer's phone number from the users database
|
|
const user = await getUserByEmail(volunteerEmail);
|
|
|
|
if (user && (user.phone || user.Phone)) {
|
|
const phoneNumber = user.phone || user.Phone;
|
|
const smsUrl = `sms:${phoneNumber}`;
|
|
window.open(smsUrl, '_self');
|
|
} else {
|
|
safeAdminCore('showStatus', `No phone number found for ${volunteerName}`, 'warning');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error looking up volunteer phone number:', error);
|
|
safeAdminCore('showStatus', 'Failed to lookup volunteer phone number', 'error');
|
|
}
|
|
}
|
|
|
|
async function callVolunteer(volunteerEmail, volunteerName) {
|
|
try {
|
|
// Look up the volunteer's phone number from the users database
|
|
const user = await getUserByEmail(volunteerEmail);
|
|
|
|
if (user && (user.phone || user.Phone)) {
|
|
const phoneNumber = user.phone || user.Phone;
|
|
const telUrl = `tel:${phoneNumber}`;
|
|
window.open(telUrl, '_self');
|
|
} else {
|
|
safeAdminCore('showStatus', `No phone number found for ${volunteerName}`, 'warning');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error looking up volunteer phone number:', error);
|
|
safeAdminCore('showStatus', 'Failed to lookup volunteer phone number', 'error');
|
|
}
|
|
}
|
|
|
|
// Helper function to get user details by email
|
|
async function getUserByEmail(email) {
|
|
try {
|
|
const response = await fetch('/api/users');
|
|
const data = await response.json();
|
|
|
|
if (data.success && data.users) {
|
|
return data.users.find(user =>
|
|
(user.email === email || user.Email === email)
|
|
);
|
|
}
|
|
return null;
|
|
} catch (error) {
|
|
console.error('Error fetching users:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Email shift details to all volunteers
|
|
async function emailShiftDetails() {
|
|
if (!currentShiftData) {
|
|
window.adminCore.showStatus('No shift selected', 'error');
|
|
return;
|
|
}
|
|
|
|
// Check if there are volunteers to email
|
|
const volunteers = currentShiftData.signups || [];
|
|
if (volunteers.length === 0) {
|
|
window.adminCore.showStatus('No volunteers signed up for this shift', 'error');
|
|
return;
|
|
}
|
|
|
|
// Confirm action
|
|
const confirmMessage = `Send shift details email to ${volunteers.length} volunteer${volunteers.length !== 1 ? 's' : ''}?`;
|
|
if (!confirm(confirmMessage)) {
|
|
return;
|
|
}
|
|
|
|
// Initialize progress tracking for shift emails
|
|
initializeShiftEmailProgress(volunteers.length);
|
|
|
|
try {
|
|
const response = await fetch(`/api/shifts/admin/${currentShiftData.ID}/email-details`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
}
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
// Display detailed results
|
|
updateShiftEmailProgress(data.results);
|
|
window.adminCore.showStatus(data.message, 'success');
|
|
console.log('Email results:', data.results);
|
|
} else {
|
|
showShiftEmailError(data.error || 'Failed to send emails');
|
|
if (data.details) {
|
|
console.error('Failed email details:', data.details);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error sending shift details emails:', error);
|
|
showShiftEmailError('Failed to send emails - Network error');
|
|
}
|
|
}
|
|
|
|
// Initialize shift email progress display
|
|
function initializeShiftEmailProgress(totalCount) {
|
|
const progressContainer = document.getElementById('shift-email-progress-container');
|
|
const statusList = document.getElementById('shift-email-status-list');
|
|
const pendingCountEl = document.getElementById('shift-pending-count');
|
|
const successCountEl = document.getElementById('shift-success-count');
|
|
const errorCountEl = document.getElementById('shift-error-count');
|
|
const progressBar = document.getElementById('shift-email-progress-bar');
|
|
const progressText = document.getElementById('shift-progress-text');
|
|
const closeBtn = document.getElementById('close-shift-progress-btn');
|
|
|
|
if (!progressContainer) return;
|
|
|
|
// Show progress container
|
|
progressContainer.classList.add('show');
|
|
|
|
// Reset counters
|
|
if (pendingCountEl) pendingCountEl.textContent = totalCount;
|
|
if (successCountEl) successCountEl.textContent = '0';
|
|
if (errorCountEl) errorCountEl.textContent = '0';
|
|
|
|
// Reset progress bar
|
|
if (progressBar) {
|
|
progressBar.style.width = '0%';
|
|
progressBar.classList.remove('complete', 'error');
|
|
}
|
|
if (progressText) progressText.textContent = '0%';
|
|
|
|
// Clear status list
|
|
if (statusList) statusList.innerHTML = '';
|
|
|
|
// Hide close button initially
|
|
if (closeBtn) closeBtn.style.display = 'none';
|
|
|
|
// Add status items for each volunteer
|
|
const volunteers = currentShiftData.signups || [];
|
|
volunteers.forEach(volunteer => {
|
|
if (statusList) {
|
|
const statusItem = document.createElement('div');
|
|
statusItem.className = 'email-status-item';
|
|
statusItem.innerHTML = `
|
|
<div class="email-status-recipient">${volunteer['User Name'] || volunteer['User Email']}</div>
|
|
<div class="email-status-result pending">
|
|
<div class="progress-spinner"></div>
|
|
<span>Sending...</span>
|
|
</div>
|
|
`;
|
|
statusList.appendChild(statusItem);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Update shift email progress with results
|
|
function updateShiftEmailProgress(results) {
|
|
const statusList = document.getElementById('shift-email-status-list');
|
|
const pendingCountEl = document.getElementById('shift-pending-count');
|
|
const successCountEl = document.getElementById('shift-success-count');
|
|
const errorCountEl = document.getElementById('shift-error-count');
|
|
const progressBar = document.getElementById('shift-email-progress-bar');
|
|
const progressText = document.getElementById('shift-progress-text');
|
|
const closeBtn = document.getElementById('close-shift-progress-btn');
|
|
|
|
const successful = results.successful || [];
|
|
const failed = results.failed || [];
|
|
const total = results.total || (successful.length + failed.length);
|
|
|
|
// Update counters
|
|
if (successCountEl) successCountEl.textContent = successful.length;
|
|
if (errorCountEl) errorCountEl.textContent = failed.length;
|
|
if (pendingCountEl) pendingCountEl.textContent = '0';
|
|
|
|
// Update progress bar
|
|
const percentage = ((successful.length + failed.length) / total * 100).toFixed(1);
|
|
if (progressBar) {
|
|
progressBar.style.width = percentage + '%';
|
|
progressText.textContent = percentage + '%';
|
|
|
|
if (failed.length > 0) {
|
|
progressBar.classList.add('error');
|
|
} else {
|
|
progressBar.classList.add('complete');
|
|
}
|
|
}
|
|
|
|
// Update individual status items
|
|
if (statusList) {
|
|
const statusItems = statusList.children;
|
|
|
|
// Update successful emails
|
|
successful.forEach(result => {
|
|
const statusItem = Array.from(statusItems).find(item =>
|
|
item.querySelector('.email-status-recipient').textContent.includes(result.email) ||
|
|
item.querySelector('.email-status-recipient').textContent.includes(result.name)
|
|
);
|
|
if (statusItem) {
|
|
statusItem.querySelector('.email-status-result').innerHTML = `
|
|
<span class="email-status-result success">✓ Sent</span>
|
|
`;
|
|
}
|
|
});
|
|
|
|
// Update failed emails
|
|
failed.forEach(result => {
|
|
const statusItem = Array.from(statusItems).find(item =>
|
|
item.querySelector('.email-status-recipient').textContent.includes(result.email) ||
|
|
item.querySelector('.email-status-recipient').textContent.includes(result.name)
|
|
);
|
|
if (statusItem) {
|
|
statusItem.querySelector('.email-status-result').innerHTML = `
|
|
<span class="email-status-result error" title="${result.error || 'Unknown error'}">✗ Failed</span>
|
|
`;
|
|
}
|
|
});
|
|
}
|
|
|
|
// Show close button
|
|
if (closeBtn) {
|
|
closeBtn.style.display = 'block';
|
|
closeBtn.onclick = () => {
|
|
const progressContainer = document.getElementById('shift-email-progress-container');
|
|
if (progressContainer) {
|
|
progressContainer.classList.remove('show');
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
// Show shift email error
|
|
function showShiftEmailError(message) {
|
|
const progressContainer = document.getElementById('shift-email-progress-container');
|
|
const progressBar = document.getElementById('shift-email-progress-bar');
|
|
const progressText = document.getElementById('shift-progress-text');
|
|
const closeBtn = document.getElementById('close-shift-progress-btn');
|
|
|
|
// Show progress container if not visible
|
|
if (progressContainer) {
|
|
progressContainer.classList.add('show');
|
|
}
|
|
|
|
// Update progress bar to show error
|
|
if (progressBar) {
|
|
progressBar.style.width = '100%';
|
|
progressBar.classList.add('error');
|
|
}
|
|
if (progressText) progressText.textContent = 'Error';
|
|
|
|
// Show close button
|
|
if (closeBtn) {
|
|
closeBtn.style.display = 'block';
|
|
closeBtn.onclick = () => {
|
|
if (progressContainer) {
|
|
progressContainer.classList.remove('show');
|
|
}
|
|
};
|
|
}
|
|
|
|
window.adminCore.showStatus(message, 'error');
|
|
}
|
|
|
|
// Setup volunteer modal event listeners
|
|
function setupVolunteerModalEventListeners() {
|
|
const closeModalBtn = document.getElementById('close-user-modal');
|
|
const addUserBtn = document.getElementById('add-user-btn');
|
|
const emailShiftDetailsBtn = document.getElementById('email-shift-details-btn');
|
|
const modal = document.getElementById('shift-user-modal');
|
|
|
|
if (closeModalBtn) {
|
|
closeModalBtn.addEventListener('click', closeShiftUserModal);
|
|
}
|
|
|
|
if (addUserBtn) {
|
|
addUserBtn.addEventListener('click', addUserToShift);
|
|
}
|
|
|
|
if (emailShiftDetailsBtn) {
|
|
emailShiftDetailsBtn.addEventListener('click', emailShiftDetails);
|
|
}
|
|
|
|
// Close modal when clicking outside
|
|
if (modal) {
|
|
modal.addEventListener('click', function(e) {
|
|
if (e.target === modal) {
|
|
closeShiftUserModal();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Export shift volunteer functions
|
|
console.log('About to export adminShiftVolunteers module...');
|
|
console.log('Functions to export:', {
|
|
showShiftUserModal: typeof showShiftUserModal,
|
|
closeShiftUserModal: typeof closeShiftUserModal,
|
|
addUserToShift: typeof addUserToShift,
|
|
removeVolunteerFromShift: typeof removeVolunteerFromShift,
|
|
emailShiftDetails: typeof emailShiftDetails,
|
|
setupVolunteerModalEventListeners: typeof setupVolunteerModalEventListeners,
|
|
loadAllUsers: typeof loadAllUsers
|
|
});
|
|
|
|
// Ensure we have all required functions before exporting
|
|
const requiredFunctions = {
|
|
showShiftUserModal,
|
|
closeShiftUserModal,
|
|
addUserToShift,
|
|
removeVolunteerFromShift,
|
|
emailShiftDetails,
|
|
setupVolunteerModalEventListeners,
|
|
loadAllUsers
|
|
};
|
|
|
|
// Validate all functions exist
|
|
const missingFunctions = Object.entries(requiredFunctions)
|
|
.filter(([name, func]) => typeof func !== 'function')
|
|
.map(([name]) => name);
|
|
|
|
if (missingFunctions.length > 0) {
|
|
console.error('Missing functions in adminShiftVolunteers:', missingFunctions);
|
|
} else {
|
|
console.log('All required functions are available');
|
|
}
|
|
|
|
try {
|
|
// Clear any existing module to avoid conflicts
|
|
if (window.adminShiftVolunteers) {
|
|
console.log('Replacing existing adminShiftVolunteers module');
|
|
}
|
|
|
|
window.adminShiftVolunteers = {
|
|
showShiftUserModal,
|
|
closeShiftUserModal,
|
|
addUserToShift,
|
|
removeVolunteerFromShift,
|
|
emailShiftDetails,
|
|
setupVolunteerModalEventListeners,
|
|
loadAllUsers,
|
|
openVolunteerSMS,
|
|
callVolunteer,
|
|
getUserByEmail,
|
|
updateModalTitle,
|
|
updateShiftInList,
|
|
getCurrentShiftData: () => currentShiftData,
|
|
getAllUsers: () => allUsers,
|
|
// Add module info for debugging
|
|
moduleVersion: '1.2',
|
|
loadedAt: new Date().toISOString()
|
|
};
|
|
|
|
// Verify the export worked
|
|
if (window.adminShiftVolunteers && typeof window.adminShiftVolunteers.showShiftUserModal === 'function') {
|
|
console.log('✅ adminShiftVolunteers module loaded successfully');
|
|
console.log('✅ Available functions:', Object.keys(window.adminShiftVolunteers));
|
|
|
|
// Dispatch a custom event to signal the module is ready
|
|
setTimeout(() => {
|
|
window.dispatchEvent(new CustomEvent('adminShiftVolunteersReady', {
|
|
detail: {
|
|
module: 'adminShiftVolunteers',
|
|
functions: Object.keys(window.adminShiftVolunteers),
|
|
timestamp: Date.now()
|
|
}
|
|
}));
|
|
console.log('✅ adminShiftVolunteersReady event dispatched');
|
|
}, 50);
|
|
|
|
} else {
|
|
throw new Error('Module export verification failed');
|
|
}
|
|
} catch (error) {
|
|
console.error('❌ Error loading adminShiftVolunteers module:', error);
|
|
console.error('❌ Stack trace:', error.stack);
|
|
} finally {
|
|
// Mark loading as complete
|
|
window.adminShiftVolunteersLoading = false;
|
|
}
|
|
|
|
// Add a global debugging function
|
|
window.debugAdminShiftVolunteers = () => {
|
|
console.log('🔍 Admin Shift Volunteers Debug Info:');
|
|
console.log('Module exists:', !!window.adminShiftVolunteers);
|
|
if (window.adminShiftVolunteers) {
|
|
console.log('Available functions:', Object.keys(window.adminShiftVolunteers));
|
|
console.log('showShiftUserModal type:', typeof window.adminShiftVolunteers.showShiftUserModal);
|
|
console.log('Module version:', window.adminShiftVolunteers.moduleVersion);
|
|
console.log('Loaded at:', window.adminShiftVolunteers.loadedAt);
|
|
}
|
|
|
|
// Check if modal elements exist
|
|
console.log('Modal element exists:', !!document.getElementById('shift-user-modal'));
|
|
console.log('Modal title element exists:', !!document.getElementById('modal-shift-title'));
|
|
console.log('Volunteers list element exists:', !!document.getElementById('current-volunteers-list'));
|
|
|
|
// Check dependencies
|
|
console.log('adminCore exists:', !!window.adminCore);
|
|
console.log('Loading state:', window.adminShiftVolunteersLoading);
|
|
|
|
return {
|
|
moduleExists: !!window.adminShiftVolunteers,
|
|
functions: window.adminShiftVolunteers ? Object.keys(window.adminShiftVolunteers) : [],
|
|
modalExists: !!document.getElementById('shift-user-modal'),
|
|
dependencies: {
|
|
adminCore: !!window.adminCore
|
|
}
|
|
};
|
|
};
|
|
|
|
} // Close the initial if statement
|