Compare commits
3 commits
e42eec5dc1
...
cef242e173
Author | SHA1 | Date | |
---|---|---|---|
cef242e173 | |||
f380397f71 | |||
90abad12b9 |
2 changed files with 105 additions and 51 deletions
|
@ -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> {
|
||||||
|
@ -461,8 +451,10 @@ export function ItemsList({ url }: ItemsListProps) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// THIRD: Add to all items with auto-population from propertyCache
|
// THIRD: Add to all items with auto-population from propertyCache AND save immediately
|
||||||
setItems(prev => prev.map(item => {
|
const itemsToSave: Item[] = [];
|
||||||
|
|
||||||
|
const updatedItems = items.map(item => {
|
||||||
const existingValue = item.customProperties[normalizedProperty] || '';
|
const existingValue = item.customProperties[normalizedProperty] || '';
|
||||||
let autoPopulatedValue = existingValue;
|
let autoPopulatedValue = existingValue;
|
||||||
|
|
||||||
|
@ -472,25 +464,56 @@ export function ItemsList({ url }: ItemsListProps) {
|
||||||
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) {
|
||||||
|
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
|
||||||
try {
|
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);
|
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 in property addition process:', error);
|
||||||
// Revert optimistic updates on error
|
// Revert optimistic updates on error
|
||||||
setSelectedProperties(prev => {
|
setSelectedProperties(prev => {
|
||||||
const newSelected = { ...prev };
|
const newSelected = { ...prev };
|
||||||
|
@ -510,6 +533,9 @@ export function ItemsList({ url }: ItemsListProps) {
|
||||||
// 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 +560,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 +583,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);
|
||||||
|
|
|
@ -165,18 +165,31 @@ export function PropertyInput({
|
||||||
|
|
||||||
// Handle blur
|
// Handle blur
|
||||||
const handleBlur = useCallback((e: React.FocusEvent<HTMLInputElement>) => {
|
const handleBlur = useCallback((e: React.FocusEvent<HTMLInputElement>) => {
|
||||||
// Delay hiding suggestions to allow for clicks on suggestions
|
// Delay and more robust checking
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const relatedTarget = e.relatedTarget as HTMLElement;
|
// Check if the active element is within our suggestions dropdown
|
||||||
if (!suggestionsRef.current?.contains(relatedTarget)) {
|
const activeElement = document.activeElement;
|
||||||
|
const isClickingOnSuggestion = suggestionsRef.current?.contains(activeElement) ||
|
||||||
|
suggestionsRef.current?.contains(e.relatedTarget as HTMLElement);
|
||||||
|
|
||||||
|
if (!isClickingOnSuggestion) {
|
||||||
setShowSuggestions(false);
|
setShowSuggestions(false);
|
||||||
setSelectedIndex(-1);
|
setSelectedIndex(-1);
|
||||||
}
|
}
|
||||||
}, 150);
|
}, 200);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Handle suggestion click
|
// Handle suggestion click
|
||||||
const handleSuggestionClick = useCallback((property: PropertySuggestion) => {
|
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
|
||||||
handlePropertySelect(property);
|
handlePropertySelect(property);
|
||||||
}, [handlePropertySelect]);
|
}, [handlePropertySelect]);
|
||||||
|
|
||||||
|
@ -246,7 +259,7 @@ export function PropertyInput({
|
||||||
: 'hover:bg-gray-50 border-l-4 border-transparent'
|
: 'hover:bg-gray-50 border-l-4 border-transparent'
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
onClick={() => handleSuggestionClick(suggestion)}
|
onClick={(event) => handleSuggestionClick(suggestion, event)}
|
||||||
onMouseEnter={() => setSelectedIndex(index)}
|
onMouseEnter={() => setSelectedIndex(index)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
|
|
Loading…
Add table
Reference in a new issue