diff --git a/src/gui/src/UI/Dashboard/TabUsage.js b/src/gui/src/UI/Dashboard/TabUsage.js index 9674b2a38..b36d7779d 100644 --- a/src/gui/src/UI/Dashboard/TabUsage.js +++ b/src/gui/src/UI/Dashboard/TabUsage.js @@ -23,6 +23,8 @@ let usageTableSortState = { direction: 'desc' // default descending (highest cost first) }; let usageTableData = []; // Store raw data for sorting +let usageTableExpanded = false; // Track if table is showing all rows +const USAGE_TABLE_INITIAL_ROWS = 10; const TabUsage = { id: 'usage', @@ -60,11 +62,8 @@ const TabUsage = { -
-
- View usage details -
- `; @@ -75,20 +74,6 @@ const TabUsage = { update_usage_details($el_window); }); - // Scoped click handler for usage details toggle - $($el_window).on('click', '.driver-usage-details', function () { - const $container = $(this).closest('.driver-usage'); - $container.find('.driver-usage-details-content').toggleClass('active'); - $(this).toggleClass('active'); - - // change the text of the driver-usage-details-text depending on the class - if ( $(this).hasClass('active') ) { - $(this).find('.driver-usage-details-text').text('Hide usage details'); - } else { - $(this).find('.driver-usage-details-text').text('View usage details'); - } - }); - // Click handler for sortable table headers $($el_window).on('click', '.driver-usage-details-content-table th[data-sort]', function () { const column = $(this).data('sort'); @@ -103,6 +88,12 @@ const TabUsage = { renderUsageTable(); }); + + // Click handler for "Show more" to expand the table + $($el_window).on('click', '.usage-table-show-more', function () { + usageTableExpanded = true; + renderUsageTable(); + }); }, }; @@ -156,8 +147,17 @@ function renderUsageTable() { return 0; }); + // Determine how many rows to show + const hasMoreRows = sortedData.length > USAGE_TABLE_INITIAL_ROWS; + const rowsToShow = usageTableExpanded ? sortedData : sortedData.slice(0, USAGE_TABLE_INITIAL_ROWS); + const hiddenRowCount = sortedData.length - USAGE_TABLE_INITIAL_ROWS; + + // Build the wrapper with potential collapsed state + const isCollapsed = hasMoreRows && !usageTableExpanded; + let h = `
`; + // Build the table - let h = ''; + h += '
'; h += ` @@ -168,7 +168,7 @@ function renderUsageTable() { `; h += ''; - for ( const row of sortedData ) { + for ( const row of rowsToShow ) { h += ` @@ -179,10 +179,22 @@ function renderUsageTable() { h += ''; h += '
${row.resource}
'; + // Add "Show more" overlay if there are hidden rows + if ( isCollapsed ) { + h += `
+ +
`; + } + + h += '
'; + $('.driver-usage-details-content').html(h); } async function update_usage_details ($el_window) { + // Reset expanded state on refresh + usageTableExpanded = false; + // Add spinning animation and record start time const startTime = Date.now(); $($el_window).find('.update-usage-details-icon').css('animation', 'spin 1s linear infinite'); diff --git a/src/gui/src/UI/Dashboard/UIDashboard.js b/src/gui/src/UI/Dashboard/UIDashboard.js index 364088f19..131b8e41a 100644 --- a/src/gui/src/UI/Dashboard/UIDashboard.js +++ b/src/gui/src/UI/Dashboard/UIDashboard.js @@ -34,8 +34,8 @@ import TabUsage from './TabUsage.js'; // Registry of all available tabs const tabs = [ TabHome, - TabApps, - TabFiles, + // TabApps, + // TabFiles, TabUsage, ]; diff --git a/src/gui/src/css/style.css b/src/gui/src/css/style.css index c631eaa71..48221522f 100644 --- a/src/gui/src/css/style.css +++ b/src/gui/src/css/style.css @@ -4585,56 +4585,81 @@ fieldset[name=number-code] { flex-direction: column; margin-top: 20px; } -.driver-usage-details-text{ - cursor: pointer !important; - font-weight: 500; -} .driver-usage-details-content { - display: none; margin-top: 10px; border-radius: 4px; } -.driver-usage-details-content.active { - display: block !important; - flex-grow: 1; - overflow-y: scroll; +.driver-usage-details-content.visible { + display: block; } -.driver-usage-details{ - display: inline-block; - cursor: pointer; - display: flex; - align-items: center; - height: 25px; +/* Usage table wrapper for collapsed/expanded states */ +.usage-table-wrapper { + position: relative; } -.driver-usage-details .caret { - transition: transform 0.1s ease-in-out; - margin-right: 3px; - display: inline-block; - width: 20px; - height: 20px; +.usage-table-wrapper.collapsed { + position: relative; +} + +/* Fade overlay with gradient */ +.usage-table-fade-overlay { + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 100px; + background: linear-gradient(to bottom, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.85) 40%, + rgba(255, 255, 255, 1) 100%); display: flex; - align-items: center; + align-items: flex-end; justify-content: center; - opacity: 0.6; - margin-left: -3px; + padding-bottom: 12px; + pointer-events: none; } -.driver-usage-details .caret svg{ - width: 14px; - height: 14px; +/* Dark theme support for fade overlay */ +html.dark-mode .usage-table-fade-overlay { + background: linear-gradient(to bottom, + rgba(31, 31, 31, 0) 0%, + rgba(31, 31, 31, 0.85) 40%, + rgba(31, 31, 31, 1) 100%); } -.driver-usage-details.active .caret { - transform: rotate(90deg); - margin-top: -2px; +/* Show more button */ +.usage-table-show-more { + pointer-events: auto; + background: #3b82f6; + color: white; + border: none; + padding: 8px 20px; + border-radius: 6px; + font-size: 13px; + font-weight: 500; + cursor: pointer; + transition: background-color 0.15s ease, transform 0.1s ease; + box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3); } +.usage-table-show-more:hover { + background: #2563eb; + transform: translateY(-1px); +} + +.usage-table-show-more:active { + transform: translateY(0); +} + + .driver-usage-details-content-table { width: 100%; border-collapse: collapse; +} + +.usage-table-wrapper:not(.collapsed) .driver-usage-details-content-table { margin-bottom: 30px; }