diff --git a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.spec.ts index a818c752b..b57a83355 100644 --- a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.spec.ts @@ -4,7 +4,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap' import { NgSelectModule } from '@ng-select/ng-select' -import { of } from 'rxjs' +import { of, throwError } from 'rxjs' import { MailAction, MailMetadataCorrespondentOption, @@ -15,6 +15,7 @@ import { CorrespondentService } from 'src/app/services/rest/correspondent.servic import { DocumentTypeService } from 'src/app/services/rest/document-type.service' import { MailAccountService } from 'src/app/services/rest/mail-account.service' import { SettingsService } from 'src/app/services/settings.service' +import { ToastService } from 'src/app/services/toast.service' import { CheckComponent } from '../../input/check/check.component' import { NumberComponent } from '../../input/number/number.component' import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component' @@ -81,6 +82,41 @@ describe('MailRuleEditDialogComponent', () => { fixture.detectChanges() }) + it('should use empty related object lists when retrieval fails', () => { + const failed = () => throwError(() => new Error('Forbidden')) + const toastSpy = jest.spyOn(TestBed.inject(ToastService), 'showError') + jest + .spyOn(TestBed.inject(MailAccountService), 'listAll') + .mockReturnValue(failed()) + jest + .spyOn(TestBed.inject(CorrespondentService), 'listAll') + .mockReturnValue(failed()) + jest + .spyOn(TestBed.inject(DocumentTypeService), 'listAll') + .mockReturnValue(failed()) + + const failedFixture = TestBed.createComponent(MailRuleEditDialogComponent) + const failedComponent = failedFixture.componentInstance + + expect(failedComponent.accounts()).toEqual([]) + expect(failedComponent.correspondents()).toEqual([]) + expect(failedComponent.documentTypes()).toEqual([]) + expect(() => failedFixture.detectChanges()).not.toThrow() + expect(toastSpy).toHaveBeenCalledTimes(3) + expect(toastSpy).toHaveBeenCalledWith( + 'Error retrieving mail accounts', + expect.any(Error) + ) + expect(toastSpy).toHaveBeenCalledWith( + 'Error retrieving correspondents', + expect.any(Error) + ) + expect(toastSpy).toHaveBeenCalledWith( + 'Error retrieving document types', + expect.any(Error) + ) + }) + it('should support create and edit modes', () => { component.dialogMode.set(EditDialogMode.CREATE) const createTitleSpy = jest.spyOn(component, 'getCreateTitle') diff --git a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts index a123c097c..7731324c4 100644 --- a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts @@ -6,7 +6,7 @@ import { FormsModule, ReactiveFormsModule, } from '@angular/forms' -import { map } from 'rxjs' +import { catchError, map, of } from 'rxjs' import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component' import { Correspondent } from 'src/app/data/correspondent' import { DocumentType } from 'src/app/data/document-type' @@ -26,6 +26,7 @@ import { MailAccountService } from 'src/app/services/rest/mail-account.service' import { MailRuleService } from 'src/app/services/rest/mail-rule.service' import { UserService } from 'src/app/services/rest/user.service' import { SettingsService } from 'src/app/services/settings.service' +import { ToastService } from 'src/app/services/toast.service' import { CheckComponent } from '../../input/check/check.component' import { NumberComponent } from '../../input/number/number.component' import { SelectComponent } from '../../input/select/select.component' @@ -158,17 +159,45 @@ export class MailRuleEditDialogComponent extends EditDialogComponent { private readonly accountService = inject(MailAccountService) private readonly correspondentService = inject(CorrespondentService) private readonly documentTypeService = inject(DocumentTypeService) + private readonly toastService = inject(ToastService) readonly accounts = toSignal( - this.accountService.listAll().pipe(map((result) => result.results)), + this.accountService.listAll().pipe( + map((result) => result.results), + catchError((error) => { + this.toastService.showError( + $localize`Error retrieving mail accounts`, + error + ) + return of([]) + }) + ), { initialValue: undefined as MailAccount[] } ) readonly correspondents = toSignal( - this.correspondentService.listAll().pipe(map((result) => result.results)), + this.correspondentService.listAll().pipe( + map((result) => result.results), + catchError((error) => { + this.toastService.showError( + $localize`Error retrieving correspondents`, + error + ) + return of([]) + }) + ), { initialValue: undefined as Correspondent[] } ) readonly documentTypes = toSignal( - this.documentTypeService.listAll().pipe(map((result) => result.results)), + this.documentTypeService.listAll().pipe( + map((result) => result.results), + catchError((error) => { + this.toastService.showError( + $localize`Error retrieving document types`, + error + ) + return of([]) + }) + ), { initialValue: undefined as DocumentType[] } ) diff --git a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.spec.ts index f50bf830e..f63a30657 100644 --- a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.spec.ts @@ -81,6 +81,23 @@ describe('UserEditDialogComponent', () => { fixture.detectChanges() }) + it('should use an empty group list when retrieval fails', () => { + const toastSpy = jest.spyOn(toastService, 'showError') + jest + .spyOn(TestBed.inject(GroupService), 'listAll') + .mockReturnValue(throwError(() => new Error('Forbidden'))) + + const failedFixture = TestBed.createComponent(UserEditDialogComponent) + const failedComponent = failedFixture.componentInstance + + expect(failedComponent.groups()).toEqual([]) + expect(() => failedFixture.detectChanges()).not.toThrow() + expect(toastSpy).toHaveBeenCalledWith( + 'Error retrieving groups', + expect.any(Error) + ) + }) + it('should support create and edit modes', () => { component.dialogMode.set(EditDialogMode.CREATE) const createTitleSpy = jest.spyOn(component, 'getCreateTitle') diff --git a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts index 426fc67e2..cd9577545 100644 --- a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts @@ -6,7 +6,7 @@ import { FormsModule, ReactiveFormsModule, } from '@angular/forms' -import { first, map } from 'rxjs' +import { catchError, first, map, of } from 'rxjs' import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component' import { Group } from 'src/app/data/group' import { User } from 'src/app/data/user' @@ -42,7 +42,13 @@ export class UserEditDialogComponent private readonly groupsService = inject(GroupService) readonly groups = toSignal( - this.groupsService.listAll().pipe(map((result) => result.results)), + this.groupsService.listAll().pipe( + map((result) => result.results), + catchError((error) => { + this.toastService.showError($localize`Error retrieving groups`, error) + return of([]) + }) + ), { initialValue: undefined as Group[] } ) readonly passwordIsSet = signal(false) diff --git a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts index 3c4d8f790..a3c4bd64c 100644 --- a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts @@ -11,7 +11,7 @@ import { } from '@angular/forms' import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap' import { NgSelectModule } from '@ng-select/ng-select' -import { of } from 'rxjs' +import { of, throwError } from 'rxjs' import { CustomFieldQueriesModel } from 'src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component' import { CustomFieldDataType } from 'src/app/data/custom-field' import { CustomFieldQueryLogicalOperator } from 'src/app/data/custom-field-query' @@ -39,6 +39,7 @@ import { DocumentTypeService } from 'src/app/services/rest/document-type.service import { MailRuleService } from 'src/app/services/rest/mail-rule.service' import { StoragePathService } from 'src/app/services/rest/storage-path.service' import { SettingsService } from 'src/app/services/settings.service' +import { ToastService } from 'src/app/services/toast.service' import { CustomFieldQueryExpression } from 'src/app/utils/custom-field-query-element' import { ConfirmButtonComponent } from '../../confirm-button/confirm-button.component' import { NumberComponent } from '../../input/number/number.component' @@ -206,6 +207,44 @@ describe('WorkflowEditDialogComponent', () => { settingsService.set(SETTINGS_KEYS.AI_ENABLED, ai) } + it('should use empty related object lists when access is forbidden', () => { + const forbidden = () => throwError(() => new Error('Forbidden')) + const toastSpy = jest.spyOn(TestBed.inject(ToastService), 'showError') + jest + .spyOn(TestBed.inject(CorrespondentService), 'listAll') + .mockReturnValue(forbidden()) + jest + .spyOn(TestBed.inject(DocumentTypeService), 'listAll') + .mockReturnValue(forbidden()) + jest + .spyOn(TestBed.inject(StoragePathService), 'listAll') + .mockReturnValue(forbidden()) + jest + .spyOn(TestBed.inject(MailRuleService), 'listAll') + .mockReturnValue(forbidden()) + jest + .spyOn(TestBed.inject(CustomFieldsService), 'listAll') + .mockReturnValue(forbidden()) + + const forbiddenFixture = TestBed.createComponent( + WorkflowEditDialogComponent + ) + const forbiddenComponent = forbiddenFixture.componentInstance + + expect(forbiddenComponent.correspondents()).toEqual([]) + expect(forbiddenComponent.documentTypes()).toEqual([]) + expect(forbiddenComponent.storagePaths()).toEqual([]) + expect(forbiddenComponent.mailRules()).toEqual([]) + expect(forbiddenComponent.customFields()).toEqual([]) + expect(forbiddenComponent.dateCustomFields()).toEqual([]) + expect(() => forbiddenFixture.detectChanges()).not.toThrow() + expect(toastSpy).toHaveBeenCalledTimes(1) + expect(toastSpy).toHaveBeenCalledWith( + 'Some workflow options could not be loaded.', + expect.any(Error) + ) + }) + it('should support create and edit modes, support adding triggers and actions on new workflow', () => { component.dialogMode.set(EditDialogMode.CREATE) const createTitleSpy = jest.spyOn(component, 'getCreateTitle') diff --git a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts index 76f9ac2af..a89c87cf9 100644 --- a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts @@ -16,7 +16,7 @@ import { } from '@angular/forms' import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' -import { Subscription, map, takeUntil } from 'rxjs' +import { Subscription, catchError, map, of, takeUntil } from 'rxjs' import { Correspondent } from 'src/app/data/correspondent' import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field' import { DocumentType } from 'src/app/data/document-type' @@ -48,6 +48,7 @@ import { StoragePathService } from 'src/app/services/rest/storage-path.service' import { UserService } from 'src/app/services/rest/user.service' import { WorkflowService } from 'src/app/services/rest/workflow.service' import { SettingsService } from 'src/app/services/settings.service' +import { ToastService } from 'src/app/services/toast.service' import { CustomFieldQueryExpression } from 'src/app/utils/custom-field-query-element' import { ConfirmButtonComponent } from '../../confirm-button/confirm-button.component' import { @@ -512,26 +513,43 @@ export class WorkflowEditDialogComponent private readonly storagePathService = inject(StoragePathService) private readonly mailRuleService = inject(MailRuleService) private readonly customFieldsService = inject(CustomFieldsService) + private readonly toastService = inject(ToastService) + private relatedObjectLoadErrorShown = false readonly templates = signal(undefined) readonly correspondents = toSignal( - this.correspondentService.listAll().pipe(map((result) => result.results)), + this.correspondentService.listAll().pipe( + map((result) => result.results), + catchError((error) => this.handleRelatedObjectLoadError(error)) + ), { initialValue: undefined as Correspondent[] } ) readonly documentTypes = toSignal( - this.documentTypeService.listAll().pipe(map((result) => result.results)), + this.documentTypeService.listAll().pipe( + map((result) => result.results), + catchError((error) => this.handleRelatedObjectLoadError(error)) + ), { initialValue: undefined as DocumentType[] } ) readonly storagePaths = toSignal( - this.storagePathService.listAll().pipe(map((result) => result.results)), + this.storagePathService.listAll().pipe( + map((result) => result.results), + catchError((error) => this.handleRelatedObjectLoadError(error)) + ), { initialValue: undefined as StoragePath[] } ) readonly mailRules = toSignal( - this.mailRuleService.listAll().pipe(map((result) => result.results)), + this.mailRuleService.listAll().pipe( + map((result) => result.results), + catchError((error) => this.handleRelatedObjectLoadError(error)) + ), { initialValue: undefined as MailRule[] } ) readonly customFields = toSignal( - this.customFieldsService.listAll().pipe(map((result) => result.results)), + this.customFieldsService.listAll().pipe( + map((result) => result.results), + catchError((error) => this.handleRelatedObjectLoadError(error)) + ), { initialValue: undefined as CustomField[] } ) readonly dateCustomFields = computed(() => @@ -545,6 +563,17 @@ export class WorkflowEditDialogComponent SETTINGS_KEYS.AI_ENABLED ) + private handleRelatedObjectLoadError(error) { + if (!this.relatedObjectLoadErrorShown) { + this.relatedObjectLoadErrorShown = true + this.toastService.showError( + $localize`Some workflow options could not be loaded.`, + error + ) + } + return of([]) + } + expandedItem: number = null private readonly triggerFilterOptionsMap = new WeakMap< diff --git a/src-ui/src/app/components/common/input/permissions/permissions-group/permissions-group.component.spec.ts b/src-ui/src/app/components/common/input/permissions/permissions-group/permissions-group.component.spec.ts index 0bfed7b37..6a43d5cd3 100644 --- a/src-ui/src/app/components/common/input/permissions/permissions-group/permissions-group.component.spec.ts +++ b/src-ui/src/app/components/common/input/permissions/permissions-group/permissions-group.component.spec.ts @@ -7,8 +7,9 @@ import { ReactiveFormsModule, } from '@angular/forms' import { NgSelectModule } from '@ng-select/ng-select' -import { of } from 'rxjs' +import { of, throwError } from 'rxjs' import { GroupService } from 'src/app/services/rest/group.service' +import { ToastService } from 'src/app/services/toast.service' import { PermissionsGroupComponent } from './permissions-group.component' describe('PermissionsGroupComponent', () => { @@ -60,4 +61,19 @@ describe('PermissionsGroupComponent', () => { expect(component.value).toEqual({ id: 2, name: 'Group 2' }) expect(groupServiceSpy).toHaveBeenCalled() }) + + it('should use an empty group list when retrieval fails', () => { + const toastSpy = jest.spyOn(TestBed.inject(ToastService), 'showError') + groupServiceSpy.mockReturnValue(throwError(() => new Error('Forbidden'))) + + const failedFixture = TestBed.createComponent(PermissionsGroupComponent) + const failedComponent = failedFixture.componentInstance + + expect(failedComponent.groups()).toEqual([]) + expect(() => failedFixture.detectChanges()).not.toThrow() + expect(toastSpy).toHaveBeenCalledWith( + 'Error retrieving groups', + expect.any(Error) + ) + }) }) diff --git a/src-ui/src/app/components/common/input/permissions/permissions-group/permissions-group.component.ts b/src-ui/src/app/components/common/input/permissions/permissions-group/permissions-group.component.ts index 7367a9ac0..60fd8ace3 100644 --- a/src-ui/src/app/components/common/input/permissions/permissions-group/permissions-group.component.ts +++ b/src-ui/src/app/components/common/input/permissions/permissions-group/permissions-group.component.ts @@ -6,9 +6,10 @@ import { ReactiveFormsModule, } from '@angular/forms' import { NgSelectComponent } from '@ng-select/ng-select' -import { map } from 'rxjs/operators' +import { catchError, map, of } from 'rxjs' import { Group } from 'src/app/data/group' import { GroupService } from 'src/app/services/rest/group.service' +import { ToastService } from 'src/app/services/toast.service' import { AbstractInputComponent } from '../../abstract-input' @Component({ @@ -26,8 +27,15 @@ import { AbstractInputComponent } from '../../abstract-input' }) export class PermissionsGroupComponent extends AbstractInputComponent { private readonly groupService = inject(GroupService) + private readonly toastService = inject(ToastService) readonly groups = toSignal( - this.groupService.listAll().pipe(map((result) => result.results)), + this.groupService.listAll().pipe( + map((result) => result.results), + catchError((error) => { + this.toastService.showError($localize`Error retrieving groups`, error) + return of([]) + }) + ), { initialValue: undefined as Group[] } ) } diff --git a/src-ui/src/app/components/common/input/permissions/permissions-user/permissions-user.component.spec.ts b/src-ui/src/app/components/common/input/permissions/permissions-user/permissions-user.component.spec.ts index 0d7a525ae..b1b4e83d2 100644 --- a/src-ui/src/app/components/common/input/permissions/permissions-user/permissions-user.component.spec.ts +++ b/src-ui/src/app/components/common/input/permissions/permissions-user/permissions-user.component.spec.ts @@ -7,8 +7,9 @@ import { ReactiveFormsModule, } from '@angular/forms' import { NgSelectModule } from '@ng-select/ng-select' -import { of } from 'rxjs' +import { of, throwError } from 'rxjs' import { UserService } from 'src/app/services/rest/user.service' +import { ToastService } from 'src/app/services/toast.service' import { PermissionsUserComponent } from './permissions-user.component' describe('PermissionsUserComponent', () => { @@ -60,4 +61,19 @@ describe('PermissionsUserComponent', () => { expect(component.value).toEqual({ id: 2, name: 'User 2' }) expect(userServiceSpy).toHaveBeenCalled() }) + + it('should use an empty user list when retrieval fails', () => { + const toastSpy = jest.spyOn(TestBed.inject(ToastService), 'showError') + userServiceSpy.mockReturnValue(throwError(() => new Error('Forbidden'))) + + const failedFixture = TestBed.createComponent(PermissionsUserComponent) + const failedComponent = failedFixture.componentInstance + + expect(failedComponent.users()).toEqual([]) + expect(() => failedFixture.detectChanges()).not.toThrow() + expect(toastSpy).toHaveBeenCalledWith( + 'Error retrieving users', + expect.any(Error) + ) + }) }) diff --git a/src-ui/src/app/components/common/input/permissions/permissions-user/permissions-user.component.ts b/src-ui/src/app/components/common/input/permissions/permissions-user/permissions-user.component.ts index f63ebecc4..cf0ee884b 100644 --- a/src-ui/src/app/components/common/input/permissions/permissions-user/permissions-user.component.ts +++ b/src-ui/src/app/components/common/input/permissions/permissions-user/permissions-user.component.ts @@ -6,9 +6,10 @@ import { ReactiveFormsModule, } from '@angular/forms' import { NgSelectComponent } from '@ng-select/ng-select' -import { map } from 'rxjs/operators' +import { catchError, map, of } from 'rxjs' import { User } from 'src/app/data/user' import { UserService } from 'src/app/services/rest/user.service' +import { ToastService } from 'src/app/services/toast.service' import { AbstractInputComponent } from '../../abstract-input' @Component({ @@ -26,8 +27,15 @@ import { AbstractInputComponent } from '../../abstract-input' }) export class PermissionsUserComponent extends AbstractInputComponent { private readonly userService = inject(UserService) + private readonly toastService = inject(ToastService) readonly users = toSignal( - this.userService.listAll().pipe(map((result) => result.results)), + this.userService.listAll().pipe( + map((result) => result.results), + catchError((error) => { + this.toastService.showError($localize`Error retrieving users`, error) + return of([]) + }) + ), { initialValue: undefined as User[] } ) } diff --git a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts index 004b2df0b..44da05b88 100644 --- a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts @@ -4,8 +4,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap' import { NgSelectModule } from '@ng-select/ng-select' -import { of } from 'rxjs' +import { of, throwError } from 'rxjs' import { UserService } from 'src/app/services/rest/user.service' +import { ToastService } from 'src/app/services/toast.service' import { PermissionsFormComponent } from '../input/permissions/permissions-form/permissions-form.component' import { PermissionsGroupComponent } from '../input/permissions/permissions-group/permissions-group.component' import { PermissionsUserComponent } from '../input/permissions/permissions-user/permissions-user.component' @@ -77,6 +78,23 @@ describe('PermissionsDialogComponent', () => { fixture.detectChanges() }) + it('should use an empty user list when retrieval fails', () => { + const toastSpy = jest.spyOn(TestBed.inject(ToastService), 'showError') + jest + .spyOn(TestBed.inject(UserService), 'listAll') + .mockReturnValue(throwError(() => new Error('Forbidden'))) + + const failedFixture = TestBed.createComponent(PermissionsDialogComponent) + const failedComponent = failedFixture.componentInstance + + expect(failedComponent.users()).toEqual([]) + expect(() => failedFixture.detectChanges()).not.toThrow() + expect(toastSpy).toHaveBeenCalledWith( + 'Error retrieving users', + expect.any(Error) + ) + }) + it('should return permissions', () => { expect(component.permissions).toEqual({ owner: null, diff --git a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.ts b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.ts index 1194886fd..89138bb92 100644 --- a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.ts +++ b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.ts @@ -14,10 +14,11 @@ import { ReactiveFormsModule, } from '@angular/forms' import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' -import { map } from 'rxjs' +import { catchError, map, of } from 'rxjs' import { ObjectWithPermissions } from 'src/app/data/object-with-permissions' import { User } from 'src/app/data/user' import { UserService } from 'src/app/services/rest/user.service' +import { ToastService } from 'src/app/services/toast.service' import { PermissionsFormComponent } from '../input/permissions/permissions-form/permissions-form.component' import { SwitchComponent } from '../input/switch/switch.component' @@ -35,9 +36,16 @@ import { SwitchComponent } from '../input/switch/switch.component' export class PermissionsDialogComponent { activeModal = inject(NgbActiveModal) private userService = inject(UserService) + private toastService = inject(ToastService) readonly users = toSignal( - this.userService.listAll().pipe(map((r) => r.results)), + this.userService.listAll().pipe( + map((r) => r.results), + catchError((error) => { + this.toastService.showError($localize`Error retrieving users`, error) + return of([]) + }) + ), { initialValue: undefined as User[] } ) readonly title = signal($localize`Set permissions`)