rails_error_dashboard 0.1.28 → 0.1.29

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: 5a0f4cca16260c154f28b927ad49e276376fb390da7469479879e59e3ae07884
4
- data.tar.gz: d33904659ed7e1e98cf76f7bef72510f610bf2be611d1562e1bbadc06db5ed82
3
+ metadata.gz: 3c2f62f1af545cfebb6eb7356950d1ef5044ca767f7f0d0f8d048b9f8cedc16f
4
+ data.tar.gz: 95239d1ecadcf74c334b6af8269b3e2fbf2fc27f8b679a91f537f9c81d2ab363
5
5
  SHA512:
6
- metadata.gz: 2adbbee9b22e299ee55090db83aea7a48f162f519837e626a198ee1ae553e5b5c4030eee28223ad367bd55042543f3c7dea612de89dfe5f0857ac410d115c577
7
- data.tar.gz: 4bb63ab6e3d16bda1c8430e35944c80b057e264bd0d87b675dd5ce0fe832af92334039e597e6708579064a3df3c537fec5aabcac715157bc47d69d8dc5e359cd
6
+ metadata.gz: 867adf79f0eebb0dd5ab6f4ecc417546af088a5ececc76595326a8d1970b1593e2eaca52023c48f5441ebd788daad167d577223500b0f5adbb07b24ef6d0e581
7
+ data.tar.gz: f626ae8fb589b8ddf4a48385c910e52de87d17218f18b62fdf4f29837a37651b8eb103839a8e4a548b6ed1df0ea773e0870ede59550f8ee1b9cf0dfdce958559
data/README.md CHANGED
@@ -856,6 +856,22 @@ See [Plugin System Guide](docs/PLUGIN_SYSTEM.md).
856
856
 
857
857
  ---
858
858
 
859
+ ## šŸ™ Contributors
860
+
861
+ Thank you to everyone who has contributed to Rails Error Dashboard!
862
+
863
+ [![Contributors](https://contrib.rocks/image?repo=AnjanJ/rails_error_dashboard)](https://github.com/AnjanJ/rails_error_dashboard/graphs/contributors)
864
+
865
+ Special thanks to:
866
+ - [@bonniesimon](https://github.com/bonniesimon) - Turbo helpers production fix
867
+ - [@gundestrup](https://github.com/gundestrup) - Security fixes, dependency updates, CI/CD improvements
868
+
869
+ See [CONTRIBUTORS.md](CONTRIBUTORS.md) for the complete list of contributors and their contributions.
870
+
871
+ Want to contribute? Check out our [Contributing Guide](CONTRIBUTING.md)!
872
+
873
+ ---
874
+
859
875
  **Made with ā¤ļø by [Anjan](https://www.anjan.dev) for the Rails community**
860
876
 
861
877
  *One Gem to rule them all, One Gem to find them, One Gem to bring them all, and in the dashboard bind them.* šŸ§™ā€ā™‚ļø
@@ -922,6 +922,11 @@
922
922
  <i class="bi bi-graph-up"></i> Analytics
923
923
  <% end %>
924
924
  </li>
925
+ <li class="nav-item">
926
+ <%= link_to correlation_errors_path(nav_params), class: "nav-link #{request.path == correlation_errors_path ? 'active' : ''}" do %>
927
+ <i class="bi bi-diagram-3"></i> Correlation
928
+ <% end %>
929
+ </li>
925
930
  <li class="nav-item">
926
931
  <%= link_to settings_path(nav_params), class: "nav-link #{request.path == settings_path ? 'active' : ''}" do %>
927
932
  <i class="bi bi-gear"></i> Settings
@@ -0,0 +1,70 @@
1
+ <div class="table-responsive">
2
+ <table class="table table-hover mb-0">
3
+ <thead class="table-light">
4
+ <tr>
5
+ <% if show_rank %>
6
+ <th>Rank</th>
7
+ <% end %>
8
+ <th>User</th>
9
+ <% if show_error_type_count %>
10
+ <th>Different Error Types</th>
11
+ <% end %>
12
+ <th>Error Count</th>
13
+ <% if show_percentage %>
14
+ <th>Percentage</th>
15
+ <% end %>
16
+ <% if show_error_types %>
17
+ <th>Error Types</th>
18
+ <% end %>
19
+ <th>Actions</th>
20
+ </tr>
21
+ </thead>
22
+ <tbody>
23
+ <% users.each_with_index do |user_data, index| %>
24
+ <tr>
25
+ <% if show_rank %>
26
+ <td><strong>#<%= index + 1 %></strong></td>
27
+ <% end %>
28
+ <td><%= user_data[:email] || user_data[:user_email] %></td>
29
+ <% if show_error_type_count %>
30
+ <td>
31
+ <span class="badge bg-warning text-dark">
32
+ <%= user_data[:error_type_count] %> types
33
+ </span>
34
+ </td>
35
+ <% end %>
36
+ <td>
37
+ <span class="badge bg-danger"><%= user_data[:count] || user_data[:total_errors] %></span>
38
+ </td>
39
+ <% if show_percentage && total_errors.present? %>
40
+ <td>
41
+ <div class="progress" style="height: 20px;">
42
+ <div class="progress-bar bg-danger"
43
+ role="progressbar"
44
+ style="width: <%= ((user_data[:count] || user_data[:total_errors]).to_f / total_errors * 100).round(1) %>%"
45
+ aria-valuenow="<%= user_data[:count] || user_data[:total_errors] %>"
46
+ aria-valuemin="0"
47
+ aria-valuemax="100">
48
+ <%= ((user_data[:count] || user_data[:total_errors]).to_f / total_errors * 100).round(1) %>%
49
+ </div>
50
+ </div>
51
+ </td>
52
+ <% end %>
53
+ <% if show_error_types && user_data[:error_types].present? %>
54
+ <td>
55
+ <% user_data[:error_types].first(3).each do |error_type| %>
56
+ <code class="small me-1"><%= error_type %></code>
57
+ <% end %>
58
+ <% if user_data[:error_types].count > 3 %>
59
+ <span class="text-muted small">+<%= user_data[:error_types].count - 3 %> more</span>
60
+ <% end %>
61
+ </td>
62
+ <% end %>
63
+ <td>
64
+ <%= link_to "View Errors", errors_path(user_id: user_data[:user_id]), class: "btn btn-sm btn-outline-primary" %>
65
+ </td>
66
+ </tr>
67
+ <% end %>
68
+ </tbody>
69
+ </table>
70
+ </div>
@@ -266,43 +266,15 @@
266
266
  <h5 class="mb-0"><i class="bi bi-people"></i> Top 10 Affected Users</h5>
267
267
  </div>
268
268
  <div class="card-body p-0">
269
- <div class="table-responsive">
270
- <table class="table table-hover mb-0">
271
- <thead class="table-light">
272
- <tr>
273
- <th>Rank</th>
274
- <th>User</th>
275
- <th>Error Count</th>
276
- <th>Percentage</th>
277
- <th>Actions</th>
278
- </tr>
279
- </thead>
280
- <tbody>
281
- <% @top_users.each_with_index do |(email, count), index| %>
282
- <tr>
283
- <td><strong>#<%= index + 1 %></strong></td>
284
- <td><%= email %></td>
285
- <td><span class="badge bg-danger"><%= count %></span></td>
286
- <td>
287
- <div class="progress" style="height: 20px;">
288
- <div class="progress-bar bg-danger"
289
- role="progressbar"
290
- style="width: <%= (count.to_f / @error_stats[:total] * 100).round(1) %>%"
291
- aria-valuenow="<%= count %>"
292
- aria-valuemin="0"
293
- aria-valuemax="<%= @error_stats[:total] %>">
294
- <%= (count.to_f / @error_stats[:total] * 100).round(1) %>%
295
- </div>
296
- </div>
297
- </td>
298
- <td>
299
- <%= link_to "View Errors", errors_path(search: email), class: "btn btn-sm btn-outline-primary" %>
300
- </td>
301
- </tr>
302
- <% end %>
303
- </tbody>
304
- </table>
305
- </div>
269
+ <%= render partial: 'user_errors_table',
270
+ locals: {
271
+ users: @top_users,
272
+ show_rank: true,
273
+ show_error_type_count: false,
274
+ show_percentage: true,
275
+ show_error_types: false,
276
+ total_errors: @error_stats[:total]
277
+ } %>
306
278
  </div>
307
279
  </div>
308
280
  </div>
@@ -216,43 +216,17 @@
216
216
  <small class="text-muted">Users experiencing 2+ different error types</small>
217
217
  </div>
218
218
  <div class="card-body">
219
- <div class="table-responsive">
220
- <table class="table table-hover">
221
- <thead>
222
- <tr>
223
- <th>User</th>
224
- <th>Different Error Types</th>
225
- <th>Total Errors</th>
226
- <th>Error Types</th>
227
- <th>Actions</th>
228
- </tr>
229
- </thead>
230
- <tbody>
231
- <% @multi_error_users.first(20).each do |user_data| %>
232
- <tr>
233
- <td><%= user_data[:user_email] %></td>
234
- <td>
235
- <span class="badge bg-warning text-dark">
236
- <%= user_data[:error_type_count] %> types
237
- </span>
238
- </td>
239
- <td><%= user_data[:total_errors] %></td>
240
- <td>
241
- <% user_data[:error_types].first(3).each do |error_type| %>
242
- <code class="small me-1"><%= error_type %></code>
243
- <% end %>
244
- <% if user_data[:error_types].count > 3 %>
245
- <span class="text-muted small">+<%= user_data[:error_types].count - 3 %> more</span>
246
- <% end %>
247
- </td>
248
- <td>
249
- <%= link_to "View", errors_path(search: user_data[:user_email]), class: "btn btn-sm btn-outline-primary" %>
250
- </td>
251
- </tr>
252
- <% end %>
253
- </tbody>
254
- </table>
255
- </div>
219
+ <%= render partial: 'user_errors_table',
220
+ locals: {
221
+ users: @multi_error_users.first(20),
222
+ show_rank: false,
223
+ show_error_type_count: true,
224
+ show_percentage: false,
225
+ show_error_types: true,
226
+ total_errors: nil
227
+ } %>
228
+ </div>
229
+ <div class="card-body pt-0 border-top">
256
230
  <% if @multi_error_users.count > 20 %>
257
231
  <p class="text-muted small mb-0">
258
232
  Showing 20 of <%= @multi_error_users.count %> users
@@ -1,5 +1,59 @@
1
1
  <% content_for :page_title, "Error ##{@error.id}" %>
2
2
 
3
+ <script>
4
+ function downloadErrorJSON(event) {
5
+ const errorData = {
6
+ id: <%= raw @error.id.to_json %>,
7
+ error_type: <%= raw @error.error_type.to_json %>,
8
+ message: <%= raw @error.message.to_json %>,
9
+ backtrace: <%= raw @error.backtrace.to_json %>,
10
+ occurred_at: <%= raw @error.occurred_at.to_json %>,
11
+ first_seen_at: <%= raw @error.first_seen_at.to_json %>,
12
+ last_seen_at: <%= raw @error.last_seen_at.to_json %>,
13
+ occurrence_count: <%= raw @error.occurrence_count.to_json %>,
14
+ resolved: <%= raw @error.resolved?.to_json %>,
15
+ resolved_at: <%= raw @error.resolved_at.to_json %>,
16
+ resolved_by_name: <%= raw @error.resolved_by_name.to_json %>,
17
+ platform: <%= raw @error.platform.to_json %>,
18
+ user_id: <%= raw @error.user_id.to_json %>,
19
+ severity: <%= raw @error.severity.to_json %>,
20
+ priority_level: <%= raw (@error.priority_level || 0).to_json %>,
21
+ <% if @error.respond_to?(:app_version) %>
22
+ app_version: <%= raw @error.app_version.to_json %>,
23
+ <% end %>
24
+ <% if @error.respond_to?(:git_commit) %>
25
+ git_commit: <%= raw @error.git_commit.to_json %>,
26
+ <% end %>
27
+ created_at: <%= raw @error.created_at.to_json %>,
28
+ updated_at: <%= raw @error.updated_at.to_json %>
29
+ };
30
+
31
+ const jsonString = JSON.stringify(errorData, null, 2);
32
+ const blob = new Blob([jsonString], { type: 'application/json' });
33
+ const url = URL.createObjectURL(blob);
34
+ const link = document.createElement('a');
35
+ link.href = url;
36
+ link.download = `error_${errorData.id}_${errorData.error_type.replace(/[^a-zA-Z0-9]/g, '_')}.json`;
37
+ document.body.appendChild(link);
38
+ link.click();
39
+ document.body.removeChild(link);
40
+ URL.revokeObjectURL(url);
41
+
42
+ // Visual feedback
43
+ const button = event.currentTarget;
44
+ const originalHTML = button.innerHTML;
45
+ button.innerHTML = '<i class="bi bi-check"></i> Downloaded!';
46
+ button.classList.remove('btn-outline-secondary');
47
+ button.classList.add('btn-success');
48
+
49
+ setTimeout(() => {
50
+ button.innerHTML = originalHTML;
51
+ button.classList.remove('btn-success');
52
+ button.classList.add('btn-outline-secondary');
53
+ }, 2000);
54
+ }
55
+ </script>
56
+
3
57
  <div class="py-4">
4
58
  <!-- Breadcrumbs -->
5
59
  <nav aria-label="breadcrumb" class="mb-3">
@@ -27,7 +81,7 @@
27
81
  </h2>
28
82
  </div>
29
83
  <div class="d-flex gap-2">
30
- <button type="button" class="btn btn-outline-secondary" onclick="downloadErrorJSON()" title="Download error details as JSON">
84
+ <button type="button" class="btn btn-outline-secondary" onclick="downloadErrorJSON(event)" title="Download error details as JSON">
31
85
  <i class="bi bi-download"></i> Export JSON
32
86
  </button>
33
87
  <% if @error.resolved? %>
@@ -42,60 +96,6 @@
42
96
  </div>
43
97
  </div>
44
98
 
45
- <script>
46
- function downloadErrorJSON() {
47
- const errorData = {
48
- id: <%= @error.id %>,
49
- error_type: <%= json_escape @error.error_type.to_json %>,
50
- message: <%= json_escape @error.message.to_json %>,
51
- backtrace: <%= json_escape @error.backtrace.to_json %>,
52
- occurred_at: <%= json_escape @error.occurred_at.to_json %>,
53
- first_seen_at: <%= json_escape @error.first_seen_at.to_json %>,
54
- last_seen_at: <%= json_escape @error.last_seen_at.to_json %>,
55
- occurrence_count: <%= @error.occurrence_count %>,
56
- resolved: <%= @error.resolved? %>,
57
- resolved_at: <%= json_escape @error.resolved_at.to_json %>,
58
- resolved_by_name: <%= json_escape @error.resolved_by_name.to_json %>,
59
- platform: <%= json_escape @error.platform.to_json %>,
60
- user_id: <%= json_escape @error.user_id.to_json %>,
61
- severity: <%= json_escape @error.severity.to_json %>,
62
- priority_level: <%= @error.priority_level || 0 %>,
63
- <% if @error.respond_to?(:app_version) %>
64
- app_version: <%= json_escape @error.app_version.to_json %>,
65
- <% end %>
66
- <% if @error.respond_to?(:git_commit) %>
67
- git_commit: <%= json_escape @error.git_commit.to_json %>,
68
- <% end %>
69
- created_at: <%= json_escape @error.created_at.to_json %>,
70
- updated_at: <%= json_escape @error.updated_at.to_json %>
71
- };
72
-
73
- const jsonString = JSON.stringify(errorData, null, 2);
74
- const blob = new Blob([jsonString], { type: 'application/json' });
75
- const url = URL.createObjectURL(blob);
76
- const link = document.createElement('a');
77
- link.href = url;
78
- link.download = `error_${errorData.id}_${errorData.error_type.replace(/[^a-zA-Z0-9]/g, '_')}.json`;
79
- document.body.appendChild(link);
80
- link.click();
81
- document.body.removeChild(link);
82
- URL.revokeObjectURL(url);
83
-
84
- // Visual feedback
85
- const button = event.currentTarget;
86
- const originalHTML = button.innerHTML;
87
- button.innerHTML = '<i class="bi bi-check"></i> Downloaded!';
88
- button.classList.remove('btn-outline-secondary');
89
- button.classList.add('btn-success');
90
-
91
- setTimeout(() => {
92
- button.innerHTML = originalHTML;
93
- button.classList.remove('btn-success');
94
- button.classList.add('btn-outline-secondary');
95
- }, 2000);
96
- }
97
- </script>
98
-
99
99
  <div class="row g-4">
100
100
  <!-- Error Information -->
101
101
  <div class="col-md-8">
@@ -741,7 +741,9 @@
741
741
  <div class="mb-3">
742
742
  <small class="text-muted d-block mb-1">Workflow Status</small>
743
743
  <% if @error.respond_to?(:status) %>
744
- <span class="badge bg-<%= @error.status_badge_color %> fs-6">
744
+ <% badge_color = @error.status_badge_color %>
745
+ <% text_dark = %w[info warning light].include?(badge_color) ? 'text-dark' : '' %>
746
+ <span class="badge bg-<%= badge_color %> <%= text_dark %> fs-6">
745
747
  <%= @error.status.titleize %>
746
748
  </span>
747
749
  <% elsif @error.resolved? %>
@@ -100,8 +100,7 @@ module RailsErrorDashboard
100
100
  .count
101
101
  .sort_by { |_, count| -count }
102
102
  .first(10)
103
- .map { |user_id, count| [ find_user_email(user_id, user_model), count ] }
104
- .to_h
103
+ .map { |user_id, count| { user_id: user_id, email: find_user_email(user_id, user_model), count: count } }
105
104
  end
106
105
 
107
106
  def find_user_email(user_id, user_model)
@@ -29,6 +29,7 @@ module RailsErrorDashboard
29
29
  query = filter_by_resolved(query)
30
30
  query = filter_by_platform(query)
31
31
  query = filter_by_application(query)
32
+ query = filter_by_user_id(query)
32
33
  query = filter_by_search(query)
33
34
  query = filter_by_severity(query)
34
35
  query = filter_by_timeframe(query)
@@ -82,6 +83,12 @@ module RailsErrorDashboard
82
83
  query.where(application_id: @filters[:application_id])
83
84
  end
84
85
 
86
+ def filter_by_user_id(query)
87
+ return query unless @filters[:user_id].present?
88
+
89
+ query.where(user_id: @filters[:user_id])
90
+ end
91
+
85
92
  def filter_by_search(query)
86
93
  return query unless @filters[:search].present?
87
94
 
@@ -1,3 +1,3 @@
1
1
  module RailsErrorDashboard
2
- VERSION = "0.1.28"
2
+ VERSION = "0.1.29"
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.28
4
+ version: 0.1.29
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anjan Jagirdar
@@ -305,6 +305,7 @@ files:
305
305
  - app/views/rails_error_dashboard/errors/_pattern_insights.html.erb
306
306
  - app/views/rails_error_dashboard/errors/_stats.html.erb
307
307
  - app/views/rails_error_dashboard/errors/_timeline.html.erb
308
+ - app/views/rails_error_dashboard/errors/_user_errors_table.html.erb
308
309
  - app/views/rails_error_dashboard/errors/analytics.html.erb
309
310
  - app/views/rails_error_dashboard/errors/correlation.html.erb
310
311
  - app/views/rails_error_dashboard/errors/index.html.erb
@@ -386,7 +387,7 @@ metadata:
386
387
  source_code_uri: https://github.com/AnjanJ/rails_error_dashboard
387
388
  changelog_uri: https://github.com/AnjanJ/rails_error_dashboard/blob/main/CHANGELOG.md
388
389
  post_install_message: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n
389
- \ Rails Error Dashboard v0.1.28\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\U0001F195
390
+ \ Rails Error Dashboard v0.1.29\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\U0001F195
390
391
  First time? Quick start:\n rails generate rails_error_dashboard:install\n rails
391
392
  db:migrate\n # Add to config/routes.rb:\n mount RailsErrorDashboard::Engine
392
393
  => '/error_dashboard'\n\n\U0001F504 Upgrading from v0.1.x?\n rails db:migrate\n