rails_error_dashboard 0.1.0 → 0.1.3

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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +305 -703
  3. data/app/assets/stylesheets/rails_error_dashboard/_catppuccin_mocha.scss +107 -0
  4. data/app/assets/stylesheets/rails_error_dashboard/_components.scss +625 -0
  5. data/app/assets/stylesheets/rails_error_dashboard/_layout.scss +257 -0
  6. data/app/assets/stylesheets/rails_error_dashboard/_theme_variables.scss +203 -0
  7. data/app/assets/stylesheets/rails_error_dashboard/application.css +926 -15
  8. data/app/assets/stylesheets/rails_error_dashboard/application.css.map +7 -0
  9. data/app/assets/stylesheets/rails_error_dashboard/application.scss +61 -0
  10. data/app/controllers/rails_error_dashboard/application_controller.rb +18 -0
  11. data/app/controllers/rails_error_dashboard/errors_controller.rb +140 -4
  12. data/app/helpers/rails_error_dashboard/application_helper.rb +55 -0
  13. data/app/helpers/rails_error_dashboard/backtrace_helper.rb +91 -0
  14. data/app/helpers/rails_error_dashboard/overview_helper.rb +78 -0
  15. data/app/helpers/rails_error_dashboard/user_agent_helper.rb +118 -0
  16. data/app/jobs/rails_error_dashboard/application_job.rb +19 -0
  17. data/app/jobs/rails_error_dashboard/async_error_logging_job.rb +48 -0
  18. data/app/jobs/rails_error_dashboard/baseline_alert_job.rb +263 -0
  19. data/app/jobs/rails_error_dashboard/discord_error_notification_job.rb +4 -8
  20. data/app/jobs/rails_error_dashboard/email_error_notification_job.rb +2 -1
  21. data/app/jobs/rails_error_dashboard/pagerduty_error_notification_job.rb +5 -5
  22. data/app/jobs/rails_error_dashboard/slack_error_notification_job.rb +10 -6
  23. data/app/jobs/rails_error_dashboard/webhook_error_notification_job.rb +5 -6
  24. data/app/mailers/rails_error_dashboard/application_mailer.rb +1 -1
  25. data/app/mailers/rails_error_dashboard/error_notification_mailer.rb +1 -1
  26. data/app/models/rails_error_dashboard/cascade_pattern.rb +74 -0
  27. data/app/models/rails_error_dashboard/error_baseline.rb +100 -0
  28. data/app/models/rails_error_dashboard/error_comment.rb +27 -0
  29. data/app/models/rails_error_dashboard/error_log.rb +471 -3
  30. data/app/models/rails_error_dashboard/error_occurrence.rb +49 -0
  31. data/app/views/layouts/rails_error_dashboard.html.erb +816 -178
  32. data/app/views/layouts/rails_error_dashboard_old_backup.html.erb +383 -0
  33. data/app/views/rails_error_dashboard/error_notification_mailer/error_alert.html.erb +3 -10
  34. data/app/views/rails_error_dashboard/error_notification_mailer/error_alert.text.erb +1 -2
  35. data/app/views/rails_error_dashboard/errors/_error_row.html.erb +78 -0
  36. data/app/views/rails_error_dashboard/errors/_pattern_insights.html.erb +209 -0
  37. data/app/views/rails_error_dashboard/errors/_stats.html.erb +34 -0
  38. data/app/views/rails_error_dashboard/errors/_timeline.html.erb +167 -0
  39. data/app/views/rails_error_dashboard/errors/analytics.html.erb +152 -56
  40. data/app/views/rails_error_dashboard/errors/correlation.html.erb +373 -0
  41. data/app/views/rails_error_dashboard/errors/index.html.erb +294 -138
  42. data/app/views/rails_error_dashboard/errors/overview.html.erb +253 -0
  43. data/app/views/rails_error_dashboard/errors/platform_comparison.html.erb +399 -0
  44. data/app/views/rails_error_dashboard/errors/show.html.erb +781 -65
  45. data/config/routes.rb +9 -0
  46. data/db/migrate/20251225071314_add_optimized_indexes_to_error_logs.rb +66 -0
  47. data/db/migrate/20251225074653_remove_environment_from_error_logs.rb +26 -0
  48. data/db/migrate/20251225085859_add_enhanced_metrics_to_error_logs.rb +12 -0
  49. data/db/migrate/20251225093603_add_similarity_tracking_to_error_logs.rb +9 -0
  50. data/db/migrate/20251225100236_create_error_occurrences.rb +31 -0
  51. data/db/migrate/20251225101920_create_cascade_patterns.rb +33 -0
  52. data/db/migrate/20251225102500_create_error_baselines.rb +38 -0
  53. data/db/migrate/20251226020000_add_workflow_fields_to_error_logs.rb +27 -0
  54. data/db/migrate/20251226020100_create_error_comments.rb +18 -0
  55. data/lib/generators/rails_error_dashboard/install/install_generator.rb +276 -1
  56. data/lib/generators/rails_error_dashboard/install/templates/initializer.rb +272 -37
  57. data/lib/generators/rails_error_dashboard/solid_queue/solid_queue_generator.rb +36 -0
  58. data/lib/generators/rails_error_dashboard/solid_queue/templates/queue.yml +55 -0
  59. data/lib/rails_error_dashboard/commands/batch_delete_errors.rb +1 -1
  60. data/lib/rails_error_dashboard/commands/batch_resolve_errors.rb +2 -2
  61. data/lib/rails_error_dashboard/commands/log_error.rb +272 -7
  62. data/lib/rails_error_dashboard/commands/resolve_error.rb +16 -0
  63. data/lib/rails_error_dashboard/configuration.rb +90 -5
  64. data/lib/rails_error_dashboard/error_reporter.rb +15 -7
  65. data/lib/rails_error_dashboard/logger.rb +105 -0
  66. data/lib/rails_error_dashboard/middleware/error_catcher.rb +17 -10
  67. data/lib/rails_error_dashboard/plugin.rb +6 -3
  68. data/lib/rails_error_dashboard/plugin_registry.rb +2 -2
  69. data/lib/rails_error_dashboard/plugins/audit_log_plugin.rb +0 -1
  70. data/lib/rails_error_dashboard/plugins/jira_integration_plugin.rb +3 -4
  71. data/lib/rails_error_dashboard/plugins/metrics_plugin.rb +1 -3
  72. data/lib/rails_error_dashboard/queries/analytics_stats.rb +44 -6
  73. data/lib/rails_error_dashboard/queries/baseline_stats.rb +107 -0
  74. data/lib/rails_error_dashboard/queries/co_occurring_errors.rb +86 -0
  75. data/lib/rails_error_dashboard/queries/dashboard_stats.rb +242 -2
  76. data/lib/rails_error_dashboard/queries/error_cascades.rb +74 -0
  77. data/lib/rails_error_dashboard/queries/error_correlation.rb +375 -0
  78. data/lib/rails_error_dashboard/queries/errors_list.rb +106 -10
  79. data/lib/rails_error_dashboard/queries/filter_options.rb +0 -1
  80. data/lib/rails_error_dashboard/queries/platform_comparison.rb +254 -0
  81. data/lib/rails_error_dashboard/queries/similar_errors.rb +93 -0
  82. data/lib/rails_error_dashboard/services/backtrace_parser.rb +113 -0
  83. data/lib/rails_error_dashboard/services/baseline_alert_throttler.rb +88 -0
  84. data/lib/rails_error_dashboard/services/baseline_calculator.rb +269 -0
  85. data/lib/rails_error_dashboard/services/cascade_detector.rb +95 -0
  86. data/lib/rails_error_dashboard/services/pattern_detector.rb +268 -0
  87. data/lib/rails_error_dashboard/services/similarity_calculator.rb +144 -0
  88. data/lib/rails_error_dashboard/value_objects/error_context.rb +27 -1
  89. data/lib/rails_error_dashboard/version.rb +1 -1
  90. data/lib/rails_error_dashboard.rb +57 -7
  91. metadata +69 -10
  92. data/app/models/rails_error_dashboard/application_record.rb +0 -5
  93. data/lib/rails_error_dashboard/queries/developer_insights.rb +0 -277
  94. data/lib/rails_error_dashboard/queries/errors_list_v2.rb +0 -149
  95. data/lib/tasks/rails_error_dashboard_tasks.rake +0 -4
@@ -0,0 +1,383 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Error Dashboard - Audio Intelli API</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <%= csrf_meta_tags %>
7
+ <%= csp_meta_tag %>
8
+
9
+ <!-- Apply theme immediately to prevent flash of wrong theme -->
10
+ <script>
11
+ // This runs BEFORE body renders to prevent flash
12
+ (function() {
13
+ const savedTheme = localStorage.getItem('theme');
14
+ if (savedTheme === 'dark') {
15
+ // Add to html element so we can style body
16
+ document.documentElement.setAttribute('data-theme', 'dark');
17
+ }
18
+ })();
19
+ </script>
20
+
21
+ <!-- Bootstrap CSS -->
22
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
23
+ <!-- Bootstrap Icons -->
24
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
25
+ <!-- Chart.js with date adapter -->
26
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
27
+ <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@3.0.0/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
28
+ <script src="https://cdn.jsdelivr.net/npm/chartkick@5.0.1/dist/chartkick.min.js"></script>
29
+
30
+ <!-- Turbo for real-time updates -->
31
+ <script type="module">
32
+ import * as Turbo from 'https://cdn.jsdelivr.net/npm/@hotwired/turbo@8.0.12/+esm'
33
+ </script>
34
+
35
+ <!-- Rails Error Dashboard Styles - Catppuccin Mocha Theme -->
36
+ <style>
37
+ <%= File.read(RailsErrorDashboard::Engine.root.join("app/assets/stylesheets/rails_error_dashboard/application.css")) %>
38
+ </style>
39
+ </head>
40
+
41
+ <body>
42
+ <!-- Loading Indicator -->
43
+ <div id="loading-indicator"></div>
44
+
45
+ <!-- Top Navbar -->
46
+ <nav class="navbar navbar-dark">
47
+ <div class="container-fluid">
48
+ <div class="d-flex align-items-center">
49
+ <!-- Mobile menu toggle -->
50
+ <button class="btn btn-link text-white d-md-none me-2" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu">
51
+ <i class="bi bi-list fs-4"></i>
52
+ </button>
53
+ <a class="navbar-brand fw-bold" href="<%= root_path %>">
54
+ <i class="bi bi-bug-fill"></i>
55
+ Error Dashboard
56
+ </a>
57
+ </div>
58
+ <div class="d-flex align-items-center gap-3">
59
+ <button class="theme-toggle" id="themeToggle" onclick="toggleTheme()">
60
+ <i class="bi bi-moon-fill" id="themeIcon"></i>
61
+ </button>
62
+ <div class="text-white d-none d-md-block">
63
+ <small><%= Rails.env.titleize %> Environment</small>
64
+ </div>
65
+ </div>
66
+ </div>
67
+ </nav>
68
+
69
+ <div class="container-fluid">
70
+ <div class="row">
71
+ <!-- Sidebar - Desktop -->
72
+ <nav class="col-md-2 d-none d-md-block sidebar" id="sidebarDesktop">
73
+ <div class="position-sticky pt-3">
74
+ <ul class="nav flex-column">
75
+ <li class="nav-item">
76
+ <%= link_to root_path, class: "nav-link #{request.path == root_path ? 'active' : ''}" do %>
77
+ <i class="bi bi-speedometer2"></i> Overview
78
+ <% end %>
79
+ </li>
80
+ <li class="nav-item">
81
+ <%= link_to errors_path, class: "nav-link #{request.path == errors_path ? 'active' : ''}" do %>
82
+ <i class="bi bi-list-ul"></i> All Errors
83
+ <% end %>
84
+ </li>
85
+ <li class="nav-item">
86
+ <%= link_to analytics_errors_path, class: "nav-link #{request.path == analytics_errors_path ? 'active' : ''}" do %>
87
+ <i class="bi bi-graph-up"></i> Analytics
88
+ <% end %>
89
+ </li>
90
+ <% if RailsErrorDashboard.configuration.enable_platform_comparison %>
91
+ <li class="nav-item">
92
+ <%= link_to platform_comparison_errors_path, class: "nav-link #{request.path == platform_comparison_errors_path ? 'active' : ''}" do %>
93
+ <i class="bi bi-phone"></i> Platform Health
94
+ <% end %>
95
+ </li>
96
+ <% end %>
97
+ <% if RailsErrorDashboard.configuration.enable_error_correlation %>
98
+ <li class="nav-item">
99
+ <%= link_to correlation_errors_path, class: "nav-link #{request.path == correlation_errors_path ? 'active' : ''}" do %>
100
+ <i class="bi bi-diagram-3"></i> Correlation
101
+ <% end %>
102
+ </li>
103
+ <% end %>
104
+ </ul>
105
+
106
+ <hr class="my-3">
107
+
108
+ <h6 class="sidebar-heading px-3 mt-4 mb-2 text-muted text-uppercase">
109
+ <small>Quick Filters</small>
110
+ </h6>
111
+ <ul class="nav flex-column">
112
+ <li class="nav-item">
113
+ <%= link_to errors_path(unresolved: true), class: "nav-link" do %>
114
+ <i class="bi bi-exclamation-circle text-danger"></i> Unresolved
115
+ <% end %>
116
+ </li>
117
+ <li class="nav-item">
118
+ <%= link_to errors_path(platform: 'iOS'), class: "nav-link" do %>
119
+ <i class="bi bi-phone"></i> iOS Errors
120
+ <% end %>
121
+ </li>
122
+ <li class="nav-item">
123
+ <%= link_to errors_path(platform: 'Android'), class: "nav-link" do %>
124
+ <i class="bi bi-phone"></i> Android Errors
125
+ <% end %>
126
+ </li>
127
+ </ul>
128
+ </div>
129
+ </nav>
130
+
131
+ <!-- Sidebar - Mobile (Offcanvas) -->
132
+ <div class="offcanvas offcanvas-start" tabindex="-1" id="sidebarMenu" aria-labelledby="sidebarMenuLabel">
133
+ <div class="offcanvas-header">
134
+ <h5 class="offcanvas-title" id="sidebarMenuLabel">
135
+ <i class="bi bi-bug-fill"></i> Error Dashboard
136
+ </h5>
137
+ <button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
138
+ </div>
139
+ <div class="offcanvas-body">
140
+ <ul class="nav flex-column">
141
+ <li class="nav-item">
142
+ <%= link_to root_path, class: "nav-link #{request.path == root_path ? 'active' : ''}", data: { bs_dismiss: "offcanvas" } do %>
143
+ <i class="bi bi-speedometer2"></i> Overview
144
+ <% end %>
145
+ </li>
146
+ <li class="nav-item">
147
+ <%= link_to errors_path, class: "nav-link #{request.path == errors_path ? 'active' : ''}", data: { bs_dismiss: "offcanvas" } do %>
148
+ <i class="bi bi-list-ul"></i> All Errors
149
+ <% end %>
150
+ </li>
151
+ <li class="nav-item">
152
+ <%= link_to analytics_errors_path, class: "nav-link #{request.path == analytics_errors_path ? 'active' : ''}", data: { bs_dismiss: "offcanvas" } do %>
153
+ <i class="bi bi-graph-up"></i> Analytics
154
+ <% end %>
155
+ </li>
156
+ <% if RailsErrorDashboard.configuration.enable_platform_comparison %>
157
+ <li class="nav-item">
158
+ <%= link_to platform_comparison_errors_path, class: "nav-link #{request.path == platform_comparison_errors_path ? 'active' : ''}", data: { bs_dismiss: "offcanvas" } do %>
159
+ <i class="bi bi-phone"></i> Platform Health
160
+ <% end %>
161
+ </li>
162
+ <% end %>
163
+ <% if RailsErrorDashboard.configuration.enable_error_correlation %>
164
+ <li class="nav-item">
165
+ <%= link_to correlation_errors_path, class: "nav-link #{request.path == correlation_errors_path ? 'active' : ''}", data: { bs_dismiss: "offcanvas" } do %>
166
+ <i class="bi bi-diagram-3"></i> Correlation
167
+ <% end %>
168
+ </li>
169
+ <% end %>
170
+ </ul>
171
+
172
+ <hr class="my-3">
173
+
174
+ <h6 class="sidebar-heading px-3 mt-4 mb-2 text-muted text-uppercase">
175
+ <small>Quick Filters</small>
176
+ </h6>
177
+ <ul class="nav flex-column">
178
+ <li class="nav-item">
179
+ <%= link_to errors_path(unresolved: true), class: "nav-link", data: { bs_dismiss: "offcanvas" } do %>
180
+ <i class="bi bi-exclamation-circle text-danger"></i> Unresolved
181
+ <% end %>
182
+ </li>
183
+ <li class="nav-item">
184
+ <%= link_to errors_path(platform: 'iOS'), class: "nav-link", data: { bs_dismiss: "offcanvas" } do %>
185
+ <i class="bi bi-phone"></i> iOS Errors
186
+ <% end %>
187
+ </li>
188
+ <li class="nav-item">
189
+ <%= link_to errors_path(platform: 'Android'), class: "nav-link", data: { bs_dismiss: "offcanvas" } do %>
190
+ <i class="bi bi-phone"></i> Android Errors
191
+ <% end %>
192
+ </li>
193
+ </ul>
194
+
195
+ <hr class="my-3">
196
+
197
+ <div class="px-3">
198
+ <small class="text-muted">Environment</small>
199
+ <div class="fw-bold"><%= Rails.env.titleize %></div>
200
+ </div>
201
+ </div>
202
+ </div>
203
+
204
+ <!-- Main content -->
205
+ <main class="col-md-10 ms-sm-auto px-md-4">
206
+ <%= yield %>
207
+ </main>
208
+ </div>
209
+ </div>
210
+
211
+ <!-- Bootstrap JS -->
212
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
213
+
214
+ <!-- Theme Toggle Script -->
215
+ <script>
216
+ // Catppuccin Mocha Chart Colors
217
+ function getCatppuccinChartConfig(isDark) {
218
+ if (!isDark) {
219
+ // Light theme - keep existing for now
220
+ return {
221
+ textColor: '#1F2937',
222
+ gridColor: 'rgba(0, 0, 0, 0.1)',
223
+ tooltipBg: 'rgba(0, 0, 0, 0.8)',
224
+ colors: {
225
+ red: '#EF4444',
226
+ green: '#10B981',
227
+ yellow: '#F59E0B',
228
+ blue: '#3B82F6',
229
+ purple: '#8B5CF6',
230
+ pink: '#EC4899',
231
+ teal: '#14B8A6',
232
+ orange: '#F97316',
233
+ sapphire: '#3B82F6'
234
+ }
235
+ };
236
+ }
237
+
238
+ // Dark theme - Catppuccin Mocha
239
+ return {
240
+ textColor: '#cdd6f4', // ctp-text
241
+ gridColor: 'rgba(88, 91, 112, 0.3)', // ctp-surface2 with opacity
242
+ tooltipBg: '#313244', // ctp-surface0
243
+ colors: {
244
+ red: '#f38ba8', // ctp-red
245
+ green: '#a6e3a1', // ctp-green
246
+ yellow: '#f9e2af', // ctp-yellow
247
+ blue: '#89b4fa', // ctp-blue
248
+ purple: '#cba6f7', // ctp-mauve
249
+ pink: '#f5c2e7', // ctp-pink
250
+ teal: '#94e2d5', // ctp-teal
251
+ orange: '#fab387', // ctp-peach
252
+ sapphire: '#74c7ec' // ctp-sapphire
253
+ }
254
+ };
255
+ }
256
+
257
+ // Get platform-specific color
258
+ function getPlatformColor(platform, isDark) {
259
+ const config = getCatppuccinChartConfig(isDark);
260
+ const platformMap = {
261
+ 'ios': isDark ? config.textColor : '#000000',
262
+ 'android': config.colors.green,
263
+ 'web': config.colors.blue,
264
+ 'api': config.colors.sapphire
265
+ };
266
+ return platformMap[platform.toLowerCase()] || config.colors.purple;
267
+ }
268
+
269
+ // Update Chart.js defaults for dark/light theme
270
+ function updateChartTheme(isDark) {
271
+ if (typeof Chart !== 'undefined') {
272
+ const config = getCatppuccinChartConfig(isDark);
273
+
274
+ Chart.defaults.color = config.textColor;
275
+ Chart.defaults.borderColor = config.gridColor;
276
+ Chart.defaults.scale.grid.color = config.gridColor;
277
+ Chart.defaults.scale.ticks.color = config.textColor;
278
+ Chart.defaults.plugins.legend.labels.color = config.textColor;
279
+ Chart.defaults.plugins.tooltip.backgroundColor = config.tooltipBg;
280
+ Chart.defaults.plugins.tooltip.titleColor = config.textColor;
281
+ Chart.defaults.plugins.tooltip.bodyColor = config.textColor;
282
+ Chart.defaults.plugins.tooltip.borderColor = config.gridColor;
283
+ }
284
+ }
285
+
286
+ // Load theme from localStorage on page load
287
+ document.addEventListener('DOMContentLoaded', function() {
288
+ console.log('📄 DOMContentLoaded - Loading theme from localStorage');
289
+
290
+ const savedTheme = localStorage.getItem('theme') || 'light';
291
+ const isDark = savedTheme === 'dark';
292
+
293
+ console.log('Saved theme:', savedTheme, '| isDark:', isDark);
294
+
295
+ if (isDark) {
296
+ document.body.classList.add('dark-mode');
297
+ document.documentElement.setAttribute('data-theme', 'dark');
298
+ console.log('✅ Applied dark theme (body.dark-mode + html[data-theme=dark])');
299
+ updateThemeIcon(true);
300
+ } else {
301
+ // Ensure light mode is clean
302
+ document.body.classList.remove('dark-mode');
303
+ document.documentElement.removeAttribute('data-theme');
304
+ console.log('✅ Applied light theme (removed classes)');
305
+ updateThemeIcon(false);
306
+ }
307
+
308
+ // Update Chart.js theme
309
+ updateChartTheme(isDark);
310
+ console.log('📊 Chart.js theme updated');
311
+ });
312
+
313
+ function toggleTheme() {
314
+ try {
315
+ console.log('🎨 Toggle theme clicked');
316
+
317
+ const body = document.body;
318
+ const isDark = body.classList.toggle('dark-mode');
319
+
320
+ console.log('Dark mode:', isDark);
321
+
322
+ // Sync with html data attribute
323
+ if (isDark) {
324
+ document.documentElement.setAttribute('data-theme', 'dark');
325
+ console.log('✅ Set data-theme=dark');
326
+ } else {
327
+ document.documentElement.removeAttribute('data-theme');
328
+ console.log('✅ Removed data-theme');
329
+ }
330
+
331
+ // Save preference
332
+ localStorage.setItem('theme', isDark ? 'dark' : 'light');
333
+ console.log('💾 Saved to localStorage:', isDark ? 'dark' : 'light');
334
+
335
+ // Update icon
336
+ updateThemeIcon(isDark);
337
+ console.log('🌙 Updated icon');
338
+
339
+ // Update Chart.js theme
340
+ updateChartTheme(isDark);
341
+ console.log('📊 Updated Chart.js');
342
+
343
+ // Reload page to apply chart theme (Chart.js requires re-render)
344
+ console.log('🔄 Reloading page...');
345
+ setTimeout(() => location.reload(), 100);
346
+ } catch (error) {
347
+ console.error('❌ Error in toggleTheme:', error);
348
+ alert('Error toggling theme: ' + error.message);
349
+ }
350
+ }
351
+
352
+ function updateThemeIcon(isDark) {
353
+ const icon = document.getElementById('themeIcon');
354
+ if (isDark) {
355
+ icon.className = 'bi bi-sun-fill';
356
+ } else {
357
+ icon.className = 'bi bi-moon-fill';
358
+ }
359
+ }
360
+
361
+ // Loading indicator for form submissions and link clicks
362
+ const loadingIndicator = document.getElementById('loading-indicator');
363
+
364
+ // Show loading on form submit
365
+ document.addEventListener('submit', function() {
366
+ loadingIndicator.classList.add('active');
367
+ });
368
+
369
+ // Show loading on link clicks (except anchors)
370
+ document.addEventListener('click', function(e) {
371
+ const link = e.target.closest('a');
372
+ if (link && link.href && !link.href.startsWith('#') && !link.target) {
373
+ loadingIndicator.classList.add('active');
374
+ }
375
+ });
376
+
377
+ // Hide loading when page loads
378
+ window.addEventListener('load', function() {
379
+ loadingIndicator.classList.remove('active');
380
+ });
381
+ </script>
382
+ </body>
383
+ </html>
@@ -136,13 +136,6 @@
136
136
  <div class="label">Error Type:</div>
137
137
  <div class="value"><code><%= @error_log.error_type %></code></div>
138
138
 
139
- <div class="label">Environment:</div>
140
- <div class="value">
141
- <span class="badge badge-<%= @error_log.environment %>">
142
- <%= @error_log.environment.upcase %>
143
- </span>
144
- </div>
145
-
146
139
  <% if @error_log.platform.present? %>
147
140
  <div class="label">Platform:</div>
148
141
  <div class="value">
@@ -155,9 +148,9 @@
155
148
  <div class="label">Occurred At:</div>
156
149
  <div class="value"><%= @error_log.occurred_at.strftime('%B %d, %Y at %I:%M %p %Z') %></div>
157
150
 
158
- <% if @error_log.user.present? %>
159
- <div class="label">User:</div>
160
- <div class="value"><%= @error_log.user.email %></div>
151
+ <% if @error_log.user_id.present? %>
152
+ <div class="label">User ID:</div>
153
+ <div class="value"><%= @error_log.user_id %></div>
161
154
  <% end %>
162
155
 
163
156
  <% if @error_log.ip_address.present? %>
@@ -3,10 +3,9 @@
3
3
  ==========================================
4
4
 
5
5
  Error Type: <%= @error_log.error_type %>
6
- Environment: <%= @error_log.environment.upcase %>
7
6
  <% if @error_log.platform.present? %>Platform: <%= @error_log.platform %><% end %>
8
7
  Occurred At: <%= @error_log.occurred_at.strftime('%B %d, %Y at %I:%M %p %Z') %>
9
- <% if @error_log.user.present? %>User: <%= @error_log.user.email %><% end %>
8
+ <% if @error_log.user_id.present? %>User ID: <%= @error_log.user_id %><% end %>
10
9
  <% if @error_log.ip_address.present? %>IP Address: <%= @error_log.ip_address %><% end %>
11
10
  <% if @error_log.request_url.present? %>Request URL: <%= @error_log.request_url %><% end %>
12
11
 
@@ -0,0 +1,78 @@
1
+ <tr id="error_<%= error.id %>">
2
+ <td onclick="event.stopPropagation();">
3
+ <input type="checkbox" class="error-checkbox form-check-input" value="<%= error.id %>" data-error-id="<%= error.id %>">
4
+ </td>
5
+ <td onclick="window.location='<%= error_path(error) %>';">
6
+ <% severity = error.severity %>
7
+ <% if severity == :critical %>
8
+ <span class="badge bg-danger" data-bs-toggle="tooltip" title="Critical severity - requires immediate attention">CRITICAL</span>
9
+ <% elsif severity == :high %>
10
+ <span class="badge bg-warning text-dark" data-bs-toggle="tooltip" title="High severity - should be fixed soon">HIGH</span>
11
+ <% elsif severity == :medium %>
12
+ <span class="badge bg-info text-dark" data-bs-toggle="tooltip" title="Medium severity - fix when possible">MEDIUM</span>
13
+ <% else %>
14
+ <span class="badge bg-secondary" data-bs-toggle="tooltip" title="Low severity - minor issue">LOW</span>
15
+ <% end %>
16
+ <% if error.respond_to?(:priority_score) && error.priority_score %>
17
+ <br><small class="text-muted" data-bs-toggle="tooltip" title="Priority score (0-100) based on severity, frequency, recency, and user impact">P<%= error.priority_score %></small>
18
+ <% end %>
19
+ </td>
20
+ <td onclick="window.location='<%= error_path(error) %>';">
21
+ <code class="text-danger" data-bs-toggle="tooltip" title="<%= error.error_type %>"><%= error.error_type.split('::').last %></code>
22
+ <% if error.respond_to?(:app_version) && error.app_version.present? %>
23
+ <br><small class="badge bg-light text-dark" data-bs-toggle="tooltip" title="App version when error occurred">v<%= error.app_version %></small>
24
+ <% end %>
25
+ </td>
26
+ <td onclick="window.location='<%= error_path(error) %>';">
27
+ <div class="text-truncate" style="max-width: 300px;" title="<%= error.message %>">
28
+ <%= error.message %>
29
+ </div>
30
+ </td>
31
+ <td onclick="window.location='<%= error_path(error) %>';">
32
+ <span class="badge bg-primary"><%= error.occurrence_count %>x</span>
33
+ </td>
34
+ <td onclick="window.location='<%= error_path(error) %>';">
35
+ <small>
36
+ <strong>First:</strong> <%= error.first_seen_at&.strftime("%m/%d %I:%M%p") || 'N/A' %><br>
37
+ <strong>Last:</strong> <%= error.last_seen_at&.strftime("%m/%d %I:%M%p") || 'N/A' %>
38
+ </small>
39
+ </td>
40
+ <% if local_assigns[:show_platform] %>
41
+ <td onclick="window.location='<%= error_path(error) %>';">
42
+ <% if error.platform == 'iOS' %>
43
+ <span class="badge badge-ios">iOS</span>
44
+ <% elsif error.platform == 'Android' %>
45
+ <span class="badge badge-android">Android</span>
46
+ <% elsif error.platform == 'Web' %>
47
+ <span class="badge badge-web">Web</span>
48
+ <% else %>
49
+ <span class="badge badge-api"><%= error.platform || 'API' %></span>
50
+ <% end %>
51
+ </td>
52
+ <% end %>
53
+ <td onclick="window.location='<%= error_path(error) %>';">
54
+ <% if error.user_id %>
55
+ <small data-bs-toggle="tooltip" title="User affected by this error">User #<%= error.user_id %></small>
56
+ <% if error.respond_to?(:user_impact_percentage) %>
57
+ <% impact = error.user_impact_percentage %>
58
+ <% if impact > 0 %>
59
+ <br><small class="badge bg-warning text-dark" data-bs-toggle="tooltip" title="Percentage of users affected by this error type"><%= impact %>% impact</small>
60
+ <% end %>
61
+ <% end %>
62
+ <% else %>
63
+ <small class="text-muted" data-bs-toggle="tooltip" title="No specific user affected">-</small>
64
+ <% end %>
65
+ </td>
66
+ <td onclick="window.location='<%= error_path(error) %>';">
67
+ <% if error.resolved? %>
68
+ <i class="bi bi-check-circle-fill text-success" title="Resolved"></i>
69
+ <% else %>
70
+ <i class="bi bi-exclamation-circle-fill text-danger" title="Unresolved"></i>
71
+ <% end %>
72
+ </td>
73
+ <td onclick="event.stopPropagation();">
74
+ <%= link_to error_path(error), class: "btn btn-sm btn-outline-primary" do %>
75
+ <i class="bi bi-eye"></i>
76
+ <% end %>
77
+ </td>
78
+ </tr>