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.
- checksums.yaml +4 -4
- data/README.md +305 -703
- data/app/assets/stylesheets/rails_error_dashboard/_catppuccin_mocha.scss +107 -0
- data/app/assets/stylesheets/rails_error_dashboard/_components.scss +625 -0
- data/app/assets/stylesheets/rails_error_dashboard/_layout.scss +257 -0
- data/app/assets/stylesheets/rails_error_dashboard/_theme_variables.scss +203 -0
- data/app/assets/stylesheets/rails_error_dashboard/application.css +926 -15
- data/app/assets/stylesheets/rails_error_dashboard/application.css.map +7 -0
- data/app/assets/stylesheets/rails_error_dashboard/application.scss +61 -0
- data/app/controllers/rails_error_dashboard/application_controller.rb +18 -0
- data/app/controllers/rails_error_dashboard/errors_controller.rb +140 -4
- data/app/helpers/rails_error_dashboard/application_helper.rb +55 -0
- data/app/helpers/rails_error_dashboard/backtrace_helper.rb +91 -0
- data/app/helpers/rails_error_dashboard/overview_helper.rb +78 -0
- data/app/helpers/rails_error_dashboard/user_agent_helper.rb +118 -0
- data/app/jobs/rails_error_dashboard/application_job.rb +19 -0
- data/app/jobs/rails_error_dashboard/async_error_logging_job.rb +48 -0
- data/app/jobs/rails_error_dashboard/baseline_alert_job.rb +263 -0
- data/app/jobs/rails_error_dashboard/discord_error_notification_job.rb +4 -8
- data/app/jobs/rails_error_dashboard/email_error_notification_job.rb +2 -1
- data/app/jobs/rails_error_dashboard/pagerduty_error_notification_job.rb +5 -5
- data/app/jobs/rails_error_dashboard/slack_error_notification_job.rb +10 -6
- data/app/jobs/rails_error_dashboard/webhook_error_notification_job.rb +5 -6
- data/app/mailers/rails_error_dashboard/application_mailer.rb +1 -1
- data/app/mailers/rails_error_dashboard/error_notification_mailer.rb +1 -1
- data/app/models/rails_error_dashboard/cascade_pattern.rb +74 -0
- data/app/models/rails_error_dashboard/error_baseline.rb +100 -0
- data/app/models/rails_error_dashboard/error_comment.rb +27 -0
- data/app/models/rails_error_dashboard/error_log.rb +471 -3
- data/app/models/rails_error_dashboard/error_occurrence.rb +49 -0
- data/app/views/layouts/rails_error_dashboard.html.erb +816 -178
- data/app/views/layouts/rails_error_dashboard_old_backup.html.erb +383 -0
- data/app/views/rails_error_dashboard/error_notification_mailer/error_alert.html.erb +3 -10
- data/app/views/rails_error_dashboard/error_notification_mailer/error_alert.text.erb +1 -2
- data/app/views/rails_error_dashboard/errors/_error_row.html.erb +78 -0
- data/app/views/rails_error_dashboard/errors/_pattern_insights.html.erb +209 -0
- data/app/views/rails_error_dashboard/errors/_stats.html.erb +34 -0
- data/app/views/rails_error_dashboard/errors/_timeline.html.erb +167 -0
- data/app/views/rails_error_dashboard/errors/analytics.html.erb +152 -56
- data/app/views/rails_error_dashboard/errors/correlation.html.erb +373 -0
- data/app/views/rails_error_dashboard/errors/index.html.erb +294 -138
- data/app/views/rails_error_dashboard/errors/overview.html.erb +253 -0
- data/app/views/rails_error_dashboard/errors/platform_comparison.html.erb +399 -0
- data/app/views/rails_error_dashboard/errors/show.html.erb +781 -65
- data/config/routes.rb +9 -0
- data/db/migrate/20251225071314_add_optimized_indexes_to_error_logs.rb +66 -0
- data/db/migrate/20251225074653_remove_environment_from_error_logs.rb +26 -0
- data/db/migrate/20251225085859_add_enhanced_metrics_to_error_logs.rb +12 -0
- data/db/migrate/20251225093603_add_similarity_tracking_to_error_logs.rb +9 -0
- data/db/migrate/20251225100236_create_error_occurrences.rb +31 -0
- data/db/migrate/20251225101920_create_cascade_patterns.rb +33 -0
- data/db/migrate/20251225102500_create_error_baselines.rb +38 -0
- data/db/migrate/20251226020000_add_workflow_fields_to_error_logs.rb +27 -0
- data/db/migrate/20251226020100_create_error_comments.rb +18 -0
- data/lib/generators/rails_error_dashboard/install/install_generator.rb +276 -1
- data/lib/generators/rails_error_dashboard/install/templates/initializer.rb +272 -37
- data/lib/generators/rails_error_dashboard/solid_queue/solid_queue_generator.rb +36 -0
- data/lib/generators/rails_error_dashboard/solid_queue/templates/queue.yml +55 -0
- data/lib/rails_error_dashboard/commands/batch_delete_errors.rb +1 -1
- data/lib/rails_error_dashboard/commands/batch_resolve_errors.rb +2 -2
- data/lib/rails_error_dashboard/commands/log_error.rb +272 -7
- data/lib/rails_error_dashboard/commands/resolve_error.rb +16 -0
- data/lib/rails_error_dashboard/configuration.rb +90 -5
- data/lib/rails_error_dashboard/error_reporter.rb +15 -7
- data/lib/rails_error_dashboard/logger.rb +105 -0
- data/lib/rails_error_dashboard/middleware/error_catcher.rb +17 -10
- data/lib/rails_error_dashboard/plugin.rb +6 -3
- data/lib/rails_error_dashboard/plugin_registry.rb +2 -2
- data/lib/rails_error_dashboard/plugins/audit_log_plugin.rb +0 -1
- data/lib/rails_error_dashboard/plugins/jira_integration_plugin.rb +3 -4
- data/lib/rails_error_dashboard/plugins/metrics_plugin.rb +1 -3
- data/lib/rails_error_dashboard/queries/analytics_stats.rb +44 -6
- data/lib/rails_error_dashboard/queries/baseline_stats.rb +107 -0
- data/lib/rails_error_dashboard/queries/co_occurring_errors.rb +86 -0
- data/lib/rails_error_dashboard/queries/dashboard_stats.rb +242 -2
- data/lib/rails_error_dashboard/queries/error_cascades.rb +74 -0
- data/lib/rails_error_dashboard/queries/error_correlation.rb +375 -0
- data/lib/rails_error_dashboard/queries/errors_list.rb +106 -10
- data/lib/rails_error_dashboard/queries/filter_options.rb +0 -1
- data/lib/rails_error_dashboard/queries/platform_comparison.rb +254 -0
- data/lib/rails_error_dashboard/queries/similar_errors.rb +93 -0
- data/lib/rails_error_dashboard/services/backtrace_parser.rb +113 -0
- data/lib/rails_error_dashboard/services/baseline_alert_throttler.rb +88 -0
- data/lib/rails_error_dashboard/services/baseline_calculator.rb +269 -0
- data/lib/rails_error_dashboard/services/cascade_detector.rb +95 -0
- data/lib/rails_error_dashboard/services/pattern_detector.rb +268 -0
- data/lib/rails_error_dashboard/services/similarity_calculator.rb +144 -0
- data/lib/rails_error_dashboard/value_objects/error_context.rb +27 -1
- data/lib/rails_error_dashboard/version.rb +1 -1
- data/lib/rails_error_dashboard.rb +57 -7
- metadata +69 -10
- data/app/models/rails_error_dashboard/application_record.rb +0 -5
- data/lib/rails_error_dashboard/queries/developer_insights.rb +0 -277
- data/lib/rails_error_dashboard/queries/errors_list_v2.rb +0 -149
- 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.
|
|
159
|
-
<div class="label">User:</div>
|
|
160
|
-
<div class="value"><%= @error_log.
|
|
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.
|
|
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>
|