|
|
document.addEventListener('DOMContentLoaded', function() { |
|
|
|
|
|
const expenses = [ |
|
|
{ id: 1, software: 'CSI [Etabs, SAFE, SAP2000]', invoiceDate: 'July 9, 2020', cost: 12176.60, renewalDate: 'July 9, 2020' }, |
|
|
{ id: 2, software: 'Plaxis', invoiceDate: 'September 1, 2020', cost: 5361.00, renewalDate: 'September 1, 2020' }, |
|
|
{ id: 3, software: 'Tekla', invoiceDate: 'December 1, 2020', cost: 13503.40, renewalDate: 'December 1, 2020' }, |
|
|
{ id: 4, software: 'CSI [Etabs, SAFE, SAP2000]', invoiceDate: 'July 9, 2021', cost: 11877.00, renewalDate: 'July 9, 2021' }, |
|
|
{ id: 5, software: 'Plaxis', invoiceDate: 'September 1, 2021', cost: 5391.00, renewalDate: 'September 1, 2021' }, |
|
|
{ id: 6, software: 'Tekla', invoiceDate: 'December 1, 2021', cost: 6868.60, renewalDate: 'December 1, 2021' }, |
|
|
{ id: 7, software: 'CSI [Etabs, SAFE, SAP2000]', invoiceDate: 'August 10, 2022', cost: 13482.00, renewalDate: 'August 10, 2022' }, |
|
|
{ id: 8, software: 'Plaxis', invoiceDate: 'September 5, 2022', cost: 5796.00, renewalDate: 'September 5, 2022' }, |
|
|
{ id: 9, software: 'Tekla', invoiceDate: 'October 18, 2022', cost: 7130.37, renewalDate: 'December 1, 2022' }, |
|
|
{ id: 10, software: 'CSI [Etabs, SAFE, SAP2000]', invoiceDate: 'July 4, 2023', cost: 11664.00, renewalDate: 'July 4, 2023' }, |
|
|
{ id: 11, software: 'Plaxis', invoiceDate: 'September 21, 2023', cost: 6201.00, renewalDate: 'September 5, 2023' }, |
|
|
{ id: 12, software: 'Tekla', invoiceDate: 'October 18, 2023', cost: 7792.20, renewalDate: 'December 1, 2023' }, |
|
|
{ id: 13, software: 'CSI [Etabs, SAFE, SAP2000]', invoiceDate: 'July 4, 2024', cost: 11336.00, renewalDate: 'July 4, 2024' }, |
|
|
{ id: 14, software: 'Plaxis', invoiceDate: 'September 18, 2024', cost: 7003.40, renewalDate: '18/9/2024' }, |
|
|
{ id: 15, software: 'Tekla', invoiceDate: 'October 10, 2024', cost: 8316.00, renewalDate: '10/10/2024' }, |
|
|
{ id: 16, software: 'CSI [Etabs, SAFE, SAP2000]', invoiceDate: 'May 22, 2025', cost: 11336.00, renewalDate: 'May 22, 2025' }, |
|
|
{ id: 17, software: 'Unreal Engine', invoiceDate: '6-Sept-24', cost: 2727.18, renewalDate: '6-Sept-24' }, |
|
|
{ id: 18, software: 'Plaxis', invoiceDate: 'September 18, 2025', cost: 7752.84, renewalDate: '18/9/2025' } |
|
|
]; |
|
|
|
|
|
|
|
|
const tableBody = document.getElementById('expenseTable'); |
|
|
expenses.forEach(expense => { |
|
|
const row = document.createElement('tr'); |
|
|
row.className = 'hover:bg-gray-50 fade-in'; |
|
|
row.innerHTML = ` |
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${expense.id}</td> |
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${expense.software}</td> |
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${expense.invoiceDate}</td> |
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${expense.cost.toLocaleString('en-SG', {style: 'currency', currency: 'SGD'})}</td> |
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${expense.renewalDate}</td> |
|
|
`; |
|
|
tableBody.appendChild(row); |
|
|
}); |
|
|
|
|
|
|
|
|
const years = ['2020', '2021', '2022', '2023', '2024', '2025']; |
|
|
const annualCosts = years.map(year => { |
|
|
return expenses |
|
|
.filter(expense => expense.renewalDate.includes(year)) |
|
|
.reduce((sum, expense) => sum + expense.cost, 0); |
|
|
}); |
|
|
|
|
|
const softwareNames = [...new Set(expenses.map(expense => expense.software))]; |
|
|
const softwareCosts = softwareNames.map(software => { |
|
|
return expenses |
|
|
.filter(expense => expense.software === software) |
|
|
.reduce((sum, expense) => sum + expense.cost, 0); |
|
|
}); |
|
|
|
|
|
|
|
|
createAnnualChart(years, annualCosts); |
|
|
createSoftwareChart(softwareNames, softwareCosts); |
|
|
}); |
|
|
|
|
|
function createAnnualChart(years, costs) { |
|
|
const ctx = document.getElementById('annualChart').getContext('2d'); |
|
|
new Chart(ctx, { |
|
|
type: 'bar', |
|
|
data: { |
|
|
labels: years, |
|
|
datasets: [{ |
|
|
label: 'Annual Software Costs (SGD)', |
|
|
data: costs, |
|
|
backgroundColor: '#3B82F6', |
|
|
borderColor: '#2563EB', |
|
|
borderWidth: 1 |
|
|
}] |
|
|
}, |
|
|
options: { |
|
|
responsive: true, |
|
|
plugins: { |
|
|
legend: { |
|
|
position: 'top', |
|
|
}, |
|
|
tooltip: { |
|
|
callbacks: { |
|
|
label: function(context) { |
|
|
return context.parsed.y.toLocaleString('en-SG', {style: 'currency', currency: 'SGD'}); |
|
|
} |
|
|
} |
|
|
} |
|
|
}, |
|
|
scales: { |
|
|
y: { |
|
|
beginAtZero: true, |
|
|
ticks: { |
|
|
callback: function(value) { |
|
|
return value.toLocaleString('en-SG', {style: 'currency', currency: 'SGD'}); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
function createSoftwareChart(softwareNames, costs) { |
|
|
const ctx = document.getElementById('softwareChart').getContext('2d'); |
|
|
new Chart(ctx, { |
|
|
type: 'pie', |
|
|
data: { |
|
|
labels: softwareNames, |
|
|
datasets: [{ |
|
|
data: costs, |
|
|
backgroundColor: [ |
|
|
'#3B82F6', |
|
|
'#10B981', |
|
|
'#F59E0B', |
|
|
'#EF4444', |
|
|
'#8B5CF6' |
|
|
], |
|
|
borderWidth: 1 |
|
|
}] |
|
|
}, |
|
|
options: { |
|
|
responsive: true, |
|
|
plugins: { |
|
|
legend: { |
|
|
position: 'right', |
|
|
}, |
|
|
tooltip: { |
|
|
callbacks: { |
|
|
label: function(context) { |
|
|
const label = context.label || ''; |
|
|
const value = context.raw || 0; |
|
|
const total = context.dataset.data.reduce((a, b) => a + b, 0); |
|
|
const percentage = Math.round((value / total) * 100); |
|
|
return `${label}: ${value.toLocaleString('en-SG', {style: 'currency', currency: 'SGD'})} (${percentage}%)`; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
} |