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
|
@@ -44,11 +44,72 @@
|
|
|
44
44
|
<%= @error.message %>
|
|
45
45
|
</div>
|
|
46
46
|
|
|
47
|
-
<h6 class="text-muted mb-2 mt-4">
|
|
47
|
+
<h6 class="text-muted mb-2 mt-4">
|
|
48
|
+
Backtrace:
|
|
49
|
+
<% if @error.backtrace.present? %>
|
|
50
|
+
<% frames = parse_backtrace(@error.backtrace) %>
|
|
51
|
+
<% app_frames = filter_app_code(frames) %>
|
|
52
|
+
<% framework_frames = filter_framework_code(frames) %>
|
|
53
|
+
<small class="text-muted">
|
|
54
|
+
(<%= app_frames.count %> your code, <%= framework_frames.count %> framework/gems)
|
|
55
|
+
</small>
|
|
56
|
+
<% end %>
|
|
57
|
+
</h6>
|
|
48
58
|
<% if @error.backtrace.present? %>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
<% frames = parse_backtrace(@error.backtrace) %>
|
|
60
|
+
<% app_frames = filter_app_code(frames) %>
|
|
61
|
+
<% framework_frames = filter_framework_code(frames) %>
|
|
62
|
+
|
|
63
|
+
<% if app_frames.any? %>
|
|
64
|
+
<!-- Your Code Section (Expanded by default) -->
|
|
65
|
+
<div class="card mb-3">
|
|
66
|
+
<div class="card-header bg-success bg-opacity-10 border-success">
|
|
67
|
+
<div class="d-flex justify-content-between align-items-center">
|
|
68
|
+
<strong class="text-success">
|
|
69
|
+
<i class="bi bi-code-square"></i> Your Code
|
|
70
|
+
</strong>
|
|
71
|
+
<span class="badge bg-success"><%= app_frames.count %> frames</span>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
<div class="card-body p-0">
|
|
75
|
+
<div class="code-block p-3" style="max-height: 300px; overflow-y: auto; overflow-x: auto;">
|
|
76
|
+
<pre class="mb-0"><code><% app_frames.each do |frame| %><span class="<%= frame_bg_class(frame[:category]) %> d-block px-2 py-1"><span class="<%= frame_color_class(frame[:category]) %>"><%= frame_icon(frame[:category]) %> <%= frame[:short_path] %>:<%= frame[:line_number] %></span> in <span class="text-info">`<%= frame[:method_name] %>'</span></span>
|
|
77
|
+
<% end %></code></pre>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
<% end %>
|
|
82
|
+
|
|
83
|
+
<% if framework_frames.any? %>
|
|
84
|
+
<!-- Framework/Gem Code Section (Collapsed by default) -->
|
|
85
|
+
<div class="accordion" id="frameworkBacktraceAccordion">
|
|
86
|
+
<div class="accordion-item">
|
|
87
|
+
<h2 class="accordion-header">
|
|
88
|
+
<button class="accordion-button collapsed bg-light" type="button" data-bs-toggle="collapse" data-bs-target="#frameworkBacktrace" aria-expanded="false" aria-controls="frameworkBacktrace">
|
|
89
|
+
<i class="bi bi-gear me-2 text-warning"></i>
|
|
90
|
+
<strong>Framework & Gem Code</strong>
|
|
91
|
+
<span class="badge bg-secondary ms-2"><%= framework_frames.count %> frames</span>
|
|
92
|
+
<small class="text-muted ms-2">(click to expand)</small>
|
|
93
|
+
</button>
|
|
94
|
+
</h2>
|
|
95
|
+
<div id="frameworkBacktrace" class="accordion-collapse collapse" data-bs-parent="#frameworkBacktraceAccordion">
|
|
96
|
+
<div class="accordion-body p-0">
|
|
97
|
+
<div class="code-block p-3" style="max-height: 400px; overflow-y: auto; overflow-x: auto;">
|
|
98
|
+
<pre class="mb-0"><code><% framework_frames.each do |frame| %><span class="<%= frame_bg_class(frame[:category]) %> d-block px-2 py-1"><span class="<%= frame_color_class(frame[:category]) %>"><%= frame_icon(frame[:category]) %> <%= frame[:short_path] %>:<%= frame[:line_number] %></span> in <span class="text-info">`<%= frame[:method_name] %>'</span></span>
|
|
99
|
+
<% end %></code></pre>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
<% end %>
|
|
106
|
+
|
|
107
|
+
<% if app_frames.empty? && framework_frames.empty? %>
|
|
108
|
+
<!-- Fallback: Show raw backtrace if parsing failed -->
|
|
109
|
+
<div class="code-block p-3 rounded" style="max-height: 400px; overflow-y: auto; overflow-x: auto;">
|
|
110
|
+
<pre class="mb-0"><code><%= @error.backtrace %></code></pre>
|
|
111
|
+
</div>
|
|
112
|
+
<% end %>
|
|
52
113
|
<% else %>
|
|
53
114
|
<p class="text-muted">No backtrace available</p>
|
|
54
115
|
<% end %>
|
|
@@ -78,7 +139,28 @@
|
|
|
78
139
|
</tr>
|
|
79
140
|
<tr>
|
|
80
141
|
<th>User Agent:</th>
|
|
81
|
-
<td
|
|
142
|
+
<td>
|
|
143
|
+
<% if @error.user_agent.present? %>
|
|
144
|
+
<% ua_info = parse_user_agent(@error.user_agent) %>
|
|
145
|
+
<div class="mb-2">
|
|
146
|
+
<%= browser_icon(ua_info) %>
|
|
147
|
+
<strong><%= ua_info[:browser_name] %></strong>
|
|
148
|
+
<% if ua_info[:browser_version].present? %>
|
|
149
|
+
<span class="text-muted"><%= ua_info[:browser_version] %></span>
|
|
150
|
+
<% end %>
|
|
151
|
+
<span class="ms-2"><%= os_icon(ua_info) %> <%= ua_info[:os_name] %></span>
|
|
152
|
+
<span class="ms-2"><%= device_icon(ua_info) %> <%= ua_info[:device_type] %></span>
|
|
153
|
+
</div>
|
|
154
|
+
<details>
|
|
155
|
+
<summary class="text-muted" style="cursor: pointer;">
|
|
156
|
+
<small>Raw User Agent</small>
|
|
157
|
+
</summary>
|
|
158
|
+
<small class="text-muted"><%= @error.user_agent %></small>
|
|
159
|
+
</details>
|
|
160
|
+
<% else %>
|
|
161
|
+
<span class="text-muted">N/A</span>
|
|
162
|
+
<% end %>
|
|
163
|
+
</td>
|
|
82
164
|
</tr>
|
|
83
165
|
<tr>
|
|
84
166
|
<th>IP Address:</th>
|
|
@@ -231,56 +313,66 @@
|
|
|
231
313
|
<% end %>
|
|
232
314
|
<% end %>
|
|
233
315
|
|
|
234
|
-
<!-- Related Errors -->
|
|
235
|
-
|
|
236
|
-
|
|
316
|
+
<!-- Timeline: Related Errors -->
|
|
317
|
+
<%= render "timeline" %>
|
|
318
|
+
|
|
319
|
+
<!-- Phase 3: Comment Threads -->
|
|
320
|
+
<% if @error.respond_to?(:comments) %>
|
|
321
|
+
<div class="card mb-4">
|
|
237
322
|
<div class="card-header bg-white">
|
|
238
|
-
<h5 class="mb-0"
|
|
323
|
+
<h5 class="mb-0">
|
|
324
|
+
<i class="bi bi-chat-dots"></i> Discussion
|
|
325
|
+
<span class="badge bg-secondary ms-2"><%= @error.comments.count %></span>
|
|
326
|
+
</h5>
|
|
239
327
|
</div>
|
|
240
|
-
<div class="card-body
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
<
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
<% @related_errors.each do |related| %>
|
|
254
|
-
<tr>
|
|
255
|
-
<td><small><%= related.occurred_at.strftime("%m/%d %I:%M%p") %></small></td>
|
|
256
|
-
<td>
|
|
257
|
-
<div class="text-truncate" style="max-width: 400px;">
|
|
258
|
-
<%= related.message %>
|
|
259
|
-
</div>
|
|
260
|
-
</td>
|
|
261
|
-
<td>
|
|
262
|
-
<% if related.platform == 'iOS' %>
|
|
263
|
-
<span class="badge badge-ios">iOS</span>
|
|
264
|
-
<% elsif related.platform == 'Android' %>
|
|
265
|
-
<span class="badge badge-android">Android</span>
|
|
266
|
-
<% else %>
|
|
267
|
-
<span class="badge badge-api"><%= related.platform || 'API' %></span>
|
|
268
|
-
<% end %>
|
|
269
|
-
</td>
|
|
270
|
-
<td>
|
|
271
|
-
<% if related.resolved? %>
|
|
272
|
-
<i class="bi bi-check-circle-fill text-success"></i>
|
|
273
|
-
<% else %>
|
|
274
|
-
<i class="bi bi-exclamation-circle-fill text-danger"></i>
|
|
328
|
+
<div class="card-body">
|
|
329
|
+
<!-- Existing Comments -->
|
|
330
|
+
<% if @error.comments.recent_first.any? %>
|
|
331
|
+
<div class="mb-4">
|
|
332
|
+
<% @error.comments.recent_first.each do |comment| %>
|
|
333
|
+
<div class="border-bottom pb-3 mb-3">
|
|
334
|
+
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
335
|
+
<div>
|
|
336
|
+
<strong class="text-primary">
|
|
337
|
+
<i class="bi bi-person-circle"></i> <%= comment.author_name %>
|
|
338
|
+
</strong>
|
|
339
|
+
<% if comment.recent? %>
|
|
340
|
+
<span class="badge bg-success ms-2">New</span>
|
|
275
341
|
<% end %>
|
|
276
|
-
</
|
|
277
|
-
<
|
|
278
|
-
<%=
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
342
|
+
</div>
|
|
343
|
+
<small class="text-muted">
|
|
344
|
+
<%= comment.formatted_time %>
|
|
345
|
+
<span class="ms-1 text-muted">(<%= time_ago_in_words(comment.created_at) %> ago)</span>
|
|
346
|
+
</small>
|
|
347
|
+
</div>
|
|
348
|
+
<div class="text-break">
|
|
349
|
+
<%= simple_format(comment.body, class: "mb-0") %>
|
|
350
|
+
</div>
|
|
351
|
+
</div>
|
|
352
|
+
<% end %>
|
|
353
|
+
</div>
|
|
354
|
+
<% else %>
|
|
355
|
+
<p class="text-muted mb-4">
|
|
356
|
+
<i class="bi bi-info-circle"></i> No comments yet. Start the discussion below.
|
|
357
|
+
</p>
|
|
358
|
+
<% end %>
|
|
359
|
+
|
|
360
|
+
<!-- Add Comment Form -->
|
|
361
|
+
<div class="border-top pt-3">
|
|
362
|
+
<h6 class="mb-3">
|
|
363
|
+
<i class="bi bi-plus-circle"></i> Add Comment
|
|
364
|
+
</h6>
|
|
365
|
+
<%= form_with url: add_comment_error_path(@error), method: :post do |f| %>
|
|
366
|
+
<div class="mb-3">
|
|
367
|
+
<label for="author_name" class="form-label">Your Name <span class="text-danger">*</span></label>
|
|
368
|
+
<%= text_field_tag :author_name, @error.assigned_to, class: "form-control", placeholder: "e.g., John Doe", required: true %>
|
|
369
|
+
</div>
|
|
370
|
+
<div class="mb-3">
|
|
371
|
+
<label for="body" class="form-label">Comment <span class="text-danger">*</span></label>
|
|
372
|
+
<%= text_area_tag :body, nil, class: "form-control", rows: 4, placeholder: "Share your thoughts, findings, or updates...", required: true %>
|
|
373
|
+
</div>
|
|
374
|
+
<%= submit_tag "Post Comment", class: "btn btn-primary" %>
|
|
375
|
+
<% end %>
|
|
284
376
|
</div>
|
|
285
377
|
</div>
|
|
286
378
|
</div>
|
|
@@ -494,9 +586,14 @@
|
|
|
494
586
|
<% end %>
|
|
495
587
|
</div>
|
|
496
588
|
|
|
589
|
+
<!-- Phase 3: Workflow Status -->
|
|
497
590
|
<div class="mb-3">
|
|
498
|
-
<small class="text-muted d-block mb-1">Status</small>
|
|
499
|
-
<% if @error.
|
|
591
|
+
<small class="text-muted d-block mb-1">Workflow Status</small>
|
|
592
|
+
<% if @error.respond_to?(:status) %>
|
|
593
|
+
<span class="badge bg-<%= @error.status_badge_color %> fs-6">
|
|
594
|
+
<%= @error.status.titleize %>
|
|
595
|
+
</span>
|
|
596
|
+
<% elsif @error.resolved? %>
|
|
500
597
|
<span class="badge bg-success">
|
|
501
598
|
<i class="bi bi-check-circle"></i> Resolved
|
|
502
599
|
</span>
|
|
@@ -513,6 +610,67 @@
|
|
|
513
610
|
<% end %>
|
|
514
611
|
</div>
|
|
515
612
|
|
|
613
|
+
<!-- Phase 3: Assignment -->
|
|
614
|
+
<% if @error.respond_to?(:assigned_to) %>
|
|
615
|
+
<div class="mb-3">
|
|
616
|
+
<small class="text-muted d-block mb-1">Assigned To</small>
|
|
617
|
+
<% if @error.assigned? %>
|
|
618
|
+
<div class="d-flex align-items-center justify-content-between">
|
|
619
|
+
<div>
|
|
620
|
+
<i class="bi bi-person-fill text-primary"></i>
|
|
621
|
+
<strong><%= @error.assigned_to %></strong>
|
|
622
|
+
<% if @error.assigned_at.present? %>
|
|
623
|
+
<br>
|
|
624
|
+
<small class="text-muted">
|
|
625
|
+
<%= time_ago_in_words(@error.assigned_at) %> ago
|
|
626
|
+
</small>
|
|
627
|
+
<% end %>
|
|
628
|
+
</div>
|
|
629
|
+
<%= button_to unassign_error_path(@error), method: :patch, class: "btn btn-sm btn-outline-secondary",
|
|
630
|
+
data: { turbo_confirm: "Remove assignment?" } do %>
|
|
631
|
+
<i class="bi bi-x"></i>
|
|
632
|
+
<% end %>
|
|
633
|
+
</div>
|
|
634
|
+
<% else %>
|
|
635
|
+
<button type="button" class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#assignModal">
|
|
636
|
+
<i class="bi bi-person-plus"></i> Assign
|
|
637
|
+
</button>
|
|
638
|
+
<% end %>
|
|
639
|
+
</div>
|
|
640
|
+
|
|
641
|
+
<!-- Phase 3: Priority -->
|
|
642
|
+
<div class="mb-3">
|
|
643
|
+
<small class="text-muted d-block mb-1">Priority</small>
|
|
644
|
+
<% if @error.respond_to?(:priority_level) %>
|
|
645
|
+
<span class="badge bg-<%= @error.priority_color %> fs-6">
|
|
646
|
+
<%= @error.priority_label %>
|
|
647
|
+
</span>
|
|
648
|
+
<button type="button" class="btn btn-sm btn-outline-secondary ms-2" data-bs-toggle="modal" data-bs-target="#priorityModal">
|
|
649
|
+
<i class="bi bi-pencil"></i>
|
|
650
|
+
</button>
|
|
651
|
+
<% end %>
|
|
652
|
+
</div>
|
|
653
|
+
|
|
654
|
+
<!-- Phase 3: Snooze -->
|
|
655
|
+
<div class="mb-3">
|
|
656
|
+
<small class="text-muted d-block mb-1">Snooze</small>
|
|
657
|
+
<% if @error.respond_to?(:snoozed?) && @error.snoozed? %>
|
|
658
|
+
<div class="alert alert-warning py-2 mb-2">
|
|
659
|
+
<i class="bi bi-alarm"></i>
|
|
660
|
+
<strong>Snoozed</strong><br>
|
|
661
|
+
<small>Until <%= @error.snoozed_until&.strftime("%b %d at %I:%M %p") %></small>
|
|
662
|
+
</div>
|
|
663
|
+
<%= button_to unsnooze_error_path(@error), method: :patch, class: "btn btn-sm btn-outline-warning" do %>
|
|
664
|
+
<i class="bi bi-alarm-fill"></i> Unsnooze
|
|
665
|
+
<% end %>
|
|
666
|
+
<% else %>
|
|
667
|
+
<button type="button" class="btn btn-sm btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#snoozeModal">
|
|
668
|
+
<i class="bi bi-alarm"></i> Snooze
|
|
669
|
+
</button>
|
|
670
|
+
<% end %>
|
|
671
|
+
</div>
|
|
672
|
+
<% end %>
|
|
673
|
+
|
|
516
674
|
<% if @error.resolved? && @error.resolved_by_name.present? %>
|
|
517
675
|
<div class="mb-3">
|
|
518
676
|
<small class="text-muted d-block mb-1">Resolved By</small>
|
|
@@ -536,6 +694,44 @@
|
|
|
536
694
|
</div>
|
|
537
695
|
<% end %>
|
|
538
696
|
|
|
697
|
+
<!-- Environment Information -->
|
|
698
|
+
<div class="mb-3">
|
|
699
|
+
<small class="text-muted d-block mb-2">
|
|
700
|
+
<i class="bi bi-gear"></i> Environment
|
|
701
|
+
</small>
|
|
702
|
+
|
|
703
|
+
<% if @error.app_version.present? %>
|
|
704
|
+
<div class="mb-1">
|
|
705
|
+
<small class="text-muted">App Version:</small>
|
|
706
|
+
<code class="ms-1"><%= @error.app_version %></code>
|
|
707
|
+
</div>
|
|
708
|
+
<% end %>
|
|
709
|
+
|
|
710
|
+
<% if @error.git_sha.present? %>
|
|
711
|
+
<div class="mb-1">
|
|
712
|
+
<small class="text-muted">Git SHA:</small>
|
|
713
|
+
<code class="ms-1"><%= @error.git_sha[0..7] %></code>
|
|
714
|
+
</div>
|
|
715
|
+
<% end %>
|
|
716
|
+
|
|
717
|
+
<div class="mb-1">
|
|
718
|
+
<small class="text-muted">Rails:</small>
|
|
719
|
+
<code class="ms-1"><%= Rails.version %></code>
|
|
720
|
+
</div>
|
|
721
|
+
|
|
722
|
+
<div class="mb-1">
|
|
723
|
+
<small class="text-muted">Ruby:</small>
|
|
724
|
+
<code class="ms-1"><%= RUBY_VERSION %></code>
|
|
725
|
+
</div>
|
|
726
|
+
|
|
727
|
+
<div class="mb-1">
|
|
728
|
+
<small class="text-muted">Environment:</small>
|
|
729
|
+
<span class="badge bg-<%= Rails.env.production? ? 'danger' : Rails.env.development? ? 'success' : 'warning' %> ms-1">
|
|
730
|
+
<%= Rails.env.titleize %>
|
|
731
|
+
</span>
|
|
732
|
+
</div>
|
|
733
|
+
</div>
|
|
734
|
+
|
|
539
735
|
<div>
|
|
540
736
|
<small class="text-muted d-block mb-1">Error ID</small>
|
|
541
737
|
<code><%= @error.id %></code>
|
|
@@ -709,3 +905,106 @@
|
|
|
709
905
|
</div>
|
|
710
906
|
</div>
|
|
711
907
|
</div>
|
|
908
|
+
|
|
909
|
+
<!-- Phase 3: Assignment Modal -->
|
|
910
|
+
<div class="modal fade" id="assignModal" tabindex="-1" aria-labelledby="assignModalLabel" aria-hidden="true">
|
|
911
|
+
<div class="modal-dialog">
|
|
912
|
+
<div class="modal-content">
|
|
913
|
+
<%= form_with url: assign_error_path(@error), method: :patch do |f| %>
|
|
914
|
+
<div class="modal-header">
|
|
915
|
+
<h5 class="modal-title" id="assignModalLabel">
|
|
916
|
+
<i class="bi bi-person-plus"></i> Assign Error
|
|
917
|
+
</h5>
|
|
918
|
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
919
|
+
</div>
|
|
920
|
+
<div class="modal-body">
|
|
921
|
+
<div class="mb-3">
|
|
922
|
+
<label for="assigned_to" class="form-label">Assign To <span class="text-danger">*</span></label>
|
|
923
|
+
<%= text_field_tag :assigned_to, nil, class: "form-control", placeholder: "e.g., John Doe", required: true, autofocus: true %>
|
|
924
|
+
<small class="text-muted">Who should investigate this error?</small>
|
|
925
|
+
</div>
|
|
926
|
+
</div>
|
|
927
|
+
<div class="modal-footer">
|
|
928
|
+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
929
|
+
<%= submit_tag "Assign", class: "btn btn-primary" %>
|
|
930
|
+
</div>
|
|
931
|
+
<% end %>
|
|
932
|
+
</div>
|
|
933
|
+
</div>
|
|
934
|
+
</div>
|
|
935
|
+
|
|
936
|
+
<!-- Phase 3: Priority Modal -->
|
|
937
|
+
<div class="modal fade" id="priorityModal" tabindex="-1" aria-labelledby="priorityModalLabel" aria-hidden="true">
|
|
938
|
+
<div class="modal-dialog">
|
|
939
|
+
<div class="modal-content">
|
|
940
|
+
<%= form_with url: update_priority_error_path(@error), method: :patch do |f| %>
|
|
941
|
+
<div class="modal-header">
|
|
942
|
+
<h5 class="modal-title" id="priorityModalLabel">
|
|
943
|
+
<i class="bi bi-flag"></i> Update Priority
|
|
944
|
+
</h5>
|
|
945
|
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
946
|
+
</div>
|
|
947
|
+
<div class="modal-body">
|
|
948
|
+
<div class="mb-3">
|
|
949
|
+
<label for="priority_level" class="form-label">Priority Level <span class="text-danger">*</span></label>
|
|
950
|
+
<%= select_tag :priority_level,
|
|
951
|
+
options_for_select([
|
|
952
|
+
['Critical (P3)', 3],
|
|
953
|
+
['High (P2)', 2],
|
|
954
|
+
['Medium (P1)', 1],
|
|
955
|
+
['Low (P0)', 0]
|
|
956
|
+
], @error.priority_level),
|
|
957
|
+
class: "form-select", required: true %>
|
|
958
|
+
<small class="text-muted">Current: <%= @error.priority_label %></small>
|
|
959
|
+
</div>
|
|
960
|
+
</div>
|
|
961
|
+
<div class="modal-footer">
|
|
962
|
+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
963
|
+
<%= submit_tag "Update Priority", class: "btn btn-warning" %>
|
|
964
|
+
</div>
|
|
965
|
+
<% end %>
|
|
966
|
+
</div>
|
|
967
|
+
</div>
|
|
968
|
+
</div>
|
|
969
|
+
|
|
970
|
+
<!-- Phase 3: Snooze Modal -->
|
|
971
|
+
<div class="modal fade" id="snoozeModal" tabindex="-1" aria-labelledby="snoozeModalLabel" aria-hidden="true">
|
|
972
|
+
<div class="modal-dialog">
|
|
973
|
+
<div class="modal-content">
|
|
974
|
+
<%= form_with url: snooze_error_path(@error), method: :patch do |f| %>
|
|
975
|
+
<div class="modal-header">
|
|
976
|
+
<h5 class="modal-title" id="snoozeModalLabel">
|
|
977
|
+
<i class="bi bi-alarm"></i> Snooze Error
|
|
978
|
+
</h5>
|
|
979
|
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
980
|
+
</div>
|
|
981
|
+
<div class="modal-body">
|
|
982
|
+
<div class="mb-3">
|
|
983
|
+
<label for="hours" class="form-label">Snooze Duration <span class="text-danger">*</span></label>
|
|
984
|
+
<%= select_tag :hours,
|
|
985
|
+
options_for_select([
|
|
986
|
+
['1 hour', 1],
|
|
987
|
+
['4 hours', 4],
|
|
988
|
+
['8 hours', 8],
|
|
989
|
+
['24 hours (1 day)', 24],
|
|
990
|
+
['48 hours (2 days)', 48],
|
|
991
|
+
['168 hours (1 week)', 168]
|
|
992
|
+
], 24),
|
|
993
|
+
class: "form-select", required: true %>
|
|
994
|
+
<small class="text-muted">Hide this error from active views</small>
|
|
995
|
+
</div>
|
|
996
|
+
|
|
997
|
+
<div class="mb-3">
|
|
998
|
+
<label for="reason" class="form-label">Reason (Optional)</label>
|
|
999
|
+
<%= text_area_tag :reason, nil, class: "form-control", rows: 3, placeholder: "Why are you snoozing this error?" %>
|
|
1000
|
+
<small class="text-muted">Reason will be added as a comment</small>
|
|
1001
|
+
</div>
|
|
1002
|
+
</div>
|
|
1003
|
+
<div class="modal-footer">
|
|
1004
|
+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
1005
|
+
<%= submit_tag "Snooze", class: "btn btn-warning" %>
|
|
1006
|
+
</div>
|
|
1007
|
+
<% end %>
|
|
1008
|
+
</div>
|
|
1009
|
+
</div>
|
|
1010
|
+
</div>
|
data/config/routes.rb
CHANGED
|
@@ -4,6 +4,13 @@ RailsErrorDashboard::Engine.routes.draw do
|
|
|
4
4
|
resources :errors, only: [ :index, :show ] do
|
|
5
5
|
member do
|
|
6
6
|
post :resolve
|
|
7
|
+
post :assign
|
|
8
|
+
post :unassign
|
|
9
|
+
post :update_priority
|
|
10
|
+
post :snooze
|
|
11
|
+
post :unsnooze
|
|
12
|
+
post :update_status
|
|
13
|
+
post :add_comment
|
|
7
14
|
end
|
|
8
15
|
collection do
|
|
9
16
|
get :analytics
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddWorkflowFieldsToErrorLogs < ActiveRecord::Migration[8.0]
|
|
4
|
+
def change
|
|
5
|
+
add_column :rails_error_dashboard_error_logs, :status, :string, default: 'new', null: false
|
|
6
|
+
add_column :rails_error_dashboard_error_logs, :assigned_to, :string
|
|
7
|
+
add_column :rails_error_dashboard_error_logs, :assigned_at, :datetime
|
|
8
|
+
add_column :rails_error_dashboard_error_logs, :snoozed_until, :datetime
|
|
9
|
+
add_column :rails_error_dashboard_error_logs, :priority_level, :integer, default: 0, null: false
|
|
10
|
+
|
|
11
|
+
add_index :rails_error_dashboard_error_logs, :status
|
|
12
|
+
add_index :rails_error_dashboard_error_logs, :assigned_to
|
|
13
|
+
add_index :rails_error_dashboard_error_logs, :snoozed_until
|
|
14
|
+
add_index :rails_error_dashboard_error_logs, :priority_level
|
|
15
|
+
|
|
16
|
+
# Update existing resolved errors to have status='resolved'
|
|
17
|
+
reversible do |dir|
|
|
18
|
+
dir.up do
|
|
19
|
+
execute <<-SQL
|
|
20
|
+
UPDATE rails_error_dashboard_error_logs
|
|
21
|
+
SET status = 'resolved'
|
|
22
|
+
WHERE resolved = true
|
|
23
|
+
SQL
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateErrorComments < ActiveRecord::Migration[8.0]
|
|
4
|
+
def change
|
|
5
|
+
create_table :rails_error_dashboard_error_comments do |t|
|
|
6
|
+
t.references :error_log,
|
|
7
|
+
null: false,
|
|
8
|
+
foreign_key: { to_table: :rails_error_dashboard_error_logs }
|
|
9
|
+
t.string :author_name, null: false
|
|
10
|
+
t.text :body, null: false
|
|
11
|
+
|
|
12
|
+
t.timestamps
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
add_index :rails_error_dashboard_error_comments, [ :error_log_id, :created_at ],
|
|
16
|
+
name: 'index_error_comments_on_error_and_time'
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -44,6 +44,7 @@ module RailsErrorDashboard
|
|
|
44
44
|
|
|
45
45
|
def select_optional_features
|
|
46
46
|
return unless options[:interactive] && behavior == :invoke
|
|
47
|
+
return unless $stdin.tty? # Skip interactive mode if not running in a terminal
|
|
47
48
|
|
|
48
49
|
say "Let's configure optional features...\n", :cyan
|
|
49
50
|
say "(You can always enable/disable these later in the initializer)\n\n", :yellow
|
|
@@ -283,8 +284,13 @@ module RailsErrorDashboard
|
|
|
283
284
|
say " 3. Restart your Rails server"
|
|
284
285
|
say " 4. Visit http://localhost:3000/error_dashboard"
|
|
285
286
|
say "\n"
|
|
286
|
-
say "📖 Documentation:
|
|
287
|
-
say "
|
|
287
|
+
say "📖 Documentation:", :light_black
|
|
288
|
+
say " • Quick Start: docs/QUICKSTART.md", :light_black
|
|
289
|
+
say " • Complete Feature Guide: docs/FEATURES.md", :light_black
|
|
290
|
+
say " • All Docs: docs/README.md", :light_black
|
|
291
|
+
say "\n"
|
|
292
|
+
say "⚙️ To enable/disable features later:", :light_black
|
|
293
|
+
say " Edit config/initializers/rails_error_dashboard.rb", :light_black
|
|
288
294
|
say "\n"
|
|
289
295
|
end
|
|
290
296
|
|
|
@@ -261,6 +261,27 @@ RailsErrorDashboard.configure do |config|
|
|
|
261
261
|
config.enable_occurrence_patterns = false
|
|
262
262
|
|
|
263
263
|
<% end -%>
|
|
264
|
+
# ============================================================================
|
|
265
|
+
# INTERNAL LOGGING (Silent by Default)
|
|
266
|
+
# ============================================================================
|
|
267
|
+
# Rails Error Dashboard logging is SILENT by default to keep your logs clean.
|
|
268
|
+
# Enable only for debugging gem issues or troubleshooting setup.
|
|
269
|
+
|
|
270
|
+
# Enable internal logging (default: false - silent)
|
|
271
|
+
config.enable_internal_logging = false
|
|
272
|
+
|
|
273
|
+
# Log level (default: :silent)
|
|
274
|
+
# Options: :debug, :info, :warn, :error, :silent
|
|
275
|
+
config.log_level = :silent
|
|
276
|
+
|
|
277
|
+
# Example: Enable verbose logging for debugging
|
|
278
|
+
# config.enable_internal_logging = true
|
|
279
|
+
# config.log_level = :debug
|
|
280
|
+
|
|
281
|
+
# Example: Log only errors (troubleshooting)
|
|
282
|
+
# config.enable_internal_logging = true
|
|
283
|
+
# config.log_level = :error
|
|
284
|
+
|
|
264
285
|
# ============================================================================
|
|
265
286
|
# ADDITIONAL CONFIGURATION
|
|
266
287
|
# ============================================================================
|
|
@@ -32,7 +32,7 @@ module RailsErrorDashboard
|
|
|
32
32
|
errors: []
|
|
33
33
|
}
|
|
34
34
|
rescue => e
|
|
35
|
-
|
|
35
|
+
RailsErrorDashboard::Logger.error("Batch delete failed: #{e.message}")
|
|
36
36
|
{ success: false, count: 0, total: @error_ids.size, errors: [ e.message ] }
|
|
37
37
|
end
|
|
38
38
|
end
|
|
@@ -37,7 +37,7 @@ module RailsErrorDashboard
|
|
|
37
37
|
resolved_errors << error
|
|
38
38
|
rescue => e
|
|
39
39
|
failed_ids << error.id
|
|
40
|
-
|
|
40
|
+
RailsErrorDashboard::Logger.error("Failed to resolve error #{error.id}: #{e.message}")
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
|
|
@@ -52,7 +52,7 @@ module RailsErrorDashboard
|
|
|
52
52
|
errors: failed_ids.empty? ? [] : [ "Failed to resolve #{failed_ids.size} error(s)" ]
|
|
53
53
|
}
|
|
54
54
|
rescue => e
|
|
55
|
-
|
|
55
|
+
RailsErrorDashboard::Logger.error("Batch resolve failed: #{e.message}")
|
|
56
56
|
{ success: false, count: 0, total: @error_ids.size, errors: [ e.message ] }
|
|
57
57
|
end
|
|
58
58
|
end
|