solidstats 2.0.0 → 3.0.0.beta.1
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 +12 -63
- data/README.md +27 -33
- data/Rakefile +3 -3
- data/app/assets/stylesheets/solidstats/application.css +1 -6
- data/app/assets/stylesheets/solidstats/dashboard.css +48 -0
- data/app/controllers/solidstats/dashboard_controller.rb +81 -62
- data/app/controllers/solidstats/logs_controller.rb +72 -0
- data/app/controllers/solidstats/performance_controller.rb +25 -0
- data/app/controllers/solidstats/productivity_controller.rb +39 -0
- data/app/controllers/solidstats/quality_controller.rb +152 -0
- data/app/controllers/solidstats/securities_controller.rb +30 -0
- data/app/helpers/solidstats/application_helper.rb +124 -11
- data/app/helpers/solidstats/performance_helper.rb +87 -0
- data/app/helpers/solidstats/productivity_helper.rb +38 -0
- data/app/services/solidstats/bundler_audit_service.rb +206 -0
- data/app/services/solidstats/coverage_compass_service.rb +335 -0
- data/app/services/solidstats/load_lens_service.rb +454 -0
- data/app/services/solidstats/log_size_monitor_service.rb +207 -76
- data/app/services/solidstats/my_todo_service.rb +242 -0
- data/app/services/solidstats/style_patrol_service.rb +319 -0
- data/app/views/layouts/solidstats/application.html.erb +8 -2
- data/app/views/layouts/solidstats/dashboard.html.erb +84 -0
- data/app/views/solidstats/dashboard/dashboard.html.erb +39 -0
- data/app/views/solidstats/logs/logs_size.html.erb +409 -0
- data/app/views/solidstats/performance/load_lens.html.erb +158 -0
- data/app/views/solidstats/productivity/_todo_list.html.erb +49 -0
- data/app/views/solidstats/productivity/my_todos.html.erb +84 -0
- data/app/views/solidstats/quality/coverage_compass.html.erb +420 -0
- data/app/views/solidstats/quality/style_patrol.html.erb +463 -0
- data/app/views/solidstats/securities/bundler_audit.html.erb +345 -0
- data/app/views/solidstats/shared/_dashboard_card.html.erb +160 -0
- data/app/views/solidstats/shared/_quick_actions.html.erb +26 -0
- data/config/routes.rb +32 -7
- data/lib/generators/solidstats/install/install_generator.rb +28 -2
- data/lib/generators/solidstats/install/templates/README +7 -0
- data/lib/solidstats/engine.rb +9 -114
- data/lib/solidstats/version.rb +1 -1
- data/lib/solidstats.rb +2 -299
- data/lib/tasks/solidstats_install.rake +2 -122
- data/lib/tasks/solidstats_performance.rake +84 -0
- metadata +32 -103
- data/app/assets/javascripts/solidstats/application.js +0 -257
- data/app/assets/javascripts/solidstats/dashboard.js +0 -225
- data/app/assets/javascripts/solidstats/gem_metadata.js +0 -554
- data/app/assets/stylesheets/solidstats/components/action_button.css +0 -99
- data/app/assets/stylesheets/solidstats/components/dashboard.css +0 -151
- data/app/assets/stylesheets/solidstats/components/dashboard_header.css +0 -93
- data/app/assets/stylesheets/solidstats/components/dashboard_layout.css +0 -97
- data/app/assets/stylesheets/solidstats/components/gem_metadata.css +0 -1403
- data/app/assets/stylesheets/solidstats/components/navigation.css +0 -80
- data/app/assets/stylesheets/solidstats/components/quick_navigation.css +0 -54
- data/app/assets/stylesheets/solidstats/components/security.css +0 -332
- data/app/assets/stylesheets/solidstats/components/status_badge.css +0 -58
- data/app/assets/stylesheets/solidstats/components/summary_card.css +0 -66
- data/app/assets/stylesheets/solidstats/components/tab_navigation.css +0 -95
- data/app/components/solidstats/base_component.rb +0 -88
- 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 +0 -45
- data/app/components/solidstats/code_quality/section_component.rb +0 -34
- data/app/components/solidstats/dashboard_header_component.html.erb +0 -39
- data/app/components/solidstats/dashboard_header_component.rb +0 -33
- data/app/components/solidstats/previews/action_button_component_preview/button_vs_link.html.erb +0 -6
- data/app/components/solidstats/previews/action_button_component_preview/sizes.html.erb +0 -6
- data/app/components/solidstats/previews/action_button_component_preview/variants.html.erb +0 -6
- data/app/components/solidstats/previews/action_button_component_preview/with_icons.html.erb +0 -6
- data/app/components/solidstats/previews/action_button_component_preview.rb +0 -64
- data/app/components/solidstats/previews/navigation_component_preview.rb +0 -74
- data/app/components/solidstats/previews/stats_overview_component_preview.rb +0 -100
- data/app/components/solidstats/previews/status_badge_component_preview/sizes.html.erb +0 -6
- data/app/components/solidstats/previews/status_badge_component_preview/statuses.html.erb +0 -6
- data/app/components/solidstats/previews/status_badge_component_preview/with_icons.html.erb +0 -6
- data/app/components/solidstats/previews/status_badge_component_preview.rb +0 -49
- data/app/components/solidstats/previews/summary_card_component_preview/clickable.html.erb +0 -9
- data/app/components/solidstats/previews/summary_card_component_preview/dashboard_layout.html.erb +0 -9
- data/app/components/solidstats/previews/summary_card_component_preview/statuses.html.erb +0 -6
- data/app/components/solidstats/previews/summary_card_component_preview/value_formats.html.erb +0 -6
- data/app/components/solidstats/previews/summary_card_component_preview.rb +0 -67
- data/app/components/solidstats/quick_navigation_component.html.erb +0 -8
- data/app/components/solidstats/quick_navigation_component.rb +0 -21
- data/app/components/solidstats/security/gem_impact_analysis_component.html.erb +0 -44
- data/app/components/solidstats/security/gem_impact_analysis_component.rb +0 -45
- data/app/components/solidstats/security/overview_component.html.erb +0 -21
- data/app/components/solidstats/security/overview_component.rb +0 -104
- data/app/components/solidstats/security/section_component.html.erb +0 -26
- data/app/components/solidstats/security/section_component.rb +0 -52
- data/app/components/solidstats/security/timeline_component.html.erb +0 -39
- data/app/components/solidstats/security/timeline_component.rb +0 -43
- data/app/components/solidstats/tasks_section_component.html.erb +0 -17
- data/app/components/solidstats/tasks_section_component.rb +0 -22
- data/app/components/solidstats/ui/action_button_component.html.erb +0 -6
- data/app/components/solidstats/ui/action_button_component.rb +0 -71
- data/app/components/solidstats/ui/dashboard_layout_component.html.erb +0 -19
- data/app/components/solidstats/ui/dashboard_layout_component.rb +0 -85
- data/app/components/solidstats/ui/navigation_component.html.erb +0 -34
- data/app/components/solidstats/ui/navigation_component.rb +0 -72
- data/app/components/solidstats/ui/stats_overview_component.html.erb +0 -14
- data/app/components/solidstats/ui/stats_overview_component.rb +0 -78
- data/app/components/solidstats/ui/status_badge_component.html.erb +0 -6
- data/app/components/solidstats/ui/status_badge_component.rb +0 -42
- data/app/components/solidstats/ui/summary_card_component.html.erb +0 -12
- data/app/components/solidstats/ui/summary_card_component.rb +0 -63
- data/app/components/solidstats/ui/tab_navigation_component.html.erb +0 -22
- data/app/components/solidstats/ui/tab_navigation_component.rb +0 -79
- data/app/controllers/solidstats/gem_metadata_controller.rb +0 -12
- data/app/services/solidstats/audit_service.rb +0 -56
- data/app/services/solidstats/data_collector_service.rb +0 -83
- data/app/services/solidstats/gem_metadata/fetcher_service.rb +0 -136
- data/app/services/solidstats/todo_service.rb +0 -114
- data/app/views/solidstats/dashboard/_log_monitor.html.erb +0 -759
- data/app/views/solidstats/dashboard/_todos.html.erb +0 -151
- data/app/views/solidstats/dashboard/audit/_additional_styles.css +0 -22
- data/app/views/solidstats/dashboard/audit/_audit_badge.html.erb +0 -5
- data/app/views/solidstats/dashboard/audit/_audit_details.html.erb +0 -495
- data/app/views/solidstats/dashboard/audit/_audit_summary.html.erb +0 -26
- data/app/views/solidstats/dashboard/audit/_no_vulnerabilities.html.erb +0 -3
- data/app/views/solidstats/dashboard/audit/_security_audit.html.erb +0 -14
- data/app/views/solidstats/dashboard/audit/_vulnerabilities_table.html.erb +0 -1120
- data/app/views/solidstats/dashboard/audit/_vulnerability_details.html.erb +0 -63
- data/app/views/solidstats/dashboard/index.html.erb +0 -81
- data/app/views/solidstats/gem_metadata/_panel.html.erb +0 -419
- data/lib/generators/solidstats/feature/feature_generator.rb +0 -170
- data/lib/generators/solidstats/feature/templates/component.html.erb +0 -84
- data/lib/generators/solidstats/feature/templates/component.rb.erb +0 -103
- data/lib/generators/solidstats/feature/templates/component.scss +0 -243
- data/lib/generators/solidstats/feature/templates/component_test.rb.erb +0 -183
- data/lib/generators/solidstats/feature/templates/controller.rb.erb +0 -44
- data/lib/generators/solidstats/feature/templates/controller_test.rb.erb +0 -111
- data/lib/generators/solidstats/feature/templates/detail_view.html.erb +0 -755
- data/lib/generators/solidstats/feature/templates/preview.rb.erb +0 -107
- data/lib/generators/solidstats/feature/templates/service.rb.erb +0 -132
- data/lib/generators/solidstats/feature/templates/service_test.rb.erb +0 -109
- data/lib/generators/solidstats/install_generator.rb +0 -109
- data/lib/generators/solidstats/templates/initializer.rb +0 -112
- data/lib/solidstats/asset_compatibility.rb +0 -238
- data/lib/solidstats/asset_manifest.rb +0 -205
- data/lib/tasks/solidstats_tasks.rake +0 -4
@@ -0,0 +1,319 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Solidstats
|
7
|
+
class StylePatrolService
|
8
|
+
CACHE_KEY = "style_patrol_data"
|
9
|
+
CACHE_DURATION = 6.hours
|
10
|
+
STANDARD_JSON_FILE = Rails.root.join('solidstats', 'standard.json')
|
11
|
+
|
12
|
+
def self.collect_data(force_refresh: false)
|
13
|
+
return cached_data unless force_refresh || cache_expired?
|
14
|
+
|
15
|
+
analysis_data = analyze_code_quality
|
16
|
+
cache_data(analysis_data)
|
17
|
+
save_to_standard_json(analysis_data)
|
18
|
+
update_summary_json(analysis_data)
|
19
|
+
analysis_data
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.get_summary
|
23
|
+
data = collect_data
|
24
|
+
{
|
25
|
+
status: data[:status],
|
26
|
+
total_files: data.dig(:summary, :total_files) || 0,
|
27
|
+
total_offenses: data.dig(:summary, :total_offenses) || 0,
|
28
|
+
correctable_count: data.dig(:summary, :correctable_count) || 0,
|
29
|
+
last_analyzed: data[:analyzed_at],
|
30
|
+
health_score: calculate_health_score(data)
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.refresh_cache
|
35
|
+
Rails.cache.delete(CACHE_KEY)
|
36
|
+
collect_data(force_refresh: true)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def self.analyze_code_quality
|
42
|
+
result = `standardrb --format json 2>&1`
|
43
|
+
|
44
|
+
# Extract JSON from output (may contain debug info or other text)
|
45
|
+
json_content = extract_json_from_output(result)
|
46
|
+
|
47
|
+
if json_content.nil?
|
48
|
+
# No JSON found - could be clean result or error
|
49
|
+
if $?.success?
|
50
|
+
# Clean result - no issues found
|
51
|
+
{
|
52
|
+
status: "clean",
|
53
|
+
issues: [],
|
54
|
+
summary: {
|
55
|
+
total_files: 0,
|
56
|
+
total_offenses: 0,
|
57
|
+
correctable_count: 0,
|
58
|
+
target_file_count: 0,
|
59
|
+
inspected_file_count: 0
|
60
|
+
},
|
61
|
+
raw_data: nil,
|
62
|
+
analyzed_at: Time.current.iso8601
|
63
|
+
}
|
64
|
+
else
|
65
|
+
{
|
66
|
+
status: "error",
|
67
|
+
error_message: result,
|
68
|
+
analyzed_at: Time.current.iso8601
|
69
|
+
}
|
70
|
+
end
|
71
|
+
else
|
72
|
+
begin
|
73
|
+
json_output = JSON.parse(json_content)
|
74
|
+
process_standard_output(json_output)
|
75
|
+
rescue JSON::ParserError => e
|
76
|
+
{
|
77
|
+
status: "error",
|
78
|
+
error_message: "Failed to parse JSON: #{e.message}\nRaw output: #{result}",
|
79
|
+
analyzed_at: Time.current.iso8601
|
80
|
+
}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
rescue StandardError => e
|
84
|
+
{
|
85
|
+
status: "error",
|
86
|
+
error_message: e.message,
|
87
|
+
analyzed_at: Time.current.iso8601
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.process_standard_output(json_data)
|
92
|
+
issues = []
|
93
|
+
|
94
|
+
# Handle StandardRB JSON format
|
95
|
+
files = json_data["files"] || []
|
96
|
+
|
97
|
+
files.each do |file_data|
|
98
|
+
file_data["offenses"]&.each do |offense|
|
99
|
+
issues << {
|
100
|
+
file: file_data["path"],
|
101
|
+
line: offense["location"]["line"],
|
102
|
+
column: offense["location"]["column"],
|
103
|
+
severity: offense["severity"],
|
104
|
+
message: offense["message"],
|
105
|
+
cop_name: offense["cop_name"],
|
106
|
+
correctable: offense["correctable"] || false
|
107
|
+
}
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Get summary from StandardRB output
|
112
|
+
summary_data = json_data["summary"] || {}
|
113
|
+
|
114
|
+
{
|
115
|
+
status: issues.any? ? "issues_found" : "clean",
|
116
|
+
issues: issues,
|
117
|
+
summary: {
|
118
|
+
total_files: files.count,
|
119
|
+
total_offenses: summary_data["offense_count"] || issues.count,
|
120
|
+
correctable_count: issues.count { |i| i[:correctable] },
|
121
|
+
target_file_count: summary_data["target_file_count"] || 0,
|
122
|
+
inspected_file_count: summary_data["inspected_file_count"] || 0
|
123
|
+
},
|
124
|
+
raw_data: json_data,
|
125
|
+
analyzed_at: Time.current.iso8601
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.cached_data
|
130
|
+
Rails.cache.fetch(CACHE_KEY, expires_in: CACHE_DURATION) do
|
131
|
+
analyze_code_quality
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.cache_data(data)
|
136
|
+
Rails.cache.write(CACHE_KEY, data, expires_in: CACHE_DURATION)
|
137
|
+
data
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.cache_expired?
|
141
|
+
cached_entry = Rails.cache.read(CACHE_KEY)
|
142
|
+
return true if cached_entry.nil?
|
143
|
+
|
144
|
+
cached_time = Time.parse(cached_entry[:analyzed_at])
|
145
|
+
Time.current > cached_time + CACHE_DURATION
|
146
|
+
rescue
|
147
|
+
true
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.calculate_health_score(data)
|
151
|
+
return 100 if data[:status] == "clean"
|
152
|
+
return 0 if data[:status] == "error"
|
153
|
+
|
154
|
+
total_offenses = data.dig(:summary, :total_offenses) || 0
|
155
|
+
total_files = data.dig(:summary, :total_files) || 1
|
156
|
+
|
157
|
+
# Health score: 100 - (offenses per file * 10), minimum 0
|
158
|
+
score = 100 - ((total_offenses.to_f / total_files) * 10).round
|
159
|
+
[score, 0].max
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.update_summary_json(data)
|
163
|
+
summary_file_path = Rails.root.join('solidstats', 'summary.json')
|
164
|
+
|
165
|
+
# Ensure directory exists
|
166
|
+
FileUtils.mkdir_p(File.dirname(summary_file_path))
|
167
|
+
|
168
|
+
# Read existing summary or create new one
|
169
|
+
begin
|
170
|
+
existing_summary = File.exist?(summary_file_path) ? JSON.parse(File.read(summary_file_path)) : {}
|
171
|
+
rescue JSON::ParserError
|
172
|
+
existing_summary = {}
|
173
|
+
end
|
174
|
+
|
175
|
+
# Calculate style patrol statistics
|
176
|
+
total_offenses = data.dig(:summary, :total_offenses) || 0
|
177
|
+
correctable_count = data.dig(:summary, :correctable_count) || 0
|
178
|
+
status = determine_dashboard_status(data[:status], total_offenses)
|
179
|
+
health_score = calculate_health_score(data)
|
180
|
+
|
181
|
+
# Create badges based on code quality metrics
|
182
|
+
badges = []
|
183
|
+
badges << { "text" => "Health: #{health_score}%", "color" => health_badge_color(health_score) }
|
184
|
+
|
185
|
+
if total_offenses > 0
|
186
|
+
badges << { "text" => "#{total_offenses} Issues", "color" => "warning" }
|
187
|
+
|
188
|
+
if correctable_count > 0
|
189
|
+
badges << { "text" => "#{correctable_count} Auto-fixable", "color" => "info" }
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Update the StylePatrol entry
|
194
|
+
existing_summary["StylePatrol"] = {
|
195
|
+
"icon" => "code",
|
196
|
+
"status" => status,
|
197
|
+
"value" => generate_dashboard_message(data[:status], total_offenses, health_score),
|
198
|
+
"last_updated" => data[:analyzed_at],
|
199
|
+
"url" => "/solidstats/quality/style_patrol",
|
200
|
+
"badges" => badges
|
201
|
+
}
|
202
|
+
|
203
|
+
# Write updated summary
|
204
|
+
File.write(summary_file_path, JSON.pretty_generate(existing_summary))
|
205
|
+
Rails.logger.info("Updated summary.json with StylePatrol data")
|
206
|
+
rescue => e
|
207
|
+
Rails.logger.error("Failed to update summary.json: #{e.message}")
|
208
|
+
end
|
209
|
+
|
210
|
+
def self.determine_dashboard_status(analysis_status, offense_count)
|
211
|
+
case analysis_status
|
212
|
+
when "clean" then "success"
|
213
|
+
when "error" then "danger"
|
214
|
+
when "issues_found"
|
215
|
+
case offense_count
|
216
|
+
when 0..5 then "info"
|
217
|
+
when 6..15 then "warning"
|
218
|
+
else "danger"
|
219
|
+
end
|
220
|
+
else "warning"
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def self.health_badge_color(score)
|
225
|
+
case score
|
226
|
+
when 90..100 then "success"
|
227
|
+
when 70..89 then "info"
|
228
|
+
when 50..69 then "warning"
|
229
|
+
else "error"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def self.generate_dashboard_message(status, offense_count, health_score)
|
234
|
+
case status
|
235
|
+
when "clean"
|
236
|
+
"Code is clean! ✨"
|
237
|
+
when "error"
|
238
|
+
"Analysis failed ❌"
|
239
|
+
when "issues_found"
|
240
|
+
"#{offense_count} issues found"
|
241
|
+
else
|
242
|
+
"Status unknown"
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def self.save_to_standard_json(data)
|
247
|
+
# Ensure directory exists
|
248
|
+
FileUtils.mkdir_p(File.dirname(STANDARD_JSON_FILE))
|
249
|
+
|
250
|
+
# Prepare data for standard.json file
|
251
|
+
standard_data = {
|
252
|
+
last_updated: data[:analyzed_at],
|
253
|
+
status: data[:status],
|
254
|
+
metadata: data.dig(:raw_data, "metadata") || {},
|
255
|
+
files: data.dig(:raw_data, "files") || [],
|
256
|
+
summary: data.dig(:raw_data, "summary") || {},
|
257
|
+
processed_summary: data[:summary] || {},
|
258
|
+
issues_count: data[:issues]&.count || 0
|
259
|
+
}
|
260
|
+
|
261
|
+
# Write to standard.json file
|
262
|
+
File.write(STANDARD_JSON_FILE, JSON.pretty_generate(standard_data))
|
263
|
+
Rails.logger.info("Saved StandardRB data to #{STANDARD_JSON_FILE}")
|
264
|
+
rescue => e
|
265
|
+
Rails.logger.error("Failed to save standard.json: #{e.message}")
|
266
|
+
end
|
267
|
+
|
268
|
+
def self.extract_json_from_output(output)
|
269
|
+
# Look for JSON content - should start with { and end with }
|
270
|
+
# Handle cases where debug info or other text appears before/after JSON
|
271
|
+
|
272
|
+
return nil if output.nil? || output.strip.empty?
|
273
|
+
|
274
|
+
# Split by lines and look for the line that starts with JSON
|
275
|
+
lines = output.split("\n")
|
276
|
+
json_lines = []
|
277
|
+
json_started = false
|
278
|
+
brace_count = 0
|
279
|
+
|
280
|
+
lines.each do |line|
|
281
|
+
# Skip debug lines that mention "Subprocess Debugger", "ruby-debug-ide", etc.
|
282
|
+
next if line.include?("Subprocess Debugger") ||
|
283
|
+
line.include?("ruby-debug-ide") ||
|
284
|
+
line.include?("debase") ||
|
285
|
+
line.include?("listens on")
|
286
|
+
|
287
|
+
# Look for the start of JSON (line starting with {)
|
288
|
+
if !json_started && line.strip.start_with?('{')
|
289
|
+
json_started = true
|
290
|
+
end
|
291
|
+
|
292
|
+
if json_started
|
293
|
+
json_lines << line
|
294
|
+
|
295
|
+
# Count braces to know when JSON ends
|
296
|
+
brace_count += line.count('{') - line.count('}')
|
297
|
+
|
298
|
+
# If we've closed all braces, we've reached the end of JSON
|
299
|
+
break if brace_count == 0
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
return nil if json_lines.empty?
|
304
|
+
|
305
|
+
json_content = json_lines.join("\n")
|
306
|
+
|
307
|
+
# Validate that it looks like StandardRB JSON by checking for expected structure
|
308
|
+
return nil unless json_content.include?('"metadata"') ||
|
309
|
+
json_content.include?('"files"') ||
|
310
|
+
json_content.include?('"summary"')
|
311
|
+
|
312
|
+
json_content
|
313
|
+
rescue => e
|
314
|
+
Rails.logger.error("Error extracting JSON from StandardRB output: #{e.message}")
|
315
|
+
Rails.logger.error("Raw output was: #{output}")
|
316
|
+
nil
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
@@ -4,15 +4,21 @@
|
|
4
4
|
<title>Solidstats</title>
|
5
5
|
<%= csrf_meta_tags %>
|
6
6
|
<%= csp_meta_tag %>
|
7
|
+
|
8
|
+
<!-- Solidstats CDN Dependencies -->
|
9
|
+
<%= solidstats_cdn_dependencies %>
|
7
10
|
|
8
11
|
<%= yield :head %>
|
9
12
|
|
13
|
+
<!-- Inline Solidstats Styles -->
|
10
14
|
<%= solidstats_styles %>
|
11
|
-
<%= solidstats_scripts %>
|
12
15
|
</head>
|
13
|
-
<body>
|
16
|
+
<body data-theme="light" class="min-h-screen bg-base-200">
|
14
17
|
|
15
18
|
<%= yield %>
|
16
19
|
|
20
|
+
<!-- Inline Solidstats Scripts -->
|
21
|
+
<%= solidstats_scripts %>
|
22
|
+
|
17
23
|
</body>
|
18
24
|
</html>
|
@@ -0,0 +1,84 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html data-theme="dark" class="transition-colors duration-300">
|
3
|
+
<head>
|
4
|
+
<title>Solidstats Dashboard</title>
|
5
|
+
<meta charset="utf-8">
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
7
|
+
<%= csrf_meta_tags %>
|
8
|
+
<%= csp_meta_tag %>
|
9
|
+
|
10
|
+
<%= yield :head %>
|
11
|
+
|
12
|
+
<!-- Solidstats CDN Dependencies -->
|
13
|
+
<%= solidstats_cdn_dependencies %>
|
14
|
+
|
15
|
+
<!-- Inline Solidstats Styles -->
|
16
|
+
<%= solidstats_styles %>
|
17
|
+
</head>
|
18
|
+
<body class="min-h-screen bg-base-100 transition-all duration-300">
|
19
|
+
|
20
|
+
<!-- DaisyUI Navbar -->
|
21
|
+
<div class="navbar bg-base-100 shadow-lg border-b border-base-300">
|
22
|
+
<div class="navbar-start">
|
23
|
+
<div class="flex items-center space-x-4">
|
24
|
+
<div class="avatar">
|
25
|
+
<div class="w-8 rounded-lg bg-gradient-to-r from-primary to-secondary flex items-center justify-center">
|
26
|
+
<i data-feather="activity" class="w-5 h-5 text-white"></i>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
<%= link_to solidstats.solidstats_dashboard_path, class: "text-xl font-bold gradient-text hover:opacity-80 transition-opacity" do %>
|
30
|
+
Solidstats
|
31
|
+
<% end %>
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<div class="navbar-end">
|
36
|
+
<!-- Simple Theme Toggle -->
|
37
|
+
<button class="btn btn-ghost btn-circle" onclick="toggleTheme()">
|
38
|
+
<i data-feather="sun" class="w-5 h-5 hidden dark:inline"></i>
|
39
|
+
<i data-feather="moon" class="w-5 h-5 dark:hidden"></i>
|
40
|
+
</button>
|
41
|
+
</div>
|
42
|
+
</div>
|
43
|
+
|
44
|
+
<!-- Main Content -->
|
45
|
+
<main class="container mx-auto px-4 py-8">
|
46
|
+
<%= yield %>
|
47
|
+
</main>
|
48
|
+
|
49
|
+
<!-- Simple Theme Toggle Script -->
|
50
|
+
<script>
|
51
|
+
// Initialize Feather icons
|
52
|
+
feather.replace();
|
53
|
+
|
54
|
+
// Simple theme toggle between dark and light
|
55
|
+
function toggleTheme() {
|
56
|
+
const currentTheme = localStorage.getItem('theme') || 'dark';
|
57
|
+
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
58
|
+
setTheme(newTheme);
|
59
|
+
}
|
60
|
+
|
61
|
+
function setTheme(theme) {
|
62
|
+
document.documentElement.setAttribute('data-theme', theme);
|
63
|
+
localStorage.setItem('theme', theme);
|
64
|
+
|
65
|
+
// Update dark class for custom CSS compatibility
|
66
|
+
if (theme === 'dark') {
|
67
|
+
document.documentElement.classList.add('dark');
|
68
|
+
} else {
|
69
|
+
document.documentElement.classList.remove('dark');
|
70
|
+
}
|
71
|
+
|
72
|
+
// Re-initialize icons after theme change
|
73
|
+
setTimeout(() => feather.replace(), 100);
|
74
|
+
}
|
75
|
+
|
76
|
+
// Check for saved theme preference or default to 'dark'
|
77
|
+
const currentTheme = localStorage.getItem('theme') || 'dark';
|
78
|
+
setTheme(currentTheme);
|
79
|
+
</script>
|
80
|
+
|
81
|
+
<!-- Inline Solidstats Scripts -->
|
82
|
+
<%= solidstats_scripts %>
|
83
|
+
</body>
|
84
|
+
</html>
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<%# Dashboard Index View %>
|
2
|
+
|
3
|
+
<div class="dashboard-enter">
|
4
|
+
<!-- Welcome Section -->
|
5
|
+
<div class="hero mb-8">
|
6
|
+
<div class="hero-content text-center">
|
7
|
+
<div class="max-w-2xl">
|
8
|
+
<h2 class="text-3xl font-bold mb-2">
|
9
|
+
Welcome to Your Dashboard
|
10
|
+
</h2>
|
11
|
+
<p class="text-base-content/70">
|
12
|
+
Monitor your application's performance, security, and health metrics in real-time
|
13
|
+
</p>
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<!-- Stats Grid -->
|
19
|
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 mb-8">
|
20
|
+
<% @dashboard_cards.each do |card| %>
|
21
|
+
<%= render 'solidstats/shared/dashboard_card', card %>
|
22
|
+
<% end %>
|
23
|
+
</div>
|
24
|
+
|
25
|
+
<!-- Quick Actions -->
|
26
|
+
<%= render 'solidstats/shared/quick_actions' %>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
<script>
|
30
|
+
feather.replace();
|
31
|
+
|
32
|
+
// Auto-refresh functionality with DaisyUI animations
|
33
|
+
setInterval(() => {
|
34
|
+
document.querySelectorAll('.card').forEach(card => {
|
35
|
+
card.style.transform = 'scale(1.02)';
|
36
|
+
setTimeout(() => card.style.transform = '', 200);
|
37
|
+
});
|
38
|
+
}, 30000);
|
39
|
+
</script>
|