Compare commits

..

No commits in common. "cef242e173d3ba3c70c0ab18d2fd8874f02c49ac" and "e42eec5dc1f81d166fc50a911b6cbd6d629a45e3" have entirely different histories.

2 changed files with 51 additions and 105 deletions

View file

@ -44,7 +44,17 @@ async function loadItemsFromDb(url: string): Promise<ItemsResponse> {
const selectedProperties = await propertiesResponse.json();
console.log('[DEBUG] Successfully received selected properties');
return { items, selectedProperties };
// 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 };
}
async function saveItemToDb(url: string, item: Item): Promise<void> {
@ -451,10 +461,8 @@ export function ItemsList({ url }: ItemsListProps) {
}
}
// THIRD: Add to all items with auto-population from propertyCache AND save immediately
const itemsToSave: Item[] = [];
const updatedItems = items.map(item => {
// THIRD: Add to all items with auto-population from propertyCache
setItems(prev => prev.map(item => {
const existingValue = item.customProperties[normalizedProperty] || '';
let autoPopulatedValue = existingValue;
@ -464,56 +472,25 @@ export function ItemsList({ url }: ItemsListProps) {
console.log(`Auto-populating ${normalizedProperty} for ${item.wikidataId} with value:`, autoPopulatedValue);
}
const updatedItem = {
return {
...item,
customProperties: {
...item.customProperties,
[normalizedProperty]: autoPopulatedValue
}
};
}));
// If we auto-populated a value, save it immediately
if (autoPopulatedValue && autoPopulatedValue !== existingValue) {
console.log(`Adding item to save queue: ${item.id} with ${normalizedProperty}=${autoPopulatedValue}`);
itemsToSave.push(updatedItem);
}
return updatedItem;
});
// Update state with all items at once
setItems(updatedItems);
// Save all auto-populated items immediately
console.log('Items to save from cache population:', itemsToSave.length);
if (itemsToSave.length > 0) {
console.log('Saving auto-populated items:', itemsToSave.map(item => ({ id: item.id, name: item.name })));
}
const cachePopulationPromises = itemsToSave.map(item => {
console.log(`Saving item ${item.id} with auto-populated value`);
return saveItemToDb(url, item);
});
console.log('Items to save from cache population:', itemsToSave.length);
if (itemsToSave.length > 0) {
console.log('Items being saved:', itemsToSave.map(item => ({ id: item.id, name: item.name, properties: item.customProperties })));
}
// FOURTH: Save property to database
// FOURTH: Save to database and refresh data
try {
if (cachePopulationPromises.length > 0) {
console.log(`Waiting for ${cachePopulationPromises.length} auto-populated items to save...`);
await Promise.all(cachePopulationPromises);
console.log('All cache-populated items saved successfully');
} else {
console.log('No auto-populated items to save');
}
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 in property addition process:', error);
console.error('Error saving property to database:', error);
// Revert optimistic updates on error
setSelectedProperties(prev => {
const newSelected = { ...prev };
@ -533,9 +510,6 @@ export function ItemsList({ url }: ItemsListProps) {
// 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<void>[] = [];
for (const item of itemsWithWikidataId) {
if (item.wikidataId) {
try {
@ -560,22 +534,20 @@ export function ItemsList({ url }: ItemsListProps) {
: prevItem
));
// 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
});
// 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);
});
}
}
} catch (error) {
console.error(`Error fetching Wikidata properties for ${item.wikidataId}:`, error);
@ -583,21 +555,8 @@ export function ItemsList({ url }: ItemsListProps) {
}
}
// 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]);
console.log('Property addition completed:', normalizedProperty);
}, [customProperties, propertyLabels, items, propertyCache, url]);
const handleWikidataSelect = useCallback(async (suggestion: WikidataSuggestion, itemId: string) => {
console.log('Wikidata selection for item:', itemId, suggestion);

View file

@ -165,31 +165,18 @@ export function PropertyInput({
// Handle blur
const handleBlur = useCallback((e: React.FocusEvent<HTMLInputElement>) => {
// Delay and more robust checking
// Delay hiding suggestions to allow for clicks on suggestions
setTimeout(() => {
// Check if the active element is within our suggestions dropdown
const activeElement = document.activeElement;
const isClickingOnSuggestion = suggestionsRef.current?.contains(activeElement) ||
suggestionsRef.current?.contains(e.relatedTarget as HTMLElement);
if (!isClickingOnSuggestion) {
const relatedTarget = e.relatedTarget as HTMLElement;
if (!suggestionsRef.current?.contains(relatedTarget)) {
setShowSuggestions(false);
setSelectedIndex(-1);
}
}, 200);
}, 150);
}, []);
// Handle suggestion click
const handleSuggestionClick = useCallback((property: PropertySuggestion, event: React.MouseEvent) => {
// Prevent the blur event from interfering
event.preventDefault();
event.stopPropagation();
// Immediately hide suggestions and process selection
setShowSuggestions(false);
setSelectedIndex(-1);
// Process the selection
const handleSuggestionClick = useCallback((property: PropertySuggestion) => {
handlePropertySelect(property);
}, [handlePropertySelect]);
@ -259,7 +246,7 @@ export function PropertyInput({
: 'hover:bg-gray-50 border-l-4 border-transparent'
}
`}
onClick={(event) => handleSuggestionClick(suggestion, event)}
onClick={() => handleSuggestionClick(suggestion)}
onMouseEnter={() => setSelectedIndex(index)}
>
<div className="flex items-center justify-between">