solidstats 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -63
  3. data/README.md +27 -0
  4. data/Rakefile +3 -3
  5. data/app/assets/javascripts/solidstats/dashboard.js +0 -46
  6. data/app/assets/stylesheets/solidstats/dashboard.css +48 -0
  7. data/app/controllers/solidstats/dashboard_controller.rb +73 -61
  8. data/app/controllers/solidstats/logs_controller.rb +72 -0
  9. data/app/controllers/solidstats/performance_controller.rb +25 -0
  10. data/app/controllers/solidstats/productivity_controller.rb +39 -0
  11. data/app/controllers/solidstats/quality_controller.rb +152 -0
  12. data/app/controllers/solidstats/securities_controller.rb +30 -0
  13. data/app/helpers/solidstats/application_helper.rb +124 -11
  14. data/app/helpers/solidstats/performance_helper.rb +87 -0
  15. data/app/helpers/solidstats/productivity_helper.rb +38 -0
  16. data/app/services/solidstats/bundler_audit_service.rb +206 -0
  17. data/app/services/solidstats/coverage_compass_service.rb +335 -0
  18. data/app/services/solidstats/load_lens_service.rb +474 -0
  19. data/app/services/solidstats/log_size_monitor_service.rb +197 -66
  20. data/app/services/solidstats/my_todo_service.rb +242 -0
  21. data/app/services/solidstats/style_patrol_service.rb +319 -0
  22. data/app/views/layouts/solidstats/application.html.erb +8 -2
  23. data/app/views/layouts/solidstats/dashboard.html.erb +84 -0
  24. data/app/views/solidstats/dashboard/dashboard.html.erb +39 -0
  25. data/app/views/solidstats/logs/logs_size.html.erb +409 -0
  26. data/app/views/solidstats/performance/load_lens.html.erb +158 -0
  27. data/app/views/solidstats/productivity/_todo_list.html.erb +49 -0
  28. data/app/views/solidstats/productivity/my_todos.html.erb +84 -0
  29. data/app/views/solidstats/quality/coverage_compass.html.erb +420 -0
  30. data/app/views/solidstats/quality/style_patrol.html.erb +463 -0
  31. data/app/views/solidstats/securities/bundler_audit.html.erb +345 -0
  32. data/app/views/solidstats/shared/_dashboard_card.html.erb +160 -0
  33. data/app/views/solidstats/shared/_quick_actions.html.erb +26 -0
  34. data/config/routes.rb +31 -6
  35. data/lib/generators/solidstats/clean/clean_generator.rb +24 -0
  36. data/lib/generators/solidstats/clean/templates/README +8 -0
  37. data/lib/generators/solidstats/install/install_generator.rb +51 -10
  38. data/lib/generators/solidstats/install/templates/README +7 -0
  39. data/lib/solidstats/engine.rb +6 -71
  40. data/lib/solidstats/version.rb +1 -1
  41. data/lib/solidstats.rb +19 -303
  42. data/lib/tasks/solidstats.rake +67 -0
  43. data/lib/tasks/solidstats_performance.rake +61 -0
  44. data/lib/tasks/solidstats_tasks.rake +16 -4
  45. metadata +33 -95
  46. data/app/assets/javascripts/solidstats/gem_metadata.js +0 -554
  47. data/app/assets/stylesheets/solidstats/components/action_button.css +0 -99
  48. data/app/assets/stylesheets/solidstats/components/dashboard.css +0 -151
  49. data/app/assets/stylesheets/solidstats/components/dashboard_header.css +0 -93
  50. data/app/assets/stylesheets/solidstats/components/dashboard_layout.css +0 -97
  51. data/app/assets/stylesheets/solidstats/components/gem_metadata.css +0 -1403
  52. data/app/assets/stylesheets/solidstats/components/navigation.css +0 -80
  53. data/app/assets/stylesheets/solidstats/components/quick_navigation.css +0 -54
  54. data/app/assets/stylesheets/solidstats/components/security.css +0 -332
  55. data/app/assets/stylesheets/solidstats/components/status_badge.css +0 -58
  56. data/app/assets/stylesheets/solidstats/components/summary_card.css +0 -66
  57. data/app/assets/stylesheets/solidstats/components/tab_navigation.css +0 -95
  58. data/app/components/solidstats/base_component.rb +0 -88
  59. data/app/components/solidstats/code_quality/code_quality_section_component.html.erb +0 -0
  60. data/app/components/solidstats/code_quality/code_quality_section_component.rb +0 -0
  61. data/app/components/solidstats/code_quality/section_component.html.erb +0 -45
  62. data/app/components/solidstats/code_quality/section_component.rb +0 -34
  63. data/app/components/solidstats/dashboard_header_component.html.erb +0 -39
  64. data/app/components/solidstats/dashboard_header_component.rb +0 -33
  65. data/app/components/solidstats/previews/action_button_component_preview/button_vs_link.html.erb +0 -6
  66. data/app/components/solidstats/previews/action_button_component_preview/sizes.html.erb +0 -6
  67. data/app/components/solidstats/previews/action_button_component_preview/variants.html.erb +0 -6
  68. data/app/components/solidstats/previews/action_button_component_preview/with_icons.html.erb +0 -6
  69. data/app/components/solidstats/previews/action_button_component_preview.rb +0 -64
  70. data/app/components/solidstats/previews/navigation_component_preview.rb +0 -74
  71. data/app/components/solidstats/previews/stats_overview_component_preview.rb +0 -100
  72. data/app/components/solidstats/previews/status_badge_component_preview/sizes.html.erb +0 -6
  73. data/app/components/solidstats/previews/status_badge_component_preview/statuses.html.erb +0 -6
  74. data/app/components/solidstats/previews/status_badge_component_preview/with_icons.html.erb +0 -6
  75. data/app/components/solidstats/previews/status_badge_component_preview.rb +0 -49
  76. data/app/components/solidstats/previews/summary_card_component_preview/clickable.html.erb +0 -9
  77. data/app/components/solidstats/previews/summary_card_component_preview/dashboard_layout.html.erb +0 -9
  78. data/app/components/solidstats/previews/summary_card_component_preview/statuses.html.erb +0 -6
  79. data/app/components/solidstats/previews/summary_card_component_preview/value_formats.html.erb +0 -6
  80. data/app/components/solidstats/previews/summary_card_component_preview.rb +0 -67
  81. data/app/components/solidstats/quick_navigation_component.html.erb +0 -8
  82. data/app/components/solidstats/quick_navigation_component.rb +0 -21
  83. data/app/components/solidstats/security/gem_impact_analysis_component.html.erb +0 -44
  84. data/app/components/solidstats/security/gem_impact_analysis_component.rb +0 -45
  85. data/app/components/solidstats/security/overview_component.html.erb +0 -21
  86. data/app/components/solidstats/security/overview_component.rb +0 -104
  87. data/app/components/solidstats/security/section_component.html.erb +0 -26
  88. data/app/components/solidstats/security/section_component.rb +0 -52
  89. data/app/components/solidstats/security/timeline_component.html.erb +0 -39
  90. data/app/components/solidstats/security/timeline_component.rb +0 -43
  91. data/app/components/solidstats/tasks_section_component.html.erb +0 -17
  92. data/app/components/solidstats/tasks_section_component.rb +0 -22
  93. data/app/components/solidstats/ui/action_button_component.html.erb +0 -6
  94. data/app/components/solidstats/ui/action_button_component.rb +0 -71
  95. data/app/components/solidstats/ui/dashboard_layout_component.html.erb +0 -19
  96. data/app/components/solidstats/ui/dashboard_layout_component.rb +0 -85
  97. data/app/components/solidstats/ui/navigation_component.html.erb +0 -34
  98. data/app/components/solidstats/ui/navigation_component.rb +0 -72
  99. data/app/components/solidstats/ui/stats_overview_component.html.erb +0 -14
  100. data/app/components/solidstats/ui/stats_overview_component.rb +0 -78
  101. data/app/components/solidstats/ui/status_badge_component.html.erb +0 -6
  102. data/app/components/solidstats/ui/status_badge_component.rb +0 -42
  103. data/app/components/solidstats/ui/summary_card_component.html.erb +0 -12
  104. data/app/components/solidstats/ui/summary_card_component.rb +0 -63
  105. data/app/components/solidstats/ui/tab_navigation_component.html.erb +0 -22
  106. data/app/components/solidstats/ui/tab_navigation_component.rb +0 -79
  107. data/app/services/solidstats/audit_service.rb +0 -56
  108. data/app/services/solidstats/data_collector_service.rb +0 -83
  109. data/app/services/solidstats/gem_metadata/fetcher_service.rb +0 -136
  110. data/app/services/solidstats/todo_service.rb +0 -114
  111. data/app/views/solidstats/dashboard/_log_monitor.html.erb +0 -759
  112. data/app/views/solidstats/dashboard/_todos.html.erb +0 -151
  113. data/app/views/solidstats/dashboard/audit/_additional_styles.css +0 -22
  114. data/app/views/solidstats/dashboard/audit/_audit_badge.html.erb +0 -5
  115. data/app/views/solidstats/dashboard/audit/_audit_details.html.erb +0 -495
  116. data/app/views/solidstats/dashboard/audit/_audit_summary.html.erb +0 -26
  117. data/app/views/solidstats/dashboard/audit/_no_vulnerabilities.html.erb +0 -3
  118. data/app/views/solidstats/dashboard/audit/_security_audit.html.erb +0 -14
  119. data/app/views/solidstats/dashboard/audit/_vulnerabilities_table.html.erb +0 -1120
  120. data/app/views/solidstats/dashboard/audit/_vulnerability_details.html.erb +0 -63
  121. data/app/views/solidstats/dashboard/index.html.erb +0 -81
  122. data/app/views/solidstats/gem_metadata/_panel.html.erb +0 -419
  123. data/lib/generators/solidstats/feature/feature_generator.rb +0 -170
  124. data/lib/generators/solidstats/feature/templates/component.html.erb +0 -84
  125. data/lib/generators/solidstats/feature/templates/component.rb.erb +0 -103
  126. data/lib/generators/solidstats/feature/templates/component.scss +0 -243
  127. data/lib/generators/solidstats/feature/templates/component_test.rb.erb +0 -183
  128. data/lib/generators/solidstats/feature/templates/controller.rb.erb +0 -44
  129. data/lib/generators/solidstats/feature/templates/controller_test.rb.erb +0 -111
  130. data/lib/generators/solidstats/feature/templates/detail_view.html.erb +0 -755
  131. data/lib/generators/solidstats/feature/templates/preview.rb.erb +0 -107
  132. data/lib/generators/solidstats/feature/templates/service.rb.erb +0 -132
  133. data/lib/generators/solidstats/feature/templates/service_test.rb.erb +0 -109
  134. data/lib/generators/solidstats/install_generator.rb +0 -109
  135. data/lib/tasks/solidstats_install.rake +0 -133
@@ -0,0 +1,84 @@
1
+ <% content_for :title, "My TODOs" %>
2
+
3
+ <div class="container mx-auto px-4 py-6">
4
+ <!-- Header -->
5
+ <div class="flex justify-between items-center mb-6">
6
+ <div>
7
+ <h1 class="text-3xl font-bold text-base-content">My TODOs</h1>
8
+ <p class="text-base-content/70 mt-1">Track TODO comments across your project</p>
9
+ </div>
10
+
11
+ <div class="flex gap-2">
12
+ <%= button_to refresh_todos_productivity_index_path,
13
+ method: :post,
14
+ class: "btn btn-primary btn-sm",
15
+ data: { confirm: "Refresh TODO cache? This may take a moment." } do %>
16
+ <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
17
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
18
+ </svg>
19
+ Refresh
20
+ <% end %>
21
+ <%= link_to solidstats.solidstats_dashboard_path, class: "btn btn-ghost btn-sm" do %>
22
+ <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
23
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
24
+ </svg>
25
+ Back to Dashboard
26
+ <% end %>
27
+ </div>
28
+ </div>
29
+
30
+ <!-- Stats Cards -->
31
+ <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
32
+ <div class="stat bg-base-200 rounded-lg">
33
+ <div class="stat-title">Total TODOs</div>
34
+ <div class="stat-value text-primary"><%= @stats[:total] %></div>
35
+ </div>
36
+
37
+ <div class="stat bg-base-200 rounded-lg">
38
+ <div class="stat-title">Files with TODOs</div>
39
+ <div class="stat-value text-secondary"><%= @stats[:files_with_todos] %></div>
40
+ </div>
41
+
42
+ <div class="stat bg-base-200 rounded-lg">
43
+ <div class="stat-title">Most Common</div>
44
+ <div class="stat-value text-accent">
45
+ <%= @stats[:by_type].max_by { |_, count| count }&.first&.upcase || 'N/A' %>
46
+ </div>
47
+ </div>
48
+
49
+ <div class="stat bg-base-200 rounded-lg">
50
+ <div class="stat-title">Last Updated</div>
51
+ <div class="stat-value text-sm">
52
+ <%= @summary[:last_updated] ? time_ago_in_words(Time.parse(@summary[:last_updated].to_s)) + ' ago' : 'Never' %>
53
+ </div>
54
+ </div>
55
+ </div>
56
+
57
+ <!-- TODO Type Tabs -->
58
+ <div class="tabs tabs-lifted mb-6">
59
+ <input type="radio" name="todo_tabs" role="tab" class="tab" aria-label="All" checked />
60
+ <div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-box p-6">
61
+ <%= render 'todo_list', todos: @todos, title: 'All TODOs' %>
62
+ </div>
63
+
64
+ <% @todos_by_type.each do |type, todos| %>
65
+ <input type="radio" name="todo_tabs" role="tab" class="tab" aria-label="<%= type.upcase %>" />
66
+ <div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-box p-6">
67
+ <%= render 'todo_list', todos: todos, title: "#{type.upcase} Items" %>
68
+ </div>
69
+ <% end %>
70
+ </div>
71
+ </div>
72
+
73
+ <script>
74
+ function copyToClipboard(text) {
75
+ navigator.clipboard.writeText(text).then(() => {
76
+ // Show toast notification
77
+ const toast = document.createElement('div');
78
+ toast.className = 'toast toast-top toast-end';
79
+ toast.innerHTML = '<div class="alert alert-success"><span>Copied to clipboard!</span></div>';
80
+ document.body.appendChild(toast);
81
+ setTimeout(() => toast.remove(), 2000);
82
+ });
83
+ }
84
+ </script>
@@ -0,0 +1,420 @@
1
+ <div class="min-h-screen bg-gradient-to-br from-base-200 to-base-300 p-4 lg:p-8">
2
+ <!-- Breadcrumbs -->
3
+ <div class="breadcrumbs text-sm mb-6">
4
+ <ul>
5
+ <li><a class="text-primary hover:text-primary-focus">Dashboard</a></li>
6
+ <li><a class="text-primary hover:text-primary-focus">Quality</a></li>
7
+ <li class="text-base-content/70">Coverage Compass</li>
8
+ </ul>
9
+ </div>
10
+
11
+ <!-- Hero Header -->
12
+ <div class="hero bg-gradient-to-r from-success via-info to-primary text-primary-content rounded-3xl mb-8 shadow-2xl overflow-hidden relative">
13
+ <!-- Background Pattern -->
14
+ <div class="absolute inset-0 opacity-10">
15
+ <svg class="w-full h-full" viewBox="0 0 100 100" fill="currentColor">
16
+ <pattern id="coverage-grid" width="8" height="8" patternUnits="userSpaceOnUse">
17
+ <circle cx="4" cy="4" r="0.8"/>
18
+ </pattern>
19
+ <rect width="100" height="100" fill="url(#coverage-grid)"/>
20
+ </svg>
21
+ </div>
22
+
23
+ <div class="hero-content text-center py-16 relative z-10">
24
+ <div class="max-w-md">
25
+ <div class="text-7xl mb-6 animate-pulse">🧭</div>
26
+ <h1 class="text-6xl font-black mb-4 bg-gradient-to-r from-white to-primary-content bg-clip-text">
27
+ CoverageCompass
28
+ </h1>
29
+ <p class="text-xl opacity-90 mb-8 leading-relaxed">
30
+ Navigate your code coverage with precision
31
+ </p>
32
+
33
+ <%= form_with url: refresh_quality_coverage_compass_path, method: :post, local: true,
34
+ data: { turbo_submits_with: "Analyzing..." }, class: "inline-block" do |form| %>
35
+ <%= form.button type: "submit",
36
+ class: "btn btn-accent btn-lg gap-3 shadow-xl hover:shadow-2xl hover:scale-105 transition-all duration-300 group" do %>
37
+ <svg class="w-5 h-5 group-hover:rotate-180 transition-transform duration-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
38
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
39
+ </svg>
40
+ Run Coverage Analysis
41
+ <% end %>
42
+ <% end %>
43
+ </div>
44
+ </div>
45
+ </div>
46
+
47
+ <!-- Status Overview -->
48
+ <div class="mb-8">
49
+ <% case @analysis_data[:status] %>
50
+ <% when "success" %>
51
+ <% if @summary[:coverage_percent] >= 90 %>
52
+ <div class="alert alert-success shadow-lg">
53
+ <div>
54
+ <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
55
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
56
+ </svg>
57
+ <div>
58
+ <h3 class="font-bold text-lg">Excellent Coverage!</h3>
59
+ <div class="text-sm opacity-80">Your code is well tested with <%= @summary[:coverage_percent] %>% coverage</div>
60
+ </div>
61
+ </div>
62
+ <div class="flex-none">
63
+ <div class="badge badge-success badge-lg font-mono"><%= @summary[:coverage_grade] %></div>
64
+ </div>
65
+ </div>
66
+ <% elsif @summary[:coverage_percent] >= 70 %>
67
+ <div class="alert alert-info shadow-lg">
68
+ <div>
69
+ <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
70
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
71
+ </svg>
72
+ <div>
73
+ <h3 class="font-bold text-lg">Good Coverage</h3>
74
+ <div class="text-sm opacity-80"><%= @summary[:coverage_percent] %>% coverage - room for improvement</div>
75
+ </div>
76
+ </div>
77
+ <div class="flex-none">
78
+ <div class="badge badge-info badge-lg font-mono"><%= @summary[:coverage_grade] %></div>
79
+ </div>
80
+ </div>
81
+ <% else %>
82
+ <div class="alert alert-warning shadow-lg">
83
+ <div>
84
+ <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
85
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 14.5c-.77.833.192 2.5 1.732 2.5z"></path>
86
+ </svg>
87
+ <div>
88
+ <h3 class="font-bold text-lg">Low Coverage</h3>
89
+ <div class="text-sm opacity-80"><%= @summary[:coverage_percent] %>% coverage - needs attention</div>
90
+ </div>
91
+ </div>
92
+ <div class="flex-none">
93
+ <div class="badge badge-warning badge-lg font-mono"><%= @summary[:coverage_grade] %></div>
94
+ </div>
95
+ </div>
96
+ <% end %>
97
+ <% when "error" %>
98
+ <div class="alert alert-error shadow-lg">
99
+ <div>
100
+ <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
101
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
102
+ </svg>
103
+ <div>
104
+ <h3 class="font-bold text-lg">Coverage Analysis Error</h3>
105
+ <div class="text-sm opacity-80">Could not analyze code coverage</div>
106
+ </div>
107
+ </div>
108
+ <div class="flex-none">
109
+ <div class="badge badge-error badge-lg">Error</div>
110
+ </div>
111
+ </div>
112
+ <% end %>
113
+
114
+ <!-- Coverage Metadata -->
115
+ <% if @analysis_data[:status] == "success" %>
116
+ <div class="stats stats-horizontal shadow-lg bg-base-100 mt-4 w-full">
117
+ <div class="stat">
118
+ <div class="stat-figure text-secondary">
119
+ <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
120
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
121
+ </svg>
122
+ </div>
123
+ <div class="stat-title">Last Analyzed</div>
124
+ <div class="stat-value text-2xl">
125
+ <% if @analysis_data[:analyzed_at] %>
126
+ <% analyzed_time = @analysis_data[:analyzed_at].is_a?(String) ? Time.parse(@analysis_data[:analyzed_at]) : @analysis_data[:analyzed_at] %>
127
+ <%= time_ago_in_words(analyzed_time) %>
128
+ <% else %>
129
+ Unknown
130
+ <% end %>
131
+ </div>
132
+ <div class="stat-desc">ago</div>
133
+ </div>
134
+
135
+ <div class="stat">
136
+ <div class="stat-figure text-primary">
137
+ <div class="radial-progress text-primary border-primary border-4" style="--value:<%= @summary[:coverage_percent] %>;" role="progressbar">
138
+ <%= @summary[:coverage_percent] %>%
139
+ </div>
140
+ </div>
141
+ <div class="stat-title">Coverage</div>
142
+ <div class="stat-value text-3xl text-primary"><%= @summary[:coverage_percent] %>%</div>
143
+ <div class="stat-desc text-primary">Grade: <%= @summary[:coverage_grade] %></div>
144
+ </div>
145
+ </div>
146
+ <% end %>
147
+ </div>
148
+
149
+ <!-- Coverage Statistics -->
150
+ <% if @analysis_data[:status] == "success" %>
151
+ <div class="stats stats-vertical lg:stats-horizontal shadow-xl bg-gradient-to-r from-base-100 to-base-200 mb-8">
152
+ <div class="stat place-items-center">
153
+ <div class="stat-figure text-info">
154
+ <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
155
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
156
+ </svg>
157
+ </div>
158
+ <div class="stat-title">Files Analyzed</div>
159
+ <div class="stat-value text-info"><%= @summary[:total_files] %></div>
160
+ <div class="stat-desc">Source files</div>
161
+ </div>
162
+
163
+ <div class="stat place-items-center">
164
+ <div class="stat-figure text-success">
165
+ <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
166
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
167
+ </svg>
168
+ </div>
169
+ <div class="stat-title">Lines Covered</div>
170
+ <div class="stat-value text-success"><%= @summary[:covered_lines] %></div>
171
+ <div class="stat-desc">out of <%= @summary[:total_lines] %></div>
172
+ </div>
173
+
174
+ <div class="stat place-items-center">
175
+ <div class="stat-figure text-error">
176
+ <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
177
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
178
+ </svg>
179
+ </div>
180
+ <div class="stat-title">Lines Missed</div>
181
+ <div class="stat-value text-error"><%= @summary[:missed_lines] %></div>
182
+ <div class="stat-desc">Need coverage</div>
183
+ </div>
184
+
185
+ <div class="stat place-items-center">
186
+ <div class="stat-figure text-primary">
187
+ <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
188
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
189
+ </svg>
190
+ </div>
191
+ <div class="stat-title">Health Score</div>
192
+ <div class="stat-value text-primary"><%= @summary[:health_score] %></div>
193
+ <div class="stat-desc">out of 100</div>
194
+ </div>
195
+ </div>
196
+
197
+ <!-- Coverage Ranges -->
198
+ <div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6 mb-8">
199
+ <div class="card bg-gradient-to-br from-success to-success/80 text-success-content shadow-xl">
200
+ <div class="card-body">
201
+ <h2 class="card-title">
202
+ <div class="text-3xl">🏆</div>
203
+ Excellent (90%+)
204
+ </h2>
205
+ <div class="text-4xl font-bold"><%= @coverage_ranges[:excellent].count %></div>
206
+ <p class="opacity-80">files</p>
207
+ </div>
208
+ </div>
209
+
210
+ <div class="card bg-gradient-to-br from-info to-info/80 text-info-content shadow-xl">
211
+ <div class="card-body">
212
+ <h2 class="card-title">
213
+ <div class="text-3xl">👍</div>
214
+ Good (70-89%)
215
+ </h2>
216
+ <div class="text-4xl font-bold"><%= @coverage_ranges[:good].count %></div>
217
+ <p class="opacity-80">files</p>
218
+ </div>
219
+ </div>
220
+
221
+ <div class="card bg-gradient-to-br from-warning to-warning/80 text-warning-content shadow-xl">
222
+ <div class="card-body">
223
+ <h2 class="card-title">
224
+ <div class="text-3xl">⚠️</div>
225
+ Needs Work (50-69%)
226
+ </h2>
227
+ <div class="text-4xl font-bold"><%= @coverage_ranges[:needs_improvement].count %></div>
228
+ <p class="opacity-80">files</p>
229
+ </div>
230
+ </div>
231
+
232
+ <div class="card bg-gradient-to-br from-error to-error/80 text-error-content shadow-xl">
233
+ <div class="card-body">
234
+ <h2 class="card-title">
235
+ <div class="text-3xl">🚨</div>
236
+ Poor (<50%)
237
+ </h2>
238
+ <div class="text-4xl font-bold"><%= @coverage_ranges[:poor].count %></div>
239
+ <p class="opacity-80">files</p>
240
+ </div>
241
+ </div>
242
+ </div>
243
+
244
+ <!-- Detailed File Coverage -->
245
+ <% if @file_coverage.any? %>
246
+ <div class="card bg-base-100 shadow-xl mb-8">
247
+ <div class="card-body">
248
+ <h2 class="card-title text-2xl mb-6">
249
+ <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
250
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
251
+ </svg>
252
+ File Coverage Details
253
+ </h2>
254
+
255
+ <div class="space-y-4">
256
+ <% @file_coverage.reverse.each do |file| %>
257
+ <div class="collapse collapse-arrow bg-base-200">
258
+ <input type="checkbox" />
259
+ <div class="collapse-title text-lg font-medium flex items-center justify-between">
260
+ <div class="flex items-center gap-3">
261
+ <svg class="w-5 h-5 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
262
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
263
+ </svg>
264
+ <span class="font-mono text-sm text-base-content/70 break-all"><%= file[:file_path] %></span>
265
+ </div>
266
+ <div class="flex items-center gap-2">
267
+ <div class="radial-progress
268
+ <%= case file[:coverage_percent]
269
+ when 90..100 then 'text-success'
270
+ when 70..89 then 'text-info'
271
+ when 50..69 then 'text-warning'
272
+ else 'text-error'
273
+ end %>
274
+ border-4
275
+ <%= case file[:coverage_percent]
276
+ when 90..100 then 'border-success'
277
+ when 70..89 then 'border-info'
278
+ when 50..69 then 'border-warning'
279
+ else 'border-error'
280
+ end %>"
281
+ style="--value:<%= file[:coverage_percent] %>; --size:3rem;">
282
+ <%= file[:coverage_percent].to_i %>%
283
+ </div>
284
+ </div>
285
+ </div>
286
+
287
+ <div class="collapse-content">
288
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4 pt-4">
289
+ <div class="stat bg-base-300 rounded-lg">
290
+ <div class="stat-title">Total Lines</div>
291
+ <div class="stat-value text-primary"><%= file[:total_lines] %></div>
292
+ </div>
293
+ <div class="stat bg-base-300 rounded-lg">
294
+ <div class="stat-title">Covered</div>
295
+ <div class="stat-value text-success"><%= file[:covered_lines] %></div>
296
+ </div>
297
+ <div class="stat bg-base-300 rounded-lg">
298
+ <div class="stat-title">Missed</div>
299
+ <div class="stat-value text-error"><%= file[:missed_lines].count %></div>
300
+ </div>
301
+ </div>
302
+
303
+ <% if file[:missed_lines].any? %>
304
+ <div class="mt-4">
305
+ <h4 class="font-semibold mb-2 text-error">Uncovered Lines:</h4>
306
+ <div class="flex flex-wrap gap-2">
307
+ <% file[:missed_lines].first(20).each do |line_num| %>
308
+ <div class="badge badge-error"><%= line_num %></div>
309
+ <% end %>
310
+ <% if file[:missed_lines].count > 20 %>
311
+ <div class="badge badge-outline">+ <%= file[:missed_lines].count - 20 %> more</div>
312
+ <% end %>
313
+ </div>
314
+ </div>
315
+ <% end %>
316
+ </div>
317
+ </div>
318
+ <% end %>
319
+ </div>
320
+ </div>
321
+ </div>
322
+ <% end %>
323
+
324
+ <% else %>
325
+ <!-- Error Display -->
326
+ <div class="card bg-error text-error-content shadow-xl mb-8">
327
+ <div class="card-body">
328
+ <h2 class="card-title text-2xl">
329
+ <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
330
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
331
+ </svg>
332
+ Coverage Analysis Error
333
+ </h2>
334
+
335
+ <div class="bg-error-content/10 p-6 rounded-lg">
336
+ <p class="mb-4 text-lg">CoverageCompass encountered an error while analyzing your code coverage:</p>
337
+ <div class="mockup-code bg-base-300 text-base-content mb-6">
338
+ <pre class="px-4"><code><%= @analysis_data[:error_message] %></code></pre>
339
+ </div>
340
+
341
+ <div class="divider"></div>
342
+
343
+ <h3 class="text-lg font-bold mb-3">💡 Setup Instructions:</h3>
344
+ <div class="space-y-2">
345
+ <div class="flex items-start gap-3 p-3 bg-base-100 text-base-content rounded-lg">
346
+ <svg class="w-5 h-5 text-info mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
347
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
348
+ </svg>
349
+ <div>
350
+ <div class="font-semibold">Add SimpleCov gems to your Gemfile:</div>
351
+ <div class="mockup-code bg-base-200 text-xs mt-2">
352
+ <pre><code>gem 'simplecov', require: false
353
+ gem 'simplecov-json', require: false</code></pre>
354
+ </div>
355
+ </div>
356
+ </div>
357
+ <div class="flex items-start gap-3 p-3 bg-base-100 text-base-content rounded-lg">
358
+ <svg class="w-5 h-5 text-info mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
359
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
360
+ </svg>
361
+ <div>
362
+ <div class="font-semibold">Configure SimpleCov in your test helper:</div>
363
+ <div class="mockup-code bg-base-200 text-xs mt-2">
364
+ <pre><code>require 'simplecov'
365
+ require 'simplecov-json'
366
+
367
+ # Set the output path for the coverage reports
368
+ SimpleCov.coverage_dir('solidstats')
369
+
370
+ SimpleCov.formatter = SimpleCov::Formatter::JSONFormatter
371
+ SimpleCov.start</code></pre>
372
+ </div>
373
+ </div>
374
+ </div>
375
+ <div class="flex items-start gap-3 p-3 bg-base-100 text-base-content rounded-lg">
376
+ <svg class="w-5 h-5 text-info mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
377
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
378
+ </svg>
379
+ <div>
380
+ <div class="font-semibold">Run your tests to generate coverage:</div>
381
+ <code class="bg-base-200 px-2 py-1 rounded text-sm">bundle exec rspec</code>
382
+ </div>
383
+ </div>
384
+ </div>
385
+ </div>
386
+ </div>
387
+ </div>
388
+ <% end %>
389
+
390
+ <!-- Floating Action Button -->
391
+ <div class="fixed bottom-6 right-6 lg:hidden">
392
+ <div class="dropdown dropdown-top dropdown-end">
393
+ <div tabindex="0" role="button" class="btn btn-circle btn-primary btn-lg shadow-2xl">
394
+ <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
395
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z"></path>
396
+ </svg>
397
+ </div>
398
+ <ul tabindex="0" class="dropdown-content z-[1] menu p-2 shadow-2xl bg-base-100 rounded-box w-52 mb-2">
399
+ <li>
400
+ <%= form_with url: refresh_quality_coverage_compass_path, method: :post, local: true, class: "contents" do |form| %>
401
+ <%= form.button type: "submit", class: "gap-3 w-full justify-start bg-transparent border-none hover:bg-base-200 text-base-content p-3 rounded-lg" do %>
402
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
403
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
404
+ </svg>
405
+ Run Analysis
406
+ <% end %>
407
+ <% end %>
408
+ </li>
409
+ <li>
410
+ <a href="#top" class="gap-3">
411
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
412
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 11l5-5m0 0l5 5m-5-5v12"></path>
413
+ </svg>
414
+ Back to Top
415
+ </a>
416
+ </li>
417
+ </ul>
418
+ </div>
419
+ </div>
420
+ </div>