Files
delphi-database/templates/documents.html
2025-08-08 15:55:15 -05:00

1149 lines
50 KiB
HTML

{% extends "base.html" %}
{% block title %}Document Management - Delphi Database{% endblock %}
{% block content %}
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-3">
<h2><i class="bi bi-file-earmark-text"></i> Document Management</h2>
<div>
<button class="btn btn-success" id="newTemplateBtn">
<i class="bi bi-plus-circle"></i> New Template (Ctrl+N)
</button>
<button class="btn btn-primary" id="generateDocBtn">
<i class="bi bi-file-plus"></i> Generate Document
</button>
<button class="btn btn-warning" id="newQdroBtn">
<i class="bi bi-file-earmark-ruled"></i> New QDRO
</button>
<button class="btn btn-info" id="statsBtn">
<i class="bi bi-graph-up"></i> Statistics
</button>
</div>
</div>
<!-- Document Management Tabs -->
<ul class="nav nav-tabs" id="documentTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="templates-tab" data-bs-toggle="tab" data-bs-target="#templates"
type="button" role="tab" aria-controls="templates" aria-selected="true">
<i class="bi bi-file-text"></i> Templates
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="qdros-tab" data-bs-toggle="tab" data-bs-target="#qdros"
type="button" role="tab" aria-controls="qdros" aria-selected="false">
<i class="bi bi-file-earmark-ruled"></i> QDROs
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="generated-tab" data-bs-toggle="tab" data-bs-target="#generated"
type="button" role="tab" aria-controls="generated" aria-selected="false">
<i class="bi bi-file-pdf"></i> Generated Documents
</button>
</li>
</ul>
<div class="tab-content" id="documentTabContent">
<!-- Templates Tab -->
<div class="tab-pane fade show active" id="templates" role="tabpanel" aria-labelledby="templates-tab">
<div class="card mt-3">
<div class="card-header">
<div class="row align-items-center">
<div class="col-md-8">
<h5 class="mb-0"><i class="bi bi-file-text"></i> Document Templates</h5>
</div>
<div class="col-md-4">
<div class="input-group">
<input type="text" class="form-control form-control-sm" id="templateSearch" placeholder="Search templates...">
<select class="form-select form-select-sm" id="categoryFilter">
<option value="">All Categories</option>
</select>
<button class="btn btn-outline-secondary btn-sm" id="refreshTemplatesBtn">
<i class="bi bi-arrow-clockwise"></i>
</button>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover" id="templatesTable">
<thead>
<tr>
<th>Template ID</th>
<th>Name</th>
<th>Category</th>
<th>Variables</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="templatesTableBody">
<!-- Templates will be loaded here -->
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- QDROs Tab -->
<div class="tab-pane fade" id="qdros" role="tabpanel" aria-labelledby="qdros-tab">
<div class="card mt-3">
<div class="card-header">
<div class="row align-items-center">
<div class="col-md-6">
<h5 class="mb-0"><i class="bi bi-file-earmark-ruled"></i> QDRO Documents</h5>
</div>
<div class="col-md-6">
<div class="input-group">
<input type="text" class="form-control form-control-sm" id="qdroSearch" placeholder="Search QDROs...">
<select class="form-select form-select-sm" id="qdroStatusFilter">
<option value="">All Status</option>
<option value="DRAFT">Draft</option>
<option value="APPROVED">Approved</option>
<option value="FILED">Filed</option>
</select>
<button class="btn btn-outline-secondary btn-sm" id="refreshQdrosBtn">
<i class="bi bi-arrow-clockwise"></i>
</button>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover" id="qdrosTable">
<thead>
<tr>
<th>File #</th>
<th>Version</th>
<th>Participant</th>
<th>Spouse</th>
<th>Plan Name</th>
<th>Status</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="qdrosTableBody">
<!-- QDROs will be loaded here -->
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Generated Documents Tab -->
<div class="tab-pane fade" id="generated" role="tabpanel" aria-labelledby="generated-tab">
<div class="card mt-3">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-file-pdf"></i> Generated Documents</h5>
</div>
<div class="card-body">
<div id="generatedDocuments">
<p class="text-muted">Generated documents will appear here...</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Template Editor Modal -->
<div class="modal fade" id="templateModal" tabindex="-1" aria-labelledby="templateModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="templateModalLabel">Template Editor</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="templateForm">
<div class="row g-3">
<div class="col-md-6">
<label for="templateId" class="form-label">Template ID *</label>
<input type="text" class="form-control" id="templateId" name="form_id" required>
</div>
<div class="col-md-6">
<label for="templateName" class="form-label">Template Name *</label>
<input type="text" class="form-control" id="templateName" name="form_name" required>
</div>
<div class="col-md-12">
<label for="templateCategory" class="form-label">Category</label>
<select class="form-select" id="templateCategory" name="category">
<option value="GENERAL">General</option>
<option value="LETTERS">Letters</option>
<option value="CONTRACTS">Contracts</option>
<option value="PLEADINGS">Pleadings</option>
<option value="FORMS">Forms</option>
<option value="NOTICES">Notices</option>
</select>
</div>
<div class="col-12">
<label for="templateContent" class="form-label">Template Content</label>
<div class="mb-2">
<small class="text-muted">
Use {{VARIABLE_NAME}} or ^VARIABLE for merge fields. Available variables: FILE_NO, CLIENT_FIRST, CLIENT_LAST, CLIENT_FULL, MATTER, OPENED, ATTORNEY, TODAY
</small>
</div>
<textarea class="form-control" id="templateContent" name="content" rows="15"
placeholder="Enter your template content here. Use {{CLIENT_FULL}} for client name, {{FILE_NO}} for file number, etc."></textarea>
</div>
<div class="col-12">
<div class="row">
<div class="col-md-6">
<button type="button" class="btn btn-outline-secondary btn-sm" id="insertVariableBtn">
<i class="bi bi-plus"></i> Insert Variable
</button>
<button type="button" class="btn btn-outline-info btn-sm" id="previewTemplateBtn">
<i class="bi bi-eye"></i> Preview
</button>
</div>
<div class="col-md-6 text-end">
<div id="variableCount" class="text-muted small">Variables detected: 0</div>
</div>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="saveTemplateBtn">
<i class="bi bi-check-circle"></i> Save Template
</button>
</div>
</div>
</div>
</div>
<!-- Document Generation Modal -->
<div class="modal fade" id="generateModal" tabindex="-1" aria-labelledby="generateModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="generateModalLabel">Generate Document</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="generateForm">
<div class="row g-3">
<div class="col-md-6">
<label for="generateTemplate" class="form-label">Select Template *</label>
<select class="form-select" id="generateTemplate" name="template_id" required>
<option value="">Choose template...</option>
</select>
</div>
<div class="col-md-6">
<label for="generateFile" class="form-label">File Number *</label>
<div class="input-group">
<input type="text" class="form-control" id="generateFile" name="file_no" required>
<button class="btn btn-outline-secondary" type="button" id="selectGenerateFileBtn">
<i class="bi bi-search"></i>
</button>
</div>
<div class="form-text" id="generateFileInfo">Enter file number or browse to select</div>
</div>
<div class="col-md-6">
<label for="outputFormat" class="form-label">Output Format</label>
<select class="form-select" id="outputFormat" name="output_format">
<option value="PDF">PDF</option>
<option value="DOCX">Word Document</option>
<option value="HTML">HTML</option>
</select>
</div>
<div class="col-md-6">
<div class="form-check mt-4">
<input class="form-check-input" type="checkbox" id="useCustomVars">
<label class="form-check-label" for="useCustomVars">
Use custom variables
</label>
</div>
</div>
<div class="col-12" id="customVariablesSection" style="display: none;">
<label class="form-label">Custom Variables</label>
<div id="customVariables">
<!-- Custom variables will be added here -->
</div>
<button type="button" class="btn btn-sm btn-outline-secondary" id="addVariableBtn">
<i class="bi bi-plus"></i> Add Variable
</button>
</div>
<div class="col-12">
<label for="templatePreview" class="form-label">Template Preview</label>
<textarea class="form-control" id="templatePreview" readonly rows="8" placeholder="Select a template to see preview..."></textarea>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="generateDocumentBtn">
<i class="bi bi-file-plus"></i> Generate Document
</button>
</div>
</div>
</div>
</div>
<!-- QDRO Modal -->
<div class="modal fade" id="qdroModal" tabindex="-1" aria-labelledby="qdroModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="qdroModalLabel">QDRO Editor</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="qdroForm">
<div class="row g-3">
<div class="col-md-6">
<label for="qdroFileNo" class="form-label">File Number *</label>
<div class="input-group">
<input type="text" class="form-control" id="qdroFileNo" name="file_no" required>
<button class="btn btn-outline-secondary" type="button" id="selectQdroFileBtn">
<i class="bi bi-search"></i>
</button>
</div>
</div>
<div class="col-md-3">
<label for="qdroVersion" class="form-label">Version</label>
<input type="text" class="form-control" id="qdroVersion" name="version" value="01">
</div>
<div class="col-md-3">
<label for="qdroStatus" class="form-label">Status</label>
<select class="form-select" id="qdroStatus" name="status">
<option value="DRAFT">Draft</option>
<option value="APPROVED">Approved</option>
<option value="FILED">Filed</option>
</select>
</div>
<div class="col-md-6">
<label for="qdroParticipant" class="form-label">Participant Name</label>
<input type="text" class="form-control" id="qdroParticipant" name="participant_name">
</div>
<div class="col-md-6">
<label for="qdroSpouse" class="form-label">Spouse Name</label>
<input type="text" class="form-control" id="qdroSpouse" name="spouse_name">
</div>
<div class="col-md-6">
<label for="qdroPlanName" class="form-label">Plan Name</label>
<input type="text" class="form-control" id="qdroPlanName" name="plan_name">
</div>
<div class="col-md-6">
<label for="qdroPlanAdmin" class="form-label">Plan Administrator</label>
<input type="text" class="form-control" id="qdroPlanAdmin" name="plan_administrator">
</div>
<div class="col-md-4">
<label for="qdroCreated" class="form-label">Created Date</label>
<input type="date" class="form-control" id="qdroCreated" name="created_date">
</div>
<div class="col-md-4">
<label for="qdroApproved" class="form-label">Approved Date</label>
<input type="date" class="form-control" id="qdroApproved" name="approved_date">
</div>
<div class="col-md-4">
<label for="qdroFiled" class="form-label">Filed Date</label>
<input type="date" class="form-control" id="qdroFiled" name="filed_date">
</div>
<div class="col-12">
<label for="qdroTitle" class="form-label">QDRO Title</label>
<input type="text" class="form-control" id="qdroTitle" name="title" placeholder="Enter QDRO title">
</div>
<div class="col-12">
<label for="qdroContent" class="form-label">QDRO Content</label>
<textarea class="form-control" id="qdroContent" name="content" rows="10" placeholder="Enter QDRO content or generate from template..."></textarea>
</div>
<div class="col-12">
<label for="qdroNotes" class="form-label">Notes</label>
<textarea class="form-control" id="qdroNotes" name="notes" rows="3" placeholder="Additional notes..."></textarea>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-warning" id="generateFromTemplateBtn">
<i class="bi bi-file-text"></i> Generate from Template
</button>
<button type="button" class="btn btn-primary" id="saveQdroBtn">
<i class="bi bi-check-circle"></i> Save QDRO
</button>
</div>
</div>
</div>
</div>
<!-- Statistics Modal -->
<div class="modal fade" id="statsModal" tabindex="-1" aria-labelledby="statsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="statsModalLabel">Document Statistics</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-6">
<div class="card bg-primary text-white">
<div class="card-body text-center">
<h3 id="totalTemplatesCount">0</h3>
<small>Total Templates</small>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card bg-success text-white">
<div class="card-body text-center">
<h3 id="totalQdrosCount">0</h3>
<small>Total QDROs</small>
</div>
</div>
</div>
</div>
<div class="row mt-3">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h6>Templates by Category</h6>
</div>
<div class="card-body">
<div id="categoriesBreakdown">
<!-- Categories will be loaded here -->
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h6>Recent Activity</h6>
</div>
<div class="card-body">
<div id="recentActivity">
<!-- Recent activity will be loaded here -->
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- Variable Selector Modal -->
<div class="modal fade" id="variableModal" tabindex="-1" aria-labelledby="variableModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="variableModalLabel">Insert Variable</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="list-group">
<button type="button" class="list-group-item list-group-item-action variable-item" data-var="FILE_NO">
<strong>{{FILE_NO}}</strong> - File Number
</button>
<button type="button" class="list-group-item list-group-item-action variable-item" data-var="CLIENT_FULL">
<strong>{{CLIENT_FULL}}</strong> - Full Client Name
</button>
<button type="button" class="list-group-item list-group-item-action variable-item" data-var="CLIENT_FIRST">
<strong>{{CLIENT_FIRST}}</strong> - Client First Name
</button>
<button type="button" class="list-group-item list-group-item-action variable-item" data-var="CLIENT_LAST">
<strong>{{CLIENT_LAST}}</strong> - Client Last Name
</button>
<button type="button" class="list-group-item list-group-item-action variable-item" data-var="MATTER">
<strong>{{MATTER}}</strong> - Matter Description
</button>
<button type="button" class="list-group-item list-group-item-action variable-item" data-var="OPENED">
<strong>{{OPENED}}</strong> - Date File Opened
</button>
<button type="button" class="list-group-item list-group-item-action variable-item" data-var="ATTORNEY">
<strong>{{ATTORNEY}}</strong> - Attorney/Employee
</button>
<button type="button" class="list-group-item list-group-item-action variable-item" data-var="TODAY">
<strong>{{TODAY}}</strong> - Today's Date
</button>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>
<script>
// Document Management JavaScript
document.addEventListener('DOMContentLoaded', function() {
// Initialize page
loadTemplates();
loadQdros();
loadCategories();
// Set up keyboard shortcuts
setupKeyboardShortcuts();
// Set up event handlers
setupEventHandlers();
// Auto-refresh every 30 seconds
setInterval(function() {
if (document.querySelector('#templates-tab').classList.contains('active')) {
loadTemplates();
} else if (document.querySelector('#qdros-tab').classList.contains('active')) {
loadQdros();
}
}, 30000);
});
function setupKeyboardShortcuts() {
document.addEventListener('keydown', function(e) {
// Ctrl+N for new template
if (e.ctrlKey && e.key === 'n') {
e.preventDefault();
document.getElementById('newTemplateBtn').click();
}
// Ctrl+G for generate document
if (e.ctrlKey && e.key === 'g') {
e.preventDefault();
document.getElementById('generateDocBtn').click();
}
});
}
function setupEventHandlers() {
// New template button
document.getElementById('newTemplateBtn').addEventListener('click', function() {
openTemplateModal();
});
// Generate document button
document.getElementById('generateDocBtn').addEventListener('click', function() {
openGenerateModal();
});
// New QDRO button
document.getElementById('newQdroBtn').addEventListener('click', function() {
openQdroModal();
});
// Statistics button
document.getElementById('statsBtn').addEventListener('click', function() {
loadDocumentStats();
});
// Save template button
document.getElementById('saveTemplateBtn').addEventListener('click', function() {
saveTemplate();
});
// Save QDRO button
document.getElementById('saveQdroBtn').addEventListener('click', function() {
saveQdro();
});
// Generate document button
document.getElementById('generateDocumentBtn').addEventListener('click', function() {
generateDocument();
});
// Insert variable button
document.getElementById('insertVariableBtn').addEventListener('click', function() {
const modal = new bootstrap.Modal(document.getElementById('variableModal'));
modal.show();
});
// Variable selector
document.querySelectorAll('.variable-item').forEach(item => {
item.addEventListener('click', function() {
const varName = this.dataset.var;
const textarea = document.getElementById('templateContent');
const cursorPos = textarea.selectionStart;
const textBefore = textarea.value.substring(0, cursorPos);
const textAfter = textarea.value.substring(textarea.selectionEnd, textarea.value.length);
textarea.value = textBefore + '{{' + varName + '}}' + textAfter;
textarea.focus();
textarea.setSelectionRange(cursorPos + varName.length + 4, cursorPos + varName.length + 4);
const modal = bootstrap.Modal.getInstance(document.getElementById('variableModal'));
modal.hide();
updateVariableCount();
});
});
// Template content change handler
document.getElementById('templateContent').addEventListener('input', function() {
updateVariableCount();
});
// Template selection change for generation
document.getElementById('generateTemplate').addEventListener('change', function() {
const templateId = this.value;
if (templateId) {
loadTemplatePreview(templateId);
} else {
document.getElementById('templatePreview').value = '';
}
});
// Custom variables checkbox
document.getElementById('useCustomVars').addEventListener('change', function() {
const section = document.getElementById('customVariablesSection');
section.style.display = this.checked ? 'block' : 'none';
});
// Add custom variable button
document.getElementById('addVariableBtn').addEventListener('click', function() {
addCustomVariableInput();
});
// Search and filter handlers
document.getElementById('templateSearch').addEventListener('input', debounce(loadTemplates, 300));
document.getElementById('categoryFilter').addEventListener('change', loadTemplates);
document.getElementById('qdroSearch').addEventListener('input', debounce(loadQdros, 300));
document.getElementById('qdroStatusFilter').addEventListener('change', loadQdros);
// Refresh buttons
document.getElementById('refreshTemplatesBtn').addEventListener('click', loadTemplates);
document.getElementById('refreshQdrosBtn').addEventListener('click', loadQdros);
}
async function loadTemplates() {
try {
const search = document.getElementById('templateSearch').value;
const category = document.getElementById('categoryFilter').value;
let url = '/api/documents/templates/?';
if (search) url += `search=${encodeURIComponent(search)}&`;
if (category) url += `category=${encodeURIComponent(category)}&`;
const response = await fetch(url);
if (!response.ok) throw new Error('Failed to load templates');
const templates = await response.json();
displayTemplates(templates);
} catch (error) {
console.error('Error loading templates:', error);
showAlert('Error loading templates: ' + error.message, 'danger');
}
}
function displayTemplates(templates) {
const tbody = document.getElementById('templatesTableBody');
tbody.innerHTML = '';
templates.forEach(template => {
const row = createTemplateRow(template);
tbody.appendChild(row);
});
}
function createTemplateRow(template) {
const row = document.createElement('tr');
const variableCount = Object.keys(template.variables || {}).length;
row.innerHTML = `
<td><code>${template.form_id}</code></td>
<td>${template.form_name}</td>
<td><span class="badge bg-secondary">${template.category}</span></td>
<td><span class="badge bg-info">${variableCount} vars</span></td>
<td>
<span class="badge ${template.active ? 'bg-success' : 'bg-warning'}">
${template.active ? 'Active' : 'Inactive'}
</span>
</td>
<td>
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-primary" onclick="editTemplate('${template.form_id}')" title="Edit">
<i class="bi bi-pencil"></i>
</button>
<button class="btn btn-outline-success" onclick="previewTemplate('${template.form_id}')" title="Preview">
<i class="bi bi-eye"></i>
</button>
<button class="btn btn-outline-info" onclick="generateFromTemplate('${template.form_id}')" title="Generate">
<i class="bi bi-file-plus"></i>
</button>
<button class="btn btn-outline-danger" onclick="deleteTemplate('${template.form_id}')" title="Delete">
<i class="bi bi-trash"></i>
</button>
</div>
</td>
`;
return row;
}
async function loadQdros() {
try {
const search = document.getElementById('qdroSearch').value;
const status = document.getElementById('qdroStatusFilter').value;
let url = '/api/documents/qdros/?';
if (search) url += `search=${encodeURIComponent(search)}&`;
if (status) url += `status_filter=${encodeURIComponent(status)}&`;
const response = await fetch(url);
if (!response.ok) throw new Error('Failed to load QDROs');
const qdros = await response.json();
displayQdros(qdros);
} catch (error) {
console.error('Error loading QDROs:', error);
showAlert('Error loading QDROs: ' + error.message, 'danger');
}
}
function displayQdros(qdros) {
const tbody = document.getElementById('qdrosTableBody');
tbody.innerHTML = '';
qdros.forEach(qdro => {
const row = createQdroRow(qdro);
tbody.appendChild(row);
});
}
function createQdroRow(qdro) {
const row = document.createElement('tr');
row.innerHTML = `
<td><code>${qdro.file_no}</code></td>
<td>${qdro.version}</td>
<td>${qdro.participant_name || ''}</td>
<td>${qdro.spouse_name || ''}</td>
<td>${qdro.plan_name || ''}</td>
<td>
<span class="badge ${getStatusBadgeClass(qdro.status)}">
${qdro.status}
</span>
</td>
<td>${qdro.created_date ? new Date(qdro.created_date).toLocaleDateString() : ''}</td>
<td>
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-primary" onclick="editQdro(${qdro.id})" title="Edit">
<i class="bi bi-pencil"></i>
</button>
<button class="btn btn-outline-info" onclick="viewQdro(${qdro.id})" title="View">
<i class="bi bi-eye"></i>
</button>
<button class="btn btn-outline-danger" onclick="deleteQdro(${qdro.id})" title="Delete">
<i class="bi bi-trash"></i>
</button>
</div>
</td>
`;
return row;
}
function getStatusBadgeClass(status) {
switch (status) {
case 'DRAFT': return 'bg-warning';
case 'APPROVED': return 'bg-success';
case 'FILED': return 'bg-primary';
default: return 'bg-secondary';
}
}
async function loadCategories() {
try {
const response = await fetch('/api/documents/categories/');
if (!response.ok) throw new Error('Failed to load categories');
const categories = await response.json();
const select = document.getElementById('categoryFilter');
const templateSelect = document.getElementById('templateCategory');
// Clear existing options except "All Categories"
select.innerHTML = '<option value="">All Categories</option>';
categories.forEach(category => {
const option = document.createElement('option');
option.value = category;
option.textContent = category;
select.appendChild(option);
});
} catch (error) {
console.error('Error loading categories:', error);
}
}
function openTemplateModal(templateId = null) {
const modal = new bootstrap.Modal(document.getElementById('templateModal'));
const form = document.getElementById('templateForm');
// Reset form
form.reset();
document.getElementById('templateModalLabel').textContent = templateId ? 'Edit Template' : 'New Template';
if (templateId) {
loadTemplateForEditing(templateId);
} else {
// Set today's date and generate ID
const today = new Date().toISOString().split('T')[0];
document.getElementById('templateId').value = 'TPL_' + Date.now();
}
modal.show();
updateVariableCount();
}
async function loadTemplateForEditing(templateId) {
try {
const response = await fetch(`/api/documents/templates/${templateId}`);
if (!response.ok) throw new Error('Failed to load template');
const template = await response.json();
document.getElementById('templateId').value = template.form_id;
document.getElementById('templateId').disabled = true; // Don't allow editing ID
document.getElementById('templateName').value = template.form_name;
document.getElementById('templateCategory').value = template.category;
document.getElementById('templateContent').value = template.content;
updateVariableCount();
} catch (error) {
console.error('Error loading template:', error);
showAlert('Error loading template: ' + error.message, 'danger');
}
}
async function saveTemplate() {
try {
const form = document.getElementById('templateForm');
const formData = new FormData(form);
const templateData = {
form_id: formData.get('form_id'),
form_name: formData.get('form_name'),
category: formData.get('category'),
content: formData.get('content')
};
const isEdit = document.getElementById('templateId').disabled;
const url = isEdit ? `/api/documents/templates/${templateData.form_id}` : '/api/documents/templates/';
const method = isEdit ? 'PUT' : 'POST';
const response = await fetch(url, {
method: method,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(templateData)
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to save template');
}
showAlert('Template saved successfully', 'success');
const modal = bootstrap.Modal.getInstance(document.getElementById('templateModal'));
modal.hide();
loadTemplates();
} catch (error) {
console.error('Error saving template:', error);
showAlert('Error saving template: ' + error.message, 'danger');
}
}
function updateVariableCount() {
const content = document.getElementById('templateContent').value;
const variables = extractVariables(content);
document.getElementById('variableCount').textContent = `Variables detected: ${variables.length}`;
}
function extractVariables(content) {
const regex = /\{\{([^}]+)\}\}/g;
const variables = [];
let match;
while ((match = regex.exec(content)) !== null) {
if (!variables.includes(match[1])) {
variables.push(match[1]);
}
}
return variables;
}
async function loadDocumentStats() {
try {
const response = await fetch('/api/documents/stats/summary');
if (!response.ok) throw new Error('Failed to load statistics');
const stats = await response.json();
document.getElementById('totalTemplatesCount').textContent = stats.total_templates;
document.getElementById('totalQdrosCount').textContent = stats.total_qdros;
// Display categories breakdown
const categoriesDiv = document.getElementById('categoriesBreakdown');
categoriesDiv.innerHTML = '';
Object.entries(stats.templates_by_category).forEach(([category, count]) => {
const div = document.createElement('div');
div.className = 'd-flex justify-content-between mb-1';
div.innerHTML = `<span>${category}</span><span class="badge bg-secondary">${count}</span>`;
categoriesDiv.appendChild(div);
});
// Display recent activity
const activityDiv = document.getElementById('recentActivity');
activityDiv.innerHTML = '';
if (stats.recent_activity.length === 0) {
activityDiv.innerHTML = '<p class="text-muted">No recent activity</p>';
} else {
stats.recent_activity.forEach(activity => {
const div = document.createElement('div');
div.className = 'mb-2 p-2 border rounded';
div.innerHTML = `
<small class="text-muted">${activity.type}</small><br>
<strong>File: ${activity.file_no}</strong><br>
<span class="badge ${getStatusBadgeClass(activity.status)}">${activity.status}</span>
`;
activityDiv.appendChild(div);
});
}
const modal = new bootstrap.Modal(document.getElementById('statsModal'));
modal.show();
} catch (error) {
console.error('Error loading statistics:', error);
showAlert('Error loading statistics: ' + error.message, 'danger');
}
}
// Utility functions
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
function showAlert(message, type = 'info') {
const alertDiv = document.createElement('div');
alertDiv.className = `alert alert-${type} alert-dismissible fade show position-fixed top-0 end-0 m-3`;
alertDiv.style.zIndex = '9999';
alertDiv.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.body.appendChild(alertDiv);
// Auto-dismiss after 5 seconds
setTimeout(() => {
if (alertDiv.parentNode) {
alertDiv.remove();
}
}, 5000);
}
// Placeholder functions for additional features
async function editTemplate(templateId) {
openTemplateModal(templateId);
}
async function previewTemplate(templateId) {
// Implement template preview
}
async function generateFromTemplate(templateId) {
document.getElementById('generateTemplate').value = templateId;
openGenerateModal();
}
async function deleteTemplate(templateId) {
if (confirm('Are you sure you want to delete this template?')) {
try {
const response = await fetch(`/api/documents/templates/${templateId}`, {
method: 'DELETE'
});
if (!response.ok) throw new Error('Failed to delete template');
showAlert('Template deleted successfully', 'success');
loadTemplates();
} catch (error) {
console.error('Error deleting template:', error);
showAlert('Error deleting template: ' + error.message, 'danger');
}
}
}
function openGenerateModal() {
const modal = new bootstrap.Modal(document.getElementById('generateModal'));
loadTemplatesForGeneration();
modal.show();
}
async function loadTemplatesForGeneration() {
try {
const response = await fetch('/api/documents/templates/');
if (!response.ok) throw new Error('Failed to load templates');
const templates = await response.json();
const select = document.getElementById('generateTemplate');
select.innerHTML = '<option value="">Choose template...</option>';
templates.forEach(template => {
const option = document.createElement('option');
option.value = template.form_id;
option.textContent = `${template.form_name} (${template.category})`;
select.appendChild(option);
});
} catch (error) {
console.error('Error loading templates:', error);
}
}
async function loadTemplatePreview(templateId) {
try {
const response = await fetch(`/api/documents/templates/${templateId}`);
if (!response.ok) throw new Error('Failed to load template');
const template = await response.json();
document.getElementById('templatePreview').value = template.content;
} catch (error) {
console.error('Error loading template preview:', error);
}
}
async function generateDocument() {
try {
const form = document.getElementById('generateForm');
const formData = new FormData(form);
const templateId = formData.get('template_id');
if (!templateId) {
showAlert('Please select a template', 'warning');
return;
}
const requestData = {
template_id: templateId,
file_no: formData.get('file_no'),
output_format: formData.get('output_format'),
variables: {}
};
// Add custom variables if any
if (document.getElementById('useCustomVars').checked) {
const customVarInputs = document.querySelectorAll('#customVariables .custom-var-input');
customVarInputs.forEach(input => {
const name = input.querySelector('.var-name').value;
const value = input.querySelector('.var-value').value;
if (name && value) {
requestData.variables[name] = value;
}
});
}
const response = await fetch(`/api/documents/generate/${templateId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestData)
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to generate document');
}
const result = await response.json();
showAlert(`Document generated successfully: ${result.file_name}`, 'success');
const modal = bootstrap.Modal.getInstance(document.getElementById('generateModal'));
modal.hide();
// Optionally trigger download
if (confirm('Document generated successfully. Download now?')) {
window.open(result.file_path, '_blank');
}
} catch (error) {
console.error('Error generating document:', error);
showAlert('Error generating document: ' + error.message, 'danger');
}
}
function addCustomVariableInput() {
const container = document.getElementById('customVariables');
const div = document.createElement('div');
div.className = 'row g-2 mb-2 custom-var-input';
div.innerHTML = `
<div class="col-md-5">
<input type="text" class="form-control form-control-sm var-name" placeholder="Variable name">
</div>
<div class="col-md-5">
<input type="text" class="form-control form-control-sm var-value" placeholder="Variable value">
</div>
<div class="col-md-2">
<button type="button" class="btn btn-sm btn-outline-danger" onclick="this.parentNode.parentNode.remove()">
<i class="bi bi-x"></i>
</button>
</div>
`;
container.appendChild(div);
}
function openQdroModal(qdroId = null) {
const modal = new bootstrap.Modal(document.getElementById('qdroModal'));
const form = document.getElementById('qdroForm');
form.reset();
document.getElementById('qdroModalLabel').textContent = qdroId ? 'Edit QDRO' : 'New QDRO';
if (!qdroId) {
document.getElementById('qdroCreated').value = new Date().toISOString().split('T')[0];
}
modal.show();
}
async function saveQdro() {
// Implement QDRO save functionality
showAlert('QDRO functionality will be implemented in the next phase', 'info');
}
async function editQdro(qdroId) {
openQdroModal(qdroId);
}
async function viewQdro(qdroId) {
// Implement QDRO view functionality
showAlert('QDRO view functionality will be implemented', 'info');
}
async function deleteQdro(qdroId) {
if (confirm('Are you sure you want to delete this QDRO?')) {
// Implement delete functionality
showAlert('QDRO delete functionality will be implemented', 'info');
}
}
</script>
{% endblock %}