rails_error_dashboard 0.1.1 → 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 +66 -21
- 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/errors_controller.rb +94 -1
- data/app/helpers/rails_error_dashboard/application_helper.rb +42 -4
- 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/models/rails_error_dashboard/error_comment.rb +27 -0
- data/app/models/rails_error_dashboard/error_log.rb +145 -0
- data/app/views/layouts/rails_error_dashboard.html.erb +796 -299
- data/app/views/layouts/rails_error_dashboard_old_backup.html.erb +383 -0
- data/app/views/rails_error_dashboard/errors/_error_row.html.erb +2 -0
- data/app/views/rails_error_dashboard/errors/_pattern_insights.html.erb +4 -4
- data/app/views/rails_error_dashboard/errors/_timeline.html.erb +167 -0
- data/app/views/rails_error_dashboard/errors/analytics.html.erb +138 -22
- data/app/views/rails_error_dashboard/errors/index.html.erb +83 -4
- data/app/views/rails_error_dashboard/errors/overview.html.erb +253 -0
- data/app/views/rails_error_dashboard/errors/platform_comparison.html.erb +29 -18
- data/app/views/rails_error_dashboard/errors/show.html.erb +353 -54
- data/config/routes.rb +7 -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 +8 -2
- data/lib/generators/rails_error_dashboard/install/templates/initializer.rb +21 -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 +47 -9
- data/lib/rails_error_dashboard/commands/resolve_error.rb +1 -1
- data/lib/rails_error_dashboard/configuration.rb +8 -0
- data/lib/rails_error_dashboard/error_reporter.rb +4 -4
- data/lib/rails_error_dashboard/logger.rb +105 -0
- data/lib/rails_error_dashboard/middleware/error_catcher.rb +2 -2
- data/lib/rails_error_dashboard/plugin.rb +3 -3
- data/lib/rails_error_dashboard/plugin_registry.rb +2 -2
- data/lib/rails_error_dashboard/plugins/jira_integration_plugin.rb +1 -1
- data/lib/rails_error_dashboard/plugins/metrics_plugin.rb +1 -1
- data/lib/rails_error_dashboard/queries/dashboard_stats.rb +109 -1
- data/lib/rails_error_dashboard/queries/errors_list.rb +61 -6
- data/lib/rails_error_dashboard/services/backtrace_parser.rb +113 -0
- data/lib/rails_error_dashboard/version.rb +1 -1
- data/lib/rails_error_dashboard.rb +2 -0
- metadata +18 -2
- data/lib/tasks/rails_error_dashboard_tasks.rake +0 -4
|
@@ -1,351 +1,750 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
|
-
<title>Error Dashboard
|
|
4
|
+
<title>Error Dashboard</title>
|
|
5
5
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
6
|
<%= csrf_meta_tags %>
|
|
7
7
|
<%= csp_meta_tag %>
|
|
8
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
9
|
<!-- Bootstrap CSS -->
|
|
22
10
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
23
11
|
<!-- Bootstrap Icons -->
|
|
24
12
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
|
|
25
|
-
|
|
13
|
+
|
|
14
|
+
<!-- Chart.js -->
|
|
26
15
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
|
|
27
16
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@3.0.0/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
|
|
28
17
|
<script src="https://cdn.jsdelivr.net/npm/chartkick@5.0.1/dist/chartkick.min.js"></script>
|
|
29
18
|
|
|
30
|
-
<!--
|
|
31
|
-
<script type="module">
|
|
32
|
-
import * as Turbo from 'https://cdn.jsdelivr.net/npm/@hotwired/turbo@8.0.12/+esm'
|
|
33
|
-
</script>
|
|
34
|
-
|
|
19
|
+
<!-- Catppuccin Mocha Theme - Pure CSS -->
|
|
35
20
|
<style>
|
|
21
|
+
/* Catppuccin Mocha Color Palette */
|
|
36
22
|
:root {
|
|
37
|
-
--
|
|
38
|
-
--
|
|
39
|
-
--
|
|
40
|
-
--
|
|
41
|
-
--
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
23
|
+
--ctp-rosewater: #f5e0dc;
|
|
24
|
+
--ctp-flamingo: #f2cdcd;
|
|
25
|
+
--ctp-pink: #f5c2e7;
|
|
26
|
+
--ctp-mauve: #cba6f7;
|
|
27
|
+
--ctp-red: #f38ba8;
|
|
28
|
+
--ctp-maroon: #eba0ac;
|
|
29
|
+
--ctp-peach: #fab387;
|
|
30
|
+
--ctp-yellow: #f9e2af;
|
|
31
|
+
--ctp-green: #a6e3a1;
|
|
32
|
+
--ctp-teal: #94e2d5;
|
|
33
|
+
--ctp-sky: #89dceb;
|
|
34
|
+
--ctp-sapphire: #74c7ec;
|
|
35
|
+
--ctp-blue: #89b4fa;
|
|
36
|
+
--ctp-lavender: #b4befe;
|
|
37
|
+
--ctp-text: #cdd6f4;
|
|
38
|
+
--ctp-subtext1: #bac2de;
|
|
39
|
+
--ctp-subtext0: #a6adc8;
|
|
40
|
+
--ctp-overlay2: #9399b2;
|
|
41
|
+
--ctp-overlay1: #7f849c;
|
|
42
|
+
--ctp-overlay0: #6c7086;
|
|
43
|
+
--ctp-surface2: #585b70;
|
|
44
|
+
--ctp-surface1: #45475a;
|
|
45
|
+
--ctp-surface0: #313244;
|
|
46
|
+
--ctp-base: #1e1e2e;
|
|
47
|
+
--ctp-mantle: #181825;
|
|
48
|
+
--ctp-crust: #11111b;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Light Theme (Default) */
|
|
45
52
|
body {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
--sidebar-bg: #FFFFFF;
|
|
50
|
-
--sidebar-hover: #F9FAFB;
|
|
51
|
-
--border-color: #E5E7EB;
|
|
52
|
-
--table-hover: #F9FAFB;
|
|
53
|
-
--nav-active-bg: #EDE9FE;
|
|
54
|
-
background-color: var(--bg-color);
|
|
55
|
-
color: var(--text-color);
|
|
56
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
57
|
-
transition: background-color 0.3s ease, color 0.3s ease;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/* Dark mode - support both class and data attribute for immediate loading */
|
|
61
|
-
body.dark-mode,
|
|
62
|
-
html[data-theme="dark"] body {
|
|
63
|
-
--bg-color: #1A1B26;
|
|
64
|
-
--text-color: #E4E5E9;
|
|
65
|
-
--card-bg: #24283B;
|
|
66
|
-
--sidebar-bg: #1F2130;
|
|
67
|
-
--sidebar-hover: #2A2D3E;
|
|
68
|
-
--border-color: #414868;
|
|
69
|
-
--table-hover: #2A2D3E;
|
|
70
|
-
--nav-active-bg: #2A2D3E;
|
|
53
|
+
background-color: #f3f4f6;
|
|
54
|
+
color: #1f2937;
|
|
55
|
+
transition: background-color 0.3s, color 0.3s;
|
|
71
56
|
}
|
|
72
57
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
color: var(--
|
|
76
|
-
|
|
58
|
+
/* Dark Theme */
|
|
59
|
+
body.dark-mode {
|
|
60
|
+
background-color: var(--ctp-base);
|
|
61
|
+
color: var(--ctp-text);
|
|
77
62
|
}
|
|
78
63
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
64
|
+
/* Navbar */
|
|
65
|
+
.navbar {
|
|
66
|
+
background: linear-gradient(135deg, #8B5CF6, #6D28D9) !important;
|
|
67
|
+
color: white !important;
|
|
68
|
+
}
|
|
69
|
+
.navbar * {
|
|
70
|
+
color: white !important;
|
|
71
|
+
}
|
|
72
|
+
.navbar-brand {
|
|
73
|
+
font-weight: bold;
|
|
83
74
|
}
|
|
84
75
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
76
|
+
/* Theme Toggle Button */
|
|
77
|
+
.theme-toggle {
|
|
78
|
+
cursor: pointer;
|
|
79
|
+
padding: 0.5rem 1rem;
|
|
80
|
+
border-radius: 0.5rem;
|
|
81
|
+
background-color: rgba(255, 255, 255, 0.1);
|
|
82
|
+
color: white;
|
|
83
|
+
border: none;
|
|
84
|
+
transition: background-color 0.2s;
|
|
85
|
+
}
|
|
86
|
+
.theme-toggle:hover {
|
|
87
|
+
background-color: rgba(255, 255, 255, 0.2);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/* Sidebar */
|
|
90
91
|
.sidebar {
|
|
91
|
-
background:
|
|
92
|
+
background: white;
|
|
92
93
|
min-height: calc(100vh - 56px);
|
|
93
|
-
box-shadow: 2px 0 4px rgba(0,0,0,0.05);
|
|
94
|
+
box-shadow: 2px 0 4px rgba(0, 0, 0, 0.05);
|
|
95
|
+
}
|
|
96
|
+
body.dark-mode .sidebar {
|
|
97
|
+
background: var(--ctp-mantle);
|
|
94
98
|
}
|
|
95
|
-
|
|
96
99
|
.sidebar .nav-link {
|
|
97
|
-
color:
|
|
100
|
+
color: #1f2937;
|
|
98
101
|
padding: 0.75rem 1.5rem;
|
|
99
102
|
border-left: 3px solid transparent;
|
|
100
103
|
transition: all 0.2s;
|
|
101
104
|
}
|
|
102
|
-
|
|
105
|
+
body.dark-mode .sidebar .nav-link {
|
|
106
|
+
color: var(--ctp-text);
|
|
107
|
+
}
|
|
103
108
|
.sidebar .nav-link:hover {
|
|
104
|
-
background-color:
|
|
105
|
-
color:
|
|
106
|
-
border-left-color:
|
|
109
|
+
background-color: #f3f4f6;
|
|
110
|
+
color: #8B5CF6;
|
|
111
|
+
border-left-color: #8B5CF6;
|
|
112
|
+
}
|
|
113
|
+
body.dark-mode .sidebar .nav-link:hover {
|
|
114
|
+
background-color: var(--ctp-surface0);
|
|
115
|
+
color: var(--ctp-mauve);
|
|
116
|
+
border-left-color: var(--ctp-mauve);
|
|
107
117
|
}
|
|
108
|
-
|
|
109
118
|
.sidebar .nav-link.active {
|
|
110
|
-
background-color:
|
|
111
|
-
color:
|
|
112
|
-
border-left-color:
|
|
119
|
+
background-color: #f3f4f6;
|
|
120
|
+
color: #8B5CF6;
|
|
121
|
+
border-left-color: #8B5CF6;
|
|
113
122
|
font-weight: 600;
|
|
114
123
|
}
|
|
124
|
+
body.dark-mode .sidebar .nav-link.active {
|
|
125
|
+
background-color: var(--ctp-surface0);
|
|
126
|
+
color: var(--ctp-mauve);
|
|
127
|
+
border-left-color: var(--ctp-mauve);
|
|
128
|
+
}
|
|
115
129
|
|
|
130
|
+
/* Cards */
|
|
131
|
+
.card {
|
|
132
|
+
background: white;
|
|
133
|
+
border: 1px solid #e5e7eb;
|
|
134
|
+
transition: background-color 0.3s, border-color 0.3s;
|
|
135
|
+
}
|
|
136
|
+
body.dark-mode .card {
|
|
137
|
+
background: var(--ctp-surface0);
|
|
138
|
+
border-color: var(--ctp-surface2);
|
|
139
|
+
color: var(--ctp-text);
|
|
140
|
+
}
|
|
141
|
+
body.dark-mode .card-header {
|
|
142
|
+
background-color: var(--ctp-surface1);
|
|
143
|
+
border-color: var(--ctp-surface2);
|
|
144
|
+
color: var(--ctp-text);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* Tables */
|
|
148
|
+
body.dark-mode .table {
|
|
149
|
+
color: var(--ctp-text);
|
|
150
|
+
}
|
|
151
|
+
body.dark-mode .table-hover tbody tr:hover {
|
|
152
|
+
background-color: var(--ctp-surface1);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* Badges - Platform Colors */
|
|
156
|
+
.badge-ios {
|
|
157
|
+
background-color: #000;
|
|
158
|
+
color: white;
|
|
159
|
+
}
|
|
160
|
+
body.dark-mode .badge-ios {
|
|
161
|
+
background-color: var(--ctp-overlay0);
|
|
162
|
+
color: var(--ctp-text);
|
|
163
|
+
border: 1px solid var(--ctp-surface2);
|
|
164
|
+
}
|
|
165
|
+
.badge-android {
|
|
166
|
+
background-color: #3DDC84;
|
|
167
|
+
color: white;
|
|
168
|
+
}
|
|
169
|
+
body.dark-mode .badge-android {
|
|
170
|
+
background-color: rgba(166, 227, 161, 0.2);
|
|
171
|
+
color: var(--ctp-green);
|
|
172
|
+
border: 1px solid var(--ctp-green);
|
|
173
|
+
}
|
|
174
|
+
.badge-web {
|
|
175
|
+
background-color: #3B82F6;
|
|
176
|
+
color: white;
|
|
177
|
+
}
|
|
178
|
+
body.dark-mode .badge-web {
|
|
179
|
+
background-color: rgba(137, 180, 250, 0.2);
|
|
180
|
+
color: var(--ctp-blue);
|
|
181
|
+
border: 1px solid var(--ctp-blue);
|
|
182
|
+
}
|
|
183
|
+
.badge-api {
|
|
184
|
+
background-color: #8B5CF6;
|
|
185
|
+
color: white;
|
|
186
|
+
}
|
|
187
|
+
body.dark-mode .badge-api {
|
|
188
|
+
background-color: rgba(116, 199, 236, 0.2);
|
|
189
|
+
color: var(--ctp-sapphire);
|
|
190
|
+
border: 1px solid var(--ctp-sapphire);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* Forms */
|
|
194
|
+
body.dark-mode .form-control,
|
|
195
|
+
body.dark-mode .form-select {
|
|
196
|
+
background-color: var(--ctp-surface0);
|
|
197
|
+
border-color: var(--ctp-surface2);
|
|
198
|
+
color: var(--ctp-text);
|
|
199
|
+
}
|
|
200
|
+
body.dark-mode .form-control:focus,
|
|
201
|
+
body.dark-mode .form-select:focus {
|
|
202
|
+
background-color: var(--ctp-surface1);
|
|
203
|
+
border-color: var(--ctp-mauve);
|
|
204
|
+
color: var(--ctp-text);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* Code Blocks */
|
|
208
|
+
.code-block,
|
|
209
|
+
pre,
|
|
210
|
+
code {
|
|
211
|
+
background-color: #f9fafb;
|
|
212
|
+
color: #1f2937;
|
|
213
|
+
}
|
|
214
|
+
body.dark-mode .code-block,
|
|
215
|
+
body.dark-mode pre,
|
|
216
|
+
body.dark-mode code {
|
|
217
|
+
background-color: var(--ctp-mantle) !important;
|
|
218
|
+
color: var(--ctp-text) !important;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* Alerts */
|
|
222
|
+
body.dark-mode .alert {
|
|
223
|
+
background-color: var(--ctp-surface0);
|
|
224
|
+
border-color: var(--ctp-surface2);
|
|
225
|
+
color: var(--ctp-text);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* Text colors */
|
|
229
|
+
body.dark-mode .text-muted {
|
|
230
|
+
color: var(--ctp-subtext0) !important;
|
|
231
|
+
}
|
|
232
|
+
body.dark-mode .text-primary {
|
|
233
|
+
color: var(--ctp-mauve) !important;
|
|
234
|
+
}
|
|
235
|
+
body.dark-mode .text-danger {
|
|
236
|
+
color: var(--ctp-red) !important;
|
|
237
|
+
}
|
|
238
|
+
body.dark-mode .text-success {
|
|
239
|
+
color: var(--ctp-green) !important;
|
|
240
|
+
}
|
|
241
|
+
body.dark-mode .text-warning {
|
|
242
|
+
color: var(--ctp-peach) !important;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* Override Bootstrap bg-white and bg-light */
|
|
246
|
+
body.dark-mode .bg-white {
|
|
247
|
+
background-color: var(--ctp-surface0) !important;
|
|
248
|
+
}
|
|
249
|
+
body.dark-mode .bg-light {
|
|
250
|
+
background-color: var(--ctp-surface1) !important;
|
|
251
|
+
color: var(--ctp-text) !important;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/* Borders */
|
|
255
|
+
body.dark-mode .border {
|
|
256
|
+
border-color: var(--ctp-surface2) !important;
|
|
257
|
+
}
|
|
258
|
+
body.dark-mode .rounded {
|
|
259
|
+
border-color: var(--ctp-surface2) !important;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/* Quick Filters Sidebar Section */
|
|
263
|
+
.sidebar h6 {
|
|
264
|
+
font-size: 0.75rem;
|
|
265
|
+
text-transform: uppercase;
|
|
266
|
+
letter-spacing: 0.05em;
|
|
267
|
+
padding: 0.75rem 1.5rem;
|
|
268
|
+
margin: 0;
|
|
269
|
+
color: #6B7280;
|
|
270
|
+
}
|
|
271
|
+
body.dark-mode .sidebar h6 {
|
|
272
|
+
color: var(--ctp-subtext0);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* Stat Cards */
|
|
116
276
|
.stat-card {
|
|
117
277
|
border-radius: 0.75rem;
|
|
118
|
-
border: none;
|
|
119
|
-
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
120
278
|
transition: transform 0.2s, box-shadow 0.2s;
|
|
121
279
|
}
|
|
122
|
-
|
|
123
280
|
.stat-card:hover {
|
|
124
281
|
transform: translateY(-2px);
|
|
125
|
-
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
282
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
126
283
|
}
|
|
127
284
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
285
|
+
/* Badges for severity */
|
|
286
|
+
.badge.bg-danger {
|
|
287
|
+
background-color: #EF4444 !important;
|
|
288
|
+
}
|
|
289
|
+
.badge.bg-warning {
|
|
290
|
+
background-color: #F59E0B !important;
|
|
291
|
+
}
|
|
292
|
+
.badge.bg-info {
|
|
293
|
+
background-color: #3B82F6 !important;
|
|
294
|
+
}
|
|
295
|
+
.badge.bg-secondary {
|
|
296
|
+
background-color: #6B7280 !important;
|
|
297
|
+
}
|
|
298
|
+
.badge.bg-success {
|
|
299
|
+
background-color: #10B981 !important;
|
|
131
300
|
}
|
|
132
301
|
|
|
133
|
-
.
|
|
134
|
-
color:
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
302
|
+
body.dark-mode .badge.bg-danger {
|
|
303
|
+
background-color: var(--ctp-red) !important;
|
|
304
|
+
color: var(--ctp-base) !important;
|
|
305
|
+
}
|
|
306
|
+
body.dark-mode .badge.bg-warning {
|
|
307
|
+
background-color: var(--ctp-peach) !important;
|
|
308
|
+
color: var(--ctp-base) !important;
|
|
309
|
+
}
|
|
310
|
+
body.dark-mode .badge.bg-info {
|
|
311
|
+
background-color: var(--ctp-blue) !important;
|
|
312
|
+
color: var(--ctp-base) !important;
|
|
313
|
+
}
|
|
314
|
+
body.dark-mode .badge.bg-secondary {
|
|
315
|
+
background-color: var(--ctp-overlay1) !important;
|
|
316
|
+
}
|
|
317
|
+
body.dark-mode .badge.bg-success {
|
|
318
|
+
background-color: var(--ctp-green) !important;
|
|
319
|
+
color: var(--ctp-base) !important;
|
|
138
320
|
}
|
|
139
321
|
|
|
140
|
-
|
|
141
|
-
|
|
322
|
+
/* Links */
|
|
323
|
+
a {
|
|
324
|
+
color: #8B5CF6;
|
|
325
|
+
text-decoration: none;
|
|
326
|
+
}
|
|
327
|
+
a:hover {
|
|
328
|
+
color: #6D28D9;
|
|
329
|
+
text-decoration: underline;
|
|
330
|
+
}
|
|
331
|
+
body.dark-mode a {
|
|
332
|
+
color: var(--ctp-mauve);
|
|
333
|
+
}
|
|
334
|
+
body.dark-mode a:hover {
|
|
335
|
+
color: var(--ctp-pink);
|
|
142
336
|
}
|
|
143
337
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
color:
|
|
338
|
+
/* Buttons */
|
|
339
|
+
body.dark-mode .btn-primary {
|
|
340
|
+
background-color: var(--ctp-mauve);
|
|
341
|
+
border-color: var(--ctp-mauve);
|
|
342
|
+
color: var(--ctp-base);
|
|
343
|
+
}
|
|
344
|
+
body.dark-mode .btn-primary:hover {
|
|
345
|
+
background-color: var(--ctp-pink);
|
|
346
|
+
border-color: var(--ctp-pink);
|
|
347
|
+
}
|
|
348
|
+
body.dark-mode .btn-outline-primary {
|
|
349
|
+
color: var(--ctp-mauve);
|
|
350
|
+
border-color: var(--ctp-mauve);
|
|
351
|
+
}
|
|
352
|
+
body.dark-mode .btn-outline-primary:hover {
|
|
353
|
+
background-color: var(--ctp-mauve);
|
|
354
|
+
color: var(--ctp-base);
|
|
355
|
+
}
|
|
356
|
+
body.dark-mode .btn-secondary,
|
|
357
|
+
body.dark-mode .btn-outline-secondary {
|
|
358
|
+
background-color: var(--ctp-surface1);
|
|
359
|
+
border-color: var(--ctp-surface2);
|
|
360
|
+
color: var(--ctp-text);
|
|
147
361
|
}
|
|
148
362
|
|
|
149
|
-
|
|
150
|
-
|
|
363
|
+
/* Modal dialogs */
|
|
364
|
+
body.dark-mode .modal-content {
|
|
365
|
+
background-color: var(--ctp-surface0);
|
|
366
|
+
color: var(--ctp-text);
|
|
367
|
+
border-color: var(--ctp-surface2);
|
|
368
|
+
}
|
|
369
|
+
body.dark-mode .modal-header {
|
|
370
|
+
background-color: var(--ctp-surface1);
|
|
371
|
+
border-bottom-color: var(--ctp-surface2);
|
|
372
|
+
color: var(--ctp-text);
|
|
373
|
+
}
|
|
374
|
+
body.dark-mode .modal-footer {
|
|
375
|
+
background-color: var(--ctp-surface1);
|
|
376
|
+
border-top-color: var(--ctp-surface2);
|
|
377
|
+
}
|
|
378
|
+
body.dark-mode .modal-title {
|
|
379
|
+
color: var(--ctp-text);
|
|
380
|
+
}
|
|
381
|
+
body.dark-mode .btn-close {
|
|
382
|
+
filter: invert(1);
|
|
151
383
|
}
|
|
152
384
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
385
|
+
/* Table headers - CRITICAL FIX */
|
|
386
|
+
body.dark-mode thead,
|
|
387
|
+
body.dark-mode thead th {
|
|
388
|
+
background-color: var(--ctp-surface1) !important;
|
|
389
|
+
color: var(--ctp-text) !important;
|
|
390
|
+
border-color: var(--ctp-surface2) !important;
|
|
391
|
+
}
|
|
392
|
+
body.dark-mode tbody tr {
|
|
393
|
+
border-color: var(--ctp-surface2) !important;
|
|
394
|
+
background-color: transparent !important;
|
|
395
|
+
}
|
|
396
|
+
body.dark-mode tbody td {
|
|
397
|
+
border-color: var(--ctp-surface2) !important;
|
|
398
|
+
background-color: transparent !important;
|
|
399
|
+
color: var(--ctp-text) !important;
|
|
400
|
+
}
|
|
401
|
+
body.dark-mode tbody th {
|
|
402
|
+
background-color: var(--ctp-surface0) !important;
|
|
403
|
+
color: var(--ctp-text) !important;
|
|
404
|
+
border-color: var(--ctp-surface2) !important;
|
|
405
|
+
}
|
|
406
|
+
body.dark-mode table {
|
|
407
|
+
color: var(--ctp-text) !important;
|
|
156
408
|
}
|
|
157
409
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
color:
|
|
410
|
+
/* Chart canvas backgrounds */
|
|
411
|
+
body.dark-mode canvas {
|
|
412
|
+
background-color: transparent !important;
|
|
161
413
|
}
|
|
162
414
|
|
|
163
|
-
|
|
164
|
-
|
|
415
|
+
/* Chart labels and text */
|
|
416
|
+
body.dark-mode .chart-container text {
|
|
417
|
+
fill: var(--ctp-text) !important;
|
|
165
418
|
}
|
|
166
419
|
|
|
167
|
-
|
|
168
|
-
|
|
420
|
+
/* Form placeholders */
|
|
421
|
+
body.dark-mode .form-control::placeholder,
|
|
422
|
+
body.dark-mode .form-select::placeholder {
|
|
423
|
+
color: var(--ctp-overlay0);
|
|
424
|
+
opacity: 1;
|
|
169
425
|
}
|
|
170
426
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
427
|
+
/* Dropdown menus */
|
|
428
|
+
body.dark-mode .dropdown-menu {
|
|
429
|
+
background-color: var(--ctp-surface0);
|
|
430
|
+
border-color: var(--ctp-surface2);
|
|
431
|
+
}
|
|
432
|
+
body.dark-mode .dropdown-item {
|
|
433
|
+
color: var(--ctp-text);
|
|
434
|
+
}
|
|
435
|
+
body.dark-mode .dropdown-item:hover {
|
|
436
|
+
background-color: var(--ctp-surface1);
|
|
437
|
+
color: var(--ctp-mauve);
|
|
174
438
|
}
|
|
175
439
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
color: var(--
|
|
440
|
+
/* Pagination */
|
|
441
|
+
body.dark-mode .pagination .page-link {
|
|
442
|
+
background-color: var(--ctp-surface0);
|
|
443
|
+
border-color: var(--ctp-surface2);
|
|
444
|
+
color: var(--ctp-text);
|
|
445
|
+
}
|
|
446
|
+
body.dark-mode .pagination .page-link:hover {
|
|
447
|
+
background-color: var(--ctp-surface1);
|
|
448
|
+
color: var(--ctp-mauve);
|
|
449
|
+
}
|
|
450
|
+
body.dark-mode .pagination .page-item.active .page-link {
|
|
451
|
+
background-color: var(--ctp-mauve);
|
|
452
|
+
border-color: var(--ctp-mauve);
|
|
453
|
+
color: var(--ctp-base);
|
|
179
454
|
}
|
|
180
455
|
|
|
181
|
-
/*
|
|
182
|
-
.
|
|
183
|
-
background-color: var(--
|
|
184
|
-
|
|
185
|
-
|
|
456
|
+
/* Progress bars */
|
|
457
|
+
body.dark-mode .progress {
|
|
458
|
+
background-color: var(--ctp-surface1);
|
|
459
|
+
}
|
|
460
|
+
body.dark-mode .progress-bar {
|
|
461
|
+
background-color: var(--ctp-mauve);
|
|
186
462
|
}
|
|
187
463
|
|
|
188
|
-
|
|
189
|
-
|
|
464
|
+
/* Horizontal rules */
|
|
465
|
+
body.dark-mode hr {
|
|
466
|
+
border-color: var(--ctp-surface2);
|
|
467
|
+
opacity: 1;
|
|
190
468
|
}
|
|
191
469
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
color: var(--
|
|
195
|
-
border-color: var(--
|
|
470
|
+
/* List groups */
|
|
471
|
+
body.dark-mode .list-group-item {
|
|
472
|
+
background-color: var(--ctp-surface0);
|
|
473
|
+
border-color: var(--ctp-surface2);
|
|
474
|
+
color: var(--ctp-text);
|
|
475
|
+
}
|
|
476
|
+
body.dark-mode .list-group-item:hover {
|
|
477
|
+
background-color: var(--ctp-surface1);
|
|
196
478
|
}
|
|
197
479
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
color: var(--
|
|
201
|
-
|
|
480
|
+
/* Offcanvas (mobile menu) */
|
|
481
|
+
body.dark-mode .offcanvas {
|
|
482
|
+
background-color: var(--ctp-mantle);
|
|
483
|
+
color: var(--ctp-text);
|
|
484
|
+
}
|
|
485
|
+
body.dark-mode .offcanvas-header {
|
|
486
|
+
border-bottom-color: var(--ctp-surface2);
|
|
202
487
|
}
|
|
203
488
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
color: var(--
|
|
489
|
+
/* Small text and labels */
|
|
490
|
+
body.dark-mode small {
|
|
491
|
+
color: var(--ctp-subtext1) !important;
|
|
492
|
+
}
|
|
493
|
+
body.dark-mode label {
|
|
494
|
+
color: var(--ctp-text);
|
|
207
495
|
}
|
|
208
496
|
|
|
209
|
-
|
|
210
|
-
|
|
497
|
+
/* Breadcrumbs */
|
|
498
|
+
body.dark-mode .breadcrumb {
|
|
499
|
+
background-color: var(--ctp-surface0);
|
|
500
|
+
}
|
|
501
|
+
body.dark-mode .breadcrumb-item {
|
|
502
|
+
color: var(--ctp-text);
|
|
503
|
+
}
|
|
504
|
+
body.dark-mode .breadcrumb-item.active {
|
|
505
|
+
color: var(--ctp-subtext0);
|
|
211
506
|
}
|
|
212
507
|
|
|
213
|
-
|
|
214
|
-
|
|
508
|
+
/* Tooltips */
|
|
509
|
+
body.dark-mode .tooltip-inner {
|
|
510
|
+
background-color: var(--ctp-surface0);
|
|
511
|
+
color: var(--ctp-text);
|
|
215
512
|
}
|
|
216
513
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
514
|
+
/* Checkboxes and radios */
|
|
515
|
+
body.dark-mode .form-check-input {
|
|
516
|
+
background-color: var(--ctp-surface1);
|
|
517
|
+
border-color: var(--ctp-surface2);
|
|
518
|
+
}
|
|
519
|
+
body.dark-mode .form-check-input:checked {
|
|
520
|
+
background-color: var(--ctp-mauve);
|
|
521
|
+
border-color: var(--ctp-mauve);
|
|
522
|
+
}
|
|
523
|
+
body.dark-mode .form-check-label {
|
|
524
|
+
color: var(--ctp-text);
|
|
220
525
|
}
|
|
221
526
|
|
|
222
|
-
|
|
223
|
-
|
|
527
|
+
/* Nav tabs */
|
|
528
|
+
body.dark-mode .nav-tabs {
|
|
529
|
+
border-bottom-color: var(--ctp-surface2);
|
|
530
|
+
}
|
|
531
|
+
body.dark-mode .nav-tabs .nav-link {
|
|
532
|
+
color: var(--ctp-text);
|
|
533
|
+
background-color: transparent;
|
|
534
|
+
border-color: transparent;
|
|
535
|
+
}
|
|
536
|
+
body.dark-mode .nav-tabs .nav-link:hover {
|
|
537
|
+
border-color: var(--ctp-surface2);
|
|
538
|
+
background-color: var(--ctp-surface1);
|
|
539
|
+
}
|
|
540
|
+
body.dark-mode .nav-tabs .nav-link.active {
|
|
541
|
+
background-color: var(--ctp-surface0);
|
|
542
|
+
border-color: var(--ctp-surface2) var(--ctp-surface2) var(--ctp-surface0);
|
|
543
|
+
color: var(--ctp-mauve);
|
|
224
544
|
}
|
|
225
545
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
border-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
546
|
+
/* Collapsible sections - AGGRESSIVE */
|
|
547
|
+
body.dark-mode .accordion-item {
|
|
548
|
+
background-color: var(--ctp-surface0) !important;
|
|
549
|
+
border-color: var(--ctp-surface2) !important;
|
|
550
|
+
}
|
|
551
|
+
body.dark-mode .accordion-button {
|
|
552
|
+
background-color: var(--ctp-surface1) !important;
|
|
553
|
+
color: var(--ctp-text) !important;
|
|
554
|
+
}
|
|
555
|
+
body.dark-mode .accordion-button.bg-light {
|
|
556
|
+
background-color: var(--ctp-surface1) !important;
|
|
557
|
+
}
|
|
558
|
+
body.dark-mode .accordion-button:not(.collapsed) {
|
|
559
|
+
background-color: var(--ctp-surface0) !important;
|
|
560
|
+
color: var(--ctp-mauve) !important;
|
|
561
|
+
}
|
|
562
|
+
body.dark-mode .accordion-button::after {
|
|
563
|
+
filter: invert(1);
|
|
564
|
+
}
|
|
565
|
+
body.dark-mode .accordion-body {
|
|
566
|
+
background-color: var(--ctp-surface0) !important;
|
|
567
|
+
color: var(--ctp-text) !important;
|
|
234
568
|
}
|
|
235
569
|
|
|
236
|
-
|
|
237
|
-
|
|
570
|
+
/* Heatmap specific styling - AGGRESSIVE */
|
|
571
|
+
body.dark-mode .heatmap-cell {
|
|
572
|
+
border-color: var(--ctp-surface2) !important;
|
|
573
|
+
color: var(--ctp-text) !important;
|
|
574
|
+
}
|
|
575
|
+
body.dark-mode .heatmap-hour {
|
|
576
|
+
color: var(--ctp-sky) !important;
|
|
577
|
+
font-weight: 600 !important;
|
|
578
|
+
font-size: 0.75rem !important;
|
|
579
|
+
}
|
|
580
|
+
body.dark-mode .heatmap-count {
|
|
581
|
+
color: var(--ctp-peach) !important;
|
|
582
|
+
font-weight: 600 !important;
|
|
583
|
+
}
|
|
584
|
+
body.dark-mode .heatmap-count.text-white {
|
|
585
|
+
color: var(--ctp-text) !important;
|
|
586
|
+
}
|
|
587
|
+
body.dark-mode .heatmap-count.text-dark {
|
|
588
|
+
color: var(--ctp-peach) !important;
|
|
589
|
+
}
|
|
590
|
+
body.dark-mode .heatmap-grid {
|
|
591
|
+
background-color: var(--ctp-surface0);
|
|
238
592
|
}
|
|
239
593
|
|
|
240
|
-
|
|
241
|
-
|
|
594
|
+
/* Definition lists (dl, dt, dd) - used in Request Context */
|
|
595
|
+
body.dark-mode dl {
|
|
596
|
+
color: var(--ctp-text);
|
|
597
|
+
}
|
|
598
|
+
body.dark-mode dt {
|
|
599
|
+
color: var(--ctp-subtext1);
|
|
600
|
+
font-weight: 600;
|
|
601
|
+
}
|
|
602
|
+
body.dark-mode dd {
|
|
603
|
+
color: var(--ctp-text);
|
|
604
|
+
background-color: var(--ctp-surface0);
|
|
242
605
|
}
|
|
243
606
|
|
|
244
|
-
|
|
245
|
-
|
|
607
|
+
/* Override any remaining white backgrounds */
|
|
608
|
+
body.dark-mode div[style*="background-color: white"],
|
|
609
|
+
body.dark-mode div[style*="background-color: #fff"],
|
|
610
|
+
body.dark-mode div[style*="background-color:#fff"],
|
|
611
|
+
body.dark-mode div[style*="background: white"],
|
|
612
|
+
body.dark-mode div[style*="background: #fff"] {
|
|
613
|
+
background-color: var(--ctp-surface0) !important;
|
|
246
614
|
}
|
|
247
615
|
|
|
248
|
-
.
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
margin: 1rem 0;
|
|
616
|
+
/* Chart.js specific fixes for axis labels */
|
|
617
|
+
body.dark-mode .chartjs-render-monitor {
|
|
618
|
+
background-color: transparent !important;
|
|
252
619
|
}
|
|
253
620
|
|
|
254
|
-
/*
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
opacity: 0;
|
|
259
|
-
background-color: #FEF3C7;
|
|
260
|
-
}
|
|
261
|
-
10% {
|
|
262
|
-
transform: translateY(0);
|
|
263
|
-
opacity: 1;
|
|
264
|
-
background-color: #FEF3C7;
|
|
265
|
-
}
|
|
266
|
-
100% {
|
|
267
|
-
background-color: transparent;
|
|
268
|
-
}
|
|
621
|
+
/* Make sure all headings are visible */
|
|
622
|
+
body.dark-mode h1, body.dark-mode h2, body.dark-mode h3,
|
|
623
|
+
body.dark-mode h4, body.dark-mode h5, body.dark-mode h6 {
|
|
624
|
+
color: var(--ctp-text);
|
|
269
625
|
}
|
|
270
626
|
|
|
271
|
-
/*
|
|
272
|
-
|
|
273
|
-
|
|
627
|
+
/* Stronger text colors for better visibility */
|
|
628
|
+
body.dark-mode .text-secondary {
|
|
629
|
+
color: var(--ctp-subtext1) !important;
|
|
274
630
|
}
|
|
275
631
|
|
|
276
|
-
/*
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
632
|
+
/* SVG text elements (for charts) - MORE AGGRESSIVE */
|
|
633
|
+
body.dark-mode svg text {
|
|
634
|
+
fill: var(--ctp-text) !important;
|
|
635
|
+
}
|
|
636
|
+
body.dark-mode svg .domain,
|
|
637
|
+
body.dark-mode svg .tick line {
|
|
638
|
+
stroke: var(--ctp-surface2) !important;
|
|
639
|
+
}
|
|
640
|
+
body.dark-mode svg tspan {
|
|
641
|
+
fill: var(--ctp-text) !important;
|
|
284
642
|
}
|
|
285
643
|
|
|
286
|
-
|
|
287
|
-
|
|
644
|
+
/* Details/Summary (collapsible sections) */
|
|
645
|
+
body.dark-mode details {
|
|
646
|
+
background-color: var(--ctp-surface0) !important;
|
|
647
|
+
border-color: var(--ctp-surface2) !important;
|
|
648
|
+
}
|
|
649
|
+
body.dark-mode summary {
|
|
650
|
+
background-color: var(--ctp-surface0) !important;
|
|
651
|
+
color: var(--ctp-text) !important;
|
|
652
|
+
border-color: var(--ctp-surface2) !important;
|
|
653
|
+
}
|
|
654
|
+
body.dark-mode details[open] summary {
|
|
655
|
+
background-color: var(--ctp-surface1) !important;
|
|
656
|
+
border-bottom-color: var(--ctp-surface2) !important;
|
|
288
657
|
}
|
|
289
658
|
|
|
290
|
-
/*
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
659
|
+
/* Button-like summary elements */
|
|
660
|
+
body.dark-mode .btn.collapsed,
|
|
661
|
+
body.dark-mode [data-bs-toggle="collapse"] {
|
|
662
|
+
background-color: var(--ctp-surface0) !important;
|
|
663
|
+
color: var(--ctp-text) !important;
|
|
664
|
+
border-color: var(--ctp-surface2) !important;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/* Specific override for white backgrounds in backtrace sections */
|
|
668
|
+
body.dark-mode .card .card-body > div[style*="background"],
|
|
669
|
+
body.dark-mode .card-body > button[style*="background"] {
|
|
670
|
+
background-color: var(--ctp-surface0) !important;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/* Error header/banner */
|
|
674
|
+
body.dark-mode .alert-danger {
|
|
675
|
+
background-color: rgba(243, 139, 168, 0.2) !important;
|
|
676
|
+
border-color: var(--ctp-red) !important;
|
|
677
|
+
color: var(--ctp-text) !important;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/* Chartkick specific - force text visibility */
|
|
681
|
+
body.dark-mode #chart-1 text,
|
|
682
|
+
body.dark-mode [id^="chart-"] text {
|
|
683
|
+
fill: var(--ctp-text) !important;
|
|
684
|
+
color: var(--ctp-text) !important;
|
|
298
685
|
}
|
|
299
686
|
|
|
300
|
-
|
|
301
|
-
|
|
687
|
+
/* Force all text in charts to be visible */
|
|
688
|
+
body.dark-mode canvas + div text,
|
|
689
|
+
body.dark-mode .chartjs-size-monitor text {
|
|
690
|
+
color: var(--ctp-text) !important;
|
|
302
691
|
}
|
|
303
692
|
|
|
304
|
-
/*
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
top: 0;
|
|
308
|
-
left: 0;
|
|
309
|
-
right: 0;
|
|
310
|
-
height: 3px;
|
|
311
|
-
background: linear-gradient(90deg, #8B5CF6 0%, #6D28D9 100%);
|
|
312
|
-
transform: scaleX(0);
|
|
313
|
-
transform-origin: left;
|
|
314
|
-
transition: transform 0.3s ease;
|
|
315
|
-
z-index: 9999;
|
|
316
|
-
display: none;
|
|
693
|
+
/* ULTRA AGGRESSIVE - Chart.js axis labels and titles */
|
|
694
|
+
body.dark-mode canvas {
|
|
695
|
+
color: var(--ctp-text) !important;
|
|
317
696
|
}
|
|
318
697
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
698
|
+
/* Target Google Charts (alternative library) */
|
|
699
|
+
body.dark-mode svg > g > g > text,
|
|
700
|
+
body.dark-mode svg g text {
|
|
701
|
+
fill: var(--ctp-text) !important;
|
|
702
|
+
font-size: 12px !important;
|
|
322
703
|
}
|
|
323
704
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
705
|
+
/* Chart.js specific selectors - NUCLEAR OPTION */
|
|
706
|
+
body.dark-mode .chartjs-render-monitor + div text,
|
|
707
|
+
body.dark-mode [class*="chart"] text,
|
|
708
|
+
body.dark-mode div[id*="chart"] text {
|
|
709
|
+
fill: var(--ctp-text) !important;
|
|
710
|
+
color: var(--ctp-text) !important;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/* Ensure axis tick labels are visible */
|
|
714
|
+
body.dark-mode g.tick text,
|
|
715
|
+
body.dark-mode .tick text,
|
|
716
|
+
body.dark-mode text.highcharts-axis-title,
|
|
717
|
+
body.dark-mode .highcharts-axis-labels text {
|
|
718
|
+
fill: var(--ctp-text) !important;
|
|
719
|
+
color: var(--ctp-text) !important;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/* Force visibility on ALL text elements inside chart containers */
|
|
723
|
+
body.dark-mode .card-body text,
|
|
724
|
+
body.dark-mode .card text {
|
|
725
|
+
fill: var(--ctp-text) !important;
|
|
328
726
|
}
|
|
329
727
|
</style>
|
|
330
728
|
</head>
|
|
331
729
|
|
|
332
730
|
<body>
|
|
333
|
-
<!-- Loading Indicator -->
|
|
334
|
-
<div id="loading-indicator"></div>
|
|
335
|
-
|
|
336
731
|
<!-- Top Navbar -->
|
|
337
732
|
<nav class="navbar navbar-dark">
|
|
338
733
|
<div class="container-fluid">
|
|
339
|
-
<
|
|
340
|
-
<
|
|
341
|
-
|
|
342
|
-
|
|
734
|
+
<div class="d-flex align-items-center">
|
|
735
|
+
<button class="btn btn-link text-white d-md-none me-2" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu">
|
|
736
|
+
<i class="bi bi-list fs-4"></i>
|
|
737
|
+
</button>
|
|
738
|
+
<a class="navbar-brand" href="<%= root_path %>">
|
|
739
|
+
<i class="bi bi-bug-fill"></i> Error Dashboard
|
|
740
|
+
</a>
|
|
741
|
+
</div>
|
|
343
742
|
<div class="d-flex align-items-center gap-3">
|
|
344
|
-
<button class="theme-toggle" id="themeToggle"
|
|
743
|
+
<button class="theme-toggle" id="themeToggle">
|
|
345
744
|
<i class="bi bi-moon-fill" id="themeIcon"></i>
|
|
346
745
|
</button>
|
|
347
|
-
<div class="text-white">
|
|
348
|
-
<small><%= Rails.env.titleize
|
|
746
|
+
<div class="text-white d-none d-md-block">
|
|
747
|
+
<small><%= Rails.env.titleize %></small>
|
|
349
748
|
</div>
|
|
350
749
|
</div>
|
|
351
750
|
</div>
|
|
@@ -354,7 +753,7 @@
|
|
|
354
753
|
<div class="container-fluid">
|
|
355
754
|
<div class="row">
|
|
356
755
|
<!-- Sidebar -->
|
|
357
|
-
<nav class="col-md-2 d-md-block sidebar">
|
|
756
|
+
<nav class="col-md-2 d-none d-md-block sidebar">
|
|
358
757
|
<div class="position-sticky pt-3">
|
|
359
758
|
<ul class="nav flex-column">
|
|
360
759
|
<li class="nav-item">
|
|
@@ -372,31 +771,13 @@
|
|
|
372
771
|
<i class="bi bi-graph-up"></i> Analytics
|
|
373
772
|
<% end %>
|
|
374
773
|
</li>
|
|
375
|
-
<% if RailsErrorDashboard.configuration.enable_platform_comparison %>
|
|
376
|
-
<li class="nav-item">
|
|
377
|
-
<%= link_to platform_comparison_errors_path, class: "nav-link #{request.path == platform_comparison_errors_path ? 'active' : ''}" do %>
|
|
378
|
-
<i class="bi bi-phone"></i> Platform Health
|
|
379
|
-
<% end %>
|
|
380
|
-
</li>
|
|
381
|
-
<% end %>
|
|
382
|
-
<% if RailsErrorDashboard.configuration.enable_error_correlation %>
|
|
383
|
-
<li class="nav-item">
|
|
384
|
-
<%= link_to correlation_errors_path, class: "nav-link #{request.path == correlation_errors_path ? 'active' : ''}" do %>
|
|
385
|
-
<i class="bi bi-diagram-3"></i> Correlation
|
|
386
|
-
<% end %>
|
|
387
|
-
</li>
|
|
388
|
-
<% end %>
|
|
389
774
|
</ul>
|
|
390
775
|
|
|
391
|
-
<
|
|
392
|
-
|
|
393
|
-
<h6 class="sidebar-heading px-3 mt-4 mb-2 text-muted text-uppercase">
|
|
394
|
-
<small>Quick Filters</small>
|
|
395
|
-
</h6>
|
|
776
|
+
<h6 class="mt-4">QUICK FILTERS</h6>
|
|
396
777
|
<ul class="nav flex-column">
|
|
397
778
|
<li class="nav-item">
|
|
398
|
-
<%= link_to errors_path(
|
|
399
|
-
<i class="bi bi-exclamation-circle
|
|
779
|
+
<%= link_to errors_path(status: 'unresolved'), class: "nav-link" do %>
|
|
780
|
+
<i class="bi bi-exclamation-circle"></i> Unresolved
|
|
400
781
|
<% end %>
|
|
401
782
|
</li>
|
|
402
783
|
<li class="nav-item">
|
|
@@ -423,69 +804,185 @@
|
|
|
423
804
|
<!-- Bootstrap JS -->
|
|
424
805
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
425
806
|
|
|
426
|
-
<!-- Theme Toggle
|
|
807
|
+
<!-- Pure JavaScript Theme Toggle -->
|
|
427
808
|
<script>
|
|
428
|
-
// Load theme
|
|
429
|
-
|
|
430
|
-
const savedTheme = localStorage.getItem('theme')
|
|
809
|
+
// Load saved theme on page load (before DOMContentLoaded to prevent flash)
|
|
810
|
+
(function() {
|
|
811
|
+
const savedTheme = localStorage.getItem('theme');
|
|
431
812
|
if (savedTheme === 'dark') {
|
|
432
813
|
document.body.classList.add('dark-mode');
|
|
433
|
-
document.documentElement.setAttribute('data-theme', 'dark');
|
|
434
|
-
updateThemeIcon(true);
|
|
435
|
-
} else {
|
|
436
|
-
// Ensure light mode is clean
|
|
437
|
-
document.body.classList.remove('dark-mode');
|
|
438
|
-
document.documentElement.removeAttribute('data-theme');
|
|
439
|
-
updateThemeIcon(false);
|
|
440
814
|
}
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
function toggleTheme() {
|
|
444
|
-
const body = document.body;
|
|
445
|
-
const isDark = body.classList.toggle('dark-mode');
|
|
446
|
-
|
|
447
|
-
// Sync with html data attribute
|
|
448
|
-
if (isDark) {
|
|
449
|
-
document.documentElement.setAttribute('data-theme', 'dark');
|
|
450
|
-
} else {
|
|
451
|
-
document.documentElement.removeAttribute('data-theme');
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// Save preference
|
|
455
|
-
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
|
456
|
-
|
|
457
|
-
// Update icon
|
|
458
|
-
updateThemeIcon(isDark);
|
|
459
|
-
}
|
|
815
|
+
})();
|
|
460
816
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
817
|
+
// Theme toggle after DOM loads
|
|
818
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
819
|
+
const themeToggle = document.getElementById('themeToggle');
|
|
820
|
+
const themeIcon = document.getElementById('themeIcon');
|
|
821
|
+
|
|
822
|
+
// Update icon based on current theme
|
|
823
|
+
function updateIcon() {
|
|
824
|
+
if (document.body.classList.contains('dark-mode')) {
|
|
825
|
+
themeIcon.className = 'bi bi-sun-fill';
|
|
826
|
+
} else {
|
|
827
|
+
themeIcon.className = 'bi bi-moon-fill';
|
|
828
|
+
}
|
|
467
829
|
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// Loading indicator for form submissions and link clicks
|
|
471
|
-
const loadingIndicator = document.getElementById('loading-indicator');
|
|
472
830
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
831
|
+
// Set initial icon
|
|
832
|
+
updateIcon();
|
|
833
|
+
|
|
834
|
+
// Toggle theme on button click
|
|
835
|
+
themeToggle.addEventListener('click', function() {
|
|
836
|
+
console.log('🎨 Theme toggle clicked');
|
|
837
|
+
|
|
838
|
+
document.body.classList.toggle('dark-mode');
|
|
839
|
+
const isDark = document.body.classList.contains('dark-mode');
|
|
840
|
+
|
|
841
|
+
console.log('Dark mode:', isDark);
|
|
842
|
+
|
|
843
|
+
// Save preference
|
|
844
|
+
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
|
845
|
+
console.log('💾 Saved to localStorage:', isDark ? 'dark' : 'light');
|
|
846
|
+
|
|
847
|
+
// Update icon
|
|
848
|
+
updateIcon();
|
|
849
|
+
console.log('✅ Theme toggled successfully');
|
|
850
|
+
|
|
851
|
+
// Reapply chart theme
|
|
852
|
+
applyChartTheme();
|
|
853
|
+
|
|
854
|
+
// Reload page to update charts properly
|
|
855
|
+
setTimeout(() => location.reload(), 300);
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
// Chart.js theme colors - ULTRA AGGRESSIVE setup
|
|
859
|
+
function applyChartTheme() {
|
|
860
|
+
if (typeof Chart !== 'undefined') {
|
|
861
|
+
const isDark = document.body.classList.contains('dark-mode');
|
|
862
|
+
const textColor = isDark ? '#cdd6f4' : '#1f2937';
|
|
863
|
+
const gridColor = isDark ? 'rgba(88, 91, 112, 0.2)' : 'rgba(0, 0, 0, 0.1)';
|
|
864
|
+
|
|
865
|
+
console.log('📊 Setting Chart.js theme:', isDark ? 'DARK' : 'light', '| Text:', textColor);
|
|
866
|
+
|
|
867
|
+
// Global defaults
|
|
868
|
+
Chart.defaults.color = textColor;
|
|
869
|
+
Chart.defaults.borderColor = gridColor;
|
|
870
|
+
Chart.defaults.font = Chart.defaults.font || {};
|
|
871
|
+
Chart.defaults.font.color = textColor;
|
|
872
|
+
|
|
873
|
+
// Scale defaults (axes) - AGGRESSIVE
|
|
874
|
+
if (Chart.defaults.scale) {
|
|
875
|
+
Chart.defaults.scale.ticks = Chart.defaults.scale.ticks || {};
|
|
876
|
+
Chart.defaults.scale.ticks.color = textColor;
|
|
877
|
+
Chart.defaults.scale.ticks.font = Chart.defaults.scale.ticks.font || {};
|
|
878
|
+
Chart.defaults.scale.ticks.font.color = textColor;
|
|
879
|
+
|
|
880
|
+
Chart.defaults.scale.grid = Chart.defaults.scale.grid || {};
|
|
881
|
+
Chart.defaults.scale.grid.color = gridColor;
|
|
882
|
+
|
|
883
|
+
// Axis title (xtitle, ytitle)
|
|
884
|
+
Chart.defaults.scale.title = Chart.defaults.scale.title || {};
|
|
885
|
+
Chart.defaults.scale.title.color = textColor;
|
|
886
|
+
Chart.defaults.scale.title.font = Chart.defaults.scale.title.font || {};
|
|
887
|
+
Chart.defaults.scale.title.font.size = 14;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// X and Y axis specific
|
|
891
|
+
if (Chart.defaults.scales) {
|
|
892
|
+
// X axis
|
|
893
|
+
Chart.defaults.scales.x = Chart.defaults.scales.x || {};
|
|
894
|
+
Chart.defaults.scales.x.ticks = Chart.defaults.scales.x.ticks || {};
|
|
895
|
+
Chart.defaults.scales.x.ticks.color = textColor;
|
|
896
|
+
Chart.defaults.scales.x.title = Chart.defaults.scales.x.title || {};
|
|
897
|
+
Chart.defaults.scales.x.title.color = textColor;
|
|
898
|
+
Chart.defaults.scales.x.grid = Chart.defaults.scales.x.grid || {};
|
|
899
|
+
Chart.defaults.scales.x.grid.color = gridColor;
|
|
900
|
+
|
|
901
|
+
// Y axis
|
|
902
|
+
Chart.defaults.scales.y = Chart.defaults.scales.y || {};
|
|
903
|
+
Chart.defaults.scales.y.ticks = Chart.defaults.scales.y.ticks || {};
|
|
904
|
+
Chart.defaults.scales.y.ticks.color = textColor;
|
|
905
|
+
Chart.defaults.scales.y.title = Chart.defaults.scales.y.title || {};
|
|
906
|
+
Chart.defaults.scales.y.title.color = textColor;
|
|
907
|
+
Chart.defaults.scales.y.grid = Chart.defaults.scales.y.grid || {};
|
|
908
|
+
Chart.defaults.scales.y.grid.color = gridColor;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// Plugin defaults
|
|
912
|
+
if (Chart.defaults.plugins) {
|
|
913
|
+
// Legend
|
|
914
|
+
if (Chart.defaults.plugins.legend) {
|
|
915
|
+
Chart.defaults.plugins.legend.labels = Chart.defaults.plugins.legend.labels || {};
|
|
916
|
+
Chart.defaults.plugins.legend.labels.color = textColor;
|
|
917
|
+
Chart.defaults.plugins.legend.labels.font = Chart.defaults.plugins.legend.labels.font || {};
|
|
918
|
+
Chart.defaults.plugins.legend.labels.font.color = textColor;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// Tooltip
|
|
922
|
+
if (Chart.defaults.plugins.tooltip) {
|
|
923
|
+
Chart.defaults.plugins.tooltip.backgroundColor = isDark ? '#313244' : 'rgba(0, 0, 0, 0.8)';
|
|
924
|
+
Chart.defaults.plugins.tooltip.titleColor = textColor;
|
|
925
|
+
Chart.defaults.plugins.tooltip.bodyColor = textColor;
|
|
926
|
+
Chart.defaults.plugins.tooltip.borderColor = isDark ? '#585b70' : 'rgba(0, 0, 0, 0.1)';
|
|
927
|
+
Chart.defaults.plugins.tooltip.borderWidth = 1;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// Title plugin
|
|
931
|
+
if (Chart.defaults.plugins.title) {
|
|
932
|
+
Chart.defaults.plugins.title.color = textColor;
|
|
933
|
+
Chart.defaults.plugins.title.font = Chart.defaults.plugins.title.font || {};
|
|
934
|
+
Chart.defaults.plugins.title.font.color = textColor;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
console.log('✅ Chart.js theme applied - all text should be:', textColor);
|
|
939
|
+
} else {
|
|
940
|
+
console.warn('⚠️ Chart.js not loaded yet');
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// Also update Google Charts if present
|
|
944
|
+
if (typeof google !== 'undefined' && google.visualization) {
|
|
945
|
+
console.log('📊 Google Charts detected - applying theme');
|
|
946
|
+
}
|
|
483
947
|
}
|
|
484
|
-
});
|
|
485
948
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
949
|
+
// Apply chart theme on load
|
|
950
|
+
applyChartTheme();
|
|
951
|
+
|
|
952
|
+
// NUCLEAR OPTION: Watch for chart creation and force colors
|
|
953
|
+
const observer = new MutationObserver(function(mutations) {
|
|
954
|
+
mutations.forEach(function(mutation) {
|
|
955
|
+
mutation.addedNodes.forEach(function(node) {
|
|
956
|
+
if (node.tagName === 'CANVAS') {
|
|
957
|
+
console.log('🎨 New chart detected, forcing theme...');
|
|
958
|
+
setTimeout(applyChartTheme, 100);
|
|
959
|
+
}
|
|
960
|
+
});
|
|
961
|
+
});
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
observer.observe(document.body, {
|
|
965
|
+
childList: true,
|
|
966
|
+
subtree: true
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
// Also listen for Chartkick chart creation events
|
|
970
|
+
document.addEventListener('chartkick:load', function() {
|
|
971
|
+
console.log('📊 Chartkick loaded, applying theme');
|
|
972
|
+
applyChartTheme();
|
|
973
|
+
});
|
|
974
|
+
|
|
975
|
+
// Force reapply every 500ms for the first 3 seconds (in case charts load late)
|
|
976
|
+
let attempts = 0;
|
|
977
|
+
const forceInterval = setInterval(function() {
|
|
978
|
+
attempts++;
|
|
979
|
+
applyChartTheme();
|
|
980
|
+
console.log('🔄 Force applying theme (attempt', attempts, ')');
|
|
981
|
+
if (attempts >= 6) {
|
|
982
|
+
clearInterval(forceInterval);
|
|
983
|
+
console.log('✅ Stopped force applying');
|
|
984
|
+
}
|
|
985
|
+
}, 500);
|
|
489
986
|
});
|
|
490
987
|
</script>
|
|
491
988
|
</body>
|