From 70f02ec932df13f3189c2476a678453b6d289f98 Mon Sep 17 00:00:00 2001 From: TheFlow Date: Mon, 27 Oct 2025 11:16:21 +1300 Subject: [PATCH] feat(bi): add interactive sliders to cost configuration modal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhanced cost configuration UX with dual-control interface: - Range sliders for quick visual adjustments - Number inputs for precise values - Real-time sync between slider and input - Live value display with formatting ($X,XXX) - Custom purple styling matching Tractatus theme Slider ranges by severity: - CRITICAL: $1k-$250k (step: $1k) - HIGH: $500-$50k (step: $500) - MEDIUM: $100-$10k (step: $100) - LOW: $50-$5k (step: $50) Users can drag sliders OR type exact amounts for maximum flexibility. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- public/js/admin/audit-analytics.js | 94 ++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 6 deletions(-) diff --git a/public/js/admin/audit-analytics.js b/public/js/admin/audit-analytics.js index 8e31b9ae..06f12992 100644 --- a/public/js/admin/audit-analytics.js +++ b/public/js/admin/audit-analytics.js @@ -950,6 +950,14 @@ async function showCostConfigModal() { return; } + // Slider ranges by severity + const sliderConfig = { + CRITICAL: { min: 1000, max: 250000, step: 1000 }, + HIGH: { min: 500, max: 50000, step: 500 }, + MEDIUM: { min: 100, max: 10000, step: 100 }, + LOW: { min: 50, max: 5000, step: 50 } + }; + const modal = document.createElement('div'); modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50'; modal.innerHTML = ` @@ -960,14 +968,34 @@ async function showCostConfigModal() {
- ${['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'].map(severity => ` + ${['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'].map(severity => { + const config = sliderConfig[severity]; + const amount = Math.min(Math.max(costFactors[severity].amount, config.min), config.max); + return `
- -
+ + +
+
+ + $${amount.toLocaleString()} +
+ +
+ $${(config.min / 1000).toFixed(0)}k + $${(config.max / 1000).toFixed(0)}k +
+
+ +
- +
@@ -977,13 +1005,14 @@ async function showCostConfigModal() { class="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-purple-500 focus:border-transparent">
-
+ +
- `).join('')} + `}).join('')}
Note: These values are illustrative placeholders for research purposes. @@ -1000,10 +1029,63 @@ async function showCostConfigModal() {
+ + `; document.body.appendChild(modal); + // Sync sliders with number inputs + ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'].forEach(severity => { + const slider = modal.querySelector(`#cost-${severity}-slider`); + const input = modal.querySelector(`#cost-${severity}-amount`); + const display = modal.querySelector(`#cost-${severity}-display`); + + // Slider changes update input and display + slider.addEventListener('input', (e) => { + const value = parseFloat(e.target.value); + input.value = value; + display.textContent = value.toLocaleString(); + }); + + // Input changes update slider and display + input.addEventListener('input', (e) => { + const value = parseFloat(e.target.value) || 0; + const config = sliderConfig[severity]; + const clampedValue = Math.min(Math.max(value, config.min), config.max); + slider.value = clampedValue; + display.textContent = value.toLocaleString(); + }); + }); + // Close on cancel modal.querySelector('#cancel-config-btn').addEventListener('click', () => { document.body.removeChild(modal);