solidstats 1.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +85 -0
- data/README.md +35 -0
- data/app/assets/javascripts/solidstats/application.js +257 -0
- data/app/assets/javascripts/solidstats/dashboard.js +225 -0
- data/app/assets/javascripts/solidstats/gem_metadata.js +554 -0
- data/app/assets/stylesheets/solidstats/application.css +6 -1
- data/app/assets/stylesheets/solidstats/components/action_button.css +99 -0
- data/app/assets/stylesheets/solidstats/components/dashboard.css +151 -0
- data/app/assets/stylesheets/solidstats/components/dashboard_header.css +93 -0
- data/app/assets/stylesheets/solidstats/components/dashboard_layout.css +97 -0
- data/app/assets/stylesheets/solidstats/components/gem_metadata.css +1403 -0
- data/app/assets/stylesheets/solidstats/components/navigation.css +80 -0
- data/app/assets/stylesheets/solidstats/components/quick_navigation.css +54 -0
- data/app/assets/stylesheets/solidstats/components/security.css +332 -0
- data/app/assets/stylesheets/solidstats/components/status_badge.css +58 -0
- data/app/assets/stylesheets/solidstats/components/summary_card.css +66 -0
- data/app/assets/stylesheets/solidstats/components/tab_navigation.css +95 -0
- data/app/components/solidstats/base_component.rb +88 -0
- data/app/components/solidstats/code_quality/code_quality_section_component.html.erb +0 -0
- data/app/components/solidstats/code_quality/code_quality_section_component.rb +0 -0
- data/app/components/solidstats/code_quality/section_component.html.erb +45 -0
- data/app/components/solidstats/code_quality/section_component.rb +34 -0
- data/app/components/solidstats/dashboard_header_component.html.erb +39 -0
- data/app/components/solidstats/dashboard_header_component.rb +33 -0
- data/app/components/solidstats/previews/action_button_component_preview/button_vs_link.html.erb +6 -0
- data/app/components/solidstats/previews/action_button_component_preview/sizes.html.erb +6 -0
- data/app/components/solidstats/previews/action_button_component_preview/variants.html.erb +6 -0
- data/app/components/solidstats/previews/action_button_component_preview/with_icons.html.erb +6 -0
- data/app/components/solidstats/previews/action_button_component_preview.rb +64 -0
- data/app/components/solidstats/previews/navigation_component_preview.rb +74 -0
- data/app/components/solidstats/previews/stats_overview_component_preview.rb +100 -0
- data/app/components/solidstats/previews/status_badge_component_preview/sizes.html.erb +6 -0
- data/app/components/solidstats/previews/status_badge_component_preview/statuses.html.erb +6 -0
- data/app/components/solidstats/previews/status_badge_component_preview/with_icons.html.erb +6 -0
- data/app/components/solidstats/previews/status_badge_component_preview.rb +49 -0
- data/app/components/solidstats/previews/summary_card_component_preview/clickable.html.erb +9 -0
- data/app/components/solidstats/previews/summary_card_component_preview/dashboard_layout.html.erb +9 -0
- data/app/components/solidstats/previews/summary_card_component_preview/statuses.html.erb +6 -0
- data/app/components/solidstats/previews/summary_card_component_preview/value_formats.html.erb +6 -0
- data/app/components/solidstats/previews/summary_card_component_preview.rb +67 -0
- data/app/components/solidstats/quick_navigation_component.html.erb +8 -0
- data/app/components/solidstats/quick_navigation_component.rb +21 -0
- data/app/components/solidstats/security/gem_impact_analysis_component.html.erb +44 -0
- data/app/components/solidstats/security/gem_impact_analysis_component.rb +45 -0
- data/app/components/solidstats/security/overview_component.html.erb +21 -0
- data/app/components/solidstats/security/overview_component.rb +104 -0
- data/app/components/solidstats/security/section_component.html.erb +26 -0
- data/app/components/solidstats/security/section_component.rb +52 -0
- data/app/components/solidstats/security/timeline_component.html.erb +39 -0
- data/app/components/solidstats/security/timeline_component.rb +43 -0
- data/app/components/solidstats/tasks_section_component.html.erb +17 -0
- data/app/components/solidstats/tasks_section_component.rb +22 -0
- data/app/components/solidstats/ui/action_button_component.html.erb +6 -0
- data/app/components/solidstats/ui/action_button_component.rb +71 -0
- data/app/components/solidstats/ui/dashboard_layout_component.html.erb +19 -0
- data/app/components/solidstats/ui/dashboard_layout_component.rb +85 -0
- data/app/components/solidstats/ui/navigation_component.html.erb +34 -0
- data/app/components/solidstats/ui/navigation_component.rb +72 -0
- data/app/components/solidstats/ui/stats_overview_component.html.erb +14 -0
- data/app/components/solidstats/ui/stats_overview_component.rb +78 -0
- data/app/components/solidstats/ui/status_badge_component.html.erb +6 -0
- data/app/components/solidstats/ui/status_badge_component.rb +42 -0
- data/app/components/solidstats/ui/summary_card_component.html.erb +12 -0
- data/app/components/solidstats/ui/summary_card_component.rb +63 -0
- data/app/components/solidstats/ui/tab_navigation_component.html.erb +22 -0
- data/app/components/solidstats/ui/tab_navigation_component.rb +79 -0
- data/app/controllers/solidstats/dashboard_controller.rb +22 -0
- data/app/controllers/solidstats/gem_metadata_controller.rb +12 -0
- data/app/helpers/solidstats/application_helper.rb +42 -0
- data/app/services/solidstats/gem_metadata/fetcher_service.rb +136 -0
- data/app/services/solidstats/log_size_monitor_service.rb +94 -0
- data/app/views/layouts/solidstats/application.html.erb +2 -1
- data/app/views/solidstats/dashboard/_log_monitor.html.erb +759 -0
- data/app/views/solidstats/dashboard/index.html.erb +67 -1323
- data/app/views/solidstats/gem_metadata/_panel.html.erb +419 -0
- data/config/routes.rb +7 -0
- data/lib/generators/solidstats/feature/feature_generator.rb +170 -0
- data/lib/generators/solidstats/feature/templates/component.html.erb +84 -0
- data/lib/generators/solidstats/feature/templates/component.rb.erb +103 -0
- data/lib/generators/solidstats/feature/templates/component.scss +243 -0
- data/lib/generators/solidstats/feature/templates/component_test.rb.erb +183 -0
- data/lib/generators/solidstats/feature/templates/controller.rb.erb +44 -0
- data/lib/generators/solidstats/feature/templates/controller_test.rb.erb +111 -0
- data/lib/generators/solidstats/feature/templates/detail_view.html.erb +755 -0
- data/lib/generators/solidstats/feature/templates/preview.rb.erb +107 -0
- data/lib/generators/solidstats/feature/templates/service.rb.erb +132 -0
- data/lib/generators/solidstats/feature/templates/service_test.rb.erb +109 -0
- data/lib/generators/solidstats/install_generator.rb +109 -0
- data/lib/generators/solidstats/templates/initializer.rb +112 -0
- data/lib/solidstats/asset_compatibility.rb +238 -0
- data/lib/solidstats/asset_manifest.rb +205 -0
- data/lib/solidstats/engine.rb +114 -9
- data/lib/solidstats/version.rb +1 -1
- data/lib/solidstats.rb +299 -2
- data/lib/tasks/solidstats_install.rake +122 -2
- metadata +99 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1087b955c118f56234de6e254a1c4f8fbb25184caccae8e72756d277b77d8a4d
|
4
|
+
data.tar.gz: fd69b3cec25f03c5c2854b76b7ea5fe5154c762a6b22dbe50ef6e423d780aef5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 45d75fb7fbbccb543628e14747177807f8fb7b652be35cb74d05a5fe65b666731c4290413ce7e0da2a5038cb9d68c265916bbe2421949b073424eb5b603018ad
|
7
|
+
data.tar.gz: 8d425d7b2f9abcba38460f5ec97fa0e5021fbc1542e00442c97e7b099707bc4a11d578f53f440fc213885e8172706fa947fd57f025283abfdfe09b93d8a965e6
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,91 @@ 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
|
+
|
76
|
+
## [1.1.0] - 2025-05-23
|
77
|
+
|
78
|
+
### Added
|
79
|
+
- Log Size Monitor feature:
|
80
|
+
- Comprehensive monitoring of all Rails application log files
|
81
|
+
- Total log directory size tracking with status indicators
|
82
|
+
- Individual log file monitoring with size and status details
|
83
|
+
- Visual indicators for log size status (OK, Warning, Danger)
|
84
|
+
- Visual meter showing log size relative to thresholds
|
85
|
+
- One-click log truncation for individual files or all logs
|
86
|
+
- Sortable log file table with size and last modified information
|
87
|
+
- Log management recommendations
|
88
|
+
|
89
|
+
### Fixed
|
90
|
+
- Fixed log file truncation when filename is provided without extension
|
91
|
+
- Added constraints to route handling to improve filename parameter handling
|
92
|
+
|
8
93
|
## [1.0.0] - 2025-05-22
|
9
94
|
|
10
95
|
### Added
|
data/README.md
CHANGED
@@ -1,16 +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
|
25
|
+
- Log Size Monitor for tracking and managing application log files
|
26
|
+
- Log file truncation tool for individual or all log files
|
10
27
|
- Rubocop offense count and quality metrics
|
11
28
|
- TODO/FIXME tracker with file hotspots
|
12
29
|
- Test coverage summary
|
13
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
|
+
|
14
37
|
## Compatibility
|
15
38
|
|
16
39
|
- Ruby 2.7+: Compatible with Rails 6.1 through Rails 7.0
|
@@ -72,6 +95,18 @@ You can refresh the dashboard data at any time by clicking the "Refresh" button
|
|
72
95
|
3. Show real-time feedback during the refresh process
|
73
96
|
4. Update the "Last Updated" timestamp
|
74
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
|
+
|
75
110
|
### Code Quality
|
76
111
|
Displays code quality metrics, test coverage, and code health indicators.
|
77
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
|
+
};
|