diff --git a/src-ui/src/app/app.module.ts b/src-ui/src/app/app.module.ts
index e10bdbd0c..584b7bc7a 100644
--- a/src-ui/src/app/app.module.ts
+++ b/src-ui/src/app/app.module.ts
@@ -40,6 +40,7 @@ import { SaveViewConfigDialogComponent } from './components/document-list/save-v
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
import { DateTimeComponent } from './components/common/input/date-time/date-time.component';
import { TagsComponent } from './components/common/input/tags/tags.component';
+import { SortableDirective } from './directives/sortable.directive';
@NgModule({
declarations: [
@@ -73,7 +74,8 @@ import { TagsComponent } from './components/common/input/tags/tags.component';
CheckComponent,
SaveViewConfigDialogComponent,
DateTimeComponent,
- TagsComponent
+ TagsComponent,
+ SortableDirective
],
imports: [
BrowserModule,
diff --git a/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.html b/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.html
index fc01471d7..a790a18b3 100644
--- a/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.html
+++ b/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.html
@@ -9,10 +9,10 @@
- Name |
- Matching |
- Document count |
- Last correspondence |
+ Name |
+ Matching |
+ Document count |
+ Last correspondence |
Actions |
diff --git a/src-ui/src/app/components/manage/document-type-list/document-type-list.component.html b/src-ui/src/app/components/manage/document-type-list/document-type-list.component.html
index 94e7aa3b7..a07f6c7e4 100644
--- a/src-ui/src/app/components/manage/document-type-list/document-type-list.component.html
+++ b/src-ui/src/app/components/manage/document-type-list/document-type-list.component.html
@@ -10,9 +10,9 @@
- Name |
- Matching |
- Document count |
+ Name |
+ Matching |
+ Document count |
Actions |
diff --git a/src-ui/src/app/components/manage/generic-list/generic-list.component.ts b/src-ui/src/app/components/manage/generic-list/generic-list.component.ts
index 12cf08ea9..d5477d010 100644
--- a/src-ui/src/app/components/manage/generic-list/generic-list.component.ts
+++ b/src-ui/src/app/components/manage/generic-list/generic-list.component.ts
@@ -1,7 +1,8 @@
-import { Directive, OnInit } from '@angular/core';
+import { Directive, OnInit, QueryList, ViewChildren } from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { MatchingModel, MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/matching-model';
import { ObjectWithId } from 'src/app/data/object-with-id';
+import { SortableDirective, SortEvent } from 'src/app/directives/sortable.directive';
import { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperless-service';
import { DeleteDialogComponent } from '../../common/delete-dialog/delete-dialog.component';
@@ -14,12 +15,17 @@ export abstract class GenericListComponent implements On
private editDialogComponent: any) {
}
+ @ViewChildren(SortableDirective) headers: QueryList;
+
public data: T[] = []
public page = 1
public collectionSize = 0
+ public sortField: string
+ public sortDirection: string
+
getMatching(o: MatchingModel) {
if (o.matching_algorithm == MATCH_AUTO) {
return "Automatic"
@@ -30,12 +36,31 @@ export abstract class GenericListComponent implements On
}
}
+ onSort(event: SortEvent) {
+
+ if (event.direction && event.direction.length > 0) {
+ this.sortField = event.column
+ this.sortDirection = event.direction
+ } else {
+ this.sortField = null
+ this.sortDirection = null
+ }
+
+ this.headers.forEach(header => {
+ if (header.sortable !== this.sortField) {
+ header.direction = '';
+ }
+ });
+
+ this.reloadData()
+ }
+
ngOnInit(): void {
this.reloadData()
}
reloadData() {
- this.service.list(this.page).subscribe(c => {
+ this.service.list(this.page, null, this.sortField, this.sortDirection).subscribe(c => {
this.data = c.results
this.collectionSize = c.count
});
diff --git a/src-ui/src/app/components/manage/tag-list/tag-list.component.html b/src-ui/src/app/components/manage/tag-list/tag-list.component.html
index 76ae9fb93..d06748cec 100644
--- a/src-ui/src/app/components/manage/tag-list/tag-list.component.html
+++ b/src-ui/src/app/components/manage/tag-list/tag-list.component.html
@@ -9,10 +9,10 @@
- Name |
+ Name |
Colour |
- Matching |
- Document count |
+ Matching |
+ Document count |
Actions |
diff --git a/src-ui/src/app/directives/sortable.directive.spec.ts b/src-ui/src/app/directives/sortable.directive.spec.ts
new file mode 100644
index 000000000..f77b499de
--- /dev/null
+++ b/src-ui/src/app/directives/sortable.directive.spec.ts
@@ -0,0 +1,8 @@
+import { SortableDirective } from './sortable.directive';
+
+describe('SortableDirective', () => {
+ it('should create an instance', () => {
+ const directive = new SortableDirective();
+ expect(directive).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/directives/sortable.directive.ts b/src-ui/src/app/directives/sortable.directive.ts
new file mode 100644
index 000000000..11c474dbb
--- /dev/null
+++ b/src-ui/src/app/directives/sortable.directive.ts
@@ -0,0 +1,30 @@
+import { Directive, EventEmitter, Input, Output } from '@angular/core';
+
+export interface SortEvent {
+ column: string;
+ direction: string;
+}
+
+const rotate: {[key: string]: string} = { 'asc': 'des', 'des': '', '': 'asc' };
+
+@Directive({
+ selector: 'th[sortable]',
+ host: {
+ '[class.asc]': 'direction === "asc"',
+ '[class.des]': 'direction === "des"',
+ '(click)': 'rotate()'
+ }
+})
+export class SortableDirective {
+
+ constructor() { }
+
+ @Input() sortable: string = '';
+ @Input() direction: string = '';
+ @Output() sort = new EventEmitter();
+
+ rotate() {
+ this.direction = rotate[this.direction];
+ this.sort.emit({column: this.sortable, direction: this.direction});
+ }
+}
diff --git a/src-ui/src/app/services/rest/abstract-paperless-service.ts b/src-ui/src/app/services/rest/abstract-paperless-service.ts
index cdf157aaa..16064c702 100644
--- a/src-ui/src/app/services/rest/abstract-paperless-service.ts
+++ b/src-ui/src/app/services/rest/abstract-paperless-service.ts
@@ -21,7 +21,17 @@ export abstract class AbstractPaperlessService {
return url
}
- list(page?: number, pageSize?: number, ordering?: string, extraParams?): Observable> {
+ private getOrderingQueryParam(sortField: string, sortDirection: string) {
+ if (sortField && sortDirection) {
+ return (sortDirection == 'des' ? '-' : '') + sortField
+ } else if (sortField) {
+ return sortField
+ } else {
+ return null
+ }
+ }
+
+ list(page?: number, pageSize?: number, sortField?: string, sortDirection?: string, extraParams?): Observable> {
let httpParams = new HttpParams()
if (page) {
httpParams = httpParams.set('page', page.toString())
@@ -29,6 +39,7 @@ export abstract class AbstractPaperlessService {
if (pageSize) {
httpParams = httpParams.set('page_size', pageSize.toString())
}
+ let ordering = this.getOrderingQueryParam(sortField, sortDirection)
if (ordering) {
httpParams = httpParams.set('ordering', ordering)
}
diff --git a/src-ui/src/app/services/rest/document.service.ts b/src-ui/src/app/services/rest/document.service.ts
index bc1afb419..88a52ee04 100644
--- a/src-ui/src/app/services/rest/document.service.ts
+++ b/src-ui/src/app/services/rest/document.service.ts
@@ -47,16 +47,8 @@ export class DocumentService extends AbstractPaperlessService
}
}
- private getOrderingQueryParam(sortField: string, sortDirection: string) {
- if (DOCUMENT_SORT_FIELDS.find(f => f.field == sortField)) {
- return (sortDirection == SORT_DIRECTION_DESCENDING ? '-' : '') + sortField
- } else {
- return null
- }
- }
-
list(page?: number, pageSize?: number, sortField?: string, sortDirection?: string, filterRules?: FilterRule[]): Observable> {
- return super.list(page, pageSize, this.getOrderingQueryParam(sortField, sortDirection), this.filterRulesToQueryParams(filterRules))
+ return super.list(page, pageSize, sortField, sortDirection, this.filterRulesToQueryParams(filterRules))
}
getPreviewUrl(id: number): string {
diff --git a/src-ui/src/styles.css b/src-ui/src/styles.css
index 0f16a68f0..c7849912e 100644
--- a/src-ui/src/styles.css
+++ b/src-ui/src/styles.css
@@ -28,4 +28,34 @@ body {
.form-control-dark:focus {
border-color: transparent;
box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
+}
+
+
+.asc {
+ background-color: #f8f9fa!important;
+}
+
+.asc:after {
+ content: '';
+ transform: rotate(180deg);
+ background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAmxJREFUeAHtmksrRVEUx72fH8CIGQNJkpGUUmakDEiZSJRIZsRQmCkTJRmZmJgQE0kpX0D5DJKJgff7v+ru2u3O3vvc67TOvsdatdrnnP1Y///v7HvvubdbUiIhBISAEBACQkAICAEhIAQ4CXSh2DnyDfmCPEG2Iv9F9MPlM/LHyAecdyMzHYNwR3fdNK/OH9HXl1UCozD24TCvILxizEDWIEzA0FcM8woCgRrJCoS5PIwrANQSMAJX1LEI9bqpQo4JYNFFKRSvIgsxHDVnqZgIkPnNBM0rIGtYk9YOOsqgbgepRCfdbmFtqhFkVEDVPjJp0+Z6e6hRHhqBKgg6ZDCvYBygVmUoEGoh5JTRvIJwhJo1aUOoh4CLPMyvxxi7EWOMgnCGsXXI1GIXlZUYX7ucU+kbR8NW8lh3O7cue0Pk32MKndfUxQFAwxdirk3fHappAnc0oqDPzDfGTBrCfHP04dM4oTV8cxr0SVzH9FF07xD3ib6xCDE+M+aUcVygtWzzbtGX2rPBrEUYfecfQkaFzYi6HjVnGBdtL7epqAlc1+jRdAap74RrnPc4BCijttY2tRcdN0g17w7HqZrXhdJTYAuS3hd8z+vKgK3V1zWPae0mZDMykadBn1hTQBLnZNwVrJpSe/NwEeDsEwCctEOsJTsgxLvCqUl2ACftEGvJDgjxrnBqkh3ASTvEWrIDQrwrnJpkB3DSDrGW7IAQ7wqnJtkBnLRztejXXVu4+mxz/nQ9jR1w5VB86ejLTFcnnDwhzV+F6T+CHZlx6THSjn76eyyBIOPHyDakhBAQAkJACAgBISAEhIAQYCLwC8JxpAmsEGt6AAAAAElFTkSuQmCC") no-repeat;
+ height: 1rem;
+ width: 1rem;
+ display: block;
+ background-size: 1rem;
+ float: right;
+}
+
+.des {
+ background-color: #f8f9fa!important;
+}
+
+.des:after {
+ content: '';
+ background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAmxJREFUeAHtmksrRVEUx72fH8CIGQNJkpGUUmakDEiZSJRIZsRQmCkTJRmZmJgQE0kpX0D5DJKJgff7v+ru2u3O3vvc67TOvsdatdrnnP1Y///v7HvvubdbUiIhBISAEBACQkAICAEhIAQ4CXSh2DnyDfmCPEG2Iv9F9MPlM/LHyAecdyMzHYNwR3fdNK/OH9HXl1UCozD24TCvILxizEDWIEzA0FcM8woCgRrJCoS5PIwrANQSMAJX1LEI9bqpQo4JYNFFKRSvIgsxHDVnqZgIkPnNBM0rIGtYk9YOOsqgbgepRCfdbmFtqhFkVEDVPjJp0+Z6e6hRHhqBKgg6ZDCvYBygVmUoEGoh5JTRvIJwhJo1aUOoh4CLPMyvxxi7EWOMgnCGsXXI1GIXlZUYX7ucU+kbR8NW8lh3O7cue0Pk32MKndfUxQFAwxdirk3fHappAnc0oqDPzDfGTBrCfHP04dM4oTV8cxr0SVzH9FF07xD3ib6xCDE+M+aUcVygtWzzbtGX2rPBrEUYfecfQkaFzYi6HjVnGBdtL7epqAlc1+jRdAap74RrnPc4BCijttY2tRcdN0g17w7HqZrXhdJTYAuS3hd8z+vKgK3V1zWPae0mZDMykadBn1hTQBLnZNwVrJpSe/NwEeDsEwCctEOsJTsgxLvCqUl2ACftEGvJDgjxrnBqkh3ASTvEWrIDQrwrnJpkB3DSDrGW7IAQ7wqnJtkBnLRztejXXVu4+mxz/nQ9jR1w5VB86ejLTFcnnDwhzV+F6T+CHZlx6THSjn76eyyBIOPHyDakhBAQAkJACAgBISAEhIAQYCLwC8JxpAmsEGt6AAAAAElFTkSuQmCC") no-repeat;
+ height: 1rem;
+ width: 1rem;
+ display: block;
+ background-size: 1rem;
+ float: right;
}
\ No newline at end of file