diff --git a/src-ui/src/app/components/ai-suggestions-panel/ai-suggestions-panel.component.ts b/src-ui/src/app/components/ai-suggestions-panel/ai-suggestions-panel.component.ts index a1a91c2ff..82d7ab369 100644 --- a/src-ui/src/app/components/ai-suggestions-panel/ai-suggestions-panel.component.ts +++ b/src-ui/src/app/components/ai-suggestions-panel/ai-suggestions-panel.component.ts @@ -140,6 +140,9 @@ export class AiSuggestionsPanelComponent implements OnChanges, OnDestroy { takeUntil(this.destroy$), catchError((error) => { console.error('Failed to load tags:', error) + this.toastService.showError( + $localize`Failed to load tags for AI suggestions. Please refresh the page.` + ) return of({ results: [] }) }) ) @@ -160,6 +163,9 @@ export class AiSuggestionsPanelComponent implements OnChanges, OnDestroy { takeUntil(this.destroy$), catchError((error) => { console.error('Failed to load correspondents:', error) + this.toastService.showError( + $localize`Failed to load correspondents for AI suggestions. Please refresh the page.` + ) return of({ results: [] }) }) ) @@ -180,6 +186,9 @@ export class AiSuggestionsPanelComponent implements OnChanges, OnDestroy { takeUntil(this.destroy$), catchError((error) => { console.error('Failed to load document types:', error) + this.toastService.showError( + $localize`Failed to load document types for AI suggestions. Please refresh the page.` + ) return of({ results: [] }) }) ) @@ -200,6 +209,9 @@ export class AiSuggestionsPanelComponent implements OnChanges, OnDestroy { takeUntil(this.destroy$), catchError((error) => { console.error('Failed to load storage paths:', error) + this.toastService.showError( + $localize`Failed to load storage paths for AI suggestions. Please refresh the page.` + ) return of({ results: [] }) }) ) @@ -220,6 +232,9 @@ export class AiSuggestionsPanelComponent implements OnChanges, OnDestroy { takeUntil(this.destroy$), catchError((error) => { console.error('Failed to load custom fields:', error) + this.toastService.showError( + $localize`Failed to load custom fields for AI suggestions. Please refresh the page.` + ) return of({ results: [] }) }) ) diff --git a/src/documents/views.py b/src/documents/views.py index 9dcdfe5aa..6913178fc 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -1413,17 +1413,24 @@ class UnifiedSearchViewSet(DocumentViewSet): status=500, ) - @action(detail=True, methods=["POST"], name="Apply AI Suggestion") + @action( + detail=True, + methods=["POST"], + name="Apply AI Suggestion", + permission_classes=[IsAuthenticated, CanApplyAISuggestionsPermission], + ) def apply_suggestion(self, request, pk=None): """ Apply an AI suggestion to a document. Records user feedback and applies the suggested change. + Requires 'can_apply_ai_suggestions' permission. """ from documents.models import AISuggestionFeedback from documents.serializers.ai_suggestions import ApplySuggestionSerializer try: + # Check permissions - get_object() validates object-level permissions document = self.get_object() # Validate input @@ -1496,14 +1503,40 @@ class UnifiedSearchViewSet(DocumentViewSet): status=400, ) - except ( - Tag.DoesNotExist, - Correspondent.DoesNotExist, - DocumentType.DoesNotExist, - StoragePath.DoesNotExist, - ): + except Tag.DoesNotExist: + logger.error( + f"Tag {value_id} not found when applying suggestion to document {pk} " + f"by user {request.user.username}", + ) return Response( - {"detail": "Referenced object not found"}, + {"detail": f"Tag with ID {value_id} not found"}, + status=404, + ) + except Correspondent.DoesNotExist: + logger.error( + f"Correspondent {value_id} not found when applying suggestion to document {pk} " + f"by user {request.user.username}", + ) + return Response( + {"detail": f"Correspondent with ID {value_id} not found"}, + status=404, + ) + except DocumentType.DoesNotExist: + logger.error( + f"DocumentType {value_id} not found when applying suggestion to document {pk} " + f"by user {request.user.username}", + ) + return Response( + {"detail": f"Document type with ID {value_id} not found"}, + status=404, + ) + except StoragePath.DoesNotExist: + logger.error( + f"StoragePath {value_id} not found when applying suggestion to document {pk} " + f"by user {request.user.username}", + ) + return Response( + {"detail": f"Storage path with ID {value_id} not found"}, status=404, ) except Exception as e: