Some of the lists etc
This commit is contained in:
parent
f0de7cfe02
commit
183425959a
File diff suppressed because it is too large
Load Diff
@ -47,7 +47,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
@for (document of documentsInTrash; track document.id) {
|
@for (document of documentsInTrash; track document.id) {
|
||||||
<tr (click)="toggleSelected(document); $event.stopPropagation();" (mouseleave)="popupPreview.close()">
|
<tr (click)="toggleSelected(document); $event.stopPropagation();" (mouseleave)="popupPreview.close()" class="data-row" [class.reveal]="reveal">
|
||||||
<td>
|
<td>
|
||||||
<div class="form-check m-0 ms-2 me-n2">
|
<div class="form-check m-0 ms-2 me-n2">
|
||||||
<input type="checkbox" class="form-check-input" id="{{document.id}}" [checked]="selectedDocuments.has(document.id)" (click)="toggleSelected(document); $event.stopPropagation();">
|
<input type="checkbox" class="form-check-input" id="{{document.id}}" [checked]="selectedDocuments.has(document.id)" (click)="toggleSelected(document); $event.stopPropagation();">
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
.data-row {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
@ -69,6 +69,7 @@ describe('TrashComponent', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should call correct service method on reload', () => {
|
it('should call correct service method on reload', () => {
|
||||||
|
jest.useFakeTimers()
|
||||||
const trashSpy = jest.spyOn(trashService, 'getTrash')
|
const trashSpy = jest.spyOn(trashService, 'getTrash')
|
||||||
trashSpy.mockReturnValue(
|
trashSpy.mockReturnValue(
|
||||||
of({
|
of({
|
||||||
@ -78,6 +79,7 @@ describe('TrashComponent', () => {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
component.reload()
|
component.reload()
|
||||||
|
jest.advanceTimersByTime(100)
|
||||||
expect(trashSpy).toHaveBeenCalled()
|
expect(trashSpy).toHaveBeenCalled()
|
||||||
expect(component.documentsInTrash).toEqual(documentsInTrash)
|
expect(component.documentsInTrash).toEqual(documentsInTrash)
|
||||||
})
|
})
|
||||||
|
@ -4,7 +4,7 @@ import { Document } from 'src/app/data/document'
|
|||||||
import { ToastService } from 'src/app/services/toast.service'
|
import { ToastService } from 'src/app/services/toast.service'
|
||||||
import { TrashService } from 'src/app/services/trash.service'
|
import { TrashService } from 'src/app/services/trash.service'
|
||||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
||||||
import { Subject, takeUntil } from 'rxjs'
|
import { delay, Subject, takeUntil, tap } from 'rxjs'
|
||||||
import { SettingsService } from 'src/app/services/settings.service'
|
import { SettingsService } from 'src/app/services/settings.service'
|
||||||
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||||
import { Router } from '@angular/router'
|
import { Router } from '@angular/router'
|
||||||
@ -21,6 +21,7 @@ export class TrashComponent implements OnDestroy {
|
|||||||
public page: number = 1
|
public page: number = 1
|
||||||
public totalDocuments: number
|
public totalDocuments: number
|
||||||
public isLoading: boolean = false
|
public isLoading: boolean = false
|
||||||
|
public reveal: boolean = false
|
||||||
unsubscribeNotifier: Subject<void> = new Subject()
|
unsubscribeNotifier: Subject<void> = new Subject()
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -40,12 +41,20 @@ export class TrashComponent implements OnDestroy {
|
|||||||
|
|
||||||
reload() {
|
reload() {
|
||||||
this.isLoading = true
|
this.isLoading = true
|
||||||
this.trashService.getTrash(this.page).subscribe((r) => {
|
this.trashService
|
||||||
this.documentsInTrash = r.results
|
.getTrash(this.page)
|
||||||
this.totalDocuments = r.count
|
.pipe(
|
||||||
this.isLoading = false
|
tap((r) => {
|
||||||
this.selectedDocuments.clear()
|
this.documentsInTrash = r.results
|
||||||
})
|
this.totalDocuments = r.count
|
||||||
|
this.selectedDocuments.clear()
|
||||||
|
}),
|
||||||
|
delay(100)
|
||||||
|
)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.reveal = true
|
||||||
|
this.isLoading = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(document: Document) {
|
delete(document: Document) {
|
||||||
|
@ -12,7 +12,6 @@ import { CorrespondentService } from 'src/app/services/rest/correspondent.servic
|
|||||||
import { ToastService } from 'src/app/services/toast.service'
|
import { ToastService } from 'src/app/services/toast.service'
|
||||||
import { CorrespondentEditDialogComponent } from '../../common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component'
|
import { CorrespondentEditDialogComponent } from '../../common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component'
|
||||||
import { ManagementListComponent } from '../management-list/management-list.component'
|
import { ManagementListComponent } from '../management-list/management-list.component'
|
||||||
import { takeUntil } from 'rxjs'
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pngx-correspondent-list',
|
selector: 'pngx-correspondent-list',
|
||||||
@ -65,24 +64,7 @@ export class CorrespondentListComponent extends ManagementListComponent<Correspo
|
|||||||
}
|
}
|
||||||
|
|
||||||
public reloadData(): void {
|
public reloadData(): void {
|
||||||
this.isLoading = true
|
super.reloadData({ last_correspondence: true })
|
||||||
this.clearSelection()
|
|
||||||
this.service
|
|
||||||
.listFiltered(
|
|
||||||
this.page,
|
|
||||||
null,
|
|
||||||
this.sortField,
|
|
||||||
this.sortReverse,
|
|
||||||
this._nameFilter,
|
|
||||||
true,
|
|
||||||
{ last_correspondence: true }
|
|
||||||
)
|
|
||||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
|
||||||
.subscribe((c) => {
|
|
||||||
this.data = c.results
|
|
||||||
this.collectionSize = c.count
|
|
||||||
this.isLoading = false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getDeleteMessage(object: Correspondent) {
|
getDeleteMessage(object: Correspondent) {
|
||||||
|
@ -13,16 +13,23 @@
|
|||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
|
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<div class="row">
|
<div class="row reveal">
|
||||||
<div class="col" i18n>Name</div>
|
<div class="col" i18n>Name</div>
|
||||||
<div class="col" i18n>Data Type</div>
|
<div class="col" i18n>Data Type</div>
|
||||||
<div class="col" i18n>Actions</div>
|
<div class="col" i18n>Actions</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@if (loading) {
|
||||||
|
<li class="list-group-item">
|
||||||
|
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
|
||||||
|
<ng-container i18n>Loading...</ng-container>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
|
||||||
@for (field of fields; track field) {
|
@for (field of fields; track field) {
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<div class="row">
|
<div class="row" [class.reveal]="reveal">
|
||||||
<div class="col d-flex align-items-center"><button class="btn btn-link p-0 text-start" type="button" (click)="editField(field)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.CustomField)">{{field.name}}</button></div>
|
<div class="col d-flex align-items-center"><button class="btn btn-link p-0 text-start" type="button" (click)="editField(field)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.CustomField)">{{field.name}}</button></div>
|
||||||
<div class="col d-flex align-items-center">{{getDataType(field)}}</div>
|
<div class="col d-flex align-items-center">{{getDataType(field)}}</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
@ -59,7 +66,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
@if (fields.length === 0) {
|
@if (!loading && fields.length === 0) {
|
||||||
<li class="list-group-item" i18n>No fields defined.</li>
|
<li class="list-group-item" i18n>No fields defined.</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -2,3 +2,12 @@
|
|||||||
.d-block.d-sm-none .dropdown-toggle::after {
|
.d-block.d-sm-none .dropdown-toggle::after {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-group-item .row {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-item .reveal {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
@ -95,6 +95,8 @@ describe('CustomFieldsComponent', () => {
|
|||||||
fixture = TestBed.createComponent(CustomFieldsComponent)
|
fixture = TestBed.createComponent(CustomFieldsComponent)
|
||||||
component = fixture.componentInstance
|
component = fixture.componentInstance
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
|
jest.useFakeTimers()
|
||||||
|
jest.advanceTimersByTime(100)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should support create, show notification on error / success', () => {
|
it('should support create, show notification on error / success', () => {
|
||||||
@ -119,6 +121,7 @@ describe('CustomFieldsComponent', () => {
|
|||||||
editDialog.succeeded.emit(fields[0])
|
editDialog.succeeded.emit(fields[0])
|
||||||
expect(toastInfoSpy).toHaveBeenCalled()
|
expect(toastInfoSpy).toHaveBeenCalled()
|
||||||
expect(reloadSpy).toHaveBeenCalled()
|
expect(reloadSpy).toHaveBeenCalled()
|
||||||
|
jest.advanceTimersByTime(100)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should support edit, show notification on error / success', () => {
|
it('should support edit, show notification on error / success', () => {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit } from '@angular/core'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { Subject, takeUntil } from 'rxjs'
|
import { delay, Subject, takeUntil, tap } from 'rxjs'
|
||||||
import { DATA_TYPE_LABELS, CustomField } from 'src/app/data/custom-field'
|
import { DATA_TYPE_LABELS, CustomField } from 'src/app/data/custom-field'
|
||||||
import { PermissionsService } from 'src/app/services/permissions.service'
|
import { PermissionsService } from 'src/app/services/permissions.service'
|
||||||
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
|
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
|
||||||
@ -28,6 +28,9 @@ export class CustomFieldsComponent
|
|||||||
{
|
{
|
||||||
public fields: CustomField[] = []
|
public fields: CustomField[] = []
|
||||||
|
|
||||||
|
public loading: boolean = true
|
||||||
|
public reveal: boolean = false
|
||||||
|
|
||||||
private unsubscribeNotifier: Subject<any> = new Subject()
|
private unsubscribeNotifier: Subject<any> = new Subject()
|
||||||
constructor(
|
constructor(
|
||||||
private customFieldsService: CustomFieldsService,
|
private customFieldsService: CustomFieldsService,
|
||||||
@ -47,9 +50,16 @@ export class CustomFieldsComponent
|
|||||||
reload() {
|
reload() {
|
||||||
this.customFieldsService
|
this.customFieldsService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
.pipe(
|
||||||
.subscribe((r) => {
|
takeUntil(this.unsubscribeNotifier),
|
||||||
this.fields = r.results
|
tap((r) => {
|
||||||
|
this.fields = r.results
|
||||||
|
}),
|
||||||
|
delay(100)
|
||||||
|
)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.reveal = true
|
||||||
|
this.loading = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
</h4>
|
</h4>
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<div class="row">
|
<div class="row reveal">
|
||||||
<div class="col" i18n>Name</div>
|
<div class="col" i18n>Name</div>
|
||||||
<div class="col" i18n>Server</div>
|
<div class="col" i18n>Server</div>
|
||||||
<div class="col d-none d-sm-block" i18n>Username</div>
|
<div class="col d-none d-sm-block" i18n>Username</div>
|
||||||
@ -34,9 +34,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@if (loadingAccounts) {
|
||||||
|
<li class="list-group-item">
|
||||||
|
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
|
||||||
|
<ng-container i18n>Loading...</ng-container>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
|
||||||
@for (account of mailAccounts; track account) {
|
@for (account of mailAccounts; track account) {
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<div class="row">
|
<div class="row" [class.reveal]="revealAccounts">
|
||||||
<div class="col d-flex align-items-center">
|
<div class="col d-flex align-items-center">
|
||||||
<button class="btn btn-link p-0 text-start" type="button" (click)="editMailAccount(account)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.MailAccount)">
|
<button class="btn btn-link p-0 text-start" type="button" (click)="editMailAccount(account)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.MailAccount)">
|
||||||
{{account.name}}@switch (account.account_type) {
|
{{account.name}}@switch (account.account_type) {
|
||||||
@ -76,7 +83,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
@if (mailAccounts.length === 0) {
|
@if (!loadingAccounts && mailAccounts.length === 0) {
|
||||||
<li class="list-group-item" i18n>No mail accounts defined.</li>
|
<li class="list-group-item" i18n>No mail accounts defined.</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
@ -92,7 +99,7 @@
|
|||||||
</h4>
|
</h4>
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<div class="row">
|
<div class="row reveal">
|
||||||
<div class="col" i18n>Name</div>
|
<div class="col" i18n>Name</div>
|
||||||
<div class="col d-none d-sm-block" i18n>Sort Order</div>
|
<div class="col d-none d-sm-block" i18n>Sort Order</div>
|
||||||
<div class="col" i18n>Account</div>
|
<div class="col" i18n>Account</div>
|
||||||
@ -101,9 +108,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@if (loadingRules) {
|
||||||
|
<li class="list-group-item">
|
||||||
|
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
|
||||||
|
<ng-container i18n>Loading...</ng-container>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
|
||||||
@for (rule of mailRules; track rule) {
|
@for (rule of mailRules; track rule) {
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<div class="row">
|
<div class="row" [class.reveal]="revealRules">
|
||||||
<div class="col d-flex align-items-center"><button class="btn btn-link p-0 text-start" type="button" (click)="editMailRule(rule)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.MailRule)">{{rule.name}}</button></div>
|
<div class="col d-flex align-items-center"><button class="btn btn-link p-0 text-start" type="button" (click)="editMailRule(rule)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.MailRule)">{{rule.name}}</button></div>
|
||||||
<div class="col d-flex align-items-center d-none d-sm-flex">{{rule.order}}</div>
|
<div class="col d-flex align-items-center d-none d-sm-flex">{{rule.order}}</div>
|
||||||
<div class="col d-flex align-items-center">{{(mailAccountService.getCached(rule.account) | async)?.name}}</div>
|
<div class="col d-flex align-items-center">{{(mailAccountService.getCached(rule.account) | async)?.name}}</div>
|
||||||
@ -151,7 +165,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
@if (mailRules.length === 0) {
|
@if (!loadingRules && mailRules.length === 0) {
|
||||||
<li class="list-group-item" i18n>No mail rules defined.</li>
|
<li class="list-group-item" i18n>No mail rules defined.</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -2,3 +2,12 @@
|
|||||||
.d-block.d-sm-none .dropdown-toggle::after {
|
.d-block.d-sm-none .dropdown-toggle::after {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-group-item .row {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-item .reveal {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
@ -129,6 +129,8 @@ describe('MailComponent', () => {
|
|||||||
fixture = TestBed.createComponent(MailComponent)
|
fixture = TestBed.createComponent(MailComponent)
|
||||||
component = fixture.componentInstance
|
component = fixture.componentInstance
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
|
jest.useFakeTimers()
|
||||||
|
jest.advanceTimersByTime(100)
|
||||||
})
|
})
|
||||||
|
|
||||||
function completeSetup(excludeService = null) {
|
function completeSetup(excludeService = null) {
|
||||||
@ -386,6 +388,7 @@ describe('MailComponent', () => {
|
|||||||
component.oAuthAccountId = 3
|
component.oAuthAccountId = 3
|
||||||
const editSpy = jest.spyOn(component, 'editMailAccount')
|
const editSpy = jest.spyOn(component, 'editMailAccount')
|
||||||
component.ngOnInit()
|
component.ngOnInit()
|
||||||
|
jest.advanceTimersByTime(200)
|
||||||
expect(editSpy).toHaveBeenCalled()
|
expect(editSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Component, OnInit, OnDestroy } from '@angular/core'
|
import { Component, OnInit, OnDestroy } from '@angular/core'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { Subject, first, takeUntil } from 'rxjs'
|
import { Subject, delay, first, takeUntil, tap } from 'rxjs'
|
||||||
import { ObjectWithPermissions } from 'src/app/data/object-with-permissions'
|
import { ObjectWithPermissions } from 'src/app/data/object-with-permissions'
|
||||||
import { MailAccount, MailAccountType } from 'src/app/data/mail-account'
|
import { MailAccount, MailAccountType } from 'src/app/data/mail-account'
|
||||||
import { MailRule } from 'src/app/data/mail-rule'
|
import { MailRule } from 'src/app/data/mail-rule'
|
||||||
@ -47,6 +47,11 @@ export class MailComponent
|
|||||||
return this.settingsService.get(SETTINGS_KEYS.OUTLOOK_OAUTH_URL)
|
return this.settingsService.get(SETTINGS_KEYS.OUTLOOK_OAUTH_URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public loadingRules: boolean = true
|
||||||
|
public revealRules: boolean = false
|
||||||
|
public loadingAccounts: boolean = true
|
||||||
|
public revealAccounts: boolean = false
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public mailAccountService: MailAccountService,
|
public mailAccountService: MailAccountService,
|
||||||
public mailRuleService: MailRuleService,
|
public mailRuleService: MailRuleService,
|
||||||
@ -62,9 +67,10 @@ export class MailComponent
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.mailAccountService
|
this.mailAccountService
|
||||||
.listAll(null, null, { full_perms: true })
|
.listAll(null, null, { full_perms: true })
|
||||||
.pipe(first(), takeUntil(this.unsubscribeNotifier))
|
.pipe(
|
||||||
.subscribe({
|
first(),
|
||||||
next: (r) => {
|
takeUntil(this.unsubscribeNotifier),
|
||||||
|
tap((r) => {
|
||||||
this.mailAccounts = r.results
|
this.mailAccounts = r.results
|
||||||
if (this.oAuthAccountId) {
|
if (this.oAuthAccountId) {
|
||||||
this.editMailAccount(
|
this.editMailAccount(
|
||||||
@ -73,6 +79,13 @@ export class MailComponent
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}),
|
||||||
|
delay(100)
|
||||||
|
)
|
||||||
|
.subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.loadingAccounts = false
|
||||||
|
this.revealAccounts = true
|
||||||
},
|
},
|
||||||
error: (e) => {
|
error: (e) => {
|
||||||
this.toastService.showError(
|
this.toastService.showError(
|
||||||
@ -84,10 +97,18 @@ export class MailComponent
|
|||||||
|
|
||||||
this.mailRuleService
|
this.mailRuleService
|
||||||
.listAll(null, null, { full_perms: true })
|
.listAll(null, null, { full_perms: true })
|
||||||
.pipe(first(), takeUntil(this.unsubscribeNotifier))
|
.pipe(
|
||||||
|
first(),
|
||||||
|
takeUntil(this.unsubscribeNotifier),
|
||||||
|
tap((r) => {
|
||||||
|
this.mailRules = r.results
|
||||||
|
}),
|
||||||
|
delay(100)
|
||||||
|
)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: (r) => {
|
next: (r) => {
|
||||||
this.mailRules = r.results
|
this.loadingRules = false
|
||||||
|
this.revealRules = true
|
||||||
},
|
},
|
||||||
error: (e) => {
|
error: (e) => {
|
||||||
this.toastService.showError($localize`Error retrieving mail rules`, e)
|
this.toastService.showError($localize`Error retrieving mail rules`, e)
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
@for (object of data; track object) {
|
@for (object of data; track object) {
|
||||||
<tr (click)="toggleSelected(object); $event.stopPropagation();">
|
<tr (click)="toggleSelected(object); $event.stopPropagation();" class="data-row" [class.reveal]="reveal">
|
||||||
<td>
|
<td>
|
||||||
<div class="form-check m-0 ms-2 me-n2">
|
<div class="form-check m-0 ms-2 me-n2">
|
||||||
<input type="checkbox" class="form-check-input" id="{{typeName}}{{object.id}}" [checked]="selectedObjects.has(object.id)" (click)="toggleSelected(object); $event.stopPropagation();">
|
<input type="checkbox" class="form-check-input" id="{{typeName}}{{object.id}}" [checked]="selectedObjects.has(object.id)" (click)="toggleSelected(object); $event.stopPropagation();">
|
||||||
|
@ -10,3 +10,12 @@ tbody tr:last-child td {
|
|||||||
.form-check {
|
.form-check {
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.data-row {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
@ -150,6 +150,7 @@ describe('ManagementListComponent', () => {
|
|||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
expect(component.nameFilter).toBeNull()
|
expect(component.nameFilter).toBeNull()
|
||||||
expect(component.data).toEqual(tags)
|
expect(component.data).toEqual(tags)
|
||||||
|
tick(100) // load
|
||||||
}))
|
}))
|
||||||
|
|
||||||
it('should support create, show notification on error / success', () => {
|
it('should support create, show notification on error / success', () => {
|
||||||
|
@ -7,7 +7,13 @@ import {
|
|||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { Subject } from 'rxjs'
|
import { Subject } from 'rxjs'
|
||||||
import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators'
|
import {
|
||||||
|
debounceTime,
|
||||||
|
delay,
|
||||||
|
distinctUntilChanged,
|
||||||
|
takeUntil,
|
||||||
|
tap,
|
||||||
|
} from 'rxjs/operators'
|
||||||
import {
|
import {
|
||||||
MatchingModel,
|
MatchingModel,
|
||||||
MATCHING_ALGORITHMS,
|
MATCHING_ALGORITHMS,
|
||||||
@ -89,6 +95,8 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
|
|||||||
public selectedObjects: Set<number> = new Set()
|
public selectedObjects: Set<number> = new Set()
|
||||||
public togggleAll: boolean = false
|
public togggleAll: boolean = false
|
||||||
|
|
||||||
|
public reveal: boolean = false
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.reloadData()
|
this.reloadData()
|
||||||
|
|
||||||
@ -132,7 +140,7 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
|
|||||||
this.reloadData()
|
this.reloadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadData() {
|
reloadData(extraParams: { [key: string]: any } = null) {
|
||||||
this.isLoading = true
|
this.isLoading = true
|
||||||
this.clearSelection()
|
this.clearSelection()
|
||||||
this.service
|
this.service
|
||||||
@ -142,12 +150,19 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
|
|||||||
this.sortField,
|
this.sortField,
|
||||||
this.sortReverse,
|
this.sortReverse,
|
||||||
this._nameFilter,
|
this._nameFilter,
|
||||||
true
|
true,
|
||||||
|
extraParams
|
||||||
)
|
)
|
||||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
.pipe(
|
||||||
.subscribe((c) => {
|
takeUntil(this.unsubscribeNotifier),
|
||||||
this.data = c.results
|
tap((c) => {
|
||||||
this.collectionSize = c.count
|
this.data = c.results
|
||||||
|
this.collectionSize = c.count
|
||||||
|
}),
|
||||||
|
delay(100)
|
||||||
|
)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.reveal = true
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
|
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<div class="row">
|
<div class="row reveal">
|
||||||
<div class="col" i18n>Name</div>
|
<div class="col" i18n>Name</div>
|
||||||
<div class="col d-none d-sm-flex" i18n>Sort order</div>
|
<div class="col d-none d-sm-flex" i18n>Sort order</div>
|
||||||
<div class="col" i18n>Status</div>
|
<div class="col" i18n>Status</div>
|
||||||
@ -22,9 +22,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@if (loading) {
|
||||||
|
<li class="list-group-item">
|
||||||
|
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
|
||||||
|
<ng-container i18n>Loading...</ng-container>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
|
||||||
@for (workflow of workflows; track workflow.id) {
|
@for (workflow of workflows; track workflow.id) {
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<div class="row">
|
<div class="row" [class.reveal]="reveal">
|
||||||
<div class="col d-flex align-items-center"><button class="btn btn-link p-0 text-start" type="button" (click)="editWorkflow(workflow)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.Workflow)">{{workflow.name}}</button></div>
|
<div class="col d-flex align-items-center"><button class="btn btn-link p-0 text-start" type="button" (click)="editWorkflow(workflow)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.Workflow)">{{workflow.name}}</button></div>
|
||||||
<div class="col d-flex align-items-center d-none d-sm-flex"><code>{{workflow.order}}</code></div>
|
<div class="col d-flex align-items-center d-none d-sm-flex"><code>{{workflow.order}}</code></div>
|
||||||
<div class="col d-flex align-items-center">
|
<div class="col d-flex align-items-center">
|
||||||
@ -69,7 +76,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
@if (workflows.length === 0) {
|
@if (!loading && workflows.length === 0) {
|
||||||
<li class="list-group-item" i18n>No workflows defined.</li>
|
<li class="list-group-item" [class.reveal]="reveal" i18n>No workflows defined.</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -2,3 +2,12 @@
|
|||||||
.d-block.d-sm-none .dropdown-toggle::after {
|
.d-block.d-sm-none .dropdown-toggle::after {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-group-item .row {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-item .reveal {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
@ -119,10 +119,11 @@ describe('WorkflowsComponent', () => {
|
|||||||
)
|
)
|
||||||
modalService = TestBed.inject(NgbModal)
|
modalService = TestBed.inject(NgbModal)
|
||||||
toastService = TestBed.inject(ToastService)
|
toastService = TestBed.inject(ToastService)
|
||||||
|
jest.useFakeTimers()
|
||||||
fixture = TestBed.createComponent(WorkflowsComponent)
|
fixture = TestBed.createComponent(WorkflowsComponent)
|
||||||
component = fixture.componentInstance
|
component = fixture.componentInstance
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
|
jest.advanceTimersByTime(100)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should support create, show notification on error / success', () => {
|
it('should support create, show notification on error / success', () => {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit } from '@angular/core'
|
||||||
import { WorkflowService } from 'src/app/services/rest/workflow.service'
|
import { WorkflowService } from 'src/app/services/rest/workflow.service'
|
||||||
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
|
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
|
||||||
import { Subject, takeUntil } from 'rxjs'
|
import { delay, Subject, takeUntil, tap } from 'rxjs'
|
||||||
import { Workflow } from 'src/app/data/workflow'
|
import { Workflow } from 'src/app/data/workflow'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { ToastService } from 'src/app/services/toast.service'
|
import { ToastService } from 'src/app/services/toast.service'
|
||||||
@ -26,6 +26,9 @@ export class WorkflowsComponent
|
|||||||
|
|
||||||
private unsubscribeNotifier: Subject<any> = new Subject()
|
private unsubscribeNotifier: Subject<any> = new Subject()
|
||||||
|
|
||||||
|
public loading: boolean = false
|
||||||
|
public reveal: boolean = false
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private workflowService: WorkflowService,
|
private workflowService: WorkflowService,
|
||||||
public permissionsService: PermissionsService,
|
public permissionsService: PermissionsService,
|
||||||
@ -40,11 +43,17 @@ export class WorkflowsComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
reload() {
|
reload() {
|
||||||
|
this.loading = true
|
||||||
this.workflowService
|
this.workflowService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
.pipe(
|
||||||
.subscribe((r) => {
|
takeUntil(this.unsubscribeNotifier),
|
||||||
this.workflows = r.results
|
tap((r) => (this.workflows = r.results)),
|
||||||
|
delay(100)
|
||||||
|
)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.reveal = true
|
||||||
|
this.loading = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user