${daysLeft} days
${plan.dueDate}
`;
});
if(upcomingList.innerHTML === '') {
upcomingList.innerHTML = `
No upcoming deadlines.
`;
}
renderStatusChart();
}
function renderStatusChart() {
const ctx = document.getElementById('statusChart').getContext('2d');
const statusCounts = state.statuses.reduce((acc, status) => {
acc[status] = state.plans.filter(p => p.status === status).length;
return acc;
}, {});
const chartData = {
labels: Object.keys(statusCounts),
datasets: [{
data: Object.values(statusCounts),
backgroundColor: ['#E5E7EB', '#3B82F6', '#10B981', '#A855F7'],
borderColor: '#FFFFFF',
borderWidth: 2
}]
};
if (statusChart) {
statusChart.data = chartData;
statusChart.update();
} else {
statusChart = new Chart(ctx, {
type: 'doughnut',
data: chartData,
options: {
responsive: true,
plugins: { legend: { position: 'bottom' } }
},
});
}
}
function renderPlanTable() {
const tableBody = document.getElementById('plan-table-body');
tableBody.innerHTML = '';
if(state.plans.length === 0) {
tableBody.innerHTML = `
| No mitigation tasks found. |
`;
return;
}
state.plans.forEach(plan => {
tableBody.innerHTML += `
|
${plan.strategy}
${plan.risk}
|
${plan.department} |
${plan.dueDate} |
${plan.status} |
|
`;
});
}
function renderConfigLists() {
const configMap = {
category: state.categories,
department: state.departments,
priority: state.priorities,
status: state.statuses
};
for (const [type, items] of Object.entries(configMap)) {
document.getElementById(`${type}-list`).innerHTML = items.map(item => `
${item}
`).join('');
}
}
function updateSelectOptions() {
const selects = {
'plan-category': state.categories,
'plan-department': state.departments,
'plan-priority': state.priorities,
'plan-status': state.statuses
};
for (const [id, options] of Object.entries(selects)) {
const selectEl = document.getElementById(id);
if(selectEl) selectEl.innerHTML = options.map(o => `
`).join('');
}
}
// --- EVENT HANDLERS ---
['category', 'department', 'priority', 'status'].forEach(type => {
document.getElementById(`${type}-form`).addEventListener('submit', (e) => handleConfigForm(e, `${type}s`));
});
function handleConfigForm(e, stateKey) {
e.preventDefault();
const input = e.target.querySelector('input');
const value = input.value.trim();
if (value && !state[stateKey].includes(value)) {
state[stateKey].push(value);
if(stateKey === 'priorities') state[stateKey].sort();
saveState();
renderConfigLists();
updateSelectOptions();
input.value = '';
}
}
planForm.addEventListener('submit', (e) => {
e.preventDefault();
const id = document.getElementById('plan-id').value;
const data = {
risk: document.getElementById('plan-risk').value,
category: document.getElementById('plan-category').value,
strategy: document.getElementById('plan-strategy').value,
department: document.getElementById('plan-department').value,
dueDate: document.getElementById('plan-due-date').value,
budget: parseFloat(document.getElementById('plan-budget').value),
priority: document.getElementById('plan-priority').value,
status: document.getElementById('plan-status').value,
};
if (id) {
const index = state.plans.findIndex(p => p.id == id);
if (index > -1) state.plans[index] = { ...state.plans[index], ...data };
} else {
data.id = state.nextPlanId++;
state.plans.push(data);
}
saveState();
renderAll();
closePlanModal();
});
// --- GLOBAL FUNCTIONS ---
const app = {
switchTab: (tabName) => {
state.currentTab = tabName;
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active', 'border-blue-500', 'text-blue-600'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
document.getElementById(`tab-btn-${tabName}`).classList.add('active', 'border-blue-500', 'text-blue-600');
document.getElementById(`tab-content-${tabName}`).classList.add('active');
},
navigateTabs: (dir) => {
const i = tabOrder.indexOf(state.currentTab);
const nextI = dir === 'next' ? (i + 1) % tabOrder.length : (i - 1 + tabOrder.length) % tabOrder.length;
app.switchTab(tabOrder[nextI]);
},
openPlanModal: (id = null) => {
planForm.reset();
document.getElementById('plan-id').value = '';
if (id) {
const plan = state.plans.find(p => p.id === id);
if(plan) {
document.getElementById('modal-title').textContent = 'Edit Mitigation Task';
Object.keys(plan).forEach(key => {
const el = document.getElementById(`plan-${key === 'id' ? 'id' : key}`);
if (el) el.value = plan[key];
});
}
} else {
document.getElementById('modal-title').textContent = 'Add New Mitigation Task';
}
planModal.classList.remove('hidden');
},
closePlanModal: () => planModal.classList.add('hidden'),
editPlan: (id) => app.openPlanModal(id),
deletePlan: (id) => {
if (confirm('Are you sure you want to delete this mitigation task?')) {
state.plans = state.plans.filter(p => p.id !== id);
saveState();
renderAll();
}
},
deleteConfigItem: (type, value) => {
const stateKey = `${type}s`;
const keyMap = { category: 'category', department: 'department', priority: 'priority', status: 'status' };
if (state.plans.some(p => p[keyMap[type]] === value)) {
alert(`Cannot delete. This ${type} is in use.`);
return;
}
state[stateKey] = state[stateKey].filter(item => item !== value);
saveState();
renderConfigLists();
updateSelectOptions();
},
generatePDF: () => {
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
if (typeof doc.autoTable !== 'function') return;
doc.setFontSize(18);
doc.text("Corporate Legal Risk Mitigation Plan Report", 14, 22);
doc.setFontSize(11);
doc.text(`Generated: ${new Date().toLocaleDateString()}`, 14, 30);
const totalBudget = state.plans.reduce((sum, p) => sum + p.budget, 0);
doc.autoTable({
startY: 40,
body: [
['Tasks In Progress', state.plans.filter(p => p.status === 'In Progress').length],
['Tasks Overdue', document.getElementById('dashboard-overdue').textContent],
['Tasks Completed', document.getElementById('dashboard-completed').textContent],
['Total Budget Allocated', formatCurrency(totalBudget)],
],
theme: 'striped',
});
const tableData = state.plans.map(p => [
p.risk,
p.strategy,
p.department,
p.dueDate,
formatCurrency(p.budget),
p.status
]);
doc.autoTable({
startY: doc.lastAutoTable.finalY + 15,
head: [['Risk', 'Mitigation Task', 'Assigned To', 'Due Date', 'Budget', 'Status']],
body: tableData,
theme: 'grid',
});
doc.save('Mitigation-Plan-Report.pdf');
}
};
window.app = app;
// Initial Load
loadState();
renderAll();
app.switchTab(state.currentTab);
});
// --- Make functions callable from inline HTML ---
function switchTab(tabName) { window.app.switchTab(tabName); }
function navigateTabs(direction) { window.app.navigateTabs(direction); }
function openPlanModal(id = null) { window.app.openPlanModal(id); }
function closePlanModal() { window.app.closePlanModal(); }
function generatePDF() { window.app.generatePDF(); }