fixed some of the loading bugs with shifts so that the map can load faster.

This commit is contained in:
2025-08-18 14:03:36 -06:00
parent c2ccddd1dc
commit 3b88eef397
4 changed files with 198 additions and 1254 deletions

View File

@@ -27,7 +27,7 @@ class ShiftsController {
// If signups sheet is configured, calculate current volunteer counts
if (config.nocodb.shiftSignupsSheetId) {
try {
const signupsResponse = await nocodbService.getAll(config.nocodb.shiftSignupsSheetId);
const signupsResponse = await nocodbService.getAllPaginated(config.nocodb.shiftSignupsSheetId);
const allSignups = signupsResponse.list || [];
// Update each shift with calculated volunteer count
@@ -78,7 +78,7 @@ class ShiftsController {
}
// Load all signups and filter in JavaScript
const allSignups = await nocodbService.getAll(config.nocodb.shiftSignupsSheetId, {
const allSignups = await nocodbService.getAllPaginated(config.nocodb.shiftSignupsSheetId, {
sort: '-Signup Date'
});
@@ -162,7 +162,7 @@ class ShiftsController {
let currentVolunteers = 0;
let allSignups = { list: [] }; // Initialize with empty list
if (config.nocodb.shiftSignupsSheetId) {
allSignups = await nocodbService.getAll(config.nocodb.shiftSignupsSheetId);
allSignups = await nocodbService.getAllPaginated(config.nocodb.shiftSignupsSheetId);
const confirmedSignups = (allSignups.list || []).filter(signup =>
signup['Shift ID'] === parseInt(shiftId) && signup.Status === 'Confirmed'
);
@@ -238,7 +238,7 @@ class ShiftsController {
logger.info(`User ${userEmail} attempting to cancel signup for shift ${shiftId}`);
// Find the signup
const allSignups = await nocodbService.getAll(config.nocodb.shiftSignupsSheetId);
const allSignups = await nocodbService.getAllPaginated(config.nocodb.shiftSignupsSheetId);
const signup = (allSignups.list || []).find(s => {
return s['Shift ID'] === parseInt(shiftId) &&
s['User Email'] === userEmail &&
@@ -258,7 +258,7 @@ class ShiftsController {
});
// Calculate current volunteers dynamically after cancellation
const allSignupsAfter = await nocodbService.getAll(config.nocodb.shiftSignupsSheetId);
const allSignupsAfter = await nocodbService.getAllPaginated(config.nocodb.shiftSignupsSheetId);
const confirmedSignupsAfter = (allSignupsAfter.list || []).filter(s =>
s['Shift ID'] === parseInt(shiftId) && s.Status === 'Confirmed'
);
@@ -380,7 +380,7 @@ class ShiftsController {
if (config.nocodb.shiftSignupsSheetId) {
try {
// Get all signups and filter in JavaScript to avoid NocoDB query issues
const allSignups = await nocodbService.getAll(config.nocodb.shiftSignupsSheetId);
const allSignups = await nocodbService.getAllPaginated(config.nocodb.shiftSignupsSheetId);
// Filter for confirmed signups for this shift
const signupsToCancel = (allSignups.list || []).filter(signup =>
@@ -455,88 +455,47 @@ class ShiftsController {
}
}
logger.info('Loaded shifts:', shifts);
logger.info('Loaded shifts:', shifts.list?.length || 0, 'records');
// Only try to get signups if the signups sheet is configured
if (config.nocodb.shiftSignupsSheetId) {
// Get signup counts for each shift
for (const shift of shifts.list || []) {
try {
// Use getAllPaginated to ensure we get ALL signup records
const signups = await nocodbService.getAllPaginated(config.nocodb.shiftSignupsSheetId);
// Debug logging for shift ID 4 (Sunday Evening Canvass Central Location)
if (shift.ID === 4) {
// Show ALL signups first
logger.info(`Debug: Shift ID 4 - All signups from NocoDB (total ${signups.list?.length || 0})`);
// Show only signups for shift ID 4 (before status filter)
const shift4Signups = (signups.list || []).filter(signup =>
parseInt(signup['Shift ID']) === 4
);
logger.info(`Debug: Shift ID 4 - All signups for this shift (${shift4Signups.length}):`);
shift4Signups.forEach((s, index) => {
logger.info(` Signup ${index + 1}:`, {
ID: s.ID,
'Shift ID': s['Shift ID'],
'Status': `"${s.Status}"`,
'Status Length': s.Status ? s.Status.length : 'null',
'Status Chars': s.Status ? Array.from(s.Status).map(c => c.charCodeAt(0)) : 'null',
'User Email': s['User Email'],
'User Name': s['User Name'],
'Signup Date': s['Signup Date']
});
});
try {
// Get ALL signups once instead of querying for each shift
logger.info('Loading all signups once for performance optimization...');
const allSignups = await nocodbService.getAllPaginated(config.nocodb.shiftSignupsSheetId);
logger.info(`Loaded ${allSignups.list?.length || 0} total signups from database`);
// Group signups by shift ID for efficient processing
const signupsByShift = {};
(allSignups.list || []).forEach(signup => {
const shiftId = parseInt(signup['Shift ID']);
if (!signupsByShift[shiftId]) {
signupsByShift[shiftId] = [];
}
// Filter signups for this shift manually with more robust checking
const shiftSignups = (signups.list || []).filter(signup => {
// Handle type conversion for Shift ID comparison
const signupShiftId = parseInt(signup['Shift ID']);
const currentShiftId = parseInt(shift.ID);
// Only process signups for this specific shift
if (signupShiftId !== currentShiftId) {
return false;
}
// For shift ID 4, let's check all possible status variations
if (currentShiftId === 4) {
const signupStatus = (signup.Status || '').toString().trim();
const isConfirmed = signupStatus.toLowerCase() === 'confirmed';
logger.info(`Debug: Shift ID 4 - Checking signup:`, {
'User Email': signup['User Email'],
'Status Raw': `"${signup.Status}"`,
'Status Trimmed': `"${signupStatus}"`,
'Status Lower': `"${signupStatus.toLowerCase()}"`,
'Is Confirmed': isConfirmed
});
return isConfirmed;
}
// Handle multiple possible "confirmed" status values for other shifts
const signupStatus = (signup.Status || '').toString().toLowerCase().trim();
const isConfirmed = signupStatus === 'confirmed' || signupStatus === 'active' ||
(signupStatus === '' && signup['User Email']); // Include records with empty status if they have an email
return isConfirmed;
});
// Only include confirmed signups
const signupStatus = (signup.Status || '').toString().toLowerCase().trim();
const isConfirmed = signupStatus === 'confirmed' || signupStatus === 'active' ||
(signupStatus === '' && signup['User Email']); // Include records with empty status if they have an email
// Debug logging for shift ID 4
if (shift.ID === 4) {
logger.info(`Debug: Shift ID 4 - Filtered signups (${shiftSignups.length}):`, shiftSignups.map(s => ({
'Shift ID': s['Shift ID'],
'Status': s.Status,
'User Email': s['User Email'],
'User Name': s['User Name']
})));
if (isConfirmed) {
signupsByShift[shiftId].push(signup);
}
shift.signups = shiftSignups;
} catch (signupError) {
logger.error(`Error loading signups for shift ${shift.ID}:`, signupError);
});
// Assign signups to each shift
for (const shift of shifts.list || []) {
const shiftId = parseInt(shift.ID);
shift.signups = signupsByShift[shiftId] || [];
}
logger.info(`Processed signups for ${Object.keys(signupsByShift).length} shifts`);
} catch (signupError) {
logger.error('Error loading signups:', signupError);
// Set empty signups for all shifts on error
for (const shift of shifts.list || []) {
shift.signups = [];
}
}
@@ -601,7 +560,7 @@ class ShiftsController {
}
// Check if user is already signed up
const allSignups = await nocodbService.getAll(config.nocodb.shiftSignupsSheetId);
const allSignups = await nocodbService.getAllPaginated(config.nocodb.shiftSignupsSheetId);
const existingSignup = (allSignups.list || []).find(signup => {
return signup['Shift ID'] === parseInt(shiftId) &&
signup['User Email'] === userEmail &&
@@ -695,7 +654,7 @@ class ShiftsController {
});
// Update shift volunteer count
const allSignups = await nocodbService.getAll(config.nocodb.shiftSignupsSheetId);
const allSignups = await nocodbService.getAllPaginated(config.nocodb.shiftSignupsSheetId);
const confirmedSignups = (allSignups.list || []).filter(s =>
s['Shift ID'] === parseInt(shiftId) && s.Status === 'Confirmed'
);
@@ -743,7 +702,7 @@ class ShiftsController {
}
// Get all confirmed signups for this shift
const allSignups = await nocodbService.getAll(config.nocodb.shiftSignupsSheetId);
const allSignups = await nocodbService.getAllPaginated(config.nocodb.shiftSignupsSheetId);
const shiftSignups = (allSignups.list || []).filter(signup =>
signup['Shift ID'] === parseInt(shiftId) && signup.Status === 'Confirmed'
);

View File

@@ -2192,8 +2192,9 @@ async function addUserToShift() {
return;
}
if (!currentShiftData) {
showStatus('No shift selected', 'error');
if (!currentShiftData || !currentShiftData.ID) {
showStatus('No shift selected or invalid shift data', 'error');
console.error('Invalid currentShiftData:', currentShiftData);
return;
}
@@ -2212,9 +2213,14 @@ async function addUserToShift() {
showStatus('User successfully added to shift', 'success');
userSelect.value = ''; // Clear selection
// Refresh the shift data and reload volunteers
await refreshCurrentShiftData();
console.log('Refreshed shift data after adding user');
// Refresh the shift data and reload volunteers with better error handling
try {
await refreshCurrentShiftData();
console.log('Refreshed shift data after adding user');
} catch (refreshError) {
console.error('Error during refresh after adding user:', refreshError);
// Still show success since the add operation worked
}
} else {
showStatus(data.error || 'Failed to add user to shift', 'error');
}
@@ -2230,8 +2236,9 @@ async function removeVolunteerFromShift(volunteerId, volunteerEmail) {
return;
}
if (!currentShiftData) {
showStatus('No shift selected', 'error');
if (!currentShiftData || !currentShiftData.ID) {
showStatus('No shift selected or invalid shift data', 'error');
console.error('Invalid currentShiftData:', currentShiftData);
return;
}
@@ -2245,9 +2252,14 @@ async function removeVolunteerFromShift(volunteerId, volunteerEmail) {
if (data.success) {
showStatus('Volunteer successfully removed from shift', 'success');
// Refresh the shift data and reload volunteers
await refreshCurrentShiftData();
console.log('Refreshed shift data after removing volunteer');
// Refresh the shift data and reload volunteers with better error handling
try {
await refreshCurrentShiftData();
console.log('Refreshed shift data after removing volunteer');
} catch (refreshError) {
console.error('Error during refresh after removing volunteer:', refreshError);
// Still show success since the remove operation worked
}
} else {
showStatus(data.error || 'Failed to remove volunteer from shift', 'error');
}
@@ -2259,45 +2271,73 @@ async function removeVolunteerFromShift(volunteerId, volunteerEmail) {
// Refresh current shift data
async function refreshCurrentShiftData() {
if (!currentShiftData) return;
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);
// Reload admin shifts to get updated data
const response = await fetch('/api/shifts/admin');
// 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) {
const updatedShift = data.shifts.find(s => s.ID === currentShiftData.ID);
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 || []);
// Immediately refresh the main shifts list to show updated counts
console.log('Refreshing main shifts list with', data.shifts.length, 'shifts');
displayAdminShifts(data.shifts);
// 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);
console.error('Failed to refresh shift data:', data.error || 'Invalid response format');
}
} catch (error) {
console.error('Error refreshing shift data:', error);
}
}
// New function to 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;
// Find the volunteer count paragraph (contains 👥)
const volunteerCountElement = Array.from(shiftItem.querySelectorAll('p')).find(p =>
p.textContent.includes('👥')
);
if (volunteerCountElement) {
volunteerCountElement.textContent = `👥 ${signupCount}/${updatedShift['Max Volunteers']} volunteers`;
}
// 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, "'"));
}
}
}
}
// Close modal
function closeShiftUserModal() {
document.getElementById('shift-user-modal').style.display = 'none';
currentShiftData = null;
// Refresh the main shifts list one more time when closing the modal
// to ensure any changes are reflected
console.log('Refreshing shifts list on modal close');
loadAdminShifts();
// 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');
}
// Email shift details to all volunteers

File diff suppressed because it is too large Load Diff