dirtycheck

This commit is contained in:
shamoon committed 2026-10-19 23:12:09 -07:00
1 parent 4d31063082
commit d2ed0c55a4
4 files changed
+52 -2

No files matched your search

+2
View File
@@ -292,6 +292,7 @@ export const routes: Routes = [
{
path: 'ocr-templates/new',
component: OcrTemplateEditorComponent,
canDeactivate: [DirtyFormGuard],
canActivate: [PermissionsGuard],
data: {
requiredPermission: {
@@ -304,6 +305,7 @@ export const routes: Routes = [
{
path: 'ocr-templates/:id',
component: OcrTemplateEditorComponent,
canDeactivate: [DirtyFormGuard],
canActivate: [PermissionsGuard],
data: {
requiredPermission: {
@@ -2,7 +2,7 @@
<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">
<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>
}
@@ -301,6 +301,24 @@ describe('OcrTemplateEditorComponent', () => {
})
})
it('tracks unsaved changes against the last loaded or saved state', () => {
fixture.detectChanges()
expect(component.isDirty$()).toBe(false)
component.template.combine_formats = { '1': '' }
expect(component.isDirty).toBe(false)
component.template.name = 'Invoices'
expect(component.isDirty).toBe(true)
templates.create.mockReturnValue(of({ ...component.template, id: 3 }))
component.save()
expect(component.isDirty).toBe(false)
component.previewDocId = 5
expect(component.isDirty).toBe(true)
})
it('hides results when extraction settings or geometry change', () => {
const selected = zone()
component.template.zones = [selected]
@@ -19,6 +19,7 @@ import {
NgbTypeaheadModule,
NgbTypeaheadSelectItemEvent,
} from '@ng-bootstrap/ng-bootstrap'
import { DirtyComponent } from '@ngneat/dirty-check-forms'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import {
catchError,
@@ -142,7 +143,9 @@ const RESIZE_CURSOR: Record<ResizeHandle, string> = {
templateUrl: './ocr-template-editor.component.html',
styleUrls: ['./ocr-template-editor.component.scss'],
})
export class OcrTemplateEditorComponent implements OnInit, OnDestroy {
export class OcrTemplateEditorComponent
implements OnInit, OnDestroy, DirtyComponent
{
private readonly route = inject(ActivatedRoute)
private readonly templateService = inject(OcrTemplateService)
private readonly customFieldsService = inject(CustomFieldsService)
@@ -269,6 +272,30 @@ export class OcrTemplateEditorComponent implements OnInit, OnDestroy {
: null
}
/** Snapshot of the editor state as last loaded or saved. */
private savedState = ''
get isDirty(): boolean {
return this.editorState() !== this.savedState
}
readonly isDirty$ = () => this.isDirty
private editorState(): string {
const formats = Object.entries(this.template.combine_formats ?? {}).filter(
([, format]) => format
)
return JSON.stringify({
...this.template,
combine_formats: Object.fromEntries(formats),
sample_document: this.previewDocId ?? null,
})
}
private markSaved() {
this.savedState = this.editorState()
}
get pageTitle(): string {
return this.isNew
? $localize`New OCR Template`
@@ -313,6 +340,7 @@ export class OcrTemplateEditorComponent implements OnInit, OnDestroy {
this.previewDocId = t.sample_document
this.loadPreview()
}
this.markSaved()
})
} else {
const qp = this.route.snapshot.queryParams
@@ -325,6 +353,7 @@ export class OcrTemplateEditorComponent implements OnInit, OnDestroy {
this.previewDocId = docId
this.loadPreview()
}
this.markSaved()
}
}
@@ -896,6 +925,7 @@ export class OcrTemplateEditorComponent implements OnInit, OnDestroy {
this.isNew = false
this.selectedZoneIndex = idx
this.saving = false
this.markSaved()
this.toastService.showInfo($localize`OCR template saved.`)
this.changeDetector.markForCheck()
},