solidstats 3.0.0.beta.1 → 3.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +33 -0
  3. data/app/assets/javascripts/solidstats/application.js +257 -0
  4. data/app/assets/javascripts/solidstats/dashboard.js +179 -0
  5. data/app/assets/stylesheets/solidstats/application.css +6 -1
  6. data/app/controllers/solidstats/dashboard_controller.rb +28 -35
  7. data/app/controllers/solidstats/gem_metadata_controller.rb +12 -0
  8. data/app/controllers/solidstats/logs_controller.rb +12 -12
  9. data/app/controllers/solidstats/performance_controller.rb +2 -2
  10. data/app/controllers/solidstats/productivity_controller.rb +10 -10
  11. data/app/controllers/solidstats/quality_controller.rb +32 -32
  12. data/app/controllers/solidstats/securities_controller.rb +7 -7
  13. data/app/helpers/solidstats/application_helper.rb +10 -10
  14. data/app/helpers/solidstats/performance_helper.rb +32 -32
  15. data/app/helpers/solidstats/productivity_helper.rb +20 -20
  16. data/app/services/solidstats/bundler_audit_service.rb +13 -13
  17. data/app/services/solidstats/coverage_compass_service.rb +59 -59
  18. data/app/services/solidstats/load_lens_service.rb +90 -70
  19. data/app/services/solidstats/log_size_monitor_service.rb +59 -59
  20. data/app/services/solidstats/my_todo_service.rb +68 -68
  21. data/app/services/solidstats/style_patrol_service.rb +44 -44
  22. data/app/views/layouts/solidstats/application.html.erb +1 -1
  23. data/app/views/solidstats/shared/_quick_actions.html.erb +1 -1
  24. data/config/routes.rb +4 -4
  25. data/lib/generators/solidstats/clean/clean_generator.rb +24 -0
  26. data/lib/generators/solidstats/clean/templates/README +8 -0
  27. data/lib/generators/solidstats/install/install_generator.rb +32 -17
  28. data/lib/generators/solidstats/templates/initializer.rb +112 -0
  29. data/lib/solidstats/asset_compatibility.rb +238 -0
  30. data/lib/solidstats/asset_manifest.rb +205 -0
  31. data/lib/solidstats/engine.rb +49 -9
  32. data/lib/solidstats/version.rb +1 -1
  33. data/lib/solidstats.rb +24 -11
  34. data/lib/tasks/solidstats.rake +67 -0
  35. data/lib/tasks/solidstats_performance.rake +6 -29
  36. data/lib/tasks/solidstats_tasks.rake +16 -0
  37. metadata +14 -5
  38. data/lib/tasks/solidstats_install.rake +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd313b23c968d18619f31b5157b96b2a9c75435398121e800deaf6c2e7d4bfd4
4
- data.tar.gz: 1f7407373917b47d1ef56ebe7e03138cd36ef111517a930e0c8b6354bc2e389f
3
+ metadata.gz: 81043c13f33e9684bcc3ea3d99f47f63d9bd17873c1f6792988e9bf752a7e572
4
+ data.tar.gz: dc4c6a7edd2f8720197e0beba69752deb847bc72422a4c6351670d6f448a04bd
5
5
  SHA512:
6
- metadata.gz: adcd27c59ddc3e68c8843583dd274d96312959155c3a0491f06540cd1eb474681bcb7de407b6d286bc1a9e97dd03b4e94099b3e40f5232a37929b3c2565e1cd2
7
- data.tar.gz: 515a1510e1911e65f8b7784bf746341112f469de7800170923f3f3f716c537825fe742afa266ecb5fc091ad9c1e3c7aebfbef328eef964da69960a7b0d63506b
6
+ metadata.gz: ae9f6ca74cfe86e05cec7f9b0be6ce1b4621b3f672def7a08a1f94790fd81a8715da2c6927825ebf7c684ce3dbd84081a825a63268963af985ad0dcebbaddbc2
7
+ data.tar.gz: f856e1776b287e7ca34752b3a6ad27b2cdf842ac7b955fbf95f2e265a4bb45a9820b05bc2403f7c3d6f03301e35dca78890192f14344cfe176a952ecdcadac16
data/README.md CHANGED
@@ -1,12 +1,27 @@
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
  - LoadLens - Development performance monitoring with request tracking
@@ -14,6 +29,12 @@ Solidstats is a local-only Rails engine that shows your project's health at `/so
14
29
  - TODO/FIXME tracker with file hotspots
15
30
  - Test coverage summary
16
31
 
32
+ ### Architecture
33
+ - **View Component Architecture**: Modern, maintainable component-based UI system
34
+ - **Feature Generator System**: Automated scaffolding for rapid development
35
+ - **CSS Component Architecture**: Organized, conflict-free styling with responsive design
36
+ - **Cross-Browser Compatibility**: Enhanced support across modern browsers
37
+
17
38
  ## Compatibility
18
39
 
19
40
  - Ruby 2.7+: Compatible with Rails 6.1 through Rails 7.0
@@ -75,6 +96,18 @@ You can refresh the dashboard data at any time by clicking the "Refresh" button
75
96
  3. Show real-time feedback during the refresh process
76
97
  4. Update the "Last Updated" timestamp
77
98
 
99
+ ### Gem Metadata
100
+ Comprehensive gem analysis and management platform featuring:
101
+ - **Complete Gem Information**: Detailed metadata for all gems in your project including versions, descriptions, dependencies, and status
102
+ - **Flexible Views**:
103
+ - **Table View**: Sortable table with comprehensive gem information, perfect for detailed analysis
104
+ - **Grid View**: Card-based layout showing 3 gems per row with visual appeal and responsive design
105
+ - **Advanced Search & Filtering**: Real-time filtering across gem names, descriptions, and dependencies
106
+ - **Dependency Analysis**: View and analyze gem dependencies with version information
107
+ - **Status Monitoring**: Health indicators and status tracking for all gems
108
+ - **Download Metrics**: Popularity statistics and download information
109
+ - **License Tracking**: Security compliance and license information for each gem
110
+
78
111
  ### Code Quality
79
112
  Displays code quality metrics, test coverage, and code health indicators.
80
113
 
@@ -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,179 @@
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
+ // Notification system
166
+ window.SolidstatsDashboard.showNotification = function(message, type) {
167
+ // Simple notification system
168
+ const notification = document.createElement('div');
169
+ notification.className = `notification notification-${type}`;
170
+ notification.textContent = message;
171
+
172
+ // Add to body
173
+ document.body.appendChild(notification);
174
+
175
+ // Remove after 3 seconds
176
+ setTimeout(() => {
177
+ notification.remove();
178
+ }, 3000);
179
+ };
@@ -10,6 +10,11 @@
10
10
  * files in this directory. Styles in this file should be added after the last require_* statement.
11
11
  * It is generally better to create a new file per style scope.
12
12
  *
13
- *= require_tree .
13
+ *= require_tree ./components
14
14
  *= require_self
15
15
  */
16
+
17
+ /* Base Solidstats styles */
18
+ .solidstats-dashboard {
19
+ font-family: system-ui, -apple-system, sans-serif;
20
+ }
@@ -1,7 +1,7 @@
1
1
  module Solidstats
2
2
  class DashboardController < ApplicationController
3
- layout 'solidstats/dashboard'
4
-
3
+ layout "solidstats/dashboard"
4
+
5
5
  TODO_CACHE_FILE = Rails.root.join("tmp", "solidstats_todos.json")
6
6
  AUDIT_CACHE_HOURS = 12 # Configure how many hours before refreshing
7
7
 
@@ -10,7 +10,7 @@ module Solidstats
10
10
  # Load dashboard cards from JSON file
11
11
  @dashboard_cards = load_dashboard_cards
12
12
  @quick_actions = quick_actions_data
13
- render 'dashboard'
13
+ render "dashboard"
14
14
  end
15
15
 
16
16
  def refresh
@@ -21,8 +21,11 @@ module Solidstats
21
21
  Solidstats::StylePatrolService.refresh_cache
22
22
  Solidstats::CoverageCompassService.refresh_cache
23
23
  Solidstats::LoadLensService.scan_and_cache
24
-
25
- redirect_to solidstats_dashboard_path, notice: 'Dashboard data refreshed successfully!'
24
+
25
+ respond_to do |format|
26
+ format.html { redirect_to solidstats_dashboard_path, notice: "Dashboard data refreshed successfully!" }
27
+ format.json { render json: { status: "success", message: "Dashboard data refreshed successfully!" } }
28
+ end
26
29
  end
27
30
 
28
31
  private
@@ -30,39 +33,39 @@ module Solidstats
30
33
  def quick_actions_data
31
34
  [
32
35
  {
33
- icon: 'refresh-cw',
34
- label: 'Refresh Data',
35
- color: 'blue',
36
- action: 'refresh_dashboard'
36
+ icon: "refresh-cw",
37
+ label: "Refresh Data",
38
+ color: "blue",
39
+ action: "refresh_path"
37
40
  },
38
41
  {
39
- icon: 'settings',
40
- label: 'Configure',
41
- color: 'purple',
42
- action: 'open_settings'
42
+ icon: "settings",
43
+ label: "Configure",
44
+ color: "purple",
45
+ action: "#"
43
46
  },
44
47
  {
45
- icon: 'download',
46
- label: 'Export',
47
- color: 'green',
48
- action: 'export_data'
48
+ icon: "download",
49
+ label: "Export",
50
+ color: "green",
51
+ action: "#"
49
52
  },
50
53
  {
51
- icon: 'bell',
52
- label: 'Alerts',
53
- color: 'orange',
54
- action: 'view_alerts'
54
+ icon: "bell",
55
+ label: "Alerts",
56
+ color: "orange",
57
+ action: "#"
55
58
  }
56
59
  ]
57
60
  end
58
61
 
59
62
  def load_dashboard_cards
60
63
  json_file_path = Rails.root.join("solidstats", "summary.json")
61
-
64
+
62
65
  begin
63
66
  # Read and parse the JSON file
64
67
  json_data = JSON.parse(File.read(json_file_path))
65
-
68
+
66
69
  # Transform the JSON data into the format expected by the view
67
70
  json_data.map do |name, data|
68
71
  {
@@ -77,23 +80,13 @@ module Solidstats
77
80
  end
78
81
  rescue Errno::ENOENT
79
82
  Rails.logger.warn("Summary JSON file not found, generating initial data...")
80
- generate_initial_data
81
- retry
83
+ # Fallback to empty array if JSON is invalid
84
+ []
82
85
  rescue JSON::ParserError => e
83
86
  Rails.logger.error("Error parsing summary JSON: #{e.message}")
84
87
  # Fallback to empty array if JSON is invalid
85
88
  []
86
89
  end
87
90
  end
88
-
89
- def generate_initial_data
90
- # Force a scan to create initial data if missing
91
- Solidstats::LogSizeMonitorService.scan_and_cache
92
- Solidstats::BundlerAuditService.scan_and_cache
93
- Solidstats::MyTodoService.collect_todos
94
- Solidstats::StylePatrolService.refresh_cache
95
- Solidstats::CoverageCompassService.refresh_cache
96
- Solidstats::DevLogParserService.scan_and_cache
97
- end
98
91
  end
99
92
  end
@@ -0,0 +1,12 @@
1
+ module Solidstats
2
+ class GemMetadataController < ApplicationController
3
+ def refresh
4
+ @gems = Solidstats::GemMetadata::FetcherService.call(nil, true)
5
+
6
+ respond_to do |format|
7
+ format.html { redirect_to "/solidstats#gem-metadata", notice: "Gem metadata refreshed successfully." }
8
+ format.json { render json: { gems: @gems } }
9
+ end
10
+ end
11
+ end
12
+ end