Support drag + drop reorder of dashboard saved views
This commit is contained in:
parent
d8314500a0
commit
96eea42a8e
13
src-ui/package-lock.json
generated
13
src-ui/package-lock.json
generated
@ -27,6 +27,7 @@
|
||||
"ngx-clipboard": "^16.0.0",
|
||||
"ngx-color": "^9.0.0",
|
||||
"ngx-cookie-service": "^16.0.1",
|
||||
"ngx-drag-drop": "^16.1.0",
|
||||
"ngx-file-drop": "^16.0.0",
|
||||
"ngx-ui-tour-ng-bootstrap": "^13.0.4",
|
||||
"rxjs": "^7.8.1",
|
||||
@ -14061,6 +14062,18 @@
|
||||
"@angular/core": "^16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ngx-drag-drop": {
|
||||
"version": "16.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ngx-drag-drop/-/ngx-drag-drop-16.1.0.tgz",
|
||||
"integrity": "sha512-y2l9pJGD7OupsIRkCElN/JqTgzjg2V9ZxymKGQR7ZjjcdjaP1wKkiFWIgVEvLNtb8wgm10U+9tkGwLClGaHkQA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^16.0.0",
|
||||
"@angular/core": "^16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ngx-file-drop": {
|
||||
"version": "16.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ngx-file-drop/-/ngx-file-drop-16.0.0.tgz",
|
||||
|
@ -29,6 +29,7 @@
|
||||
"ngx-clipboard": "^16.0.0",
|
||||
"ngx-color": "^9.0.0",
|
||||
"ngx-cookie-service": "^16.0.1",
|
||||
"ngx-drag-drop": "^16.1.0",
|
||||
"ngx-file-drop": "^16.0.0",
|
||||
"ngx-ui-tour-ng-bootstrap": "^13.0.4",
|
||||
"rxjs": "^7.8.1",
|
||||
|
@ -252,10 +252,13 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public get dragDropEnabled(): boolean {
|
||||
return this.permissionsService.currentUserCan(
|
||||
return (
|
||||
this.settings.globalDropzoneEnabled &&
|
||||
this.permissionsService.currentUserCan(
|
||||
PermissionAction.Add,
|
||||
PermissionType.Document
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
public fileOver() {
|
||||
|
@ -99,6 +99,7 @@ import { ConsumptionTemplatesComponent } from './components/manage/consumption-t
|
||||
import { ConsumptionTemplateEditDialogComponent } from './components/common/edit-dialog/consumption-template-edit-dialog/consumption-template-edit-dialog.component'
|
||||
import { MailComponent } from './components/manage/mail/mail.component'
|
||||
import { UsersAndGroupsComponent } from './components/admin/users-groups/users-groups.component'
|
||||
import { DndModule } from 'ngx-drag-drop'
|
||||
|
||||
import localeAf from '@angular/common/locales/af'
|
||||
import localeAr from '@angular/common/locales/ar'
|
||||
@ -254,6 +255,7 @@ function initializeApp(settings: SettingsService) {
|
||||
NgSelectModule,
|
||||
ColorSliderModule,
|
||||
TourNgBootstrapModule,
|
||||
DndModule,
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
|
@ -4,7 +4,11 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-auto col-lg-8 col-xl-9 mb-4">
|
||||
<div class="row row-cols-1 g-4" tourAnchor="tour.dashboard">
|
||||
<div class="row row-cols-1 g-4" tourAnchor="tour.dashboard"
|
||||
dndDropzone
|
||||
dndEffectAllowed="move"
|
||||
(dndDrop)="onDrop($event)"
|
||||
>
|
||||
<div *ngIf="savedViewService.loading" class="col">
|
||||
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
|
||||
<ng-container i18n>Loading...</ng-container>
|
||||
@ -15,12 +19,17 @@
|
||||
</div>
|
||||
|
||||
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.SavedView }">
|
||||
<ng-container *ngFor="let v of savedViewService.dashboardViews; first as isFirst">
|
||||
<div class="col">
|
||||
<pngx-saved-view-widget [savedView]="v"></pngx-saved-view-widget>
|
||||
<div *ngFor="let v of dashboardViews" class="col">
|
||||
<pngx-saved-view-widget
|
||||
[savedView]="v"
|
||||
(dndStart)="onDragStart($event)"
|
||||
(dndMoved)="onDragged(v)"
|
||||
(dndEnd)="onDragEnd($event)"
|
||||
>
|
||||
</pngx-saved-view-widget>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<div class="p-1" dndPlaceholderRef></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto col-lg-4 col-xl-3">
|
||||
|
@ -17,12 +17,56 @@ import { NgxFileDropModule } from 'ngx-file-drop'
|
||||
import { RouterTestingModule } from '@angular/router/testing'
|
||||
import { TourNgBootstrapModule, TourService } from 'ngx-ui-tour-ng-bootstrap'
|
||||
import { LogoComponent } from '../common/logo/logo.component'
|
||||
import { of, throwError } from 'rxjs'
|
||||
import { DndDropEvent, DndModule } from 'ngx-drag-drop'
|
||||
import { ToastService } from 'src/app/services/toast.service'
|
||||
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
|
||||
|
||||
const saved_views = [
|
||||
{
|
||||
name: 'Saved View 0',
|
||||
id: 0,
|
||||
show_on_dashboard: true,
|
||||
show_in_sidebar: true,
|
||||
sort_field: 'name',
|
||||
sort_reverse: true,
|
||||
filter_rules: [],
|
||||
},
|
||||
{
|
||||
name: 'Saved View 1',
|
||||
id: 1,
|
||||
show_on_dashboard: false,
|
||||
show_in_sidebar: false,
|
||||
sort_field: 'name',
|
||||
sort_reverse: true,
|
||||
filter_rules: [],
|
||||
},
|
||||
{
|
||||
name: 'Saved View 2',
|
||||
id: 2,
|
||||
show_on_dashboard: true,
|
||||
show_in_sidebar: false,
|
||||
sort_field: 'name',
|
||||
sort_reverse: true,
|
||||
filter_rules: [],
|
||||
},
|
||||
{
|
||||
name: 'Saved View 3',
|
||||
id: 3,
|
||||
show_on_dashboard: true,
|
||||
show_in_sidebar: false,
|
||||
sort_field: 'name',
|
||||
sort_reverse: true,
|
||||
filter_rules: [],
|
||||
},
|
||||
]
|
||||
|
||||
describe('DashboardComponent', () => {
|
||||
let component: DashboardComponent
|
||||
let fixture: ComponentFixture<DashboardComponent>
|
||||
let settingsService: SettingsService
|
||||
let tourService: TourService
|
||||
let toastService: ToastService
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
@ -47,24 +91,13 @@ describe('DashboardComponent', () => {
|
||||
{
|
||||
provide: SavedViewService,
|
||||
useValue: {
|
||||
dashboardViews: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'saved view 1',
|
||||
show_on_dashboard: true,
|
||||
sort_field: 'added',
|
||||
sort_reverse: true,
|
||||
filter_rules: [],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'saved view 2',
|
||||
show_on_dashboard: true,
|
||||
sort_field: 'created',
|
||||
sort_reverse: true,
|
||||
filter_rules: [],
|
||||
},
|
||||
],
|
||||
listAll: () =>
|
||||
of({
|
||||
all: [saved_views.map((v) => v.id)],
|
||||
count: saved_views.length,
|
||||
results: saved_views,
|
||||
}),
|
||||
dashboardViews: saved_views.filter((v) => v.show_on_dashboard),
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -74,6 +107,7 @@ describe('DashboardComponent', () => {
|
||||
NgxFileDropModule,
|
||||
RouterTestingModule,
|
||||
TourNgBootstrapModule,
|
||||
DndModule,
|
||||
],
|
||||
}).compileComponents()
|
||||
|
||||
@ -82,7 +116,11 @@ describe('DashboardComponent', () => {
|
||||
first_name: 'Foo',
|
||||
last_name: 'Bar',
|
||||
}
|
||||
jest.spyOn(settingsService, 'get').mockImplementation((key) => {
|
||||
if (key === SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER) return [0, 2, 3]
|
||||
})
|
||||
tourService = TestBed.inject(TourService)
|
||||
toastService = TestBed.inject(ToastService)
|
||||
fixture = TestBed.createComponent(DashboardComponent)
|
||||
component = fixture.componentInstance
|
||||
|
||||
@ -100,7 +138,7 @@ describe('DashboardComponent', () => {
|
||||
it('should show dashboard widgets', () => {
|
||||
expect(
|
||||
fixture.debugElement.queryAll(By.directive(SavedViewWidgetComponent))
|
||||
).toHaveLength(2)
|
||||
).toHaveLength(saved_views.filter((v) => v.show_on_dashboard).length)
|
||||
})
|
||||
|
||||
it('should end tour service if still running and welcome widget dismissed', () => {
|
||||
@ -116,4 +154,44 @@ describe('DashboardComponent', () => {
|
||||
component.completeTour()
|
||||
expect(settingsCompleteTourSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should disable global dropzone on start drag + drop, re-enable after', () => {
|
||||
expect(settingsService.globalDropzoneEnabled).toBeTruthy()
|
||||
component.onDragStart(null)
|
||||
expect(settingsService.globalDropzoneEnabled).toBeFalsy()
|
||||
component.onDragEnd(null)
|
||||
expect(settingsService.globalDropzoneEnabled).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should update saved view sorting on drag + drop, show info', () => {
|
||||
const settingsSpy = jest.spyOn(settingsService, 'updateDashboardViewsSort')
|
||||
const toastSpy = jest.spyOn(toastService, 'showInfo')
|
||||
jest.spyOn(settingsService, 'storeSettings').mockReturnValue(of(true))
|
||||
component.onDrop({ index: 2, data: saved_views[0] } as DndDropEvent)
|
||||
component.onDragged(saved_views[0])
|
||||
expect(settingsSpy).toHaveBeenCalledWith([
|
||||
saved_views[2],
|
||||
saved_views[0],
|
||||
saved_views[3],
|
||||
])
|
||||
expect(toastSpy).toHaveBeenCalled()
|
||||
component.onDrop({ data: saved_views[3] } as DndDropEvent)
|
||||
})
|
||||
|
||||
it('should update saved view sorting on drag + drop, show info2', () => {
|
||||
jest.spyOn(settingsService, 'get').mockImplementation((key) => {
|
||||
if (key === SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER) return []
|
||||
})
|
||||
fixture.destroy()
|
||||
fixture = TestBed.createComponent(DashboardComponent)
|
||||
component = fixture.componentInstance
|
||||
fixture.detectChanges()
|
||||
const toastSpy = jest.spyOn(toastService, 'showError')
|
||||
jest
|
||||
.spyOn(settingsService, 'storeSettings')
|
||||
.mockReturnValue(throwError(() => new Error('unable to save')))
|
||||
component.onDrop({ index: 2, data: saved_views[0] } as DndDropEvent)
|
||||
component.onDragged(saved_views[0])
|
||||
expect(toastSpy).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
@ -3,6 +3,10 @@ import { SavedViewService } from 'src/app/services/rest/saved-view.service'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
import { ComponentWithPermissions } from '../with-permissions/with-permissions.component'
|
||||
import { TourService } from 'ngx-ui-tour-ng-bootstrap'
|
||||
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
|
||||
import { DndDropEvent } from 'ngx-drag-drop'
|
||||
import { ToastService } from 'src/app/services/toast.service'
|
||||
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
|
||||
|
||||
@Component({
|
||||
selector: 'pngx-dashboard',
|
||||
@ -10,12 +14,38 @@ import { TourService } from 'ngx-ui-tour-ng-bootstrap'
|
||||
styleUrls: ['./dashboard.component.scss'],
|
||||
})
|
||||
export class DashboardComponent extends ComponentWithPermissions {
|
||||
public dashboardViews: PaperlessSavedView[] = []
|
||||
constructor(
|
||||
public settingsService: SettingsService,
|
||||
public savedViewService: SavedViewService,
|
||||
private tourService: TourService
|
||||
private tourService: TourService,
|
||||
private toastService: ToastService
|
||||
) {
|
||||
super()
|
||||
|
||||
this.savedViewService.listAll().subscribe(() => {
|
||||
const sorted: number[] = this.settingsService.get(
|
||||
SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER
|
||||
)
|
||||
this.dashboardViews =
|
||||
sorted?.length > 0
|
||||
? sorted
|
||||
.map((id) =>
|
||||
this.savedViewService.dashboardViews.find((v) => v.id === id)
|
||||
)
|
||||
.concat(
|
||||
this.savedViewService.dashboardViews.filter(
|
||||
(v) => !sorted.includes(v.id)
|
||||
)
|
||||
)
|
||||
.filter((v) => v)
|
||||
: [...this.savedViewService.dashboardViews]
|
||||
console.log(
|
||||
this.dashboardViews,
|
||||
sorted,
|
||||
this.savedViewService.dashboardViews
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
get subtitle() {
|
||||
@ -33,4 +63,35 @@ export class DashboardComponent extends ComponentWithPermissions {
|
||||
this.settingsService.completeTour()
|
||||
}
|
||||
}
|
||||
|
||||
onDragStart(event: DragEvent) {
|
||||
this.settingsService.globalDropzoneEnabled = false
|
||||
}
|
||||
|
||||
onDragged(v: PaperlessSavedView) {
|
||||
const index = this.dashboardViews.indexOf(v)
|
||||
this.dashboardViews.splice(index, 1)
|
||||
this.settingsService
|
||||
.updateDashboardViewsSort(this.dashboardViews)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.toastService.showInfo($localize`Dashboard updated`)
|
||||
},
|
||||
error: (e) => {
|
||||
this.toastService.showError($localize`Error updating dashboard`, e)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
onDragEnd(event: DragEvent) {
|
||||
this.settingsService.globalDropzoneEnabled = true
|
||||
}
|
||||
|
||||
onDrop(event: DndDropEvent) {
|
||||
if (typeof event.index === 'undefined') {
|
||||
event.index = this.dashboardViews.length
|
||||
}
|
||||
|
||||
this.dashboardViews.splice(event.index, 0, event.data)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,13 @@
|
||||
<pngx-widget-frame [title]="savedView.name" [loading]="loading" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }">
|
||||
<pngx-widget-frame
|
||||
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }"
|
||||
[title]="savedView.name"
|
||||
[loading]="loading"
|
||||
[draggable]="savedView"
|
||||
(dndStart)="dndStart.emit($event)"
|
||||
(dndMoved)="dndMoved.emit($event)"
|
||||
(dndCanceled)="dndCanceled.emit($event)"
|
||||
(dndEnd)="dndEnd.emit($event)"
|
||||
>
|
||||
|
||||
<a *ngIf="documents.length" class="btn-link" header-buttons [routerLink]="[]" (click)="showAll()" i18n>Show all</a>
|
||||
|
||||
|
@ -28,6 +28,7 @@ import { WidgetFrameComponent } from '../widget-frame/widget-frame.component'
|
||||
import { SavedViewWidgetComponent } from './saved-view-widget.component'
|
||||
import { By } from '@angular/platform-browser'
|
||||
import { SafeUrlPipe } from 'src/app/pipes/safeurl.pipe'
|
||||
import { DndModule } from 'ngx-drag-drop'
|
||||
|
||||
const savedView: PaperlessSavedView = {
|
||||
id: 1,
|
||||
@ -90,6 +91,7 @@ describe('SavedViewWidgetComponent', () => {
|
||||
HttpClientTestingModule,
|
||||
NgbModule,
|
||||
RouterTestingModule.withRoutes(routes),
|
||||
DndModule,
|
||||
],
|
||||
}).compileComponents()
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
Output,
|
||||
QueryList,
|
||||
ViewChildren,
|
||||
} from '@angular/core'
|
||||
@ -51,6 +53,18 @@ export class SavedViewWidgetComponent
|
||||
@Input()
|
||||
savedView: PaperlessSavedView
|
||||
|
||||
@Output()
|
||||
dndStart: EventEmitter<DragEvent> = new EventEmitter()
|
||||
|
||||
@Output()
|
||||
dndMoved: EventEmitter<DragEvent> = new EventEmitter()
|
||||
|
||||
@Output()
|
||||
dndCanceled: EventEmitter<DragEvent> = new EventEmitter()
|
||||
|
||||
@Output()
|
||||
dndEnd: EventEmitter<DragEvent> = new EventEmitter()
|
||||
|
||||
documents: PaperlessDocument[] = []
|
||||
|
||||
unsubscribeNotifier: Subject<any> = new Subject()
|
||||
|
@ -12,6 +12,7 @@ import { RouterTestingModule } from '@angular/router/testing'
|
||||
import { routes } from 'src/app/app-routing.module'
|
||||
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
|
||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
||||
import { DndModule } from 'ngx-drag-drop'
|
||||
|
||||
describe('StatisticsWidgetComponent', () => {
|
||||
let component: StatisticsWidgetComponent
|
||||
@ -30,6 +31,7 @@ describe('StatisticsWidgetComponent', () => {
|
||||
HttpClientTestingModule,
|
||||
NgbModule,
|
||||
RouterTestingModule.withRoutes(routes),
|
||||
DndModule,
|
||||
],
|
||||
}).compileComponents()
|
||||
|
||||
|
@ -26,6 +26,7 @@ import { PermissionsService } from 'src/app/services/permissions.service'
|
||||
import { UploadDocumentsService } from 'src/app/services/upload-documents.service'
|
||||
import { WidgetFrameComponent } from '../widget-frame/widget-frame.component'
|
||||
import { UploadFileWidgetComponent } from './upload-file-widget.component'
|
||||
import { DndModule } from 'ngx-drag-drop'
|
||||
|
||||
describe('UploadFileWidgetComponent', () => {
|
||||
let component: UploadFileWidgetComponent
|
||||
@ -55,6 +56,7 @@ describe('UploadFileWidgetComponent', () => {
|
||||
RouterTestingModule.withRoutes(routes),
|
||||
NgxFileDropModule,
|
||||
NgbAlertModule,
|
||||
DndModule,
|
||||
],
|
||||
}).compileComponents()
|
||||
|
||||
|
@ -1,7 +1,21 @@
|
||||
<div class="card shadow-sm bg-light">
|
||||
<div class="card shadow-sm bg-light"
|
||||
[dndDraggable]="draggable"
|
||||
dndEffectAllowed="move"
|
||||
[dndDisableIf]="!draggable"
|
||||
(dndStart)="dndStart.emit($event)"
|
||||
(dndMoved)="dndMoved.emit($event)"
|
||||
(dndCanceled)="dndCanceled.emit($event)"
|
||||
(dndEnd)="dndEnd.emit($event)">
|
||||
<div class="card-header">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="d-flex">
|
||||
<div *ngIf="draggable" class="ms-n2 me-1" dndHandle>
|
||||
<svg class="sidebaricon text-muted" fill="currentColor">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#grip-vertical"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h6 class="card-title mb-0">{{title}}</h6>
|
||||
</div>
|
||||
<ng-container *ngIf="loading">
|
||||
<div class="spinner-border spinner-border-sm fw-normal ms-2 me-auto" role="status"></div>
|
||||
<div class="visually-hidden" i18n>Loading...</div>
|
||||
|
@ -0,0 +1,3 @@
|
||||
svg {
|
||||
cursor: move;
|
||||
}
|
@ -4,6 +4,7 @@ import { By } from '@angular/platform-browser'
|
||||
import { NgbAlertModule, NgbAlert } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
|
||||
import { WidgetFrameComponent } from './widget-frame.component'
|
||||
import { DndModule } from 'ngx-drag-drop'
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@ -29,7 +30,7 @@ describe('WidgetFrameComponent', () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [WidgetFrameComponent, WidgetFrameComponent],
|
||||
providers: [PermissionsGuard],
|
||||
imports: [NgbAlertModule],
|
||||
imports: [NgbAlertModule, DndModule],
|
||||
}).compileComponents()
|
||||
|
||||
fixture = TestBed.createComponent(WidgetFrameComponent)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core'
|
||||
|
||||
@Component({
|
||||
selector: 'pngx-widget-frame',
|
||||
@ -13,4 +13,19 @@ export class WidgetFrameComponent {
|
||||
|
||||
@Input()
|
||||
loading: boolean = false
|
||||
|
||||
@Input()
|
||||
draggable: any
|
||||
|
||||
@Output()
|
||||
dndStart: EventEmitter<DragEvent> = new EventEmitter()
|
||||
|
||||
@Output()
|
||||
dndMoved: EventEmitter<DragEvent> = new EventEmitter()
|
||||
|
||||
@Output()
|
||||
dndCanceled: EventEmitter<DragEvent> = new EventEmitter()
|
||||
|
||||
@Output()
|
||||
dndEnd: EventEmitter<DragEvent> = new EventEmitter()
|
||||
}
|
||||
|
@ -41,6 +41,8 @@ export const SETTINGS_KEYS = {
|
||||
'general-settings:update-checking:backend-setting',
|
||||
SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE:
|
||||
'general-settings:saved-views:warn-on-unsaved-change',
|
||||
DASHBOARD_VIEWS_SORT_ORDER:
|
||||
'general-settings:saved-views:dashboard-views-sort-order',
|
||||
TOUR_COMPLETE: 'general-settings:tour-complete',
|
||||
DEFAULT_PERMS_OWNER: 'general-settings:permissions:default-owner',
|
||||
DEFAULT_PERMS_VIEW_USERS: 'general-settings:permissions:default-view-users',
|
||||
@ -180,4 +182,9 @@ export const SETTINGS: PaperlessUiSetting[] = [
|
||||
type: 'array',
|
||||
default: [],
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER,
|
||||
type: 'array',
|
||||
default: [],
|
||||
},
|
||||
]
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
SETTINGS_KEYS,
|
||||
} from '../data/paperless-uisettings'
|
||||
import { SettingsService } from './settings.service'
|
||||
import { PaperlessSavedView } from '../data/paperless-saved-view'
|
||||
|
||||
describe('SettingsService', () => {
|
||||
let httpTestingController: HttpTestingController
|
||||
@ -277,4 +278,22 @@ describe('SettingsService', () => {
|
||||
)[0]
|
||||
expect(req.request.method).toEqual('POST')
|
||||
})
|
||||
|
||||
it('should update saved view sorting', () => {
|
||||
httpTestingController
|
||||
.expectOne(`${environment.apiBaseUrl}ui_settings/`)
|
||||
.flush(ui_settings)
|
||||
const setSpy = jest.spyOn(settingsService, 'set')
|
||||
settingsService.updateDashboardViewsSort([
|
||||
{ id: 1 } as PaperlessSavedView,
|
||||
{ id: 4 } as PaperlessSavedView,
|
||||
])
|
||||
expect(setSpy).toHaveBeenCalledWith(
|
||||
SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER,
|
||||
[1, 4]
|
||||
)
|
||||
httpTestingController
|
||||
.expectOne(`${environment.apiBaseUrl}ui_settings/`)
|
||||
.flush(ui_settings)
|
||||
})
|
||||
})
|
||||
|
@ -26,6 +26,7 @@ import { PaperlessUser } from '../data/paperless-user'
|
||||
import { PermissionsService } from './permissions.service'
|
||||
import { SavedViewService } from './rest/saved-view.service'
|
||||
import { ToastService } from './toast.service'
|
||||
import { PaperlessSavedView } from '../data/paperless-saved-view'
|
||||
|
||||
export interface LanguageOption {
|
||||
code: string
|
||||
@ -54,6 +55,8 @@ export class SettingsService {
|
||||
return this._renderer
|
||||
}
|
||||
|
||||
public globalDropzoneEnabled: boolean = true
|
||||
|
||||
constructor(
|
||||
rendererFactory: RendererFactory2,
|
||||
@Inject(DOCUMENT) private document,
|
||||
@ -531,4 +534,13 @@ export class SettingsService {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
updateDashboardViewsSort(
|
||||
dashboardViews: PaperlessSavedView[]
|
||||
): Observable<any> {
|
||||
this.set(SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER, [
|
||||
...new Set(dashboardViews.map((v) => v.id)),
|
||||
])
|
||||
return this.storeSettings()
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user