rails_error_dashboard 0.1.13 → 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: 2cb43c836bc9a37283967490ad1c9e38eec95903fcb6eb1f8fda39e305eda044
4
- data.tar.gz: 9b5ae145da9967e59a0ad17154af14fc9fdb85037649d5587c473d008c0c89fc
3
+ metadata.gz: da18321511a53167fa248ae72974c6c15807a725396aabd27af1059296ecbc46
4
+ data.tar.gz: 84f348d386b6b07375797275dedf97e5a9504522270447bebd6b56307f5e4a7d
5
5
  SHA512:
6
- metadata.gz: c39e424531a1bc9869845fca21af2f71098da5ee18f09b04d310b2719d0c108021b917a70b73a39d8b42c5048cf939c3c8c716c14b7b199dd65f3845f3029ac6
7
- data.tar.gz: 04b650c61784d45b51c16a803601518c0dae6765ad5bede0a94748eedad92addc841582933ccd70f9c8479280c2a9e335788389c6ce770437dbc7c5901d9bdba
6
+ metadata.gz: b5b3d045f7a0b7bcae61823aa57e373111b6dab59e5a651ae5a33df9a3f043598e6469195b9cb508e79ad6bda62182877572888dba3cea37a4039715eee79349
7
+ data.tar.gz: 460491a0614445e91f098dbb792d86d9d7c8616f0456ad88cd6b14fdf27aba4d87990b080bd6c289eafe03befccceec94b6b23dcce29d850fd05b3bda946d39d
@@ -111,5 +111,24 @@ module RailsErrorDashboard
111
111
  content_tag(:span, icon, class: "text-muted small")
112
112
  end
113
113
  end
114
+
115
+ # Generates a link to a git commit if repository URL is configured
116
+ # @param git_sha [String] The git commit SHA
117
+ # @param short [Boolean] Whether to show short SHA (7 chars) or full SHA
118
+ # @return [String] HTML safe link to commit or plain text if no repo configured
119
+ def git_commit_link(git_sha, short: true)
120
+ return "" if git_sha.blank?
121
+
122
+ config = RailsErrorDashboard.configuration
123
+ display_sha = short ? git_sha[0..6] : git_sha
124
+
125
+ if config.git_repository_url.present?
126
+ # Support GitHub, GitLab, Bitbucket URL formats
127
+ commit_url = "#{config.git_repository_url.chomp("/")}/commit/#{git_sha}"
128
+ link_to display_sha, commit_url, class: "text-decoration-none font-monospace", target: "_blank", rel: "noopener"
129
+ else
130
+ content_tag(:code, display_sha, class: "font-monospace")
131
+ end
132
+ end
114
133
  end
115
134
  end
@@ -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>
@@ -499,7 +499,7 @@
499
499
  <strong>Git SHA</strong>
500
500
  <br><small class="text-muted">Deployed commit</small>
501
501
  </div>
502
- <code class="fs-6"><%= @config.git_sha[0..7] %></code>
502
+ <span class="fs-6"><%= git_commit_link(@config.git_sha) %></span>
503
503
  </div>
504
504
  </div>
505
505
  <% end %>
@@ -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">
@@ -745,7 +884,7 @@
745
884
  <% if @error.git_sha.present? %>
746
885
  <div class="mb-1">
747
886
  <small class="text-muted">Git SHA:</small>
748
- <code class="ms-1"><%= @error.git_sha[0..7] %></code>
887
+ <span class="ms-1"><%= git_commit_link(@error.git_sha) %></span>
749
888
  </div>
750
889
  <% end %>
751
890
 
Binary file
@@ -296,4 +296,11 @@ RailsErrorDashboard.configure do |config|
296
296
  config.app_version = ENV["APP_VERSION"]
297
297
  config.git_sha = ENV["GIT_SHA"]
298
298
  # config.total_users_for_impact = 10000 # For user impact % calculation
299
+
300
+ # Git repository URL for clickable commit links
301
+ # Examples:
302
+ # GitHub: "https://github.com/username/repo"
303
+ # GitLab: "https://gitlab.com/username/repo"
304
+ # Bitbucket: "https://bitbucket.org/username/repo"
305
+ # config.git_repository_url = ENV["GIT_REPOSITORY_URL"]
299
306
  end
@@ -69,6 +69,9 @@ module RailsErrorDashboard
69
69
  attr_accessor :git_sha
70
70
  attr_accessor :total_users_for_impact # For user impact % calculation
71
71
 
72
+ # Git repository URL for linking commits (e.g., "https://github.com/user/repo")
73
+ attr_accessor :git_repository_url
74
+
72
75
  # Advanced error analysis features
73
76
  attr_accessor :enable_similar_errors # Fuzzy error matching
74
77
  attr_accessor :enable_co_occurring_errors # Detect errors happening together
@@ -142,6 +145,7 @@ module RailsErrorDashboard
142
145
  @app_version = ENV["APP_VERSION"]
143
146
  @git_sha = ENV["GIT_SHA"]
144
147
  @total_users_for_impact = nil # Auto-detect if not set
148
+ @git_repository_url = ENV["GIT_REPOSITORY_URL"]
145
149
 
146
150
  # Advanced error analysis features (all OFF by default - opt-in)
147
151
  @enable_similar_errors = false # Fuzzy error matching
@@ -1,3 +1,3 @@
1
1
  module RailsErrorDashboard
2
- VERSION = "0.1.13"
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.13
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.13\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