rails_error_dashboard 0.1.14 → 0.1.15

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1f6863e7b28220e7b384b6a2f27d88e2f996187fdff0947683eda7cbf28055d
4
- data.tar.gz: 00f620f673e8b900c242e3dce814c1068619c2ad5345e8cf37cf22b2753d65ee
3
+ metadata.gz: da18321511a53167fa248ae72974c6c15807a725396aabd27af1059296ecbc46
4
+ data.tar.gz: 84f348d386b6b07375797275dedf97e5a9504522270447bebd6b56307f5e4a7d
5
5
  SHA512:
6
- metadata.gz: bc9f07b4ff7765d9446f2e323da276abd1e5e1c35c4ed2c68af813a6ccf853d70fc0c4109ae41609913f554f6633cd89a887065c8f7adcd4a843116440e697c9
7
- data.tar.gz: 2defe3dbcf36a20b83e1faf1da908fe4a731072ea981dc6bee568ecf8bfde45ec0eaa46aa1ae77726e7bb5ae34b96bdd429b515280e54beeea581804120d4855
6
+ metadata.gz: b5b3d045f7a0b7bcae61823aa57e373111b6dab59e5a651ae5a33df9a3f043598e6469195b9cb508e79ad6bda62182877572888dba3cea37a4039715eee79349
7
+ data.tar.gz: 460491a0614445e91f098dbb792d86d9d7c8616f0456ad88cd6b14fdf27aba4d87990b080bd6c289eafe03befccceec94b6b23dcce29d850fd05b3bda946d39d
@@ -783,12 +783,30 @@
783
783
  <button class="btn btn-link text-white d-md-none me-2" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMenu">
784
784
  <i class="bi bi-list fs-4"></i>
785
785
  </button>
786
- <a class="navbar-brand" href="<%= main_app.root_path %>" title="Back to <%= Rails.application.class.module_parent_name %>">
787
- <i class="bi bi-bug-fill"></i>
788
- <span class="d-none d-sm-inline"><%= Rails.application.class.module_parent_name %></span>
789
- <span class="d-none d-md-inline text-white-50 mx-2">|</span>
790
- <span class="d-none d-md-inline">Error Dashboard</span>
791
- </a>
786
+ <%
787
+ # Check if main app has a root route defined
788
+ begin
789
+ root_url = main_app.root_path
790
+ has_root = true
791
+ rescue NoMethodError
792
+ has_root = false
793
+ end
794
+ %>
795
+ <% if has_root %>
796
+ <a class="navbar-brand" href="<%= root_url %>" title="Back to <%= Rails.application.class.module_parent_name %>">
797
+ <i class="bi bi-bug-fill"></i>
798
+ <span class="d-none d-sm-inline"><%= Rails.application.class.module_parent_name %></span>
799
+ <span class="d-none d-md-inline text-white-50 mx-2">|</span>
800
+ <span class="d-none d-md-inline">Error Dashboard</span>
801
+ </a>
802
+ <% else %>
803
+ <span class="navbar-brand">
804
+ <i class="bi bi-bug-fill"></i>
805
+ <span class="d-none d-sm-inline"><%= Rails.application.class.module_parent_name %></span>
806
+ <span class="d-none d-md-inline text-white-50 mx-2">|</span>
807
+ <span class="d-none d-md-inline">Error Dashboard</span>
808
+ </span>
809
+ <% end %>
792
810
  </div>
793
811
  <div class="d-flex align-items-center gap-3">
794
812
  <button class="theme-toggle" id="themeToggle">
@@ -867,6 +885,43 @@
867
885
  </div>
868
886
  </div>
869
887
 
888
+ <!-- Keyboard Shortcuts Modal -->
889
+ <div class="modal fade" id="keyboardShortcutsModal" tabindex="-1" aria-labelledby="keyboardShortcutsModalLabel" aria-hidden="true">
890
+ <div class="modal-dialog modal-dialog-centered">
891
+ <div class="modal-content">
892
+ <div class="modal-header">
893
+ <h5 class="modal-title" id="keyboardShortcutsModalLabel">
894
+ <i class="bi bi-keyboard"></i> Keyboard Shortcuts
895
+ </h5>
896
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
897
+ </div>
898
+ <div class="modal-body">
899
+ <div class="list-group list-group-flush">
900
+ <div class="list-group-item d-flex justify-content-between align-items-center">
901
+ <span><i class="bi bi-arrow-clockwise text-primary"></i> Refresh page</span>
902
+ <kbd class="bg-secondary text-white px-2 py-1 rounded">R</kbd>
903
+ </div>
904
+ <div class="list-group-item d-flex justify-content-between align-items-center">
905
+ <span><i class="bi bi-search text-primary"></i> Focus search</span>
906
+ <kbd class="bg-secondary text-white px-2 py-1 rounded">/</kbd>
907
+ </div>
908
+ <div class="list-group-item d-flex justify-content-between align-items-center">
909
+ <span><i class="bi bi-graph-up text-primary"></i> Go to analytics</span>
910
+ <kbd class="bg-secondary text-white px-2 py-1 rounded">A</kbd>
911
+ </div>
912
+ <div class="list-group-item d-flex justify-content-between align-items-center">
913
+ <span><i class="bi bi-question-circle text-primary"></i> Show this help</span>
914
+ <kbd class="bg-secondary text-white px-2 py-1 rounded">?</kbd>
915
+ </div>
916
+ </div>
917
+ </div>
918
+ <div class="modal-footer">
919
+ <small class="text-muted">Press <kbd class="bg-secondary text-white px-2 py-1 rounded">?</kbd> anytime to show shortcuts</small>
920
+ </div>
921
+ </div>
922
+ </div>
923
+ </div>
924
+
870
925
  <!-- Bootstrap JS -->
871
926
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
872
927
 
@@ -19,6 +19,9 @@
19
19
  </td>
20
20
  <td onclick="window.location='<%= error_path(error) %>';">
21
21
  <code class="text-danger" data-bs-toggle="tooltip" title="<%= error.error_type %>"><%= error.error_type.split('::').last %></code>
22
+ <% if error.recent? %>
23
+ <span class="badge bg-success ms-1" data-bs-toggle="tooltip" title="Error occurred within the last hour">NEW</span>
24
+ <% end %>
22
25
  <% if error.respond_to?(:app_version) && error.app_version.present? %>
23
26
  <br><small class="badge bg-light text-dark" data-bs-toggle="tooltip" title="App version when error occurred">v<%= error.app_version %></small>
24
27
  <% end %>
@@ -1,6 +1,6 @@
1
1
  <%# Timeline view for related errors leading up to this error %>
2
2
  <% if @related_errors.any? %>
3
- <div class="card">
3
+ <div class="card" id="timeline">
4
4
  <div class="card-header bg-white">
5
5
  <h5 class="mb-0">
6
6
  <i class="bi bi-clock-history"></i> Timeline
@@ -530,11 +530,8 @@
530
530
  // '?' - Show keyboard shortcuts help
531
531
  if (e.key === '?') {
532
532
  e.preventDefault();
533
- alert('Keyboard Shortcuts:\n\n' +
534
- 'r - Refresh page\n' +
535
- '/ - Focus search\n' +
536
- 'a - Analytics page\n' +
537
- '? - Show this help');
533
+ const modal = new bootstrap.Modal(document.getElementById('keyboardShortcutsModal'));
534
+ modal.show();
538
535
  }
539
536
  });
540
537
 
@@ -570,4 +567,12 @@
570
567
  });
571
568
  }
572
569
  });
570
+
571
+ // Update browser tab title with unresolved error count
572
+ document.addEventListener('DOMContentLoaded', function() {
573
+ const unresolvedCount = <%= @stats[:unresolved] || 0 %>;
574
+ if (unresolvedCount > 0) {
575
+ document.title = `(${unresolvedCount}) ${document.title}`;
576
+ }
577
+ });
573
578
  </script>
@@ -26,7 +26,13 @@
26
26
  <% end %>
27
27
  </h2>
28
28
  </div>
29
- <div>
29
+ <div class="d-flex gap-2">
30
+ <button type="button" class="btn btn-outline-secondary" onclick="downloadErrorJSON()" title="Download error details as JSON">
31
+ <i class="bi bi-download"></i> Export JSON
32
+ </button>
33
+ <button type="button" class="btn btn-outline-primary" onclick="copyErrorURL()" title="Copy shareable link to this error">
34
+ <i class="bi bi-share"></i> Share
35
+ </button>
30
36
  <% if @error.resolved? %>
31
37
  <span class="badge bg-success fs-6">
32
38
  <i class="bi bi-check-circle"></i> Resolved
@@ -39,6 +45,80 @@
39
45
  </div>
40
46
  </div>
41
47
 
48
+ <script>
49
+ function copyErrorURL() {
50
+ const url = window.location.href;
51
+ navigator.clipboard.writeText(url).then(() => {
52
+ // Change button text temporarily
53
+ const button = event.currentTarget;
54
+ const originalHTML = button.innerHTML;
55
+ button.innerHTML = '<i class="bi bi-check"></i> Copied!';
56
+ button.classList.remove('btn-outline-primary');
57
+ button.classList.add('btn-success');
58
+
59
+ setTimeout(() => {
60
+ button.innerHTML = originalHTML;
61
+ button.classList.remove('btn-success');
62
+ button.classList.add('btn-outline-primary');
63
+ }, 2000);
64
+ }).catch(err => {
65
+ alert('Failed to copy URL: ' + err);
66
+ });
67
+ }
68
+
69
+ function downloadErrorJSON() {
70
+ const errorData = {
71
+ id: <%= @error.id %>,
72
+ error_type: <%= raw @error.error_type.to_json %>,
73
+ message: <%= raw @error.message.to_json %>,
74
+ backtrace: <%= raw @error.backtrace.to_json %>,
75
+ occurred_at: <%= raw @error.occurred_at.to_json %>,
76
+ first_seen_at: <%= raw @error.first_seen_at.to_json %>,
77
+ last_seen_at: <%= raw @error.last_seen_at.to_json %>,
78
+ occurrence_count: <%= @error.occurrence_count %>,
79
+ resolved: <%= @error.resolved? %>,
80
+ resolved_at: <%= raw @error.resolved_at.to_json %>,
81
+ resolved_by_name: <%= raw @error.resolved_by_name.to_json %>,
82
+ platform: <%= raw @error.platform.to_json %>,
83
+ user_id: <%= raw @error.user_id.to_json %>,
84
+ severity: <%= raw @error.severity.to_json %>,
85
+ priority_level: <%= @error.priority_level || 0 %>,
86
+ <% if @error.respond_to?(:app_version) %>
87
+ app_version: <%= raw @error.app_version.to_json %>,
88
+ <% end %>
89
+ <% if @error.respond_to?(:git_commit) %>
90
+ git_commit: <%= raw @error.git_commit.to_json %>,
91
+ <% end %>
92
+ created_at: <%= raw @error.created_at.to_json %>,
93
+ updated_at: <%= raw @error.updated_at.to_json %>
94
+ };
95
+
96
+ const jsonString = JSON.stringify(errorData, null, 2);
97
+ const blob = new Blob([jsonString], { type: 'application/json' });
98
+ const url = URL.createObjectURL(blob);
99
+ const link = document.createElement('a');
100
+ link.href = url;
101
+ link.download = `error_${errorData.id}_${errorData.error_type.replace(/[^a-zA-Z0-9]/g, '_')}.json`;
102
+ document.body.appendChild(link);
103
+ link.click();
104
+ document.body.removeChild(link);
105
+ URL.revokeObjectURL(url);
106
+
107
+ // Visual feedback
108
+ const button = event.currentTarget;
109
+ const originalHTML = button.innerHTML;
110
+ button.innerHTML = '<i class="bi bi-check"></i> Downloaded!';
111
+ button.classList.remove('btn-outline-secondary');
112
+ button.classList.add('btn-success');
113
+
114
+ setTimeout(() => {
115
+ button.innerHTML = originalHTML;
116
+ button.classList.remove('btn-success');
117
+ button.classList.add('btn-outline-secondary');
118
+ }, 2000);
119
+ }
120
+ </script>
121
+
42
122
  <div class="row g-4">
43
123
  <!-- Error Information -->
44
124
  <div class="col-md-8">
@@ -46,7 +126,12 @@
46
126
  <div class="card mb-4">
47
127
  <div class="card-header bg-danger text-white">
48
128
  <div class="d-flex justify-content-between align-items-center">
49
- <h5 class="mb-0"><i class="bi bi-bug-fill"></i> <%= @error.error_type %></h5>
129
+ <h5 class="mb-0">
130
+ <i class="bi bi-bug-fill"></i> <%= @error.error_type %>
131
+ <% if @error.recent? %>
132
+ <span class="badge bg-success ms-2" data-bs-toggle="tooltip" title="Error occurred within the last hour">NEW</span>
133
+ <% end %>
134
+ </h5>
50
135
  <button class="btn btn-sm btn-outline-light" onclick="copyToClipboard('<%= j @error.error_type %>', this)" title="Copy error type">
51
136
  <i class="bi bi-clipboard"></i>
52
137
  </button>
@@ -402,11 +487,57 @@
402
487
  </div>
403
488
  <div class="mb-3">
404
489
  <label for="body" class="form-label">Comment <span class="text-danger">*</span></label>
405
- <%= text_area_tag :body, nil, class: "form-control", rows: 4, placeholder: "Share your thoughts, findings, or updates...", required: true %>
490
+
491
+ <!-- Quick Templates -->
492
+ <div class="mb-2">
493
+ <small class="text-muted d-block mb-1">
494
+ <i class="bi bi-lightning-fill"></i> Quick templates:
495
+ </small>
496
+ <div class="d-flex flex-wrap gap-1">
497
+ <button type="button" class="btn btn-sm btn-outline-secondary" onclick="insertTemplate('investigating')">
498
+ <i class="bi bi-search"></i> Investigating
499
+ </button>
500
+ <button type="button" class="btn btn-sm btn-outline-secondary" onclick="insertTemplate('found_fix')">
501
+ <i class="bi bi-wrench"></i> Found Fix
502
+ </button>
503
+ <button type="button" class="btn btn-sm btn-outline-secondary" onclick="insertTemplate('need_info')">
504
+ <i class="bi bi-question-circle"></i> Need Info
505
+ </button>
506
+ <button type="button" class="btn btn-sm btn-outline-secondary" onclick="insertTemplate('duplicate')">
507
+ <i class="bi bi-files"></i> Duplicate
508
+ </button>
509
+ <button type="button" class="btn btn-sm btn-outline-secondary" onclick="insertTemplate('cannot_reproduce')">
510
+ <i class="bi bi-x-circle"></i> Cannot Reproduce
511
+ </button>
512
+ </div>
513
+ </div>
514
+
515
+ <%= text_area_tag :body, nil, class: "form-control", rows: 4, placeholder: "Share your thoughts, findings, or updates...", required: true, id: "comment_body" %>
406
516
  </div>
407
517
  <%= submit_tag "Post Comment", class: "btn btn-primary" %>
408
518
  <% end %>
409
519
  </div>
520
+
521
+ <script>
522
+ function insertTemplate(templateType) {
523
+ const textarea = document.getElementById('comment_body');
524
+ const templates = {
525
+ investigating: "šŸ” Investigating this issue now. Will update with findings.",
526
+ found_fix: "āœ… Found the fix!\n\nRoot cause: \nSolution: \nPR: ",
527
+ need_info: "ā„¹ļø Need more information:\n\n- \n- \n\nPlease provide details to help debug this issue.",
528
+ duplicate: "šŸ“‹ This appears to be a duplicate of error #\n\nClosing as duplicate.",
529
+ cannot_reproduce: "āŒ Cannot reproduce this issue.\n\nAttempted:\n- \n- \n\nNeed more details or steps to reproduce."
530
+ };
531
+
532
+ const template = templates[templateType];
533
+ if (template) {
534
+ textarea.value = template;
535
+ textarea.focus();
536
+ // Move cursor to end
537
+ textarea.setSelectionRange(textarea.value.length, textarea.value.length);
538
+ }
539
+ }
540
+ </script>
410
541
  </div>
411
542
  </div>
412
543
  <% end %>
@@ -572,8 +703,16 @@
572
703
 
573
704
  <div class="mb-3">
574
705
  <small class="text-muted d-block mb-1">First Seen</small>
575
- <strong><%= @error.first_seen_at&.strftime("%B %d, %Y") || 'N/A' %></strong><br>
576
- <small><%= @error.first_seen_at&.strftime("%I:%M:%S %p %Z") || 'N/A' %></small>
706
+ <% if @related_errors.any? %>
707
+ <%= link_to "#timeline", class: "text-decoration-none", data: { bs_toggle: "tooltip" }, title: "Jump to timeline" do %>
708
+ <strong><%= @error.first_seen_at&.strftime("%B %d, %Y") || 'N/A' %></strong><br>
709
+ <small><%= @error.first_seen_at&.strftime("%I:%M:%S %p %Z") || 'N/A' %></small>
710
+ <i class="bi bi-arrow-down-circle ms-1"></i>
711
+ <% end %>
712
+ <% else %>
713
+ <strong><%= @error.first_seen_at&.strftime("%B %d, %Y") || 'N/A' %></strong><br>
714
+ <small><%= @error.first_seen_at&.strftime("%I:%M:%S %p %Z") || 'N/A' %></small>
715
+ <% end %>
577
716
  </div>
578
717
 
579
718
  <div class="mb-3">
Binary file
@@ -1,3 +1,3 @@
1
1
  module RailsErrorDashboard
2
- VERSION = "0.1.14"
2
+ VERSION = "0.1.15"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_error_dashboard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.14
4
+ version: 0.1.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anjan Jagirdar
@@ -312,6 +312,7 @@ files:
312
312
  - app/views/rails_error_dashboard/errors/settings.html.erb
313
313
  - app/views/rails_error_dashboard/errors/show.html.erb
314
314
  - config/routes.rb
315
+ - db/development.sqlite3
315
316
  - db/migrate/20251224000001_create_rails_error_dashboard_error_logs.rb
316
317
  - db/migrate/20251224081522_add_better_tracking_to_error_logs.rb
317
318
  - db/migrate/20251224101217_add_controller_action_to_error_logs.rb
@@ -378,7 +379,7 @@ metadata:
378
379
  source_code_uri: https://github.com/AnjanJ/rails_error_dashboard
379
380
  changelog_uri: https://github.com/AnjanJ/rails_error_dashboard/blob/main/CHANGELOG.md
380
381
  post_install_message: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n
381
- \ Rails Error Dashboard v0.1.14\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\U0001F195
382
+ \ Rails Error Dashboard v0.1.15\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\U0001F195
382
383
  First time? Quick start:\n rails generate rails_error_dashboard:install\n rails
383
384
  db:migrate\n # Add to config/routes.rb:\n mount RailsErrorDashboard::Engine
384
385
  => '/error_dashboard'\n\n\U0001F504 Upgrading from v0.1.x?\n rails db:migrate\n