rails_vitals 0.2.0 → 0.3.0

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.
@@ -0,0 +1,186 @@
1
+ <style>
2
+ .plan-node {
3
+ border-left: 2px solid #4a5568;
4
+ margin-left: 16px;
5
+ padding-left: 16px;
6
+ margin-top: 8px;
7
+ }
8
+ .plan-node:first-child {
9
+ margin-left: 0;
10
+ padding-left: 0;
11
+ border-left: none;
12
+ }
13
+ .node-box {
14
+ border-radius: 6px;
15
+ padding: 12px 16px;
16
+ margin-bottom: 4px;
17
+ cursor: pointer;
18
+ transition: opacity 0.15s;
19
+ }
20
+ .node-box:hover { opacity: 0.85; }
21
+ </style>
22
+
23
+ <%# Header %>
24
+ <div class="mb-20">
25
+ <% if @record %>
26
+ <a href="<%= rails_vitals.request_path(@record.id) %>" class="text-blue" style="font-size:13px;">
27
+ ← Back to Request Detail
28
+ </a>
29
+ <% end %>
30
+
31
+ <h2 class="page-heading" style="margin:8px 0;">
32
+ EXPLAIN Visualizer
33
+ </h2>
34
+
35
+ <div class="mono text-grey text-sm word-break">
36
+ <%= @sql %>
37
+ </div>
38
+ </div>
39
+
40
+ <% if @result.error %>
41
+ <div
42
+ class="text-danger"
43
+ style="background:#2d1515;border:1px solid #fc818166;border-radius:6px;padding:16px;font-size:13px;"
44
+ >
45
+ <%= @result.error %>
46
+ </div>
47
+ <% else %>
48
+ <%# Summary stats row %>
49
+ <div style="display:grid;grid-template-columns:repeat(5,1fr);gap:12px;margin-bottom:24px;">
50
+ <% stats = [
51
+ { label: "Total Cost", value: @result.total_cost, color: cost_color(@result.total_cost) },
52
+ { label: "Actual Time", value: "#{@result.actual_time_ms}ms", color: time_color(@result.actual_time_ms) },
53
+ { label: "Rows Examined", value: @result.rows_examined, color: rows_color(@result.rows_examined) },
54
+ { label: "Warnings", value: @result.warnings.size, color: @result.warnings.any? ? "#fc8181" : "#68d391" },
55
+ { label: "Row Width", value: "#{@result.plan&.plan_width}B", color: @result.plan&.plan_width.to_i > 200 ? "#f6ad55" : "#68d391" }
56
+ ] %>
57
+
58
+ <% stats.each do |s| %>
59
+ <div class="stat-card-dark">
60
+ <div class="bold mono text-24" style="color:<%= s[:color] %>;">
61
+ <%= s[:value] %>
62
+ </div>
63
+ <div class="text-muted text-sm mt-4">
64
+ <%= s[:label] %>
65
+ </div>
66
+ </div>
67
+ <% end %>
68
+ </div>
69
+
70
+ <%# Warnings %>
71
+ <% if @result.warnings.any? %>
72
+ <div class="card mb-20">
73
+ <div class="card-title text-danger">
74
+ ⚠ Warnings (<%= @result.warnings.size %>)
75
+ </div>
76
+
77
+ <% @result.warnings.each do |w| %>
78
+ <% severity_color = w[:severity] == :danger ? "#fc8181" : "#f6ad55" %>
79
+
80
+ <div
81
+ style="background:#1a202c;border-left:3px solid <%= severity_color %>;
82
+ border-radius:4px;padding:12px 16px;margin-bottom:8px;font-size:13px;"
83
+ >
84
+ <% case w[:type] %>
85
+ <% when :sequential_scan %>
86
+ <div class="bold mb-4" style="color:<%= severity_color %>;">
87
+ Sequential Scan on
88
+ <span class="mono"><%= w[:table] %></span>
89
+ </div>
90
+ <div class="text-muted">
91
+ Scanned <%= (w[:rows].to_i + w[:removed].to_i).to_s %> rows, returned
92
+ <%= w[:rows] %>, removed <%= w[:removed] || 0 %> by filter.
93
+ <% if w[:filter] %>
94
+ <span class="mono text-grey text-sm">Filter: <%= w[:filter] %></span>
95
+ <% end %>
96
+ </div>
97
+ <% when :sort_without_index %>
98
+ <div class="bold mb-4" style="color:<%= severity_color %>;">Sort without index</div>
99
+ <div class="text-muted">
100
+ PostgreSQL sorted rows in memory — ORDER BY column may lack an index.
101
+ </div>
102
+ <% when :large_nested_loop %>
103
+ <div class="bold mb-4" style="color:<%= severity_color %>;">
104
+ Large Nested Loop (<%= w[:rows] %> rows)
105
+ </div>
106
+ <div class="text-muted">
107
+ Nested Loop processed a large number of rows. Verify join columns are indexed.
108
+ </div>
109
+ <% end %>
110
+ </div>
111
+ <% end %>
112
+ </div>
113
+ <% end %>
114
+
115
+ <%# Fix suggestions %>
116
+ <% if @result.suggestions.any? %>
117
+ <div class="card mb-20">
118
+ <div class="card-title text-healthy">💡 Fix Suggestions</div>
119
+
120
+ <% @result.suggestions.each do |s| %>
121
+ <% s_color = s[:severity] == :danger ? "#fc8181" : "#f6ad55" %>
122
+
123
+ <div class="info-block mb-12">
124
+ <div class="bold mb-8" style="color:<%= s_color %>;font-size:13px;">
125
+ <%= s[:title] %>
126
+ </div>
127
+
128
+ <div class="text-muted mb-12 line-relaxed" style="font-size:13px;">
129
+ <%= s[:body] %>
130
+ </div>
131
+
132
+ <% if s[:migration] %>
133
+ <div class="mb-6">
134
+ <div class="mini-label">Migration</div>
135
+ <div class="code-block-dark text-healthy"><%= s[:migration] %></div>
136
+ </div>
137
+ <% end %>
138
+
139
+ <% if s[:command] %>
140
+ <div>
141
+ <div class="mini-label">Generate</div>
142
+ <div class="code-block-dark text-blue">$ <%= s[:command] %></div>
143
+ </div>
144
+ <% end %>
145
+ </div>
146
+ <% end %>
147
+ </div>
148
+ <% end %>
149
+
150
+ <%# Interpretation summary %>
151
+ <% if @result.interpretation %>
152
+ <div
153
+ class="mb-16 line-relaxed"
154
+ style="
155
+ background:#1a202c;
156
+ border-radius:6px;
157
+ padding:12px 16px;
158
+ font-size:13px;
159
+ color:<%= @result.warnings.any? ? '#f6ad55' : '#68d391' %>;
160
+ border-left:3px solid <%= @result.warnings.any? ? '#f6ad55' : '#68d391' %>;
161
+ "
162
+ >
163
+ <%= @result.interpretation %>
164
+ </div>
165
+ <% end %>
166
+
167
+ <%# Plan tree %>
168
+ <div class="card">
169
+ <div class="card-title">
170
+ Execution Plan
171
+ <span class="text-muted text-sm ml-8" style="font-weight:normal;">click any node to expand explanation</span>
172
+ </div>
173
+
174
+ <div style="padding:8px 0;">
175
+ <%= render partial: "rails_vitals/explains/plan_node",
176
+ locals: { node: @result.plan, depth: 0 } %>
177
+ </div>
178
+ </div>
179
+ <% end %>
180
+
181
+ <%# Push current URL into history so back-nav reuses the cached EXPLAIN result %>
182
+ <script>
183
+ if (window.history && window.history.replaceState) {
184
+ window.history.replaceState(null, "", window.location.href);
185
+ }
186
+ </script>
@@ -1,6 +1,6 @@
1
- <div style="margin-bottom:24px;">
2
- <h1 style="font-size:20px;font-weight:bold;color:#e2e8f0;">Endpoint Heatmap</h1>
3
- <p style="color:#a0aec0;font-size:13px;margin-top:4px;">
1
+ <div class="page-header">
2
+ <h1 class="page-heading">Endpoint Heatmap</h1>
3
+ <p class="page-subtitle">
4
4
  <%= @total %> requests recorded across <%= @heatmap.size %> endpoints
5
5
  </p>
6
6
  </div>
@@ -23,17 +23,17 @@
23
23
  <% @heatmap.each do |row| %>
24
24
  <% color = score_label_to_color(row[:avg_score]) %>
25
25
  <tr>
26
- <td style="font-weight:bold;">
26
+ <td class="bold">
27
27
  <%= link_to row[:endpoint],
28
28
  requests_path(endpoint: row[:endpoint]),
29
- style: "color:#90cdf4;" %>
29
+ class: "text-accent" %>
30
30
  </td>
31
31
  <td>
32
32
  <span class="badge badge-<%= color %>">
33
33
  <%= row[:avg_score] %>
34
34
  </span>
35
35
  </td>
36
- <td style="color:#a0aec0;"><%= row[:hits] %></td>
36
+ <td class="text-muted"><%= row[:hits] %></td>
37
37
  <td>
38
38
  <span style="color:<%= query_heat_color(row[:avg_queries]) %>">
39
39
  <%= row[:avg_queries] %>
@@ -60,7 +60,7 @@
60
60
  </table>
61
61
  </div>
62
62
  <% else %>
63
- <div class="card" style="color:#a0aec0;text-align:center;padding:48px;">
63
+ <div class="card empty-state">
64
64
  No requests recorded yet. Visit your app to generate data.
65
65
  </div>
66
66
  <% end %>
@@ -1,42 +1,35 @@
1
- <div style="margin-bottom:24px;">
2
- <h1 style="font-size:20px;font-weight:bold;color:#e2e8f0;">Per-Model Breakdown</h1>
3
- <p style="color:#a0aec0;font-size:13px;margin-top:4px;">
1
+ <div class="page-header">
2
+ <h1 class="page-heading">Per-Model Breakdown</h1>
3
+ <p class="page-subtitle">
4
4
  Query activity aggregated across <%= @total_requests %> recorded requests
5
5
  </p>
6
6
  </div>
7
7
 
8
8
  <% if @breakdown.any? %>
9
9
  <% @breakdown.each do |row| %>
10
- <div class="card" style="margin-bottom:16px;">
10
+ <div class="card mb-16">
11
11
 
12
12
  <%# Model header %>
13
- <div style="
14
- display:flex;
15
- justify-content:space-between;
16
- align-items:center;
17
- margin-bottom:16px;
18
- padding-bottom:12px;
19
- border-bottom:1px solid #2d3748;
20
- ">
21
- <span style="font-size:16px;font-weight:bold;color:#90cdf4;">
13
+ <div class="flex-between mb-16 section-divider">
14
+ <span class="text-accent bold text-16">
22
15
  <%= row[:model] %>
23
16
  </span>
24
- <div style="display:flex;gap:24px;font-size:13px;">
17
+ <div class="flex" style="gap:24px;">
25
18
  <span>
26
- <span style="color:#a0aec0;">Queries</span>
27
- <span style="color:#e2e8f0;margin-left:6px;font-weight:bold;">
19
+ <span class="text-muted">Queries</span>
20
+ <span class="text-primary bold ml-6">
28
21
  <%= row[:query_count] %>
29
22
  </span>
30
23
  </span>
31
24
  <span>
32
- <span style="color:#a0aec0;">Total DB Time</span>
33
- <span style="color:<%= time_heat_color(row[:total_time_ms]) %>;margin-left:6px;font-weight:bold;">
25
+ <span class="text-muted">Total DB Time</span>
26
+ <span class="bold ml-6" style="color:<%= time_heat_color(row[:total_time_ms]) %>;">
34
27
  <%= row[:total_time_ms] %>ms
35
28
  </span>
36
29
  </span>
37
30
  <span>
38
- <span style="color:#a0aec0;">Avg Query Time</span>
39
- <span style="color:<%= time_heat_color(row[:avg_time_ms]) %>;margin-left:6px;font-weight:bold;">
31
+ <span class="text-muted">Avg Query Time</span>
32
+ <span class="bold ml-6" style="color:<%= time_heat_color(row[:avg_time_ms]) %>;">
40
33
  <%= row[:avg_time_ms] %>ms
41
34
  </span>
42
35
  </span>
@@ -47,9 +40,7 @@
47
40
 
48
41
  <%# Endpoints that triggered this model %>
49
42
  <div>
50
- <div style="color:#a0aec0;font-size:11px;font-weight:bold;margin-bottom:8px;text-transform:uppercase;letter-spacing:0.05em;">
51
- Triggered By
52
- </div>
43
+ <div class="section-label">Triggered By</div>
53
44
  <table>
54
45
  <thead>
55
46
  <tr>
@@ -63,9 +54,9 @@
63
54
  <td>
64
55
  <%= link_to endpoint,
65
56
  requests_path(endpoint: endpoint),
66
- style: "color:#90cdf4;font-size:12px;" %>
57
+ class: "text-accent" %>
67
58
  </td>
68
- <td style="color:#a0aec0;"><%= count %></td>
59
+ <td class="text-muted"><%= count %></td>
69
60
  </tr>
70
61
  <% end %>
71
62
  </tbody>
@@ -75,9 +66,7 @@
75
66
  <%# Callback types — only if present %>
76
67
  <% if row[:callbacks].any? %>
77
68
  <div>
78
- <div style="color:#a0aec0;font-size:11px;font-weight:bold;margin-bottom:8px;text-transform:uppercase;letter-spacing:0.05em;">
79
- Callbacks
80
- </div>
69
+ <div class="section-label">Callbacks</div>
81
70
  <table>
82
71
  <thead>
83
72
  <tr>
@@ -89,13 +78,7 @@
89
78
  <% row[:callbacks].each do |type, count| %>
90
79
  <tr>
91
80
  <td>
92
- <span style="
93
- background:<%= callback_color(type) %>;
94
- color:#fff;
95
- padding:1px 6px;
96
- border-radius:3px;
97
- font-size:11px;
98
- ">
81
+ <span class="callback-badge" style="background:<%= callback_color(type) %>;">
99
82
  <%= type %>
100
83
  </span>
101
84
  </td>
@@ -111,7 +94,7 @@
111
94
  </div>
112
95
  <% end %>
113
96
  <% else %>
114
- <div class="card" style="color:#a0aec0;text-align:center;padding:48px;">
97
+ <div class="card empty-state">
115
98
  No data recorded yet. Visit your app to generate data.
116
99
  </div>
117
100
  <% end %>
@@ -1,6 +1,6 @@
1
- <div style="margin-bottom:24px;">
2
- <h1 style="font-size:20px;font-weight:bold;color:#e2e8f0;">N+1 Patterns</h1>
3
- <p style="color:#a0aec0;font-size:13px;margin-top:4px;">
1
+ <div class="page-header">
2
+ <h1 class="page-heading">N+1 Patterns</h1>
3
+ <p class="page-subtitle">
4
4
  Aggregated across <%= @total_requests %> recorded requests
5
5
  </p>
6
6
  </div>
@@ -20,22 +20,22 @@
20
20
  <tbody>
21
21
  <% @patterns.each do |p| %>
22
22
  <tr>
23
- <td style="font-family:monospace;font-size:11px;color:#a0aec0;max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">
23
+ <td class="mono text-sm text-muted truncate" style="max-width:300px;">
24
24
  <%= p[:pattern] %>
25
25
  </td>
26
26
  <td>
27
- <span style="color:#fc8181;font-weight:bold;"><%= p[:occurrences] %>x</span>
27
+ <span class="text-danger bold"><%= p[:occurrences] %>x</span>
28
28
  </td>
29
- <td style="color:#a0aec0;font-size:12px;">
29
+ <td class="text-muted">
30
30
  <%= p[:endpoints].keys.join(", ") %>
31
31
  </td>
32
- <td style="font-family:monospace;font-size:11px;color:#68d391;">
32
+ <td class="mono text-sm text-healthy">
33
33
  <%= p[:fix_suggestion][:code] %>
34
34
  </td>
35
35
  <td>
36
36
  <%= link_to "Simulate →",
37
37
  n_plus_one_path(pattern_id(p)),
38
- style: "color:#90cdf4;font-size:12px;" %>
38
+ class: "text-accent" %>
39
39
  </td>
40
40
  </tr>
41
41
  <% end %>
@@ -43,7 +43,7 @@
43
43
  </table>
44
44
  </div>
45
45
  <% else %>
46
- <div class="card" style="color:#a0aec0;text-align:center;padding:48px;">
46
+ <div class="card empty-state">
47
47
  No N+1 patterns detected yet.
48
48
  </div>
49
49
  <% end %>
@@ -1,112 +1,67 @@
1
1
  <%# app/views/rails_vitals/n_plus_one/show.html.erb %>
2
- <div style="margin-bottom:24px;">
3
- <div style="margin-bottom:8px;">
4
- <%= link_to "← N+1 Patterns", n_plus_ones_path,
5
- style: "color:#a0aec0;font-size:13px;" %>
2
+ <div class="page-header">
3
+ <div class="mb-8">
4
+ <%= link_to "← N+1 Patterns", n_plus_ones_path, class: "back-link" %>
6
5
  </div>
7
- <h1 style="font-size:20px;font-weight:bold;color:#e2e8f0;">Impact Simulator</h1>
6
+ <h1 class="page-heading">Impact Simulator</h1>
8
7
  </div>
9
8
 
10
9
  <%# Pattern card %>
11
- <div class="card" style="margin-bottom:16px;">
10
+ <div class="card mb-16">
12
11
  <div class="card-title">Detected Pattern</div>
13
- <pre style="
14
- background:#1a202c;
15
- padding:12px;
16
- border-radius:6px;
17
- font-size:12px;
18
- color:#a0aec0;
19
- white-space:pre-wrap;
20
- word-break:break-all;
21
- margin-bottom:16px;
22
- "><%= @pattern[:pattern] %></pre>
12
+ <pre class="code-block text-muted mb-16"><%= @pattern[:pattern] %></pre>
23
13
 
24
14
  <div style="display:grid;grid-template-columns:repeat(3,1fr);gap:16px;font-size:13px;">
25
15
  <div>
26
- <div style="color:#a0aec0;margin-bottom:4px;">Total Occurrences</div>
27
- <div style="color:#fc8181;font-size:20px;font-weight:bold;"><%= @pattern[:occurrences] %>x</div>
16
+ <div class="text-muted mb-4">Total Occurrences</div>
17
+ <div class="text-danger stat-value-lg"><%= @pattern[:occurrences] %>x</div>
28
18
  </div>
29
19
  <div>
30
- <div style="color:#a0aec0;margin-bottom:4px;">Estimated Time Wasted</div>
31
- <div style="color:#fc8181;font-size:20px;font-weight:bold;"><%= @estimated_saving_ms %>ms</div>
20
+ <div class="text-muted mb-4">Estimated Time Wasted</div>
21
+ <div class="text-danger stat-value-lg"><%= @estimated_saving_ms %>ms</div>
32
22
  </div>
33
23
  <div>
34
- <div style="color:#a0aec0;margin-bottom:4px;">Affected Requests</div>
35
- <div style="color:#e2e8f0;font-size:20px;font-weight:bold;"><%= @affected_requests.size %></div>
24
+ <div class="text-muted mb-4">Affected Requests</div>
25
+ <div class="text-primary stat-value-lg"><%= @affected_requests.size %></div>
36
26
  </div>
37
27
  </div>
38
28
  </div>
39
29
 
40
30
  <%# Fix suggestion card %>
41
- <div class="card" style="margin-bottom:16px;">
31
+ <div class="card mb-16">
42
32
  <div class="card-title">Suggested Fix</div>
43
- <div style="margin-bottom:12px;color:#a0aec0;font-size:13px;">
33
+ <div class="text-muted mb-12">
44
34
  <%= @pattern[:fix_suggestion][:description] %>
45
35
  </div>
46
- <pre style="
47
- background:#1a202c;
48
- padding:12px;
49
- border-radius:6px;
50
- font-size:13px;
51
- color:#68d391;
52
- "><%= @pattern[:fix_suggestion][:code] %></pre>
36
+ <pre class="code-block text-healthy"><%= @pattern[:fix_suggestion][:code] %></pre>
53
37
  </div>
54
38
 
55
39
  <%# Impact simulator card %>
56
- <div class="card" style="margin-bottom:16px;">
40
+ <div class="card mb-16">
57
41
  <div class="card-title">
58
42
  Impact Simulator
59
- <span style="color:#a0aec0;font-weight:normal;font-size:12px;margin-left:8px;">
43
+ <span class="text-muted text-12 ml-8" style="font-weight:normal;">
60
44
  if you fix this N+1
61
45
  </span>
62
46
  </div>
63
47
 
64
- <div style="display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:24px;">
65
- <div style="
66
- background:#1a202c;
67
- border-radius:8px;
68
- padding:16px;
69
- text-align:center;
70
- ">
71
- <div style="color:#a0aec0;font-size:11px;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:8px;">
72
- Est. Saving Per Request
73
- </div>
74
- <div style="color:#68d391;font-size:28px;font-weight:bold;">
75
- <%= @avg_saving_per_request %>ms
76
- </div>
48
+ <div style="display:grid;grid-template-columns:repeat(3,1fr);gap:16px;" class="mb-24">
49
+ <div class="impact-box">
50
+ <div class="section-label">Est. Saving Per Request</div>
51
+ <div class="text-healthy stat-value-xl"><%= @avg_saving_per_request %>ms</div>
77
52
  </div>
78
- <div style="
79
- background:#1a202c;
80
- border-radius:8px;
81
- padding:16px;
82
- text-align:center;
83
- ">
84
- <div style="color:#a0aec0;font-size:11px;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:8px;">
85
- Total Time Recovered
86
- </div>
87
- <div style="color:#68d391;font-size:28px;font-weight:bold;">
88
- <%= @estimated_saving_ms %>ms
89
- </div>
53
+ <div class="impact-box">
54
+ <div class="section-label">Total Time Recovered</div>
55
+ <div class="text-healthy stat-value-xl"><%= @estimated_saving_ms %>ms</div>
90
56
  </div>
91
- <div style="
92
- background:#1a202c;
93
- border-radius:8px;
94
- padding:16px;
95
- text-align:center;
96
- ">
97
- <div style="color:#a0aec0;font-size:11px;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:8px;">
98
- Requests Affected
99
- </div>
100
- <div style="color:#e2e8f0;font-size:28px;font-weight:bold;">
101
- <%= @affected_requests.size %>
102
- </div>
57
+ <div class="impact-box">
58
+ <div class="section-label">Requests Affected</div>
59
+ <div class="text-primary stat-value-xl"><%= @affected_requests.size %></div>
103
60
  </div>
104
61
  </div>
105
62
 
106
63
  <%# Affected requests list %>
107
- <div style="color:#a0aec0;font-size:11px;font-weight:bold;margin-bottom:8px;text-transform:uppercase;letter-spacing:0.05em;">
108
- Affected Requests
109
- </div>
64
+ <div class="section-label">Affected Requests</div>
110
65
  <table>
111
66
  <thead>
112
67
  <tr>
@@ -127,10 +82,9 @@
127
82
  </span>
128
83
  </td>
129
84
  <td><%= r.total_query_count %></td>
130
- <td style="color:#a0aec0;"><%= r.duration_ms.round(1) %>ms</td>
85
+ <td class="text-muted"><%= r.duration_ms.round(1) %>ms</td>
131
86
  <td>
132
- <%= link_to "→", request_path(r.id),
133
- style: "color:#90cdf4;" %>
87
+ <%= link_to "→", request_path(r.id), class: "text-accent" %>
134
88
  </td>
135
89
  </tr>
136
90
  <% end %>
@@ -10,11 +10,11 @@
10
10
  </div>
11
11
 
12
12
  <% if params[:endpoint].present? %>
13
- <div style="margin:20px 0;font-size:13px;color:#a0aec0;">
13
+ <div class="text-muted" style="margin:20px 0;">
14
14
  Filtering by endpoint:
15
- <span style="color:#90cdf4;"><%= params[:endpoint] %></span>
15
+ <span class="text-accent"><%= params[:endpoint] %></span>
16
16
  &nbsp;
17
- <%= link_to "✕ Clear", requests_path, style: "color:#fc8181;text-decoration:none;" %>
17
+ <%= link_to "✕ Clear", requests_path, class: "text-danger" %>
18
18
  </div>
19
19
  <% end %>
20
20
 
@@ -35,7 +35,7 @@
35
35
  <tbody>
36
36
  <% @records.each do |r| %>
37
37
  <tr>
38
- <td style="color:#a0aec0;font-size:11px;"><%= r.recorded_at.strftime("%H:%M:%S") %></td>
38
+ <td class="text-muted text-sm"><%= r.recorded_at.strftime("%H:%M:%S") %></td>
39
39
  <td><%= r.endpoint %></td>
40
40
  <td>
41
41
  <span class="badge badge-<%= r.color %>">
@@ -48,7 +48,7 @@
48
48
  <% if r.n_plus_one_patterns.any? %>
49
49
  <span class="n1-badge"><%= r.n_plus_one_patterns.size %></span>
50
50
  <% else %>
51
- <span style="color:#68d391;">—</span>
51
+ <span class="text-healthy">—</span>
52
52
  <% end %>
53
53
  </td>
54
54
  <td><%= r.duration_ms&.round(1) %>ms</td>