diff --git a/PROGRESS.md b/PROGRESS.md index 2f1cc8d..5e3bedb 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -191,6 +191,13 @@ - Updated test widgets: matrix_widget, answer_options, question_display to use presigned URLs - Updated tests_state_manager: converts TestButtonDto and MatrixCardDto to use imageUrl when available - All widgets maintain backward compatibility with fallback to ApiConfigV2 URL building + - **Test Images Fix**: Fixed missing images in game tests + - Fixed matrix question generation: now uses card.image instead of card.id for images + - Unified MinIO bucket structure: all images (cards and tests) now use cardImagesBucket + - Fixed image conversion logic: keeps image as objectId and adds imageUrl as presigned URL + - Removed testImagesBucket - simplified to single bucket for all images + - Updated all API endpoints and tests to use unified bucket structure + - Added comprehensive unit tests for image conversion in test_manager_image_conversion_test.dart ### Common Libraries - **mnemo_cards_common**: Shared models and utilities diff --git a/TODO.md b/TODO.md index 73fb5e3..6456be8 100644 --- a/TODO.md +++ b/TODO.md @@ -105,6 +105,14 @@ - Added sun image (el_sol.png) for light theme and moon image (la_luna.png) for dark theme - Created theme-aware loading screen components using StateBuilder - Added assets/images/ to pubspec.yaml and updated app.dart with new loading UI + - ✅ **Test Images Fix**: Fixed missing images in game tests (matrix questions) + - Fixed matrix question generation: now uses card.image instead of card.id for button images + - Unified MinIO bucket structure: all images (cards and tests) now use single cardImagesBucket + - Removed testImagesBucket bucket - simplified architecture to single bucket for all images + - Fixed image conversion to keep image as objectId and add imageUrl as presigned URL (no overwrite) + - Updated all API endpoints (MediaApiV2, AdminTestsApiV2, TestManager) to use unified bucket + - Updated all tests (media_api_v2_test, minio_service_test) to reflect single bucket structure + - Added comprehensive unit tests in test_manager_image_conversion_test.dart with 3 test cases - [ ] **Integration Tests**: Implement comprehensive integration testing - API endpoint testing diff --git a/mnemo_cards_admin/src/components/CardVoicesManager.tsx b/mnemo_cards_admin/src/components/CardVoicesManager.tsx index 4de18a5..f36a644 100644 --- a/mnemo_cards_admin/src/components/CardVoicesManager.tsx +++ b/mnemo_cards_admin/src/components/CardVoicesManager.tsx @@ -21,9 +21,14 @@ export function CardVoicesManager({ cardId, disabled = false }: CardVoicesManage const [newLanguage, setNewLanguage] = useState('en') // Load voices for the card - const { data: voicesData, isLoading } = useQuery({ + const { data: voicesData, isLoading, refetch } = useQuery({ queryKey: ['cardVoices', cardId], - queryFn: () => voicesApi.getCardVoices(cardId), + queryFn: async () => { + console.log('🔍 CardVoicesManager: Fetching voices for card', cardId) + const result = await voicesApi.getCardVoices(cardId) + console.log('✅ CardVoicesManager: Fetched voices', result) + return result + }, // Voices should still load even if parent form is disabled (e.g. during save) enabled: !!cardId, }) @@ -38,10 +43,19 @@ export function CardVoicesManager({ cardId, disabled = false }: CardVoicesManage }) return voicesApi.addCardVoice(cardId, voiceUrl, language) }, - onSuccess: () => { + onSuccess: async () => { console.log('✅ CardVoicesManager: addVoiceMutation.onSuccess') - queryClient.invalidateQueries({ queryKey: ['cardVoices', cardId] }) + + // Invalidate and refetch to ensure we get the latest data + await queryClient.invalidateQueries({ queryKey: ['cardVoices', cardId] }) + + // Force refetch to ensure the list is updated + const refetchResult = await refetch() + console.log('✅ CardVoicesManager: Refetched voices after add', refetchResult.data) + toast.success('Voice added successfully') + + // Auto-close form and reset after successful addition setShowAddForm(false) setNewAudio(undefined) setNewLanguage('en') @@ -66,30 +80,7 @@ export function CardVoicesManager({ cardId, disabled = false }: CardVoicesManage }, }) - const handleAddVoice = () => { - console.log('🔍 CardVoicesManager: handleAddVoice called', { - newAudio, - newLanguage, - cardId, - }) - - if (!newAudio) { - console.warn('⚠️ CardVoicesManager: newAudio is empty') - toast.error('Please upload an audio file') - return - } - - console.log('✅ CardVoicesManager: Calling addVoiceMutation.mutate', { - cardId, - voiceUrl: newAudio, - language: newLanguage, - }) - - addVoiceMutation.mutate({ - voiceUrl: newAudio, - language: newLanguage, - }) - } + // handleAddVoice is no longer needed - voice is added automatically after upload const handleRemoveVoice = (voiceId: string) => { if (confirm('Are you sure you want to remove this voice?')) { @@ -124,23 +115,34 @@ export function CardVoicesManager({ cardId, disabled = false }: CardVoicesManage { + onChange={async (value) => { console.log('🔍 CardVoicesManager: AudioUpload onChange', { value, cardId }) setNewAudio(value) + + // Automatically add voice after upload + if (value && cardId) { + console.log('✅ CardVoicesManager: Auto-adding voice after upload', { + cardId, + voiceUrl: value, + language: newLanguage, + }) + + addVoiceMutation.mutate({ + voiceUrl: value, + language: newLanguage, + }) + } }} language={newLanguage} onLanguageChange={setNewLanguage} disabled={disabled || addVoiceMutation.isPending} /> + {addVoiceMutation.isPending && ( +
+ Adding voice... +
+ )}
-