mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-10-29 19:15:43 +00:00
So we can use pagination
This commit is contained in:
3 files changed
+74
-34
No files matched your search
+14
@@ -149,6 +149,20 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@if (total() > pageSize) {
|
||||||
|
<div class="d-flex justify-content-end mt-3">
|
||||||
|
<ngb-pagination
|
||||||
|
[pageSize]="pageSize"
|
||||||
|
[collectionSize]="total()"
|
||||||
|
[page]="page()"
|
||||||
|
[maxSize]="5"
|
||||||
|
(pageChange)="setPage($event)"
|
||||||
|
size="sm"
|
||||||
|
aria-label="Share link bundles pagination"
|
||||||
|
i18n-aria-label
|
||||||
|
></ngb-pagination>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
+27
-19
@@ -13,7 +13,7 @@ import { environment } from 'src/environments/environment'
|
|||||||
import { ShareLinkBundleListComponent } from './share-link-bundle-list.component'
|
import { ShareLinkBundleListComponent } from './share-link-bundle-list.component'
|
||||||
|
|
||||||
class MockShareLinkBundleService {
|
class MockShareLinkBundleService {
|
||||||
listAllBundles = jest.fn()
|
list = jest.fn()
|
||||||
delete = jest.fn()
|
delete = jest.fn()
|
||||||
rebuildBundle = jest.fn()
|
rebuildBundle = jest.fn()
|
||||||
}
|
}
|
||||||
@@ -36,7 +36,7 @@ describe('ShareLinkBundleListComponent', () => {
|
|||||||
toastService = new MockToastService()
|
toastService = new MockToastService()
|
||||||
originalApiBaseUrl = environment.apiBaseUrl
|
originalApiBaseUrl = environment.apiBaseUrl
|
||||||
|
|
||||||
service.listAllBundles.mockReturnValue(of([]))
|
service.list.mockReturnValue(of({ count: 0, results: [] }))
|
||||||
service.delete.mockReturnValue(of(true))
|
service.delete.mockReturnValue(of(true))
|
||||||
service.rebuildBundle.mockReturnValue(of(sampleBundle()))
|
service.rebuildBundle.mockReturnValue(of(sampleBundle()))
|
||||||
|
|
||||||
@@ -80,28 +80,28 @@ describe('ShareLinkBundleListComponent', () => {
|
|||||||
it('loads bundles on init and polls periodically', () => {
|
it('loads bundles on init and polls periodically', () => {
|
||||||
jest.useFakeTimers()
|
jest.useFakeTimers()
|
||||||
const bundles = [sampleBundle({ status: ShareLinkBundleStatus.Ready })]
|
const bundles = [sampleBundle({ status: ShareLinkBundleStatus.Ready })]
|
||||||
service.listAllBundles.mockReset()
|
service.list.mockReset()
|
||||||
service.listAllBundles
|
service.list
|
||||||
.mockReturnValueOnce(of(bundles))
|
.mockReturnValueOnce(of({ count: bundles.length, results: bundles }))
|
||||||
.mockReturnValue(of(bundles))
|
.mockReturnValue(of({ count: bundles.length, results: bundles }))
|
||||||
|
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
|
|
||||||
expect(service.listAllBundles).toHaveBeenCalledTimes(1)
|
expect(service.list).toHaveBeenCalledWith(1, 25, 'created', true)
|
||||||
expect(component.bundles()).toEqual(bundles)
|
expect(component.bundles()).toEqual(bundles)
|
||||||
expect(component.loading()).toBe(false)
|
expect(component.loading()).toBe(false)
|
||||||
expect(component.error()).toBeNull()
|
expect(component.error()).toBeNull()
|
||||||
|
|
||||||
jest.advanceTimersByTime(5000)
|
jest.advanceTimersByTime(5000)
|
||||||
expect(service.listAllBundles).toHaveBeenCalledTimes(2)
|
expect(service.list).toHaveBeenCalledTimes(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('handles errors when loading bundles', () => {
|
it('handles errors when loading bundles', () => {
|
||||||
jest.useFakeTimers()
|
jest.useFakeTimers()
|
||||||
service.listAllBundles.mockReset()
|
service.list.mockReset()
|
||||||
service.listAllBundles
|
service.list
|
||||||
.mockReturnValueOnce(throwError(() => new Error('load fail')))
|
.mockReturnValueOnce(throwError(() => new Error('load fail')))
|
||||||
.mockReturnValue(of([]))
|
.mockReturnValue(of({ count: 0, results: [] }))
|
||||||
|
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
|
|
||||||
@@ -110,7 +110,15 @@ describe('ShareLinkBundleListComponent', () => {
|
|||||||
expect(component.loading()).toBe(false)
|
expect(component.loading()).toBe(false)
|
||||||
|
|
||||||
jest.advanceTimersByTime(5000)
|
jest.advanceTimersByTime(5000)
|
||||||
expect(service.listAllBundles).toHaveBeenCalledTimes(2)
|
expect(service.list).toHaveBeenCalledTimes(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('loads another page', () => {
|
||||||
|
fixture.detectChanges()
|
||||||
|
|
||||||
|
component.setPage(2)
|
||||||
|
|
||||||
|
expect(service.list).toHaveBeenLastCalledWith(2, 25, 'created', true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('copies bundle links when ready', () => {
|
it('copies bundle links when ready', () => {
|
||||||
@@ -150,7 +158,7 @@ describe('ShareLinkBundleListComponent', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('deletes bundles and refreshes list', () => {
|
it('deletes bundles and refreshes list', () => {
|
||||||
service.listAllBundles.mockReturnValue(of([]))
|
service.list.mockReturnValue(of({ count: 0, results: [] }))
|
||||||
service.delete.mockReturnValue(of(true))
|
service.delete.mockReturnValue(of(true))
|
||||||
|
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
@@ -161,12 +169,12 @@ describe('ShareLinkBundleListComponent', () => {
|
|||||||
expect(toastService.showInfo).toHaveBeenCalledWith(
|
expect(toastService.showInfo).toHaveBeenCalledWith(
|
||||||
expect.stringContaining('deleted.')
|
expect.stringContaining('deleted.')
|
||||||
)
|
)
|
||||||
expect(service.listAllBundles).toHaveBeenCalledTimes(2)
|
expect(service.list).toHaveBeenCalledTimes(2)
|
||||||
expect(component.loading()).toBe(false)
|
expect(component.loading()).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('handles delete errors gracefully', () => {
|
it('handles delete errors gracefully', () => {
|
||||||
service.listAllBundles.mockReturnValue(of([]))
|
service.list.mockReturnValue(of({ count: 0, results: [] }))
|
||||||
service.delete.mockReturnValue(throwError(() => new Error('delete fail')))
|
service.delete.mockReturnValue(throwError(() => new Error('delete fail')))
|
||||||
|
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
@@ -178,7 +186,7 @@ describe('ShareLinkBundleListComponent', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('retries bundle build and replaces existing entry', () => {
|
it('retries bundle build and replaces existing entry', () => {
|
||||||
service.listAllBundles.mockReturnValue(of([]))
|
service.list.mockReturnValue(of({ count: 0, results: [] }))
|
||||||
const updated = sampleBundle({ status: ShareLinkBundleStatus.Ready })
|
const updated = sampleBundle({ status: ShareLinkBundleStatus.Ready })
|
||||||
service.rebuildBundle.mockReturnValue(of(updated))
|
service.rebuildBundle.mockReturnValue(of(updated))
|
||||||
|
|
||||||
@@ -193,7 +201,7 @@ describe('ShareLinkBundleListComponent', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('adds new bundle when retry returns unknown entry', () => {
|
it('adds new bundle when retry returns unknown entry', () => {
|
||||||
service.listAllBundles.mockReturnValue(of([]))
|
service.list.mockReturnValue(of({ count: 0, results: [] }))
|
||||||
service.rebuildBundle.mockReturnValue(
|
service.rebuildBundle.mockReturnValue(
|
||||||
of(sampleBundle({ id: 99, slug: 'new-slug' }))
|
of(sampleBundle({ id: 99, slug: 'new-slug' }))
|
||||||
)
|
)
|
||||||
@@ -207,7 +215,7 @@ describe('ShareLinkBundleListComponent', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('handles retry errors', () => {
|
it('handles retry errors', () => {
|
||||||
service.listAllBundles.mockReturnValue(of([]))
|
service.list.mockReturnValue(of({ count: 0, results: [] }))
|
||||||
service.rebuildBundle.mockReturnValue(throwError(() => new Error('fail')))
|
service.rebuildBundle.mockReturnValue(throwError(() => new Error('fail')))
|
||||||
|
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
@@ -218,7 +226,7 @@ describe('ShareLinkBundleListComponent', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('maps status and file version helpers', () => {
|
it('maps status and file version helpers', () => {
|
||||||
service.listAllBundles.mockReturnValue(of([]))
|
service.list.mockReturnValue(of({ count: 0, results: [] }))
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
|
|
||||||
expect(component.statusLabel(ShareLinkBundleStatus.Processing)).toContain(
|
expect(component.statusLabel(ShareLinkBundleStatus.Processing)).toContain(
|
||||||
|
|||||||
+33
-15
@@ -1,7 +1,10 @@
|
|||||||
import { Clipboard } from '@angular/cdk/clipboard'
|
import { Clipboard } from '@angular/cdk/clipboard'
|
||||||
import { CommonModule } from '@angular/common'
|
import { CommonModule } from '@angular/common'
|
||||||
import { Component, OnDestroy, OnInit, inject, signal } from '@angular/core'
|
import { Component, OnDestroy, OnInit, inject, signal } from '@angular/core'
|
||||||
import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
|
import {
|
||||||
|
NgbPaginationModule,
|
||||||
|
NgbPopoverModule,
|
||||||
|
} from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
import { Subject, catchError, of, switchMap, takeUntil, timer } from 'rxjs'
|
import { Subject, catchError, of, switchMap, takeUntil, timer } from 'rxjs'
|
||||||
import { FileVersion } from 'src/app/data/share-link'
|
import { FileVersion } from 'src/app/data/share-link'
|
||||||
@@ -25,6 +28,7 @@ import { LoadingComponentWithPermissions } from 'src/app/components/loading-comp
|
|||||||
imports: [
|
imports: [
|
||||||
ConfirmButtonComponent,
|
ConfirmButtonComponent,
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
NgbPaginationModule,
|
||||||
NgbPopoverModule,
|
NgbPopoverModule,
|
||||||
NgxBootstrapIconsModule,
|
NgxBootstrapIconsModule,
|
||||||
FileSizePipe,
|
FileSizePipe,
|
||||||
@@ -41,6 +45,9 @@ export class ShareLinkBundleListComponent
|
|||||||
readonly bundles = signal<ShareLinkBundleSummary[]>([])
|
readonly bundles = signal<ShareLinkBundleSummary[]>([])
|
||||||
readonly error = signal<string | null>(null)
|
readonly error = signal<string | null>(null)
|
||||||
readonly copiedSlug = signal<string | null>(null)
|
readonly copiedSlug = signal<string | null>(null)
|
||||||
|
readonly total = signal(0)
|
||||||
|
readonly page = signal(1)
|
||||||
|
readonly pageSize = 25
|
||||||
|
|
||||||
readonly statuses = ShareLinkBundleStatus
|
readonly statuses = ShareLinkBundleStatus
|
||||||
readonly fileVersions = FileVersion
|
readonly fileVersions = FileVersion
|
||||||
@@ -55,25 +62,28 @@ export class ShareLinkBundleListComponent
|
|||||||
this.loading.set(true)
|
this.loading.set(true)
|
||||||
}
|
}
|
||||||
this.error.set(null)
|
this.error.set(null)
|
||||||
return this.shareLinkBundleService.listAllBundles().pipe(
|
return this.shareLinkBundleService
|
||||||
catchError((error) => {
|
.list(this.page(), this.pageSize, 'created', true)
|
||||||
if (!silent) {
|
.pipe(
|
||||||
this.loading.set(false)
|
catchError((error) => {
|
||||||
}
|
if (!silent) {
|
||||||
this.error.set($localize`Failed to load share link bundles.`)
|
this.loading.set(false)
|
||||||
this.toastService.showError(
|
}
|
||||||
$localize`Error retrieving share link bundles.`,
|
this.error.set($localize`Failed to load share link bundles.`)
|
||||||
error
|
this.toastService.showError(
|
||||||
)
|
$localize`Error retrieving share link bundles.`,
|
||||||
return of(null)
|
error
|
||||||
})
|
)
|
||||||
)
|
return of(null)
|
||||||
|
})
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
takeUntil(this.unsubscribeNotifier)
|
takeUntil(this.unsubscribeNotifier)
|
||||||
)
|
)
|
||||||
.subscribe((results) => {
|
.subscribe((results) => {
|
||||||
if (results) {
|
if (results) {
|
||||||
this.bundles.set(results)
|
this.bundles.set(results.results)
|
||||||
|
this.total.set(results.count)
|
||||||
this.copiedSlug.set(null)
|
this.copiedSlug.set(null)
|
||||||
}
|
}
|
||||||
this.loading.set(false)
|
this.loading.set(false)
|
||||||
@@ -96,6 +106,11 @@ export class ShareLinkBundleListComponent
|
|||||||
}`
|
}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPage(page: number): void {
|
||||||
|
this.page.set(page)
|
||||||
|
this.triggerRefresh(false)
|
||||||
|
}
|
||||||
|
|
||||||
copy(bundle: ShareLinkBundleSummary): void {
|
copy(bundle: ShareLinkBundleSummary): void {
|
||||||
if (bundle.status !== ShareLinkBundleStatus.Ready) {
|
if (bundle.status !== ShareLinkBundleStatus.Ready) {
|
||||||
return
|
return
|
||||||
@@ -114,6 +129,9 @@ export class ShareLinkBundleListComponent
|
|||||||
this.loading.set(true)
|
this.loading.set(true)
|
||||||
this.shareLinkBundleService.delete(bundle).subscribe({
|
this.shareLinkBundleService.delete(bundle).subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
|
if (this.bundles().length === 1 && this.page() > 1) {
|
||||||
|
this.page.update((page) => page - 1)
|
||||||
|
}
|
||||||
this.toastService.showInfo($localize`Share link bundle deleted.`)
|
this.toastService.showInfo($localize`Share link bundle deleted.`)
|
||||||
this.triggerRefresh(false)
|
this.triggerRefresh(false)
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in new issue
Block a user