Complexity: ${c.complexity}
`).join('');
if (assignedCases.length === 0) {
casesHtml = '
`;
container.innerHTML += attorneyCard;
});
};
/**
* Renders the list of unassigned cases on the dashboard.
*/
const renderDashboardUnassignedCases = () => {
const container = document.getElementById('unassigned-cases-container');
if (!container) return; // Null check
const unassignedCases = cases.filter(c => c.attorneyId === null);
container.innerHTML = '';
if (unassignedCases.length === 0) {
container.innerHTML = '
`;
container.innerHTML += caseCard;
});
};
/**
* Renders the editable table of attorneys in the configuration tab.
*/
const renderConfigAttorneys = () => {
const container = document.getElementById('attorney-list-container');
if (!container) return; // Null check
let tableHtml = `
`;
container.innerHTML = tableHtml;
};
/**
* Renders the editable table of unassigned cases in the configuration tab.
*/
const renderConfigCases = () => {
const container = document.getElementById('case-list-container');
if (!container) return; // Null check
const unassignedCases = cases.filter(c => c.attorneyId === null);
let tableHtml = `
`;
container.innerHTML = tableHtml;
};
/**
* Master render function to update the entire UI.
*/
const renderAll = () => {
renderDashboardAttorneys();
renderDashboardUnassignedCases();
renderConfigAttorneys();
renderConfigCases();
};
// --- EVENT HANDLERS & FORM LOGIC ---
function handleAttorneySave(e) {
e.preventDefault();
const id = document.getElementById('attorney-id').value;
const name = document.getElementById('attorney-name').value;
const specialty = document.getElementById('attorney-specialty').value;
const capacity = parseInt(document.getElementById('attorney-capacity').value);
if (id) { // Update existing
const index = attorneys.findIndex(a => a.id == id);
if (index !== -1) {
attorneys[index] = { ...attorneys[index], name, specialty, capacity };
}
} else { // Add new
attorneys.push({ id: getNextId(attorneys), name, specialty, capacity });
}
clearAttorneyForm();
renderAll();
}
window.clearAttorneyForm = () => {
attorneyForm.reset();
document.getElementById('attorney-id').value = '';
};
window.editAttorney = (id) => {
const attorney = attorneys.find(a => a.id === id);
if (attorney) {
document.getElementById('attorney-id').value = attorney.id;
document.getElementById('attorney-name').value = attorney.name;
document.getElementById('attorney-specialty').value = attorney.specialty;
document.getElementById('attorney-capacity').value = attorney.capacity;
document.getElementById('attorney-name').focus();
}
};
window.deleteAttorney = (id) => {
if (cases.some(c => c.attorneyId === id)) {
// Simple validation; a more robust app would use a custom modal
console.warn("Cannot delete attorney with assigned cases. Please reassign cases first.");
// In a real app, you would show a user-friendly message here instead of an alert.
// For now, we will prevent deletion and log to console.
return;
}
attorneys = attorneys.filter(a => a.id !== id);
renderAll();
};
function handleCaseSave(e) {
e.preventDefault();
const id = document.getElementById('case-id').value;
const name = document.getElementById('case-name').value;
const type = document.getElementById('case-type').value;
const complexity = parseInt(document.getElementById('case-complexity').value);
const value = parseFloat(document.getElementById('case-value').value);
if (id) { // Update
const index = cases.findIndex(c => c.id == id);
if (index !== -1) {
cases[index] = { ...cases[index], name, type, complexity, value };
}
} else { // Add new
cases.push({ id: getNextId(cases), name, type, complexity, value, attorneyId: null });
}
clearCaseForm();
renderAll();
}
window.clearCaseForm = () => {
caseForm.reset();
document.getElementById('case-id').value = '';
};
window.editCase = (id) => {
const caseItem = cases.find(c => c.id === id);
if (caseItem) {
document.getElementById('case-id').value = caseItem.id;
document.getElementById('case-name').value = caseItem.name;
document.getElementById('case-type').value = caseItem.type;
document.getElementById('case-complexity').value = caseItem.complexity;
document.getElementById('case-value').value = caseItem.value;
document.getElementById('case-name').focus();
}
};
window.deleteCase = (id) => {
cases = cases.filter(c => c.id !== id);
renderAll();
};
window.assignCase = (caseId) => {
const selectElement = document.getElementById(`assign-attorney-${caseId}`);
if (!selectElement) return;
const attorneyId = parseInt(selectElement.value);
if (isNaN(attorneyId) || attorneyId === "") return;
const caseIndex = cases.findIndex(c => c.id === caseId);
if (caseIndex !== -1) {
cases[caseIndex].attorneyId = attorneyId;
renderAll();
}
};
// --- TAB NAVIGATION ---
window.changeTab = (tabIndex) => {
currentTab = tabIndex;
const tabs = document.querySelectorAll('.tab');
const tabContents = document.querySelectorAll('.tab-content');
tabs.forEach((tab, index) => {
if (index === tabIndex) {
tab.classList.remove('tab-inactive');
tab.classList.add('tab-active');
} else {
tab.classList.remove('tab-active');
tab.classList.add('tab-inactive');
}
});
tabContents.forEach((content, index) => {
content.style.display = index === tabIndex ? 'block' : 'none';
});
};
window.navigateTab = (direction) => {
const numTabs = document.querySelectorAll('.tab').length;
let newTab = currentTab + direction;
if (newTab < 0) newTab = numTabs - 1;
if (newTab >= numTabs) newTab = 0;
changeTab(newTab);
};
// --- PDF DOWNLOAD FUNCTIONALITY ---
window.downloadPDF = () => {
const { jsPDF } = window.jspdf;
const doc = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' });
doc.setFontSize(18);
doc.text('Attorney Workload & Case Allocation Summary', 14, 22);
doc.setFontSize(11);
doc.setTextColor(100);
doc.text(`Generated on: ${new Date().toLocaleDateString('en-US')}`, 14, 30);
const head = [['Attorney', 'Specialty', 'Assigned Cases', 'Workload (Points)', 'Capacity']];
const body = attorneys.map(attorney => {
const assignedCases = cases.filter(c => c.attorneyId === attorney.id);
const currentLoad = assignedCases.reduce((sum, c) => sum + c.complexity, 0);
const caseList = assignedCases.map(c => `- ${c.name} (C: ${c.complexity})`).join('\n') || 'None';
return [
attorney.name,
attorney.specialty,
caseList,
`${currentLoad}`,
`${attorney.capacity}`
];
});
doc.autoTable({
startY: 40,
head: head,
body: body,
theme: 'striped',
headStyles: { fillColor: [22, 160, 133] },
styles: { cellPadding: 2, fontSize: 9 },
didDrawCell: (data) => {
// For multiline text in 'Assigned Cases' column
if (data.column.index === 2 && data.cell.section === 'body') {
doc.autoTableText(data.cell.text, data.cell.x + 2, data.cell.y + 2, {
halign: 'left',
valign: 'top'
});
}
}
});
const unassignedCases = cases.filter(c => c.attorneyId === null);
if (unassignedCases.length > 0) {
const unassignedHead = [['Case Name / Client', 'Type', 'Complexity', 'Value']];
const unassignedBody = unassignedCases.map(c => [c.name, c.type, c.complexity, formatCurrency(c.value)]);
doc.autoTable({
startY: doc.autoTable.previous.finalY + 15,
head: [['Unassigned Cases']],
theme: 'plain',
styles: { fontSize: 14, fontStyle: 'bold' }
});
doc.autoTable({
startY: doc.autoTable.previous.finalY + 2,
head: unassignedHead,
body: unassignedBody,
theme: 'grid',
headStyles: { fillColor: [44, 62, 80] },
});
}
doc.save('Attorney_Workload_Summary.pdf');
};
// --- INITIAL RENDER ---
renderAll();
});
No cases assigned.
'; } const attorneyCard = `${attorney.name}
${attorney.specialty}
Workload: ${currentLoad} / ${attorney.capacity}
Assigned Cases:
- ${casesHtml}
No unassigned cases.
';
return;
}
unassignedCases.forEach(c => {
const attorneyOptions = attorneys.map(a => ``).join('');
const caseCard = `
${c.name}
${c.type}
Complexity: ${c.complexity}
Value: ${formatCurrency(c.value)}
| Name | Specialty | Capacity | Actions |
|---|---|---|---|
| No attorneys added. | |||
| ${a.name} | ${a.specialty} | ${a.capacity} | |
| Case Name | Type | Complexity | Actions |
|---|---|---|---|
| No unassigned cases. | |||
| ${c.name} | ${c.type} | ${c.complexity} | |
