mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-09 09:58:20 -05:00
Implement warning on close unsaved view
This commit is contained in:
parent
1b55717cc7
commit
7efdce44f7
@ -15,6 +15,7 @@ import { DirtyFormGuard } from './guards/dirty-form.guard'
|
|||||||
import { StoragePathListComponent } from './components/manage/storage-path-list/storage-path-list.component'
|
import { StoragePathListComponent } from './components/manage/storage-path-list/storage-path-list.component'
|
||||||
import { TasksComponent } from './components/manage/tasks/tasks.component'
|
import { TasksComponent } from './components/manage/tasks/tasks.component'
|
||||||
import { DirtyDocGuard } from './guards/dirty-doc.guard'
|
import { DirtyDocGuard } from './guards/dirty-doc.guard'
|
||||||
|
import { DirtySavedViewGuard } from './guards/dirty-saved-view.guard'
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
|
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
|
||||||
@ -24,8 +25,16 @@ const routes: Routes = [
|
|||||||
canDeactivate: [DirtyDocGuard],
|
canDeactivate: [DirtyDocGuard],
|
||||||
children: [
|
children: [
|
||||||
{ path: 'dashboard', component: DashboardComponent },
|
{ path: 'dashboard', component: DashboardComponent },
|
||||||
{ path: 'documents', component: DocumentListComponent },
|
{
|
||||||
{ path: 'view/:id', component: DocumentListComponent },
|
path: 'documents',
|
||||||
|
component: DocumentListComponent,
|
||||||
|
canDeactivate: [DirtySavedViewGuard],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'view/:id',
|
||||||
|
component: DocumentListComponent,
|
||||||
|
canDeactivate: [DirtySavedViewGuard],
|
||||||
|
},
|
||||||
{ path: 'documents/:id', component: DocumentDetailComponent },
|
{ path: 'documents/:id', component: DocumentDetailComponent },
|
||||||
{ path: 'asn/:id', component: DocumentAsnComponent },
|
{ path: 'asn/:id', component: DocumentAsnComponent },
|
||||||
{ path: 'tags', component: TagListComponent },
|
{ path: 'tags', component: TagListComponent },
|
||||||
|
@ -69,6 +69,7 @@ import { ColorComponent } from './components/common/input/color/color.component'
|
|||||||
import { DocumentAsnComponent } from './components/document-asn/document-asn.component'
|
import { DocumentAsnComponent } from './components/document-asn/document-asn.component'
|
||||||
import { DocumentCommentsComponent } from './components/document-comments/document-comments.component'
|
import { DocumentCommentsComponent } from './components/document-comments/document-comments.component'
|
||||||
import { DirtyDocGuard } from './guards/dirty-doc.guard'
|
import { DirtyDocGuard } from './guards/dirty-doc.guard'
|
||||||
|
import { DirtySavedViewGuard } from './guards/dirty-saved-view.guard'
|
||||||
import { StoragePathListComponent } from './components/manage/storage-path-list/storage-path-list.component'
|
import { StoragePathListComponent } from './components/manage/storage-path-list/storage-path-list.component'
|
||||||
import { StoragePathEditDialogComponent } from './components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component'
|
import { StoragePathEditDialogComponent } from './components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component'
|
||||||
import { SettingsService } from './services/settings.service'
|
import { SettingsService } from './services/settings.service'
|
||||||
@ -215,6 +216,7 @@ function initializeApp(settings: SettingsService) {
|
|||||||
{ provide: NgbDateAdapter, useClass: ISODateAdapter },
|
{ provide: NgbDateAdapter, useClass: ISODateAdapter },
|
||||||
{ provide: NgbDateParserFormatter, useClass: LocalizedDateParserFormatter },
|
{ provide: NgbDateParserFormatter, useClass: LocalizedDateParserFormatter },
|
||||||
DirtyDocGuard,
|
DirtyDocGuard,
|
||||||
|
DirtySavedViewGuard,
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
})
|
})
|
||||||
|
@ -16,4 +16,7 @@
|
|||||||
<ngb-progressbar *ngIf="!confirmButtonEnabled" style="height: 1px;" type="dark" [max]="secondsTotal" [value]="seconds"></ngb-progressbar>
|
<ngb-progressbar *ngIf="!confirmButtonEnabled" style="height: 1px;" type="dark" [max]="secondsTotal" [value]="seconds"></ngb-progressbar>
|
||||||
<span class="visually-hidden">{{ seconds | number: '1.0-0' }} seconds</span>
|
<span class="visually-hidden">{{ seconds | number: '1.0-0' }} seconds</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button *ngIf="alternativeBtnCaption" type="button" class="btn" [class]="alternativeBtnClass" (click)="alternative()" [disabled]="!alternativeButtonEnabled || !buttonsEnabled">
|
||||||
|
{{alternativeBtnCaption}}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,6 +13,9 @@ export class ConfirmDialogComponent {
|
|||||||
@Output()
|
@Output()
|
||||||
public confirmClicked = new EventEmitter()
|
public confirmClicked = new EventEmitter()
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
public alternativeClicked = new EventEmitter()
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
title = $localize`Confirmation`
|
title = $localize`Confirmation`
|
||||||
|
|
||||||
@ -28,14 +31,22 @@ export class ConfirmDialogComponent {
|
|||||||
@Input()
|
@Input()
|
||||||
btnCaption = $localize`Confirm`
|
btnCaption = $localize`Confirm`
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
alternativeBtnClass = 'btn-secondary'
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
alternativeBtnCaption
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
buttonsEnabled = true
|
buttonsEnabled = true
|
||||||
|
|
||||||
confirmButtonEnabled = true
|
confirmButtonEnabled = true
|
||||||
|
alternativeButtonEnabled = true
|
||||||
seconds = 0
|
seconds = 0
|
||||||
secondsTotal = 0
|
secondsTotal = 0
|
||||||
|
|
||||||
confirmSubject: Subject<boolean>
|
confirmSubject: Subject<boolean>
|
||||||
|
alternativeSubject: Subject<boolean>
|
||||||
|
|
||||||
delayConfirm(seconds: number) {
|
delayConfirm(seconds: number) {
|
||||||
const refreshInterval = 0.15 // s
|
const refreshInterval = 0.15 // s
|
||||||
@ -68,4 +79,10 @@ export class ConfirmDialogComponent {
|
|||||||
this.confirmSubject?.next(true)
|
this.confirmSubject?.next(true)
|
||||||
this.confirmSubject?.complete()
|
this.confirmSubject?.complete()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alternative() {
|
||||||
|
this.alternativeClicked.emit()
|
||||||
|
this.alternativeSubject?.next(true)
|
||||||
|
this.alternativeSubject?.complete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="btn-group ms-2 flex-fill" ngbDropdown role="group">
|
<div class="btn-group ms-2 flex-fill" ngbDropdown role="group">
|
||||||
<button class="btn btn-sm btn-outline-primary dropdown-toggle flex-fill" tourAnchor="tour.documents-views" ngbDropdownToggle i18n>Views</button>
|
<button class="btn btn-sm btn-outline-primary dropdown-toggle flex-fill" tourAnchor="tour.documents-views" ngbDropdownToggle>
|
||||||
|
<ng-container i18n>Views</ng-container>
|
||||||
|
<div *ngIf="savedViewIsModified" class="position-absolute top-0 start-100 p-2 translate-middle badge bg-secondary border border-light rounded-circle">
|
||||||
|
<span class="visually-hidden">selected</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
<div class="dropdown-menu shadow dropdown-menu-right" ngbDropdownMenu>
|
<div class="dropdown-menu shadow dropdown-menu-right" ngbDropdownMenu>
|
||||||
<ng-container *ngIf="!list.activeSavedViewId">
|
<ng-container *ngIf="!list.activeSavedViewId">
|
||||||
<button ngbDropdownItem *ngFor="let view of savedViewService.allViews" (click)="loadViewConfig(view.id)">{{view.name}}</button>
|
<button ngbDropdownItem *ngFor="let view of savedViewService.allViews" (click)="loadViewConfig(view.id)">{{view.name}}</button>
|
||||||
|
51
src-ui/src/app/guards/dirty-saved-view.guard.ts
Normal file
51
src-ui/src/app/guards/dirty-saved-view.guard.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { CanDeactivate } from '@angular/router'
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { first, Observable, Subject } from 'rxjs'
|
||||||
|
import { DocumentListComponent } from '../components/document-list/document-list.component'
|
||||||
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { ConfirmDialogComponent } from '../components/common/confirm-dialog/confirm-dialog.component'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DirtySavedViewGuard
|
||||||
|
implements CanDeactivate<DocumentListComponent>
|
||||||
|
{
|
||||||
|
constructor(private modalService: NgbModal) {}
|
||||||
|
|
||||||
|
canDeactivate(
|
||||||
|
component: DocumentListComponent
|
||||||
|
): boolean | Observable<boolean> {
|
||||||
|
return component.savedViewIsModified ? this.warn(component) : true
|
||||||
|
}
|
||||||
|
|
||||||
|
warn(component: DocumentListComponent) {
|
||||||
|
let modal = this.modalService.open(ConfirmDialogComponent, {
|
||||||
|
backdrop: 'static',
|
||||||
|
})
|
||||||
|
modal.componentInstance.title = $localize`Unsaved Changes`
|
||||||
|
modal.componentInstance.messageBold =
|
||||||
|
$localize`You have unsaved changes to the saved view` +
|
||||||
|
' "' +
|
||||||
|
component.getTitle()
|
||||||
|
;('".')
|
||||||
|
modal.componentInstance.message = $localize`Are you sure you want to close this saved view?`
|
||||||
|
modal.componentInstance.btnClass = 'btn-secondary'
|
||||||
|
modal.componentInstance.btnCaption = $localize`Close`
|
||||||
|
modal.componentInstance.alternativeBtnClass = 'btn-primary'
|
||||||
|
modal.componentInstance.alternativeBtnCaption = $localize`Save and close`
|
||||||
|
modal.componentInstance.alternativeClicked.pipe(first()).subscribe(() => {
|
||||||
|
modal.componentInstance.buttonsEnabled = false
|
||||||
|
component.saveViewConfig()
|
||||||
|
modal.close()
|
||||||
|
})
|
||||||
|
modal.componentInstance.confirmClicked.pipe(first()).subscribe(() => {
|
||||||
|
modal.componentInstance.buttonsEnabled = false
|
||||||
|
modal.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
const subject = new Subject<boolean>()
|
||||||
|
modal.componentInstance.confirmSubject = subject
|
||||||
|
modal.componentInstance.alternativeSubject = subject
|
||||||
|
|
||||||
|
return subject
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user