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 +4 -4
- data/app/views/layouts/rails_error_dashboard.html.erb +61 -6
- data/app/views/rails_error_dashboard/errors/_error_row.html.erb +3 -0
- data/app/views/rails_error_dashboard/errors/_timeline.html.erb +1 -1
- data/app/views/rails_error_dashboard/errors/index.html.erb +10 -5
- data/app/views/rails_error_dashboard/errors/show.html.erb +144 -5
- data/db/development.sqlite3 +0 -0
- data/lib/rails_error_dashboard/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: da18321511a53167fa248ae72974c6c15807a725396aabd27af1059296ecbc46
|
|
4
|
+
data.tar.gz: 84f348d386b6b07375797275dedf97e5a9504522270447bebd6b56307f5e4a7d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
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 %>
|
|
@@ -530,11 +530,8 @@
|
|
|
530
530
|
// '?' - Show keyboard shortcuts help
|
|
531
531
|
if (e.key === '?') {
|
|
532
532
|
e.preventDefault();
|
|
533
|
-
|
|
534
|
-
|
|
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"
|
|
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
|
-
|
|
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
|
-
|
|
576
|
-
|
|
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
|
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.
|
|
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.
|
|
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
|