- {
expect(component.name).toBe('test_title')
})
- it('should add a default atom on open and focus the select field', fakeAsync(() => {
+ it('should add a default atom on open', async () => {
expect(component.selectionModel.queries.length).toBe(0)
component.onOpenChange(true)
fixture.detectChanges()
- tick()
+ await fixture.whenStable()
expect(component.selectionModel.queries.length).toBe(1)
- expect(window.document.activeElement.tagName).toBe('INPUT')
- }))
+ })
describe('CustomFieldQueriesModel', () => {
let model: CustomFieldQueriesModel
diff --git a/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.spec.ts b/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.spec.ts
index ee99a726b..91e06c428 100644
--- a/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.spec.ts
+++ b/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.spec.ts
@@ -1,12 +1,7 @@
import { DatePipe } from '@angular/common'
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
import { provideHttpClientTesting } from '@angular/common/http/testing'
-import {
- ComponentFixture,
- TestBed,
- fakeAsync,
- tick,
-} from '@angular/core/testing'
+import { ComponentFixture, TestBed } from '@angular/core/testing'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
@@ -59,27 +54,32 @@ describe('DatesDropdownComponent', () => {
expect(settingsSpy).toHaveBeenCalled()
})
- it('should support date input, emit change', fakeAsync(() => {
+ it('should support date input, emit change', () => {
+ jest.useFakeTimers()
let result: string
component.createdDateFromChange.subscribe((date) => (result = date))
const input: HTMLInputElement = fixture.nativeElement.querySelector('input')
input.value = '5/30/2023'
input.dispatchEvent(new Event('change'))
- tick(500)
+ jest.advanceTimersByTime(500)
expect(result).not.toBeNull()
- }))
+ jest.useRealTimers()
+ })
- it('should support date select, emit datesSet change', fakeAsync(() => {
+ it('should support date select, emit datesSet change', () => {
+ jest.useFakeTimers()
let result: DateSelection
component.datesSet.subscribe((date) => (result = date))
const input: HTMLInputElement = fixture.nativeElement.querySelector('input')
input.value = '5/30/2023'
input.dispatchEvent(new Event('dateSelect'))
- tick(500)
+ jest.advanceTimersByTime(500)
expect(result).not.toBeNull()
- }))
+ jest.useRealTimers()
+ })
- it('should support relative dates', fakeAsync(() => {
+ it('should support relative dates', () => {
+ jest.useFakeTimers()
let result: DateSelection
component.datesSet.subscribe((date) => (result = date))
component.createdRelativeDate = RelativeDate.WITHIN_1_WEEK // normally set by ngModel binding in dropdown
@@ -88,7 +88,7 @@ describe('DatesDropdownComponent', () => {
} as any)
component.addedRelativeDate = RelativeDate.WITHIN_1_WEEK // normally set by ngModel binding in dropdown
component.onSetAddedRelativeDate({ id: RelativeDate.WITHIN_1_WEEK } as any)
- tick(500)
+ jest.advanceTimersByTime(500)
expect(result).toEqual({
createdFrom: null,
createdTo: null,
@@ -97,7 +97,8 @@ describe('DatesDropdownComponent', () => {
addedTo: null,
addedRelativeDateID: RelativeDate.WITHIN_1_WEEK,
})
- }))
+ jest.useRealTimers()
+ })
it('should support report if active', () => {
component.createdRelativeDate = RelativeDate.WITHIN_1_WEEK
@@ -177,11 +178,12 @@ describe('DatesDropdownComponent', () => {
expect(eventSpy).toHaveBeenCalled()
})
- it('should support debounce', fakeAsync(() => {
+ it('should support debounce', () => {
+ jest.useFakeTimers()
let result: DateSelection
component.datesSet.subscribe((date) => (result = date))
component.onChangeDebounce()
- tick(500)
+ jest.advanceTimersByTime(500)
expect(result).toEqual({
createdFrom: null,
createdTo: null,
@@ -190,5 +192,6 @@ describe('DatesDropdownComponent', () => {
addedTo: null,
addedRelativeDateID: null,
})
- }))
+ jest.useRealTimers()
+ })
})
diff --git a/src-ui/src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.spec.ts
index 467aabc4f..9e1661a13 100644
--- a/src-ui/src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.spec.ts
+++ b/src-ui/src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.spec.ts
@@ -41,20 +41,20 @@ describe('CorrespondentEditDialogComponent', () => {
fixture = TestBed.createComponent(CorrespondentEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
- settingsService.currentUser = { id: 99, username: 'user99' }
+ settingsService.currentUser.set({ id: 99, username: 'user99' })
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support create and edit modes', () => {
- component.dialogMode = EditDialogMode.CREATE
+ component.dialogMode.set(EditDialogMode.CREATE)
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
- component.dialogMode = EditDialogMode.EDIT
+ component.dialogMode.set(EditDialogMode.EDIT)
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
})
diff --git a/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.spec.ts
index 419fd89bb..970c7f364 100644
--- a/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.spec.ts
+++ b/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.spec.ts
@@ -44,33 +44,33 @@ describe('CustomFieldEditDialogComponent', () => {
fixture = TestBed.createComponent(CustomFieldEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
- settingsService.currentUser = { id: 99, username: 'user99' }
+ settingsService.currentUser.set({ id: 99, username: 'user99' })
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support create and edit modes', () => {
- component.dialogMode = EditDialogMode.CREATE
+ component.dialogMode.set(EditDialogMode.CREATE)
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
- component.dialogMode = EditDialogMode.EDIT
+ component.dialogMode.set(EditDialogMode.EDIT)
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
})
it('should disable data type select on edit', () => {
- component.dialogMode = EditDialogMode.EDIT
+ component.dialogMode.set(EditDialogMode.EDIT)
fixture.detectChanges()
component.ngOnInit()
expect(component.objectForm.get('data_type').disabled).toBeTruthy()
})
it('should initialize select options on edit', () => {
- component.dialogMode = EditDialogMode.EDIT
+ component.dialogMode.set(EditDialogMode.EDIT)
component.object = {
id: 1,
name: 'Field 1',
@@ -91,7 +91,7 @@ describe('CustomFieldEditDialogComponent', () => {
})
it('should support add / remove select options', () => {
- component.dialogMode = EditDialogMode.CREATE
+ component.dialogMode.set(EditDialogMode.CREATE)
fixture.detectChanges()
component.ngOnInit()
expect(
@@ -115,7 +115,7 @@ describe('CustomFieldEditDialogComponent', () => {
const selectOptionInputs = component[
'selectOptionInputs'
] as QueryList
- component.dialogMode = EditDialogMode.CREATE
+ component.dialogMode.set(EditDialogMode.CREATE)
component.objectForm.get('data_type').setValue(CustomFieldDataType.Select)
component.ngOnInit()
component.ngAfterViewInit()
@@ -125,7 +125,7 @@ describe('CustomFieldEditDialogComponent', () => {
})
it('should send all select options including those changed in form on save', () => {
- component.dialogMode = EditDialogMode.EDIT
+ component.dialogMode.set(EditDialogMode.EDIT)
component.object = {
id: 1,
name: 'Field 1',
diff --git a/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.ts
index 8e8bddfab..78fd7e748 100644
--- a/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.ts
+++ b/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.ts
@@ -149,7 +149,7 @@ export class CustomFieldEditDialogComponent
}
get typeFieldDisabled(): boolean {
- return this.dialogMode === EditDialogMode.EDIT
+ return this.dialogMode() === EditDialogMode.EDIT
}
private updateSelectOptions() {
diff --git a/src-ui/src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.spec.ts
index c3c46c980..c107c39d3 100644
--- a/src-ui/src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.spec.ts
+++ b/src-ui/src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.spec.ts
@@ -41,20 +41,20 @@ describe('DocumentTypeEditDialogComponent', () => {
fixture = TestBed.createComponent(DocumentTypeEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
- settingsService.currentUser = { id: 99, username: 'user99' }
+ settingsService.currentUser.set({ id: 99, username: 'user99' })
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support create and edit modes', () => {
- component.dialogMode = EditDialogMode.CREATE
+ component.dialogMode.set(EditDialogMode.CREATE)
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
- component.dialogMode = EditDialogMode.EDIT
+ component.dialogMode.set(EditDialogMode.EDIT)
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
})
diff --git a/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.spec.ts
index 1dbd54edf..6dd1e11c9 100644
--- a/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.spec.ts
+++ b/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.spec.ts
@@ -4,12 +4,7 @@ import {
provideHttpClientTesting,
} from '@angular/common/http/testing'
import { Component } from '@angular/core'
-import {
- ComponentFixture,
- TestBed,
- fakeAsync,
- tick,
-} from '@angular/core/testing'
+import { ComponentFixture, TestBed } from '@angular/core/testing'
import {
FormControl,
FormGroup,
@@ -122,7 +117,7 @@ describe('EditDialogComponent', () => {
tagService = TestBed.inject(TagService)
permissionsService = TestBed.inject(PermissionsService)
settingsService = TestBed.inject(SettingsService)
- settingsService.currentUser = currentUser
+ settingsService.currentUser.set(currentUser as any)
permissionsService.initialize([], currentUser as any)
activeModal = TestBed.inject(NgbActiveModal)
httpTestingController = TestBed.inject(HttpTestingController)
@@ -136,7 +131,7 @@ describe('EditDialogComponent', () => {
it('should interpolate object permissions', () => {
component.getMatchingAlgorithms() // coverage
component.object = tag
- component.dialogMode = EditDialogMode.EDIT
+ component.dialogMode.set(EditDialogMode.EDIT)
component.ngOnInit()
expect(component.objectForm.get('permissions_form').value).toEqual({
@@ -145,15 +140,17 @@ describe('EditDialogComponent', () => {
})
})
- it('should delay close enabled', fakeAsync(() => {
+ it('should delay close enabled', () => {
+ jest.useFakeTimers()
expect(component.closeEnabled).toBeFalsy()
component.ngOnInit()
- tick(100)
+ jest.advanceTimersByTime(100)
expect(component.closeEnabled).toBeTruthy()
- }))
+ jest.useRealTimers()
+ })
it('should set default owner when in create mode if unset', () => {
- component.dialogMode = EditDialogMode.CREATE
+ component.dialogMode.set(EditDialogMode.CREATE)
component.ngOnInit()
expect(component.objectForm.get('permissions_form').value.owner).toEqual(
currentUser.id
@@ -164,7 +161,7 @@ describe('EditDialogComponent', () => {
})
it('should set default perms when in create mode if set', () => {
- component.dialogMode = EditDialogMode.CREATE
+ component.dialogMode.set(EditDialogMode.CREATE)
settingsService.set(SETTINGS_KEYS.DEFAULT_PERMS_OWNER, 11)
settingsService.set(SETTINGS_KEYS.DEFAULT_PERMS_VIEW_USERS, [1, 2])
settingsService.set(SETTINGS_KEYS.DEFAULT_PERMS_VIEW_GROUPS, [3])
@@ -203,18 +200,18 @@ describe('EditDialogComponent', () => {
})
it('should support create and edit modes', () => {
- component.dialogMode = EditDialogMode.CREATE
+ component.dialogMode.set(EditDialogMode.CREATE)
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
- fixture.detectChanges()
+ component.getTitle()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
- component.dialogMode = EditDialogMode.EDIT
- fixture.detectChanges()
+ component.dialogMode.set(EditDialogMode.EDIT)
+ component.getTitle()
expect(editTitleSpy).toHaveBeenCalled()
// coverage
- component.dialogMode = null
- fixture.detectChanges()
+ component.dialogMode.set(null)
+ component.getTitle()
})
it('should close on cancel', () => {
@@ -225,14 +222,14 @@ describe('EditDialogComponent', () => {
it('should update an object on save in edit mode', () => {
const updateSpy = jest.spyOn(tagService, 'update')
- component.dialogMode = EditDialogMode.EDIT
+ component.dialogMode.set(EditDialogMode.EDIT)
component.save()
expect(updateSpy).toHaveBeenCalled()
})
it('should not submit owner or permissions for non-owner edits', () => {
component.object = tag
- component.dialogMode = EditDialogMode.EDIT
+ component.dialogMode.set(EditDialogMode.EDIT)
component.ngOnInit()
component.objectForm.get('name').setValue('Updated tag')
@@ -251,7 +248,7 @@ describe('EditDialogComponent', () => {
it('should create an object on save in edit mode', () => {
const createSpy = jest.spyOn(tagService, 'create')
- component.dialogMode = EditDialogMode.CREATE
+ component.dialogMode.set(EditDialogMode.CREATE)
component.save()
expect(createSpy).toHaveBeenCalled()
})
diff --git a/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts
index 3d9026833..456ba69f9 100644
--- a/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts
+++ b/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts
@@ -5,6 +5,7 @@ import {
OnInit,
Output,
inject,
+ model,
} from '@angular/core'
import { FormGroup } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
@@ -45,10 +46,7 @@ export abstract class EditDialogComponent<
protected settingsService = inject(SettingsService)
protected permissionsService = inject(PermissionsService)
- users: User[]
-
- @Input()
- dialogMode: EditDialogMode = EditDialogMode.CREATE
+ dialogMode = model(EditDialogMode.CREATE)
@Input()
object: T
@@ -59,18 +57,20 @@ export abstract class EditDialogComponent<
@Output()
failed = new EventEmitter()
+ users: User[]
+
networkActive = false
closeEnabled = false
- error = null
+ error: any = null
abstract getForm(): FormGroup
objectForm: FormGroup = this.getForm()
ngOnInit(): void {
- if (this.object != null && this.dialogMode !== EditDialogMode.CREATE) {
+ if (this.object != null && this.dialogMode() !== EditDialogMode.CREATE) {
this.object['permissions_form'] = {
owner: (this.object as ObjectWithPermissions).owner,
set_permissions: (this.object as ObjectWithPermissions).permissions,
@@ -124,7 +124,7 @@ export abstract class EditDialogComponent<
}
getTitle() {
- switch (this.dialogMode) {
+ switch (this.dialogMode()) {
case EditDialogMode.CREATE:
return this.getCreateTitle()
case EditDialogMode.EDIT:
@@ -151,7 +151,7 @@ export abstract class EditDialogComponent<
protected shouldSubmitPermissions(): boolean {
return (
- this.dialogMode === EditDialogMode.CREATE ||
+ this.dialogMode() === EditDialogMode.CREATE ||
this.permissionsService.currentUserOwnsObject(this.object)
)
}
@@ -172,7 +172,7 @@ export abstract class EditDialogComponent<
delete newObject['set_permissions']
}
var serverResponse: Observable
- switch (this.dialogMode) {
+ switch (this.dialogMode()) {
case EditDialogMode.CREATE:
serverResponse = this.service.create(newObject)
break
diff --git a/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.spec.ts
index 88e989942..b7002853b 100644
--- a/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.spec.ts
+++ b/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.spec.ts
@@ -45,20 +45,20 @@ describe('GroupEditDialogComponent', () => {
fixture = TestBed.createComponent(GroupEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
- settingsService.currentUser = { id: 99, username: 'user99' }
+ settingsService.currentUser.set({ id: 99, username: 'user99' })
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support create and edit modes', () => {
- component.dialogMode = EditDialogMode.CREATE
+ component.dialogMode.set(EditDialogMode.CREATE)
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
- component.dialogMode = EditDialogMode.EDIT
+ component.dialogMode.set(EditDialogMode.EDIT)
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
})
diff --git a/src-ui/src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.html b/src-ui/src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.html
index feb834035..af99a5f91 100644
--- a/src-ui/src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.html
+++ b/src-ui/src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.html
@@ -25,8 +25,8 @@
-
+
@if (showCorrespondentField) {
-
+
}
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 af3b4dbd8..a818c752b 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
@@ -75,20 +75,20 @@ describe('MailRuleEditDialogComponent', () => {
fixture = TestBed.createComponent(MailRuleEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
- settingsService.currentUser = { id: 99, username: 'user99' }
+ settingsService.currentUser.set({ id: 99, username: 'user99' })
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support create and edit modes', () => {
- component.dialogMode = EditDialogMode.CREATE
+ component.dialogMode.set(EditDialogMode.CREATE)
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
- component.dialogMode = EditDialogMode.EDIT
+ component.dialogMode.set(EditDialogMode.EDIT)
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
})
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 436569a08..a123c097c 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
@@ -1,11 +1,12 @@
import { Component, inject } from '@angular/core'
+import { toSignal } from '@angular/core/rxjs-interop'
import {
FormControl,
FormGroup,
FormsModule,
ReactiveFormsModule,
} from '@angular/forms'
-import { first } from 'rxjs'
+import { map } 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'
@@ -154,37 +155,28 @@ const METADATA_CORRESPONDENT_OPTIONS = [
],
})
export class MailRuleEditDialogComponent extends EditDialogComponent {
- private accountService: MailAccountService
- private correspondentService: CorrespondentService
- private documentTypeService: DocumentTypeService
+ private readonly accountService = inject(MailAccountService)
+ private readonly correspondentService = inject(CorrespondentService)
+ private readonly documentTypeService = inject(DocumentTypeService)
- accounts: MailAccount[]
- correspondents: Correspondent[]
- documentTypes: DocumentType[]
+ readonly accounts = toSignal(
+ this.accountService.listAll().pipe(map((result) => result.results)),
+ { initialValue: undefined as MailAccount[] }
+ )
+ readonly correspondents = toSignal(
+ this.correspondentService.listAll().pipe(map((result) => result.results)),
+ { initialValue: undefined as Correspondent[] }
+ )
+ readonly documentTypes = toSignal(
+ this.documentTypeService.listAll().pipe(map((result) => result.results)),
+ { initialValue: undefined as DocumentType[] }
+ )
constructor() {
super()
this.service = inject(MailRuleService)
- this.accountService = inject(MailAccountService)
- this.correspondentService = inject(CorrespondentService)
- this.documentTypeService = inject(DocumentTypeService)
this.userService = inject(UserService)
this.settingsService = inject(SettingsService)
-
- this.accountService
- .listAll()
- .pipe(first())
- .subscribe((result) => (this.accounts = result.results))
-
- this.correspondentService
- .listAll()
- .pipe(first())
- .subscribe((result) => (this.correspondents = result.results))
-
- this.documentTypeService
- .listAll()
- .pipe(first())
- .subscribe((result) => (this.documentTypes = result.results))
}
getCreateTitle() {
diff --git a/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.spec.ts
index 2466ced73..67bbba641 100644
--- a/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.spec.ts
+++ b/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.spec.ts
@@ -30,20 +30,20 @@ describe('StoragePathEditDialogComponent', () => {
documentService = TestBed.inject(DocumentService)
fixture = TestBed.createComponent(StoragePathEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
- settingsService.currentUser = { id: 99, username: 'user99' }
+ settingsService.currentUser.set({ id: 99, username: 'user99' })
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support create and edit modes', () => {
- component.dialogMode = EditDialogMode.CREATE
+ component.dialogMode.set(EditDialogMode.CREATE)
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
- component.dialogMode = EditDialogMode.EDIT
+ component.dialogMode.set(EditDialogMode.EDIT)
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
})
diff --git a/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.ts
index 68ce40f5e..f0d17a965 100644
--- a/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.ts
+++ b/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.ts
@@ -67,11 +67,11 @@ export class StoragePathEditDialogComponent
private testDocument: Document
public testResult: string
public testFailed: boolean = false
- public loading = false
public testLoading = false
constructor() {
super()
+ this.loading.set(false)
this.service = inject(StoragePathService)
this.userService = inject(UserService)
this.settingsService = inject(SettingsService)
@@ -138,7 +138,7 @@ export class StoragePathEditDialogComponent
this.documentsInput$.pipe(
distinctUntilChanged(),
takeUntil(this.unsubscribeNotifier),
- tap(() => (this.loading = true)),
+ tap(() => this.loading.set(true)),
switchMap((title) =>
this.documentsService
.listFiltered(
@@ -152,7 +152,7 @@ export class StoragePathEditDialogComponent
.pipe(
map((result) => result.results),
catchError(() => of([])), // empty on error
- tap(() => (this.loading = false))
+ tap(() => this.loading.set(false))
)
)
)
diff --git a/src-ui/src/app/components/common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component.spec.ts
index 16fb7a9f3..c16130a18 100644
--- a/src-ui/src/app/components/common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component.spec.ts
+++ b/src-ui/src/app/components/common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component.spec.ts
@@ -48,20 +48,20 @@ describe('TagEditDialogComponent', () => {
fixture = TestBed.createComponent(TagEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
- settingsService.currentUser = { id: 99, username: 'user99' }
+ settingsService.currentUser.set({ id: 99, username: 'user99' })
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support create and edit modes', () => {
- component.dialogMode = EditDialogMode.CREATE
+ component.dialogMode.set(EditDialogMode.CREATE)
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
- component.dialogMode = EditDialogMode.EDIT
+ component.dialogMode.set(EditDialogMode.EDIT)
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
})
diff --git a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html
index df1593073..3bab247dc 100644
--- a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html
+++ b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html
@@ -31,7 +31,7 @@
-
+
@if (object?.is_mfa_enabled && currentUserIsSuperUser) {
@@ -42,7 +42,7 @@
i18n-title
buttonClasses="btn-outline-danger btn-sm"
iconName="trash"
- [disabled]="totpLoading"
+ [disabled]="totpLoading()"
(confirm)="deactivateTotp()">
}
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 9ffa1ea95..f50bf830e 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
@@ -73,7 +73,7 @@ describe('UserEditDialogComponent', () => {
fixture = TestBed.createComponent(UserEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
- settingsService.currentUser = { id: 99, username: 'user99' }
+ settingsService.currentUser.set({ id: 99, username: 'user99' })
permissionsService = TestBed.inject(PermissionsService)
toastService = TestBed.inject(ToastService)
component = fixture.componentInstance
@@ -82,13 +82,13 @@ describe('UserEditDialogComponent', () => {
})
it('should support create and edit modes', () => {
- component.dialogMode = EditDialogMode.CREATE
+ component.dialogMode.set(EditDialogMode.CREATE)
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
- component.dialogMode = EditDialogMode.EDIT
+ component.dialogMode.set(EditDialogMode.EDIT)
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
})
@@ -114,17 +114,17 @@ describe('UserEditDialogComponent', () => {
it('should detect whether password was changed in form on save', () => {
component.objectForm.get('password').setValue(null)
component.save()
- expect(component.passwordIsSet).toBeFalsy()
+ expect(component.passwordIsSet()).toBeFalsy()
// unchanged pw
component.objectForm.get('password').setValue('*******')
component.save()
- expect(component.passwordIsSet).toBeFalsy()
+ expect(component.passwordIsSet()).toBeFalsy()
// unchanged pw
component.objectForm.get('password').setValue('helloworld')
component.save()
- expect(component.passwordIsSet).toBeTruthy()
+ expect(component.passwordIsSet()).toBeTruthy()
})
it('should support deactivation of TOTP', () => {
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 215b2c137..426fc67e2 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
@@ -1,11 +1,12 @@
-import { Component, OnInit, inject } from '@angular/core'
+import { Component, OnInit, inject, signal } from '@angular/core'
+import { toSignal } from '@angular/core/rxjs-interop'
import {
FormControl,
FormGroup,
FormsModule,
ReactiveFormsModule,
} from '@angular/forms'
-import { first } from 'rxjs'
+import { first, map } 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'
@@ -38,22 +39,19 @@ export class UserEditDialogComponent
implements OnInit
{
private toastService = inject(ToastService)
- private groupsService: GroupService
+ private readonly groupsService = inject(GroupService)
- groups: Group[]
- passwordIsSet: boolean = false
- public totpLoading: boolean = false
+ readonly groups = toSignal(
+ this.groupsService.listAll().pipe(map((result) => result.results)),
+ { initialValue: undefined as Group[] }
+ )
+ readonly passwordIsSet = signal(false)
+ readonly totpLoading = signal(false)
constructor() {
super()
this.service = inject(UserService)
- this.groupsService = inject(GroupService)
this.settingsService = inject(SettingsService)
-
- this.groupsService
- .listAll()
- .pipe(first())
- .subscribe((result) => (this.groups = result.results))
}
ngOnInit(): void {
@@ -103,14 +101,15 @@ export class UserEditDialogComponent
if (!groupsVal) return []
else
return groupsVal.flatMap(
- (id) => this.groups.find((g) => g.id == id)?.permissions
+ (id) => this.groups()?.find((g) => g.id == id)?.permissions
)
}
save(): void {
- this.passwordIsSet =
+ this.passwordIsSet.set(
this.objectForm.get('password').value?.toString().replaceAll('*', '')
.length > 0
+ )
super.save()
}
@@ -119,13 +118,13 @@ export class UserEditDialogComponent
}
deactivateTotp() {
- this.totpLoading = true
+ this.totpLoading.set(true)
;(this.service as UserService)
.deactivateTotp(this.object)
.pipe(first())
.subscribe({
next: (result) => {
- this.totpLoading = false
+ this.totpLoading.set(false)
if (result) {
this.toastService.showInfo($localize`Totp deactivated`)
this.object.is_mfa_enabled = false
@@ -134,7 +133,7 @@ export class UserEditDialogComponent
}
},
error: (e) => {
- this.totpLoading = false
+ this.totpLoading.set(false)
this.toastService.showError($localize`Totp deactivation failed`, e)
},
})
diff --git a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html
index 51b8a2a5d..9dce30891 100644
--- a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html
+++ b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html
@@ -140,7 +140,7 @@
@if (formGroup.get('schedule_date_field').value === 'custom_field') {
}
@@ -162,7 +162,7 @@
@if (formGroup.get('type').value === WorkflowTriggerType.Consumption) {
-
+
}
@if (formGroup.get('type').value === WorkflowTriggerType.DocumentAdded || formGroup.get('type').value === WorkflowTriggerType.DocumentUpdated || formGroup.get('type').value === WorkflowTriggerType.Scheduled) {
@@ -262,10 +262,10 @@
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 070e5124f..abc773ddf 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
@@ -187,14 +187,14 @@ describe('WorkflowEditDialogComponent', () => {
fixture = TestBed.createComponent(WorkflowEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
- settingsService.currentUser = { id: 99, username: 'user99' }
+ settingsService.currentUser.set({ id: 99, username: 'user99' })
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support create and edit modes, support adding triggers and actions on new workflow', () => {
- component.dialogMode = EditDialogMode.CREATE
+ component.dialogMode.set(EditDialogMode.CREATE)
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
@@ -209,7 +209,7 @@ describe('WorkflowEditDialogComponent', () => {
expect(component.object).not.toBeUndefined()
expect(component.object.triggers).toHaveLength(1)
- component.dialogMode = EditDialogMode.EDIT
+ component.dialogMode.set(EditDialogMode.EDIT)
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
})
@@ -771,25 +771,21 @@ describe('WorkflowEditDialogComponent', () => {
false
)
- component.correspondents = [{ id: 1, name: 'C1' } as any]
- component.documentTypes = [{ id: 2, name: 'DT' } as any]
- component.storagePaths = [{ id: 3, name: 'SP' } as any]
-
expect(
component.getFilterSelectItems(TriggerFilterType.CorrespondentIs)
- ).toEqual(component.correspondents)
+ ).toEqual(component.correspondents())
expect(
component.getFilterSelectItems(TriggerFilterType.DocumentTypeIs)
- ).toEqual(component.documentTypes)
+ ).toEqual(component.documentTypes())
expect(
component.getFilterSelectItems(TriggerFilterType.DocumentTypeAny)
- ).toEqual(component.documentTypes)
+ ).toEqual(component.documentTypes())
expect(
component.getFilterSelectItems(TriggerFilterType.StoragePathIs)
- ).toEqual(component.storagePaths)
+ ).toEqual(component.storagePaths())
expect(
component.getFilterSelectItems(TriggerFilterType.StoragePathAny)
- ).toEqual(component.storagePaths)
+ ).toEqual(component.storagePaths())
expect(component.getFilterSelectItems(TriggerFilterType.TagsAll)).toEqual(
[]
)
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 83e7a40f9..4db656149 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
@@ -4,7 +4,8 @@ import {
moveItemInArray,
} from '@angular/cdk/drag-drop'
import { NgTemplateOutlet } from '@angular/common'
-import { Component, OnInit, inject } from '@angular/core'
+import { Component, OnInit, computed, inject, signal } from '@angular/core'
+import { toSignal } from '@angular/core/rxjs-interop'
import {
AbstractControl,
FormArray,
@@ -15,7 +16,7 @@ import {
} from '@angular/forms'
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
-import { Subscription, first, takeUntil } from 'rxjs'
+import { Subscription, map, 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'
@@ -470,23 +471,40 @@ export class WorkflowEditDialogComponent
public TriggerFilterType = TriggerFilterType
public filterDefinitions = TRIGGER_FILTER_DEFINITIONS
- private correspondentService: CorrespondentService
- private documentTypeService: DocumentTypeService
- private storagePathService: StoragePathService
- private mailRuleService: MailRuleService
- private customFieldsService: CustomFieldsService
+ private readonly correspondentService = inject(CorrespondentService)
+ private readonly documentTypeService = inject(DocumentTypeService)
+ private readonly storagePathService = inject(StoragePathService)
+ private readonly mailRuleService = inject(MailRuleService)
+ private readonly customFieldsService = inject(CustomFieldsService)
- templates: Workflow[]
- correspondents: Correspondent[]
- documentTypes: DocumentType[]
- storagePaths: StoragePath[]
- mailRules: MailRule[]
- customFields: CustomField[]
- dateCustomFields: CustomField[]
+ readonly templates = signal(undefined)
+ readonly correspondents = toSignal(
+ this.correspondentService.listAll().pipe(map((result) => result.results)),
+ { initialValue: undefined as Correspondent[] }
+ )
+ readonly documentTypes = toSignal(
+ this.documentTypeService.listAll().pipe(map((result) => result.results)),
+ { initialValue: undefined as DocumentType[] }
+ )
+ readonly storagePaths = toSignal(
+ this.storagePathService.listAll().pipe(map((result) => result.results)),
+ { initialValue: undefined as StoragePath[] }
+ )
+ readonly mailRules = toSignal(
+ this.mailRuleService.listAll().pipe(map((result) => result.results)),
+ { initialValue: undefined as MailRule[] }
+ )
+ readonly customFields = toSignal(
+ this.customFieldsService.listAll().pipe(map((result) => result.results)),
+ { initialValue: undefined as CustomField[] }
+ )
+ readonly dateCustomFields = computed(() =>
+ this.customFields()?.filter((f) => f.data_type === CustomFieldDataType.Date)
+ )
expandedItem: number = null
- private allowedActionTypes = []
+ readonly allowedActionTypes = signal([])
private readonly triggerFilterOptionsMap = new WeakMap<
FormArray,
@@ -496,43 +514,8 @@ export class WorkflowEditDialogComponent
constructor() {
super()
this.service = inject(WorkflowService)
- this.correspondentService = inject(CorrespondentService)
- this.documentTypeService = inject(DocumentTypeService)
- this.storagePathService = inject(StoragePathService)
- this.mailRuleService = inject(MailRuleService)
this.userService = inject(UserService)
this.settingsService = inject(SettingsService)
- this.customFieldsService = inject(CustomFieldsService)
-
- this.correspondentService
- .listAll()
- .pipe(first())
- .subscribe((result) => (this.correspondents = result.results))
-
- this.documentTypeService
- .listAll()
- .pipe(first())
- .subscribe((result) => (this.documentTypes = result.results))
-
- this.storagePathService
- .listAll()
- .pipe(first())
- .subscribe((result) => (this.storagePaths = result.results))
-
- this.mailRuleService
- .listAll()
- .pipe(first())
- .subscribe((result) => (this.mailRules = result.results))
-
- this.customFieldsService
- .listAll()
- .pipe(first())
- .subscribe((result) => {
- this.customFields = result.results
- this.dateCustomFields = this.customFields?.filter(
- (f) => f.data_type === CustomFieldDataType.Date
- )
- })
}
getCreateTitle() {
@@ -565,11 +548,13 @@ export class WorkflowEditDialogComponent
this.checkRemovalActionFields.bind(this)
)
this.checkRemovalActionFields(this.objectForm.value)
- this.allowedActionTypes = this.settingsService.get(
- SETTINGS_KEYS.EMAIL_ENABLED
+ this.allowedActionTypes.set(
+ this.settingsService.get(SETTINGS_KEYS.EMAIL_ENABLED)
+ ? WORKFLOW_ACTION_OPTIONS
+ : WORKFLOW_ACTION_OPTIONS.filter(
+ (a) => a.id !== WorkflowActionType.Email
+ )
)
- ? WORKFLOW_ACTION_OPTIONS
- : WORKFLOW_ACTION_OPTIONS.filter((a) => a.id !== WorkflowActionType.Email)
}
private checkRemovalActionFields(formWorkflow: Workflow) {
@@ -954,11 +939,11 @@ export class WorkflowEditDialogComponent
switch (definition.selectItems) {
case 'correspondents':
- return this.correspondents
+ return this.correspondents()
case 'documentTypes':
- return this.documentTypes
+ return this.documentTypes()
case 'storagePaths':
- return this.storagePaths
+ return this.storagePaths()
default:
return []
}
@@ -1294,7 +1279,8 @@ export class WorkflowEditDialogComponent
}
get actionTypeOptions() {
- return this.allowedActionTypes
+ this.settingsService.trackChanges()
+ return this.allowedActionTypes()
}
getActionTypeOptionName(type: WorkflowActionType): string {
diff --git a/src-ui/src/app/components/common/email-document-dialog/email-document-dialog.component.html b/src-ui/src/app/components/common/email-document-dialog/email-document-dialog.component.html
index 079790c4b..3a5d5799a 100644
--- a/src-ui/src/app/components/common/email-document-dialog/email-document-dialog.component.html
+++ b/src-ui/src/app/components/common/email-document-dialog/email-document-dialog.component.html
@@ -1,8 +1,8 @@
@@ -23,11 +23,11 @@