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
|
@@ -1,7 +1,7 @@
|
|
|
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 %>
|
|
@@ -10,226 +10,719 @@
|
|
|
10
10
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
11
11
|
<!-- Bootstrap Icons -->
|
|
12
12
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
<!-- Chart.js -->
|
|
14
15
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
|
|
15
16
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@3.0.0/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
|
|
16
17
|
<script src="https://cdn.jsdelivr.net/npm/chartkick@5.0.1/dist/chartkick.min.js"></script>
|
|
17
18
|
|
|
19
|
+
<!-- Catppuccin Mocha Theme - Pure CSS -->
|
|
18
20
|
<style>
|
|
21
|
+
/* Catppuccin Mocha Color Palette */
|
|
19
22
|
:root {
|
|
20
|
-
--
|
|
21
|
-
--
|
|
22
|
-
--
|
|
23
|
-
--
|
|
24
|
-
--
|
|
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) */
|
|
52
|
+
body {
|
|
53
|
+
background-color: #f3f4f6;
|
|
54
|
+
color: #1f2937;
|
|
55
|
+
transition: background-color 0.3s, color 0.3s;
|
|
25
56
|
}
|
|
26
57
|
|
|
27
|
-
/*
|
|
28
|
-
body {
|
|
29
|
-
--bg-color: #F3F4F6;
|
|
30
|
-
--text-color: #1F2937;
|
|
31
|
-
--card-bg: #FFFFFF;
|
|
32
|
-
--sidebar-bg: #FFFFFF;
|
|
33
|
-
--sidebar-hover: #F9FAFB;
|
|
34
|
-
--border-color: #E5E7EB;
|
|
35
|
-
--table-hover: #F9FAFB;
|
|
36
|
-
--nav-active-bg: #EDE9FE;
|
|
37
|
-
background-color: var(--bg-color);
|
|
38
|
-
color: var(--text-color);
|
|
39
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
40
|
-
transition: background-color 0.3s ease, color 0.3s ease;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/* Dark mode */
|
|
58
|
+
/* Dark Theme */
|
|
44
59
|
body.dark-mode {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
--card-bg: #24283B;
|
|
48
|
-
--sidebar-bg: #1F2130;
|
|
49
|
-
--sidebar-hover: #2A2D3E;
|
|
50
|
-
--border-color: #414868;
|
|
51
|
-
--table-hover: #2A2D3E;
|
|
52
|
-
--nav-active-bg: #2A2D3E;
|
|
60
|
+
background-color: var(--ctp-base);
|
|
61
|
+
color: var(--ctp-text);
|
|
53
62
|
}
|
|
54
63
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
64
|
+
/* Navbar */
|
|
65
|
+
.navbar {
|
|
66
|
+
background: linear-gradient(135deg, #8B5CF6, #6D28D9) !important;
|
|
67
|
+
color: white !important;
|
|
59
68
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
69
|
+
.navbar * {
|
|
70
|
+
color: white !important;
|
|
71
|
+
}
|
|
72
|
+
.navbar-brand {
|
|
73
|
+
font-weight: bold;
|
|
65
74
|
}
|
|
66
75
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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);
|
|
70
88
|
}
|
|
71
89
|
|
|
90
|
+
/* Sidebar */
|
|
72
91
|
.sidebar {
|
|
73
|
-
background:
|
|
92
|
+
background: white;
|
|
74
93
|
min-height: calc(100vh - 56px);
|
|
75
|
-
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);
|
|
76
98
|
}
|
|
77
|
-
|
|
78
99
|
.sidebar .nav-link {
|
|
79
|
-
color:
|
|
100
|
+
color: #1f2937;
|
|
80
101
|
padding: 0.75rem 1.5rem;
|
|
81
102
|
border-left: 3px solid transparent;
|
|
82
103
|
transition: all 0.2s;
|
|
83
104
|
}
|
|
84
|
-
|
|
105
|
+
body.dark-mode .sidebar .nav-link {
|
|
106
|
+
color: var(--ctp-text);
|
|
107
|
+
}
|
|
85
108
|
.sidebar .nav-link:hover {
|
|
86
|
-
background-color:
|
|
87
|
-
color:
|
|
88
|
-
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);
|
|
89
117
|
}
|
|
90
|
-
|
|
91
118
|
.sidebar .nav-link.active {
|
|
92
|
-
background-color:
|
|
93
|
-
color:
|
|
94
|
-
border-left-color:
|
|
119
|
+
background-color: #f3f4f6;
|
|
120
|
+
color: #8B5CF6;
|
|
121
|
+
border-left-color: #8B5CF6;
|
|
95
122
|
font-weight: 600;
|
|
96
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
|
+
}
|
|
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
|
+
}
|
|
97
274
|
|
|
275
|
+
/* Stat Cards */
|
|
98
276
|
.stat-card {
|
|
99
277
|
border-radius: 0.75rem;
|
|
100
|
-
border: none;
|
|
101
|
-
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
102
278
|
transition: transform 0.2s, box-shadow 0.2s;
|
|
103
279
|
}
|
|
104
|
-
|
|
105
280
|
.stat-card:hover {
|
|
106
281
|
transform: translateY(-2px);
|
|
107
|
-
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
282
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
108
283
|
}
|
|
109
284
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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;
|
|
113
300
|
}
|
|
114
301
|
|
|
115
|
-
.
|
|
116
|
-
color:
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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;
|
|
120
320
|
}
|
|
121
321
|
|
|
122
|
-
|
|
123
|
-
|
|
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);
|
|
124
336
|
}
|
|
125
337
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
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);
|
|
129
361
|
}
|
|
130
362
|
|
|
131
|
-
|
|
132
|
-
|
|
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);
|
|
133
383
|
}
|
|
134
384
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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;
|
|
138
408
|
}
|
|
139
409
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
color:
|
|
410
|
+
/* Chart canvas backgrounds */
|
|
411
|
+
body.dark-mode canvas {
|
|
412
|
+
background-color: transparent !important;
|
|
143
413
|
}
|
|
144
414
|
|
|
145
|
-
|
|
146
|
-
|
|
415
|
+
/* Chart labels and text */
|
|
416
|
+
body.dark-mode .chart-container text {
|
|
417
|
+
fill: var(--ctp-text) !important;
|
|
147
418
|
}
|
|
148
419
|
|
|
149
|
-
|
|
150
|
-
|
|
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;
|
|
151
425
|
}
|
|
152
426
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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);
|
|
156
438
|
}
|
|
157
439
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
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);
|
|
161
454
|
}
|
|
162
455
|
|
|
163
|
-
/*
|
|
164
|
-
.
|
|
165
|
-
background-color: var(--
|
|
166
|
-
|
|
167
|
-
|
|
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);
|
|
168
462
|
}
|
|
169
463
|
|
|
170
|
-
|
|
171
|
-
|
|
464
|
+
/* Horizontal rules */
|
|
465
|
+
body.dark-mode hr {
|
|
466
|
+
border-color: var(--ctp-surface2);
|
|
467
|
+
opacity: 1;
|
|
172
468
|
}
|
|
173
469
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
color: var(--
|
|
177
|
-
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);
|
|
178
478
|
}
|
|
179
479
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
color: var(--
|
|
183
|
-
|
|
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);
|
|
184
487
|
}
|
|
185
488
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
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);
|
|
189
495
|
}
|
|
190
496
|
|
|
191
|
-
|
|
192
|
-
|
|
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);
|
|
193
506
|
}
|
|
194
507
|
|
|
195
|
-
|
|
196
|
-
|
|
508
|
+
/* Tooltips */
|
|
509
|
+
body.dark-mode .tooltip-inner {
|
|
510
|
+
background-color: var(--ctp-surface0);
|
|
511
|
+
color: var(--ctp-text);
|
|
197
512
|
}
|
|
198
513
|
|
|
199
|
-
|
|
200
|
-
|
|
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);
|
|
201
525
|
}
|
|
202
526
|
|
|
203
|
-
|
|
204
|
-
|
|
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);
|
|
205
544
|
}
|
|
206
545
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
border-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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;
|
|
215
568
|
}
|
|
216
569
|
|
|
217
|
-
|
|
218
|
-
|
|
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);
|
|
219
592
|
}
|
|
220
593
|
|
|
221
|
-
|
|
222
|
-
|
|
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);
|
|
605
|
+
}
|
|
606
|
+
|
|
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;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/* Chart.js specific fixes for axis labels */
|
|
617
|
+
body.dark-mode .chartjs-render-monitor {
|
|
618
|
+
background-color: transparent !important;
|
|
619
|
+
}
|
|
620
|
+
|
|
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);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/* Stronger text colors for better visibility */
|
|
628
|
+
body.dark-mode .text-secondary {
|
|
629
|
+
color: var(--ctp-subtext1) !important;
|
|
630
|
+
}
|
|
631
|
+
|
|
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;
|
|
642
|
+
}
|
|
643
|
+
|
|
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;
|
|
657
|
+
}
|
|
658
|
+
|
|
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;
|
|
223
685
|
}
|
|
224
686
|
|
|
225
|
-
|
|
226
|
-
|
|
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;
|
|
227
691
|
}
|
|
228
692
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
693
|
+
/* ULTRA AGGRESSIVE - Chart.js axis labels and titles */
|
|
694
|
+
body.dark-mode canvas {
|
|
695
|
+
color: var(--ctp-text) !important;
|
|
696
|
+
}
|
|
697
|
+
|
|
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;
|
|
703
|
+
}
|
|
704
|
+
|
|
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;
|
|
233
726
|
}
|
|
234
727
|
</style>
|
|
235
728
|
</head>
|
|
@@ -238,16 +731,20 @@
|
|
|
238
731
|
<!-- Top Navbar -->
|
|
239
732
|
<nav class="navbar navbar-dark">
|
|
240
733
|
<div class="container-fluid">
|
|
241
|
-
<
|
|
242
|
-
<
|
|
243
|
-
|
|
244
|
-
|
|
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>
|
|
245
742
|
<div class="d-flex align-items-center gap-3">
|
|
246
|
-
<button class="theme-toggle" id="themeToggle"
|
|
743
|
+
<button class="theme-toggle" id="themeToggle">
|
|
247
744
|
<i class="bi bi-moon-fill" id="themeIcon"></i>
|
|
248
745
|
</button>
|
|
249
|
-
<div class="text-white">
|
|
250
|
-
<small><%= Rails.env.titleize
|
|
746
|
+
<div class="text-white d-none d-md-block">
|
|
747
|
+
<small><%= Rails.env.titleize %></small>
|
|
251
748
|
</div>
|
|
252
749
|
</div>
|
|
253
750
|
</div>
|
|
@@ -256,7 +753,7 @@
|
|
|
256
753
|
<div class="container-fluid">
|
|
257
754
|
<div class="row">
|
|
258
755
|
<!-- Sidebar -->
|
|
259
|
-
<nav class="col-md-2 d-md-block sidebar">
|
|
756
|
+
<nav class="col-md-2 d-none d-md-block sidebar">
|
|
260
757
|
<div class="position-sticky pt-3">
|
|
261
758
|
<ul class="nav flex-column">
|
|
262
759
|
<li class="nav-item">
|
|
@@ -276,15 +773,11 @@
|
|
|
276
773
|
</li>
|
|
277
774
|
</ul>
|
|
278
775
|
|
|
279
|
-
<
|
|
280
|
-
|
|
281
|
-
<h6 class="sidebar-heading px-3 mt-4 mb-2 text-muted text-uppercase">
|
|
282
|
-
<small>Quick Filters</small>
|
|
283
|
-
</h6>
|
|
776
|
+
<h6 class="mt-4">QUICK FILTERS</h6>
|
|
284
777
|
<ul class="nav flex-column">
|
|
285
778
|
<li class="nav-item">
|
|
286
|
-
<%= link_to errors_path(
|
|
287
|
-
<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
|
|
288
781
|
<% end %>
|
|
289
782
|
</li>
|
|
290
783
|
<li class="nav-item">
|
|
@@ -297,11 +790,6 @@
|
|
|
297
790
|
<i class="bi bi-phone"></i> Android Errors
|
|
298
791
|
<% end %>
|
|
299
792
|
</li>
|
|
300
|
-
<li class="nav-item">
|
|
301
|
-
<%= link_to errors_path(environment: 'production'), class: "nav-link" do %>
|
|
302
|
-
<i class="bi bi-server"></i> Production
|
|
303
|
-
<% end %>
|
|
304
|
-
</li>
|
|
305
793
|
</ul>
|
|
306
794
|
</div>
|
|
307
795
|
</nav>
|
|
@@ -316,36 +804,186 @@
|
|
|
316
804
|
<!-- Bootstrap JS -->
|
|
317
805
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
318
806
|
|
|
319
|
-
<!-- Theme Toggle
|
|
807
|
+
<!-- Pure JavaScript Theme Toggle -->
|
|
320
808
|
<script>
|
|
321
|
-
// Load theme
|
|
322
|
-
|
|
323
|
-
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');
|
|
324
812
|
if (savedTheme === 'dark') {
|
|
325
813
|
document.body.classList.add('dark-mode');
|
|
326
|
-
updateThemeIcon(true);
|
|
327
814
|
}
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
function toggleTheme() {
|
|
331
|
-
const body = document.body;
|
|
332
|
-
const isDark = body.classList.toggle('dark-mode');
|
|
333
|
-
|
|
334
|
-
// Save preference
|
|
335
|
-
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
|
815
|
+
})();
|
|
336
816
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
+
}
|
|
829
|
+
}
|
|
340
830
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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
|
+
}
|
|
347
947
|
}
|
|
348
|
-
|
|
948
|
+
|
|
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);
|
|
986
|
+
});
|
|
349
987
|
</script>
|
|
350
988
|
</body>
|
|
351
989
|
</html>
|