rails_error_dashboard 0.1.0 → 0.1.1
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 +257 -700
- data/app/controllers/rails_error_dashboard/application_controller.rb +18 -0
- data/app/controllers/rails_error_dashboard/errors_controller.rb +47 -4
- data/app/helpers/rails_error_dashboard/application_helper.rb +17 -0
- data/app/jobs/rails_error_dashboard/application_job.rb +19 -0
- data/app/jobs/rails_error_dashboard/async_error_logging_job.rb +48 -0
- data/app/jobs/rails_error_dashboard/baseline_alert_job.rb +263 -0
- data/app/jobs/rails_error_dashboard/discord_error_notification_job.rb +4 -8
- data/app/jobs/rails_error_dashboard/email_error_notification_job.rb +2 -1
- data/app/jobs/rails_error_dashboard/pagerduty_error_notification_job.rb +5 -5
- data/app/jobs/rails_error_dashboard/slack_error_notification_job.rb +10 -6
- data/app/jobs/rails_error_dashboard/webhook_error_notification_job.rb +5 -6
- data/app/mailers/rails_error_dashboard/application_mailer.rb +1 -1
- data/app/mailers/rails_error_dashboard/error_notification_mailer.rb +1 -1
- data/app/models/rails_error_dashboard/cascade_pattern.rb +74 -0
- data/app/models/rails_error_dashboard/error_baseline.rb +100 -0
- data/app/models/rails_error_dashboard/error_log.rb +326 -3
- data/app/models/rails_error_dashboard/error_occurrence.rb +49 -0
- data/app/views/layouts/rails_error_dashboard.html.erb +150 -9
- data/app/views/rails_error_dashboard/error_notification_mailer/error_alert.html.erb +3 -10
- data/app/views/rails_error_dashboard/error_notification_mailer/error_alert.text.erb +1 -2
- data/app/views/rails_error_dashboard/errors/_error_row.html.erb +76 -0
- data/app/views/rails_error_dashboard/errors/_pattern_insights.html.erb +209 -0
- data/app/views/rails_error_dashboard/errors/_stats.html.erb +34 -0
- data/app/views/rails_error_dashboard/errors/analytics.html.erb +19 -39
- data/app/views/rails_error_dashboard/errors/correlation.html.erb +373 -0
- data/app/views/rails_error_dashboard/errors/index.html.erb +215 -138
- data/app/views/rails_error_dashboard/errors/platform_comparison.html.erb +388 -0
- data/app/views/rails_error_dashboard/errors/show.html.erb +428 -11
- data/config/routes.rb +2 -0
- data/db/migrate/20251225071314_add_optimized_indexes_to_error_logs.rb +66 -0
- data/db/migrate/20251225074653_remove_environment_from_error_logs.rb +26 -0
- data/db/migrate/20251225085859_add_enhanced_metrics_to_error_logs.rb +12 -0
- data/db/migrate/20251225093603_add_similarity_tracking_to_error_logs.rb +9 -0
- data/db/migrate/20251225100236_create_error_occurrences.rb +31 -0
- data/db/migrate/20251225101920_create_cascade_patterns.rb +33 -0
- data/db/migrate/20251225102500_create_error_baselines.rb +38 -0
- data/lib/generators/rails_error_dashboard/install/install_generator.rb +270 -1
- data/lib/generators/rails_error_dashboard/install/templates/initializer.rb +251 -37
- data/lib/generators/rails_error_dashboard/solid_queue/solid_queue_generator.rb +36 -0
- data/lib/generators/rails_error_dashboard/solid_queue/templates/queue.yml +55 -0
- data/lib/rails_error_dashboard/commands/log_error.rb +234 -7
- data/lib/rails_error_dashboard/commands/resolve_error.rb +16 -0
- data/lib/rails_error_dashboard/configuration.rb +82 -5
- data/lib/rails_error_dashboard/error_reporter.rb +15 -7
- data/lib/rails_error_dashboard/middleware/error_catcher.rb +17 -10
- data/lib/rails_error_dashboard/plugin.rb +6 -3
- data/lib/rails_error_dashboard/plugins/audit_log_plugin.rb +0 -1
- data/lib/rails_error_dashboard/plugins/jira_integration_plugin.rb +2 -3
- data/lib/rails_error_dashboard/plugins/metrics_plugin.rb +0 -2
- data/lib/rails_error_dashboard/queries/analytics_stats.rb +44 -6
- data/lib/rails_error_dashboard/queries/baseline_stats.rb +107 -0
- data/lib/rails_error_dashboard/queries/co_occurring_errors.rb +86 -0
- data/lib/rails_error_dashboard/queries/dashboard_stats.rb +134 -2
- data/lib/rails_error_dashboard/queries/error_cascades.rb +74 -0
- data/lib/rails_error_dashboard/queries/error_correlation.rb +375 -0
- data/lib/rails_error_dashboard/queries/errors_list.rb +52 -11
- data/lib/rails_error_dashboard/queries/filter_options.rb +0 -1
- data/lib/rails_error_dashboard/queries/platform_comparison.rb +254 -0
- data/lib/rails_error_dashboard/queries/similar_errors.rb +93 -0
- data/lib/rails_error_dashboard/services/baseline_alert_throttler.rb +88 -0
- data/lib/rails_error_dashboard/services/baseline_calculator.rb +269 -0
- data/lib/rails_error_dashboard/services/cascade_detector.rb +95 -0
- data/lib/rails_error_dashboard/services/pattern_detector.rb +268 -0
- data/lib/rails_error_dashboard/services/similarity_calculator.rb +144 -0
- data/lib/rails_error_dashboard/value_objects/error_context.rb +27 -1
- data/lib/rails_error_dashboard/version.rb +1 -1
- data/lib/rails_error_dashboard.rb +55 -7
- metadata +52 -9
- data/app/models/rails_error_dashboard/application_record.rb +0 -5
- data/lib/rails_error_dashboard/queries/developer_insights.rb +0 -277
- data/lib/rails_error_dashboard/queries/errors_list_v2.rb +0 -149
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
<div class="container-fluid py-4">
|
|
2
|
+
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
3
|
+
<h1 class="h3 mb-0">
|
|
4
|
+
<i class="bi bi-phone me-2"></i>
|
|
5
|
+
Platform Health Comparison
|
|
6
|
+
</h1>
|
|
7
|
+
|
|
8
|
+
<div class="btn-group" role="group">
|
|
9
|
+
<%= link_to platform_comparison_errors_path(days: 7), class: "btn btn-sm #{@days == 7 ? 'btn-primary' : 'btn-outline-primary'}" do %>
|
|
10
|
+
7 Days
|
|
11
|
+
<% end %>
|
|
12
|
+
<%= link_to platform_comparison_errors_path(days: 14), class: "btn btn-sm #{@days == 14 ? 'btn-primary' : 'btn-outline-primary'}" do %>
|
|
13
|
+
14 Days
|
|
14
|
+
<% end %>
|
|
15
|
+
<%= link_to platform_comparison_errors_path(days: 30), class: "btn btn-sm #{@days == 30 ? 'btn-primary' : 'btn-outline-primary'}" do %>
|
|
16
|
+
30 Days
|
|
17
|
+
<% end %>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<% if @platform_health.empty? %>
|
|
22
|
+
<div class="alert alert-info">
|
|
23
|
+
<i class="bi bi-info-circle me-2"></i>
|
|
24
|
+
No platform data available for the selected time period.
|
|
25
|
+
</div>
|
|
26
|
+
<% else %>
|
|
27
|
+
<!-- Platform Health Summary Cards -->
|
|
28
|
+
<div class="row mb-4">
|
|
29
|
+
<% @platform_health.each do |platform, health| %>
|
|
30
|
+
<div class="col-md-6 col-lg-3 mb-3">
|
|
31
|
+
<div class="card h-100 <%= health[:health_status] == :healthy ? 'border-success' : health[:health_status] == :warning ? 'border-warning' : 'border-danger' %>">
|
|
32
|
+
<div class="card-body">
|
|
33
|
+
<h5 class="card-title text-capitalize">
|
|
34
|
+
<%= platform || 'Unknown' %>
|
|
35
|
+
<% if health[:health_status] == :healthy %>
|
|
36
|
+
<span class="badge bg-success float-end">Healthy</span>
|
|
37
|
+
<% elsif health[:health_status] == :warning %>
|
|
38
|
+
<span class="badge bg-warning float-end">Warning</span>
|
|
39
|
+
<% else %>
|
|
40
|
+
<span class="badge bg-danger float-end">Critical</span>
|
|
41
|
+
<% end %>
|
|
42
|
+
</h5>
|
|
43
|
+
|
|
44
|
+
<div class="mb-3">
|
|
45
|
+
<div class="d-flex justify-content-between align-items-center mb-1">
|
|
46
|
+
<small class="text-muted">Stability Score</small>
|
|
47
|
+
<strong><%= health[:stability_score] %>/100</strong>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="progress" style="height: 8px;">
|
|
50
|
+
<div class="progress-bar <%= health[:stability_score] >= 80 ? 'bg-success' : health[:stability_score] >= 60 ? 'bg-warning' : 'bg-danger' %>"
|
|
51
|
+
role="progressbar"
|
|
52
|
+
style="width: <%= health[:stability_score] %>%">
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<div class="small">
|
|
58
|
+
<div class="d-flex justify-content-between mb-1">
|
|
59
|
+
<span class="text-muted">Total Errors:</span>
|
|
60
|
+
<strong><%= health[:total_errors] %></strong>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="d-flex justify-content-between mb-1">
|
|
63
|
+
<span class="text-muted">Critical:</span>
|
|
64
|
+
<strong class="text-danger"><%= health[:critical_errors] %></strong>
|
|
65
|
+
</div>
|
|
66
|
+
<div class="d-flex justify-content-between mb-1">
|
|
67
|
+
<span class="text-muted">Unresolved:</span>
|
|
68
|
+
<strong><%= health[:unresolved_errors] %></strong>
|
|
69
|
+
</div>
|
|
70
|
+
<div class="d-flex justify-content-between mb-1">
|
|
71
|
+
<span class="text-muted">Resolution Rate:</span>
|
|
72
|
+
<strong><%= health[:resolution_rate] %>%</strong>
|
|
73
|
+
</div>
|
|
74
|
+
<div class="d-flex justify-content-between">
|
|
75
|
+
<span class="text-muted">Error Velocity:</span>
|
|
76
|
+
<strong class="<%= health[:error_velocity] > 0 ? 'text-danger' : 'text-success' %>">
|
|
77
|
+
<%= health[:error_velocity] > 0 ? '+' : '' %><%= health[:error_velocity] %>%
|
|
78
|
+
</strong>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
<% end %>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<!-- Error Rate Comparison Chart -->
|
|
88
|
+
<div class="card mb-4">
|
|
89
|
+
<div class="card-header bg-white">
|
|
90
|
+
<h5 class="mb-0">
|
|
91
|
+
<i class="bi bi-bar-chart me-2"></i>
|
|
92
|
+
Error Rate by Platform
|
|
93
|
+
</h5>
|
|
94
|
+
</div>
|
|
95
|
+
<div class="card-body">
|
|
96
|
+
<canvas id="errorRateChart" height="80"></canvas>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<!-- Daily Trend Charts -->
|
|
101
|
+
<div class="card mb-4">
|
|
102
|
+
<div class="card-header bg-white">
|
|
103
|
+
<h5 class="mb-0">
|
|
104
|
+
<i class="bi bi-graph-up me-2"></i>
|
|
105
|
+
Daily Error Trends
|
|
106
|
+
</h5>
|
|
107
|
+
</div>
|
|
108
|
+
<div class="card-body">
|
|
109
|
+
<canvas id="dailyTrendChart" height="80"></canvas>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<div class="row mb-4">
|
|
114
|
+
<!-- Severity Distribution -->
|
|
115
|
+
<div class="col-lg-6 mb-4">
|
|
116
|
+
<div class="card h-100">
|
|
117
|
+
<div class="card-header bg-white">
|
|
118
|
+
<h5 class="mb-0">
|
|
119
|
+
<i class="bi bi-exclamation-triangle me-2"></i>
|
|
120
|
+
Severity Distribution
|
|
121
|
+
</h5>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="card-body">
|
|
124
|
+
<% @severity_distribution.each do |platform, severities| %>
|
|
125
|
+
<div class="mb-3">
|
|
126
|
+
<h6 class="text-capitalize"><%= platform || 'Unknown' %></h6>
|
|
127
|
+
<% total = severities.values.sum %>
|
|
128
|
+
<% if total > 0 %>
|
|
129
|
+
<div class="progress" style="height: 25px;">
|
|
130
|
+
<% if severities[:critical].to_i > 0 %>
|
|
131
|
+
<div class="progress-bar bg-danger"
|
|
132
|
+
role="progressbar"
|
|
133
|
+
style="width: <%= (severities[:critical].to_f / total * 100).round(1) %>%"
|
|
134
|
+
title="Critical: <%= severities[:critical] %>">
|
|
135
|
+
<%= severities[:critical] if (severities[:critical].to_f / total) > 0.1 %>
|
|
136
|
+
</div>
|
|
137
|
+
<% end %>
|
|
138
|
+
<% if severities[:high].to_i > 0 %>
|
|
139
|
+
<div class="progress-bar bg-warning"
|
|
140
|
+
role="progressbar"
|
|
141
|
+
style="width: <%= (severities[:high].to_f / total * 100).round(1) %>%"
|
|
142
|
+
title="High: <%= severities[:high] %>">
|
|
143
|
+
<%= severities[:high] if (severities[:high].to_f / total) > 0.1 %>
|
|
144
|
+
</div>
|
|
145
|
+
<% end %>
|
|
146
|
+
<% if severities[:medium].to_i > 0 %>
|
|
147
|
+
<div class="progress-bar bg-info"
|
|
148
|
+
role="progressbar"
|
|
149
|
+
style="width: <%= (severities[:medium].to_f / total * 100).round(1) %>%"
|
|
150
|
+
title="Medium: <%= severities[:medium] %>">
|
|
151
|
+
<%= severities[:medium] if (severities[:medium].to_f / total) > 0.1 %>
|
|
152
|
+
</div>
|
|
153
|
+
<% end %>
|
|
154
|
+
<% if severities[:low].to_i > 0 %>
|
|
155
|
+
<div class="progress-bar bg-secondary"
|
|
156
|
+
role="progressbar"
|
|
157
|
+
style="width: <%= (severities[:low].to_f / total * 100).round(1) %>%"
|
|
158
|
+
title="Low: <%= severities[:low] %>">
|
|
159
|
+
<%= severities[:low] if (severities[:low].to_f / total) > 0.1 %>
|
|
160
|
+
</div>
|
|
161
|
+
<% end %>
|
|
162
|
+
</div>
|
|
163
|
+
<div class="small text-muted mt-1">
|
|
164
|
+
Critical: <%= severities[:critical] || 0 %> |
|
|
165
|
+
High: <%= severities[:high] || 0 %> |
|
|
166
|
+
Medium: <%= severities[:medium] || 0 %> |
|
|
167
|
+
Low: <%= severities[:low] || 0 %>
|
|
168
|
+
</div>
|
|
169
|
+
<% else %>
|
|
170
|
+
<p class="text-muted small mb-0">No errors</p>
|
|
171
|
+
<% end %>
|
|
172
|
+
</div>
|
|
173
|
+
<% end %>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
<!-- Resolution Time Comparison -->
|
|
179
|
+
<div class="col-lg-6 mb-4">
|
|
180
|
+
<div class="card h-100">
|
|
181
|
+
<div class="card-header bg-white">
|
|
182
|
+
<h5 class="mb-0">
|
|
183
|
+
<i class="bi bi-clock-history me-2"></i>
|
|
184
|
+
Average Resolution Time
|
|
185
|
+
</h5>
|
|
186
|
+
</div>
|
|
187
|
+
<div class="card-body">
|
|
188
|
+
<canvas id="resolutionTimeChart" height="200"></canvas>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
<!-- Cross-Platform Errors -->
|
|
195
|
+
<% if @cross_platform_errors.any? %>
|
|
196
|
+
<div class="card mb-4">
|
|
197
|
+
<div class="card-header bg-white">
|
|
198
|
+
<h5 class="mb-0">
|
|
199
|
+
<i class="bi bi-layers me-2"></i>
|
|
200
|
+
Cross-Platform Errors
|
|
201
|
+
<span class="badge bg-secondary ms-2"><%= @cross_platform_errors.count %></span>
|
|
202
|
+
</h5>
|
|
203
|
+
</div>
|
|
204
|
+
<div class="card-body">
|
|
205
|
+
<div class="table-responsive">
|
|
206
|
+
<table class="table table-hover">
|
|
207
|
+
<thead>
|
|
208
|
+
<tr>
|
|
209
|
+
<th>Error Type</th>
|
|
210
|
+
<th>Platforms</th>
|
|
211
|
+
<th>Total Occurrences</th>
|
|
212
|
+
<th>Breakdown</th>
|
|
213
|
+
</tr>
|
|
214
|
+
</thead>
|
|
215
|
+
<tbody>
|
|
216
|
+
<% @cross_platform_errors.first(10).each do |error| %>
|
|
217
|
+
<tr>
|
|
218
|
+
<td>
|
|
219
|
+
<code class="small"><%= error[:error_type] %></code>
|
|
220
|
+
</td>
|
|
221
|
+
<td>
|
|
222
|
+
<% error[:platforms].each do |platform| %>
|
|
223
|
+
<span class="badge bg-secondary text-capitalize me-1"><%= platform %></span>
|
|
224
|
+
<% end %>
|
|
225
|
+
</td>
|
|
226
|
+
<td>
|
|
227
|
+
<strong><%= error[:total_occurrences] %></strong>
|
|
228
|
+
</td>
|
|
229
|
+
<td class="small text-muted">
|
|
230
|
+
<% error[:platform_breakdown].each do |platform, count| %>
|
|
231
|
+
<%= platform %>: <%= count %>
|
|
232
|
+
<% end %>
|
|
233
|
+
</td>
|
|
234
|
+
</tr>
|
|
235
|
+
<% end %>
|
|
236
|
+
</tbody>
|
|
237
|
+
</table>
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
<% end %>
|
|
242
|
+
|
|
243
|
+
<!-- Top Errors by Platform -->
|
|
244
|
+
<div class="row">
|
|
245
|
+
<% @top_errors_by_platform.each do |platform, errors| %>
|
|
246
|
+
<div class="col-lg-6 mb-4">
|
|
247
|
+
<div class="card h-100">
|
|
248
|
+
<div class="card-header bg-white">
|
|
249
|
+
<h5 class="mb-0 text-capitalize">
|
|
250
|
+
<i class="bi bi-bug me-2"></i>
|
|
251
|
+
Top Errors - <%= platform || 'Unknown' %>
|
|
252
|
+
</h5>
|
|
253
|
+
</div>
|
|
254
|
+
<div class="card-body">
|
|
255
|
+
<% if errors.any? %>
|
|
256
|
+
<div class="list-group list-group-flush">
|
|
257
|
+
<% errors.first(5).each do |error| %>
|
|
258
|
+
<%= link_to error_path(error[:id]), class: "list-group-item list-group-item-action" do %>
|
|
259
|
+
<div class="d-flex justify-content-between align-items-start">
|
|
260
|
+
<div class="flex-grow-1">
|
|
261
|
+
<code class="small"><%= error[:error_type] %></code>
|
|
262
|
+
<p class="mb-1 small text-muted"><%= error[:message] %></p>
|
|
263
|
+
</div>
|
|
264
|
+
<span class="badge bg-primary rounded-pill ms-2">
|
|
265
|
+
<%= error[:occurrence_count] %>
|
|
266
|
+
</span>
|
|
267
|
+
</div>
|
|
268
|
+
<% end %>
|
|
269
|
+
<% end %>
|
|
270
|
+
</div>
|
|
271
|
+
<% else %>
|
|
272
|
+
<p class="text-muted mb-0">No errors found</p>
|
|
273
|
+
<% end %>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
<% end %>
|
|
278
|
+
</div>
|
|
279
|
+
<% end %>
|
|
280
|
+
</div>
|
|
281
|
+
|
|
282
|
+
<script>
|
|
283
|
+
// Error Rate Chart
|
|
284
|
+
const errorRateCtx = document.getElementById('errorRateChart');
|
|
285
|
+
if (errorRateCtx) {
|
|
286
|
+
new Chart(errorRateCtx, {
|
|
287
|
+
type: 'bar',
|
|
288
|
+
data: {
|
|
289
|
+
labels: <%= raw @error_rate_by_platform.keys.map { |k| k.to_s.capitalize }.to_json %>,
|
|
290
|
+
datasets: [{
|
|
291
|
+
label: 'Total Errors',
|
|
292
|
+
data: <%= raw @error_rate_by_platform.values.to_json %>,
|
|
293
|
+
backgroundColor: 'rgba(54, 162, 235, 0.5)',
|
|
294
|
+
borderColor: 'rgba(54, 162, 235, 1)',
|
|
295
|
+
borderWidth: 1
|
|
296
|
+
}]
|
|
297
|
+
},
|
|
298
|
+
options: {
|
|
299
|
+
responsive: true,
|
|
300
|
+
maintainAspectRatio: true,
|
|
301
|
+
plugins: {
|
|
302
|
+
legend: { display: false }
|
|
303
|
+
},
|
|
304
|
+
scales: {
|
|
305
|
+
y: { beginAtZero: true }
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Daily Trend Chart
|
|
312
|
+
const dailyTrendCtx = document.getElementById('dailyTrendChart');
|
|
313
|
+
if (dailyTrendCtx) {
|
|
314
|
+
const colors = {
|
|
315
|
+
ios: 'rgba(255, 99, 132, 1)',
|
|
316
|
+
android: 'rgba(75, 192, 192, 1)',
|
|
317
|
+
api: 'rgba(255, 206, 86, 1)',
|
|
318
|
+
web: 'rgba(153, 102, 255, 1)',
|
|
319
|
+
unknown: 'rgba(201, 203, 207, 1)'
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const datasets = <%= raw @daily_trends.map { |platform, data|
|
|
323
|
+
{
|
|
324
|
+
label: platform.to_s.capitalize,
|
|
325
|
+
data: data.values,
|
|
326
|
+
borderColor: "colors['#{platform}'] || 'rgba(75, 192, 192, 1)'",
|
|
327
|
+
backgroundColor: "colors['#{platform}'] ? colors['#{platform}'].replace('1)', '0.1)') : 'rgba(75, 192, 192, 0.1)'",
|
|
328
|
+
tension: 0.1
|
|
329
|
+
}
|
|
330
|
+
}.to_json %>;
|
|
331
|
+
|
|
332
|
+
// Replace string placeholders with actual colors
|
|
333
|
+
datasets.forEach(ds => {
|
|
334
|
+
const platform = ds.label.toLowerCase();
|
|
335
|
+
ds.borderColor = colors[platform] || 'rgba(75, 192, 192, 1)';
|
|
336
|
+
ds.backgroundColor = colors[platform] ? colors[platform].replace('1)', '0.1)') : 'rgba(75, 192, 192, 0.1)';
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
new Chart(dailyTrendCtx, {
|
|
340
|
+
type: 'line',
|
|
341
|
+
data: {
|
|
342
|
+
labels: <%= raw @daily_trends.values.first&.keys&.map { |d| d.strftime('%b %d') }&.to_json || [].to_json %>,
|
|
343
|
+
datasets: datasets
|
|
344
|
+
},
|
|
345
|
+
options: {
|
|
346
|
+
responsive: true,
|
|
347
|
+
maintainAspectRatio: true,
|
|
348
|
+
plugins: {
|
|
349
|
+
legend: { position: 'top' }
|
|
350
|
+
},
|
|
351
|
+
scales: {
|
|
352
|
+
y: { beginAtZero: true }
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Resolution Time Chart
|
|
359
|
+
const resolutionTimeCtx = document.getElementById('resolutionTimeChart');
|
|
360
|
+
if (resolutionTimeCtx) {
|
|
361
|
+
const platforms = <%= raw @resolution_times.keys.map { |k| k.to_s.capitalize }.to_json %>;
|
|
362
|
+
const times = <%= raw @resolution_times.values.map { |v| v || 0 }.to_json %>;
|
|
363
|
+
|
|
364
|
+
new Chart(resolutionTimeCtx, {
|
|
365
|
+
type: 'horizontalBar',
|
|
366
|
+
data: {
|
|
367
|
+
labels: platforms,
|
|
368
|
+
datasets: [{
|
|
369
|
+
label: 'Hours to Resolve',
|
|
370
|
+
data: times,
|
|
371
|
+
backgroundColor: 'rgba(255, 159, 64, 0.5)',
|
|
372
|
+
borderColor: 'rgba(255, 159, 64, 1)',
|
|
373
|
+
borderWidth: 1
|
|
374
|
+
}]
|
|
375
|
+
},
|
|
376
|
+
options: {
|
|
377
|
+
responsive: true,
|
|
378
|
+
maintainAspectRatio: false,
|
|
379
|
+
plugins: {
|
|
380
|
+
legend: { display: false }
|
|
381
|
+
},
|
|
382
|
+
scales: {
|
|
383
|
+
x: { beginAtZero: true, title: { display: true, text: 'Hours' } }
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
</script>
|