OCR Template mapping done Now lets work on user and permission modules

This commit is contained in:
2026-07-14 16:49:54 +05:30
parent c725014788
commit b812d3cb82
4 changed files with 143 additions and 4 deletions

View File

@@ -13,12 +13,26 @@
</div>
</div>
<div class="preview-area p-4" style="overflow-y: auto;">
<div *ngIf="layoutNodes.length === 0" class="empty-state">
<i class="pi pi-file-pdf text-4xl mb-3"></i>
<div class="preview-area p-4" style="overflow-y: auto; position: relative;">
<div *ngIf="layoutNodes.length === 0 && !isScanning" class="empty-state">
<i class="pi pi-file text-4xl mb-3"></i>
<p>Upload a document to view its extracted layout structure.</p>
</div>
<!-- Scanning Overlay -->
<div *ngIf="isScanning" class="scanning-overlay">
<div class="scanner-container">
<i class="pi pi-file text-6xl text-gray-400 mb-4 scanner-doc"></i>
<div class="laser-beam"></div>
<div class="scanner-grid"></div>
</div>
<div class="scanning-text mt-4">
<i class="pi pi-spin pi-cog mr-2"></i>
<span class="font-semibold text-lg text-primary">Analyzing Document Layout...</span>
</div>
<p class="text-sm text-gray-500 mt-2">Extracting text, tables, and regions</p>
</div>
<div *ngFor="let page of pages; let i = index" class="document-page-container mb-5">
<div class="page-header text-sm font-semibold text-gray-500 mb-2">Page {{ page.page_number }}</div>

View File

@@ -225,3 +225,74 @@
.cdk-drag-animating {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
/* Scanning Overlay Animation */
.scanning-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.9);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 100;
}
.scanner-container {
position: relative;
width: 120px;
height: 120px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
overflow: hidden;
background: #f8f9fa;
box-shadow: inset 0 0 10px rgba(0,0,0,0.05);
border: 1px solid #e9ecef;
}
.scanner-doc {
z-index: 1;
}
.laser-beam {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 3px;
background: var(--primary-color, #3b82f6);
box-shadow: 0 0 10px 2px var(--primary-color, #3b82f6);
z-index: 2;
animation: scan-laser 2s ease-in-out infinite alternate;
}
.scanner-grid {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: linear-gradient(var(--primary-100, #dbeafe) 1px, transparent 1px),
linear-gradient(90deg, var(--primary-100, #dbeafe) 1px, transparent 1px);
background-size: 10px 10px;
z-index: 0;
opacity: 0.5;
animation: scan-grid 4s linear infinite;
}
@keyframes scan-laser {
0% { top: -5%; opacity: 0; }
10% { opacity: 1; }
90% { opacity: 1; }
100% { top: 105%; opacity: 0; }
}
@keyframes scan-grid {
0% { background-position: 0 0; }
100% { background-position: 20px 20px; }
}

View File

@@ -55,6 +55,7 @@ export class TemplatesComponent implements OnInit {
{ label: 'TEXT', value: 'TEXT' },
{ label: 'NUMBER', value: 'NUMBER' },
{ label: 'DATE', value: 'DATE' },
{ label: 'DATETIME', value: 'DATETIME' },
{ label: 'AMOUNT', value: 'AMOUNT' },
{ label: 'ADDRESS', value: 'ADDRESS' },
{ label: 'TABLE_COLUMN', value: 'TABLE_COLUMN' }
@@ -79,6 +80,7 @@ export class TemplatesComponent implements OnInit {
next: (res) => {
this.documentId = res.id || res.pk_document_id;
this.messageService.add({ severity: 'success', summary: 'Uploaded', detail: 'Document uploaded successfully.' });
this.isScanning = true;
this.fetchLayout();
},
error: (err) => {
@@ -98,6 +100,8 @@ export class TemplatesComponent implements OnInit {
setTimeout(() => this.fetchLayout(), 2000);
return;
}
this.isScanning = false;
this.layoutNodes = layouts;
const pageMap = new Map<number, { page_number: number, nodes: DocumentLayout[], width: number, height: number }>();
@@ -300,6 +304,27 @@ export class TemplatesComponent implements OnInit {
} else {
clonedNode.text_value = '';
}
} else if (field && (field.field_type === 'DATE' || field.field_type === 'DATETIME') && clonedNode.text_value) {
// Attempt to parse the date and output standard format
const timestamp = Date.parse(clonedNode.text_value);
if (!isNaN(timestamp)) {
const parsedDate = new Date(timestamp);
// Extract YYYY-MM-DD
const yyyy = parsedDate.getFullYear();
const mm = String(parsedDate.getMonth() + 1).padStart(2, '0');
const dd = String(parsedDate.getDate()).padStart(2, '0');
if (field.field_type === 'DATE') {
clonedNode.text_value = `${yyyy}-${mm}-${dd}`;
} else {
const hh = String(parsedDate.getHours()).padStart(2, '0');
const min = String(parsedDate.getMinutes()).padStart(2, '0');
const ss = String(parsedDate.getSeconds()).padStart(2, '0');
clonedNode.text_value = `${yyyy}-${mm}-${dd}T${hh}:${min}:${ss}`;
}
} else {
// If we can't parse it reliably on frontend, we leave it or let backend handle it
// We'll leave it as is to give user visual feedback, backend parser is more robust (dateutil)
}
}
}