Add column sorting

This commit is contained in:
shamoon
2026-09-08 09:52:46 -07:00
parent 279e075f58
commit 2d25400a23
8 changed files with 79 additions and 9 deletions
@@ -13,10 +13,10 @@
<table class="table table-sm align-middle mb-0 bg-body">
<thead>
<tr>
<th scope="col" i18n>Created</th>
<th scope="col" i18n>Status</th>
<th scope="col" class="fw-normal" pngxSortable="created" [currentSortField]="sortField()" [currentSortReverse]="sortReverse()" (sort)="onSort($event)" i18n>Created</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>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>File version</th>
<th scope="col" class="text-end" i18n>Actions</th>
@@ -123,6 +123,16 @@ describe('ShareLinkBundleListComponent', () => {
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', () => {
service.list.mockReturnValue(
of({
@@ -16,6 +16,10 @@ import {
ShareLinkBundleSummary,
} from 'src/app/data/share-link-bundle'
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 { ShareLinkBundleService } from 'src/app/services/rest/share-link-bundle.service'
import { SettingsService } from 'src/app/services/settings.service'
@@ -35,6 +39,7 @@ import { LoadingComponentWithPermissions } from 'src/app/components/loading-comp
NgbPaginationModule,
NgbPopoverModule,
NgxBootstrapIconsModule,
SortableDirective,
FileSizePipe,
],
})
@@ -52,6 +57,8 @@ export class ShareLinkBundleListComponent
readonly copiedSlug = signal<string | null>(null)
readonly total = signal(0)
readonly page = signal(1)
readonly sortField = signal('created')
readonly sortReverse = signal(true)
readonly statuses = ShareLinkBundleStatus
readonly fileVersions = FileVersion
@@ -90,7 +97,12 @@ export class ShareLinkBundleListComponent
}
this.error.set(null)
return this.shareLinkBundleService
.list(this.page(), this.pageSize, 'created', true)
.list(
this.page(),
this.pageSize,
this.sortField(),
this.sortReverse()
)
.pipe(
catchError((error) => {
if (!silent) {
@@ -138,6 +150,13 @@ export class ShareLinkBundleListComponent
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 {
if (bundle.status !== ShareLinkBundleStatus.Ready) {
return
@@ -12,9 +12,9 @@
<table class="table table-sm align-middle mb-0 bg-body">
<thead>
<tr>
<th scope="col" i18n>Document</th>
<th scope="col" i18n>Created</th>
<th scope="col" i18n>Expires</th>
<th scope="col" class="fw-normal" pngxSortable="document__title" [currentSortField]="sortField()" [currentSortReverse]="sortReverse()" (sort)="onSort($event)" i18n>Document</th>
<th scope="col" class="fw-normal" pngxSortable="created" [currentSortField]="sortField()" [currentSortReverse]="sortReverse()" (sort)="onSort($event)" i18n>Created</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" class="text-end" i18n>Actions</th>
</tr>
@@ -75,6 +75,16 @@ describe('ShareLinkListComponent', () => {
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', () => {
service.list.mockReturnValue(
of({
@@ -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 { SETTINGS_KEYS } from 'src/app/data/ui-settings'
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 {
PermissionAction,
@@ -34,6 +38,7 @@ import { environment } from 'src/environments/environment'
NgbPaginationModule,
NgxBootstrapIconsModule,
RouterModule,
SortableDirective,
],
})
export class ShareLinkListComponent
@@ -48,6 +53,8 @@ export class ShareLinkListComponent
readonly links = signal<ShareLink[]>([])
readonly total = signal(0)
readonly page = signal(1)
readonly sortField = signal('created')
readonly sortReverse = signal(true)
readonly copiedID = signal<number | null>(null)
readonly copiedDocumentID = signal<number | null>(null)
readonly error = signal<string | null>(null)
@@ -85,7 +92,7 @@ export class ShareLinkListComponent
this.loading.set(true)
this.error.set(null)
this.shareLinkService
.list(this.page(), this.pageSize, 'created', true)
.list(this.page(), this.pageSize, this.sortField(), this.sortReverse())
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe({
next: (results) => {
@@ -109,6 +116,13 @@ export class ShareLinkListComponent
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 {
const apiURL = new URL(environment.apiBaseUrl)
return `${apiURL.origin}${apiURL.pathname.replace(/\/api\/$/, '/share/')}${
+17
View File
@@ -3796,6 +3796,23 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
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:
"""
GIVEN:
+1 -1
View File
@@ -4571,7 +4571,7 @@ class ShareLinkViewSet(
PermittedObjectsFilter,
)
filterset_class = ShareLinkFilterSet
ordering_fields = ("created", "expiration", "document")
ordering_fields = ("created", "expiration", "document__title")
@extend_schema_view(