Tasks and trash to direct signals

This commit is contained in:
shamoon
2026-07-08 13:02:10 -07:00
parent b7836d7cfd
commit 1b0232a4d7
6 changed files with 59 additions and 121 deletions
@@ -4,22 +4,22 @@
info="Manage trashed documents that are pending deletion."
i18n-info
infoLink="usage/#document-trash">
<button class="btn btn-sm btn-outline-secondary" (click)="clearSelection()" [hidden]="selectedDocuments.size === 0">
<button class="btn btn-sm btn-outline-secondary" (click)="clearSelection()" [hidden]="selectedDocuments().size === 0">
<i-bs name="x" class="me-1"></i-bs><ng-container i18n>Clear selection</ng-container>
</button>
<button type="button" class="btn btn-sm btn-outline-primary" (click)="restoreAll(selectedDocuments)" [disabled]="selectedDocuments.size === 0">
<button type="button" class="btn btn-sm btn-outline-primary" (click)="restoreAll(selectedDocuments())" [disabled]="selectedDocuments().size === 0">
<i-bs name="arrow-counterclockwise" class="me-1"></i-bs><ng-container i18n>Restore selected</ng-container>
</button>
<button type="button" class="btn btn-sm btn-outline-danger" (click)="emptyTrash(selectedDocuments)" [disabled]="selectedDocuments.size === 0">
<button type="button" class="btn btn-sm btn-outline-danger" (click)="emptyTrash(selectedDocuments())" [disabled]="selectedDocuments().size === 0">
<i-bs name="trash" class="me-1"></i-bs><ng-container i18n>Delete selected</ng-container>
</button>
<button type="button" class="btn btn-sm btn-outline-danger" (click)="emptyTrash()" [disabled]="documentsInTrash.length === 0">
<button type="button" class="btn btn-sm btn-outline-danger" (click)="emptyTrash()" [disabled]="documentsInTrash().length === 0">
<i-bs name="trash" class="me-1"></i-bs><ng-container i18n>Empty trash</ng-container>
</button>
</pngx-page-header>
<div class="row mb-3">
<ngb-pagination class="col-auto" [pageSize]="25" [collectionSize]="totalDocuments" [(page)]="page" [maxSize]="5" (pageChange)="reload()" size="sm" aria-label="Pagination"></ngb-pagination>
<ngb-pagination class="col-auto" [pageSize]="25" [collectionSize]="totalDocuments()" [page]="page()" [maxSize]="5" (pageChange)="page.set($event); reload()" size="sm" aria-label="Pagination"></ngb-pagination>
</div>
<div class="card border table-responsive mb-3">
@@ -28,7 +28,7 @@
<tr>
<th scope="col">
<div class="form-check m-0 ms-2 me-n2">
<input type="checkbox" class="form-check-input" id="all-objects" [(ngModel)]="allToggled" [disabled]="documentsInTrash.length === 0" (click)="toggleAll($event); $event.stopPropagation();">
<input type="checkbox" class="form-check-input" id="all-objects" [ngModel]="allToggled()" (ngModelChange)="allToggled.set($event)" [disabled]="documentsInTrash().length === 0" (click)="toggleAll($event); $event.stopPropagation();">
<label class="form-check-label" for="all-objects"></label>
</div>
</th>
@@ -46,11 +46,11 @@
</td>
</tr>
}
@for (document of documentsInTrash; track document.id) {
@for (document of documentsInTrash(); track document.id) {
<tr (click)="toggleSelected(document); $event.stopPropagation();" (mouseleave)="popupPreview.close()" class="data-row fade" [class.show]="show">
<td>
<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();">
<label class="form-check-label" for="{{document.id}}"></label>
</div>
</td>
@@ -91,13 +91,13 @@
@if (!loading) {
<div class="d-flex mb-2">
<div>
<ng-container i18n>{totalDocuments, plural, =1 {One document in trash} other {{{totalDocuments || 0}} total documents in trash}}</ng-container>
@if (selectedDocuments.size > 0) {
&nbsp;({{selectedDocuments.size}} selected)
<ng-container i18n>{totalDocuments(), plural, =1 {One document in trash} other {{{totalDocuments() || 0}} total documents in trash}}</ng-container>
@if (selectedDocuments().size > 0) {
&nbsp;({{selectedDocuments().size}} selected)
}
</div>
@if (documentsInTrash.length > 20) {
<ngb-pagination class="ms-auto" [pageSize]="25" [collectionSize]="totalDocuments" [(page)]="page" [maxSize]="5" (pageChange)="reload()" size="sm" aria-label="Pagination"></ngb-pagination>
@if (documentsInTrash().length > 20) {
<ngb-pagination class="ms-auto" [pageSize]="25" [collectionSize]="totalDocuments()" [page]="page()" [maxSize]="5" (pageChange)="page.set($event); reload()" size="sm" aria-label="Pagination"></ngb-pagination>
}
</div>
}
@@ -77,7 +77,7 @@ describe('TrashComponent', () => {
component.reload()
jest.advanceTimersByTime(100)
expect(trashSpy).toHaveBeenCalled()
expect(component.documentsInTrash).toEqual(documentsInTrash)
expect(component.documentsInTrash()).toEqual(documentsInTrash)
})
it('should support delete document, show error if needed', () => {
@@ -179,8 +179,8 @@ describe('TrashComponent', () => {
})
it('should support toggle all items in view', () => {
component.documentsInTrash = documentsInTrash
expect(component.selectedDocuments.size).toEqual(0)
component.documentsInTrash.set(documentsInTrash)
expect(component.selectedDocuments().size).toEqual(0)
const toggleAllSpy = jest.spyOn(component, 'toggleAll')
const checkButton = fixture.debugElement.queryAll(
By.css('input.form-check-input')
@@ -189,21 +189,21 @@ describe('TrashComponent', () => {
checkButton.nativeElement.checked = true
checkButton.nativeElement.dispatchEvent(new Event('click'))
expect(toggleAllSpy).toHaveBeenCalled()
expect(component.selectedDocuments.size).toEqual(documentsInTrash.length)
expect(component.selectedDocuments().size).toEqual(documentsInTrash.length)
})
it('should support toggle item', () => {
component.selectedDocuments = new Set([1])
component.selectedDocuments.set(new Set([1]))
component.toggleSelected(documentsInTrash[0])
expect(component.selectedDocuments.size).toEqual(0)
expect(component.selectedDocuments().size).toEqual(0)
component.toggleSelected(documentsInTrash[0])
expect(component.selectedDocuments.size).toEqual(1)
expect(component.selectedDocuments().size).toEqual(1)
})
it('should support clear selection', () => {
component.selectedDocuments = new Set([1])
component.selectedDocuments.set(new Set([1]))
component.clearSelection()
expect(component.selectedDocuments.size).toEqual(0)
expect(component.selectedDocuments().size).toEqual(0)
})
it('should correctly display days remaining', () => {
@@ -42,51 +42,11 @@ export class TrashComponent
private settingsService = inject(SettingsService)
private router = inject(Router)
private documentsInTrashSignal = signal<Document[]>([])
private selectedDocumentsSignal = signal<Set<number>>(new Set())
private allToggledSignal = signal(false)
private pageSignal = signal(1)
private totalDocumentsSignal = signal<number>(undefined)
public get documentsInTrash(): Document[] {
return this.documentsInTrashSignal()
}
public set documentsInTrash(documentsInTrash: Document[]) {
this.documentsInTrashSignal.set(documentsInTrash)
}
public get selectedDocuments(): Set<number> {
return this.selectedDocumentsSignal()
}
public set selectedDocuments(selectedDocuments: Set<number>) {
this.selectedDocumentsSignal.set(selectedDocuments)
}
public get allToggled(): boolean {
return this.allToggledSignal()
}
public set allToggled(allToggled: boolean) {
this.allToggledSignal.set(allToggled)
}
public get page(): number {
return this.pageSignal()
}
public set page(page: number) {
this.pageSignal.set(page)
}
public get totalDocuments(): number {
return this.totalDocumentsSignal()
}
public set totalDocuments(totalDocuments: number) {
this.totalDocumentsSignal.set(totalDocuments)
}
readonly documentsInTrash = signal<Document[]>([])
readonly selectedDocuments = signal<Set<number>>(new Set())
readonly allToggled = signal(false)
readonly page = signal(1)
readonly totalDocuments = signal<number>(undefined)
constructor() {
super()
@@ -96,12 +56,12 @@ export class TrashComponent
reload() {
this.loading = true
this.trashService
.getTrash(this.page)
.getTrash(this.page())
.pipe(
tap((r) => {
this.documentsInTrash = r.results
this.totalDocuments = r.count
this.selectedDocuments = new Set()
this.documentsInTrash.set(r.results)
this.totalDocuments.set(r.count)
this.selectedDocuments.set(new Set())
this.loading = false
})
)
@@ -161,7 +121,7 @@ export class TrashComponent
.subscribe({
next: () => {
this.toastService.showInfo($localize`Document(s) deleted`)
this.allToggled = false
this.allToggled.set(false)
modal.close()
this.reload()
},
@@ -204,7 +164,7 @@ export class TrashComponent
.subscribe({
next: () => {
this.toastService.showInfo($localize`Document(s) restored`)
this.allToggled = false
this.allToggled.set(false)
this.reload()
},
error: (err) => {
@@ -218,23 +178,25 @@ export class TrashComponent
toggleAll(event: PointerEvent) {
if ((event.target as HTMLInputElement).checked) {
this.selectedDocuments = new Set(this.documentsInTrash.map((t) => t.id))
this.selectedDocuments.set(
new Set(this.documentsInTrash().map((t) => t.id))
)
} else {
this.clearSelection()
}
}
toggleSelected(object: Document) {
const selectedDocuments = new Set(this.selectedDocuments)
const selectedDocuments = new Set(this.selectedDocuments())
selectedDocuments.has(object.id)
? selectedDocuments.delete(object.id)
: selectedDocuments.add(object.id)
this.selectedDocuments = selectedDocuments
this.selectedDocuments.set(selectedDocuments)
}
clearSelection() {
this.allToggled = false
this.selectedDocuments = new Set()
this.allToggled.set(false)
this.selectedDocuments.set(new Set())
}
getDaysRemaining(document: Document): number {