From f979d41565576fd1f15a056bf6eb0bebbc5f8f7c Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Mon, 17 Jun 2024 09:29:08 -0400 Subject: [PATCH] Mostly ready to combine with actual data --- .../javascripts/dashboardVisualization.js | 132 ++++++++++++------ .../sass/uswds/_data-visualization.scss | 23 +-- app/templates/views/dashboard/dashboard.html | 24 ++-- 3 files changed, 114 insertions(+), 65 deletions(-) diff --git a/app/assets/javascripts/dashboardVisualization.js b/app/assets/javascripts/dashboardVisualization.js index 15dea82b2..3bc24b237 100644 --- a/app/assets/javascripts/dashboardVisualization.js +++ b/app/assets/javascripts/dashboardVisualization.js @@ -9,13 +9,30 @@ const FONT_WEIGHT = 'bold'; const MAX_Y = 120; + let currentLabels = []; + let currentDeliveredData = []; + let currentFailedData = []; + + // Function to format the date + function formatDate(dateStr) { + const date = new Date(dateStr); + const month = date.getMonth() + 1; + const day = date.getDate(); + const year = date.getFullYear().toString().slice(-2); + return `${month}/${day}/${year}`; + } + // Function to create a stacked bar chart with animation using D3.js function createChart(containerId, labels, deliveredData, failedData) { + currentLabels = labels; + currentDeliveredData = deliveredData; + currentFailedData = failedData; + const container = d3.select(containerId); container.selectAll('*').remove(); // Clear any existing content - const margin = { top: 20, right: 30, bottom: 40, left: 40 }; - const width = container.node().clientWidth - margin.left - margin.right; + const margin = { top: 60, right: 20, bottom: 40, left: 20 }; // Adjusted top margin for legend + const width = container.node().getBoundingClientRect().width - margin.left - margin.right; const height = 400 - margin.top - margin.bottom; const svg = container.append('svg') @@ -24,8 +41,34 @@ .append('g') .attr('transform', `translate(${margin.left},${margin.top})`); + // Create legend + const legendData = [ + { label: 'Delivered', color: COLORS.delivered }, + { label: 'Failed', color: COLORS.failed } + ]; + + const legend = d3.select('.chart-legend').selectAll('.legend-item') + .data(legendData); + + const legendEnter = legend.enter().append('div') + .attr('class', 'legend-item'); + + legendEnter.append('rect') + .attr('width', 18) + .attr('height', 18) + .attr('fill', d => d.color); + + legendEnter.append('text') + .attr('x', 24) + .attr('y', 9) + .attr('dy', '0.35em') + .style('text-anchor', 'start') + .text(d => d.label); + + legend.exit().remove(); + const x = d3.scaleBand() - .domain(labels) + .domain(labels.map(formatDate)) // Format the dates .range([0, width]) .padding(0.1); @@ -52,7 +95,7 @@ // Data for stacking const stackData = labels.map((label, i) => ({ - label: label, + label: formatDate(label), // Format the dates delivered: deliveredData[i], failed: failedData[i] || 0 // Ensure there's a value for failed, even if it's 0 })); @@ -89,20 +132,18 @@ .attr('y', height) .attr('height', 0) .attr('width', x.bandwidth()) - .on('mouseover', function(event, d) { + .on('mouseover', function (event, d) { const key = d3.select(this.parentNode).datum().key; const capitalizedKey = key.charAt(0).toUpperCase() + key.slice(1); tooltip.style('display', 'block') - .html(`${d.data.label}
${capitalizedKey}: ${d.data[key]}`) - .style('left', `${event.pageX + 10}px`) - .style('top', `${event.pageY - 20}px`); + .html(`${d.data.label}
${capitalizedKey}: ${d.data[key]}`); }) - .on('mousemove', function(event) { - const [mouseX, mouseY] = d3.pointer(event); - tooltip.style('left', `${mouseX + 10}px`) - .style('top', `${mouseY + 30}px`); + .on('mousemove', function (event) { + const containerPosition = container.node().getBoundingClientRect(); + tooltip.style('left', `${event.clientX - containerPosition.left + 35}px`) + .style('top', `${event.clientY - containerPosition.top + 135}px`); }) - .on('mouseout', function() { + .on('mouseout', function () { tooltip.style('display', 'none'); }) .transition() @@ -114,6 +155,8 @@ // Function to create an accessible table function createTable(tableId, chartType, labels, deliveredData, failedData) { const table = document.getElementById(tableId); + table.innerHTML = ""; // Clear previous data + const captionText = document.querySelector(`#${chartType} .chart-subtitle`).textContent; const caption = document.createElement('caption'); caption.textContent = captionText; @@ -134,7 +177,7 @@ labels.forEach((label, index) => { const row = document.createElement('tr'); const cellDay = document.createElement('td'); - cellDay.textContent = label; + cellDay.textContent = formatDate(label); // Format the dates row.appendChild(cellDay); const cellDelivered = document.createElement('td'); @@ -150,29 +193,7 @@ table.appendChild(caption); table.appendChild(thead); - table.appendChild(tbody); - } - - // Function to handle dropdown change - function handleDropdownChange(event) { - const selectedValue = event.target.value; - const subTitle = document.querySelector(`#chartsArea .chart-subtitle`); - const selectElement = document.getElementById('options'); - const selectedText = selectElement.options[selectElement.selectedIndex].text; - - if (selectedValue === "individual") { - // Mock individual data - const labels = ["2024-06-06", "2024-06-07", "2024-06-08", "2024-06-09", "2024-06-10", "2024-06-11", "2024-06-12"]; - const deliveredData = labels.map(() => Math.floor(Math.random() * 5) + 1); // Random between 1 and 5 - const failedData = deliveredData.map(delivered => Math.floor(delivered * (Math.random() * 0.15 + 0.05))); // 5-20% of delivered - subTitle.textContent = selectedText + " - Last 7 Days"; - createChart('#weeklyChart', labels, deliveredData, failedData); - createTable('weeklyTable', 'Weekly', labels, deliveredData, failedData); - } else if (selectedValue === "service") { - subTitle.textContent = selectedText + " - Last 7 Days"; - // Fetch and use real service data - fetchServiceData(); - } + table.append(tbody); } function fetchServiceData() { @@ -184,11 +205,11 @@ var socket = io(); var serviceId = ctx.getAttribute('data-service-id'); - socket.on('connect', function() { + socket.on('connect', function () { socket.emit('fetch_daily_stats', serviceId); }); - socket.on('daily_stats_update', function(data) { + socket.on('daily_stats_update', function (data) { var labels = []; var deliveredData = []; var failedData = []; @@ -203,12 +224,40 @@ createTable('weeklyTable', 'Weekly', labels, deliveredData, failedData); }); - socket.on('error', function(data) { + socket.on('error', function (data) { console.log('Error:', data); }); } - document.addEventListener('DOMContentLoaded', function() { + // Function to handle dropdown change + function handleDropdownChange(event) { + const selectedValue = event.target.value; + const subTitle = document.querySelector(`#chartsArea .chart-subtitle`); + const selectElement = document.getElementById('options'); + const selectedText = selectElement.options[selectElement.selectedIndex].text; + + if (selectedValue === "individual") { + // Mock individual data + const labels = ["2024-06-06", "2024-06-07", "2024-06-08", "2024-06-09", "2024-06-10", "2024-06-11", "2024-06-12"]; + const deliveredData = labels.map(() => Math.floor(Math.random() * 5) + 1); // Random between 1 and 5 + const failedData = [0, 1, 0, 0, 1, 2, 1]; + subTitle.textContent = selectedText + " - Last 7 Days"; + createChart('#weeklyChart', labels, deliveredData, failedData); + createTable('weeklyTable', 'Weekly', labels, deliveredData, failedData); + } else if (selectedValue === "service") { + subTitle.textContent = selectedText + " - Last 7 Days"; + // Fetch and use real service data + fetchServiceData(); + } + } + + function updateChartSize() { + createChart('#weeklyChart', currentLabels, currentDeliveredData, currentFailedData); + } + + window.addEventListener('resize', updateChartSize); + + document.addEventListener('DOMContentLoaded', function () { // Initialize weekly chart and table with service data by default fetchServiceData(); @@ -216,5 +265,4 @@ const dropdown = document.getElementById('options'); dropdown.addEventListener('change', handleDropdownChange); }); - })(window); diff --git a/app/assets/sass/uswds/_data-visualization.scss b/app/assets/sass/uswds/_data-visualization.scss index 2525a6ecf..c8386fff4 100644 --- a/app/assets/sass/uswds/_data-visualization.scss +++ b/app/assets/sass/uswds/_data-visualization.scss @@ -6,7 +6,6 @@ $failed: color('orange-30v'); .chart-container { display: flex; - height: units(8); &.usage { height: units(4); } @@ -58,7 +57,7 @@ $failed: color('orange-30v'); line-height: 1; .usa-tooltip__body { width: units(mobile); - font-size: units(1); + font-size: size("body", 2); height: auto; white-space: wrap; line-height: units(1); @@ -104,7 +103,7 @@ $failed: color('orange-30v'); // .tabcontent { // display: none; // height: 400px; -// width: 100%; +// width: 100%;h // padding: 10px; /* Replace units(1) with 10px */ // } @@ -115,9 +114,9 @@ $failed: color('orange-30v'); .chart-subtitle { text-align: left; width: 100%; - font-size: units(2); + font-size: size("body", 6); font-weight: bold; - padding: 10px; + padding: 0 units(1); margin: 0; } @@ -129,7 +128,7 @@ $failed: color('orange-30v'); } .axis text { - font-size: size("body", 2); + font-size: size("body", 1); } .axis line, @@ -145,16 +144,18 @@ $failed: color('orange-30v'); .chart-container { width: 100%; - position: relative; /* Ensure it is relative for absolute positioning of tooltip */ + position: relative; } #tooltip { position: absolute; display: none; - background: #fff; + background: color('ink'); + color: #FFF; border: 1px solid #ccc; - padding: 5px; + padding: units(1); + border-radius: units(1); pointer-events: none; - font-size: size("body", 2); - z-index: 100; /* Ensure tooltip appears above other elements */ + z-index: 100; + font-size: size("body", 4); } diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index 755273282..cef783043 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -20,25 +20,25 @@ -

Service Name Dashboard

+

{{ current_service.name }} Dashboard

-
- - -
- -
+
+
+ + +
{{ current_service.name }} - Last 7 Days
-
+
+