mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-09-11 20:28:01 +00:00
Add column sorting
This commit is contained in:
+3
-3
@@ -13,10 +13,10 @@
|
|||||||
<table class="table table-sm align-middle mb-0 bg-body">
|
<table class="table table-sm align-middle mb-0 bg-body">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" i18n>Created</th>
|
<th scope="col" class="fw-normal" pngxSortable="created" [currentSortField]="sortField()" [currentSortReverse]="sortReverse()" (sort)="onSort($event)" i18n>Created</th>
|
||||||
<th scope="col" i18n>Status</th>
|
<th scope="col" class="fw-normal" pngxSortable="status" [currentSortField]="sortField()" [currentSortReverse]="sortReverse()" (sort)="onSort($event)" i18n>Status</th>
|
||||||
<th scope="col" i18n>Size</th>
|
<th scope="col" i18n>Size</th>
|
||||||
<th scope="col" i18n>Expires</th>
|
<th scope="col" class="fw-normal" pngxSortable="expiration" [currentSortField]="sortField()" [currentSortReverse]="sortReverse()" (sort)="onSort($event)" i18n>Expires</th>
|
||||||
<th scope="col" i18n>Documents</th>
|
<th scope="col" i18n>Documents</th>
|
||||||
<th scope="col" i18n>File version</th>
|
<th scope="col" i18n>File version</th>
|
||||||
<th scope="col" class="text-end" i18n>Actions</th>
|
<th scope="col" class="text-end" i18n>Actions</th>
|
||||||
|
|||||||
+10
@@ -123,6 +123,16 @@ describe('ShareLinkBundleListComponent', () => {
|
|||||||
expect(service.list).toHaveBeenLastCalledWith(2, 25, 'created', true)
|
expect(service.list).toHaveBeenLastCalledWith(2, 25, 'created', true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('sorts bundles and returns to the first page', () => {
|
||||||
|
fixture.detectChanges()
|
||||||
|
component.page.set(2)
|
||||||
|
|
||||||
|
component.onSort({ column: 'status', reverse: false })
|
||||||
|
|
||||||
|
expect(component.page()).toBe(1)
|
||||||
|
expect(service.list).toHaveBeenLastCalledWith(1, 25, 'status', false)
|
||||||
|
})
|
||||||
|
|
||||||
it('marks expired share link bundles', () => {
|
it('marks expired share link bundles', () => {
|
||||||
service.list.mockReturnValue(
|
service.list.mockReturnValue(
|
||||||
of({
|
of({
|
||||||
|
|||||||
+20
-1
@@ -16,6 +16,10 @@ import {
|
|||||||
ShareLinkBundleSummary,
|
ShareLinkBundleSummary,
|
||||||
} from 'src/app/data/share-link-bundle'
|
} from 'src/app/data/share-link-bundle'
|
||||||
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||||
|
import {
|
||||||
|
SortEvent,
|
||||||
|
SortableDirective,
|
||||||
|
} from 'src/app/directives/sortable.directive'
|
||||||
import { FileSizePipe } from 'src/app/pipes/file-size.pipe'
|
import { FileSizePipe } from 'src/app/pipes/file-size.pipe'
|
||||||
import { ShareLinkBundleService } from 'src/app/services/rest/share-link-bundle.service'
|
import { ShareLinkBundleService } from 'src/app/services/rest/share-link-bundle.service'
|
||||||
import { SettingsService } from 'src/app/services/settings.service'
|
import { SettingsService } from 'src/app/services/settings.service'
|
||||||
@@ -35,6 +39,7 @@ import { LoadingComponentWithPermissions } from 'src/app/components/loading-comp
|
|||||||
NgbPaginationModule,
|
NgbPaginationModule,
|
||||||
NgbPopoverModule,
|
NgbPopoverModule,
|
||||||
NgxBootstrapIconsModule,
|
NgxBootstrapIconsModule,
|
||||||
|
SortableDirective,
|
||||||
FileSizePipe,
|
FileSizePipe,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@@ -52,6 +57,8 @@ export class ShareLinkBundleListComponent
|
|||||||
readonly copiedSlug = signal<string | null>(null)
|
readonly copiedSlug = signal<string | null>(null)
|
||||||
readonly total = signal(0)
|
readonly total = signal(0)
|
||||||
readonly page = signal(1)
|
readonly page = signal(1)
|
||||||
|
readonly sortField = signal('created')
|
||||||
|
readonly sortReverse = signal(true)
|
||||||
|
|
||||||
readonly statuses = ShareLinkBundleStatus
|
readonly statuses = ShareLinkBundleStatus
|
||||||
readonly fileVersions = FileVersion
|
readonly fileVersions = FileVersion
|
||||||
@@ -90,7 +97,12 @@ export class ShareLinkBundleListComponent
|
|||||||
}
|
}
|
||||||
this.error.set(null)
|
this.error.set(null)
|
||||||
return this.shareLinkBundleService
|
return this.shareLinkBundleService
|
||||||
.list(this.page(), this.pageSize, 'created', true)
|
.list(
|
||||||
|
this.page(),
|
||||||
|
this.pageSize,
|
||||||
|
this.sortField(),
|
||||||
|
this.sortReverse()
|
||||||
|
)
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError((error) => {
|
catchError((error) => {
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
@@ -138,6 +150,13 @@ export class ShareLinkBundleListComponent
|
|||||||
this.triggerRefresh(false)
|
this.triggerRefresh(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onSort(event: SortEvent): void {
|
||||||
|
this.sortField.set(event.column || 'created')
|
||||||
|
this.sortReverse.set(event.column ? event.reverse : true)
|
||||||
|
this.page.set(1)
|
||||||
|
this.triggerRefresh(false)
|
||||||
|
}
|
||||||
|
|
||||||
copy(bundle: ShareLinkBundleSummary): void {
|
copy(bundle: ShareLinkBundleSummary): void {
|
||||||
if (bundle.status !== ShareLinkBundleStatus.Ready) {
|
if (bundle.status !== ShareLinkBundleStatus.Ready) {
|
||||||
return
|
return
|
||||||
|
|||||||
+3
-3
@@ -12,9 +12,9 @@
|
|||||||
<table class="table table-sm align-middle mb-0 bg-body">
|
<table class="table table-sm align-middle mb-0 bg-body">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" i18n>Document</th>
|
<th scope="col" class="fw-normal" pngxSortable="document__title" [currentSortField]="sortField()" [currentSortReverse]="sortReverse()" (sort)="onSort($event)" i18n>Document</th>
|
||||||
<th scope="col" i18n>Created</th>
|
<th scope="col" class="fw-normal" pngxSortable="created" [currentSortField]="sortField()" [currentSortReverse]="sortReverse()" (sort)="onSort($event)" i18n>Created</th>
|
||||||
<th scope="col" i18n>Expires</th>
|
<th scope="col" class="fw-normal" pngxSortable="expiration" [currentSortField]="sortField()" [currentSortReverse]="sortReverse()" (sort)="onSort($event)" i18n>Expires</th>
|
||||||
<th scope="col" i18n>File version</th>
|
<th scope="col" i18n>File version</th>
|
||||||
<th scope="col" class="text-end" i18n>Actions</th>
|
<th scope="col" class="text-end" i18n>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
+10
@@ -75,6 +75,16 @@ describe('ShareLinkListComponent', () => {
|
|||||||
expect(service.list).toHaveBeenLastCalledWith(2, 25, 'created', true)
|
expect(service.list).toHaveBeenLastCalledWith(2, 25, 'created', true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('sorts links and returns to the first page', () => {
|
||||||
|
fixture.detectChanges()
|
||||||
|
component.page.set(2)
|
||||||
|
|
||||||
|
component.onSort({ column: 'expiration', reverse: false })
|
||||||
|
|
||||||
|
expect(component.page()).toBe(1)
|
||||||
|
expect(service.list).toHaveBeenLastCalledWith(1, 25, 'expiration', false)
|
||||||
|
})
|
||||||
|
|
||||||
it('marks expired share links', () => {
|
it('marks expired share links', () => {
|
||||||
service.list.mockReturnValue(
|
service.list.mockReturnValue(
|
||||||
of({
|
of({
|
||||||
|
|||||||
+15
-1
@@ -12,6 +12,10 @@ import { FileVersion, ShareLink } from 'src/app/data/share-link'
|
|||||||
import { SHARE_LINK_BUNDLE_FILE_VERSION_LABELS } from 'src/app/data/share-link-bundle'
|
import { SHARE_LINK_BUNDLE_FILE_VERSION_LABELS } from 'src/app/data/share-link-bundle'
|
||||||
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
||||||
|
import {
|
||||||
|
SortEvent,
|
||||||
|
SortableDirective,
|
||||||
|
} from 'src/app/directives/sortable.directive'
|
||||||
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
|
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
|
||||||
import {
|
import {
|
||||||
PermissionAction,
|
PermissionAction,
|
||||||
@@ -34,6 +38,7 @@ import { environment } from 'src/environments/environment'
|
|||||||
NgbPaginationModule,
|
NgbPaginationModule,
|
||||||
NgxBootstrapIconsModule,
|
NgxBootstrapIconsModule,
|
||||||
RouterModule,
|
RouterModule,
|
||||||
|
SortableDirective,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ShareLinkListComponent
|
export class ShareLinkListComponent
|
||||||
@@ -48,6 +53,8 @@ export class ShareLinkListComponent
|
|||||||
readonly links = signal<ShareLink[]>([])
|
readonly links = signal<ShareLink[]>([])
|
||||||
readonly total = signal(0)
|
readonly total = signal(0)
|
||||||
readonly page = signal(1)
|
readonly page = signal(1)
|
||||||
|
readonly sortField = signal('created')
|
||||||
|
readonly sortReverse = signal(true)
|
||||||
readonly copiedID = signal<number | null>(null)
|
readonly copiedID = signal<number | null>(null)
|
||||||
readonly copiedDocumentID = signal<number | null>(null)
|
readonly copiedDocumentID = signal<number | null>(null)
|
||||||
readonly error = signal<string | null>(null)
|
readonly error = signal<string | null>(null)
|
||||||
@@ -85,7 +92,7 @@ export class ShareLinkListComponent
|
|||||||
this.loading.set(true)
|
this.loading.set(true)
|
||||||
this.error.set(null)
|
this.error.set(null)
|
||||||
this.shareLinkService
|
this.shareLinkService
|
||||||
.list(this.page(), this.pageSize, 'created', true)
|
.list(this.page(), this.pageSize, this.sortField(), this.sortReverse())
|
||||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: (results) => {
|
next: (results) => {
|
||||||
@@ -109,6 +116,13 @@ export class ShareLinkListComponent
|
|||||||
this.reload()
|
this.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onSort(event: SortEvent): void {
|
||||||
|
this.sortField.set(event.column || 'created')
|
||||||
|
this.sortReverse.set(event.column ? event.reverse : true)
|
||||||
|
this.page.set(1)
|
||||||
|
this.reload()
|
||||||
|
}
|
||||||
|
|
||||||
getShareUrl(link: ShareLink): string {
|
getShareUrl(link: ShareLink): string {
|
||||||
const apiURL = new URL(environment.apiBaseUrl)
|
const apiURL = new URL(environment.apiBaseUrl)
|
||||||
return `${apiURL.origin}${apiURL.pathname.replace(/\/api\/$/, '/share/')}${
|
return `${apiURL.origin}${apiURL.pathname.replace(/\/api\/$/, '/share/')}${
|
||||||
|
|||||||
@@ -3796,6 +3796,23 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
def test_order_share_links_by_document_title(self) -> None:
|
||||||
|
document_zulu = Document.objects.create(title="Zulu")
|
||||||
|
document_alpha = Document.objects.create(title="Alpha")
|
||||||
|
ShareLink.objects.create(document=document_zulu, slug="zulu-link")
|
||||||
|
ShareLink.objects.create(document=document_alpha, slug="alpha-link")
|
||||||
|
|
||||||
|
response = self.client.get(
|
||||||
|
"/api/share_links/?ordering=document__title",
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(
|
||||||
|
[link["document_title"] for link in response.data["results"]],
|
||||||
|
["Alpha", "Zulu"],
|
||||||
|
)
|
||||||
|
|
||||||
def test_share_links_permissions_aware(self) -> None:
|
def test_share_links_permissions_aware(self) -> None:
|
||||||
"""
|
"""
|
||||||
GIVEN:
|
GIVEN:
|
||||||
|
|||||||
@@ -4571,7 +4571,7 @@ class ShareLinkViewSet(
|
|||||||
PermittedObjectsFilter,
|
PermittedObjectsFilter,
|
||||||
)
|
)
|
||||||
filterset_class = ShareLinkFilterSet
|
filterset_class = ShareLinkFilterSet
|
||||||
ordering_fields = ("created", "expiration", "document")
|
ordering_fields = ("created", "expiration", "document__title")
|
||||||
|
|
||||||
|
|
||||||
@extend_schema_view(
|
@extend_schema_view(
|
||||||
|
|||||||
Reference in New Issue
Block a user