163 lines
5.3 KiB
TypeScript
163 lines
5.3 KiB
TypeScript
import { Component, OnInit } from '@angular/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { FormsModule } from '@angular/forms';
|
|
import { DragDropModule, CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
|
|
import { SplitterModule } from 'primeng/splitter';
|
|
import { ButtonModule } from 'primeng/button';
|
|
import { DialogModule } from 'primeng/dialog';
|
|
import { InputTextModule } from 'primeng/inputtext';
|
|
import { DropdownModule } from 'primeng/dropdown';
|
|
import { TableModule } from 'primeng/table';
|
|
import { ToastModule } from 'primeng/toast';
|
|
import { MessageService } from 'primeng/api';
|
|
|
|
import { TemplateService, DocumentLayout, TemplateField } from '../services/template.service';
|
|
|
|
@Component({
|
|
selector: 'app-templates',
|
|
standalone: true,
|
|
imports: [
|
|
CommonModule,
|
|
FormsModule,
|
|
DragDropModule,
|
|
SplitterModule,
|
|
ButtonModule,
|
|
DialogModule,
|
|
InputTextModule,
|
|
DropdownModule,
|
|
TableModule,
|
|
ToastModule
|
|
],
|
|
providers: [MessageService],
|
|
templateUrl: './templates.component.html',
|
|
styleUrls: ['./templates.component.scss']
|
|
})
|
|
export class TemplatesComponent implements OnInit {
|
|
// Document State
|
|
documentId: string | null = null;
|
|
layoutNodes: DocumentLayout[] = [];
|
|
|
|
// Template State
|
|
templateName: string = '';
|
|
templateFields: TemplateField[] = [];
|
|
mappings: { [fieldId: number]: DocumentLayout[] } = {};
|
|
|
|
// UI State
|
|
displayAddField: boolean = false;
|
|
newField: TemplateField = { field_label: '', field_type: 'TEXT', display_order: 0, required_flag: false };
|
|
fieldTypes = [
|
|
{ label: 'TEXT', value: 'TEXT' },
|
|
{ label: 'NUMBER', value: 'NUMBER' },
|
|
{ label: 'DATE', value: 'DATE' },
|
|
{ label: 'AMOUNT', value: 'AMOUNT' },
|
|
{ label: 'ADDRESS', value: 'ADDRESS' },
|
|
{ label: 'TABLE_COLUMN', value: 'TABLE_COLUMN' }
|
|
];
|
|
|
|
constructor(
|
|
private templateService: TemplateService,
|
|
private messageService: MessageService
|
|
) {}
|
|
|
|
ngOnInit(): void {}
|
|
|
|
onFileUpload(event: any) {
|
|
const file = event.target.files[0];
|
|
if (file) {
|
|
this.templateService.uploadDocument(file).subscribe({
|
|
next: (res) => {
|
|
this.documentId = res.id || res.pk_document_id;
|
|
this.messageService.add({ severity: 'success', summary: 'Uploaded', detail: 'Document uploaded successfully.' });
|
|
this.fetchLayout();
|
|
},
|
|
error: (err) => {
|
|
this.messageService.add({ severity: 'error', summary: 'Error', detail: 'Upload failed.' });
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
fetchLayout() {
|
|
if (!this.documentId) return;
|
|
this.templateService.getDocumentLayout(this.documentId).subscribe({
|
|
next: (layouts) => {
|
|
if (layouts.length === 0) {
|
|
// Document might still be processing in the background Celery worker
|
|
// Poll again after 2 seconds
|
|
setTimeout(() => this.fetchLayout(), 2000);
|
|
return;
|
|
}
|
|
this.layoutNodes = layouts;
|
|
// Trigger auto-recognition
|
|
this.autoRecognize();
|
|
}
|
|
});
|
|
}
|
|
|
|
autoRecognize() {
|
|
if (!this.documentId) return;
|
|
this.templateService.recognizeTemplate(this.documentId).subscribe({
|
|
next: (res) => {
|
|
if (res.templateMatched) {
|
|
this.messageService.add({ severity: 'info', summary: 'Template Recognized', detail: `Confidence: ${res.confidence}%` });
|
|
// In a full implementation, we'd load the template fields and populate the mappings.
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
showAddFieldDialog() {
|
|
this.newField = { field_label: '', field_type: 'TEXT', display_order: this.templateFields.length, required_flag: false };
|
|
this.displayAddField = true;
|
|
}
|
|
|
|
addField() {
|
|
if (!this.newField.field_label) return;
|
|
this.newField.pk_template_field_id = new Date().getTime(); // mock ID until saved
|
|
this.templateFields.push({ ...this.newField });
|
|
this.mappings[this.newField.pk_template_field_id] = [];
|
|
this.displayAddField = false;
|
|
}
|
|
|
|
saveTemplateAndMappings() {
|
|
if (!this.templateName || this.templateName.trim() === '') {
|
|
this.messageService.add({ severity: 'warn', summary: 'Warning', detail: 'Please provide a template name.' });
|
|
return;
|
|
}
|
|
|
|
if (this.templateFields.length === 0) {
|
|
this.messageService.add({ severity: 'warn', summary: 'Warning', detail: 'Please add at least one template field.' });
|
|
return;
|
|
}
|
|
|
|
this.templateService.createTemplate({
|
|
template_name: this.templateName,
|
|
fields: this.templateFields
|
|
}).subscribe({
|
|
next: (res) => {
|
|
// In a real scenario, we pass res.templateId and map the layoutNodes
|
|
this.messageService.add({ severity: 'success', summary: 'Success', detail: 'Template & Mappings saved successfully.' });
|
|
}
|
|
});
|
|
}
|
|
|
|
// Drag and Drop Logic
|
|
drop(event: CdkDragDrop<DocumentLayout[]>, fieldId?: number) {
|
|
if (event.previousContainer === event.container) {
|
|
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
|
|
} else {
|
|
transferArrayItem(
|
|
event.previousContainer.data,
|
|
event.container.data,
|
|
event.previousIndex,
|
|
event.currentIndex,
|
|
);
|
|
}
|
|
}
|
|
|
|
getConnectedDropLists() {
|
|
// Return all field ID strings as drop lists
|
|
return this.templateFields.map(f => 'field-' + f.pk_template_field_id);
|
|
}
|
|
}
|