diff --git a/app/assets/javascripts/dataVisualization.js b/app/assets/javascripts/dataVisualization.js new file mode 100644 index 000000000..bcb2c4195 --- /dev/null +++ b/app/assets/javascripts/dataVisualization.js @@ -0,0 +1,71 @@ +(function (window) { + + // Dummy data (replace with API data) + const data = { + messageStats: { + totalMessages: 1000, + delivered: 800, + pending: 100, + failed: 100 + }, + dailyUsage: { + dailyUsage: 20, + dailyUsageLimit: 100 + }, + yearlyUsage: { + yearlyUsage: 2000, + yearlyUsageLimit: 250000 + } + }; + + // Update total messages progress bar percentages + const messageStats = data.messageStats; + + const messageBars = ["delivered", "pending", "failed"]; + for (const bar of messageBars) { + const percentage = messageStats[bar] / messageStats.totalMessages * 100; + const elementId = `${bar}-bar`; + const element = document.getElementById(elementId); + element.style.width = `${parseFloat(percentage)}%`; + } + + // Update total messages progress bar percentages + const dailyUsage = data.dailyUsage; + const dailyUsageLimit = dailyUsage.dailyUsageLimit; + const yearlyUsage = data.yearlyUsage; + const yearlyUsageLimit = yearlyUsage.yearlyUsageLimit; + + function updateUsageBar(elementId, dailyUsage, dailyUsageLimit, yearlyUsage, yearlyUsageLimit) { + // Ensure element exists + if (!elementId || !document.getElementById(elementId)) { + console.error(`Element with ID "${elementId}" not found`); + return; + } + + // Calculate percentage + const percentage = dailyUsage / dailyUsageLimit * 100; + + // Update bar width + const element = document.getElementById(elementId); + element.style.width = `${parseFloat(percentage)}%`; + } + + // Update usage bars + updateUsageBar("dailyUsage-bar", dailyUsage.dailyUsage, dailyUsageLimit); + updateUsageBar("dailyUsageRemaining-bar", dailyUsageLimit - dailyUsage.dailyUsage, dailyUsageLimit); + updateUsageBar("yearlyUsage-bar", yearlyUsage.yearlyUsage, yearlyUsageLimit); + updateUsageBar("yearlyUsageRemaining-bar", yearlyUsageLimit - yearlyUsage.yearlyUsage, yearlyUsageLimit); + + // Update total messages legend values + document.getElementById("total-value").innerText = `Total: ${messageStats.totalMessages} messages`; + document.getElementById("delivered-value").innerText = `Delivered: ${messageStats.delivered}`; + document.getElementById("pending-value").innerText = `Pending: ${messageStats.pending}`; + document.getElementById("failed-value").innerText = `Failed: ${messageStats.failed}`; + + // Update usage legend values + document.getElementById("daily-usage-value").innerText = `Daily usage: ${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-remaining-value").innerText = `Remaining messages: ${yearlyUsage.yearlyUsageLimit - yearlyUsage.yearlyUsage}`; + +})(window); diff --git a/app/assets/sass/uswds/_data-visualization.scss b/app/assets/sass/uswds/_data-visualization.scss new file mode 100644 index 000000000..261fb3b1b --- /dev/null +++ b/app/assets/sass/uswds/_data-visualization.scss @@ -0,0 +1,79 @@ +@use "uswds-core" as *; + +$delivered: color('blue-50v'); +$pending: color('green-cool-40v'); +$failed: color('orange-30v'); + +.chart-container { + display: flex; + height: units(8); + &.usage { + height: units(4); + } +} + +.bar { + border-radius: units(0.5); + &.delivered, &.usage { + background-color: $delivered; + margin-right: 1px; + } + &.pending{ + background-color: $pending; + margin-right: 1px; + } + &.failed, &.remaining { + background-color: $failed; + } +} + +.legend { + display: flex; + margin: units(1) 0; + .legend-item { + display: flex; + align-items: flex-start; + margin-right: units(2); + } + .legend-color { + width: units(3); + height: units(3); + margin-right: 0; + padding: 0; + border-radius: 2px; + background-color: $delivered; + &.pending { + background-color: $pending; + } + &.failed, &.remaining { + background-color: $failed; + } + } + .legend-value { + margin: 0 units(1); + } +} + +.usa-tooltip { + line-height: 1; + .usa-tooltip__body { + width: units(mobile); + height: auto; + white-space: wrap; + line-height: 1.3; + } +} + +.progress-bar { + width: 300px; + height: 20px; + background-color: #eee; + border-radius: 5px; + margin-bottom: 10px; +} + +.progress-bar-inner { + height: 100%; + background-color: #007bff; + border-radius: inherit; +} diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index 0a558ad8f..cb72c885e 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -18,6 +18,39 @@ {{ ajax_block(partials, updates_url, 'upcoming') }} +

+ SMS Activity +

+

+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+

Messages sent

diff --git a/poetry.lock b/poetry.lock index f70d06207..dc1346c4b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1517,7 +1517,6 @@ files = [ {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, - {file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"}, {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, ] @@ -2384,6 +2383,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},