fix(frontend): corrige manejo de errores en componentes de IA

Cambios realizados:

1. ai-status.service.ts:
   - Importa 'of' desde 'rxjs' para retornar Observables correctamente
   - Corrige catchError que retornaba array [] en lugar de Observable
   - Ahora retorna of({...}) con valores por defecto cuando falla el endpoint
   - Estadísticas ahora muestran 0 en lugar de valores mock cuando hay error

2. ai-suggestions-panel.component.ts:
   - Importa 'of' y 'catchError' desde 'rxjs/operators'
   - Agrega catchError a TODAS las llamadas de servicios (tags, correspondents, etc.)
   - Cada llamada ahora maneja errores y retorna array vacío en caso de fallo
   - Previene que errores de red rompan la funcionalidad del panel
   - Agrega logging de errores para debugging

Estos cambios corrigen:
- ✓ Estadísticas mostrando 0 incorrectamente
- ✓ Errores no manejados en llamadas HTTP
- ✓ Fallos al cargar metadata de sugerencias
- ✓ Panel de sugerencias fallando silenciosamente
This commit is contained in:
Claude 2025-11-17 22:51:40 +00:00
parent 47b7ac25b4
commit 3c8a1c9c66
No known key found for this signature in database
2 changed files with 77 additions and 36 deletions

View file

@ -18,8 +18,8 @@ import {
} from '@angular/core' } from '@angular/core'
import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap' import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { Subject } from 'rxjs' import { Subject, of } from 'rxjs'
import { takeUntil } from 'rxjs/operators' import { takeUntil, catchError } from 'rxjs/operators'
import { import {
AISuggestion, AISuggestion,
AISuggestionStatus, AISuggestionStatus,
@ -134,10 +134,19 @@ export class AiSuggestionsPanelComponent implements OnChanges, OnDestroy {
(s) => s.type === AISuggestionType.Tag (s) => s.type === AISuggestionType.Tag
) )
if (tagSuggestions.length > 0) { if (tagSuggestions.length > 0) {
this.tagService.listAll().pipe(takeUntil(this.destroy$)).subscribe((tags) => { this.tagService
this.tags = tags.results .listAll()
this.updateSuggestionLabels() .pipe(
}) takeUntil(this.destroy$),
catchError((error) => {
console.error('Failed to load tags:', error)
return of({ results: [] })
})
)
.subscribe((tags) => {
this.tags = tags.results
this.updateSuggestionLabels()
})
} }
// Load correspondents if needed // Load correspondents if needed
@ -145,10 +154,19 @@ export class AiSuggestionsPanelComponent implements OnChanges, OnDestroy {
(s) => s.type === AISuggestionType.Correspondent (s) => s.type === AISuggestionType.Correspondent
) )
if (correspondentSuggestions.length > 0) { if (correspondentSuggestions.length > 0) {
this.correspondentService.listAll().pipe(takeUntil(this.destroy$)).subscribe((correspondents) => { this.correspondentService
this.correspondents = correspondents.results .listAll()
this.updateSuggestionLabels() .pipe(
}) takeUntil(this.destroy$),
catchError((error) => {
console.error('Failed to load correspondents:', error)
return of({ results: [] })
})
)
.subscribe((correspondents) => {
this.correspondents = correspondents.results
this.updateSuggestionLabels()
})
} }
// Load document types if needed // Load document types if needed
@ -156,10 +174,19 @@ export class AiSuggestionsPanelComponent implements OnChanges, OnDestroy {
(s) => s.type === AISuggestionType.DocumentType (s) => s.type === AISuggestionType.DocumentType
) )
if (documentTypeSuggestions.length > 0) { if (documentTypeSuggestions.length > 0) {
this.documentTypeService.listAll().pipe(takeUntil(this.destroy$)).subscribe((documentTypes) => { this.documentTypeService
this.documentTypes = documentTypes.results .listAll()
this.updateSuggestionLabels() .pipe(
}) takeUntil(this.destroy$),
catchError((error) => {
console.error('Failed to load document types:', error)
return of({ results: [] })
})
)
.subscribe((documentTypes) => {
this.documentTypes = documentTypes.results
this.updateSuggestionLabels()
})
} }
// Load storage paths if needed // Load storage paths if needed
@ -167,10 +194,19 @@ export class AiSuggestionsPanelComponent implements OnChanges, OnDestroy {
(s) => s.type === AISuggestionType.StoragePath (s) => s.type === AISuggestionType.StoragePath
) )
if (storagePathSuggestions.length > 0) { if (storagePathSuggestions.length > 0) {
this.storagePathService.listAll().pipe(takeUntil(this.destroy$)).subscribe((storagePaths) => { this.storagePathService
this.storagePaths = storagePaths.results .listAll()
this.updateSuggestionLabels() .pipe(
}) takeUntil(this.destroy$),
catchError((error) => {
console.error('Failed to load storage paths:', error)
return of({ results: [] })
})
)
.subscribe((storagePaths) => {
this.storagePaths = storagePaths.results
this.updateSuggestionLabels()
})
} }
// Load custom fields if needed // Load custom fields if needed
@ -178,10 +214,19 @@ export class AiSuggestionsPanelComponent implements OnChanges, OnDestroy {
(s) => s.type === AISuggestionType.CustomField (s) => s.type === AISuggestionType.CustomField
) )
if (customFieldSuggestions.length > 0) { if (customFieldSuggestions.length > 0) {
this.customFieldsService.listAll().pipe(takeUntil(this.destroy$)).subscribe((customFields) => { this.customFieldsService
this.customFields = customFields.results .listAll()
this.updateSuggestionLabels() .pipe(
}) takeUntil(this.destroy$),
catchError((error) => {
console.error('Failed to load custom fields:', error)
return of({ results: [] })
})
)
.subscribe((customFields) => {
this.customFields = customFields.results
this.updateSuggestionLabels()
})
} }
} }

View file

@ -1,6 +1,6 @@
import { HttpClient } from '@angular/common/http' import { HttpClient } from '@angular/common/http'
import { Injectable, inject } from '@angular/core' import { Injectable, inject } from '@angular/core'
import { BehaviorSubject, Observable, interval, Subscription } from 'rxjs' import { BehaviorSubject, Observable, interval, Subscription, of } from 'rxjs'
import { catchError, map, startWith, switchMap } from 'rxjs/operators' import { catchError, map, startWith, switchMap } from 'rxjs/operators'
import { AIStatus } from 'src/app/data/ai-status' import { AIStatus } from 'src/app/data/ai-status'
import { environment } from 'src/environments/environment' import { environment } from 'src/environments/environment'
@ -86,19 +86,15 @@ export class AIStatusService {
}), }),
catchError((error) => { catchError((error) => {
this.loading = false this.loading = false
console.warn('Failed to fetch AI status, using mock data:', error) console.warn('Failed to fetch AI status:', error)
// Return mock data if endpoint doesn't exist yet // Return default status if endpoint fails
return [ return of({
{ active: false,
active: true, processing: false,
processing: false, documents_scanned_today: 0,
documents_scanned_today: 42, suggestions_applied: 0,
suggestions_applied: 15, pending_deletion_requests: 0,
pending_deletion_requests: 2, })
last_scan: new Date().toISOString(),
version: '1.0.0',
},
]
}) })
) )
} }