mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-25 00:33:58 -04:00
Tentatively finished up the tabbed area with weekly/monthly/yearly stacked bar charts with dummy data
This commit is contained in:
192
app/assets/javascripts/dashboardVisualization.js
Normal file
192
app/assets/javascripts/dashboardVisualization.js
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Define constants
|
||||||
|
const COLORS = {
|
||||||
|
delivered: '#0076d6',
|
||||||
|
failed: '#fa9441',
|
||||||
|
text: '#666'
|
||||||
|
};
|
||||||
|
|
||||||
|
const FONT_SIZE = 16;
|
||||||
|
const FONT_WEIGHT = 'bold';
|
||||||
|
const MAX_Y = 120;
|
||||||
|
|
||||||
|
const tabButtons = document.querySelectorAll('.tablinks');
|
||||||
|
let monthlyChartInitialized = false;
|
||||||
|
let yearlyChartInitialized = false;
|
||||||
|
|
||||||
|
|
||||||
|
// Function to create a chart
|
||||||
|
function createChart(ctx, labels, deliveredData, failedData) {
|
||||||
|
return new Chart(ctx, {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: labels,
|
||||||
|
datasets: [{
|
||||||
|
label: 'Delivered',
|
||||||
|
data: deliveredData,
|
||||||
|
backgroundColor: COLORS.delivered,
|
||||||
|
stack: 'Stack 0'
|
||||||
|
}, {
|
||||||
|
label: 'Failed',
|
||||||
|
data: failedData,
|
||||||
|
backgroundColor: COLORS.failed,
|
||||||
|
stack: 'Stack 0'
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
stacked: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
stacked: true,
|
||||||
|
beginAtZero: true,
|
||||||
|
max: MAX_Y,
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'SMS Sent',
|
||||||
|
color: COLORS.text,
|
||||||
|
font: {
|
||||||
|
size: FONT_SIZE,
|
||||||
|
weight: FONT_WEIGHT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'top',
|
||||||
|
align: 'end',
|
||||||
|
labels: {
|
||||||
|
padding: 20,
|
||||||
|
boxWidth: 14,
|
||||||
|
font: {
|
||||||
|
size: 14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to get number of days in a month
|
||||||
|
function getDaysInMonth(year, month) {
|
||||||
|
return new Date(year, month + 1, 0).getDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateYearlyData(labels) {
|
||||||
|
const deliveredData = labels.map((label, index) => {
|
||||||
|
return index < 6 ? Math.floor(Math.random() * 81) + 20 : 0; // Random between 20 and 100 for months Jan-June, zero for others
|
||||||
|
});
|
||||||
|
const failedData = deliveredData.map(delivered => Math.floor(delivered * (Math.random() * 0.15 + 0.05))); // 5-20% of delivered
|
||||||
|
return { deliveredData, failedData };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to generate random data
|
||||||
|
function generateRandomData(labels) {
|
||||||
|
const deliveredData = labels.map(() => Math.floor(Math.random() * 81) + 20); // Random between 20 and 100
|
||||||
|
const failedData = deliveredData.map(delivered => Math.floor(delivered * (Math.random() * 0.15 + 0.05))); // 5-20% of delivered
|
||||||
|
return { deliveredData, failedData };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to create an accessible table
|
||||||
|
function createTable(tableId, chartType, labels, deliveredData, failedData) {
|
||||||
|
const table = document.getElementById(tableId);
|
||||||
|
const captionText = document.querySelector(`#${chartType} .chart-subtitle`).textContent;
|
||||||
|
const caption = document.createElement('caption');
|
||||||
|
caption.textContent = captionText;
|
||||||
|
const thead = document.createElement('thead');
|
||||||
|
const tbody = document.createElement('tbody');
|
||||||
|
|
||||||
|
// Create table header
|
||||||
|
const headerRow = document.createElement('tr');
|
||||||
|
const headers = ['Day', 'Delivered', 'Failed'];
|
||||||
|
headers.forEach(headerText => {
|
||||||
|
const th = document.createElement('th');
|
||||||
|
th.textContent = headerText;
|
||||||
|
headerRow.appendChild(th);
|
||||||
|
});
|
||||||
|
thead.appendChild(headerRow);
|
||||||
|
|
||||||
|
// Create table body
|
||||||
|
labels.forEach((label, index) => {
|
||||||
|
const row = document.createElement('tr');
|
||||||
|
const cellDay = document.createElement('td');
|
||||||
|
cellDay.textContent = label;
|
||||||
|
row.appendChild(cellDay);
|
||||||
|
|
||||||
|
const cellDelivered = document.createElement('td');
|
||||||
|
cellDelivered.textContent = deliveredData[index];
|
||||||
|
row.appendChild(cellDelivered);
|
||||||
|
|
||||||
|
const cellFailed = document.createElement('td');
|
||||||
|
cellFailed.textContent = failedData[index];
|
||||||
|
row.appendChild(cellFailed);
|
||||||
|
|
||||||
|
tbody.appendChild(row);
|
||||||
|
});
|
||||||
|
|
||||||
|
table.appendChild(caption);
|
||||||
|
table.appendChild(thead);
|
||||||
|
table.appendChild(tbody);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to handle tab switching
|
||||||
|
function openTab(button, tabName) {
|
||||||
|
// Hide all tab contents
|
||||||
|
document.querySelectorAll('.tabcontent').forEach(content => {
|
||||||
|
content.style.display = 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove "active" class from all buttons
|
||||||
|
tabButtons.forEach(button => {
|
||||||
|
button.classList.remove('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show the current tab and add "active" class to the button
|
||||||
|
document.getElementById(tabName).style.display = 'block';
|
||||||
|
button.classList.add('active');
|
||||||
|
|
||||||
|
// Initialize monthly chart if the "Monthly" tab is clicked
|
||||||
|
if (tabName === 'Monthly' && !monthlyChartInitialized) {
|
||||||
|
const monthlyCtx = document.getElementById('monthlyChart').getContext('2d');
|
||||||
|
const now = new Date();
|
||||||
|
const daysInMonth = getDaysInMonth(now.getFullYear(), now.getMonth());
|
||||||
|
const monthlyLabels = Array.from({ length: daysInMonth }, (_, i) => `${i + 1}`);
|
||||||
|
const { deliveredData, failedData } = generateRandomData(monthlyLabels);
|
||||||
|
createChart(monthlyCtx, monthlyLabels, deliveredData, failedData);
|
||||||
|
createTable('monthlyTable', 'Monthly', monthlyLabels, deliveredData, failedData);
|
||||||
|
monthlyChartInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize yearly chart if the "Yearly" tab is clicked
|
||||||
|
if (tabName === 'Yearly' && !yearlyChartInitialized) {
|
||||||
|
const yearlyCtx = document.getElementById('yearlyChart').getContext('2d');
|
||||||
|
const yearlyLabels = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
|
||||||
|
const { deliveredData, failedData } = generateYearlyData(yearlyLabels);
|
||||||
|
createChart(yearlyCtx, yearlyLabels, deliveredData, failedData);
|
||||||
|
createTable('yearlyTable', 'Yearly', yearlyLabels, deliveredData, failedData);
|
||||||
|
yearlyChartInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add event listeners to the tab buttons
|
||||||
|
tabButtons.forEach(button => {
|
||||||
|
button.addEventListener('click', function(event) {
|
||||||
|
openTab(this, this.getAttribute('data-tab'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show the first tab by default
|
||||||
|
tabButtons[0].click();
|
||||||
|
|
||||||
|
// Initialize weekly chart and table
|
||||||
|
const weeklyCtx = document.getElementById('weeklyChart').getContext('2d');
|
||||||
|
const weeklyLabels = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
|
||||||
|
const { deliveredData: weeklyDeliveredData, failedData: weeklyFailedData } = generateRandomData(weeklyLabels);
|
||||||
|
createChart(weeklyCtx, weeklyLabels, weeklyDeliveredData, weeklyFailedData);
|
||||||
|
createTable('weeklyTable', 'Weekly', weeklyLabels, weeklyDeliveredData, weeklyFailedData);
|
||||||
|
});
|
||||||
@@ -1,71 +1,71 @@
|
|||||||
(function (window) {
|
// (function (window) {
|
||||||
|
|
||||||
// Dummy data (replace with API data)
|
// // Dummy data (replace with API data)
|
||||||
const data = {
|
// const data = {
|
||||||
messageStats: {
|
// messageStats: {
|
||||||
totalMessages: 1000,
|
// totalMessages: 1000,
|
||||||
delivered: 520,
|
// delivered: 520,
|
||||||
pending: 280,
|
// pending: 280,
|
||||||
failed: 100
|
// failed: 100
|
||||||
},
|
// },
|
||||||
dailyUsage: {
|
// dailyUsage: {
|
||||||
dailyUsage: 20,
|
// dailyUsage: 20,
|
||||||
dailyUsageLimit: 100
|
// dailyUsageLimit: 100
|
||||||
},
|
// },
|
||||||
yearlyUsage: {
|
// yearlyUsage: {
|
||||||
yearlyUsage: 2000,
|
// yearlyUsage: 2000,
|
||||||
yearlyUsageLimit: 250000
|
// yearlyUsageLimit: 250000
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
// Update total messages progress bar percentages
|
// // Update total messages progress bar percentages
|
||||||
const messageStats = data.messageStats;
|
// const messageStats = data.messageStats;
|
||||||
|
|
||||||
const messageBars = ["delivered", "pending", "failed"];
|
// const messageBars = ["delivered", "pending", "failed"];
|
||||||
for (const bar of messageBars) {
|
// for (const bar of messageBars) {
|
||||||
const percentage = messageStats[bar] / messageStats.totalMessages * 100;
|
// const percentage = messageStats[bar] / messageStats.totalMessages * 100;
|
||||||
const elementId = `${bar}-bar`;
|
// const elementId = `${bar}-bar`;
|
||||||
const element = document.getElementById(elementId);
|
// const element = document.getElementById(elementId);
|
||||||
element.style.width = `${parseFloat(percentage)}%`;
|
// element.style.width = `${parseFloat(percentage)}%`;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Update total messages progress bar percentages
|
// // Update total messages progress bar percentages
|
||||||
const dailyUsage = data.dailyUsage;
|
// const dailyUsage = data.dailyUsage;
|
||||||
// const dailyUsageLimit = dailyUsage.dailyUsageLimit;
|
// // const dailyUsageLimit = dailyUsage.dailyUsageLimit;
|
||||||
const yearlyUsage = data.yearlyUsage;
|
// const yearlyUsage = data.yearlyUsage;
|
||||||
// const yearlyUsageLimit = yearlyUsage.yearlyUsageLimit;
|
// // const yearlyUsageLimit = yearlyUsage.yearlyUsageLimit;
|
||||||
|
|
||||||
function updateUsageBar(elementId, dailyUsage, dailyUsageLimit, yearlyUsage, yearlyUsageLimit) {
|
// function updateUsageBar(elementId, dailyUsage, dailyUsageLimit, yearlyUsage, yearlyUsageLimit) {
|
||||||
// Ensure element exists
|
// // Ensure element exists
|
||||||
if (!elementId || !document.getElementById(elementId)) {
|
// if (!elementId || !document.getElementById(elementId)) {
|
||||||
console.error(`Element with ID "${elementId}" not found`);
|
// console.error(`Element with ID "${elementId}" not found`);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Calculate percentage
|
// // Calculate percentage
|
||||||
const percentage = dailyUsage / dailyUsageLimit * 100;
|
// const percentage = dailyUsage / dailyUsageLimit * 100;
|
||||||
|
|
||||||
// Update bar width
|
// // Update bar width
|
||||||
const element = document.getElementById(elementId);
|
// const element = document.getElementById(elementId);
|
||||||
element.style.width = `${parseFloat(percentage)}%`;
|
// element.style.width = `${parseFloat(percentage)}%`;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Update usage bars
|
// // Update usage bars
|
||||||
// updateUsageBar("dailyUsage-bar", dailyUsage.dailyUsage, dailyUsageLimit);
|
// // updateUsageBar("dailyUsage-bar", dailyUsage.dailyUsage, dailyUsageLimit);
|
||||||
// updateUsageBar("dailyUsageRemaining-bar", dailyUsageLimit - dailyUsage.dailyUsage, dailyUsageLimit);
|
// // updateUsageBar("dailyUsageRemaining-bar", dailyUsageLimit - dailyUsage.dailyUsage, dailyUsageLimit);
|
||||||
// updateUsageBar("yearlyUsage-bar", yearlyUsage.yearlyUsage, yearlyUsageLimit);
|
// // updateUsageBar("yearlyUsage-bar", yearlyUsage.yearlyUsage, yearlyUsageLimit);
|
||||||
// updateUsageBar("yearlyUsageRemaining-bar", yearlyUsageLimit - yearlyUsage.yearlyUsage, yearlyUsageLimit);
|
// // updateUsageBar("yearlyUsageRemaining-bar", yearlyUsageLimit - yearlyUsage.yearlyUsage, yearlyUsageLimit);
|
||||||
|
|
||||||
// Update total messages legend values
|
// // Update total messages legend values
|
||||||
document.getElementById("total-value").innerText = `Total: ${messageStats.totalMessages} messages`;
|
// document.getElementById("total-value").innerText = `Total: ${messageStats.totalMessages} messages`;
|
||||||
document.getElementById("delivered-value").innerText = `Delivered: ${messageStats.delivered}`;
|
// document.getElementById("delivered-value").innerText = `Delivered: ${messageStats.delivered}`;
|
||||||
document.getElementById("pending-value").innerText = `Pending: ${messageStats.pending}`;
|
// document.getElementById("pending-value").innerText = `Pending: ${messageStats.pending}`;
|
||||||
document.getElementById("failed-value").innerText = `Failed: ${messageStats.failed}`;
|
// document.getElementById("failed-value").innerText = `Failed: ${messageStats.failed}`;
|
||||||
|
|
||||||
// Update usage legend values
|
// // Update usage legend values
|
||||||
// document.getElementById("daily-usage-value").innerText = `Daily usage: ${dailyUsage.dailyUsage}`;
|
// // document.getElementById("daily-usage-value").innerText = `Daily usage: ${dailyUsage.dailyUsage}`;
|
||||||
// document.getElementById("daily-remaining-value").innerText = `Remaining messages: ${dailyUsage.dailyUsageLimit - dailyUsage.dailyUsage}`;
|
// // document.getElementById("daily-remaining-value").innerText = `Remaining messages: ${dailyUsage.dailyUsageLimit - dailyUsage.dailyUsage}`;
|
||||||
// document.getElementById("yearly-usage-value").innerText = `Yearly usage: ${yearlyUsage.yearlyUsage}`;
|
// // document.getElementById("yearly-usage-value").innerText = `Yearly usage: ${yearlyUsage.yearlyUsage}`;
|
||||||
// document.getElementById("yearly-remaining-value").innerText = `Remaining messages: ${yearlyUsage.yearlyUsageLimit - yearlyUsage.yearlyUsage}`;
|
// // document.getElementById("yearly-remaining-value").innerText = `Remaining messages: ${yearlyUsage.yearlyUsageLimit - yearlyUsage.yearlyUsage}`;
|
||||||
|
|
||||||
})(window);
|
// })(window);
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const ctx = document.getElementById('myChart').getContext('2d');
|
|
||||||
const labels = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
|
|
||||||
|
|
||||||
const dataDelivered = labels.map(() => Math.floor(Math.random() * 81) + 20); // Random between 20 and 100
|
|
||||||
const dataFailed = dataDelivered.map(delivered => Math.floor(delivered * (Math.random() * 0.15 + 0.05))); // 5-20% of delivered
|
|
||||||
|
|
||||||
const myChart = new Chart(ctx, {
|
|
||||||
type: 'bar',
|
|
||||||
data: {
|
|
||||||
labels: labels,
|
|
||||||
datasets: [{
|
|
||||||
label: 'Delivered',
|
|
||||||
data: dataDelivered,
|
|
||||||
backgroundColor: '#0076d6',
|
|
||||||
stack: 'Stack 0'
|
|
||||||
}, {
|
|
||||||
label: 'Failed',
|
|
||||||
data: dataFailed,
|
|
||||||
backgroundColor: '#fa9441',
|
|
||||||
stack: 'Stack 0'
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
scales: {
|
|
||||||
x: {
|
|
||||||
stacked: true,
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
stacked: true,
|
|
||||||
beginAtZero: true,
|
|
||||||
max: 120,
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: 'SMS Sent', // Label for the Y-axis
|
|
||||||
color: '#666', // Optional: you can change the label color
|
|
||||||
font: {
|
|
||||||
size: 16, // Optional: change the font size
|
|
||||||
weight: 'bold', // Optional: change the font weight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
position: 'top',
|
|
||||||
align: 'end', // This aligns the legend items to the end of the area
|
|
||||||
labels: {
|
|
||||||
padding: 20,
|
|
||||||
boxWidth: 14,
|
|
||||||
font: {
|
|
||||||
size: 14
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -77,3 +77,37 @@ $failed: color('orange-30v');
|
|||||||
background-color: #007bff;
|
background-color: #007bff;
|
||||||
border-radius: inherit;
|
border-radius: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// tabs
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: units(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab button {
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 0;
|
||||||
|
margin-right: units(-1px);
|
||||||
|
&:focus {
|
||||||
|
outline-width: 2px;
|
||||||
|
}
|
||||||
|
&.active, &:hover {
|
||||||
|
background-color: color("blue-60v");
|
||||||
|
color: #FFF;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tabcontent {
|
||||||
|
display: none;
|
||||||
|
padding: units(1);
|
||||||
|
}
|
||||||
|
.chart-subtitle {
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
font-size: size("body", 5);
|
||||||
|
font-weight: bold;
|
||||||
|
padding: units(1);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -341,10 +341,10 @@ h2.recipient-list {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.js-focus-style {
|
// .js-focus-style {
|
||||||
outline: 3px solid color("blue-40v");
|
// outline: 3px solid color("blue-40v");
|
||||||
box-shadow: 0 0 0 7px color("blue-40v");
|
// box-shadow: 0 0 0 7px color("blue-40v");
|
||||||
*:focus {
|
// *:focus {
|
||||||
outline: none;
|
// outline: none;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -22,9 +22,8 @@ i.e.
|
|||||||
|
|
||||||
@use "uswds-core" as *;
|
@use "uswds-core" as *;
|
||||||
|
|
||||||
iframe:focus, [href]:focus, [tabindex]:focus, [contentEditable=true]:focus {
|
iframe:focus, [href]:focus, [tabindex]:focus, [contentEditable=true]:focus, button:not([disabled]):focus {
|
||||||
outline: units(1px) dotted color('blue-40v');
|
outline: units(2px) solid color('blue-40v');
|
||||||
outline: units(1px) auto color('blue-40v');
|
|
||||||
outline-offset: 0.3rem;
|
outline-offset: 0.3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,11 +18,35 @@
|
|||||||
|
|
||||||
{{ ajax_block(partials, updates_url, 'upcoming') }}
|
{{ ajax_block(partials, updates_url, 'upcoming') }}
|
||||||
|
|
||||||
<h2 class="font-body-xl margin-0">Service Name Dashboard</h2>
|
<h2 class="font-body-xl margin-top-0">Service Name Dashboard</h2>
|
||||||
|
|
||||||
<canvas id="myChart" width="600" height="400"></canvas>
|
<div class="tab">
|
||||||
|
<button class="tablinks usa-button usa-button--outline margin-right-0 border-right-0" data-tab="Weekly">Weekly</button>
|
||||||
|
<button class="tablinks usa-button usa-button--outline margin-right-0" data-tab="Monthly">Monthly</button>
|
||||||
|
<button class="tablinks usa-button usa-button--outline" data-tab="Yearly">Yearly</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h3 id="total-value" class="margin-y-1"></h3>
|
<div id="Weekly" class="tabcontent">
|
||||||
|
<div class="chart-subtitle">2024 Total Message Allowance - Weekly</div>
|
||||||
|
<canvas id="weeklyChart"></canvas>
|
||||||
|
<table id="weeklyTable" class="usa-sr-only"></table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="Monthly" class="tabcontent">
|
||||||
|
<div class="chart-subtitle">2024 Total Message Allowance - Monthly</div>
|
||||||
|
<canvas id="monthlyChart"></canvas>
|
||||||
|
<table id="monthlyTable" class="usa-sr-only"></table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="Yearly" class="tabcontent">
|
||||||
|
<div class="chart-subtitle">2024 Total Message Allowance - Yearly</div>
|
||||||
|
<canvas id="yearlyChart"></canvas>
|
||||||
|
<table id="yearlyTable" class="usa-sr-only"></table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="message"></div>
|
||||||
|
|
||||||
|
<!-- <h3 id="total-value" class="margin-y-1"></h3>
|
||||||
<div class="chart-container">
|
<div class="chart-container">
|
||||||
<div class="bar delivered" id="delivered-bar"></div>
|
<div class="bar delivered" id="delivered-bar"></div>
|
||||||
<div class="bar pending" id="pending-bar"></div>
|
<div class="bar pending" id="pending-bar"></div>
|
||||||
@@ -50,7 +74,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<div class="legend-value" id="failed-value"></div>
|
<div class="legend-value" id="failed-value"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<h2 class="font-body-2xl line-height-sans-2 margin-bottom-0 margin-top-4">
|
<h2 class="font-body-2xl line-height-sans-2 margin-bottom-0 margin-top-4">
|
||||||
Messages sent
|
Messages sent
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ const javascripts = () => {
|
|||||||
paths.src + 'javascripts/date.js',
|
paths.src + 'javascripts/date.js',
|
||||||
paths.src + 'javascripts/loginAlert.js',
|
paths.src + 'javascripts/loginAlert.js',
|
||||||
paths.src + 'javascripts/dataVisualization.js',
|
paths.src + 'javascripts/dataVisualization.js',
|
||||||
paths.src + 'javascripts/show.js',
|
paths.src + 'javascripts/dashboardVisualization.js',
|
||||||
paths.src + 'javascripts/main.js',
|
paths.src + 'javascripts/main.js',
|
||||||
])
|
])
|
||||||
.pipe(plugins.prettyerror())
|
.pipe(plugins.prettyerror())
|
||||||
|
|||||||
Reference in New Issue
Block a user