mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-07-09 21:45:11 +00:00
Permissions dialog, management lists, saved views, workflow edit
This commit is contained in:
+10
-10
@@ -140,7 +140,7 @@
|
||||
</div>
|
||||
@if (formGroup.get('schedule_date_field').value === 'custom_field') {
|
||||
<div class="col-4">
|
||||
<pngx-input-select i18n-title title="Custom field" formControlName="schedule_date_custom_field" [items]="dateCustomFields" i18n-hint hint="Custom field to use for date." [error]="error?.schedule_date_custom_field"></pngx-input-select>
|
||||
<pngx-input-select i18n-title title="Custom field" formControlName="schedule_date_custom_field" [items]="dateCustomFields()" i18n-hint hint="Custom field to use for date." [error]="error?.schedule_date_custom_field"></pngx-input-select>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@@ -162,7 +162,7 @@
|
||||
@if (formGroup.get('type').value === WorkflowTriggerType.Consumption) {
|
||||
<pngx-input-select i18n-title title="Filter sources" [items]="sourceOptions" horizontal="true" [multiple]="true" formControlName="sources" [error]="error?.sources"></pngx-input-select>
|
||||
<pngx-input-text i18n-title title="Filter path" formControlName="filter_path" horizontal="true" i18n-hint hint="Apply to documents that match this path. Wildcards specified as * are allowed. Case-normalized." [error]="error?.filter_path"></pngx-input-text>
|
||||
<pngx-input-select i18n-title title="Filter mail rule" [items]="mailRules" horizontal="true" [allowNull]="true" formControlName="filter_mailrule" i18n-hint hint="Apply to documents consumed via this mail rule." [error]="error?.filter_mailrule"></pngx-input-select>
|
||||
<pngx-input-select i18n-title title="Filter mail rule" [items]="mailRules()" horizontal="true" [allowNull]="true" formControlName="filter_mailrule" i18n-hint hint="Apply to documents consumed via this mail rule." [error]="error?.filter_mailrule"></pngx-input-select>
|
||||
}
|
||||
@if (formGroup.get('type').value === WorkflowTriggerType.DocumentAdded || formGroup.get('type').value === WorkflowTriggerType.DocumentUpdated || formGroup.get('type').value === WorkflowTriggerType.Scheduled) {
|
||||
<pngx-input-select i18n-title title="Content matching algorithm" horizontal="true" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
|
||||
@@ -262,10 +262,10 @@
|
||||
<div class="col">
|
||||
<pngx-input-text i18n-title title="Assign title" formControlName="assign_title" i18n-hint hint="Can include some placeholders, see <a target='_blank' href='https://docs.paperless-ngx.com/usage/#workflows'>documentation</a>." [error]="error?.actions?.[i]?.assign_title"></pngx-input-text>
|
||||
<pngx-input-tags [allowCreate]="false" i18n-title title="Assign tags" formControlName="assign_tags"></pngx-input-tags>
|
||||
<pngx-input-select i18n-title title="Assign document type" [items]="documentTypes" [allowNull]="true" formControlName="assign_document_type"></pngx-input-select>
|
||||
<pngx-input-select i18n-title title="Assign correspondent" [items]="correspondents" [allowNull]="true" formControlName="assign_correspondent"></pngx-input-select>
|
||||
<pngx-input-select i18n-title title="Assign storage path" [items]="storagePaths" [allowNull]="true" formControlName="assign_storage_path"></pngx-input-select>
|
||||
<pngx-input-select i18n-title title="Assign custom fields" multiple="true" [items]="customFields" [allowNull]="true" formControlName="assign_custom_fields"></pngx-input-select>
|
||||
<pngx-input-select i18n-title title="Assign document type" [items]="documentTypes()" [allowNull]="true" formControlName="assign_document_type"></pngx-input-select>
|
||||
<pngx-input-select i18n-title title="Assign correspondent" [items]="correspondents()" [allowNull]="true" formControlName="assign_correspondent"></pngx-input-select>
|
||||
<pngx-input-select i18n-title title="Assign storage path" [items]="storagePaths()" [allowNull]="true" formControlName="assign_storage_path"></pngx-input-select>
|
||||
<pngx-input-select i18n-title title="Assign custom fields" multiple="true" [items]="customFields()" [allowNull]="true" formControlName="assign_custom_fields"></pngx-input-select>
|
||||
<pngx-input-custom-fields-values formControlName="assign_custom_fields_values" [selectedFields]="formGroup.get('assign_custom_fields').value" (removeSelectedField)="removeSelectedCustomField($event, formGroup)"></pngx-input-custom-fields-values>
|
||||
</div>
|
||||
<div class="col">
|
||||
@@ -326,25 +326,25 @@
|
||||
<h6 class="form-label" i18n>Remove correspondents</h6>
|
||||
<pngx-input-switch i18n-title title="Remove all" [horizontal]="true" formControlName="remove_all_correspondents"></pngx-input-switch>
|
||||
<div class="mt-n3">
|
||||
<pngx-input-select i18n-title title="" multiple="true" [items]="correspondents" formControlName="remove_correspondents"></pngx-input-select>
|
||||
<pngx-input-select i18n-title title="" multiple="true" [items]="correspondents()" formControlName="remove_correspondents"></pngx-input-select>
|
||||
</div>
|
||||
|
||||
<h6 class="form-label" i18n>Remove document types</h6>
|
||||
<pngx-input-switch i18n-title title="Remove all" [horizontal]="true" formControlName="remove_all_document_types"></pngx-input-switch>
|
||||
<div class="mt-n3">
|
||||
<pngx-input-select i18n-title title="" multiple="true" [items]="documentTypes" formControlName="remove_document_types"></pngx-input-select>
|
||||
<pngx-input-select i18n-title title="" multiple="true" [items]="documentTypes()" formControlName="remove_document_types"></pngx-input-select>
|
||||
</div>
|
||||
|
||||
<h6 class="form-label" i18n>Remove storage paths</h6>
|
||||
<pngx-input-switch i18n-title title="Remove all" [horizontal]="true" formControlName="remove_all_storage_paths"></pngx-input-switch>
|
||||
<div class="mt-n3">
|
||||
<pngx-input-select i18n-title title="" multiple="true" [items]="storagePaths" formControlName="remove_storage_paths"></pngx-input-select>
|
||||
<pngx-input-select i18n-title title="" multiple="true" [items]="storagePaths()" formControlName="remove_storage_paths"></pngx-input-select>
|
||||
</div>
|
||||
|
||||
<h6 class="form-label" i18n>Remove custom fields</h6>
|
||||
<pngx-input-switch i18n-title title="Remove all" [horizontal]="true" formControlName="remove_all_custom_fields"></pngx-input-switch>
|
||||
<div class="mt-n3">
|
||||
<pngx-input-select i18n-title title="" multiple="true" [items]="customFields" formControlName="remove_custom_fields"></pngx-input-select>
|
||||
<pngx-input-select i18n-title title="" multiple="true" [items]="customFields()" formControlName="remove_custom_fields"></pngx-input-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
|
||||
+5
-9
@@ -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(
|
||||
[]
|
||||
)
|
||||
|
||||
+43
-122
@@ -4,7 +4,8 @@ import {
|
||||
moveItemInArray,
|
||||
} from '@angular/cdk/drag-drop'
|
||||
import { NgTemplateOutlet } from '@angular/common'
|
||||
import { Component, OnInit, inject, signal } 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,79 +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 correspondentService = inject(CorrespondentService)
|
||||
private documentTypeService = inject(DocumentTypeService)
|
||||
private storagePathService = inject(StoragePathService)
|
||||
private mailRuleService = inject(MailRuleService)
|
||||
private customFieldsService = inject(CustomFieldsService)
|
||||
|
||||
private templatesSignal = signal<Workflow[]>(undefined)
|
||||
private correspondentsSignal = signal<Correspondent[]>(undefined)
|
||||
private documentTypesSignal = signal<DocumentType[]>(undefined)
|
||||
private storagePathsSignal = signal<StoragePath[]>(undefined)
|
||||
private mailRulesSignal = signal<MailRule[]>(undefined)
|
||||
private customFieldsSignal = signal<CustomField[]>(undefined)
|
||||
private dateCustomFieldsSignal = signal<CustomField[]>(undefined)
|
||||
|
||||
get templates(): Workflow[] {
|
||||
return this.templatesSignal()
|
||||
}
|
||||
|
||||
set templates(templates: Workflow[]) {
|
||||
this.templatesSignal.set(templates)
|
||||
}
|
||||
|
||||
get correspondents(): Correspondent[] {
|
||||
return this.correspondentsSignal()
|
||||
}
|
||||
|
||||
set correspondents(correspondents: Correspondent[]) {
|
||||
this.correspondentsSignal.set(correspondents)
|
||||
}
|
||||
|
||||
get documentTypes(): DocumentType[] {
|
||||
return this.documentTypesSignal()
|
||||
}
|
||||
|
||||
set documentTypes(documentTypes: DocumentType[]) {
|
||||
this.documentTypesSignal.set(documentTypes)
|
||||
}
|
||||
|
||||
get storagePaths(): StoragePath[] {
|
||||
return this.storagePathsSignal()
|
||||
}
|
||||
|
||||
set storagePaths(storagePaths: StoragePath[]) {
|
||||
this.storagePathsSignal.set(storagePaths)
|
||||
}
|
||||
|
||||
get mailRules(): MailRule[] {
|
||||
return this.mailRulesSignal()
|
||||
}
|
||||
|
||||
set mailRules(mailRules: MailRule[]) {
|
||||
this.mailRulesSignal.set(mailRules)
|
||||
}
|
||||
|
||||
get customFields(): CustomField[] {
|
||||
return this.customFieldsSignal()
|
||||
}
|
||||
|
||||
set customFields(customFields: CustomField[]) {
|
||||
this.customFieldsSignal.set(customFields)
|
||||
}
|
||||
|
||||
get dateCustomFields(): CustomField[] {
|
||||
return this.dateCustomFieldsSignal()
|
||||
}
|
||||
|
||||
set dateCustomFields(dateCustomFields: CustomField[]) {
|
||||
this.dateCustomFieldsSignal.set(dateCustomFields)
|
||||
}
|
||||
readonly templates = signal<Workflow[]>(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 allowedActionTypesSignal = signal([])
|
||||
readonly allowedActionTypes = signal([])
|
||||
|
||||
private readonly triggerFilterOptionsMap = new WeakMap<
|
||||
FormArray,
|
||||
@@ -552,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() {
|
||||
@@ -621,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) {
|
||||
@@ -1010,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 []
|
||||
}
|
||||
@@ -1351,15 +1280,7 @@ export class WorkflowEditDialogComponent
|
||||
|
||||
get actionTypeOptions() {
|
||||
this.settingsService.trackChanges()
|
||||
return this.allowedActionTypes
|
||||
}
|
||||
|
||||
get allowedActionTypes() {
|
||||
return this.allowedActionTypesSignal()
|
||||
}
|
||||
|
||||
set allowedActionTypes(allowedActionTypes) {
|
||||
this.allowedActionTypesSignal.set(allowedActionTypes)
|
||||
return this.allowedActionTypes()
|
||||
}
|
||||
|
||||
getActionTypeOptionName(type: WorkflowActionType): string {
|
||||
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="modal-basic-title">{{title}}</h4>
|
||||
<h4 class="modal-title" id="modal-basic-title">{{title()}}</h4>
|
||||
<button type="button" class="btn-close" aria-label="Close" (click)="cancelClicked()">
|
||||
</button>
|
||||
</div>
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
<form [formGroup]="form">
|
||||
<div class="form-group">
|
||||
<pngx-permissions-form [users]="users" formControlName="permissions_form"></pngx-permissions-form>
|
||||
<pngx-permissions-form [users]="users()" formControlName="permissions_form"></pngx-permissions-form>
|
||||
</div>
|
||||
<div class="form-group mt-4">
|
||||
<div class="offset-lg-3 row">
|
||||
@@ -16,18 +16,18 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if (note) {
|
||||
@if (note()) {
|
||||
<div class="small text-muted fst-italic mt-2">
|
||||
{{ note }}
|
||||
{{ note() }}
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@if (!buttonsEnabled) {
|
||||
@if (!buttonsEnabled()) {
|
||||
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
|
||||
<span class="visually-hidden" i18n>Loading...</span>
|
||||
}
|
||||
<button type="button" class="btn btn-outline-primary" (click)="cancelClicked()" [disabled]="!buttonsEnabled" i18n>Cancel</button>
|
||||
<button type="button" class="btn btn-primary" (click)="confirm()" [disabled]="!buttonsEnabled" i18n>Confirm</button>
|
||||
<button type="button" class="btn btn-outline-primary" (click)="cancelClicked()" [disabled]="!buttonsEnabled()" i18n>Cancel</button>
|
||||
<button type="button" class="btn btn-primary" (click)="confirm()" [disabled]="!buttonsEnabled()" i18n>Confirm</button>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ describe('PermissionsDialogComponent', () => {
|
||||
permissions: set_permissions.set_permissions,
|
||||
}
|
||||
component.object = obj
|
||||
expect(component.title).toEqual(`Edit permissions for ${obj.name}`)
|
||||
expect(component.title()).toEqual(`Edit permissions for ${obj.name}`)
|
||||
expect(component.permissions).toEqual(set_permissions)
|
||||
})
|
||||
|
||||
|
||||
+10
-43
@@ -6,6 +6,7 @@ import {
|
||||
inject,
|
||||
signal,
|
||||
} from '@angular/core'
|
||||
import { toSignal } from '@angular/core/rxjs-interop'
|
||||
import {
|
||||
FormControl,
|
||||
FormGroup,
|
||||
@@ -13,6 +14,7 @@ import {
|
||||
ReactiveFormsModule,
|
||||
} from '@angular/forms'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { map } 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'
|
||||
@@ -34,49 +36,22 @@ export class PermissionsDialogComponent {
|
||||
activeModal = inject(NgbActiveModal)
|
||||
private userService = inject(UserService)
|
||||
|
||||
private usersSignal = signal<User[]>(undefined)
|
||||
private titleSignal = signal($localize`Set permissions`)
|
||||
private noteSignal = signal<string>(null)
|
||||
private buttonsEnabledSignal = signal(true)
|
||||
readonly users = toSignal(
|
||||
this.userService.listAll().pipe(map((r) => r.results)),
|
||||
{ initialValue: undefined as User[] }
|
||||
)
|
||||
readonly title = signal($localize`Set permissions`)
|
||||
readonly note = signal<string>(null)
|
||||
readonly buttonsEnabled = signal(true)
|
||||
private o: ObjectWithPermissions = undefined
|
||||
|
||||
constructor() {
|
||||
this.userService.listAll().subscribe((r) => (this.users = r.results))
|
||||
}
|
||||
|
||||
get users(): User[] {
|
||||
return this.usersSignal()
|
||||
}
|
||||
|
||||
set users(users: User[]) {
|
||||
this.usersSignal.set(users)
|
||||
}
|
||||
|
||||
@Output()
|
||||
public confirmClicked = new EventEmitter()
|
||||
|
||||
@Input()
|
||||
get title(): string {
|
||||
return this.titleSignal()
|
||||
}
|
||||
|
||||
set title(title: string) {
|
||||
this.titleSignal.set(title)
|
||||
}
|
||||
|
||||
@Input()
|
||||
get note(): string {
|
||||
return this.noteSignal()
|
||||
}
|
||||
|
||||
set note(note: string) {
|
||||
this.noteSignal.set(note)
|
||||
}
|
||||
|
||||
@Input()
|
||||
set object(o: ObjectWithPermissions) {
|
||||
this.o = o
|
||||
this.title = $localize`Edit permissions for ` + o['name']
|
||||
this.title.set($localize`Edit permissions for ` + o['name'])
|
||||
this.form.patchValue({
|
||||
merge: true,
|
||||
permissions_form: {
|
||||
@@ -95,14 +70,6 @@ export class PermissionsDialogComponent {
|
||||
merge: new FormControl(true),
|
||||
})
|
||||
|
||||
get buttonsEnabled(): boolean {
|
||||
return this.buttonsEnabledSignal()
|
||||
}
|
||||
|
||||
set buttonsEnabled(buttonsEnabled: boolean) {
|
||||
this.buttonsEnabledSignal.set(buttonsEnabled)
|
||||
}
|
||||
|
||||
get permissions() {
|
||||
return {
|
||||
owner: this.form.get('permissions_form').value?.owner ?? null,
|
||||
|
||||
@@ -914,7 +914,7 @@ export class BulkEditorComponent
|
||||
})
|
||||
modal.componentInstance.confirmClicked.subscribe(
|
||||
({ permissions, merge }) => {
|
||||
modal.componentInstance.buttonsEnabled = false
|
||||
modal.componentInstance.buttonsEnabled.set(false)
|
||||
this.executeBulkEditMethod(modal, 'set_permissions', {
|
||||
...permissions,
|
||||
merge,
|
||||
|
||||
+13
-13
@@ -19,7 +19,7 @@
|
||||
</select>
|
||||
<span class="input-group-text text-muted d-none d-md-flex" i18n>per page</span>
|
||||
</div>
|
||||
<ngb-pagination [pageSize]="pageSize" [collectionSize]="collectionSize" [(page)]="page" [maxSize]="5" (pageChange)="reloadData()" size="sm" aria-label="Pagination"></ngb-pagination>
|
||||
<ngb-pagination [pageSize]="pageSize" [collectionSize]="collectionSize()" [page]="page()" [maxSize]="5" (pageChange)="page.set($event); reloadData()" size="sm" aria-label="Pagination"></ngb-pagination>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -31,21 +31,21 @@
|
||||
<tr>
|
||||
<th>
|
||||
<div class="form-check m-0 ms-2 me-n2">
|
||||
<input type="checkbox" class="form-check-input" id="all-objects" [(ngModel)]="togggleAll" [disabled]="data.length === 0" (change)="$event.target.checked ? selectPage() : clearSelection(); $event.stopPropagation();">
|
||||
<input type="checkbox" class="form-check-input" id="all-objects" [ngModel]="togggleAll()" [disabled]="data().length === 0" (change)="$event.target.checked ? selectPage() : clearSelection(); $event.stopPropagation();">
|
||||
<label class="form-check-label" for="all-objects"></label>
|
||||
</div>
|
||||
</th>
|
||||
<th class="fw-normal" pngxSortable="name" [currentSortField]="sortField" [currentSortReverse]="sortReverse" (sort)="onSort($event)" i18n>Name</th>
|
||||
<th class="fw-normal d-none d-sm-table-cell" pngxSortable="matching_algorithm" [currentSortField]="sortField" [currentSortReverse]="sortReverse" (sort)="onSort($event)" i18n>Matching</th>
|
||||
<th class="fw-normal" pngxSortable="document_count" [currentSortField]="sortField" [currentSortReverse]="sortReverse" (sort)="onSort($event)" i18n>Document count</th>
|
||||
<th class="fw-normal" pngxSortable="name" [currentSortField]="sortField()" [currentSortReverse]="sortReverse()" (sort)="onSort($event)" i18n>Name</th>
|
||||
<th class="fw-normal d-none d-sm-table-cell" pngxSortable="matching_algorithm" [currentSortField]="sortField()" [currentSortReverse]="sortReverse()" (sort)="onSort($event)" i18n>Matching</th>
|
||||
<th class="fw-normal" pngxSortable="document_count" [currentSortField]="sortField()" [currentSortReverse]="sortReverse()" (sort)="onSort($event)" i18n>Document count</th>
|
||||
@for (column of extraColumns; track column) {
|
||||
<th class="fw-normal" [ngClass]="{ 'd-none d-sm-table-cell' : column.hideOnMobile }" pngxSortable="{{column.key}}" [currentSortField]="sortField" [currentSortReverse]="sortReverse" (sort)="onSort($event)">{{column.name}}</th>
|
||||
<th class="fw-normal" [ngClass]="{ 'd-none d-sm-table-cell' : column.hideOnMobile }" pngxSortable="{{column.key}}" [currentSortField]="sortField()" [currentSortReverse]="sortReverse()" (sort)="onSort($event)">{{column.name}}</th>
|
||||
}
|
||||
<th class="fw-normal" i18n>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if (loading && data.length === 0) {
|
||||
@if (loading() && data().length === 0) {
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
|
||||
@@ -53,25 +53,25 @@
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
@for (object of data; track object) {
|
||||
@for (object of data(); track object) {
|
||||
<ng-container [ngTemplateOutlet]="objectRow" [ngTemplateOutletContext]="{ object: object, depth: 0 }"></ng-container>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@if (!loading || data.length > 0) {
|
||||
@if (!loading() || data().length > 0) {
|
||||
<div class="d-flex mb-2">
|
||||
@if (displayCollectionSize > 0) {
|
||||
@if (displayCollectionSize() > 0) {
|
||||
<div>
|
||||
<ng-container i18n>{displayCollectionSize, plural, =1 {One {{typeName}}} other {{{displayCollectionSize || 0}} total {{typeNamePlural}}}}</ng-container>
|
||||
<ng-container i18n>{displayCollectionSize(), plural, =1 {One {{typeName}}} other {{{displayCollectionSize() || 0}} total {{typeNamePlural}}}}</ng-container>
|
||||
@if (hasSelection) {
|
||||
({{selectedCount}} selected)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@if (collectionSize > 20) {
|
||||
<ngb-pagination class="ms-auto" [pageSize]="pageSize" [collectionSize]="collectionSize" [(page)]="page" [maxSize]="5" (pageChange)="reloadData()" size="sm" aria-label="Pagination"></ngb-pagination>
|
||||
@if (collectionSize() > 20) {
|
||||
<ngb-pagination class="ms-auto" [pageSize]="pageSize" [collectionSize]="collectionSize()" [page]="page()" [maxSize]="5" (pageChange)="page.set($event); reloadData()" size="sm" aria-label="Pagination"></ngb-pagination>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
+24
-24
@@ -147,7 +147,7 @@ describe('ManagementListComponent', () => {
|
||||
component.nameFilter = 'foo' // subject normally triggered by ngModel
|
||||
tick(400) // debounce
|
||||
fixture.detectChanges()
|
||||
expect(component.data).toEqual([tags[0]])
|
||||
expect(component.data()).toEqual([tags[0]])
|
||||
|
||||
nameFilterInput.nativeElement.dispatchEvent(
|
||||
new KeyboardEvent('keyup', { code: 'Escape' })
|
||||
@@ -155,7 +155,7 @@ describe('ManagementListComponent', () => {
|
||||
tick(400) // debounce
|
||||
fixture.detectChanges()
|
||||
expect(component.nameFilter).toBeNull()
|
||||
expect(component.data).toEqual(tags)
|
||||
expect(component.data()).toEqual(tags)
|
||||
tick(100) // load
|
||||
}))
|
||||
|
||||
@@ -242,8 +242,8 @@ describe('ManagementListComponent', () => {
|
||||
component.reloadData()
|
||||
tick(100)
|
||||
|
||||
expect(component.collectionSize).toBe(1)
|
||||
expect(component.displayCollectionSize).toBe(3)
|
||||
expect(component.collectionSize()).toBe(1)
|
||||
expect(component.displayCollectionSize()).toBe(3)
|
||||
}))
|
||||
|
||||
it('should support quick filter for objects', () => {
|
||||
@@ -275,9 +275,9 @@ describe('ManagementListComponent', () => {
|
||||
})
|
||||
)
|
||||
)
|
||||
component.page = 2
|
||||
component.page.set(2)
|
||||
component.reloadData()
|
||||
expect(component.page).toEqual(1)
|
||||
expect(component.page()).toEqual(1)
|
||||
})
|
||||
|
||||
it('should support toggle select page in vew', () => {
|
||||
@@ -295,28 +295,28 @@ describe('ManagementListComponent', () => {
|
||||
|
||||
it('selectNone should clear selection and reset toggle flag', () => {
|
||||
component.selectedObjects = new Set([tags[0].id, tags[1].id])
|
||||
component.togggleAll = true
|
||||
component.togggleAll.set(true)
|
||||
|
||||
component.selectNone()
|
||||
|
||||
expect(component.selectedObjects.size).toBe(0)
|
||||
expect(component.togggleAll).toBe(false)
|
||||
expect(component.togggleAll()).toBe(false)
|
||||
})
|
||||
|
||||
it('selectPage should select current page items or clear selection', () => {
|
||||
component.selectPage()
|
||||
expect(component.selectedObjects).toEqual(new Set(tags.map((t) => t.id)))
|
||||
expect(component.togggleAll).toBe(true)
|
||||
expect(component.togggleAll()).toBe(true)
|
||||
|
||||
component.togggleAll = true
|
||||
component.togggleAll.set(true)
|
||||
component.clearSelection()
|
||||
expect(component.selectedObjects.size).toBe(0)
|
||||
expect(component.togggleAll).toBe(false)
|
||||
expect(component.togggleAll()).toBe(false)
|
||||
})
|
||||
|
||||
it('selectAll should activate all-selection mode', () => {
|
||||
;(tagService.listFiltered as jest.Mock).mockClear()
|
||||
component.collectionSize = tags.length
|
||||
component.collectionSize.set(tags.length)
|
||||
|
||||
component.selectAll()
|
||||
|
||||
@@ -325,41 +325,41 @@ describe('ManagementListComponent', () => {
|
||||
expect((component as any).allSelectionActive).toBe(true)
|
||||
expect(component.hasSelection).toBe(true)
|
||||
expect(component.selectedCount).toBe(tags.length)
|
||||
expect(component.togggleAll).toBe(true)
|
||||
expect(component.togggleAll()).toBe(true)
|
||||
})
|
||||
|
||||
it('selectAll should clear selection when collection size is zero', () => {
|
||||
component.selectedObjects = new Set([1])
|
||||
component.collectionSize = 0
|
||||
component.togggleAll = true
|
||||
component.collectionSize.set(0)
|
||||
component.togggleAll.set(true)
|
||||
|
||||
component.selectAll()
|
||||
|
||||
expect(component.selectedObjects.size).toBe(0)
|
||||
expect(component.togggleAll).toBe(false)
|
||||
expect(component.togggleAll()).toBe(false)
|
||||
})
|
||||
|
||||
it('toggleSelected should toggle object selection and update toggle state', () => {
|
||||
component.toggleSelected(tags[0])
|
||||
expect(component.selectedObjects.has(tags[0].id)).toBe(true)
|
||||
expect(component.togggleAll).toBe(false)
|
||||
expect(component.togggleAll()).toBe(false)
|
||||
|
||||
component.toggleSelected(tags[1])
|
||||
component.toggleSelected(tags[2])
|
||||
expect(component.togggleAll).toBe(true)
|
||||
expect(component.togggleAll()).toBe(true)
|
||||
|
||||
component.toggleSelected(tags[1])
|
||||
expect(component.selectedObjects.has(tags[1].id)).toBe(false)
|
||||
expect(component.togggleAll).toBe(false)
|
||||
expect(component.togggleAll()).toBe(false)
|
||||
})
|
||||
|
||||
it('areAllPageItemsSelected should return false when page has no selectable items', () => {
|
||||
component.data = []
|
||||
component.data.set([])
|
||||
component.selectedObjects.clear()
|
||||
|
||||
expect((component as any).areAllPageItemsSelected()).toBe(false)
|
||||
|
||||
component.data = tags
|
||||
component.data.set(tags)
|
||||
})
|
||||
|
||||
it('should support bulk edit permissions', () => {
|
||||
@@ -498,7 +498,7 @@ describe('ManagementListComponent', () => {
|
||||
document_count: 10,
|
||||
parent: 1,
|
||||
}
|
||||
component['unfilteredData'].push(childTag)
|
||||
component['unfilteredData'].update((data) => [...data, childTag])
|
||||
const original = component.getOriginalObject({ id: 4 } as Tag)
|
||||
expect(original).toEqual(childTag)
|
||||
})
|
||||
@@ -536,7 +536,7 @@ describe('ManagementListComponent', () => {
|
||||
.mockReturnValue(of({ success: true }))
|
||||
|
||||
component.typeNamePlural = 'tags'
|
||||
component.page = 2
|
||||
component.page.set(2)
|
||||
component.pageSize = 100
|
||||
|
||||
tick()
|
||||
@@ -545,7 +545,7 @@ describe('ManagementListComponent', () => {
|
||||
SETTINGS_KEYS.OBJECT_LIST_SIZES,
|
||||
{ tags: 100 }
|
||||
)
|
||||
expect(component.page).toBe(1)
|
||||
expect(component.page()).toBe(1)
|
||||
expect(reloadSpy).toHaveBeenCalled()
|
||||
expect(toastErrorSpy).not.toHaveBeenCalled()
|
||||
}))
|
||||
|
||||
+35
-99
@@ -88,89 +88,25 @@ export abstract class ManagementListComponent<T extends MatchingModel>
|
||||
|
||||
@ViewChildren(SortableDirective) headers: QueryList<SortableDirective>
|
||||
|
||||
private dataSignal = signal<T[]>([])
|
||||
private unfilteredDataSignal = signal<T[]>([])
|
||||
readonly data = signal<T[]>([])
|
||||
private readonly unfilteredData = signal<T[]>([])
|
||||
private currentExtraParams: { [key: string]: any } = null
|
||||
private allSelectionActive = false
|
||||
|
||||
private pageSignal = signal(1)
|
||||
readonly page = signal(1)
|
||||
|
||||
private collectionSizeSignal = signal(0)
|
||||
private displayCollectionSizeSignal = signal(0)
|
||||
readonly collectionSize = signal(0)
|
||||
readonly displayCollectionSize = signal(0)
|
||||
|
||||
private sortFieldSignal = signal<string>(undefined)
|
||||
private sortReverseSignal = signal<boolean>(undefined)
|
||||
readonly sortField = signal<string>(undefined)
|
||||
readonly sortReverse = signal<boolean>(undefined)
|
||||
|
||||
private nameFilterDebounce: Subject<string>
|
||||
protected unsubscribeNotifier: Subject<any> = new Subject()
|
||||
protected _nameFilter: string
|
||||
|
||||
public selectedObjects: Set<number> = new Set()
|
||||
private togggleAllSignal = signal(false)
|
||||
|
||||
public get data(): T[] {
|
||||
return this.dataSignal()
|
||||
}
|
||||
|
||||
public set data(data: T[]) {
|
||||
this.dataSignal.set(data)
|
||||
}
|
||||
|
||||
private get unfilteredData(): T[] {
|
||||
return this.unfilteredDataSignal()
|
||||
}
|
||||
|
||||
private set unfilteredData(data: T[]) {
|
||||
this.unfilteredDataSignal.set(data)
|
||||
}
|
||||
|
||||
public get page(): number {
|
||||
return this.pageSignal()
|
||||
}
|
||||
|
||||
public set page(page: number) {
|
||||
this.pageSignal.set(page)
|
||||
}
|
||||
|
||||
public get collectionSize(): number {
|
||||
return this.collectionSizeSignal()
|
||||
}
|
||||
|
||||
public set collectionSize(collectionSize: number) {
|
||||
this.collectionSizeSignal.set(collectionSize)
|
||||
}
|
||||
|
||||
public get displayCollectionSize(): number {
|
||||
return this.displayCollectionSizeSignal()
|
||||
}
|
||||
|
||||
public set displayCollectionSize(displayCollectionSize: number) {
|
||||
this.displayCollectionSizeSignal.set(displayCollectionSize)
|
||||
}
|
||||
|
||||
public get sortField(): string {
|
||||
return this.sortFieldSignal()
|
||||
}
|
||||
|
||||
public set sortField(sortField: string) {
|
||||
this.sortFieldSignal.set(sortField)
|
||||
}
|
||||
|
||||
public get sortReverse(): boolean {
|
||||
return this.sortReverseSignal()
|
||||
}
|
||||
|
||||
public set sortReverse(sortReverse: boolean) {
|
||||
this.sortReverseSignal.set(sortReverse)
|
||||
}
|
||||
|
||||
public get togggleAll(): boolean {
|
||||
return this.togggleAllSignal()
|
||||
}
|
||||
|
||||
public set togggleAll(togggleAll: boolean) {
|
||||
this.togggleAllSignal.set(togggleAll)
|
||||
}
|
||||
readonly togggleAll = signal(false)
|
||||
|
||||
public get hasSelection(): boolean {
|
||||
return this.selectedObjects.size > 0 || this.allSelectionActive
|
||||
@@ -178,7 +114,7 @@ export abstract class ManagementListComponent<T extends MatchingModel>
|
||||
|
||||
public get selectedCount(): number {
|
||||
return this.allSelectionActive
|
||||
? this.displayCollectionSize
|
||||
? this.displayCollectionSize()
|
||||
: this.selectedObjects.size
|
||||
}
|
||||
|
||||
@@ -195,7 +131,7 @@ export abstract class ManagementListComponent<T extends MatchingModel>
|
||||
)
|
||||
.subscribe((title) => {
|
||||
this._nameFilter = title
|
||||
this.page = 1
|
||||
this.page.set(1)
|
||||
this.reloadData()
|
||||
})
|
||||
}
|
||||
@@ -215,8 +151,8 @@ export abstract class ManagementListComponent<T extends MatchingModel>
|
||||
}
|
||||
|
||||
onSort(event: SortEvent) {
|
||||
this.sortField = event.column
|
||||
this.sortReverse = event.reverse
|
||||
this.sortField.set(event.column)
|
||||
this.sortReverse.set(event.reverse)
|
||||
this.reloadData()
|
||||
}
|
||||
|
||||
@@ -235,13 +171,13 @@ export abstract class ManagementListComponent<T extends MatchingModel>
|
||||
getDocumentCount(object: MatchingModel): number {
|
||||
return (
|
||||
object.document_count ??
|
||||
this.unfilteredData.find((d) => d.id == object.id)?.document_count ??
|
||||
this.unfilteredData().find((d) => d.id == object.id)?.document_count ??
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
public getOriginalObject(object: T): T {
|
||||
return this.unfilteredData.find((d) => d?.id == object?.id) || object
|
||||
return this.unfilteredData().find((d) => d?.id == object?.id) || object
|
||||
}
|
||||
|
||||
reloadData(extraParams: { [key: string]: any } = null) {
|
||||
@@ -250,10 +186,10 @@ export abstract class ManagementListComponent<T extends MatchingModel>
|
||||
this.clearSelection()
|
||||
this.service
|
||||
.listFiltered(
|
||||
this.page,
|
||||
this.page(),
|
||||
this.pageSize,
|
||||
this.sortField,
|
||||
this.sortReverse,
|
||||
this.sortField(),
|
||||
this.sortReverse(),
|
||||
this._nameFilter,
|
||||
true,
|
||||
extraParams
|
||||
@@ -261,16 +197,16 @@ export abstract class ManagementListComponent<T extends MatchingModel>
|
||||
.pipe(
|
||||
takeUntil(this.unsubscribeNotifier),
|
||||
tap((c) => {
|
||||
this.unfilteredData = c.results
|
||||
this.data = this.filterData(c.results)
|
||||
this.collectionSize = this.getCollectionSize(c)
|
||||
this.displayCollectionSize = this.getDisplayCollectionSize(c)
|
||||
this.unfilteredData.set(c.results)
|
||||
this.data.set(this.filterData(c.results))
|
||||
this.collectionSize.set(this.getCollectionSize(c))
|
||||
this.displayCollectionSize.set(this.getDisplayCollectionSize(c))
|
||||
})
|
||||
)
|
||||
.subscribe({
|
||||
error: (error: HttpErrorResponse) => {
|
||||
if (error.error?.detail?.includes('Invalid page')) {
|
||||
this.page = 1
|
||||
this.page.set(1)
|
||||
this.reloadData()
|
||||
}
|
||||
},
|
||||
@@ -385,7 +321,7 @@ export abstract class ManagementListComponent<T extends MatchingModel>
|
||||
})
|
||||
this.settingsService.storeSettings().subscribe({
|
||||
next: () => {
|
||||
this.page = 1
|
||||
this.page.set(1)
|
||||
this.reloadData()
|
||||
},
|
||||
error: (error) => {
|
||||
@@ -409,7 +345,7 @@ export abstract class ManagementListComponent<T extends MatchingModel>
|
||||
if (!this.permissionsService.currentUserCan(action, this.permissionType))
|
||||
return false
|
||||
let ownsAll: boolean = true
|
||||
const objects = this.data.filter((o) => this.selectedObjects.has(o.id))
|
||||
const objects = this.data().filter((o) => this.selectedObjects.has(o.id))
|
||||
ownsAll = objects.every((o) =>
|
||||
this.permissionsService.currentUserOwnsObject(o)
|
||||
)
|
||||
@@ -430,7 +366,7 @@ export abstract class ManagementListComponent<T extends MatchingModel>
|
||||
|
||||
clearSelection() {
|
||||
this.allSelectionActive = false
|
||||
this.togggleAll = false
|
||||
this.togggleAll.set(false)
|
||||
this.selectedObjects.clear()
|
||||
}
|
||||
|
||||
@@ -440,19 +376,19 @@ export abstract class ManagementListComponent<T extends MatchingModel>
|
||||
|
||||
selectPage() {
|
||||
this.allSelectionActive = false
|
||||
this.selectedObjects = new Set(this.getSelectableIDs(this.data))
|
||||
this.togggleAll = this.areAllPageItemsSelected()
|
||||
this.selectedObjects = new Set(this.getSelectableIDs(this.data()))
|
||||
this.togggleAll.set(this.areAllPageItemsSelected())
|
||||
}
|
||||
|
||||
selectAll() {
|
||||
if (!this.collectionSize) {
|
||||
if (!this.collectionSize()) {
|
||||
this.clearSelection()
|
||||
return
|
||||
}
|
||||
|
||||
this.allSelectionActive = true
|
||||
this.selectedObjects = new Set(this.getSelectableIDs(this.data))
|
||||
this.togggleAll = this.areAllPageItemsSelected()
|
||||
this.selectedObjects = new Set(this.getSelectableIDs(this.data()))
|
||||
this.togggleAll.set(this.areAllPageItemsSelected())
|
||||
}
|
||||
|
||||
toggleSelected(object) {
|
||||
@@ -462,14 +398,14 @@ export abstract class ManagementListComponent<T extends MatchingModel>
|
||||
this.selectedObjects.has(object.id)
|
||||
? this.selectedObjects.delete(object.id)
|
||||
: this.selectedObjects.add(object.id)
|
||||
this.togggleAll = this.areAllPageItemsSelected()
|
||||
this.togggleAll.set(this.areAllPageItemsSelected())
|
||||
}
|
||||
|
||||
protected areAllPageItemsSelected(): boolean {
|
||||
if (this.allSelectionActive) {
|
||||
return this.data.length > 0
|
||||
return this.data().length > 0
|
||||
}
|
||||
const ids = this.getSelectableIDs(this.data)
|
||||
const ids = this.getSelectableIDs(this.data())
|
||||
return ids.length > 0 && ids.every((id) => this.selectedObjects.has(id))
|
||||
}
|
||||
|
||||
@@ -479,7 +415,7 @@ export abstract class ManagementListComponent<T extends MatchingModel>
|
||||
})
|
||||
modal.componentInstance.confirmClicked.subscribe(
|
||||
({ permissions, merge }) => {
|
||||
modal.componentInstance.buttonsEnabled = false
|
||||
modal.componentInstance.buttonsEnabled.set(false)
|
||||
this.service
|
||||
.bulk_edit_objects(
|
||||
this.allSelectionActive ? [] : Array.from(this.selectedObjects),
|
||||
@@ -498,7 +434,7 @@ export abstract class ManagementListComponent<T extends MatchingModel>
|
||||
this.reloadData()
|
||||
},
|
||||
error: (error) => {
|
||||
modal.componentInstance.buttonsEnabled = true
|
||||
modal.componentInstance.buttonsEnabled.set(true)
|
||||
this.toastService.showError(
|
||||
$localize`Error updating permissions`,
|
||||
error
|
||||
|
||||
+1
-1
@@ -136,7 +136,7 @@ describe('TagListComponent', () => {
|
||||
],
|
||||
}
|
||||
|
||||
component.data = [parent as any]
|
||||
component.data.set([parent as any])
|
||||
component.selectPage()
|
||||
|
||||
expect(component.selectedObjects.has(10)).toBe(true)
|
||||
|
||||
@@ -330,7 +330,7 @@ export class MailComponent
|
||||
dialog.object = object
|
||||
modal.componentInstance.confirmClicked.subscribe(
|
||||
({ permissions, merge }) => {
|
||||
modal.componentInstance.buttonsEnabled = false
|
||||
modal.componentInstance.buttonsEnabled.set(false)
|
||||
const service: AbstractPaperlessService<MailRule | MailAccount> =
|
||||
'account' in object ? this.mailRuleService : this.mailAccountService
|
||||
object.owner = permissions['owner']
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { DragDropModule } from '@angular/cdk/drag-drop'
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import { signal } from '@angular/core'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { NgbModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
@@ -226,7 +227,8 @@ describe('SavedViewsComponent', () => {
|
||||
const modalRef = {
|
||||
componentInstance: {
|
||||
confirmClicked,
|
||||
buttonsEnabled: true,
|
||||
buttonsEnabled: signal(true),
|
||||
note: signal(null),
|
||||
},
|
||||
close: jest.fn(),
|
||||
} as any
|
||||
|
||||
@@ -256,10 +256,12 @@ export class SavedViewsComponent
|
||||
})
|
||||
const dialog = modal.componentInstance as PermissionsDialogComponent
|
||||
dialog.object = savedView
|
||||
dialog.note = $localize`Note: Sharing saved views does not share the underlying documents.`
|
||||
dialog.note.set(
|
||||
$localize`Note: Sharing saved views does not share the underlying documents.`
|
||||
)
|
||||
|
||||
modal.componentInstance.confirmClicked.subscribe(({ permissions }) => {
|
||||
modal.componentInstance.buttonsEnabled = false
|
||||
modal.componentInstance.buttonsEnabled.set(false)
|
||||
const view = {
|
||||
id: savedView.id,
|
||||
owner: permissions.owner,
|
||||
|
||||
Reference in New Issue
Block a user