Files
delphi-database-v2/static/js/custom.js
HotSwapp 950d261eb4 File Cabinet MVP: case detail with inline Ledger CRUD
- Extend Transaction with ledger fields (item_no, employee_number, t_code, t_type_l, quantity, rate, billed)
- Startup SQLite migration to add missing columns on transactions
- Ledger create/update/delete endpoints with validations and auto-compute Amount = Quantity × Rate
- Uniqueness: ensure (transaction_date, item_no) per case by auto-incrementing
- Compute case totals (billed/unbilled/overall) and display in case view
- Update case.html for master-detail ledger UI; add client-side auto-compute JS
- Enhance import_ledger_data to populate extended fields
- Close/Reopen actions retained; case detail sorting by date/item
- Auth: switch to pbkdf2_sha256 default (bcrypt fallback) and seed admin robustness

Tested in Docker: health OK, login OK, import ROLODEX/FILES OK, ledger create persisted and totals displayed.
2025-10-07 09:26:58 -05:00

101 lines
3.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Custom JavaScript for Delphi Database
document.addEventListener('DOMContentLoaded', function() {
// Initialize tooltips if any
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
// Auto-hide alerts after 5 seconds
var alerts = document.querySelectorAll('.alert:not(.alert-permanent)');
alerts.forEach(function(alert) {
setTimeout(function() {
var bsAlert = new bootstrap.Alert(alert);
bsAlert.close();
}, 5000);
});
// Confirm delete actions
var deleteButtons = document.querySelectorAll('[data-confirm-delete]');
deleteButtons.forEach(function(button) {
button.addEventListener('click', function(e) {
var message = this.getAttribute('data-confirm-delete') || 'Are you sure you want to delete this item?';
if (!confirm(message)) {
e.preventDefault();
}
});
});
// Form validation enhancement
var forms = document.querySelectorAll('.needs-validation');
Array.prototype.slice.call(forms).forEach(function(form) {
form.addEventListener('submit', function(event) {
if (!form.checkValidity()) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add('was-validated');
}, false);
});
// Dynamic form field enabling/disabling
var toggleFields = document.querySelectorAll('[data-toggle-field]');
toggleFields.forEach(function(element) {
element.addEventListener('change', function() {
var targetSelector = this.getAttribute('data-toggle-field');
var targetField = document.querySelector(targetSelector);
if (targetField) {
targetField.disabled = !this.checked;
}
});
});
// Auto-compute Amount = Quantity × Rate in ledger add form
var qtyInput = document.querySelector('form[action*="/ledger"] .js-qty');
var rateInput = document.querySelector('form[action*="/ledger"] .js-rate');
var amountInput = document.querySelector('form[action*="/ledger"] .js-amount');
function recomputeAmount() {
if (!qtyInput || !rateInput || !amountInput) return;
var q = parseFloat(qtyInput.value);
var r = parseFloat(rateInput.value);
if (!isNaN(q) && !isNaN(r)) {
var amt = (q * r);
amountInput.value = amt.toFixed(2);
}
}
if (qtyInput) qtyInput.addEventListener('input', recomputeAmount);
if (rateInput) rateInput.addEventListener('input', recomputeAmount);
});
// Utility functions
function formatDate(dateString) {
if (!dateString) return '';
var date = new Date(dateString);
return date.toLocaleDateString();
}
function formatCurrency(amount) {
if (!amount) return '$0.00';
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(amount);
}
function showLoading(button) {
if (button) {
button.disabled = true;
button.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Loading...';
}
}
function hideLoading(button, originalText) {
if (button) {
button.disabled = false;
button.innerHTML = originalText;
}
}