solidstats 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +68 -0
  3. data/README.md +33 -0
  4. data/app/assets/javascripts/solidstats/application.js +257 -0
  5. data/app/assets/javascripts/solidstats/dashboard.js +225 -0
  6. data/app/assets/javascripts/solidstats/gem_metadata.js +554 -0
  7. data/app/assets/stylesheets/solidstats/application.css +6 -1
  8. data/app/assets/stylesheets/solidstats/components/action_button.css +99 -0
  9. data/app/assets/stylesheets/solidstats/components/dashboard.css +151 -0
  10. data/app/assets/stylesheets/solidstats/components/dashboard_header.css +93 -0
  11. data/app/assets/stylesheets/solidstats/components/dashboard_layout.css +97 -0
  12. data/app/assets/stylesheets/solidstats/components/gem_metadata.css +1403 -0
  13. data/app/assets/stylesheets/solidstats/components/navigation.css +80 -0
  14. data/app/assets/stylesheets/solidstats/components/quick_navigation.css +54 -0
  15. data/app/assets/stylesheets/solidstats/components/security.css +332 -0
  16. data/app/assets/stylesheets/solidstats/components/status_badge.css +58 -0
  17. data/app/assets/stylesheets/solidstats/components/summary_card.css +66 -0
  18. data/app/assets/stylesheets/solidstats/components/tab_navigation.css +95 -0
  19. data/app/components/solidstats/base_component.rb +88 -0
  20. data/app/components/solidstats/code_quality/code_quality_section_component.html.erb +0 -0
  21. data/app/components/solidstats/code_quality/code_quality_section_component.rb +0 -0
  22. data/app/components/solidstats/code_quality/section_component.html.erb +45 -0
  23. data/app/components/solidstats/code_quality/section_component.rb +34 -0
  24. data/app/components/solidstats/dashboard_header_component.html.erb +39 -0
  25. data/app/components/solidstats/dashboard_header_component.rb +33 -0
  26. data/app/components/solidstats/previews/action_button_component_preview/button_vs_link.html.erb +6 -0
  27. data/app/components/solidstats/previews/action_button_component_preview/sizes.html.erb +6 -0
  28. data/app/components/solidstats/previews/action_button_component_preview/variants.html.erb +6 -0
  29. data/app/components/solidstats/previews/action_button_component_preview/with_icons.html.erb +6 -0
  30. data/app/components/solidstats/previews/action_button_component_preview.rb +64 -0
  31. data/app/components/solidstats/previews/navigation_component_preview.rb +74 -0
  32. data/app/components/solidstats/previews/stats_overview_component_preview.rb +100 -0
  33. data/app/components/solidstats/previews/status_badge_component_preview/sizes.html.erb +6 -0
  34. data/app/components/solidstats/previews/status_badge_component_preview/statuses.html.erb +6 -0
  35. data/app/components/solidstats/previews/status_badge_component_preview/with_icons.html.erb +6 -0
  36. data/app/components/solidstats/previews/status_badge_component_preview.rb +49 -0
  37. data/app/components/solidstats/previews/summary_card_component_preview/clickable.html.erb +9 -0
  38. data/app/components/solidstats/previews/summary_card_component_preview/dashboard_layout.html.erb +9 -0
  39. data/app/components/solidstats/previews/summary_card_component_preview/statuses.html.erb +6 -0
  40. data/app/components/solidstats/previews/summary_card_component_preview/value_formats.html.erb +6 -0
  41. data/app/components/solidstats/previews/summary_card_component_preview.rb +67 -0
  42. data/app/components/solidstats/quick_navigation_component.html.erb +8 -0
  43. data/app/components/solidstats/quick_navigation_component.rb +21 -0
  44. data/app/components/solidstats/security/gem_impact_analysis_component.html.erb +44 -0
  45. data/app/components/solidstats/security/gem_impact_analysis_component.rb +45 -0
  46. data/app/components/solidstats/security/overview_component.html.erb +21 -0
  47. data/app/components/solidstats/security/overview_component.rb +104 -0
  48. data/app/components/solidstats/security/section_component.html.erb +26 -0
  49. data/app/components/solidstats/security/section_component.rb +52 -0
  50. data/app/components/solidstats/security/timeline_component.html.erb +39 -0
  51. data/app/components/solidstats/security/timeline_component.rb +43 -0
  52. data/app/components/solidstats/tasks_section_component.html.erb +17 -0
  53. data/app/components/solidstats/tasks_section_component.rb +22 -0
  54. data/app/components/solidstats/ui/action_button_component.html.erb +6 -0
  55. data/app/components/solidstats/ui/action_button_component.rb +71 -0
  56. data/app/components/solidstats/ui/dashboard_layout_component.html.erb +19 -0
  57. data/app/components/solidstats/ui/dashboard_layout_component.rb +85 -0
  58. data/app/components/solidstats/ui/navigation_component.html.erb +34 -0
  59. data/app/components/solidstats/ui/navigation_component.rb +72 -0
  60. data/app/components/solidstats/ui/stats_overview_component.html.erb +14 -0
  61. data/app/components/solidstats/ui/stats_overview_component.rb +78 -0
  62. data/app/components/solidstats/ui/status_badge_component.html.erb +6 -0
  63. data/app/components/solidstats/ui/status_badge_component.rb +42 -0
  64. data/app/components/solidstats/ui/summary_card_component.html.erb +12 -0
  65. data/app/components/solidstats/ui/summary_card_component.rb +63 -0
  66. data/app/components/solidstats/ui/tab_navigation_component.html.erb +22 -0
  67. data/app/components/solidstats/ui/tab_navigation_component.rb +79 -0
  68. data/app/controllers/solidstats/dashboard_controller.rb +8 -5
  69. data/app/controllers/solidstats/gem_metadata_controller.rb +12 -0
  70. data/app/helpers/solidstats/application_helper.rb +42 -0
  71. data/app/services/solidstats/gem_metadata/fetcher_service.rb +136 -0
  72. data/app/services/solidstats/log_size_monitor_service.rb +10 -10
  73. data/app/views/layouts/solidstats/application.html.erb +2 -1
  74. data/app/views/solidstats/dashboard/index.html.erb +67 -1337
  75. data/app/views/solidstats/gem_metadata/_panel.html.erb +419 -0
  76. data/config/routes.rb +5 -2
  77. data/lib/generators/solidstats/feature/feature_generator.rb +170 -0
  78. data/lib/generators/solidstats/feature/templates/component.html.erb +84 -0
  79. data/lib/generators/solidstats/feature/templates/component.rb.erb +103 -0
  80. data/lib/generators/solidstats/feature/templates/component.scss +243 -0
  81. data/lib/generators/solidstats/feature/templates/component_test.rb.erb +183 -0
  82. data/lib/generators/solidstats/feature/templates/controller.rb.erb +44 -0
  83. data/lib/generators/solidstats/feature/templates/controller_test.rb.erb +111 -0
  84. data/lib/generators/solidstats/feature/templates/detail_view.html.erb +755 -0
  85. data/lib/generators/solidstats/feature/templates/preview.rb.erb +107 -0
  86. data/lib/generators/solidstats/feature/templates/service.rb.erb +132 -0
  87. data/lib/generators/solidstats/feature/templates/service_test.rb.erb +109 -0
  88. data/lib/generators/solidstats/install_generator.rb +109 -0
  89. data/lib/generators/solidstats/templates/initializer.rb +112 -0
  90. data/lib/solidstats/asset_compatibility.rb +238 -0
  91. data/lib/solidstats/asset_manifest.rb +205 -0
  92. data/lib/solidstats/engine.rb +114 -9
  93. data/lib/solidstats/version.rb +1 -1
  94. data/lib/solidstats.rb +299 -2
  95. data/lib/tasks/solidstats_install.rake +122 -2
  96. metadata +97 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b1e51da79f65a50793bc1bfc777ed19df5d81795739ceda8558d3261745b53ae
4
- data.tar.gz: e58f0018fd96135789f192372b2b85f7165cf51a6921fa7e2a56057bebdb14b5
3
+ metadata.gz: 1087b955c118f56234de6e254a1c4f8fbb25184caccae8e72756d277b77d8a4d
4
+ data.tar.gz: fd69b3cec25f03c5c2854b76b7ea5fe5154c762a6b22dbe50ef6e423d780aef5
5
5
  SHA512:
6
- metadata.gz: 3fc113aaaf952e1d9a320b55618b72d4b6025ec81d3bc274f29a090ddbc6a0f500bbd50aa2873d8e02d850dac992ef28214e5709f0160bc2ce0e88e348182f0a
7
- data.tar.gz: 976d3989eeffc2781c1040846af1435147be33a407b6e5a2bae1db8dfba091f9678635b1863c3dcc36916adf5d2b334cc0f0a7d523225066bb4fdac074ebf270
6
+ metadata.gz: 45d75fb7fbbccb543628e14747177807f8fb7b652be35cb74d05a5fe65b666731c4290413ce7e0da2a5038cb9d68c265916bbe2421949b073424eb5b603018ad
7
+ data.tar.gz: 8d425d7b2f9abcba38460f5ec97fa0e5021fbc1542e00442c97e7b099707bc4a11d578f53f440fc213885e8172706fa947fd57f025283abfdfe09b93d8a965e6
data/CHANGELOG.md CHANGED
@@ -5,6 +5,74 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.0.0] - 2025-05-26
9
+
10
+ ### Added
11
+ - **Complete Gem Metadata System**: Comprehensive gem dependency tracking and management
12
+ - Dual-view interface with grid and table layouts
13
+ - Real-time search and filtering by gem name, status, and other criteria
14
+ - Smart sorting by name, status, release date, and version information
15
+ - Status indicators for outdated, up-to-date, and unavailable gems
16
+ - Dependency visualization with runtime dependency details
17
+ - Version comparison showing current vs. latest versions
18
+ - CSV export functionality for gem data analysis
19
+ - Refresh capability to update gem metadata from RubyGems API
20
+ - Responsive design with 3-cards-per-row grid (2 on tablet, 1 on mobile)
21
+
22
+ - **View Component Architecture**: Modern component-based UI system for maintainable code
23
+ - BaseComponent foundation for all UI components
24
+ - Specialized components for different dashboard sections
25
+ - Component preview system for development and testing
26
+ - Reusable UI components (ActionButton, SummaryCard, Navigation, TabNavigation)
27
+ - Clean separation of concerns between HTML, CSS, and JavaScript
28
+
29
+ - **Feature Generator System**: Automated development tools for rapid feature development
30
+ - `rails g solidstats:feature` generator for creating new dashboard sections
31
+ - Automatic component, controller, view, and asset generation
32
+ - Built-in best practices and conventions
33
+ - Template-based code generation with customizable options
34
+
35
+ - **CSS Component Architecture**: Modular styling system with 1,631+ lines of extracted CSS
36
+ - Dedicated component stylesheets for maintainable styling
37
+ - Conflict-free CSS with proper specificity management
38
+ - Responsive design patterns and mobile-first approach
39
+ - Consistent design tokens and reusable style patterns
40
+
41
+ ### Fixed
42
+ - **Gem Metadata Table Layout**: Eliminated horizontal scrolling issues
43
+ - Changed table wrapper from `overflow-x: auto` to `width: 100%`
44
+ - Removed restrictive `white-space: nowrap` from table headers
45
+ - Implemented percentage-based column widths for better responsiveness
46
+ - Added proper word-wrapping for long content
47
+
48
+ - **Empty State Positioning**: Perfect centering for "No matching gems found" messages
49
+ - Implemented flexbox-based centering for both grid and table views
50
+ - Added proper fallback support for older browsers
51
+ - Enhanced visual hierarchy and user experience
52
+
53
+ - **Navigation System**: Fixed section-based navigation throughout the application
54
+ - Removed external routing in favor of seamless section switching
55
+ - Updated all navigation components to use `data-section` attributes
56
+ - Eliminated page reloads when switching between dashboard sections
57
+ - Consistent navigation behavior across all components
58
+
59
+ - **CSS Conflicts**: Resolved styling conflicts between component stylesheets
60
+ - Fixed security.css interference with gem metadata styling
61
+ - Implemented proper CSS specificity to prevent style bleeding
62
+ - Added scoped styling for component isolation
63
+
64
+ ### Improved
65
+ - **Performance Optimizations**: Enhanced user experience with better responsiveness
66
+ - Debounced search functionality to reduce unnecessary API calls
67
+ - Efficient DOM manipulation and rendering
68
+ - Optimized CSS delivery and reduced file sizes
69
+
70
+ - **Cross-Browser Compatibility**: Enhanced support for different browsers and devices
71
+ - Fallback CSS for older browser versions
72
+ - Progressive enhancement patterns
73
+ - Improved mobile experience and touch interactions
74
+
75
+
8
76
  ## [1.1.0] - 2025-05-23
9
77
 
10
78
  ### Added
data/README.md CHANGED
@@ -1,18 +1,39 @@
1
1
  Solidstats is a local-only Rails engine that shows your project's health at `/solidstats`. The dashboard provides real-time insights into your application's security, code quality, and development tasks.
2
2
 
3
3
  ## Features
4
+
5
+ ### Core Dashboard
4
6
  - Interactive security dashboard with real-time refresh capability
5
7
  - Comprehensive gem vulnerability analysis with severity breakdown
6
8
  - Visual security score rating (A+, B, C) and metrics
7
9
  - Bundler Audit scan with detailed remediation suggestions
8
10
  - Interactive vulnerability details with patched version information
9
11
  - Gem impact analysis showing affected gems by severity
12
+
13
+ ### Gem Metadata System
14
+ - **Complete Gem Analysis Platform**: Comprehensive gem metadata dashboard with detailed information about all gems in your project
15
+ - **Dual-View System**: Switch between table and grid layouts for optimal data presentation
16
+ - **Table View**: Sortable columns with gem name, version, description, dependencies, and status
17
+ - **Grid View**: Card-based layout with 3 cards per row (responsive: 3 on desktop, 2 on tablet, 1 on mobile)
18
+ - **Advanced Filtering**: Real-time search and filtering across all gem attributes
19
+ - **Dependency Analysis**: View gem dependencies with version compatibility information
20
+ - **Status Monitoring**: Real-time health indicators and gem status tracking
21
+ - **Download Statistics**: Popularity metrics and download statistics for gems
22
+ - **License Information**: Security compliance and license tracking
23
+
24
+ ### System Monitoring
10
25
  - Log Size Monitor for tracking and managing application log files
11
26
  - Log file truncation tool for individual or all log files
12
27
  - Rubocop offense count and quality metrics
13
28
  - TODO/FIXME tracker with file hotspots
14
29
  - Test coverage summary
15
30
 
31
+ ### Architecture
32
+ - **View Component Architecture**: Modern, maintainable component-based UI system
33
+ - **Feature Generator System**: Automated scaffolding for rapid development
34
+ - **CSS Component Architecture**: Organized, conflict-free styling with responsive design
35
+ - **Cross-Browser Compatibility**: Enhanced support across modern browsers
36
+
16
37
  ## Compatibility
17
38
 
18
39
  - Ruby 2.7+: Compatible with Rails 6.1 through Rails 7.0
@@ -74,6 +95,18 @@ You can refresh the dashboard data at any time by clicking the "Refresh" button
74
95
  3. Show real-time feedback during the refresh process
75
96
  4. Update the "Last Updated" timestamp
76
97
 
98
+ ### Gem Metadata
99
+ Comprehensive gem analysis and management platform featuring:
100
+ - **Complete Gem Information**: Detailed metadata for all gems in your project including versions, descriptions, dependencies, and status
101
+ - **Flexible Views**:
102
+ - **Table View**: Sortable table with comprehensive gem information, perfect for detailed analysis
103
+ - **Grid View**: Card-based layout showing 3 gems per row with visual appeal and responsive design
104
+ - **Advanced Search & Filtering**: Real-time filtering across gem names, descriptions, and dependencies
105
+ - **Dependency Analysis**: View and analyze gem dependencies with version information
106
+ - **Status Monitoring**: Health indicators and status tracking for all gems
107
+ - **Download Metrics**: Popularity statistics and download information
108
+ - **License Tracking**: Security compliance and license information for each gem
109
+
77
110
  ### Code Quality
78
111
  Displays code quality metrics, test coverage, and code health indicators.
79
112
 
@@ -0,0 +1,257 @@
1
+ // Solidstats JavaScript Application Manifest
2
+ // This file includes all JavaScript modules for the Solidstats dashboard
3
+ //
4
+ //= require dashboard
5
+ //= require_tree .
6
+
7
+ // Initialize dashboard when DOM is ready
8
+ document.addEventListener('DOMContentLoaded', function() {
9
+ console.log('🚀 Solidstats Dashboard JavaScript loaded');
10
+
11
+ // Ensure dashboard module is available
12
+ if (typeof window.SolidstatsDashboard !== 'undefined') {
13
+ console.log('✅ Dashboard module loaded successfully');
14
+
15
+ // Verify key functions are available
16
+ const requiredFunctions = ['init', 'navigateToSection', 'setupMainNavigation'];
17
+ const missingFunctions = requiredFunctions.filter(func =>
18
+ typeof window.SolidstatsDashboard[func] !== 'function'
19
+ );
20
+
21
+ if (missingFunctions.length === 0) {
22
+ console.log('✅ All dashboard functions available');
23
+ } else {
24
+ console.warn('⚠️ Missing functions:', missingFunctions);
25
+ }
26
+ } else {
27
+ console.warn('⚠️ Dashboard module not found');
28
+ console.log('Available globals:', Object.keys(window).filter(k => k.includes('Solid')));
29
+ }
30
+ });
31
+
32
+
33
+ // Dashboard JavaScript functionality
34
+
35
+ // Create the Solidstats Dashboard namespace
36
+ window.SolidstatsDashboard = window.SolidstatsDashboard || {};
37
+
38
+ // Main dashboard initialization
39
+ window.SolidstatsDashboard.init = function() {
40
+ handleUrlNavigation();
41
+ setupEventListeners();
42
+ };
43
+
44
+ document.addEventListener('DOMContentLoaded', function() {
45
+ window.SolidstatsDashboard.init();
46
+ });
47
+
48
+ function handleUrlNavigation() {
49
+ // Parse URL hash for initial navigation
50
+ const hash = window.location.hash.substring(1);
51
+ if (hash) {
52
+ const [section, tab] = hash.split('/');
53
+ if (section) {
54
+ setTimeout(() => {
55
+ window.SolidstatsDashboard.navigateToSection(section, tab, true);
56
+ }, 100);
57
+ }
58
+ }
59
+ }
60
+
61
+ function setupEventListeners() {
62
+ window.SolidstatsDashboard.setupMainNavigation();
63
+ window.SolidstatsDashboard.setupTabNavigation();
64
+ window.SolidstatsDashboard.setupQuickNavigation();
65
+ window.SolidstatsDashboard.setupSummaryCardNavigation();
66
+ }
67
+
68
+ // Function to update URL hash with current state
69
+ window.SolidstatsDashboard.updateUrlHash = function(section, tab = null) {
70
+ let hash = '#' + section;
71
+ if (tab) {
72
+ hash += '/' + tab;
73
+ }
74
+ history.replaceState(null, null, hash);
75
+ };
76
+
77
+ // Function to navigate to section and tab
78
+ window.SolidstatsDashboard.navigateToSection = function(section, tab = null, shouldScroll = false) {
79
+ // Remove active class from all nav items and sections
80
+ document.querySelectorAll('.nav-item').forEach(item => item.classList.remove('active'));
81
+ document.querySelectorAll('.dashboard-section').forEach(section => section.classList.remove('active'));
82
+
83
+ // Add active class to matching nav item
84
+ const navItem = document.querySelector(`.nav-item[data-section="${section}"]`);
85
+ if (navItem) {
86
+ navItem.classList.add('active');
87
+ }
88
+
89
+ // Show corresponding section
90
+ const sectionElement = document.getElementById(section);
91
+ if (sectionElement) {
92
+ sectionElement.classList.add('active');
93
+
94
+ // If tab is specified, activate that tab
95
+ if (tab) {
96
+ const sectionEl = sectionElement;
97
+ if (sectionEl) {
98
+ // Deactivate all tabs first
99
+ sectionEl.querySelectorAll('.tab-item').forEach(item => item.classList.remove('active'));
100
+ sectionEl.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
101
+
102
+ // Activate the target tab
103
+ const targetTabItem = sectionEl.querySelector(`.tab-item[data-tab="${tab}"]`);
104
+ const targetTabContent = sectionEl.querySelector(`#${tab}`);
105
+
106
+ if (targetTabItem) targetTabItem.classList.add('active');
107
+ if (targetTabContent) targetTabContent.classList.add('active');
108
+ }
109
+ }
110
+
111
+ // Scroll to section if requested (with a small delay to ensure rendering)
112
+ if (shouldScroll) {
113
+ setTimeout(() => {
114
+ sectionElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
115
+ }, 100);
116
+ }
117
+
118
+ // Update URL hash
119
+ window.SolidstatsDashboard.updateUrlHash(section, tab);
120
+ }
121
+ };
122
+
123
+ // Main navigation
124
+ window.SolidstatsDashboard.setupMainNavigation = function() {
125
+ document.querySelectorAll('.nav-item').forEach(function(navItem) {
126
+ navItem.addEventListener('click', function(e) {
127
+ e.preventDefault();
128
+
129
+ // Get section ID from data attribute
130
+ const sectionId = this.getAttribute('data-section');
131
+
132
+ // Navigate to the section
133
+ window.SolidstatsDashboard.navigateToSection(sectionId);
134
+ });
135
+ });
136
+ };
137
+
138
+ // Tab navigation
139
+ window.SolidstatsDashboard.setupTabNavigation = function() {
140
+ document.querySelectorAll('.tab-item').forEach(function(tabItem) {
141
+ if (!tabItem.hasAttribute('disabled')) {
142
+ tabItem.addEventListener('click', function(e) {
143
+ e.preventDefault();
144
+ const tabContainer = this.closest('.dashboard-section');
145
+ const tabId = this.getAttribute('data-tab');
146
+
147
+ // Find the current active section
148
+ const currentSection = tabContainer.id;
149
+
150
+ // Remove active class from all tab items and tabs within this container
151
+ tabContainer.querySelectorAll('.tab-item').forEach(item => item.classList.remove('active'));
152
+ tabContainer.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
153
+
154
+ // Add active class to clicked item and corresponding tab
155
+ this.classList.add('active');
156
+ const targetContent = tabContainer.querySelector(`#${tabId}`);
157
+ if (targetContent) {
158
+ targetContent.classList.add('active');
159
+ }
160
+
161
+ // Update URL hash with current section and tab
162
+ window.SolidstatsDashboard.updateUrlHash(currentSection, tabId);
163
+ });
164
+ }
165
+ });
166
+ };
167
+
168
+ // Quick navigation
169
+ window.SolidstatsDashboard.setupQuickNavigation = function() {
170
+ document.querySelectorAll('.quick-nav-item').forEach(function(navItem) {
171
+ navItem.addEventListener('click', function(e) {
172
+ e.preventDefault();
173
+ const targetId = this.getAttribute('href').substring(1);
174
+
175
+ // Navigate to the specified section
176
+ window.SolidstatsDashboard.navigateToSection(targetId);
177
+
178
+ // Close the quick nav menu
179
+ document.querySelector('.quick-nav-menu').style.display = 'none';
180
+ });
181
+ });
182
+ };
183
+
184
+ // Summary card navigation
185
+ window.SolidstatsDashboard.setupSummaryCardNavigation = function() {
186
+ document.querySelectorAll('.summary-card').forEach(function(card) {
187
+ card.addEventListener('click', function() {
188
+ const section = this.getAttribute('data-section');
189
+ const tab = this.getAttribute('data-tab');
190
+
191
+ // Navigate to the specified section and tab, with scrolling
192
+ window.SolidstatsDashboard.navigateToSection(section, tab, true);
193
+ });
194
+ });
195
+ };
196
+
197
+ // Refresh functionality
198
+ window.SolidstatsDashboard.refreshAudit = function() {
199
+ // Show loading indicator
200
+ const refreshButton = document.querySelector('.action-button');
201
+ const originalText = refreshButton.innerHTML;
202
+ refreshButton.innerHTML = '<span class="action-icon">⟳</span> Refreshing...';
203
+ refreshButton.disabled = true;
204
+
205
+ // Make AJAX call to refresh endpoint
206
+ fetch('/solidstats/refresh', {
207
+ method: 'GET',
208
+ headers: {
209
+ 'Accept': 'application/json',
210
+ 'X-Requested-With': 'XMLHttpRequest'
211
+ },
212
+ credentials: 'same-origin'
213
+ })
214
+ .then(response => {
215
+ if (!response.ok) {
216
+ throw new Error('Network response was not ok');
217
+ }
218
+ return response.json();
219
+ })
220
+ .then(data => {
221
+ // Update the dashboard with fresh data
222
+ location.reload();
223
+
224
+ // Show success notification
225
+ window.SolidstatsDashboard.showNotification('Dashboard data refreshed successfully', 'success');
226
+
227
+ // Reset button state
228
+ refreshButton.innerHTML = originalText;
229
+ refreshButton.disabled = false;
230
+ })
231
+ .catch(error => {
232
+ console.error('Error refreshing data:', error);
233
+
234
+ // Show error notification
235
+ window.SolidstatsDashboard.showNotification('Failed to refresh data. Please try again.', 'error');
236
+
237
+ // Reset button state
238
+ refreshButton.innerHTML = originalText;
239
+ refreshButton.disabled = false;
240
+ });
241
+ };
242
+
243
+ // Notification system
244
+ window.SolidstatsDashboard.showNotification = function(message, type) {
245
+ // Simple notification system
246
+ const notification = document.createElement('div');
247
+ notification.className = `notification notification-${type}`;
248
+ notification.textContent = message;
249
+
250
+ // Add to body
251
+ document.body.appendChild(notification);
252
+
253
+ // Remove after 3 seconds
254
+ setTimeout(() => {
255
+ notification.remove();
256
+ }, 3000);
257
+ };
@@ -0,0 +1,225 @@
1
+ // Dashboard JavaScript functionality
2
+
3
+ // Create the Solidstats Dashboard namespace
4
+ window.SolidstatsDashboard = window.SolidstatsDashboard || {};
5
+
6
+ // Main dashboard initialization
7
+ window.SolidstatsDashboard.init = function() {
8
+ handleUrlNavigation();
9
+ setupEventListeners();
10
+ };
11
+
12
+ document.addEventListener('DOMContentLoaded', function() {
13
+ window.SolidstatsDashboard.init();
14
+ });
15
+
16
+ function handleUrlNavigation() {
17
+ // Parse URL hash for initial navigation
18
+ const hash = window.location.hash.substring(1);
19
+ if (hash) {
20
+ const [section, tab] = hash.split('/');
21
+ if (section) {
22
+ setTimeout(() => {
23
+ window.SolidstatsDashboard.navigateToSection(section, tab, true);
24
+ }, 100);
25
+ }
26
+ }
27
+ }
28
+
29
+ function setupEventListeners() {
30
+ window.SolidstatsDashboard.setupMainNavigation();
31
+ window.SolidstatsDashboard.setupTabNavigation();
32
+ window.SolidstatsDashboard.setupQuickNavigation();
33
+ window.SolidstatsDashboard.setupSummaryCardNavigation();
34
+ }
35
+
36
+ // Function to update URL hash with current state
37
+ window.SolidstatsDashboard.updateUrlHash = function(section, tab = null) {
38
+ let hash = '#' + section;
39
+ if (tab) {
40
+ hash += '/' + tab;
41
+ }
42
+ history.replaceState(null, null, hash);
43
+ };
44
+
45
+ // Function to navigate to section and tab
46
+ window.SolidstatsDashboard.navigateToSection = function(section, tab = null, shouldScroll = false) {
47
+ // Remove active class from all nav items and sections
48
+ document.querySelectorAll('.nav-item').forEach(item => item.classList.remove('active'));
49
+ document.querySelectorAll('.dashboard-section').forEach(section => section.classList.remove('active'));
50
+
51
+ // Add active class to matching nav item
52
+ const navItem = document.querySelector(`.nav-item[data-section="${section}"]`);
53
+ if (navItem) {
54
+ navItem.classList.add('active');
55
+ }
56
+
57
+ // Show corresponding section
58
+ const sectionElement = document.getElementById(section);
59
+ if (sectionElement) {
60
+ sectionElement.classList.add('active');
61
+
62
+ // If tab is specified, activate that tab
63
+ if (tab) {
64
+ const sectionEl = sectionElement;
65
+ if (sectionEl) {
66
+ // Deactivate all tabs first
67
+ sectionEl.querySelectorAll('.tab-item').forEach(item => item.classList.remove('active'));
68
+ sectionEl.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
69
+
70
+ // Activate the target tab
71
+ const targetTabItem = sectionEl.querySelector(`.tab-item[data-tab="${tab}"]`);
72
+ const targetTabContent = sectionEl.querySelector(`#${tab}`);
73
+
74
+ if (targetTabItem) targetTabItem.classList.add('active');
75
+ if (targetTabContent) targetTabContent.classList.add('active');
76
+ }
77
+ }
78
+
79
+ // Scroll to section if requested (with a small delay to ensure rendering)
80
+ if (shouldScroll) {
81
+ setTimeout(() => {
82
+ sectionElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
83
+ }, 100);
84
+ }
85
+
86
+ // Update URL hash
87
+ window.SolidstatsDashboard.updateUrlHash(section, tab);
88
+ }
89
+ };
90
+
91
+ // Main navigation
92
+ window.SolidstatsDashboard.setupMainNavigation = function() {
93
+ document.querySelectorAll('.nav-item').forEach(function(navItem) {
94
+ navItem.addEventListener('click', function(e) {
95
+ e.preventDefault();
96
+
97
+ // Get section ID from data attribute
98
+ const sectionId = this.getAttribute('data-section');
99
+
100
+ // Navigate to the section
101
+ window.SolidstatsDashboard.navigateToSection(sectionId);
102
+ });
103
+ });
104
+ };
105
+
106
+ // Tab navigation
107
+ window.SolidstatsDashboard.setupTabNavigation = function() {
108
+ document.querySelectorAll('.tab-item').forEach(function(tabItem) {
109
+ if (!tabItem.hasAttribute('disabled')) {
110
+ tabItem.addEventListener('click', function(e) {
111
+ e.preventDefault();
112
+ const tabContainer = this.closest('.dashboard-section');
113
+ const tabId = this.getAttribute('data-tab');
114
+
115
+ // Find the current active section
116
+ const currentSection = tabContainer.id;
117
+
118
+ // Remove active class from all tab items and tabs within this container
119
+ tabContainer.querySelectorAll('.tab-item').forEach(item => item.classList.remove('active'));
120
+ tabContainer.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
121
+
122
+ // Add active class to clicked item and corresponding tab
123
+ this.classList.add('active');
124
+ const targetContent = tabContainer.querySelector(`#${tabId}`);
125
+ if (targetContent) {
126
+ targetContent.classList.add('active');
127
+ }
128
+
129
+ // Update URL hash with current section and tab
130
+ window.SolidstatsDashboard.updateUrlHash(currentSection, tabId);
131
+ });
132
+ }
133
+ });
134
+ };
135
+
136
+ // Quick navigation
137
+ window.SolidstatsDashboard.setupQuickNavigation = function() {
138
+ document.querySelectorAll('.quick-nav-item').forEach(function(navItem) {
139
+ navItem.addEventListener('click', function(e) {
140
+ e.preventDefault();
141
+ const targetId = this.getAttribute('href').substring(1);
142
+
143
+ // Navigate to the specified section
144
+ window.SolidstatsDashboard.navigateToSection(targetId);
145
+
146
+ // Close the quick nav menu
147
+ document.querySelector('.quick-nav-menu').style.display = 'none';
148
+ });
149
+ });
150
+ };
151
+
152
+ // Summary card navigation
153
+ window.SolidstatsDashboard.setupSummaryCardNavigation = function() {
154
+ document.querySelectorAll('.summary-card').forEach(function(card) {
155
+ card.addEventListener('click', function() {
156
+ const section = this.getAttribute('data-section');
157
+ const tab = this.getAttribute('data-tab');
158
+
159
+ // Navigate to the specified section and tab, with scrolling
160
+ window.SolidstatsDashboard.navigateToSection(section, tab, true);
161
+ });
162
+ });
163
+ };
164
+
165
+ // Refresh functionality
166
+ window.SolidstatsDashboard.refreshAudit = function() {
167
+ // Show loading indicator
168
+ const refreshButton = document.querySelector('.action-button');
169
+ const originalText = refreshButton.innerHTML;
170
+ refreshButton.innerHTML = '<span class="action-icon">⟳</span> Refreshing...';
171
+ refreshButton.disabled = true;
172
+
173
+ // Make AJAX call to refresh endpoint
174
+ fetch('/solidstats/refresh', {
175
+ method: 'GET',
176
+ headers: {
177
+ 'Accept': 'application/json',
178
+ 'X-Requested-With': 'XMLHttpRequest'
179
+ },
180
+ credentials: 'same-origin'
181
+ })
182
+ .then(response => {
183
+ if (!response.ok) {
184
+ throw new Error('Network response was not ok');
185
+ }
186
+ return response.json();
187
+ })
188
+ .then(data => {
189
+ // Update the dashboard with fresh data
190
+ location.reload();
191
+
192
+ // Show success notification
193
+ window.SolidstatsDashboard.showNotification('Dashboard data refreshed successfully', 'success');
194
+
195
+ // Reset button state
196
+ refreshButton.innerHTML = originalText;
197
+ refreshButton.disabled = false;
198
+ })
199
+ .catch(error => {
200
+ console.error('Error refreshing data:', error);
201
+
202
+ // Show error notification
203
+ window.SolidstatsDashboard.showNotification('Failed to refresh data. Please try again.', 'error');
204
+
205
+ // Reset button state
206
+ refreshButton.innerHTML = originalText;
207
+ refreshButton.disabled = false;
208
+ });
209
+ };
210
+
211
+ // Notification system
212
+ window.SolidstatsDashboard.showNotification = function(message, type) {
213
+ // Simple notification system
214
+ const notification = document.createElement('div');
215
+ notification.className = `notification notification-${type}`;
216
+ notification.textContent = message;
217
+
218
+ // Add to body
219
+ document.body.appendChild(notification);
220
+
221
+ // Remove after 3 seconds
222
+ setTimeout(() => {
223
+ notification.remove();
224
+ }, 3000);
225
+ };