fix(add property): edit ItemsList component to save auto-populated items immediately and invalidate queries after all operations are complete

This commit is contained in:
ryan 2025-07-16 17:15:04 +03:00
parent 90abad12b9
commit f380397f71

View file

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