mirror of
https://github.com/Solo-Web-Works/BillTrak.git
synced 2026-01-29 09:50:34 +00:00
✨feature: Added chart functionality
This commit is contained in:
133
css/style.css
133
css/style.css
@@ -521,9 +521,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
@layer utilities {
|
@layer utilities {
|
||||||
.collapse {
|
|
||||||
visibility: collapse;
|
|
||||||
}
|
|
||||||
.absolute {
|
.absolute {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
@@ -545,9 +542,6 @@
|
|||||||
.right-2 {
|
.right-2 {
|
||||||
right: calc(var(--spacing) * 2);
|
right: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
.col-span-4 {
|
|
||||||
grid-column: span 4 / span 4;
|
|
||||||
}
|
|
||||||
.container {
|
.container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@media (width >= 40rem) {
|
@media (width >= 40rem) {
|
||||||
@@ -569,18 +563,9 @@
|
|||||||
.mx-auto {
|
.mx-auto {
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
}
|
}
|
||||||
.mt-2 {
|
|
||||||
margin-top: calc(var(--spacing) * 2);
|
|
||||||
}
|
|
||||||
.mt-4 {
|
.mt-4 {
|
||||||
margin-top: calc(var(--spacing) * 4);
|
margin-top: calc(var(--spacing) * 4);
|
||||||
}
|
}
|
||||||
.mr-2 {
|
|
||||||
margin-right: calc(var(--spacing) * 2);
|
|
||||||
}
|
|
||||||
.mr-auto {
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
.mb-0 {
|
.mb-0 {
|
||||||
margin-bottom: calc(var(--spacing) * 0);
|
margin-bottom: calc(var(--spacing) * 0);
|
||||||
}
|
}
|
||||||
@@ -608,30 +593,9 @@
|
|||||||
.hidden {
|
.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.inline {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
.inline-block {
|
.inline-block {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
.inline-flex {
|
|
||||||
display: inline-flex;
|
|
||||||
}
|
|
||||||
.list-item {
|
|
||||||
display: list-item;
|
|
||||||
}
|
|
||||||
.table {
|
|
||||||
display: table;
|
|
||||||
}
|
|
||||||
.w-3 {
|
|
||||||
width: calc(var(--spacing) * 3);
|
|
||||||
}
|
|
||||||
.w-3\/4 {
|
|
||||||
width: calc(3/4 * 100%);
|
|
||||||
}
|
|
||||||
.w-5 {
|
|
||||||
width: calc(var(--spacing) * 5);
|
|
||||||
}
|
|
||||||
.w-5\/6 {
|
.w-5\/6 {
|
||||||
width: calc(5/6 * 100%);
|
width: calc(5/6 * 100%);
|
||||||
}
|
}
|
||||||
@@ -644,21 +608,6 @@
|
|||||||
.w-full {
|
.w-full {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.flex-1 {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.border-collapse {
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
.transform {
|
|
||||||
transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y);
|
|
||||||
}
|
|
||||||
.resize {
|
|
||||||
resize: both;
|
|
||||||
}
|
|
||||||
.list-disc {
|
|
||||||
list-style-type: disc;
|
|
||||||
}
|
|
||||||
.list-none {
|
.list-none {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
}
|
}
|
||||||
@@ -694,9 +643,6 @@
|
|||||||
margin-inline-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)));
|
margin-inline-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.overflow-hidden {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.rounded {
|
.rounded {
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
}
|
}
|
||||||
@@ -707,14 +653,6 @@
|
|||||||
border-style: var(--tw-border-style);
|
border-style: var(--tw-border-style);
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
}
|
}
|
||||||
.border-t {
|
|
||||||
border-top-style: var(--tw-border-style);
|
|
||||||
border-top-width: 1px;
|
|
||||||
}
|
|
||||||
.border-b {
|
|
||||||
border-bottom-style: var(--tw-border-style);
|
|
||||||
border-bottom-width: 1px;
|
|
||||||
}
|
|
||||||
.bg-black {
|
.bg-black {
|
||||||
background-color: var(--color-black);
|
background-color: var(--color-black);
|
||||||
}
|
}
|
||||||
@@ -727,9 +665,6 @@
|
|||||||
.bg-gray-100 {
|
.bg-gray-100 {
|
||||||
background-color: var(--color-gray-100);
|
background-color: var(--color-gray-100);
|
||||||
}
|
}
|
||||||
.bg-gray-300 {
|
|
||||||
background-color: var(--color-gray-300);
|
|
||||||
}
|
|
||||||
.bg-green-500 {
|
.bg-green-500 {
|
||||||
background-color: var(--color-green-500);
|
background-color: var(--color-green-500);
|
||||||
}
|
}
|
||||||
@@ -739,9 +674,6 @@
|
|||||||
.bg-white {
|
.bg-white {
|
||||||
background-color: var(--color-white);
|
background-color: var(--color-white);
|
||||||
}
|
}
|
||||||
.p-1 {
|
|
||||||
padding: calc(var(--spacing) * 1);
|
|
||||||
}
|
|
||||||
.p-2 {
|
.p-2 {
|
||||||
padding: calc(var(--spacing) * 2);
|
padding: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
@@ -751,9 +683,6 @@
|
|||||||
.p-6 {
|
.p-6 {
|
||||||
padding: calc(var(--spacing) * 6);
|
padding: calc(var(--spacing) * 6);
|
||||||
}
|
}
|
||||||
.px-1 {
|
|
||||||
padding-inline: calc(var(--spacing) * 1);
|
|
||||||
}
|
|
||||||
.px-2 {
|
.px-2 {
|
||||||
padding-inline: calc(var(--spacing) * 2);
|
padding-inline: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
@@ -766,9 +695,6 @@
|
|||||||
.py-0 {
|
.py-0 {
|
||||||
padding-block: calc(var(--spacing) * 0);
|
padding-block: calc(var(--spacing) * 0);
|
||||||
}
|
}
|
||||||
.py-1 {
|
|
||||||
padding-block: calc(var(--spacing) * 1);
|
|
||||||
}
|
|
||||||
.py-2 {
|
.py-2 {
|
||||||
padding-block: calc(var(--spacing) * 2);
|
padding-block: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
@@ -778,9 +704,6 @@
|
|||||||
.pl-0 {
|
.pl-0 {
|
||||||
padding-left: calc(var(--spacing) * 0);
|
padding-left: calc(var(--spacing) * 0);
|
||||||
}
|
}
|
||||||
.pl-5 {
|
|
||||||
padding-left: calc(var(--spacing) * 5);
|
|
||||||
}
|
|
||||||
.text-2xl {
|
.text-2xl {
|
||||||
font-size: var(--text-2xl);
|
font-size: var(--text-2xl);
|
||||||
line-height: var(--tw-leading, var(--text-2xl--line-height));
|
line-height: var(--tw-leading, var(--text-2xl--line-height));
|
||||||
@@ -819,9 +742,6 @@
|
|||||||
.text-white {
|
.text-white {
|
||||||
color: var(--color-white);
|
color: var(--color-white);
|
||||||
}
|
}
|
||||||
.underline {
|
|
||||||
text-decoration-line: underline;
|
|
||||||
}
|
|
||||||
.shadow {
|
.shadow {
|
||||||
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
||||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||||
@@ -834,10 +754,6 @@
|
|||||||
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
||||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||||
}
|
}
|
||||||
.outline {
|
|
||||||
outline-style: var(--tw-outline-style);
|
|
||||||
outline-width: 1px;
|
|
||||||
}
|
|
||||||
.hover\:bg-blue-600 {
|
.hover\:bg-blue-600 {
|
||||||
&:hover {
|
&:hover {
|
||||||
@media (hover: hover) {
|
@media (hover: hover) {
|
||||||
@@ -845,13 +761,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.hover\:bg-gray-400 {
|
|
||||||
&:hover {
|
|
||||||
@media (hover: hover) {
|
|
||||||
background-color: var(--color-gray-400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.hover\:bg-green-600 {
|
.hover\:bg-green-600 {
|
||||||
&:hover {
|
&:hover {
|
||||||
@media (hover: hover) {
|
@media (hover: hover) {
|
||||||
@@ -866,23 +775,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.hover\:text-gray-700 {
|
|
||||||
&:hover {
|
|
||||||
@media (hover: hover) {
|
|
||||||
color: var(--color-gray-700);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.md\:grid-cols-2 {
|
.md\:grid-cols-2 {
|
||||||
@media (width >= 48rem) {
|
@media (width >= 48rem) {
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.md\:grid-cols-4 {
|
|
||||||
@media (width >= 48rem) {
|
|
||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.md\:grid-cols-5 {
|
.md\:grid-cols-5 {
|
||||||
@media (width >= 48rem) {
|
@media (width >= 48rem) {
|
||||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||||
@@ -925,31 +822,6 @@
|
|||||||
animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
|
animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@property --tw-rotate-x {
|
|
||||||
syntax: "*";
|
|
||||||
inherits: false;
|
|
||||||
initial-value: rotateX(0);
|
|
||||||
}
|
|
||||||
@property --tw-rotate-y {
|
|
||||||
syntax: "*";
|
|
||||||
inherits: false;
|
|
||||||
initial-value: rotateY(0);
|
|
||||||
}
|
|
||||||
@property --tw-rotate-z {
|
|
||||||
syntax: "*";
|
|
||||||
inherits: false;
|
|
||||||
initial-value: rotateZ(0);
|
|
||||||
}
|
|
||||||
@property --tw-skew-x {
|
|
||||||
syntax: "*";
|
|
||||||
inherits: false;
|
|
||||||
initial-value: skewX(0);
|
|
||||||
}
|
|
||||||
@property --tw-skew-y {
|
|
||||||
syntax: "*";
|
|
||||||
inherits: false;
|
|
||||||
initial-value: skewY(0);
|
|
||||||
}
|
|
||||||
@property --tw-space-y-reverse {
|
@property --tw-space-y-reverse {
|
||||||
syntax: "*";
|
syntax: "*";
|
||||||
inherits: false;
|
inherits: false;
|
||||||
@@ -1024,8 +896,3 @@
|
|||||||
inherits: false;
|
inherits: false;
|
||||||
initial-value: 0 0 #0000;
|
initial-value: 0 0 #0000;
|
||||||
}
|
}
|
||||||
@property --tw-outline-style {
|
|
||||||
syntax: "*";
|
|
||||||
inherits: false;
|
|
||||||
initial-value: solid;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -179,6 +179,7 @@ try {
|
|||||||
ORDER BY payees.name ASC
|
ORDER BY payees.name ASC
|
||||||
");
|
");
|
||||||
$stmt->execute([$year]);
|
$stmt->execute([$year]);
|
||||||
|
|
||||||
$payeeAmounts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$payeeAmounts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
// Fetch overall YTD amount
|
// Fetch overall YTD amount
|
||||||
|
|||||||
@@ -48,12 +48,12 @@
|
|||||||
|
|
||||||
<!-- Chart & Totals -->
|
<!-- Chart & Totals -->
|
||||||
<div class="border p-4 rounded shadow bg-gray-100 grid grid-cols-1 lg:grid-cols-2 gap-4">
|
<div class="border p-4 rounded shadow bg-gray-100 grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||||
<div id="chartSection" class="bg-white p-4 rounded shadow mb-6">
|
<div id="ytdPieChartSection" class="bg-white p-4 rounded shadow">
|
||||||
<h3 class="text-xl font-bold mb-4">Chart</h3>
|
<h3 class="text-xl font-bold mb-4">YTD Chart</h3>
|
||||||
<canvas id="chart"></canvas>
|
<canvas id="ytdPieChart"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="ytdSection" class="bg-white p-4 rounded shadow mb-6">
|
<div id="ytdSection" class="bg-white p-4 rounded shadow">
|
||||||
<h3 class="text-xl font-bold mb-4">Year-to-Date Summary</h3>
|
<h3 class="text-xl font-bold mb-4">Year-to-Date Summary</h3>
|
||||||
|
|
||||||
<div id="ytdOverall" class="mb-4">
|
<div id="ytdOverall" class="mb-4">
|
||||||
|
|||||||
72
js/app.js
72
js/app.js
@@ -4,6 +4,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const billList = document.getElementById('billList');
|
const billList = document.getElementById('billList');
|
||||||
const form = document.getElementById('billForm');
|
const form = document.getElementById('billForm');
|
||||||
const currentYear = new Date().getFullYear();
|
const currentYear = new Date().getFullYear();
|
||||||
|
let ytdPieChart; // To hold the chart instance
|
||||||
|
|
||||||
// Populate the year dropdown
|
// Populate the year dropdown
|
||||||
async function loadYears() {
|
async function loadYears() {
|
||||||
@@ -140,10 +141,72 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function renderYtdPieChart(year) {
|
||||||
|
const response = await fetch(`/includes/api.php?action=getYtdAmounts&year=${year}`);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
// Extract payee amounts from the response
|
||||||
|
const payeeAmounts = data.payeeAmounts || [];
|
||||||
|
const overallTotal = data.overallAmount || 0;
|
||||||
|
|
||||||
|
if (overallTotal === 0) {
|
||||||
|
console.warn('Overall total is zero. No data available for the chart.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract labels (payee names) and data (YTD totals)
|
||||||
|
const labels = payeeAmounts.map(item => item.payeeName);
|
||||||
|
const totals = payeeAmounts.map(item => item.totalAmount);
|
||||||
|
|
||||||
|
// Get the chart canvas
|
||||||
|
const ctx = document.getElementById('ytdPieChart').getContext('2d');
|
||||||
|
|
||||||
|
// Destroy the existing chart if it exists
|
||||||
|
if (ytdPieChart) {
|
||||||
|
ytdPieChart.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new pie chart
|
||||||
|
ytdPieChart = new Chart(ctx, {
|
||||||
|
type: 'pie',
|
||||||
|
data: {
|
||||||
|
labels: labels,
|
||||||
|
datasets: [{
|
||||||
|
data: totals,
|
||||||
|
backgroundColor: [
|
||||||
|
'#003962', '#00607F', '#008B9C', '#00B8B9', '#00E6D6', '#00FFF4'
|
||||||
|
], // Add more colors if needed
|
||||||
|
hoverBackgroundColor: [
|
||||||
|
'#042c4c', '#004968', '#007384', '#009FA1', '#00CCBD', '#00FBDA'
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'bottom'
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label: function (tooltipItem) {
|
||||||
|
const value = tooltipItem.raw; // Raw data for this item
|
||||||
|
const percentage = ((value / overallTotal) * 100).toFixed(2); // Calculate percentage
|
||||||
|
return ` $${value.toFixed(2)} (${percentage}%)`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Add event listener for year selection
|
// Add event listener for year selection
|
||||||
yearSelect.addEventListener('change', (e) => {
|
yearSelect.addEventListener('change', (e) => {
|
||||||
const selectedYear = e.target.value;
|
const selectedYear = e.target.value;
|
||||||
loadYtdAmounts(selectedYear);
|
loadYtdAmounts(selectedYear); // Update YTD amounts when the year changes
|
||||||
|
renderYtdPieChart(selectedYear); // Update the pie chart when the year changes
|
||||||
loadBills(selectedYear, sortSelect.value); // Use the selected sort order
|
loadBills(selectedYear, sortSelect.value); // Use the selected sort order
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -229,7 +292,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// On page load
|
// On page load
|
||||||
loadPayees('payeeId');
|
loadPayees('payeeId'); // Load payees
|
||||||
loadYears();
|
loadYears(); // Load years
|
||||||
loadYtdAmounts(currentYear);
|
loadYtdAmounts(currentYear); // Load YTD amounts for the current year
|
||||||
|
renderYtdPieChart(currentYear); // Render the pie chart for the current year
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,3 +2,4 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
/* Basic project styles */
|
/* Basic project styles */
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user