rails_error_dashboard 0.5.7 → 0.5.9
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/README.md +30 -3
- data/app/controllers/rails_error_dashboard/errors_controller.rb +81 -4
- data/app/controllers/rails_error_dashboard/webhooks_controller.rb +192 -0
- data/app/jobs/rails_error_dashboard/add_issue_recurrence_comment_job.rb +71 -0
- data/app/jobs/rails_error_dashboard/close_linked_issue_job.rb +43 -0
- data/app/jobs/rails_error_dashboard/create_issue_job.rb +68 -0
- data/app/jobs/rails_error_dashboard/reopen_linked_issue_job.rb +44 -0
- data/app/models/rails_error_dashboard/error_log.rb +2 -1
- data/app/views/layouts/rails_error_dashboard.html.erb +19 -6
- data/app/views/rails_error_dashboard/errors/_breadcrumbs_group.html.erb +1 -1
- data/app/views/rails_error_dashboard/errors/_discussion.html.erb +92 -100
- data/app/views/rails_error_dashboard/errors/_issue_section.html.erb +121 -0
- data/app/views/rails_error_dashboard/errors/_show_scripts.html.erb +1 -0
- data/app/views/rails_error_dashboard/errors/_sidebar_metadata.html.erb +77 -73
- data/app/views/rails_error_dashboard/errors/activestorage_health_summary.html.erb +148 -0
- data/app/views/rails_error_dashboard/errors/show.html.erb +13 -9
- data/config/routes.rb +6 -1
- data/db/migrate/20251223000000_create_rails_error_dashboard_complete_schema.rb +5 -0
- data/db/migrate/20260326000001_add_issue_tracking_to_error_logs.rb +15 -0
- data/lib/generators/rails_error_dashboard/install/install_generator.rb +12 -4
- data/lib/rails_error_dashboard/commands/create_issue.rb +59 -0
- data/lib/rails_error_dashboard/commands/link_existing_issue.rb +65 -0
- data/lib/rails_error_dashboard/configuration.rb +99 -0
- data/lib/rails_error_dashboard/engine.rb +39 -0
- data/lib/rails_error_dashboard/queries/active_storage_summary.rb +101 -0
- data/lib/rails_error_dashboard/services/codeberg_issue_client.rb +122 -0
- data/lib/rails_error_dashboard/services/github_issue_client.rb +117 -0
- data/lib/rails_error_dashboard/services/github_link_generator.rb +19 -1
- data/lib/rails_error_dashboard/services/gitlab_issue_client.rb +121 -0
- data/lib/rails_error_dashboard/services/issue_body_formatter.rb +132 -0
- data/lib/rails_error_dashboard/services/issue_tracker_client.rb +168 -0
- data/lib/rails_error_dashboard/services/markdown_error_formatter.rb +12 -0
- data/lib/rails_error_dashboard/subscribers/active_storage_subscriber.rb +112 -0
- data/lib/rails_error_dashboard/subscribers/issue_tracker_subscriber.rb +71 -0
- data/lib/rails_error_dashboard/version.rb +1 -1
- data/lib/rails_error_dashboard.rb +11 -1
- metadata +21 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
|
-
<title><%= content_for?(:page_title) ? "#{content_for(:page_title)} | " : "" %><%= Rails.application.class.module_parent_name %> -
|
|
4
|
+
<title><%= content_for?(:page_title) ? "#{content_for(:page_title)} | " : "" %><%= Rails.application.class.module_parent_name %> - RED</title>
|
|
5
5
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
6
|
<meta name="turbo-visit-control" content="reload">
|
|
7
7
|
<% if respond_to?(:csrf_meta_tags) %>
|
|
@@ -1547,18 +1547,24 @@ body.dark-mode .sidebar-section-body { background: var(--ctp-mantle); }
|
|
|
1547
1547
|
end
|
|
1548
1548
|
%>
|
|
1549
1549
|
<% if has_root %>
|
|
1550
|
-
<a class="navbar-brand" href="<%= root_url %>" title="Back to <%= Rails.application.class.module_parent_name %>">
|
|
1550
|
+
<a class="navbar-brand d-flex align-items-center" href="<%= root_url %>" title="Back to <%= Rails.application.class.module_parent_name %>">
|
|
1551
1551
|
<i class="bi bi-bug-fill"></i>
|
|
1552
1552
|
<span class="d-none d-sm-inline"><%= Rails.application.class.module_parent_name %></span>
|
|
1553
1553
|
<span class="d-none d-md-inline text-white-50 mx-2">|</span>
|
|
1554
|
-
<span class="d-none d-md-inline">
|
|
1554
|
+
<span class="d-none d-md-inline">
|
|
1555
|
+
<strong>RED</strong>
|
|
1556
|
+
<small class="text-white-50 ms-1" style="font-size: 0.65em; vertical-align: middle;">Rails Error Dashboard</small>
|
|
1557
|
+
</span>
|
|
1555
1558
|
</a>
|
|
1556
1559
|
<% else %>
|
|
1557
|
-
<span class="navbar-brand">
|
|
1560
|
+
<span class="navbar-brand d-flex align-items-center">
|
|
1558
1561
|
<i class="bi bi-bug-fill"></i>
|
|
1559
1562
|
<span class="d-none d-sm-inline"><%= Rails.application.class.module_parent_name %></span>
|
|
1560
1563
|
<span class="d-none d-md-inline text-white-50 mx-2">|</span>
|
|
1561
|
-
<span class="d-none d-md-inline">
|
|
1564
|
+
<span class="d-none d-md-inline">
|
|
1565
|
+
<strong>RED</strong>
|
|
1566
|
+
<small class="text-white-50 ms-1" style="font-size: 0.65em; vertical-align: middle;">Rails Error Dashboard</small>
|
|
1567
|
+
</span>
|
|
1562
1568
|
</span>
|
|
1563
1569
|
<% end %>
|
|
1564
1570
|
</div>
|
|
@@ -1681,6 +1687,13 @@ body.dark-mode .sidebar-section-body { background: var(--ctp-mantle); }
|
|
|
1681
1687
|
<% end %>
|
|
1682
1688
|
</li>
|
|
1683
1689
|
<% end %>
|
|
1690
|
+
<% if RailsErrorDashboard.configuration.enable_activestorage_tracking && RailsErrorDashboard.configuration.enable_breadcrumbs %>
|
|
1691
|
+
<li class="nav-item">
|
|
1692
|
+
<%= link_to activestorage_health_summary_errors_path(nav_params), class: "nav-link #{request.path == activestorage_health_summary_errors_path ? 'active' : ''}" do %>
|
|
1693
|
+
<i class="bi bi-cloud-arrow-up"></i> ActiveStorage
|
|
1694
|
+
<% end %>
|
|
1695
|
+
</li>
|
|
1696
|
+
<% end %>
|
|
1684
1697
|
<% if RailsErrorDashboard.configuration.enable_system_health %>
|
|
1685
1698
|
<li class="nav-item">
|
|
1686
1699
|
<%= link_to job_health_summary_errors_path(nav_params), class: "nav-link #{request.path == job_health_summary_errors_path ? 'active' : ''}" do %>
|
|
@@ -1820,7 +1833,7 @@ body.dark-mode .sidebar-section-body { background: var(--ctp-mantle); }
|
|
|
1820
1833
|
<div class="container">
|
|
1821
1834
|
<p class="text-muted mb-1">
|
|
1822
1835
|
<i class="bi bi-bug-fill text-primary"></i>
|
|
1823
|
-
Built with <a href="https://github.com/AnjanJ/rails_error_dashboard" target="_blank" class="text-decoration-none">Rails Error Dashboard</a>
|
|
1836
|
+
Built with <a href="https://github.com/AnjanJ/rails_error_dashboard" target="_blank" class="text-decoration-none"><strong>RED</strong> — Rails Error Dashboard</a>
|
|
1824
1837
|
</p>
|
|
1825
1838
|
<p class="text-muted small mb-0">
|
|
1826
1839
|
Created by <a href="https://www.anjan.dev/" target="_blank" class="text-decoration-none">Anjan Jagirdar</a>
|
|
@@ -196,7 +196,7 @@
|
|
|
196
196
|
</h5>
|
|
197
197
|
<small class="text-muted">Activity trail leading up to this error</small>
|
|
198
198
|
</div>
|
|
199
|
-
<div class="card-body p-0">
|
|
199
|
+
<div class="card-body p-0" style="max-height: 400px; overflow-y: auto;">
|
|
200
200
|
<div class="table-responsive">
|
|
201
201
|
<table class="table table-sm table-hover mb-0">
|
|
202
202
|
<thead class="table-light">
|
|
@@ -1,107 +1,99 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
<h5 class="mb-0">
|
|
6
|
-
<i class="bi bi-chat-dots"></i> Discussion
|
|
7
|
-
<span class="badge bg-secondary ms-2"><%= error.comments.count %></span>
|
|
8
|
-
</h5>
|
|
9
|
-
</div>
|
|
10
|
-
<div class="card-body">
|
|
11
|
-
<!-- Existing Comments -->
|
|
12
|
-
<% if error.comments.recent_first.any? %>
|
|
13
|
-
<div class="mb-4">
|
|
14
|
-
<% error.comments.recent_first.each_with_index do |comment, index| %>
|
|
15
|
-
<div class="<%= index < error.comments.count - 1 ? 'border-bottom' : '' %> pb-3 mb-3">
|
|
16
|
-
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
17
|
-
<div>
|
|
18
|
-
<strong class="text-primary">
|
|
19
|
-
<i class="bi bi-person-circle"></i> <%= comment.author_name %>
|
|
20
|
-
</strong>
|
|
21
|
-
<% if comment.recent? %>
|
|
22
|
-
<span class="badge bg-success ms-2">New</span>
|
|
23
|
-
<% end %>
|
|
24
|
-
</div>
|
|
25
|
-
<small class="text-muted">
|
|
26
|
-
<%= local_time(comment.created_at, format: :datetime) %>
|
|
27
|
-
<span class="ms-1 text-muted">(<%= local_time_ago(comment.created_at) %>)</span>
|
|
28
|
-
</small>
|
|
29
|
-
</div>
|
|
30
|
-
<div class="text-break">
|
|
31
|
-
<%= auto_link_urls(comment.body, error: error).html_safe %>
|
|
32
|
-
</div>
|
|
33
|
-
</div>
|
|
34
|
-
<% end %>
|
|
35
|
-
</div>
|
|
36
|
-
<% else %>
|
|
37
|
-
<p class="text-muted mb-4">
|
|
38
|
-
<i class="bi bi-info-circle"></i> No comments yet. Start the discussion below.
|
|
39
|
-
</p>
|
|
40
|
-
<% end %>
|
|
41
|
-
|
|
42
|
-
<!-- Add Comment Form -->
|
|
43
|
-
<div class="<%= error.comments.any? ? '' : 'border-top' %> pt-3">
|
|
44
|
-
<h6 class="mb-3">
|
|
45
|
-
<i class="bi bi-plus-circle"></i> Add Comment
|
|
46
|
-
</h6>
|
|
47
|
-
<%= form_with url: add_comment_error_path(error), method: :post do |f| %>
|
|
48
|
-
<div class="mb-3">
|
|
49
|
-
<label for="author_name" class="form-label">Your Name <span class="text-danger">*</span></label>
|
|
50
|
-
<%= text_field_tag :author_name, error.assigned_to, class: "form-control", placeholder: "e.g., John Doe", required: true %>
|
|
51
|
-
</div>
|
|
52
|
-
<div class="mb-3">
|
|
53
|
-
<label for="body" class="form-label">Comment <span class="text-danger">*</span></label>
|
|
1
|
+
<% has_linked_issue = error.respond_to?(:external_issue_url) && error.external_issue_url.present? %>
|
|
2
|
+
<% has_audit_comments = error.respond_to?(:comments) && error.comments.any? %>
|
|
3
|
+
<% platform_comments = @platform_comments || [] %>
|
|
4
|
+
<% issue_tracking_enabled = RailsErrorDashboard.configuration.enable_issue_tracking %>
|
|
54
5
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
6
|
+
<% if has_linked_issue && (platform_comments.any? || has_audit_comments) %>
|
|
7
|
+
<div class="card mb-4" id="section-discussion">
|
|
8
|
+
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
|
9
|
+
<h5 class="mb-0">
|
|
10
|
+
<i class="bi bi-chat-dots"></i> Discussion
|
|
11
|
+
<% if platform_comments.any? %>
|
|
12
|
+
<span class="badge bg-secondary ms-2"><%= platform_comments.size %></span>
|
|
13
|
+
<% end %>
|
|
14
|
+
</h5>
|
|
15
|
+
<a href="<%= error.external_issue_url %>" target="_blank" rel="noopener" class="btn btn-sm btn-outline-primary">
|
|
16
|
+
<i class="bi bi-box-arrow-up-right me-1"></i> Reply on <%= error.external_issue_provider&.capitalize || "Platform" %>
|
|
17
|
+
</a>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="card-body p-0">
|
|
20
|
+
<% if platform_comments.any? %>
|
|
21
|
+
<div style="max-height: 400px; overflow-y: auto;">
|
|
22
|
+
<% platform_comments.each_with_index do |comment, index| %>
|
|
23
|
+
<div class="px-3 py-3 <%= index < platform_comments.size - 1 ? 'border-bottom' : '' %>">
|
|
24
|
+
<div class="d-flex align-items-start gap-2 mb-2">
|
|
25
|
+
<% if comment[:avatar_url].present? %>
|
|
26
|
+
<img src="<%= comment[:avatar_url] %>" alt="<%= comment[:author] %>" width="24" height="24" class="rounded-circle" style="flex-shrink: 0;">
|
|
27
|
+
<% else %>
|
|
28
|
+
<i class="bi bi-person-circle" style="font-size: 1.3em; flex-shrink: 0;"></i>
|
|
29
|
+
<% end %>
|
|
30
|
+
<div>
|
|
31
|
+
<strong class="text-primary"><%= comment[:author] || "Unknown" %></strong>
|
|
32
|
+
<small class="text-muted ms-2"><%= comment[:created_at] ? Time.parse(comment[:created_at]).strftime("%b %d, %Y %H:%M") : "" %></small>
|
|
80
33
|
</div>
|
|
81
|
-
|
|
82
|
-
|
|
34
|
+
</div>
|
|
35
|
+
<div class="ms-4 text-break" style="font-size: 0.9em;">
|
|
36
|
+
<%= simple_format(h(comment[:body].to_s.truncate(500)), {}, wrapper_tag: "span") %>
|
|
37
|
+
</div>
|
|
83
38
|
</div>
|
|
39
|
+
<% end %>
|
|
40
|
+
</div>
|
|
41
|
+
<% end %>
|
|
84
42
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
43
|
+
<% if has_audit_comments %>
|
|
44
|
+
<!-- Audit trail — workflow actions -->
|
|
45
|
+
<div class="px-3 py-2 bg-light border-top">
|
|
46
|
+
<small class="text-muted fw-bold"><i class="bi bi-clock-history"></i> Activity Log</small>
|
|
47
|
+
</div>
|
|
48
|
+
<% error.comments.recent_first.each_with_index do |comment, index| %>
|
|
49
|
+
<div class="px-3 py-2 <%= index < error.comments.count - 1 ? 'border-bottom' : '' %>">
|
|
50
|
+
<div class="d-flex justify-content-between align-items-start">
|
|
51
|
+
<div>
|
|
52
|
+
<small class="text-primary fw-bold"><i class="bi bi-person-circle"></i> <%= comment.author_name %></small>
|
|
53
|
+
<small class="text-muted ms-2"><%= comment.formatted_time %></small>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<small class="text-muted text-break"><%= simple_format(h(comment.body), {}, wrapper_tag: "span") %></small>
|
|
57
|
+
</div>
|
|
58
|
+
<% end %>
|
|
59
|
+
<% end %>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
<% elsif has_linked_issue %>
|
|
63
|
+
<!-- Issue linked but no comments yet -->
|
|
64
|
+
<div class="card mb-4" id="section-discussion">
|
|
65
|
+
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
|
66
|
+
<h5 class="mb-0">
|
|
67
|
+
<i class="bi bi-chat-dots"></i> Discussion
|
|
68
|
+
</h5>
|
|
69
|
+
<a href="<%= error.external_issue_url %>" target="_blank" rel="noopener" class="btn btn-sm btn-outline-primary">
|
|
70
|
+
<i class="bi bi-box-arrow-up-right me-1"></i> Start discussion on <%= error.external_issue_provider&.capitalize || "Platform" %>
|
|
71
|
+
</a>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="card-body text-center py-3">
|
|
74
|
+
<p class="text-muted small mb-0">No comments yet. Be the first to discuss this error on <%= error.external_issue_provider&.capitalize || "the platform" %>.</p>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
<% elsif has_audit_comments %>
|
|
78
|
+
<!-- No issue linked, but has audit trail -->
|
|
79
|
+
<div class="card mb-4" id="section-discussion">
|
|
80
|
+
<div class="card-header bg-white">
|
|
81
|
+
<h5 class="mb-0">
|
|
82
|
+
<i class="bi bi-clock-history"></i> Activity Log
|
|
83
|
+
</h5>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="card-body p-0">
|
|
86
|
+
<% error.comments.recent_first.each_with_index do |comment, index| %>
|
|
87
|
+
<div class="px-3 py-2 <%= index < error.comments.count - 1 ? 'border-bottom' : '' %>">
|
|
88
|
+
<div class="d-flex justify-content-between align-items-start">
|
|
89
|
+
<div>
|
|
90
|
+
<small class="text-primary fw-bold"><i class="bi bi-person-circle"></i> <%= comment.author_name %></small>
|
|
91
|
+
<small class="text-muted ms-2"><%= comment.formatted_time %></small>
|
|
92
|
+
</div>
|
|
105
93
|
</div>
|
|
94
|
+
<small class="text-muted text-break"><%= simple_format(h(comment.body), {}, wrapper_tag: "span") %></small>
|
|
106
95
|
</div>
|
|
107
96
|
<% end %>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
<% end %>
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<% if RailsErrorDashboard.configuration.enable_issue_tracking %>
|
|
2
|
+
<div class="card mb-4" id="issue-tracking">
|
|
3
|
+
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
|
4
|
+
<h5 class="mb-0">
|
|
5
|
+
<i class="bi bi-link-45deg me-2"></i>
|
|
6
|
+
Issue Tracker
|
|
7
|
+
</h5>
|
|
8
|
+
<% if @error.external_issue_url.present? %>
|
|
9
|
+
<a href="<%= @error.external_issue_url %>" target="_blank" rel="noopener" class="btn btn-sm btn-outline-primary">
|
|
10
|
+
<i class="bi bi-box-arrow-up-right me-1"></i> View Issue
|
|
11
|
+
</a>
|
|
12
|
+
<% end %>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="card-body">
|
|
15
|
+
<% if @error.external_issue_url.present? %>
|
|
16
|
+
<% provider = @error.external_issue_provider %>
|
|
17
|
+
<% icon = case provider
|
|
18
|
+
when "github" then "bi-github"
|
|
19
|
+
when "gitlab" then "bi-gitlab"
|
|
20
|
+
when "codeberg" then "bi-git"
|
|
21
|
+
else "bi-link-45deg"
|
|
22
|
+
end %>
|
|
23
|
+
<% issue = @platform_issue %>
|
|
24
|
+
|
|
25
|
+
<!-- Issue badge + status -->
|
|
26
|
+
<div class="d-flex align-items-center gap-2 mb-3">
|
|
27
|
+
<span class="badge bg-success">
|
|
28
|
+
<i class="bi <%= icon %> me-1"></i>
|
|
29
|
+
<%= provider&.capitalize || "Linked" %> #<%= @error.external_issue_number || "?" %>
|
|
30
|
+
</span>
|
|
31
|
+
<% if issue %>
|
|
32
|
+
<% state_color = issue[:state] == "open" ? "success" : "secondary" %>
|
|
33
|
+
<% state_icon = issue[:state] == "open" ? "bi-circle-fill" : "bi-check-circle-fill" %>
|
|
34
|
+
<span class="badge bg-<%= state_color %>">
|
|
35
|
+
<i class="bi <%= state_icon %> me-1" style="font-size: 0.6em;"></i>
|
|
36
|
+
<%= issue[:state]&.capitalize %>
|
|
37
|
+
</span>
|
|
38
|
+
<% end %>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<% if issue %>
|
|
42
|
+
<div class="row">
|
|
43
|
+
<!-- Assignees -->
|
|
44
|
+
<div class="col-md-6 mb-2">
|
|
45
|
+
<small class="text-muted d-block mb-1">Assignees</small>
|
|
46
|
+
<% if issue[:assignees].present? && issue[:assignees].any? %>
|
|
47
|
+
<div class="d-flex align-items-center gap-2 flex-wrap">
|
|
48
|
+
<% issue[:assignees].each do |assignee| %>
|
|
49
|
+
<span class="d-inline-flex align-items-center gap-1 border rounded-pill px-2 py-1" style="font-size: 0.85em;">
|
|
50
|
+
<% if assignee[:avatar_url].present? %>
|
|
51
|
+
<img src="<%= assignee[:avatar_url] %>" width="18" height="18" class="rounded-circle">
|
|
52
|
+
<% end %>
|
|
53
|
+
<%= assignee[:login] %>
|
|
54
|
+
</span>
|
|
55
|
+
<% end %>
|
|
56
|
+
</div>
|
|
57
|
+
<% else %>
|
|
58
|
+
<span class="text-muted" style="font-size: 0.85em;">Unassigned</span>
|
|
59
|
+
<% end %>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<!-- Labels -->
|
|
63
|
+
<div class="col-md-6 mb-2">
|
|
64
|
+
<small class="text-muted d-block mb-1">Labels</small>
|
|
65
|
+
<% if issue[:labels].present? && issue[:labels].any? %>
|
|
66
|
+
<div class="d-flex align-items-center gap-1 flex-wrap">
|
|
67
|
+
<% issue[:labels].each do |label| %>
|
|
68
|
+
<% bg_color = label[:color].present? ? "##{label[:color]}" : "#6c757d" %>
|
|
69
|
+
<% text_color = label[:color].present? && label[:color].scan(/../).map { |c| c.to_i(16) }.sum > 382 ? "#000" : "#fff" %>
|
|
70
|
+
<span class="badge rounded-pill" style="background-color: <%= bg_color %>; color: <%= text_color %>; font-size: 0.78em;">
|
|
71
|
+
<%= label[:name] %>
|
|
72
|
+
</span>
|
|
73
|
+
<% end %>
|
|
74
|
+
</div>
|
|
75
|
+
<% else %>
|
|
76
|
+
<span class="text-muted" style="font-size: 0.85em;">No labels</span>
|
|
77
|
+
<% end %>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
<% end %>
|
|
81
|
+
|
|
82
|
+
<% else %>
|
|
83
|
+
<!-- No linked issue — show create/link options -->
|
|
84
|
+
<div class="row">
|
|
85
|
+
<div class="col-md-6">
|
|
86
|
+
<h6 class="text-muted mb-2">Create New Issue</h6>
|
|
87
|
+
<% provider = RailsErrorDashboard.configuration.effective_issue_tracker_provider %>
|
|
88
|
+
<% if provider && RailsErrorDashboard.configuration.effective_issue_tracker_token %>
|
|
89
|
+
<%= button_to create_issue_error_path(@error), method: :post, class: "btn btn-sm btn-primary" do %>
|
|
90
|
+
<i class="bi bi-plus-circle me-1"></i>
|
|
91
|
+
Create <%= provider.to_s.capitalize %> Issue
|
|
92
|
+
<% end %>
|
|
93
|
+
<% else %>
|
|
94
|
+
<p class="text-muted small mb-0">
|
|
95
|
+
<i class="bi bi-info-circle"></i>
|
|
96
|
+
Configure <code>issue_tracker_token</code> and <code>git_repository_url</code> in your initializer to enable issue creation.
|
|
97
|
+
</p>
|
|
98
|
+
<% end %>
|
|
99
|
+
</div>
|
|
100
|
+
<div class="col-md-6">
|
|
101
|
+
<h6 class="text-muted mb-2">Link Existing Issue</h6>
|
|
102
|
+
<%= form_tag link_issue_error_path(@error), method: :post, class: "d-flex gap-2" do %>
|
|
103
|
+
<%= text_field_tag :issue_url, nil, placeholder: "https://github.com/user/repo/issues/42", class: "form-control form-control-sm", required: true %>
|
|
104
|
+
<button type="submit" class="btn btn-sm btn-outline-secondary text-nowrap">
|
|
105
|
+
<i class="bi bi-link"></i> Link
|
|
106
|
+
</button>
|
|
107
|
+
<% end %>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
<% end %>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<% if flash[:new_issue_url].present? %>
|
|
115
|
+
<script>
|
|
116
|
+
(function() {
|
|
117
|
+
window.open('<%= j flash[:new_issue_url] %>', '_blank');
|
|
118
|
+
})();
|
|
119
|
+
</script>
|
|
120
|
+
<% end %>
|
|
121
|
+
<% end %>
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
{ id: 'section-similar-errors', icon: 'bi-diagram-3', label: 'Similar' },
|
|
67
67
|
{ id: 'section-co-occurring', icon: 'bi-intersect', label: 'Co-occurring' },
|
|
68
68
|
{ id: 'section-timeline', icon: 'bi-clock-history', label: 'Timeline' },
|
|
69
|
+
{ id: 'issue-tracking', icon: 'bi-link-45deg', label: 'Issue' },
|
|
69
70
|
{ id: 'section-discussion', icon: 'bi-chat-dots', label: 'Discussion' },
|
|
70
71
|
{ id: 'section-error-cascades', icon: 'bi-share', label: 'Cascades' },
|
|
71
72
|
{ id: 'section-occurrence-patterns', icon: 'bi-graph-up', label: 'Patterns' }
|
|
@@ -71,88 +71,92 @@
|
|
|
71
71
|
<% end %>
|
|
72
72
|
</div>
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
-
<%
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<%=
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
<
|
|
86
|
-
|
|
87
|
-
|
|
74
|
+
<% unless RailsErrorDashboard.configuration.enable_issue_tracking %>
|
|
75
|
+
<!-- Workflow Status (hidden when issue tracking enabled — platform is source of truth) -->
|
|
76
|
+
<div class="mb-3">
|
|
77
|
+
<small class="metadata-label d-block mb-1">Workflow Status</small>
|
|
78
|
+
<% if error.respond_to?(:status) %>
|
|
79
|
+
<% badge_color = error.status_badge_color %>
|
|
80
|
+
<% text_dark = %w[info warning light].include?(badge_color) ? 'text-dark' : '' %>
|
|
81
|
+
<span class="badge bg-<%= badge_color %> <%= text_dark %> fs-6">
|
|
82
|
+
<%= error.status.titleize %>
|
|
83
|
+
</span>
|
|
84
|
+
<% elsif error.resolved? %>
|
|
85
|
+
<span class="badge bg-success">
|
|
86
|
+
<i class="bi bi-check-circle"></i> Resolved
|
|
87
|
+
</span>
|
|
88
|
+
<% if error.resolved_at.present? %>
|
|
89
|
+
<br>
|
|
90
|
+
<small class="text-muted mt-1 d-block">
|
|
91
|
+
<%= local_time(error.resolved_at, format: :full) %>
|
|
92
|
+
</small>
|
|
93
|
+
<% end %>
|
|
94
|
+
<% else %>
|
|
95
|
+
<span class="badge bg-danger">
|
|
96
|
+
<i class="bi bi-exclamation-circle"></i> Unresolved
|
|
97
|
+
</span>
|
|
98
|
+
<% end %>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<!-- Reopened indicator -->
|
|
102
|
+
<% if error.reopened? %>
|
|
103
|
+
<div class="mb-3">
|
|
104
|
+
<small class="metadata-label d-block mb-1">Reopened</small>
|
|
105
|
+
<span class="badge bg-warning text-dark">
|
|
106
|
+
<i class="bi bi-arrow-counterclockwise"></i> Reopened
|
|
107
|
+
</span>
|
|
88
108
|
<br>
|
|
89
109
|
<small class="text-muted mt-1 d-block">
|
|
90
|
-
<%= local_time(error.
|
|
110
|
+
<%= local_time(error.reopened_at, format: :full) %>
|
|
91
111
|
</small>
|
|
92
|
-
|
|
93
|
-
<% else %>
|
|
94
|
-
<span class="badge bg-danger">
|
|
95
|
-
<i class="bi bi-exclamation-circle"></i> Unresolved
|
|
96
|
-
</span>
|
|
112
|
+
</div>
|
|
97
113
|
<% end %>
|
|
98
|
-
</div>
|
|
99
114
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
<div class="d-flex align-items-center justify-content-between">
|
|
120
|
-
<div>
|
|
121
|
-
<i class="bi bi-person-fill text-primary"></i>
|
|
122
|
-
<strong><%= error.assigned_to %></strong>
|
|
123
|
-
<% if error.assigned_at.present? %>
|
|
124
|
-
<br>
|
|
125
|
-
<small class="text-muted">
|
|
126
|
-
<%= local_time_ago(error.assigned_at) %>
|
|
127
|
-
</small>
|
|
115
|
+
<!-- Assignment -->
|
|
116
|
+
<% if error.respond_to?(:assigned_to) %>
|
|
117
|
+
<div class="mb-3">
|
|
118
|
+
<small class="metadata-label d-block mb-1">Assigned To</small>
|
|
119
|
+
<% if error.assigned? %>
|
|
120
|
+
<div class="d-flex align-items-center justify-content-between">
|
|
121
|
+
<div>
|
|
122
|
+
<i class="bi bi-person-fill text-primary"></i>
|
|
123
|
+
<strong><%= error.assigned_to %></strong>
|
|
124
|
+
<% if error.assigned_at.present? %>
|
|
125
|
+
<br>
|
|
126
|
+
<small class="text-muted">
|
|
127
|
+
<%= local_time_ago(error.assigned_at) %>
|
|
128
|
+
</small>
|
|
129
|
+
<% end %>
|
|
130
|
+
</div>
|
|
131
|
+
<%= button_to unassign_error_path(error), method: :post, class: "btn btn-sm btn-outline-secondary",
|
|
132
|
+
data: { turbo_confirm: "Remove assignment?" } do %>
|
|
133
|
+
<i class="bi bi-x"></i>
|
|
128
134
|
<% end %>
|
|
129
135
|
</div>
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
<i class="bi bi-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
<button type="button" class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#assignModal">
|
|
137
|
-
<i class="bi bi-person-plus"></i> Assign
|
|
138
|
-
</button>
|
|
139
|
-
<% end %>
|
|
140
|
-
</div>
|
|
136
|
+
<% else %>
|
|
137
|
+
<button type="button" class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#assignModal">
|
|
138
|
+
<i class="bi bi-person-plus"></i> Assign
|
|
139
|
+
</button>
|
|
140
|
+
<% end %>
|
|
141
|
+
</div>
|
|
141
142
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
143
|
+
<!-- Priority -->
|
|
144
|
+
<div class="mb-3">
|
|
145
|
+
<small class="metadata-label d-block mb-1">Priority</small>
|
|
146
|
+
<% if error.respond_to?(:priority_level) %>
|
|
147
|
+
<span class="badge bg-<%= error.priority_color %> fs-6">
|
|
148
|
+
<%= error.priority_label %>
|
|
149
|
+
</span>
|
|
150
|
+
<button type="button" class="btn btn-sm btn-outline-secondary ms-2" data-bs-toggle="modal" data-bs-target="#priorityModal">
|
|
151
|
+
<i class="bi bi-pencil"></i>
|
|
152
|
+
</button>
|
|
153
|
+
<% end %>
|
|
154
|
+
</div>
|
|
155
|
+
<% end %>
|
|
156
|
+
<% end %>
|
|
154
157
|
|
|
155
|
-
|
|
158
|
+
<!-- Snooze (always visible — no platform equivalent) -->
|
|
159
|
+
<% if error.respond_to?(:assigned_to) %>
|
|
156
160
|
<div class="mb-3">
|
|
157
161
|
<small class="metadata-label d-block mb-1">Snooze</small>
|
|
158
162
|
<% if error.respond_to?(:snoozed?) && error.snoozed? %>
|