Project Contract Dashboard
Contracts Requiring Attention
No contracts require attention.
';
} else {
renewalsContainer.innerHTML = expiringSoon.sort((a,b) => new Date(a.ExpiryDate) - new Date(b.ExpiryDate)).map(c => {
const daysLeft = Math.ceil((new Date(c.ExpiryDate) - today) / (1000 * 60 * 60 * 24));
return `
${c.Name}
vs. ${c.Counterparty}
${daysLeft}d
`;
}).join('');
}
renderCharts();
}
function renderCharts() {
const statusColors = {'Active':'#16a34a', 'Draft':'#64748b', 'In Review':'#f59e0b', 'Expired':'#94a3b8', 'Terminated':'#ef4444'};
const statusCounts = contractsData.reduce((acc, c) => { acc[c.Status] = (acc[c.Status] || 0) + 1; return acc; }, {});
const statusCtx = document.getElementById('pcd-status-chart').getContext('2d');
if(charts.status) charts.status.destroy();
charts.status = new Chart(statusCtx, {
type: 'doughnut',
data: {
labels: Object.keys(statusCounts),
datasets: [{ data: Object.values(statusCounts), backgroundColor: Object.keys(statusCounts).map(s => statusColors[s]) }]
},
options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom' } } }
});
}
function renderContractsTable() {
const table = document.getElementById('contracts-table');
const headers = ['Contract Name', 'Counterparty', 'Type', 'Status', 'Effective Date', 'Expiry Date', 'Value ($)'];
table.innerHTML = `
${headers.map(h=>`| ${h} | `).join('')}Actions |
${contractsData.sort((a,b)=>new Date(b.ExpiryDate) - new Date(a.ExpiryDate)).map(c => `
|
|
|
|
|
|
|
|
`).join('')}
`;
}
// --- CRUD Functions (FIXED) ---
window.pcd_updateContract = (id, key, value) => {
const contract = contractsData.find(c => c.id === id);
if(contract) contract[key] = (key === 'Value') ? parseFloat(value) || 0 : value;
renderAll();
};
window.pcd_addContract = () => {
contractsData.push({ id: Date.now(), Name: 'New Contract', Counterparty: '', Type: TYPES[0], Status: 'Draft', EffectiveDate: new Date().toISOString().split('T')[0], ExpiryDate: '', Value: 0 });
renderAll();
};
window.pcd_removeContract = (id) => {
contractsData = contractsData.filter(c => c.id !== id);
renderAll();
};
function initialize() {
const today = new Date("2025-07-02");
const createDate = (offsetDays) => {
const date = new Date(today);
date.setDate(today.getDate() + offsetDays);
return date.toISOString().split('T')[0];
};
contractsData = [
{ id: 1, Name: 'Master Service Agreement', Counterparty: 'Innovate Corp', Type: 'Retainer', Status: 'Active', EffectiveDate: '2023-01-01', ExpiryDate: '2025-12-31', Value: 250000 },
{ id: 2, Name: 'Software License', Counterparty: 'AlphaWare', Type: 'Fixed Price', Status: 'Active', EffectiveDate: '2025-02-15', ExpiryDate: '2026-02-14', Value: 75000 },
{ id: 3, Name: 'Cloud Hosting Services', Counterparty: 'Cloud IO', Type: 'Time & Materials', Status: 'Active', EffectiveDate: '2024-08-01', ExpiryDate: createDate(45), Value: 120000 },
{ id: 4, Name: 'Consulting SOW', Counterparty: 'Strategy Partners', Type: 'Fixed Price', Status: 'Expired', EffectiveDate: '2024-06-01', ExpiryDate: '2025-05-31', Value: 50000 },
{ id: 5, Name: 'Office Lease', Counterparty: 'Metro Properties', Type: 'Fixed Price', Status: 'Active', EffectiveDate: '2022-01-01', ExpiryDate: createDate(25), Value: 180000 },
{ id: 6, Name: 'New Client Project', Counterparty: 'Beta Solutions', Type: 'Draft', EffectiveDate: createDate(10), ExpiryDate: createDate(375), Value: 150000 }
];
renderAll();
}
initialize();
});