1149 lines
50 KiB
HTML
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 %} |