improve data sync

This commit is contained in:
chark1es 2025-05-30 23:06:44 -07:00
parent 4349b4d034
commit 014d9492ac
5 changed files with 79 additions and 18 deletions

View file

@ -136,7 +136,8 @@ const EventRequestForm: React.FC = () => {
other_logos: [], other_logos: [],
room_booking_files: [], room_booking_files: [],
invoice: null, invoice: null,
invoice_files: [] invoice_files: [],
savedAt: Date.now() // Add timestamp for stale data detection
}; };
localStorage.setItem('eventRequestFormData', JSON.stringify(dataToStore)); localStorage.setItem('eventRequestFormData', JSON.stringify(dataToStore));
@ -153,12 +154,27 @@ const EventRequestForm: React.FC = () => {
if (savedData) { if (savedData) {
try { try {
const parsedData = JSON.parse(savedData); const parsedData = JSON.parse(savedData);
setFormData(prevData => ({
...prevData, // Check if the saved data is stale (older than 24 hours)
...parsedData 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) { } catch (e) {
console.error('Error parsing saved form data:', 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: [], other_logos: [],
room_booking_files: [], room_booking_files: [],
invoice: null, invoice: null,
invoice_files: [] invoice_files: [],
savedAt: Date.now() // Add timestamp for stale data detection
}; };
localStorage.setItem('eventRequestFormData', JSON.stringify(dataToStore)); localStorage.setItem('eventRequestFormData', JSON.stringify(dataToStore));
@ -355,8 +372,8 @@ const EventRequestForm: React.FC = () => {
// This will send the data to the server // This will send the data to the server
const record = await update.create('event_request', submissionData); const record = await update.create('event_request', submissionData);
// Force sync the event requests collection to update IndexedDB // Force sync the event requests collection to update IndexedDB with deletion detection
await dataSync.syncCollection(Collections.EVENT_REQUESTS); await dataSync.syncCollection(Collections.EVENT_REQUESTS, "", "-created", {}, true);
console.log('Event request record created:', record.id); console.log('Event request record created:', record.id);

View file

@ -258,12 +258,14 @@ const UserEventRequests: React.FC<UserEventRequestsProps> = ({ eventRequests: in
return; 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<EventRequest>( const updatedRequests = await dataSync.getData<EventRequest>(
Collections.EVENT_REQUESTS, Collections.EVENT_REQUESTS,
true, // Force sync true, // Force sync
`requested_user="${userId}"`, `requested_user="${userId}"`,
'-created' '-created',
{}, // expand
true // Enable deletion detection for user-specific requests
); );
setEventRequests(updatedRequests); setEventRequests(updatedRequests);

View file

@ -68,13 +68,14 @@ const EventRequestManagementTable = ({
// console.log("Fetching event requests..."); // 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<ExtendedEventRequest>( const updatedRequests = await dataSync.getData<ExtendedEventRequest>(
Collections.EVENT_REQUESTS, Collections.EVENT_REQUESTS,
true, // Force sync true, // Force sync
'', // No filter '', // No filter - get all requests
'-created', '-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 // If we still have "Unknown" users, try to fetch them directly

View file

@ -269,9 +269,9 @@ const EventRequestModal: React.FC<EventRequestModalProps> = ({ eventRequests })
const update = Update.getInstance(); const update = Update.getInstance();
await update.updateField("event_request", id, "status", status); 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(); 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 // Find the request to get its name and previous status
const request = localEventRequests.find((req) => req.id === id); const request = localEventRequests.find((req) => req.id === id);

View file

@ -106,6 +106,7 @@ export class DataSyncService {
filter: string = "", filter: string = "",
sort: string = "-created", sort: string = "-created",
expand: Record<string, any> | string[] | string = {}, expand: Record<string, any> | string[] | string = {},
detectDeletions: boolean = true,
): Promise<T[]> { ): Promise<T[]> {
// Skip in non-browser environments // Skip in non-browser environments
if (!isBrowser) { if (!isBrowser) {
@ -169,12 +170,15 @@ export class DataSyncService {
return []; return [];
} }
// Get existing items to handle conflicts // Get existing items to handle conflicts and deletions
const existingItems = await table.toArray(); const existingItems = await table.toArray();
const existingItemsMap = new Map( const existingItemsMap = new Map(
existingItems.map((item) => [item.id, item]), 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 // Handle conflicts and merge changes
const itemsToStore = await Promise.all( const itemsToStore = await Promise.all(
items.map(async (item) => { 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); await table.bulkPut(itemsToStore);
// Update last sync timestamp // Update last sync timestamp
@ -448,6 +488,7 @@ export class DataSyncService {
filter: string = "", filter: string = "",
sort: string = "-created", sort: string = "-created",
expand: Record<string, any> | string[] | string = {}, expand: Record<string, any> | string[] | string = {},
detectDeletions: boolean = true,
): Promise<T[]> { ): Promise<T[]> {
const db = this.dexieService.getDB(); const db = this.dexieService.getDB();
const table = this.getTableForCollection(collection); const table = this.getTableForCollection(collection);
@ -464,7 +505,7 @@ export class DataSyncService {
if (!this.offlineMode && (forceSync || now - lastSync > syncThreshold)) { if (!this.offlineMode && (forceSync || now - lastSync > syncThreshold)) {
try { try {
await this.syncCollection<T>(collection, filter, sort, expand); await this.syncCollection<T>(collection, filter, sort, expand, detectDeletions);
} catch (error) { } catch (error) {
console.error(`Error syncing ${collection}, using cached data:`, error); console.error(`Error syncing ${collection}, using cached data:`, error);
} }