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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +68 -0
- data/README.md +33 -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 +8 -5
- 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 +10 -10
- data/app/views/layouts/solidstats/application.html.erb +2 -1
- data/app/views/solidstats/dashboard/index.html.erb +67 -1337
- data/app/views/solidstats/gem_metadata/_panel.html.erb +419 -0
- data/config/routes.rb +5 -2
- 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 +97 -2
@@ -0,0 +1,419 @@
|
|
1
|
+
<%= stylesheet_link_tag 'solidstats/components/gem_metadata' %>
|
2
|
+
<%= javascript_include_tag 'solidstats/gem_metadata' %>
|
3
|
+
|
4
|
+
<%# Enhanced Gem Metadata Index with Modern UI/UX %>
|
5
|
+
<div class="gem-metadata-index">
|
6
|
+
<!-- Page Header -->
|
7
|
+
<div class="page-header">
|
8
|
+
<div class="header-content">
|
9
|
+
<div class="header-title-section">
|
10
|
+
<h1 class="page-title">
|
11
|
+
<span class="page-icon">💎</span>
|
12
|
+
Gem Dependencies
|
13
|
+
</h1>
|
14
|
+
<div class="page-subtitle">
|
15
|
+
Complete overview of your project's gem dependencies
|
16
|
+
</div>
|
17
|
+
</div>
|
18
|
+
<div class="header-actions">
|
19
|
+
<%= button_to refresh_gem_metadata_path,
|
20
|
+
method: :post,
|
21
|
+
class: "action-btn refresh-btn",
|
22
|
+
title: "Refresh gem metadata",
|
23
|
+
form: { style: "display: inline-block;" },
|
24
|
+
data: { confirm: 'This will fetch the latest gem information. Continue?' } do %>
|
25
|
+
<i class="fas fa-sync-alt"></i>
|
26
|
+
<span class="btn-text">Refresh All</span>
|
27
|
+
<% end %>
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
|
32
|
+
<!-- Stats Overview -->
|
33
|
+
<div class="stats-overview">
|
34
|
+
<% total_gems = @gems.size %>
|
35
|
+
<% outdated_gems = @gems.count { |gem| gem.current_version && gem.version && gem.version != gem.current_version } %>
|
36
|
+
<% up_to_date_gems = @gems.count { |gem| gem.current_version && gem.version && gem.version == gem.current_version } %>
|
37
|
+
<% unavailable_gems = @gems.count { |gem| gem.info == '(API unavailable)' } %>
|
38
|
+
|
39
|
+
<div class="stat-card total">
|
40
|
+
<div class="stat-icon">📦</div>
|
41
|
+
<div class="stat-content">
|
42
|
+
<div class="stat-number"><%= total_gems %></div>
|
43
|
+
<div class="stat-label">Total Gems</div>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
|
47
|
+
<div class="stat-card outdated">
|
48
|
+
<div class="stat-icon">⚠️</div>
|
49
|
+
<div class="stat-content">
|
50
|
+
<div class="stat-number"><%= outdated_gems %></div>
|
51
|
+
<div class="stat-label">Need Updates</div>
|
52
|
+
</div>
|
53
|
+
</div>
|
54
|
+
|
55
|
+
<div class="stat-card up-to-date">
|
56
|
+
<div class="stat-icon">✅</div>
|
57
|
+
<div class="stat-content">
|
58
|
+
<div class="stat-number"><%= up_to_date_gems %></div>
|
59
|
+
<div class="stat-label">Up to Date</div>
|
60
|
+
</div>
|
61
|
+
</div>
|
62
|
+
|
63
|
+
<div class="stat-card unavailable">
|
64
|
+
<div class="stat-icon">❓</div>
|
65
|
+
<div class="stat-content">
|
66
|
+
<div class="stat-number"><%= unavailable_gems %></div>
|
67
|
+
<div class="stat-label">Unavailable</div>
|
68
|
+
</div>
|
69
|
+
</div>
|
70
|
+
</div>
|
71
|
+
|
72
|
+
<!-- Search and Filters -->
|
73
|
+
<div class="search-filters">
|
74
|
+
<div class="search-section">
|
75
|
+
<div class="search-input-wrapper">
|
76
|
+
<i class="fas fa-search search-icon"></i>
|
77
|
+
<input type="search"
|
78
|
+
class="search-input"
|
79
|
+
id="gem-search"
|
80
|
+
placeholder="Search gems by name..."
|
81
|
+
aria-label="Search gems">
|
82
|
+
<button class="clear-search" id="clear-search" style="display: none;">
|
83
|
+
<i class="fas fa-times"></i>
|
84
|
+
</button>
|
85
|
+
</div>
|
86
|
+
</div>
|
87
|
+
|
88
|
+
<div class="filters-section">
|
89
|
+
<div class="filter-group">
|
90
|
+
<label class="filter-label">Status:</label>
|
91
|
+
<select class="filter-select" id="status-filter">
|
92
|
+
<option value="">All</option>
|
93
|
+
<option value="outdated">Need Updates</option>
|
94
|
+
<option value="up-to-date">Up to Date</option>
|
95
|
+
<option value="unavailable">Unavailable</option>
|
96
|
+
</select>
|
97
|
+
</div>
|
98
|
+
|
99
|
+
<div class="filter-group">
|
100
|
+
<label class="filter-label">Sort by:</label>
|
101
|
+
<select class="filter-select" id="sort-filter">
|
102
|
+
<option value="name-asc">Name (A-Z)</option>
|
103
|
+
<option value="name-desc">Name (Z-A)</option>
|
104
|
+
<option value="status-desc">Status (Updates First)</option>
|
105
|
+
<option value="release-desc">Release Date (Newest)</option>
|
106
|
+
<option value="release-asc">Release Date (Oldest)</option>
|
107
|
+
</select>
|
108
|
+
</div>
|
109
|
+
|
110
|
+
<div class="filter-group view-toggle-group">
|
111
|
+
<label class="filter-label">View:</label>
|
112
|
+
<div class="view-toggle">
|
113
|
+
<button class="view-toggle-btn active" id="grid-view-btn" title="Grid View" aria-label="Switch to grid view">
|
114
|
+
<i class="fas fa-th"></i>
|
115
|
+
</button>
|
116
|
+
<button class="view-toggle-btn" id="table-view-btn" title="Table View" aria-label="Switch to table view">
|
117
|
+
<i class="fas fa-table"></i>
|
118
|
+
</button>
|
119
|
+
</div>
|
120
|
+
</div>
|
121
|
+
|
122
|
+
<div class="filter-group export-group" id="export-group" style="display: none;">
|
123
|
+
<button class="export-btn" id="export-table-btn" title="Export table data to CSV">
|
124
|
+
<i class="fas fa-download"></i>
|
125
|
+
Export CSV
|
126
|
+
</button>
|
127
|
+
</div>
|
128
|
+
|
129
|
+
<button class="reset-filters" id="reset-filters">
|
130
|
+
<i class="fas fa-undo"></i>
|
131
|
+
Reset
|
132
|
+
</button>
|
133
|
+
</div>
|
134
|
+
</div>
|
135
|
+
|
136
|
+
<!-- Loading State -->
|
137
|
+
<div class="loading-overlay" id="loading-overlay" style="display: none;">
|
138
|
+
<div class="loading-content">
|
139
|
+
<div class="loading-spinner"></div>
|
140
|
+
<div class="loading-text">Loading gem metadata...</div>
|
141
|
+
</div>
|
142
|
+
</div>
|
143
|
+
|
144
|
+
<!-- Gems Grid -->
|
145
|
+
<div class="gems-container">
|
146
|
+
<% if @gems.any? %>
|
147
|
+
<div class="gems-grid" id="gems-grid">
|
148
|
+
<% @gems.each_with_index do |gem, index| %>
|
149
|
+
<div class="gem-card-full <%= 'unavailable' if gem.info == '(API unavailable)' %>"
|
150
|
+
data-gem-name="<%= gem.name.downcase %>"
|
151
|
+
data-status="<%= gem.current_version && gem.version && gem.version != gem.current_version ? 'outdated' : (gem.current_version && gem.version && gem.version == gem.current_version ? 'up-to-date' : 'unavailable') %>"
|
152
|
+
data-release-date="<%= gem.released&.to_i || 0 %>"
|
153
|
+
style="animation-delay: <%= (index * 0.05).round(2) %>s">
|
154
|
+
|
155
|
+
<div class="gem-card-header">
|
156
|
+
<div class="gem-title-section">
|
157
|
+
<h3 class="gem-name"><%= gem.name %></h3>
|
158
|
+
<div class="gem-status-badges">
|
159
|
+
<% if gem.current_version && gem.version && gem.version != gem.current_version %>
|
160
|
+
<span class="status-badge upgrade-available" title="Upgrade available">
|
161
|
+
<i class="fas fa-arrow-up"></i>
|
162
|
+
Update Available
|
163
|
+
</span>
|
164
|
+
<% elsif gem.current_version && gem.version && gem.version == gem.current_version %>
|
165
|
+
<span class="status-badge up-to-date" title="Up to date">
|
166
|
+
<i class="fas fa-check"></i>
|
167
|
+
Latest
|
168
|
+
</span>
|
169
|
+
<% elsif gem.info == '(API unavailable)' %>
|
170
|
+
<span class="status-badge unavailable" title="API unavailable">
|
171
|
+
<i class="fas fa-exclamation-triangle"></i>
|
172
|
+
Unavailable
|
173
|
+
</span>
|
174
|
+
<% end %>
|
175
|
+
</div>
|
176
|
+
</div>
|
177
|
+
</div>
|
178
|
+
|
179
|
+
<div class="gem-card-body">
|
180
|
+
<div class="versions-section">
|
181
|
+
<div class="version-group">
|
182
|
+
<div class="version-item">
|
183
|
+
<span class="version-label">Current</span>
|
184
|
+
<% if gem.current_version %>
|
185
|
+
<span class="version-badge current" title="Current version">
|
186
|
+
<%= gem.current_version %>
|
187
|
+
</span>
|
188
|
+
<% else %>
|
189
|
+
<span class="version-badge unknown">N/A</span>
|
190
|
+
<% end %>
|
191
|
+
</div>
|
192
|
+
|
193
|
+
<div class="version-item">
|
194
|
+
<span class="version-label">Latest</span>
|
195
|
+
<% if gem.version %>
|
196
|
+
<span class="version-badge latest" title="Latest version">
|
197
|
+
<%= gem.version %>
|
198
|
+
</span>
|
199
|
+
<% else %>
|
200
|
+
<span class="version-badge unknown">N/A</span>
|
201
|
+
<% end %>
|
202
|
+
</div>
|
203
|
+
</div>
|
204
|
+
</div>
|
205
|
+
|
206
|
+
<div class="gem-info-section">
|
207
|
+
<div class="release-info">
|
208
|
+
<i class="fas fa-calendar-alt"></i>
|
209
|
+
<% if gem.released %>
|
210
|
+
<span title="<%= gem.released.strftime('%Y-%m-%d %H:%M') %>">
|
211
|
+
<%= time_ago_in_words(gem.released) %> ago
|
212
|
+
</span>
|
213
|
+
<% else %>
|
214
|
+
<span>Unknown release date</span>
|
215
|
+
<% end %>
|
216
|
+
</div>
|
217
|
+
|
218
|
+
<% if gem.info && gem.info != '(API unavailable)' %>
|
219
|
+
<div class="gem-description">
|
220
|
+
<i class="fas fa-info-circle"></i>
|
221
|
+
<span title="<%= gem.info %>"><%= truncate(gem.info, length: 120) %></span>
|
222
|
+
</div>
|
223
|
+
<% end %>
|
224
|
+
</div>
|
225
|
+
|
226
|
+
<% if gem.runtime&.any? %>
|
227
|
+
<div class="dependencies-section">
|
228
|
+
<div class="deps-header">
|
229
|
+
<i class="fas fa-cubes"></i>
|
230
|
+
<span>Dependencies (<%= gem.runtime.size %>)</span>
|
231
|
+
</div>
|
232
|
+
<div class="deps-grid">
|
233
|
+
<% gem.runtime.first(6).each do |dep| %>
|
234
|
+
<span class="dep-badge <%= 'rails-dep' if dep['name'] == 'rails' %>"
|
235
|
+
title="<%= dep['name'] %> <%= dep['requirements'] %>">
|
236
|
+
<%= dep['name'] %>
|
237
|
+
<span class="dep-version"><%= dep['requirements'] %></span>
|
238
|
+
</span>
|
239
|
+
<% end %>
|
240
|
+
<% if gem.runtime.size > 6 %>
|
241
|
+
<span class="dep-more"
|
242
|
+
title="<%= gem.runtime.drop(6).map { |d| "#{d['name']} #{d['requirements']}" }.join(', ') %>">
|
243
|
+
+<%= gem.runtime.size - 6 %> more
|
244
|
+
</span>
|
245
|
+
<% end %>
|
246
|
+
</div>
|
247
|
+
</div>
|
248
|
+
<% end %>
|
249
|
+
</div>
|
250
|
+
</div>
|
251
|
+
<% end %>
|
252
|
+
</div>
|
253
|
+
|
254
|
+
<!-- Table View (Hidden by default) -->
|
255
|
+
<div class="gems-table-container" id="gems-table-container" style="display: none;">
|
256
|
+
<div class="table-wrapper">
|
257
|
+
<table class="gems-table" id="gems-table">
|
258
|
+
<thead>
|
259
|
+
<tr>
|
260
|
+
<th class="sortable" data-sort="name">
|
261
|
+
<span>Gem Name</span>
|
262
|
+
<i class="fas fa-sort sort-icon"></i>
|
263
|
+
</th>
|
264
|
+
<th class="sortable" data-sort="status">
|
265
|
+
<span>Status</span>
|
266
|
+
<i class="fas fa-sort sort-icon"></i>
|
267
|
+
</th>
|
268
|
+
<th class="sortable" data-sort="current">
|
269
|
+
<span>Current</span>
|
270
|
+
<i class="fas fa-sort sort-icon"></i>
|
271
|
+
</th>
|
272
|
+
<th class="sortable" data-sort="latest">
|
273
|
+
<span>Latest</span>
|
274
|
+
<i class="fas fa-sort sort-icon"></i>
|
275
|
+
</th>
|
276
|
+
<th class="sortable" data-sort="released">
|
277
|
+
<span>Released</span>
|
278
|
+
<i class="fas fa-sort sort-icon"></i>
|
279
|
+
</th>
|
280
|
+
<th>Description</th>
|
281
|
+
<th>Dependencies</th>
|
282
|
+
</tr>
|
283
|
+
</thead>
|
284
|
+
<tbody id="gems-table-body">
|
285
|
+
<% @gems.each do |gem| %>
|
286
|
+
<tr class="gem-table-row <%= 'unavailable' if gem.info == '(API unavailable)' %>"
|
287
|
+
data-gem-name="<%= gem.name.downcase %>"
|
288
|
+
data-status="<%= gem.current_version && gem.version && gem.version != gem.current_version ? 'outdated' : (gem.current_version && gem.version && gem.version == gem.current_version ? 'up-to-date' : 'unavailable') %>"
|
289
|
+
data-release-date="<%= gem.released&.to_i || 0 %>">
|
290
|
+
|
291
|
+
<td class="gem-name-cell">
|
292
|
+
<strong class="gem-name-table"><%= gem.name %></strong>
|
293
|
+
</td>
|
294
|
+
|
295
|
+
<td class="status-cell">
|
296
|
+
<% if gem.current_version && gem.version && gem.version != gem.current_version %>
|
297
|
+
<span class="status-badge-table upgrade-available" title="Upgrade available">
|
298
|
+
<i class="fas fa-arrow-up"></i>
|
299
|
+
Update Available
|
300
|
+
</span>
|
301
|
+
<% elsif gem.current_version && gem.version && gem.version == gem.current_version %>
|
302
|
+
<span class="status-badge-table up-to-date" title="Up to date">
|
303
|
+
<i class="fas fa-check"></i>
|
304
|
+
Latest
|
305
|
+
</span>
|
306
|
+
<% elsif gem.info == '(API unavailable)' %>
|
307
|
+
<span class="status-badge-table unavailable" title="API unavailable">
|
308
|
+
<i class="fas fa-exclamation-triangle"></i>
|
309
|
+
Unavailable
|
310
|
+
</span>
|
311
|
+
<% else %>
|
312
|
+
<span class="status-badge-table unknown">
|
313
|
+
Unknown
|
314
|
+
</span>
|
315
|
+
<% end %>
|
316
|
+
</td>
|
317
|
+
|
318
|
+
<td class="version-cell">
|
319
|
+
<% if gem.current_version %>
|
320
|
+
<span class="version-badge-table current" title="Current version">
|
321
|
+
<%= gem.current_version %>
|
322
|
+
</span>
|
323
|
+
<% else %>
|
324
|
+
<span class="version-badge-table unknown">N/A</span>
|
325
|
+
<% end %>
|
326
|
+
</td>
|
327
|
+
|
328
|
+
<td class="version-cell">
|
329
|
+
<% if gem.version %>
|
330
|
+
<span class="version-badge-table latest" title="Latest version">
|
331
|
+
<%= gem.version %>
|
332
|
+
</span>
|
333
|
+
<% else %>
|
334
|
+
<span class="version-badge-table unknown">N/A</span>
|
335
|
+
<% end %>
|
336
|
+
</td>
|
337
|
+
|
338
|
+
<td class="release-cell">
|
339
|
+
<% if gem.released %>
|
340
|
+
<span title="<%= gem.released.strftime('%Y-%m-%d %H:%M') %>">
|
341
|
+
<%= time_ago_in_words(gem.released) %> ago
|
342
|
+
</span>
|
343
|
+
<% else %>
|
344
|
+
<span class="text-muted">Unknown</span>
|
345
|
+
<% end %>
|
346
|
+
</td>
|
347
|
+
|
348
|
+
<td class="description-cell">
|
349
|
+
<% if gem.info && gem.info != '(API unavailable)' %>
|
350
|
+
<span title="<%= gem.info %>"><%= truncate(gem.info, length: 80) %></span>
|
351
|
+
<% else %>
|
352
|
+
<span class="text-muted">No description available</span>
|
353
|
+
<% end %>
|
354
|
+
</td>
|
355
|
+
|
356
|
+
<td class="dependencies-cell">
|
357
|
+
<% if gem.runtime&.any? %>
|
358
|
+
<div class="table-deps">
|
359
|
+
<% gem.runtime.first(3).each do |dep| %>
|
360
|
+
<span class="dep-badge-table <%= 'rails-dep' if dep['name'] == 'rails' %>"
|
361
|
+
title="<%= dep['name'] %> <%= dep['requirements'] %>">
|
362
|
+
<%= dep['name'] %>
|
363
|
+
</span>
|
364
|
+
<% end %>
|
365
|
+
<% if gem.runtime.size > 3 %>
|
366
|
+
<span class="dep-more-table"
|
367
|
+
title="<%= gem.runtime.drop(3).map { |d| "#{d['name']} #{d['requirements']}" }.join(', ') %>">
|
368
|
+
+<%= gem.runtime.size - 3 %>
|
369
|
+
</span>
|
370
|
+
<% end %>
|
371
|
+
</div>
|
372
|
+
<% else %>
|
373
|
+
<span class="text-muted">None</span>
|
374
|
+
<% end %>
|
375
|
+
</td>
|
376
|
+
</tr>
|
377
|
+
<% end %>
|
378
|
+
</tbody>
|
379
|
+
</table>
|
380
|
+
</div>
|
381
|
+
</div>
|
382
|
+
<% else %>
|
383
|
+
<div class="empty-state">
|
384
|
+
<div class="empty-icon">🔍</div>
|
385
|
+
<div class="empty-title">No Gem Data Found</div>
|
386
|
+
<div class="empty-description">
|
387
|
+
It looks like we couldn't find any gem metadata. This could happen if your Gemfile.lock is missing or the API is unavailable.
|
388
|
+
</div>
|
389
|
+
<div class="empty-actions">
|
390
|
+
<%= button_to refresh_gem_metadata_path,
|
391
|
+
method: :post,
|
392
|
+
class: "action-btn refresh-btn primary",
|
393
|
+
form: { style: "display: inline-block;" } do %>
|
394
|
+
<i class="fas fa-sync-alt"></i>
|
395
|
+
Try Refreshing
|
396
|
+
<% end %>
|
397
|
+
</div>
|
398
|
+
</div>
|
399
|
+
<% end %>
|
400
|
+
</div>
|
401
|
+
|
402
|
+
<!-- Footer -->
|
403
|
+
<div class="page-footer">
|
404
|
+
<div class="footer-content">
|
405
|
+
<div class="footer-links">
|
406
|
+
<%= link_to "https://railsbump.org",
|
407
|
+
class: "footer-link external",
|
408
|
+
target: "_blank",
|
409
|
+
rel: "noopener" do %>
|
410
|
+
<i class="fas fa-external-link-alt"></i>
|
411
|
+
Run full Rails compatibility scan on RailsBump
|
412
|
+
<% end %>
|
413
|
+
</div>
|
414
|
+
<div class="footer-stats">
|
415
|
+
Last updated: <span id="last-updated">just now</span>
|
416
|
+
</div>
|
417
|
+
</div>
|
418
|
+
</div>
|
419
|
+
</div>
|
data/config/routes.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
Solidstats::Engine.routes.draw do
|
2
2
|
root to: "dashboard#index"
|
3
3
|
get "refresh", to: "dashboard#refresh", as: :refresh
|
4
|
-
|
4
|
+
|
5
5
|
# Route for truncating logs - accepts filename without extension
|
6
|
-
post "truncate-log(/:filename)", to: "dashboard#truncate_log", as: :truncate_log,
|
6
|
+
post "truncate-log(/:filename)", to: "dashboard#truncate_log", as: :truncate_log,
|
7
7
|
constraints: { filename: /[^\.]+/ }
|
8
|
+
|
9
|
+
post "gem_metadata/refresh", to: "gem_metadata#refresh", as: :refresh_gem_metadata
|
10
|
+
get "gem_metadata/refresh", to: "gem_metadata#refresh" # Fallback for direct URL access
|
8
11
|
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
require "rails/generators/named_base"
|
2
|
+
|
3
|
+
module Solidstats
|
4
|
+
module Generators
|
5
|
+
class FeatureGenerator < Rails::Generators::NamedBase
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
7
|
+
|
8
|
+
desc "Generate a new Solidstats feature with service, component, and views"
|
9
|
+
|
10
|
+
class_option :icon, type: :string, default: "📊", desc: "Icon for the feature"
|
11
|
+
class_option :section, type: :string, default: "metrics", desc: "Dashboard section (overview, security, code-quality, metrics)"
|
12
|
+
class_option :status_colors, type: :boolean, default: true, desc: "Include status-based coloring"
|
13
|
+
class_option :cache_hours, type: :numeric, default: 1, desc: "Cache duration in hours"
|
14
|
+
|
15
|
+
def create_service
|
16
|
+
template "service.rb.erb", "app/services/solidstats/#{file_name}_service.rb"
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_component_helper
|
20
|
+
template "component.rb.erb", "app/helpers/solidstats/#{file_name}_helper.rb"
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_component_partial
|
24
|
+
template "component.html.erb", "app/views/solidstats/dashboard/_#{file_name}_summary.html.erb"
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_component_styles
|
28
|
+
template "component.scss", "app/assets/stylesheets/solidstats/#{file_name}_component.scss"
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_preview
|
32
|
+
template "preview.rb.erb", "app/views/solidstats/#{file_name}/preview.html.erb"
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_detail_view
|
36
|
+
template "detail_view.html.erb", "app/views/solidstats/#{file_name}/index.html.erb"
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_controller
|
40
|
+
template "controller.rb.erb", "app/controllers/solidstats/#{file_name}_controller.rb"
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_service_test
|
44
|
+
template "service_test.rb.erb", "test/services/solidstats/#{file_name}_service_test.rb"
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_component_test
|
48
|
+
template "component_test.rb.erb", "test/helpers/solidstats/#{file_name}_helper_test.rb"
|
49
|
+
end
|
50
|
+
|
51
|
+
def create_controller_test
|
52
|
+
template "controller_test.rb.erb", "test/controllers/solidstats/#{file_name}_controller_test.rb"
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_route
|
56
|
+
route_content = " get \"#{file_name}\", to: \"#{file_name}#index\", as: :#{file_name}"
|
57
|
+
inject_into_file "config/routes.rb", after: "Solidstats::Engine.routes.draw do\n" do
|
58
|
+
"#{route_content}\n"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def integrate_with_dashboard
|
63
|
+
# Add instance variable to dashboard controller
|
64
|
+
inject_into_file "app/controllers/solidstats/dashboard_controller.rb",
|
65
|
+
after: "def index\n" do
|
66
|
+
" @#{file_name}_data = #{class_name}Service.new.summary\n"
|
67
|
+
end
|
68
|
+
|
69
|
+
# Add to refresh method
|
70
|
+
inject_into_file "app/controllers/solidstats/dashboard_controller.rb",
|
71
|
+
after: "def refresh\n" do
|
72
|
+
" #{file_name}_data = #{class_name}Service.new.fetch(true)\n"
|
73
|
+
end
|
74
|
+
|
75
|
+
# Add to refresh JSON response
|
76
|
+
inject_into_file "app/controllers/solidstats/dashboard_controller.rb",
|
77
|
+
after: "render json: {\n" do
|
78
|
+
" #{file_name}_data: #{file_name}_data,\n"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def update_dashboard_view
|
83
|
+
# Add summary card to overview section
|
84
|
+
card_partial = <<~ERB
|
85
|
+
|
86
|
+
<div class="summary-card <%= @#{file_name}_data&.dig(:status) || 'status-ok' %>" data-section="#{options[:section]}" data-tab="#{file_name.dasherize}">
|
87
|
+
<div class="summary-icon">#{options[:icon]}</div>
|
88
|
+
<div class="summary-data">
|
89
|
+
<div class="summary-value"><%= @#{file_name}_data&.dig(:value) || 0 %></div>
|
90
|
+
<div class="summary-label">#{human_name}</div>
|
91
|
+
</div>
|
92
|
+
</div>
|
93
|
+
ERB
|
94
|
+
|
95
|
+
inject_into_file "app/views/solidstats/dashboard/index.html.erb",
|
96
|
+
after: '<div class="stats-summary">' do
|
97
|
+
card_partial
|
98
|
+
end
|
99
|
+
|
100
|
+
# Add section if it doesn't exist for metrics
|
101
|
+
if options[:section] == "metrics"
|
102
|
+
section_content = <<~ERB
|
103
|
+
|
104
|
+
<!-- #{human_name} Section -->
|
105
|
+
<section id="#{file_name.dasherize}" class="dashboard-section">
|
106
|
+
<h2 class="section-title">#{human_name}</h2>
|
107
|
+
<%= render Solidstats::#{class_name}Component.new(data: @#{file_name}_data) %>
|
108
|
+
</section>
|
109
|
+
ERB
|
110
|
+
|
111
|
+
inject_into_file "app/views/solidstats/dashboard/index.html.erb",
|
112
|
+
before: " <!-- Floating quick navigation -->" do
|
113
|
+
section_content
|
114
|
+
end
|
115
|
+
|
116
|
+
# Add to navigation
|
117
|
+
nav_item = <<~ERB
|
118
|
+
|
119
|
+
<li><a href="##{file_name.dasherize}" class="nav-item" data-section="#{file_name.dasherize}">#{human_name}</a></li>
|
120
|
+
ERB
|
121
|
+
|
122
|
+
inject_into_file "app/views/solidstats/dashboard/index.html.erb",
|
123
|
+
after: '<nav class="dashboard-nav">' do
|
124
|
+
" <ul>#{nav_item}"
|
125
|
+
end
|
126
|
+
|
127
|
+
# Add to quick nav
|
128
|
+
quick_nav_item = <<~ERB
|
129
|
+
|
130
|
+
<a href="##{file_name.dasherize}" class="quick-nav-item">#{human_name}</a>
|
131
|
+
ERB
|
132
|
+
|
133
|
+
inject_into_file "app/views/solidstats/dashboard/index.html.erb",
|
134
|
+
after: '<div class="quick-nav-menu">' do
|
135
|
+
quick_nav_item
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def create_directory_structure
|
141
|
+
empty_directory "app/views/solidstats/#{file_name}"
|
142
|
+
end
|
143
|
+
|
144
|
+
def show_next_steps
|
145
|
+
say "\n" + set_color("#{human_name} feature generated successfully!", :green, :bold)
|
146
|
+
say "\nNext steps:"
|
147
|
+
say " 1. Implement data collection logic in #{set_color("app/services/solidstats/#{file_name}_service.rb", :yellow)}"
|
148
|
+
say " 2. Customize the component template in #{set_color("app/components/solidstats/#{file_name}_component.html.erb", :yellow)}"
|
149
|
+
say " 3. Add any required styling in #{set_color("app/components/solidstats/#{file_name}_component.scss", :yellow)}"
|
150
|
+
say " 4. Run tests: #{set_color("rails test test/services/solidstats/#{file_name}_service_test.rb", :cyan)}"
|
151
|
+
say " 5. Visit #{set_color("http://localhost:3000/rails/view_components", :cyan)} to preview the component"
|
152
|
+
say "\nThe feature will appear in the #{set_color(options[:section], :blue)} section of the dashboard."
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
def human_name
|
158
|
+
file_name.humanize
|
159
|
+
end
|
160
|
+
|
161
|
+
def class_name
|
162
|
+
file_name.camelize
|
163
|
+
end
|
164
|
+
|
165
|
+
def section_human_name
|
166
|
+
options[:section].humanize
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|