From a33a6d62d295b83071da9f2348c29f2d3b23678a Mon Sep 17 00:00:00 2001 From: Matt Batchelder Date: Sat, 21 Feb 2026 02:08:54 -0500 Subject: [PATCH] Refactor dashboard SVG charts and animations - Removed the old SVG file for the dashboard chart and replaced it with a new implementation directly in the PHP file. - Updated the SVG structure to improve accessibility and styling, including the use of CSS classes for dynamic theming. - Enhanced the bar charts, line graph, and pie chart with new gradients and animations. - Adjusted the enqueue script for the dashboard animator to include versioning based on file modification time. --- theme/assets/css/main.css | 123 +------ theme/assets/js/dashboard-animator.js | 449 +++++++++----------------- theme/assets/svg/dashboard-chart.svg | 123 ------- theme/blocks/index.php | 208 ++++++------ theme/inc/enqueue.php | 2 +- 5 files changed, 271 insertions(+), 634 deletions(-) delete mode 100644 theme/assets/svg/dashboard-chart.svg diff --git a/theme/assets/css/main.css b/theme/assets/css/main.css index 12e6dac..ea82c15 100644 --- a/theme/assets/css/main.css +++ b/theme/assets/css/main.css @@ -2602,137 +2602,32 @@ p:last-child { margin-bottom: 0; } 100% { opacity: 1; } } -/* ── Dashboard Chart Animations ────────────────────────── */ +/* ── Dashboard Chart ───────────────────────────────────── */ .dashboard-chart-container { position: relative; width: 100%; max-width: 900px; margin: 0 auto; padding: 1.5rem; - background: var(--card-bg); - border: 1px solid var(--color-border); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-md); - will-change: opacity; - data-dashboard-container: true; + background: var(--card-bg, rgba(255,255,255,.04)); + border: 1px solid var(--color-border, #333); + border-radius: var(--radius-lg, 20px); + box-shadow: var(--shadow-md, 0 4px 24px rgba(0,0,0,.12)); } - .dashboard-chart { width: 100%; height: auto; display: block; - font-family: var(--font-sans); user-select: none; + overflow: visible; } - -/* Bar animation - smooth height transitions */ -.dashboard-chart .bar { - fill: url(#barGradient); - will-change: height; - transition: height 0.3s ease-out; - vector-effect: non-scaling-stroke; -} - -/* Line path - draw animation */ -.dashboard-chart .line-path { - stroke: var(--color-accent); - fill: none; - stroke-width: 2.5; - stroke-linecap: round; - stroke-linejoin: round; - will-change: stroke-dashoffset, d; - transition: stroke 0.3s ease; -} - -.dashboard-chart .line-fill { - fill: url(#lineGradient); - will-change: d; - opacity: 0.5; -} - -/* Pie chart segment animations */ -.dashboard-chart .pie-segment { - will-change: transform; - transform-origin: center; - transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); -} - -/* Chart title styling */ -.dashboard-chart .chart-title { - font-size: 14px; - font-weight: 600; - fill: var(--color-heading); - letter-spacing: 0.3px; -} - -.dashboard-chart .chart-label { - font-size: 12px; - fill: var(--color-text-muted); - font-weight: 500; - text-transform: uppercase; - letter-spacing: 0.2px; -} - -.dashboard-chart .chart-value { - font-size: 13px; - font-weight: 600; - fill: var(--color-text); - transition: fill 0.2s ease; -} - -/* Dark mode support */ -[data-theme="dark"] .dashboard-chart { - --text-primary: #E0E0E0; - --text-secondary: #9E9E9E; - --primary-color: #66BB6A; - --accent-color: #4CAF50; - --border-color: #333333; -} - -[data-theme="light"] .dashboard-chart { - --text-primary: #1a1a1a; - --text-secondary: #666; - --primary-color: #3b82f6; - --accent-color: #10b981; - --border-color: #e5e7eb; -} - -/* Performance: disable animations on reduced motion preference */ +/* Reduced-motion: JS already checks the media query, belt-and-suspenders */ @media (prefers-reduced-motion: reduce) { .dashboard-chart .bar, - .dashboard-chart .line-path, - .dashboard-chart .line-fill, - .dashboard-chart .pie-segment { - animation: none !important; - transition: none !important; - } + .dashboard-chart .pie-segment { transition: none !important; } } - -/* Responsive: Adjust for smaller screens */ @media (max-width: 768px) { - .dashboard-chart-container { - padding: 1rem; - border-radius: var(--radius-md); - } - - .dashboard-chart .chart-title { - font-size: 13px; - } - - .dashboard-chart .chart-label { - font-size: 11px; - } - - .dashboard-chart .chart-value { - font-size: 12px; - } -} - -/* Touch devices: No hover effects */ -@media (hover: none) { - .dashboard-chart-container:hover { - box-shadow: var(--shadow-md); - } + .dashboard-chart-container { padding: 1rem; border-radius: var(--radius-md, 12px); } } .cta-banner { diff --git a/theme/assets/js/dashboard-animator.js b/theme/assets/js/dashboard-animator.js index b28cbb0..e6b574f 100644 --- a/theme/assets/js/dashboard-animator.js +++ b/theme/assets/js/dashboard-animator.js @@ -1,311 +1,160 @@ /** * Dashboard Chart Animator - * Continuously animates bar charts, line graphs, and pie charts - * Performance-optimized with IntersectionObserver and requestAnimationFrame + * Continuously animates SVG bar charts, line graph, and pie chart. + * Uses requestAnimationFrame, pauses off-screen via IntersectionObserver. */ +(function () { + 'use strict'; -class DashboardAnimator { - constructor(svgElement) { - this.svg = svgElement; - this.animationId = null; - this.isPaused = false; - this.isReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; - this.startTime = Date.now(); - - // Chart dimensions - this.barChartHeight = 120; - this.chartUpdateSpeed = 1500; // Faster: 1.5s cycle for constant movement - - // Cache DOM elements - try multiple selector methods for robustness - this.barsGroup1 = this.svg.querySelectorAll('#bars-group-1 rect.bar'); - this.barsGroup2 = this.svg.querySelectorAll('#bars-group-2 rect.bar'); - this.valuesGroup1 = this.svg.querySelectorAll('#values-group-1 text.chart-value'); - this.valuesGroup2 = this.svg.querySelectorAll('#values-group-2 text.chart-value'); - this.linePath = this.svg.querySelector('#line-path'); - this.lineFill = this.svg.querySelector('#line-fill'); - this.pieSegments = this.svg.querySelectorAll('g.pie-segment'); - - // Debug: Check if elements were found - const hasElements = this.barsGroup1.length > 0 || this.linePath || this.pieSegments.length > 0; - if (!hasElements) { - console.warn('[DashboardAnimator] No chart elements found. Debugging info:'); - console.warn(' SVG element:', this.svg); - console.warn(' bars-group-1:', this.barsGroup1.length); - console.warn(' line-path:', !!this.linePath); - console.warn(' pie-segment:', this.pieSegments.length); - // Try direct getElementById as fallback - console.warn(' Testing getElementById on document:', document.getElementById('bars-group-1')); - } else { - console.log('[DashboardAnimator] ✓ Found elements - Bars:', this.barsGroup1.length, 'Line:', !!this.linePath, 'Pie:', this.pieSegments.length); - } - - // Noise generators for smooth, realistic data - this.noisePhase1 = Math.random() * Math.PI * 2; - this.noisePhase2 = Math.random() * Math.PI * 2; - this.noisePhase3 = Math.random() * Math.PI * 2; - this.noisePhase4 = Math.random() * Math.PI * 2; - this.noisePhase5 = Math.random() * Math.PI * 2; - - this.init(); - } - - init() { - if (!this.isReducedMotion && (this.barsGroup1.length || this.linePath || this.pieSegments.length)) { - console.log('[DashboardAnimator] ✓ Starting animation loop'); - // Visual indicator that animator started - this.svg.style.cssText = 'border: 2px solid green; border-radius: 4px;'; - setTimeout(() => { - this.svg.style.border = ''; - }, 500); - this.animate(); - this.attachHoverListeners(); - } else if (this.isReducedMotion) { - console.log('[DashboardAnimator] Motion preference: reduced'); - } else { - console.log('[DashboardAnimator] ✗ No animatable elements found'); - this.svg.style.cssText = 'border: 2px solid red; border-radius: 4px;'; - } - } - - /** - * Perlin-like noise for smooth, natural-looking data - * Uses sine waves at different frequencies - */ - generateNoise(phase, frequency = 0.5) { - return Math.sin(phase * frequency) * 0.5 + 0.5; // Normalize to 0-1 - } - - /** - * Generate smooth, continuous data values - * Uses combination of sine waves for organic motion - */ - generateValue(basePhase, index, max = 100) { - const time = (Date.now() - this.startTime) / this.chartUpdateSpeed; - const phase = basePhase + time * 0.3 + index * 0.4; - - // Multi-frequency sine wave for natural variation - const noise1 = Math.sin(phase * 0.7) * 0.4; - const noise2 = Math.sin(phase * 1.3) * 0.3; - const noise3 = Math.sin(phase * 0.3) * 0.2; - const base = 0.5 + noise1 + noise2 + noise3; - - // Ensure value stays in range - return Math.max(10, Math.min(max, base * max * 0.9)); - } - - /** - * Update bar chart with smooth animation - */ - updateBars(barsElements, valuesElements, maxValue = 100, isPercent = true) { - if (!barsElements || barsElements.length === 0) return; - - barsElements.forEach((bar, index) => { - const value = this.generateValue(this.noisePhase1, index, maxValue); - const heightPercent = (value / maxValue) * 100; - const height = (heightPercent / 100) * this.barChartHeight; - - // For SVG bars to grow upward, adjust y position inversely - const y = this.barChartHeight - height; - - bar.setAttribute('height', height); - bar.setAttribute('y', y); - - if (valuesElements && valuesElements[index]) { - valuesElements[index].textContent = isPercent ? - Math.round(value) + '%' : - Math.round(value); - valuesElements[index].setAttribute('data-value', value.toFixed(1)); - } - }); - } - - /** - * Update line graph with smooth curve - */ - updateLineGraph() { - if (!this.linePath || !this.lineFill) return; - - const time = (Date.now() - this.startTime) / this.chartUpdateSpeed; - const points = []; - - // Generate 3 points for quadratic bezier curve - for (let i = 0; i < 3; i++) { - const x = i * 100; - const basePhase = this.noisePhase3 + time * 0.2 + i * 0.5; - const noise = Math.sin(basePhase) * 0.3 + Math.sin(basePhase * 0.5) * 0.2; - const y = 80 + noise * 60 - i * 15; - points.push({ x, y }); - } - - // Build quadratic bezier path - const pathData = `M${points[0].x},${points[0].y} Q${points[1].x},${points[1].y} ${points[2].x},${points[2].y}`; - this.linePath.setAttribute('d', pathData); - this.lineFill.setAttribute('d', `${pathData} L200,160 Q100,140 0,160 Z`); - - // Animate stroke-dasharray for drawing effect - const strokeLength = 200; - const offset = ((time * 50) % strokeLength); - this.linePath.setAttribute('stroke-dashoffset', offset); - } - - /** - * Update pie chart with smooth segment rotation - */ - updatePieChart() { - if (!this.pieSegments || this.pieSegments.length === 0) return; - - const time = (Date.now() - this.startTime) / (this.chartUpdateSpeed * 0.5); - const baseRotation = (time * 15) % 360; // Slow rotation - - this.pieSegments.forEach((segment, index) => { - // Add wobble effect to each segment - const wobble = Math.sin(time * 0.5 + index * Math.PI / 2) * 3; - segment.setAttribute('transform', `rotate(${baseRotation + index * 90 + wobble})`); - }); - } - - /** - * Main animation loop using requestAnimationFrame - */ - animate = () => { - if (!this.isPaused) { - try { - this.updateBars(this.barsGroup1, this.valuesGroup1, 100, true); - this.updateBars(this.barsGroup2, this.valuesGroup2, 5000, false); - this.updateLineGraph(); - this.updatePieChart(); - } catch (err) { - console.error('[DashboardAnimator] Animation error:', err); - } - } - - this.animationId = requestAnimationFrame(this.animate); - } - - /** - * Attach hover event listeners to pause/resume animation - */ - attachHoverListeners() { - const container = this.svg.closest('[data-dashboard-container]') || this.svg.parentElement; - - if (container) { - container.addEventListener('mouseenter', () => this.pause()); - container.addEventListener('mouseleave', () => this.resume()); - - // Touch support: pause on touch - container.addEventListener('touchstart', () => this.pause()); - container.addEventListener('touchend', () => this.resume()); - } - } - - /** - * Pause animation (on hover) - */ - pause() { - this.isPaused = true; - } - - /** - * Resume animation (after hover) - */ - resume() { - this.isPaused = false; - } - - /** - * Stop animation and clean up - */ - destroy() { - if (this.animationId) { - cancelAnimationFrame(this.animationId); - this.animationId = null; - } - } -} + /* ── Configuration ──────────────────────────────────── */ + var SPEED = 0.0025; // phase increment per ms (~4-6 s visible cycle) + var BAR_H = 120; // max bar height in SVG units + var BAR_MIN = 0.12; // minimum bar height ratio (12 %) + var LINE_PTS = 8; // number of points on the line graph -/** - * Initialize dashboard animation when element enters viewport - */ -function initDashboardAnimation() { - const dashboardCharts = document.querySelectorAll('.dashboard-chart'); - console.log('[initDashboardAnimation] Found', dashboardCharts.length, 'dashboard charts'); - - if (!dashboardCharts.length) return; - - dashboardCharts.forEach((chart, idx) => { - // Check if element is likely in viewport - const rect = chart.getBoundingClientRect(); - const isVisible = rect.top < window.innerHeight && rect.bottom > 0; - console.log('[initDashboardAnimation] Chart', idx, 'visible:', isVisible, 'rect:', rect); - - if (isVisible || !chart._dashboardAnimator) { - // Start animation immediately if visible or if not set up yet - if (!chart._dashboardAnimator) { - console.log('[initDashboardAnimation] Creating DashboardAnimator for chart', idx); - try { - chart._dashboardAnimator = new DashboardAnimator(chart); - console.log('[initDashboardAnimation] ✓ DashboardAnimator created successfully'); - } catch (err) { - console.error('[initDashboardAnimation] ✗ Failed to create DashboardAnimator:', err); - } - } - } - }); - - // Also set up IntersectionObserver for when elements come into view - if ('IntersectionObserver' in window) { - const observerOptions = { - root: null, - rootMargin: '100px', - threshold: 0.05 + /* ── Colour sets (matches theme tokens) ─────────────── */ + var DARK = { text: '#E0E0E0', muted: '#9E9E9E', border: '#333', center: '#222' }; + var LIGHT = { text: '#333333', muted: '#666666', border: '#E0E0E0', center: '#fff' }; + + function isDark() { + return document.documentElement.getAttribute('data-theme') === 'dark'; + } + function palette() { return isDark() ? DARK : LIGHT; } + + /** Stacked sine waves => smooth organic value in 0..1 range */ + function wave(t, offset) { + return Math.max(0, Math.min(1, + 0.55 + + Math.sin(t * 1.0 + offset) * 0.30 + + Math.sin(t * 2.3 + offset * 1.4) * 0.25 + + Math.sin(t * 0.7 + offset * 0.6) * 0.20 + )); + } + + /* ── Per-chart state ────────────────────────────────── */ + function makeState(svg) { + return { + svg: svg, + bars1: svg.querySelectorAll('#bars-group-1 .bar'), + bars2: svg.querySelectorAll('#bars-group-2 .bar'), + vals1: svg.querySelectorAll('#values-group-1 text'), + vals2: svg.querySelectorAll('#values-group-2 text'), + linePath: svg.querySelector('#line-path'), + lineFill: svg.querySelector('#line-fill'), + pieSegs: svg.querySelectorAll('.pie-segment'), + phase: Math.random() * Math.PI * 2, + paused: false, + raf: null, + themeN: 0 }; - - const observer = new IntersectionObserver((entries) => { - entries.forEach(entry => { - console.log('[IntersectionObserver]', entry.isIntersecting ? 'Intersecting' : 'Not intersecting'); - if (entry.isIntersecting) { - if (!entry.target._dashboardAnimator) { - console.log('[IntersectionObserver] Creating new DashboardAnimator'); - try { - entry.target._dashboardAnimator = new DashboardAnimator(entry.target); - } catch (err) { - console.error('[IntersectionObserver] Failed:', err); - } - } else { - entry.target._dashboardAnimator.resume(); - } - } else { - if (entry.target._dashboardAnimator) { - entry.target._dashboardAnimator.pause(); - } - } - }); - }, observerOptions); - - dashboardCharts.forEach(chart => observer.observe(chart)); } -} -// Auto-initialize when DOM is ready -console.log('[Dashboard Animator] Script loaded, readyState:', document.readyState); + /* ── Update routines ────────────────────────────────── */ + function updateBars(bars, vals, st, pct) { + var i, v, h; + for (i = 0; i < bars.length; i++) { + v = wave(st.phase, i * 1.1); + v = Math.max(BAR_MIN, v); + h = v * BAR_H; + bars[i].setAttribute('height', h); + bars[i].setAttribute('y', BAR_H - h); + } + for (i = 0; i < Math.min(bars.length, vals.length); i++) { + v = wave(st.phase, i * 1.1); + v = Math.max(BAR_MIN, v); + vals[i].textContent = pct ? Math.round(v * 100) + '%' : Math.round(v * 5000); + } + } -function tryInitialize() { - console.log('[Dashboard Animator] tryInitialize called'); - const charts = document.querySelectorAll('.dashboard-chart'); - console.log('[Dashboard Animator] Found', charts.length, 'dashboard charts on page'); - if (charts.length > 0) { - console.log('[Dashboard Animator] Found charts, initializing now...'); - initDashboardAnimation(); + function updateLine(st) { + if (!st.linePath) return; + var d = 'M', i, x, y, v; + for (i = 0; i < LINE_PTS; i++) { + x = (i / (LINE_PTS - 1)) * 200; + v = wave(st.phase * 0.8, i * 0.9); + y = 25 + (1 - v) * 110; + d += (i ? ' L' : '') + x.toFixed(1) + ',' + y.toFixed(1); + } + st.linePath.setAttribute('d', d); + if (st.lineFill) st.lineFill.setAttribute('d', d + ' L200,145 L0,145 Z'); + } + + function updatePie(st) { + if (!st.pieSegs.length) return; + var base = (st.phase * 40) % 360; + for (var i = 0; i < st.pieSegs.length; i++) { + var w = Math.sin(st.phase * 0.6 + i * 1.5) * 4; + st.pieSegs[i].setAttribute('transform', 'rotate(' + (base + i * 90 + w) + ')'); + } + } + + function applyTheme(st) { + var c = palette(); + var q = st.svg.querySelectorAll.bind(st.svg); + var all, j; + + all = q('.ct'); for (j = 0; j < all.length; j++) all[j].setAttribute('fill', c.text); + all = q('.cl'); for (j = 0; j < all.length; j++) all[j].setAttribute('fill', c.muted); + all = q('.cv'); for (j = 0; j < all.length; j++) all[j].setAttribute('fill', c.text); + all = q('.grid-line'); for (j = 0; j < all.length; j++) all[j].setAttribute('stroke', c.border); + + var cc = st.svg.querySelector('#pie-center'); + if (cc) { cc.setAttribute('fill', c.center); cc.setAttribute('stroke', c.border); } + var pt = st.svg.querySelector('#pie-center-text'); + if (pt) pt.setAttribute('fill', c.text); + } + + /* ── Animation loop ─────────────────────────────────── */ + function tick(st) { + if (!st.paused) { + st.phase += SPEED * 16; // ~16 ms per frame at 60 fps + updateBars(st.bars1, st.vals1, st, true); + updateBars(st.bars2, st.vals2, st, false); + updateLine(st); + updatePie(st); + + // Re-apply theme colours every ~30 frames (~0.5 s) + if (++st.themeN > 30) { st.themeN = 0; applyTheme(st); } + } + st.raf = requestAnimationFrame(function () { tick(st); }); + } + + /* ── Hover pause / resume ───────────────────────────── */ + function attachHover(st) { + var el = st.svg.closest('[data-dashboard-container]') || st.svg.parentElement; + if (!el) return; + el.addEventListener('mouseenter', function () { st.paused = true; }); + el.addEventListener('mouseleave', function () { st.paused = false; }); + el.addEventListener('touchstart', function () { st.paused = true; }, { passive: true }); + el.addEventListener('touchend', function () { st.paused = false; }, { passive: true }); + } + + /* ── IntersectionObserver (pause off-screen) ────────── */ + function observe(st) { + if (!('IntersectionObserver' in window)) return; + new IntersectionObserver(function (entries) { + for (var i = 0; i < entries.length; i++) st.paused = !entries[i].isIntersecting; + }, { rootMargin: '200px', threshold: 0.05 }).observe(st.svg); + } + + /* ── Bootstrap ──────────────────────────────────────── */ + function boot() { + var svgs = document.querySelectorAll('.dashboard-chart'); + if (!svgs.length) return; + var reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches; + + for (var i = 0; i < svgs.length; i++) { + if (svgs[i]._dbAnim) continue; + var st = makeState(svgs[i]); + svgs[i]._dbAnim = st; + applyTheme(st); + if (!reduced) { tick(st); attachHover(st); observe(st); } + } + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', boot); } else { - console.log('[Dashboard Animator] No charts found yet, retrying in 100ms...'); - setTimeout(tryInitialize, 100); + boot(); } -} - -if (document.readyState === 'loading') { - console.log('[Dashboard Animator] DOM still loading, waiting for DOMContentLoaded'); - document.addEventListener('DOMContentLoaded', tryInitialize); -} else { - console.log('[Dashboard Animator] DOM already loaded, initializing immediately'); - tryInitialize(); -} +})(); diff --git a/theme/assets/svg/dashboard-chart.svg b/theme/assets/svg/dashboard-chart.svg deleted file mode 100644 index 82706e5..0000000 --- a/theme/assets/svg/dashboard-chart.svg +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - Performance - - - - - - - - - API - Cache - DB - Queue - Worker - - - 0% - 0% - 0% - 0% - 0% - - - - Requests/sec - - - - - - - - Read - Write - Update - Delete - - - 0 - 0 - 0 - 0 - - - - - - Traffic Trend - - - - - - - - - - - - - - Distribution - - - - - - - - - - - - - - - - - 100% - - - - - Service A - - - Service B - - - Service C - - - Service D - - - \ No newline at end of file diff --git a/theme/blocks/index.php b/theme/blocks/index.php index 5ce3e3b..a4a5ef2 100644 --- a/theme/blocks/index.php +++ b/theme/blocks/index.php @@ -1344,102 +1344,118 @@ function oribi_render_platform_row( $a ) { $is_dashboard = ! empty( $a['isDashboard'] ) || stripos( $heading_text, 'dashboard' ) !== false || stripos( $heading_text, 'data' ) !== false; if ( $is_dashboard ) { - // Render dashboard chart animation - $visual_html = '
- - - - - - - - - - - - Performance - - - - - - - - - API - Cache - DB - Queue - Worker - - - 0% - 0% - 0% - 0% - 0% - - Requests/sec - - - - - - - - Read - Write - Update - Delete - - - 0 - 0 - 0 - 0 - - - - Traffic Trend - - - - - - - - - - - Distribution - - - - - - - - - - - - - - - 100% - - - - Service A - - Service B - - Service C - - Service D - - -
'; + // Render animated dashboard chart SVG + // Text uses class hooks: .ct = title, .cl = label, .cv = value + // JS will dynamically set fill colours based on data-theme + $visual_html = '
+ + + + + + + + + + + + + + + Performance + + + + + + + + + API + Cache + DB + Queue + Worker + + + 0% + 0% + 0% + 0% + 0% + + + + + + Requests/sec + + + + + + + + Read + Write + Update + Delete + + + 0 + 0 + 0 + 0 + + + + + + Traffic Trend + + + + + + + + + + + + + + + Distribution + + + + + + + + + + + + + + + 100% + + + + Service A + + Service B + + Service C + + Service D + + + +
'; $visual_cls = 'platform-visual has-dashboard'; } elseif ( $img_url ) { $img_style = 'width:' . $img_w . 'px;max-width:100%;height:auto;border-radius:var(--radius-sm);object-fit:contain;display:block;margin-inline:auto;'; diff --git a/theme/inc/enqueue.php b/theme/inc/enqueue.php index 22e18bb..a46cb8b 100644 --- a/theme/inc/enqueue.php +++ b/theme/inc/enqueue.php @@ -34,7 +34,7 @@ add_action( 'wp_enqueue_scripts', function () { 'oribi-dashboard-animator', ORIBI_URI . '/assets/js/dashboard-animator.js', [], - ORIBI_VERSION, + ORIBI_VERSION . '.' . filemtime( ORIBI_DIR . '/assets/js/dashboard-animator.js' ), true );