diff --git a/src/components/ItemsList.tsx b/src/components/ItemsList.tsx index e93b636..f87a339 100644 --- a/src/components/ItemsList.tsx +++ b/src/components/ItemsList.tsx @@ -44,17 +44,7 @@ async function loadItemsFromDb(url: string): Promise { const selectedProperties = await propertiesResponse.json(); console.log('[DEBUG] Successfully received selected properties'); - // Filter items to only include selected properties - const filteredItems = items.map((item: Item) => ({ - ...item, - customProperties: Object.fromEntries( - Object.entries(item.customProperties).filter(([key]) => - selectedProperties.includes(key) - ) - ) - })); - - return { items: filteredItems, selectedProperties }; + return { items, selectedProperties }; } async function saveItemToDb(url: string, item: Item): Promise { @@ -444,13 +434,13 @@ export function ItemsList({ url }: ItemsListProps) { console.log('Property already visible in UI:', normalizedProperty); return; } - + console.log('Proceeding to add property:', normalizedProperty); - + // FIRST: Add to selected properties and custom properties (optimistic update) setSelectedProperties(prev => ({ ...prev, [normalizedProperty]: true })); setCustomProperties(prev => [...prev, normalizedProperty]); - + // SECOND: Fetch label if not exists if (!propertyLabels[normalizedProperty]) { try { @@ -460,35 +450,48 @@ export function ItemsList({ url }: ItemsListProps) { console.error('Error fetching property labels:', error); } } - - // THIRD: Add to all items with auto-population from propertyCache + + // THIRD: Add to all items with auto-population from propertyCache AND save immediately + const itemsToSave: Item[] = []; + setItems(prev => prev.map(item => { const existingValue = item.customProperties[normalizedProperty] || ''; let autoPopulatedValue = existingValue; - + // If item has Wikidata ID and we have cached properties, use the cached value if (item.wikidataId && propertyCache[item.wikidataId] && propertyCache[item.wikidataId][normalizedProperty]) { autoPopulatedValue = propertyCache[item.wikidataId][normalizedProperty]; console.log(`Auto-populating ${normalizedProperty} for ${item.wikidataId} with value:`, autoPopulatedValue); } - - return { + + const updatedItem = { ...item, customProperties: { ...item.customProperties, [normalizedProperty]: autoPopulatedValue } }; - })); - // FOURTH: Save to database and refresh data + // If we auto-populated a value, save it immediately + if (autoPopulatedValue && autoPopulatedValue !== existingValue) { + itemsToSave.push(updatedItem); + } + + return updatedItem; + })); + + // Save all auto-populated items immediately + const cachePopulationPromises = itemsToSave.map(item => saveItemToDb(url, item)); + + // FOURTH: Save property to database try { + if (cachePopulationPromises.length > 0) { + await Promise.all(cachePopulationPromises); + console.log('All cache-populated items saved successfully'); + } + await addPropertyToDb(url, normalizedProperty); console.log('Property successfully saved to database:', normalizedProperty); - - // Invalidate queries to refresh data from database - queryClient.invalidateQueries({ queryKey: ['items', url] }); - } catch (error) { console.error('Error saving property to database:', error); // Revert optimistic updates on error @@ -506,10 +509,13 @@ export function ItemsList({ url }: ItemsListProps) { }))); return; } - + // FIFTH: Fetch Wikidata properties for items with wikidata_id that don't have cached data const itemsWithWikidataId = items.filter(item => item.wikidataId && !propertyCache[item.wikidataId]); - + + // Create array to store all save promises + const savePromises: Promise[] = []; + for (const item of itemsWithWikidataId) { if (item.wikidataId) { try { @@ -534,20 +540,22 @@ export function ItemsList({ url }: ItemsListProps) { : prevItem )); - // Save the updated item to database to persist the auto-populated value - const updatedItem = items.find(i => i.wikidataId === item.wikidataId); - if (updatedItem) { - const itemToSave = { - ...updatedItem, - customProperties: { - ...updatedItem.customProperties, - [normalizedProperty]: properties[normalizedProperty] - } - }; - saveItemToDb(url, itemToSave).catch(error => { - console.error('Error saving auto-populated value:', error); - }); - } + // Get current items state and save the updated item to database + setItems(currentItems => { + const updatedItem = currentItems.find(i => i.wikidataId === item.wikidataId); + if (updatedItem) { + const itemToSave = { + ...updatedItem, + customProperties: { + ...updatedItem.customProperties, + [normalizedProperty]: properties[normalizedProperty] + } + }; + // Add save promise to array instead of awaiting immediately + savePromises.push(saveItemToDb(url, itemToSave)); + } + return currentItems; // Return unchanged state since we already updated it above + }); } } catch (error) { console.error(`Error fetching Wikidata properties for ${item.wikidataId}:`, error); @@ -555,8 +563,21 @@ export function ItemsList({ url }: ItemsListProps) { } } - console.log('Property addition completed:', normalizedProperty); - }, [customProperties, propertyLabels, items, propertyCache, url]); + // WAIT for all saves to complete before invalidating queries + if (savePromises.length > 0) { + try { + await Promise.all(savePromises); + console.log('All auto-populated items saved successfully'); + } catch (error) { + console.error('Error saving some auto-populated items:', error); + } + } + + // SIXTH: Only invalidate queries after ALL operations are complete + console.log('Property addition completed, refreshing data:', normalizedProperty); + queryClient.invalidateQueries({ queryKey: ['items', url] }); + + }, [customProperties, propertyLabels, items, propertyCache, url, queryClient, saveItemToDb]); const handleWikidataSelect = useCallback(async (suggestion: WikidataSuggestion, itemId: string) => { console.log('Wikidata selection for item:', itemId, suggestion);