diff --git a/src/components/dashboard/Officer_EventRequestForm/EventRequestForm.tsx b/src/components/dashboard/Officer_EventRequestForm/EventRequestForm.tsx index 0e84cd7..2d2d578 100644 --- a/src/components/dashboard/Officer_EventRequestForm/EventRequestForm.tsx +++ b/src/components/dashboard/Officer_EventRequestForm/EventRequestForm.tsx @@ -136,7 +136,8 @@ const EventRequestForm: React.FC = () => { other_logos: [], room_booking_files: [], invoice: null, - invoice_files: [] + invoice_files: [], + savedAt: Date.now() // Add timestamp for stale data detection }; localStorage.setItem('eventRequestFormData', JSON.stringify(dataToStore)); @@ -153,12 +154,27 @@ const EventRequestForm: React.FC = () => { if (savedData) { try { const parsedData = JSON.parse(savedData); - setFormData(prevData => ({ - ...prevData, - ...parsedData - })); + + // Check if the saved data is stale (older than 24 hours) + const now = Date.now(); + const savedTime = parsedData.savedAt || 0; + const staleThreshold = 24 * 60 * 60 * 1000; // 24 hours + + if (now - savedTime > staleThreshold) { + // Clear stale data + localStorage.removeItem('eventRequestFormData'); + console.log('Cleared stale form data from localStorage'); + } else { + // Load the saved data + setFormData(prevData => ({ + ...prevData, + ...parsedData + })); + } } catch (e) { console.error('Error parsing saved form data:', e); + // Clear corrupted data + localStorage.removeItem('eventRequestFormData'); } } }, []); @@ -186,7 +202,8 @@ const EventRequestForm: React.FC = () => { other_logos: [], room_booking_files: [], invoice: null, - invoice_files: [] + invoice_files: [], + savedAt: Date.now() // Add timestamp for stale data detection }; localStorage.setItem('eventRequestFormData', JSON.stringify(dataToStore)); @@ -355,8 +372,8 @@ const EventRequestForm: React.FC = () => { // This will send the data to the server const record = await update.create('event_request', submissionData); - // Force sync the event requests collection to update IndexedDB - await dataSync.syncCollection(Collections.EVENT_REQUESTS); + // Force sync the event requests collection to update IndexedDB with deletion detection + await dataSync.syncCollection(Collections.EVENT_REQUESTS, "", "-created", {}, true); console.log('Event request record created:', record.id); diff --git a/src/components/dashboard/Officer_EventRequestForm/UserEventRequests.tsx b/src/components/dashboard/Officer_EventRequestForm/UserEventRequests.tsx index f29d0d8..32db23a 100644 --- a/src/components/dashboard/Officer_EventRequestForm/UserEventRequests.tsx +++ b/src/components/dashboard/Officer_EventRequestForm/UserEventRequests.tsx @@ -258,12 +258,14 @@ const UserEventRequests: React.FC = ({ eventRequests: in return; } - // Use DataSyncService to get data from IndexedDB with forced sync + // Use DataSyncService to get data from IndexedDB with forced sync and deletion detection const updatedRequests = await dataSync.getData( Collections.EVENT_REQUESTS, true, // Force sync `requested_user="${userId}"`, - '-created' + '-created', + {}, // expand + true // Enable deletion detection for user-specific requests ); setEventRequests(updatedRequests); diff --git a/src/components/dashboard/Officer_EventRequestManagement/EventRequestManagementTable.tsx b/src/components/dashboard/Officer_EventRequestManagement/EventRequestManagementTable.tsx index 3551531..53cd8cd 100644 --- a/src/components/dashboard/Officer_EventRequestManagement/EventRequestManagementTable.tsx +++ b/src/components/dashboard/Officer_EventRequestManagement/EventRequestManagementTable.tsx @@ -68,13 +68,14 @@ const EventRequestManagementTable = ({ // console.log("Fetching event requests..."); - // Use DataSyncService to get data from IndexedDB with forced sync + // Use DataSyncService to get data from IndexedDB with forced sync and deletion detection const updatedRequests = await dataSync.getData( Collections.EVENT_REQUESTS, true, // Force sync - '', // No filter + '', // No filter - get all requests '-created', - 'requested_user' // This is correct but we need to ensure it's working in the DataSyncService + 'requested_user', // Expand user data + true // Enable deletion detection for all event requests ); // If we still have "Unknown" users, try to fetch them directly diff --git a/src/components/dashboard/Officer_EventRequestManagement/EventRequestModal.tsx b/src/components/dashboard/Officer_EventRequestManagement/EventRequestModal.tsx index e575243..f9edd6b 100644 --- a/src/components/dashboard/Officer_EventRequestManagement/EventRequestModal.tsx +++ b/src/components/dashboard/Officer_EventRequestManagement/EventRequestModal.tsx @@ -269,9 +269,9 @@ const EventRequestModal: React.FC = ({ eventRequests }) const update = Update.getInstance(); await update.updateField("event_request", id, "status", status); - // Force sync to update IndexedDB + // Force sync to update IndexedDB with deletion detection enabled const dataSync = DataSyncService.getInstance(); - await dataSync.syncCollection(Collections.EVENT_REQUESTS); + await dataSync.syncCollection(Collections.EVENT_REQUESTS, "", "-created", {}, true); // Find the request to get its name and previous status const request = localEventRequests.find((req) => req.id === id); diff --git a/src/scripts/database/DataSyncService.ts b/src/scripts/database/DataSyncService.ts index dd485e5..ea3002f 100644 --- a/src/scripts/database/DataSyncService.ts +++ b/src/scripts/database/DataSyncService.ts @@ -106,6 +106,7 @@ export class DataSyncService { filter: string = "", sort: string = "-created", expand: Record | string[] | string = {}, + detectDeletions: boolean = true, ): Promise { // Skip in non-browser environments if (!isBrowser) { @@ -169,12 +170,15 @@ export class DataSyncService { return []; } - // Get existing items to handle conflicts + // Get existing items to handle conflicts and deletions const existingItems = await table.toArray(); const existingItemsMap = new Map( existingItems.map((item) => [item.id, item]), ); + // Create a set of server item IDs for efficient deletion detection + const serverItemIds = new Set(items.map(item => item.id)); + // Handle conflicts and merge changes const itemsToStore = await Promise.all( items.map(async (item) => { @@ -206,7 +210,43 @@ export class DataSyncService { }), ); - // Store in IndexedDB + // Handle deletions: find items that exist locally but not on server + // Only detect deletions when: + // 1. detectDeletions is true AND + // 2. No filter is applied (full collection sync) OR filter is a user-specific filter + const shouldDetectDeletions = detectDeletions && (!filter || filter.includes('requested_user=') || filter.includes('user=')); + + if (shouldDetectDeletions) { + const itemsToDelete = existingItems.filter(localItem => { + // For user-specific filters, only delete items that match the filter criteria + // but don't exist on the server + if (filter && filter.includes('requested_user=')) { + // Extract user ID from filter + const userMatch = filter.match(/requested_user="([^"]+)"/); + const userId = userMatch ? userMatch[1] : null; + + // Only consider items for deletion if they belong to the same user + if (userId && (localItem as any).requested_user === userId) { + return !serverItemIds.has(localItem.id); + } + return false; // Don't delete items that don't match the user filter + } + + // For full collection syncs, delete any item not on the server + return !serverItemIds.has(localItem.id); + }); + + // Perform deletions + if (itemsToDelete.length > 0) { + // console.log(`Deleting ${itemsToDelete.length} items from ${collection} that no longer exist on server`); + + // Delete items that no longer exist on the server + const idsToDelete = itemsToDelete.map(item => item.id); + await table.bulkDelete(idsToDelete); + } + } + + // Store/update items from the server await table.bulkPut(itemsToStore); // Update last sync timestamp @@ -448,6 +488,7 @@ export class DataSyncService { filter: string = "", sort: string = "-created", expand: Record | string[] | string = {}, + detectDeletions: boolean = true, ): Promise { const db = this.dexieService.getDB(); const table = this.getTableForCollection(collection); @@ -464,7 +505,7 @@ export class DataSyncService { if (!this.offlineMode && (forceSync || now - lastSync > syncThreshold)) { try { - await this.syncCollection(collection, filter, sort, expand); + await this.syncCollection(collection, filter, sort, expand, detectDeletions); } catch (error) { console.error(`Error syncing ${collection}, using cached data:`, error); }