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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +66 -21
  3. data/app/assets/stylesheets/rails_error_dashboard/_catppuccin_mocha.scss +107 -0
  4. data/app/assets/stylesheets/rails_error_dashboard/_components.scss +625 -0
  5. data/app/assets/stylesheets/rails_error_dashboard/_layout.scss +257 -0
  6. data/app/assets/stylesheets/rails_error_dashboard/_theme_variables.scss +203 -0
  7. data/app/assets/stylesheets/rails_error_dashboard/application.css +926 -15
  8. data/app/assets/stylesheets/rails_error_dashboard/application.css.map +7 -0
  9. data/app/assets/stylesheets/rails_error_dashboard/application.scss +61 -0
  10. data/app/controllers/rails_error_dashboard/errors_controller.rb +94 -1
  11. data/app/helpers/rails_error_dashboard/application_helper.rb +42 -4
  12. data/app/helpers/rails_error_dashboard/backtrace_helper.rb +91 -0
  13. data/app/helpers/rails_error_dashboard/overview_helper.rb +78 -0
  14. data/app/helpers/rails_error_dashboard/user_agent_helper.rb +118 -0
  15. data/app/models/rails_error_dashboard/error_comment.rb +27 -0
  16. data/app/models/rails_error_dashboard/error_log.rb +145 -0
  17. data/app/views/layouts/rails_error_dashboard.html.erb +796 -299
  18. data/app/views/layouts/rails_error_dashboard_old_backup.html.erb +383 -0
  19. data/app/views/rails_error_dashboard/errors/_error_row.html.erb +2 -0
  20. data/app/views/rails_error_dashboard/errors/_pattern_insights.html.erb +4 -4
  21. data/app/views/rails_error_dashboard/errors/_timeline.html.erb +167 -0
  22. data/app/views/rails_error_dashboard/errors/analytics.html.erb +138 -22
  23. data/app/views/rails_error_dashboard/errors/index.html.erb +83 -4
  24. data/app/views/rails_error_dashboard/errors/overview.html.erb +253 -0
  25. data/app/views/rails_error_dashboard/errors/platform_comparison.html.erb +29 -18
  26. data/app/views/rails_error_dashboard/errors/show.html.erb +353 -54
  27. data/config/routes.rb +7 -0
  28. data/db/migrate/20251226020000_add_workflow_fields_to_error_logs.rb +27 -0
  29. data/db/migrate/20251226020100_create_error_comments.rb +18 -0
  30. data/lib/generators/rails_error_dashboard/install/install_generator.rb +8 -2
  31. data/lib/generators/rails_error_dashboard/install/templates/initializer.rb +21 -0
  32. data/lib/rails_error_dashboard/commands/batch_delete_errors.rb +1 -1
  33. data/lib/rails_error_dashboard/commands/batch_resolve_errors.rb +2 -2
  34. data/lib/rails_error_dashboard/commands/log_error.rb +47 -9
  35. data/lib/rails_error_dashboard/commands/resolve_error.rb +1 -1
  36. data/lib/rails_error_dashboard/configuration.rb +8 -0
  37. data/lib/rails_error_dashboard/error_reporter.rb +4 -4
  38. data/lib/rails_error_dashboard/logger.rb +105 -0
  39. data/lib/rails_error_dashboard/middleware/error_catcher.rb +2 -2
  40. data/lib/rails_error_dashboard/plugin.rb +3 -3
  41. data/lib/rails_error_dashboard/plugin_registry.rb +2 -2
  42. data/lib/rails_error_dashboard/plugins/jira_integration_plugin.rb +1 -1
  43. data/lib/rails_error_dashboard/plugins/metrics_plugin.rb +1 -1
  44. data/lib/rails_error_dashboard/queries/dashboard_stats.rb +109 -1
  45. data/lib/rails_error_dashboard/queries/errors_list.rb +61 -6
  46. data/lib/rails_error_dashboard/services/backtrace_parser.rb +113 -0
  47. data/lib/rails_error_dashboard/version.rb +1 -1
  48. data/lib/rails_error_dashboard.rb +2 -0
  49. metadata +18 -2
  50. 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">Backtrace:</h6>
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
- <div class="bg-dark text-light p-3 rounded" style="max-height: 400px; overflow-y: auto;">
50
- <pre class="mb-0"><code><%= @error.backtrace %></code></pre>
51
- </div>
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><small><%= @error.user_agent || 'N/A' %></small></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
- <% if @related_errors.any? %>
236
- <div class="card">
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"><i class="bi bi-collection"></i> Related Errors</h5>
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 p-0">
241
- <div class="table-responsive">
242
- <table class="table table-hover mb-0">
243
- <thead class="table-light">
244
- <tr>
245
- <th>Time</th>
246
- <th>Message</th>
247
- <th>Platform</th>
248
- <th>Status</th>
249
- <th></th>
250
- </tr>
251
- </thead>
252
- <tbody>
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
- </td>
277
- <td>
278
- <%= link_to "View", error_path(related), class: "btn btn-sm btn-outline-primary" %>
279
- </td>
280
- </tr>
281
- <% end %>
282
- </tbody>
283
- </table>
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.resolved? %>
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: docs/QUICKSTART.md", :light_black
287
- say "⚙️ To enable/disable features: config/initializers/rails_error_dashboard.rb", :light_black
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
- Rails.logger.error("Batch delete failed: #{e.message}")
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
- Rails.logger.error("Failed to resolve error #{error.id}: #{e.message}")
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
- Rails.logger.error("Batch resolve failed: #{e.message}")
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