rails_error_dashboard 0.1.27 → 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: 2dfa475005d810cf2797830e0e00df4ad5b83f7e63df2b8c859ce8c1a4aa555e
4
- data.tar.gz: d35282497e6f04be43816e1473e941e7e1e797ab276082643148504fc11d1806
3
+ metadata.gz: 3c2f62f1af545cfebb6eb7356950d1ef5044ca767f7f0d0f8d048b9f8cedc16f
4
+ data.tar.gz: 95239d1ecadcf74c334b6af8269b3e2fbf2fc27f8b679a91f537f9c81d2ab363
5
5
  SHA512:
6
- metadata.gz: 03d48971cf4056b8fcf8facc6a8355484647ba725d65a56bdf93e448e5a84b2406784b9a05818a2285e89b49ae1b2eea1f798b43be4c4a273d123c411f755d57
7
- data.tar.gz: e70c135a00a15f7325eb10a0c58be45ecf1b8e05f1b7eeb42e538450e07cb69a68a36816d78b49a0c3e5089b58a251bfef45d1155cf3cdc58c3a804cb9d53e20
6
+ metadata.gz: 867adf79f0eebb0dd5ab6f4ecc417546af088a5ececc76595326a8d1970b1593e2eaca52023c48f5441ebd788daad167d577223500b0f5adbb07b24ef6d0e581
7
+ data.tar.gz: f626ae8fb589b8ddf4a48385c910e52de87d17218f18b62fdf4f29837a37651b8eb103839a8e4a548b6ed1df0ea773e0870ede59550f8ee1b9cf0dfdce958559
data/README.md CHANGED
@@ -29,9 +29,9 @@ Experience the full dashboard with 250+ realistic Rails errors, LOTR-themed demo
29
29
  ---
30
30
 
31
31
  ### ⚠️ BETA SOFTWARE
32
- This Rails Engine is in beta and under active development. While functional and tested (850+ tests passing), the API may change before v1.0.0. Use in production at your own discretion.
32
+ This Rails Engine is in beta and under active development. While functional and tested (935+ tests passing), the API may change before v1.0.0. Use in production at your own discretion.
33
33
 
34
- **Supports**: Rails 7.0 - 8.1 | Ruby 3.2+
34
+ **Supports**: Rails 7.0 - 8.0 (+ edge 8.1) | Ruby 3.2 - 3.4
35
35
 
36
36
  ---
37
37
 
@@ -80,10 +80,6 @@ This Rails Engine is in beta and under active development. While functional and
80
80
 
81
81
  ---
82
82
 
83
- ![Dashboard Screenshot](https://via.placeholder.com/800x400?text=Error+Dashboard+Screenshot)
84
-
85
- ---
86
-
87
83
  ## ✨ Features
88
84
 
89
85
  ### Core Features (Always Enabled)
@@ -121,7 +117,7 @@ HTTP Basic Auth, environment-based settings, optional separate database for isol
121
117
  - **Separate Database** - Isolate error data for better performance
122
118
  - **Database Indexes** - Composite indexes and PostgreSQL GIN full-text search
123
119
 
124
- #### 🧠 Advanced Analytics (8 Powerful Features)
120
+ #### 🧠 Advanced Analytics (7 Powerful Features)
125
121
 
126
122
  **1. Baseline Anomaly Alerts** 🔔
127
123
  Automatically detect unusual error rate spikes using statistical analysis (mean + std dev). Get proactive notifications when errors exceed expected baselines with intelligent cooldown to avoid alert fatigue.
@@ -144,8 +140,8 @@ Compare iOS vs Android vs Web health metrics side-by-side. Platform-specific err
144
140
  **7. Occurrence Pattern Detection** 📈
145
141
  Detect cyclical patterns (business hours, nighttime, weekend rhythms) and error bursts (many errors in short time). Understand when and how your errors happen.
146
142
 
147
- **8. Developer Insights** 💡
148
- AI-powered insights with severity detection, platform stability scoring, actionable recommendations, and recent error activity summaries.
143
+ **Plus: Developer Insights Dashboard** 💡
144
+ Built-in analytics dashboard with severity detection, platform stability scoring, actionable recommendations, and recent error activity summaries (always available, no configuration needed).
149
145
 
150
146
  #### 🔌 Plugin System
151
147
  Extensible architecture with event hooks (`on_error_logged`, `on_error_resolved`, `on_threshold_exceeded`). Built-in examples for Jira integration, metrics tracking, audit logging. Easy to create custom plugins - just drop a file in `config/initializers/error_dashboard_plugins/`.
@@ -173,7 +169,7 @@ rails db:migrate
173
169
  The installer will guide you through optional feature selection:
174
170
  - **Notifications** (Slack, Email, Discord, PagerDuty, Webhooks)
175
171
  - **Performance** (Async Logging, Error Sampling, Separate Database)
176
- - **Advanced Analytics** (8 powerful features including baseline alerts, fuzzy matching, platform comparison)
172
+ - **Advanced Analytics** (7 powerful features including baseline alerts, fuzzy matching, platform comparison)
177
173
 
178
174
  **All features are opt-in** - choose what you need during installation, or enable/disable them later in the initializer.
179
175
 
@@ -665,6 +661,193 @@ Rails Error Dashboard is available as open source under the terms of the [MIT Li
665
661
 
666
662
  ---
667
663
 
664
+ ## ❓ Frequently Asked Questions
665
+
666
+ <details>
667
+ <summary><strong>Is this production-ready?</strong></summary>
668
+
669
+ This is currently in **beta** but actively tested with 935+ passing tests across Rails 7.0-8.0 and Ruby 3.2-3.4. Many users are running it in production. See [production requirements](docs/FEATURES.md#production-readiness).
670
+ </details>
671
+
672
+ <details>
673
+ <summary><strong>How does this compare to Sentry/Rollbar/Honeybadger?</strong></summary>
674
+
675
+ **Similar**: Error tracking, grouping, notifications, dashboards
676
+ **Better**: 100% free, self-hosted (your data stays with you), no usage limits, Rails-optimized
677
+ **Trade-offs**: You manage hosting/backups, fewer integrations than commercial services
678
+
679
+ See [full comparison](docs/features/PLATFORM_COMPARISON.md).
680
+ </details>
681
+
682
+ <details>
683
+ <summary><strong>What's the performance impact?</strong></summary>
684
+
685
+ Minimal with async logging enabled:
686
+ - **Synchronous**: ~10-50ms per error (blocks request)
687
+ - **Async (recommended)**: ~1-2ms (queues to background job)
688
+ - **Sampling**: Log only 10% of non-critical errors for high-traffic apps
689
+
690
+ See [Performance Guide](docs/guides/ERROR_SAMPLING_AND_FILTERING.md).
691
+ </details>
692
+
693
+ <details>
694
+ <summary><strong>Can I use a separate database?</strong></summary>
695
+
696
+ Yes! Configure in your initializer:
697
+
698
+ ```ruby
699
+ RailsErrorDashboard.configure do |config|
700
+ config.database = :errors # Use separate database
701
+ end
702
+ ```
703
+
704
+ See [Database Options Guide](docs/guides/DATABASE_OPTIONS.md).
705
+ </details>
706
+
707
+ <details>
708
+ <summary><strong>How do I migrate from Sentry/Rollbar?</strong></summary>
709
+
710
+ 1. Install Rails Error Dashboard
711
+ 2. Run both systems in parallel (1-2 weeks)
712
+ 3. Verify all errors are captured
713
+ 4. Remove old error tracking gem
714
+ 5. Update team documentation
715
+
716
+ Historical data cannot be imported (different formats).
717
+ </details>
718
+
719
+ <details>
720
+ <summary><strong>Does it work with API-only Rails apps?</strong></summary>
721
+
722
+ Yes! The error logging works in API-only mode. The dashboard UI requires a browser but can be:
723
+ - Mounted in a separate admin app
724
+ - Run in a separate Rails instance pointing to the same database
725
+ - Accessed via SSH tunnel
726
+
727
+ See [API-only setup](docs/guides/MOBILE_APP_INTEGRATION.md#backend-setup-rails-api).
728
+ </details>
729
+
730
+ <details>
731
+ <summary><strong>How do I track multiple Rails apps?</strong></summary>
732
+
733
+ Automatic! Just set `APP_NAME` environment variable:
734
+
735
+ ```bash
736
+ # App 1
737
+ APP_NAME=my-api rails server
738
+
739
+ # App 2
740
+ APP_NAME=my-admin rails server
741
+ ```
742
+
743
+ All apps share the same dashboard. See [Multi-App Guide](docs/MULTI_APP_PERFORMANCE.md).
744
+ </details>
745
+
746
+ <details>
747
+ <summary><strong>Can I customize error severity levels?</strong></summary>
748
+
749
+ Yes! Configure custom rules in your initializer:
750
+
751
+ ```ruby
752
+ RailsErrorDashboard.configure do |config|
753
+ config.custom_severity_rules = {
754
+ /ActiveRecord::RecordNotFound/ => :low,
755
+ /Stripe::/ => :critical
756
+ }
757
+ end
758
+ ```
759
+
760
+ See [Customization Guide](docs/CUSTOMIZATION.md).
761
+ </details>
762
+
763
+ <details>
764
+ <summary><strong>How long are errors stored?</strong></summary>
765
+
766
+ Forever by default. Configure retention policy:
767
+
768
+ ```ruby
769
+ RailsErrorDashboard.configure do |config|
770
+ config.retention_days = 90 # Auto-delete after 90 days
771
+ end
772
+ ```
773
+
774
+ Or clean up manually with rake tasks. See [Database Optimization](docs/guides/DATABASE_OPTIMIZATION.md).
775
+ </details>
776
+
777
+ <details>
778
+ <summary><strong>Can I get Slack/Discord notifications?</strong></summary>
779
+
780
+ Yes! Enable during installation or configure manually:
781
+
782
+ ```ruby
783
+ RailsErrorDashboard.configure do |config|
784
+ config.enable_slack_notifications = true
785
+ config.slack_webhook_url = ENV['SLACK_WEBHOOK_URL']
786
+ end
787
+ ```
788
+
789
+ Supports Slack, Discord, Email, PagerDuty, and custom webhooks. See [Notifications Guide](docs/guides/NOTIFICATIONS.md).
790
+ </details>
791
+
792
+ <details>
793
+ <summary><strong>Does it work with Turbo/Hotwire?</strong></summary>
794
+
795
+ Yes! Includes Turbo Streams support for real-time updates. Errors appear in the dashboard instantly without page refresh.
796
+ </details>
797
+
798
+ <details>
799
+ <summary><strong>How do I report errors from mobile apps (React Native/Flutter)?</strong></summary>
800
+
801
+ Make HTTP POST requests to your Rails API:
802
+
803
+ ```javascript
804
+ // React Native example
805
+ fetch('https://api.example.com/error_dashboard/api/v1/errors', {
806
+ method: 'POST',
807
+ headers: {
808
+ 'Content-Type': 'application/json',
809
+ 'Authorization': 'Basic ' + btoa('admin:password')
810
+ },
811
+ body: JSON.stringify({
812
+ error_class: 'TypeError',
813
+ message: 'Cannot read property...',
814
+ platform: 'iOS'
815
+ })
816
+ });
817
+ ```
818
+
819
+ See [Mobile App Integration](docs/guides/MOBILE_APP_INTEGRATION.md).
820
+ </details>
821
+
822
+ <details>
823
+ <summary><strong>Can I build custom integrations?</strong></summary>
824
+
825
+ Yes! Use the plugin system:
826
+
827
+ ```ruby
828
+ class MyCustomPlugin < RailsErrorDashboard::Plugin
829
+ def on_error_logged(error_log)
830
+ # Your custom logic
831
+ end
832
+ end
833
+
834
+ RailsErrorDashboard::PluginRegistry.register(MyCustomPlugin.new)
835
+ ```
836
+
837
+ See [Plugin System Guide](docs/PLUGIN_SYSTEM.md).
838
+ </details>
839
+
840
+ <details>
841
+ <summary><strong>What if I need help?</strong></summary>
842
+
843
+ - **📖 Read the docs**: [docs/README.md](docs/README.md)
844
+ - **🐛 Report bugs**: [GitHub Issues](https://github.com/AnjanJ/rails_error_dashboard/issues)
845
+ - **💡 Ask questions**: [GitHub Discussions](https://github.com/AnjanJ/rails_error_dashboard/discussions)
846
+ - **🔒 Security issues**: See [SECURITY.md](SECURITY.md)
847
+ </details>
848
+
849
+ ---
850
+
668
851
  ## 💬 Support
669
852
 
670
853
  - **📖 Documentation**: [docs/](docs/README.md)
@@ -673,6 +856,22 @@ Rails Error Dashboard is available as open source under the terms of the [MIT Li
673
856
 
674
857
  ---
675
858
 
676
- **Made with ❤️ by Anjan for the Rails community**
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
+
875
+ **Made with ❤️ by [Anjan](https://www.anjan.dev) for the Rails community**
677
876
 
678
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.27"
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.27
4
+ version: 0.1.29
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anjan Jagirdar
@@ -116,7 +116,7 @@ dependencies:
116
116
  version: 1.3.0
117
117
  - - "<"
118
118
  - !ruby/object:Gem::Version
119
- version: 1.3.5
119
+ version: 1.3.7
120
120
  type: :runtime
121
121
  prerelease: false
122
122
  version_requirements: !ruby/object:Gem::Requirement
@@ -126,7 +126,7 @@ dependencies:
126
126
  version: 1.3.0
127
127
  - - "<"
128
128
  - !ruby/object:Gem::Version
129
- version: 1.3.5
129
+ version: 1.3.7
130
130
  - !ruby/object:Gem::Dependency
131
131
  name: rspec-rails
132
132
  requirement: !ruby/object:Gem::Requirement
@@ -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.27\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