mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-10-20 23:08:14 +00:00
Fuck it, make it a modal like literally everything else, drop the routes
This commit is contained in:
7 files changed
+213
-84
No files matched your search
@@ -13,7 +13,6 @@ import { DocumentDetailComponent } from './components/document-detail/document-d
|
||||
import { DocumentListComponent } from './components/document-list/document-list.component'
|
||||
import { DocumentAttributesComponent } from './components/manage/document-attributes/document-attributes.component'
|
||||
import { MailComponent } from './components/manage/mail/mail.component'
|
||||
import { OcrTemplateEditorComponent } from './components/manage/ocr-templates/ocr-template-editor/ocr-template-editor.component'
|
||||
import { OcrTemplatesComponent } from './components/manage/ocr-templates/ocr-templates.component'
|
||||
import { SavedViewsComponent } from './components/manage/saved-views/saved-views.component'
|
||||
import { ShareLinksComponent } from './components/manage/share-links/share-links.component'
|
||||
@@ -289,32 +288,6 @@ export const routes: Routes = [
|
||||
componentName: 'OcrTemplatesComponent',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'ocr-templates/new',
|
||||
component: OcrTemplateEditorComponent,
|
||||
canDeactivate: [DirtyFormGuard],
|
||||
canActivate: [PermissionsGuard],
|
||||
data: {
|
||||
requiredPermission: {
|
||||
action: PermissionAction.Add,
|
||||
type: PermissionType.OcrTemplate,
|
||||
},
|
||||
componentName: 'OcrTemplateEditorComponent',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'ocr-templates/:id',
|
||||
component: OcrTemplateEditorComponent,
|
||||
canDeactivate: [DirtyFormGuard],
|
||||
canActivate: [PermissionsGuard],
|
||||
data: {
|
||||
requiredPermission: {
|
||||
action: PermissionAction.Change,
|
||||
type: PermissionType.OcrTemplate,
|
||||
},
|
||||
componentName: 'OcrTemplateEditorComponent',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'mail',
|
||||
component: MailComponent,
|
||||
|
||||
+18
-11
@@ -1,15 +1,12 @@
|
||||
<pngx-page-header [title]="pageTitle" [id]="template.id">
|
||||
<a routerLink="/ocr-templates" class="btn btn-sm btn-outline-secondary" title="Close" i18n-title aria-label="Close" i18n-aria-label>
|
||||
<i-bs width="1.2em" height="1.2em" name="x"></i-bs><span class="d-none d-lg-inline ps-1" i18n>Close</span>
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-primary" (click)="save()" [disabled]="saving || !isDirty">
|
||||
@if (saving) {
|
||||
<span class="spinner-border spinner-border-sm me-1" aria-hidden="true"></span>
|
||||
}
|
||||
<span i18n>Save</span>
|
||||
</button>
|
||||
</pngx-page-header>
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="ocr-template-editor-title">{{ dialogTitle }}</h4>
|
||||
@if (template.id) {
|
||||
<span class="badge bg-primary text-primary-text-contrast ms-2">ID: {{ template.id }}</span>
|
||||
}
|
||||
<button type="button" class="btn-close" (click)="activeModal.dismiss()" [disabled]="saving" aria-label="Close" i18n-aria-label></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="row" [attr.inert]="saving ? '' : null" [attr.aria-busy]="saving">
|
||||
<div class="col-lg-4 mb-3">
|
||||
<ul ngbNav #nav="ngbNav" [(activeId)]="activeTab" class="nav-underline flex-nowrap flex-md-wrap overflow-auto">
|
||||
@@ -343,3 +340,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="activeModal.dismiss()" [disabled]="saving" i18n>Cancel</button>
|
||||
<button type="button" class="btn btn-primary" (click)="save()" [disabled]="saving || !isDirty">
|
||||
@if (saving) {
|
||||
<span class="spinner-border spinner-border-sm me-1" aria-hidden="true"></span>
|
||||
}
|
||||
<span i18n>Save</span>
|
||||
</button>
|
||||
</div>
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
:host {
|
||||
display: block;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.zone-preview-scroll {
|
||||
|
||||
+53
-11
@@ -1,11 +1,14 @@
|
||||
import { ElementRef } from '@angular/core'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { ActivatedRoute } from '@angular/router'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { of, Subject, throwError } from 'rxjs'
|
||||
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
|
||||
import { Document } from 'src/app/data/document'
|
||||
import { OcrTemplateZone, OcrZoneTestResult } from 'src/app/data/ocr-template'
|
||||
import {
|
||||
OcrTemplate,
|
||||
OcrTemplateZone,
|
||||
OcrZoneTestResult,
|
||||
} from 'src/app/data/ocr-template'
|
||||
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
|
||||
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
|
||||
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
|
||||
@@ -40,22 +43,23 @@ describe('OcrTemplateEditorComponent', () => {
|
||||
const templates = {
|
||||
testZone: jest.fn(),
|
||||
create: jest.fn(),
|
||||
update: jest.fn(),
|
||||
getPageImageUrl: (id: number, page: number) => `/preview/${id}/${page}`,
|
||||
}
|
||||
const modal = { open: jest.fn() }
|
||||
const activeModal = { close: jest.fn(), dismiss: jest.fn() }
|
||||
|
||||
beforeEach(async () => {
|
||||
documents.list.mockReset().mockReturnValue(of({ results: [] }))
|
||||
documents.get.mockReset().mockReturnValue(of({ id: 1, page_count: 3 }))
|
||||
templates.testZone.mockReset()
|
||||
templates.create.mockReset()
|
||||
templates.update.mockReset()
|
||||
activeModal.close.mockReset()
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [OcrTemplateEditorComponent],
|
||||
providers: [
|
||||
{
|
||||
provide: ActivatedRoute,
|
||||
useValue: { snapshot: { paramMap: new Map() } },
|
||||
},
|
||||
{ provide: NgbActiveModal, useValue: activeModal },
|
||||
{ provide: DocumentService, useValue: documents },
|
||||
{ provide: OcrTemplateService, useValue: templates },
|
||||
...[CustomFieldsService, DocumentTypeService, CorrespondentService].map(
|
||||
@@ -124,6 +128,46 @@ describe('OcrTemplateEditorComponent', () => {
|
||||
) as PointerEvent
|
||||
}
|
||||
|
||||
it('edits a copy of the supplied template and restores its sample document', () => {
|
||||
const original: OcrTemplate = {
|
||||
...component.template,
|
||||
id: 3,
|
||||
name: 'Invoices',
|
||||
sample_document: 5,
|
||||
zones: [zone()],
|
||||
combine_formats: { '1': '{Invoice number}' },
|
||||
}
|
||||
component.object = original
|
||||
fixture.detectChanges()
|
||||
|
||||
expect(component.isNew).toBe(false)
|
||||
expect(component.previewDocId).toBe(5)
|
||||
expect(component.pageImageUrl).toBe('/preview/5/0')
|
||||
expect(component.isDirty).toBe(false)
|
||||
component.template.name = 'Edited'
|
||||
component.template.zones[0].x = 500
|
||||
component.template.combine_formats['1'] = 'Edited format'
|
||||
expect(original.name).toBe('Invoices')
|
||||
expect(original.zones[0].x).toBe(100)
|
||||
expect(original.combine_formats['1']).toBe('{Invoice number}')
|
||||
})
|
||||
|
||||
it('updates an existing template, notifies the list, and closes the dialog', () => {
|
||||
component.object = { ...component.template, id: 3, name: 'Invoices' }
|
||||
fixture.detectChanges()
|
||||
component.template.name = 'Renamed'
|
||||
const saved = { ...component.template }
|
||||
templates.update.mockReturnValue(of(saved))
|
||||
const succeeded = jest.spyOn(component.succeeded, 'emit')
|
||||
|
||||
component.save()
|
||||
|
||||
expect(templates.update).toHaveBeenCalledWith(component.template)
|
||||
expect(templates.create).not.toHaveBeenCalled()
|
||||
expect(succeeded).toHaveBeenCalledWith(saved)
|
||||
expect(activeModal.close).toHaveBeenCalledWith(saved)
|
||||
})
|
||||
|
||||
it('searches across document metadata even when a template document type is set', () => {
|
||||
jest.useFakeTimers()
|
||||
const term$ = new Subject<string>()
|
||||
@@ -355,7 +399,7 @@ describe('OcrTemplateEditorComponent', () => {
|
||||
|
||||
it('tracks unsaved changes against the last loaded or saved state', () => {
|
||||
fixture.detectChanges()
|
||||
expect(component.isDirty$()).toBe(false)
|
||||
expect(component.isDirty).toBe(false)
|
||||
|
||||
component.template.combine_formats = { '1': '' }
|
||||
expect(component.isDirty).toBe(false)
|
||||
@@ -390,9 +434,7 @@ describe('OcrTemplateEditorComponent', () => {
|
||||
pending.next({ ...component.template, id: 3 })
|
||||
pending.complete()
|
||||
expect(component.saving).toBe(false)
|
||||
component.onOverlayPointerDown(pointer(500, 500))
|
||||
component.onOverlayPointerUp(pointer(700, 700))
|
||||
expect(component.template.zones).toHaveLength(1)
|
||||
expect(activeModal.close).toHaveBeenCalledWith(component.template)
|
||||
})
|
||||
|
||||
it('stops an active move and unlocks editing without losing changes after a failed save', () => {
|
||||
|
||||
+23
-28
@@ -4,23 +4,25 @@ import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
inject,
|
||||
Input,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
Output,
|
||||
signal,
|
||||
ViewChild,
|
||||
} from '@angular/core'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { ActivatedRoute, RouterModule } from '@angular/router'
|
||||
import {
|
||||
NgbActiveModal,
|
||||
NgbModal,
|
||||
NgbNavModule,
|
||||
NgbPopoverModule,
|
||||
NgbTypeaheadModule,
|
||||
NgbTypeaheadSelectItemEvent,
|
||||
} from '@ng-bootstrap/ng-bootstrap'
|
||||
import { DirtyComponent } from '@ngneat/dirty-check-forms'
|
||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
import {
|
||||
catchError,
|
||||
@@ -40,7 +42,6 @@ import { NumberComponent } from 'src/app/components/common/input/number/number.c
|
||||
import { SelectComponent } from 'src/app/components/common/input/select/select.component'
|
||||
import { SwitchComponent } from 'src/app/components/common/input/switch/switch.component'
|
||||
import { TextComponent } from 'src/app/components/common/input/text/text.component'
|
||||
import { PageHeaderComponent } from 'src/app/components/common/page-header/page-header.component'
|
||||
import { CustomField } from 'src/app/data/custom-field'
|
||||
import { Document } from 'src/app/data/document'
|
||||
import { DocumentType } from 'src/app/data/document-type'
|
||||
@@ -128,14 +129,12 @@ const RESIZE_CURSOR: Record<ResizeHandle, string> = {
|
||||
standalone: true,
|
||||
imports: [
|
||||
DragDropModule,
|
||||
PageHeaderComponent,
|
||||
NumberComponent,
|
||||
TextComponent,
|
||||
SelectComponent,
|
||||
SwitchComponent,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
RouterModule,
|
||||
NgbNavModule,
|
||||
NgbPopoverModule,
|
||||
NgbTypeaheadModule,
|
||||
@@ -145,10 +144,8 @@ const RESIZE_CURSOR: Record<ResizeHandle, string> = {
|
||||
templateUrl: './ocr-template-editor.component.html',
|
||||
styleUrls: ['./ocr-template-editor.component.scss'],
|
||||
})
|
||||
export class OcrTemplateEditorComponent
|
||||
implements OnInit, OnDestroy, DirtyComponent
|
||||
{
|
||||
private readonly route = inject(ActivatedRoute)
|
||||
export class OcrTemplateEditorComponent implements OnInit, OnDestroy {
|
||||
readonly activeModal = inject(NgbActiveModal)
|
||||
private readonly templateService = inject(OcrTemplateService)
|
||||
private readonly customFieldsService = inject(CustomFieldsService)
|
||||
private readonly documentTypeService = inject(DocumentTypeService)
|
||||
@@ -161,6 +158,9 @@ export class OcrTemplateEditorComponent
|
||||
private readonly cancelZoneTest$ = new Subject<void>()
|
||||
private readonly customDateFormatZones = new WeakSet<OcrTemplateZone>()
|
||||
|
||||
@Input() object: OcrTemplate
|
||||
@Output() succeeded = new EventEmitter<OcrTemplate>()
|
||||
|
||||
@ViewChild('zoneOverlay') overlayRef: ElementRef<SVGSVGElement>
|
||||
imageRef: ElementRef<HTMLImageElement>
|
||||
private previewResizeObserver: ResizeObserver | null = null
|
||||
@@ -281,8 +281,6 @@ export class OcrTemplateEditorComponent
|
||||
return this.editorState() !== this.savedState
|
||||
}
|
||||
|
||||
readonly isDirty$ = () => this.isDirty
|
||||
|
||||
private editorState(): string {
|
||||
const formats = Object.entries(this.template.combine_formats ?? {}).filter(
|
||||
([, format]) => format
|
||||
@@ -298,7 +296,7 @@ export class OcrTemplateEditorComponent
|
||||
this.savedState = this.editorState()
|
||||
}
|
||||
|
||||
get pageTitle(): string {
|
||||
get dialogTitle(): string {
|
||||
return this.isNew
|
||||
? $localize`New OCR Template`
|
||||
: $localize`Edit OCR Template`
|
||||
@@ -329,24 +327,19 @@ export class OcrTemplateEditorComponent
|
||||
this.changeDetector.markForCheck()
|
||||
})
|
||||
|
||||
const id = this.route.snapshot.paramMap.get('id')
|
||||
if (id) {
|
||||
if (this.object) {
|
||||
this.isNew = false
|
||||
this.templateService
|
||||
.get(parseInt(id))
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe((t) => {
|
||||
this.template = t
|
||||
this.changeDetector.markForCheck()
|
||||
if (t.sample_document) {
|
||||
this.previewDocId = t.sample_document
|
||||
this.loadPreview()
|
||||
}
|
||||
this.markSaved()
|
||||
})
|
||||
} else {
|
||||
this.markSaved()
|
||||
this.template = {
|
||||
...this.object,
|
||||
zones: this.object.zones.map((zone) => ({ ...zone })),
|
||||
combine_formats: { ...this.object.combine_formats },
|
||||
}
|
||||
if (this.template.sample_document) {
|
||||
this.previewDocId = this.template.sample_document
|
||||
this.loadPreview()
|
||||
}
|
||||
}
|
||||
this.markSaved()
|
||||
}
|
||||
|
||||
searchDocuments = (text$: Observable<string>): Observable<Document[]> =>
|
||||
@@ -923,6 +916,8 @@ export class OcrTemplateEditorComponent
|
||||
this.markSaved()
|
||||
this.toastService.showInfo($localize`OCR template saved.`)
|
||||
this.changeDetector.markForCheck()
|
||||
this.succeeded.emit(saved)
|
||||
this.activeModal.close(saved)
|
||||
},
|
||||
error: (e) => {
|
||||
this.saving = false
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import { EventEmitter } from '@angular/core'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { NgbModal, NgbModalOptions } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { of } from 'rxjs'
|
||||
import { OcrTemplate } from 'src/app/data/ocr-template'
|
||||
import { DirtyFormGuard } from 'src/app/guards/dirty-form.guard'
|
||||
import { PermissionsService } from 'src/app/services/permissions.service'
|
||||
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
|
||||
import { OcrTemplateService } from 'src/app/services/rest/ocr-template.service'
|
||||
import { ToastService } from 'src/app/services/toast.service'
|
||||
import { OcrTemplateEditorComponent } from './ocr-template-editor/ocr-template-editor.component'
|
||||
import { OcrTemplatesComponent } from './ocr-templates.component'
|
||||
|
||||
describe('OcrTemplatesComponent', () => {
|
||||
let fixture: ComponentFixture<OcrTemplatesComponent>
|
||||
let component: OcrTemplatesComponent
|
||||
let options: NgbModalOptions
|
||||
const editor = {
|
||||
object: undefined as OcrTemplate,
|
||||
saving: false,
|
||||
isDirty: false,
|
||||
succeeded: new EventEmitter<OcrTemplate>(),
|
||||
}
|
||||
const modal = { open: jest.fn() }
|
||||
const dirtyFormGuard = { confirmChanges: jest.fn() }
|
||||
|
||||
beforeEach(async () => {
|
||||
editor.object = undefined
|
||||
editor.saving = false
|
||||
editor.isDirty = false
|
||||
editor.succeeded = new EventEmitter<OcrTemplate>()
|
||||
modal.open.mockReset().mockImplementation((_, modalOptions) => {
|
||||
options = modalOptions
|
||||
return { componentInstance: editor }
|
||||
})
|
||||
dirtyFormGuard.confirmChanges.mockReset().mockReturnValue(of(true))
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [OcrTemplatesComponent],
|
||||
providers: [
|
||||
{ provide: NgbModal, useValue: modal },
|
||||
{ provide: DirtyFormGuard, useValue: dirtyFormGuard },
|
||||
{ provide: PermissionsService, useValue: {} },
|
||||
{ provide: OcrTemplateService, useValue: {} },
|
||||
{ provide: DocumentTypeService, useValue: {} },
|
||||
{ provide: ToastService, useValue: {} },
|
||||
],
|
||||
})
|
||||
.overrideComponent(OcrTemplatesComponent, {
|
||||
set: { template: '', imports: [] },
|
||||
})
|
||||
.compileComponents()
|
||||
fixture = TestBed.createComponent(OcrTemplatesComponent)
|
||||
component = fixture.componentInstance
|
||||
})
|
||||
|
||||
it('opens creation in a dialog and refreshes the list after saving', () => {
|
||||
const reload = jest.spyOn(component, 'reload').mockImplementation(() => {})
|
||||
component.createTemplate()
|
||||
|
||||
expect(modal.open).toHaveBeenCalledWith(
|
||||
OcrTemplateEditorComponent,
|
||||
expect.any(Object)
|
||||
)
|
||||
expect(editor.object).toBeUndefined()
|
||||
expect(options.backdrop).toBe('static')
|
||||
expect(options.beforeDismiss()).toBe(true)
|
||||
editor.succeeded.emit({ id: 3 } as OcrTemplate)
|
||||
expect(reload).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('passes the selected template to the same editor dialog', () => {
|
||||
const template = { id: 3, name: 'Invoices' } as OcrTemplate
|
||||
component.editTemplate(template)
|
||||
|
||||
expect(editor.object).toBe(template)
|
||||
})
|
||||
|
||||
it('confirms dismissal only when dirty and never dismisses while saving', async () => {
|
||||
component.createTemplate()
|
||||
editor.isDirty = true
|
||||
dirtyFormGuard.confirmChanges.mockReturnValue(of(false))
|
||||
expect(await options.beforeDismiss()).toBe(false)
|
||||
dirtyFormGuard.confirmChanges.mockReturnValue(of(true))
|
||||
expect(await options.beforeDismiss()).toBe(true)
|
||||
expect(dirtyFormGuard.confirmChanges).toHaveBeenCalledTimes(2)
|
||||
|
||||
editor.saving = true
|
||||
expect(options.beforeDismiss()).toBe(false)
|
||||
expect(dirtyFormGuard.confirmChanges).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
})
|
||||
@@ -1,11 +1,11 @@
|
||||
import { ChangeDetectorRef, Component, OnInit, inject } from '@angular/core'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { Router } from '@angular/router'
|
||||
import { NgbDropdownModule, NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
import { delay, takeUntil, tap } from 'rxjs'
|
||||
import { delay, firstValueFrom, takeUntil, tap } from 'rxjs'
|
||||
import { OcrTemplate } from 'src/app/data/ocr-template'
|
||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
||||
import { DirtyFormGuard } from 'src/app/guards/dirty-form.guard'
|
||||
import { PermissionsService } from 'src/app/services/permissions.service'
|
||||
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
|
||||
import { OcrTemplateService } from 'src/app/services/rest/ocr-template.service'
|
||||
@@ -13,6 +13,7 @@ import { ToastService } from 'src/app/services/toast.service'
|
||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
||||
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
|
||||
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
|
||||
import { OcrTemplateEditorComponent } from './ocr-template-editor/ocr-template-editor.component'
|
||||
|
||||
@Component({
|
||||
selector: 'pngx-ocr-templates',
|
||||
@@ -31,8 +32,8 @@ export class OcrTemplatesComponent
|
||||
{
|
||||
private readonly service = inject(OcrTemplateService)
|
||||
private readonly documentTypeService = inject(DocumentTypeService)
|
||||
private readonly router = inject(Router)
|
||||
private readonly modalService = inject(NgbModal)
|
||||
private readonly dirtyFormGuard = inject(DirtyFormGuard)
|
||||
private readonly toastService = inject(ToastService)
|
||||
private readonly changeDetector = inject(ChangeDetectorRef)
|
||||
permissionsService = inject(PermissionsService)
|
||||
@@ -75,11 +76,29 @@ export class OcrTemplatesComponent
|
||||
}
|
||||
|
||||
createTemplate() {
|
||||
this.router.navigate(['/ocr-templates', 'new'])
|
||||
this.editTemplate()
|
||||
}
|
||||
|
||||
editTemplate(t: OcrTemplate) {
|
||||
this.router.navigate(['/ocr-templates', t.id])
|
||||
editTemplate(t?: OcrTemplate) {
|
||||
const modal = this.modalService.open(OcrTemplateEditorComponent, {
|
||||
backdrop: 'static',
|
||||
keyboard: false,
|
||||
size: 'xl',
|
||||
fullscreen: 'lg',
|
||||
scrollable: true,
|
||||
ariaLabelledBy: 'ocr-template-editor-title',
|
||||
beforeDismiss: () => {
|
||||
const editor: OcrTemplateEditorComponent = modal.componentInstance
|
||||
if (editor.saving) return false
|
||||
return editor.isDirty
|
||||
? firstValueFrom(this.dirtyFormGuard.confirmChanges())
|
||||
: true
|
||||
},
|
||||
})
|
||||
modal.componentInstance.object = t
|
||||
modal.componentInstance.succeeded
|
||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe(() => this.reload())
|
||||
}
|
||||
|
||||
toggleTemplate(t: OcrTemplate) {
|
||||
|
||||
Reference in new issue
Block a user