code_healer 0.1.14 → 0.1.15
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/CHANGELOG.md +11 -0
- data/config/routes.rb +16 -0
- data/lib/code_healer/controllers/dashboard_controller.rb +26 -29
- data/lib/code_healer/engine.rb +12 -0
- data/lib/code_healer/version.rb +1 -1
- data/lib/code_healer/views/dashboard/healing_details.html.erb +356 -0
- data/lib/code_healer/views/dashboard/index.html.erb +370 -123
- data/lib/code_healer.rb +1 -23
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a0eb37a96fca96ae0321a1ee24addb93e3dee8d0755ca5df8934c04420ef3548
|
4
|
+
data.tar.gz: b49f5a80ae6cc86b5bc0cd1ccd798a7279ea5d444be32894fb5514599cd13482
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 98e5125e4f561073f31a8f263b5aba7b4ec62dba1d7a9c6b3dca818aa412c5f4578a55ff28dc95dd27768aa86dc61353050c43960ed29d8cc2499790bccef987
|
7
|
+
data.tar.gz: 52611005c0b0e47e16e86fb6f0d62ff0aea6a338e42de0b725e5de7a1d77713f3e89494db821d5f06c350283bf6027d629c859a3515e8dcb7cdb0cda113823f7
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
+
## [0.1.15] - 2025-08-21
|
9
|
+
|
10
|
+
### Fixed
|
11
|
+
- **Dashboard Template Loading**: Fixed template loading issues by explicitly specifying view paths
|
12
|
+
- **Engine Views Configuration**: Properly configured engine views path to resolve template missing errors
|
13
|
+
- **Controller Template Rendering**: Updated render calls to use explicit template paths
|
14
|
+
|
15
|
+
### Changed
|
16
|
+
- **Template Rendering**: Changed from implicit template rendering to explicit template path specification
|
17
|
+
- **View Path Configuration**: Enhanced engine configuration for proper view loading
|
18
|
+
|
8
19
|
## [0.1.14] - 2025-08-21
|
9
20
|
|
10
21
|
### Added
|
data/config/routes.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
CodeHealer::Engine.routes.draw do
|
2
|
+
get '/dashboard', to: 'dashboard#index'
|
3
|
+
get '/dashboard/metrics', to: 'dashboard#metrics'
|
4
|
+
get '/dashboard/trends', to: 'dashboard#trends'
|
5
|
+
get '/dashboard/performance', to: 'dashboard#performance'
|
6
|
+
get '/dashboard/healing/:healing_id', to: 'dashboard#healing_details'
|
7
|
+
|
8
|
+
# API endpoints (JSON only)
|
9
|
+
namespace :api do
|
10
|
+
get '/dashboard/summary', to: 'dashboard#summary'
|
11
|
+
get '/dashboard/metrics', to: 'dashboard#metrics'
|
12
|
+
get '/dashboard/trends', to: 'dashboard#trends'
|
13
|
+
get '/dashboard/performance', to: 'dashboard#performance'
|
14
|
+
get '/dashboard/healing/:healing_id', to: 'dashboard#healing_details'
|
15
|
+
end
|
16
|
+
end
|
@@ -1,11 +1,14 @@
|
|
1
1
|
module CodeHealer
|
2
2
|
class DashboardController < ActionController::Base
|
3
|
+
# Set the view path to look in the engine's views directory
|
4
|
+
self.view_paths = ["#{CodeHealer::Engine.root}/lib/code_healer/views"]
|
5
|
+
|
3
6
|
def index
|
4
7
|
@summary = MetricsCollector.dashboard_summary
|
5
8
|
@recent_healings = HealingMetric.order(created_at: :desc).limit(10)
|
6
9
|
|
7
10
|
respond_to do |format|
|
8
|
-
format.html {
|
11
|
+
format.html { render template: "dashboard/index" }
|
9
12
|
format.json { render json: @summary }
|
10
13
|
end
|
11
14
|
end
|
@@ -20,7 +23,7 @@ module CodeHealer
|
|
20
23
|
@metrics = @metrics.recent(params[:days].to_i) if params[:days].present?
|
21
24
|
|
22
25
|
respond_to do |format|
|
23
|
-
format.html { render :metrics }
|
26
|
+
format.html { render template: "dashboard/metrics" }
|
24
27
|
format.json { render json: @metrics }
|
25
28
|
end
|
26
29
|
end
|
@@ -28,22 +31,27 @@ module CodeHealer
|
|
28
31
|
def healing_details
|
29
32
|
@healing = HealingMetric.find_by(healing_id: params[:healing_id])
|
30
33
|
|
31
|
-
|
32
|
-
render
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
34
|
+
respond_to do |format|
|
35
|
+
format.html { render template: "dashboard/healing_details" }
|
36
|
+
format.json do
|
37
|
+
if @healing
|
38
|
+
render json: {
|
39
|
+
healing: @healing,
|
40
|
+
timing: {
|
41
|
+
total_duration: @healing.duration_seconds,
|
42
|
+
ai_processing: @healing.ai_processing_seconds,
|
43
|
+
git_operations: @healing.git_operations_seconds
|
44
|
+
},
|
45
|
+
status: {
|
46
|
+
success: @healing.success_status,
|
47
|
+
evolution_method: @healing.evolution_method_display,
|
48
|
+
ai_provider: @healing.ai_provider_display
|
49
|
+
}
|
50
|
+
}
|
51
|
+
else
|
52
|
+
render json: { error: 'Healing not found' }, status: :not_found
|
53
|
+
end
|
54
|
+
end
|
47
55
|
end
|
48
56
|
end
|
49
57
|
|
@@ -77,16 +85,5 @@ module CodeHealer
|
|
77
85
|
def summary
|
78
86
|
render json: MetricsCollector.dashboard_summary
|
79
87
|
end
|
80
|
-
|
81
|
-
private
|
82
|
-
|
83
|
-
def render_dashboard
|
84
|
-
# This will be replaced with actual dashboard view
|
85
|
-
render plain: "CodeHealer Dashboard - Coming Soon!\n\n" \
|
86
|
-
"Total Healings: #{@summary[:total_healing]}\n" \
|
87
|
-
"Success Rate: #{@summary[:success_rate]}%\n" \
|
88
|
-
"Healings Today: #{@summary[:healings_today]}\n" \
|
89
|
-
"Healings This Week: #{@summary[:healings_this_week]}"
|
90
|
-
end
|
91
88
|
end
|
92
89
|
end
|
data/lib/code_healer/engine.rb
CHANGED
@@ -7,6 +7,11 @@ module CodeHealer
|
|
7
7
|
app.config.autoload_paths += %W(#{config.root}/lib/code_healer)
|
8
8
|
end
|
9
9
|
|
10
|
+
# Add views path to the main app
|
11
|
+
initializer "code_healer.add_views_path" do |app|
|
12
|
+
app.config.paths["app/views"] << "#{config.root}/lib/code_healer/views"
|
13
|
+
end
|
14
|
+
|
10
15
|
# Copy migrations
|
11
16
|
initializer "code_healer.copy_migrations" do |app|
|
12
17
|
if app.root.to_s.match root.to_s
|
@@ -15,5 +20,12 @@ module CodeHealer
|
|
15
20
|
end
|
16
21
|
end
|
17
22
|
end
|
23
|
+
|
24
|
+
# Ensure the engine is properly loaded
|
25
|
+
config.autoload_paths += %W(#{config.root}/lib)
|
26
|
+
config.eager_load_paths += %W(#{config.root}/lib)
|
27
|
+
|
28
|
+
# Configure the engine's own paths
|
29
|
+
config.paths["app/views"] = ["#{config.root}/lib/code_healer/views"]
|
18
30
|
end
|
19
31
|
end
|
data/lib/code_healer/version.rb
CHANGED
@@ -0,0 +1,356 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>Healing Details - CodeHealer Dashboard</title>
|
7
|
+
<style>
|
8
|
+
* {
|
9
|
+
margin: 0;
|
10
|
+
padding: 0;
|
11
|
+
box-sizing: border-box;
|
12
|
+
}
|
13
|
+
|
14
|
+
body {
|
15
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
16
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
17
|
+
min-height: 100vh;
|
18
|
+
color: #333;
|
19
|
+
}
|
20
|
+
|
21
|
+
.container {
|
22
|
+
max-width: 1200px;
|
23
|
+
margin: 0 auto;
|
24
|
+
padding: 20px;
|
25
|
+
}
|
26
|
+
|
27
|
+
.header {
|
28
|
+
text-align: center;
|
29
|
+
margin-bottom: 40px;
|
30
|
+
color: white;
|
31
|
+
}
|
32
|
+
|
33
|
+
.header h1 {
|
34
|
+
font-size: 2.5rem;
|
35
|
+
font-weight: 700;
|
36
|
+
margin-bottom: 10px;
|
37
|
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
38
|
+
}
|
39
|
+
|
40
|
+
.back-link {
|
41
|
+
color: white;
|
42
|
+
text-decoration: none;
|
43
|
+
font-size: 1.1rem;
|
44
|
+
opacity: 0.9;
|
45
|
+
}
|
46
|
+
|
47
|
+
.back-link:hover {
|
48
|
+
opacity: 1;
|
49
|
+
}
|
50
|
+
|
51
|
+
.healing-card {
|
52
|
+
background: white;
|
53
|
+
border-radius: 16px;
|
54
|
+
padding: 30px;
|
55
|
+
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
56
|
+
margin-bottom: 30px;
|
57
|
+
}
|
58
|
+
|
59
|
+
.card-title {
|
60
|
+
font-size: 1.5rem;
|
61
|
+
font-weight: 600;
|
62
|
+
margin-bottom: 20px;
|
63
|
+
color: #333;
|
64
|
+
border-bottom: 2px solid #667eea;
|
65
|
+
padding-bottom: 10px;
|
66
|
+
}
|
67
|
+
|
68
|
+
.info-grid {
|
69
|
+
display: grid;
|
70
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
71
|
+
gap: 20px;
|
72
|
+
margin-bottom: 30px;
|
73
|
+
}
|
74
|
+
|
75
|
+
.info-section {
|
76
|
+
background: #f8f9fa;
|
77
|
+
padding: 20px;
|
78
|
+
border-radius: 12px;
|
79
|
+
border-left: 4px solid #667eea;
|
80
|
+
}
|
81
|
+
|
82
|
+
.info-section h3 {
|
83
|
+
font-size: 1.1rem;
|
84
|
+
font-weight: 600;
|
85
|
+
margin-bottom: 15px;
|
86
|
+
color: #333;
|
87
|
+
}
|
88
|
+
|
89
|
+
.info-item {
|
90
|
+
display: flex;
|
91
|
+
justify-content: space-between;
|
92
|
+
margin-bottom: 10px;
|
93
|
+
padding: 8px 0;
|
94
|
+
border-bottom: 1px solid #e9ecef;
|
95
|
+
}
|
96
|
+
|
97
|
+
.info-item:last-child {
|
98
|
+
border-bottom: none;
|
99
|
+
}
|
100
|
+
|
101
|
+
.info-label {
|
102
|
+
font-weight: 500;
|
103
|
+
color: #666;
|
104
|
+
}
|
105
|
+
|
106
|
+
.info-value {
|
107
|
+
font-weight: 600;
|
108
|
+
color: #333;
|
109
|
+
}
|
110
|
+
|
111
|
+
.status-badge {
|
112
|
+
padding: 6px 12px;
|
113
|
+
border-radius: 20px;
|
114
|
+
font-size: 0.8rem;
|
115
|
+
font-weight: 500;
|
116
|
+
text-transform: uppercase;
|
117
|
+
}
|
118
|
+
|
119
|
+
.status-success {
|
120
|
+
background-color: #d4edda;
|
121
|
+
color: #155724;
|
122
|
+
}
|
123
|
+
|
124
|
+
.status-failed {
|
125
|
+
background-color: #f8d7da;
|
126
|
+
color: #721c24;
|
127
|
+
}
|
128
|
+
|
129
|
+
.timing-chart {
|
130
|
+
background: #f8f9fa;
|
131
|
+
padding: 20px;
|
132
|
+
border-radius: 12px;
|
133
|
+
margin-top: 20px;
|
134
|
+
}
|
135
|
+
|
136
|
+
.timing-bar {
|
137
|
+
display: flex;
|
138
|
+
align-items: center;
|
139
|
+
margin-bottom: 15px;
|
140
|
+
}
|
141
|
+
|
142
|
+
.timing-label {
|
143
|
+
width: 120px;
|
144
|
+
font-weight: 500;
|
145
|
+
color: #666;
|
146
|
+
}
|
147
|
+
|
148
|
+
.timing-progress {
|
149
|
+
flex: 1;
|
150
|
+
height: 20px;
|
151
|
+
background: #e9ecef;
|
152
|
+
border-radius: 10px;
|
153
|
+
overflow: hidden;
|
154
|
+
margin: 0 15px;
|
155
|
+
}
|
156
|
+
|
157
|
+
.timing-fill {
|
158
|
+
height: 100%;
|
159
|
+
background: linear-gradient(90deg, #667eea, #764ba2);
|
160
|
+
transition: width 0.3s ease;
|
161
|
+
}
|
162
|
+
|
163
|
+
.timing-value {
|
164
|
+
width: 80px;
|
165
|
+
text-align: right;
|
166
|
+
font-weight: 600;
|
167
|
+
color: #333;
|
168
|
+
}
|
169
|
+
|
170
|
+
.error-details {
|
171
|
+
background: #fff5f5;
|
172
|
+
border: 1px solid #fed7d7;
|
173
|
+
border-radius: 12px;
|
174
|
+
padding: 20px;
|
175
|
+
margin-top: 20px;
|
176
|
+
}
|
177
|
+
|
178
|
+
.error-details h3 {
|
179
|
+
color: #c53030;
|
180
|
+
margin-bottom: 15px;
|
181
|
+
}
|
182
|
+
|
183
|
+
.code-block {
|
184
|
+
background: #2d3748;
|
185
|
+
color: #e2e8f0;
|
186
|
+
padding: 15px;
|
187
|
+
border-radius: 8px;
|
188
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
189
|
+
font-size: 0.9rem;
|
190
|
+
overflow-x: auto;
|
191
|
+
margin-top: 10px;
|
192
|
+
}
|
193
|
+
|
194
|
+
@media (max-width: 768px) {
|
195
|
+
.info-grid {
|
196
|
+
grid-template-columns: 1fr;
|
197
|
+
}
|
198
|
+
|
199
|
+
.header h1 {
|
200
|
+
font-size: 2rem;
|
201
|
+
}
|
202
|
+
}
|
203
|
+
</style>
|
204
|
+
</head>
|
205
|
+
<body>
|
206
|
+
<div class="container">
|
207
|
+
<div class="header">
|
208
|
+
<h1>🏥 Healing Details</h1>
|
209
|
+
<a href="/code_healer/dashboard" class="back-link">← Back to Dashboard</a>
|
210
|
+
</div>
|
211
|
+
|
212
|
+
<% if @healing %>
|
213
|
+
<div class="healing-card">
|
214
|
+
<div class="card-title">Healing Information</div>
|
215
|
+
|
216
|
+
<div class="info-grid">
|
217
|
+
<div class="info-section">
|
218
|
+
<h3>Basic Details</h3>
|
219
|
+
<div class="info-item">
|
220
|
+
<span class="info-label">Healing ID:</span>
|
221
|
+
<span class="info-value"><%= @healing.healing_id %></span>
|
222
|
+
</div>
|
223
|
+
<div class="info-item">
|
224
|
+
<span class="info-label">Class:</span>
|
225
|
+
<span class="info-value"><%= @healing.class_name %></span>
|
226
|
+
</div>
|
227
|
+
<div class="info-item">
|
228
|
+
<span class="info-label">Method:</span>
|
229
|
+
<span class="info-value"><%= @healing.method_name %></span>
|
230
|
+
</div>
|
231
|
+
<div class="info-item">
|
232
|
+
<span class="info-label">Status:</span>
|
233
|
+
<span class="status-badge status-<%= @healing.healing_successful ? 'success' : 'failed' %>">
|
234
|
+
<%= @healing.healing_successful ? '✅ Success' : '❌ Failed' %>
|
235
|
+
</span>
|
236
|
+
</div>
|
237
|
+
</div>
|
238
|
+
|
239
|
+
<div class="info-section">
|
240
|
+
<h3>AI & Evolution</h3>
|
241
|
+
<div class="info-item">
|
242
|
+
<span class="info-label">Evolution Method:</span>
|
243
|
+
<span class="info-value"><%= @healing.evolution_method_display %></span>
|
244
|
+
</div>
|
245
|
+
<div class="info-item">
|
246
|
+
<span class="info-label">AI Provider:</span>
|
247
|
+
<span class="info-value"><%= @healing.ai_provider_display %></span>
|
248
|
+
</div>
|
249
|
+
<div class="info-item">
|
250
|
+
<span class="info-label">AI Success:</span>
|
251
|
+
<span class="info-value"><%= @healing.ai_success ? '✅ Yes' : '❌ No' %></span>
|
252
|
+
</div>
|
253
|
+
<div class="info-item">
|
254
|
+
<span class="info-label">Tests Passed:</span>
|
255
|
+
<span class="info-value"><%= @healing.tests_passed ? '✅ Yes' : '❌ No' %></span>
|
256
|
+
</div>
|
257
|
+
</div>
|
258
|
+
|
259
|
+
<div class="info-section">
|
260
|
+
<h3>Timing & Performance</h3>
|
261
|
+
<div class="info-item">
|
262
|
+
<span class="info-label">Started:</span>
|
263
|
+
<span class="info-value"><%= @healing.healing_started_at&.strftime("%Y-%m-%d %H:%M:%S") || 'N/A' %></span>
|
264
|
+
</div>
|
265
|
+
<div class="info-item">
|
266
|
+
<span class="info-label">Completed:</span>
|
267
|
+
<span class="info-value"><%= @healing.healing_completed_at&.strftime("%Y-%m-%d %H:%M:%S") || 'N/A' %></span>
|
268
|
+
</div>
|
269
|
+
<div class="info-item">
|
270
|
+
<span class="info-label">Total Duration:</span>
|
271
|
+
<span class="info-value"><%= @healing.duration_seconds ? "#{@healing.duration_seconds}s" : 'N/A' %></span>
|
272
|
+
</div>
|
273
|
+
<div class="info-item">
|
274
|
+
<span class="info-label">Syntax Valid:</span>
|
275
|
+
<span class="info-value"><%= @healing.syntax_valid ? '✅ Yes' : '❌ No' %></span>
|
276
|
+
</div>
|
277
|
+
</div>
|
278
|
+
</div>
|
279
|
+
|
280
|
+
<div class="timing-chart">
|
281
|
+
<h3>Processing Breakdown</h3>
|
282
|
+
<div class="timing-bar">
|
283
|
+
<span class="timing-label">AI Processing:</span>
|
284
|
+
<div class="timing-progress">
|
285
|
+
<div class="timing-fill" style="width: <%= @healing.ai_processing_seconds ? (@healing.ai_processing_seconds / @healing.duration_seconds.to_f * 100).round(1) : 0 %>%"></div>
|
286
|
+
</div>
|
287
|
+
<span class="timing-value"><%= @healing.ai_processing_seconds ? "#{@healing.ai_processing_seconds}s" : 'N/A' %></span>
|
288
|
+
</div>
|
289
|
+
<div class="timing-bar">
|
290
|
+
<span class="timing-label">Git Operations:</span>
|
291
|
+
<div class="timing-progress">
|
292
|
+
<div class="timing-fill" style="width: <%= @healing.git_operations_seconds ? (@healing.git_operations_seconds / @healing.duration_seconds.to_f * 100).round(1) : 0 %>%"></div>
|
293
|
+
</div>
|
294
|
+
<span class="timing-value"><%= @healing.git_operations_seconds ? "#{@healing.git_operations_seconds}s" : 'N/A' %></span>
|
295
|
+
</div>
|
296
|
+
</div>
|
297
|
+
|
298
|
+
<% if @healing.error_class.present? %>
|
299
|
+
<div class="error-details">
|
300
|
+
<h3>Error Information</h3>
|
301
|
+
<div class="info-item">
|
302
|
+
<span class="info-label">Error Class:</span>
|
303
|
+
<span class="info-value"><%= @healing.error_class %></span>
|
304
|
+
</div>
|
305
|
+
<div class="info-item">
|
306
|
+
<span class="info-label">Error Message:</span>
|
307
|
+
<span class="info-value"><%= @healing.error_message %></span>
|
308
|
+
</div>
|
309
|
+
<div class="info-item">
|
310
|
+
<span class="info-label">File Path:</span>
|
311
|
+
<span class="info-value"><%= @healing.file_path %></span>
|
312
|
+
</div>
|
313
|
+
<% if @healing.failure_reason.present? %>
|
314
|
+
<div class="info-item">
|
315
|
+
<span class="info-label">Failure Reason:</span>
|
316
|
+
<span class="info-value"><%= @healing.failure_reason %></span>
|
317
|
+
</div>
|
318
|
+
<% end %>
|
319
|
+
</div>
|
320
|
+
<% end %>
|
321
|
+
|
322
|
+
<% if @healing.healing_branch.present? %>
|
323
|
+
<div class="info-section">
|
324
|
+
<h3>Git Operations</h3>
|
325
|
+
<div class="info-item">
|
326
|
+
<span class="info-label">Healing Branch:</span>
|
327
|
+
<span class="info-value"><%= @healing.healing_branch %></span>
|
328
|
+
</div>
|
329
|
+
<% if @healing.pull_request_url.present? %>
|
330
|
+
<div class="info-item">
|
331
|
+
<span class="info-label">Pull Request:</span>
|
332
|
+
<span class="info-value">
|
333
|
+
<a href="<%= @healing.pull_request_url %>" target="_blank" style="color: #667eea; text-decoration: none;">
|
334
|
+
🔗 View PR
|
335
|
+
</a>
|
336
|
+
</span>
|
337
|
+
</div>
|
338
|
+
<% end %>
|
339
|
+
<div class="info-item">
|
340
|
+
<span class="info-label">PR Created:</span>
|
341
|
+
<span class="info-value"><%= @healing.pr_created ? '✅ Yes' : '❌ No' %></span>
|
342
|
+
</div>
|
343
|
+
</div>
|
344
|
+
<% end %>
|
345
|
+
</div>
|
346
|
+
<% else %>
|
347
|
+
<div class="healing-card">
|
348
|
+
<div class="error-details">
|
349
|
+
<h3>Healing Not Found</h3>
|
350
|
+
<p>The requested healing operation could not be found.</p>
|
351
|
+
</div>
|
352
|
+
</div>
|
353
|
+
<% end %>
|
354
|
+
</div>
|
355
|
+
</body>
|
356
|
+
</html>
|
@@ -1,161 +1,408 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
|
-
<html>
|
2
|
+
<html lang="en">
|
3
3
|
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
4
6
|
<title>CodeHealer Dashboard</title>
|
5
7
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
6
8
|
<style>
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
.
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
.
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
.
|
33
|
-
|
34
|
-
|
9
|
+
* {
|
10
|
+
margin: 0;
|
11
|
+
padding: 0;
|
12
|
+
box-sizing: border-box;
|
13
|
+
}
|
14
|
+
|
15
|
+
body {
|
16
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
17
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
18
|
+
min-height: 100vh;
|
19
|
+
color: #333;
|
20
|
+
}
|
21
|
+
|
22
|
+
.dashboard-container {
|
23
|
+
max-width: 1400px;
|
24
|
+
margin: 0 auto;
|
25
|
+
padding: 20px;
|
26
|
+
}
|
27
|
+
|
28
|
+
.dashboard-header {
|
29
|
+
text-align: center;
|
30
|
+
margin-bottom: 40px;
|
31
|
+
color: white;
|
32
|
+
}
|
33
|
+
|
34
|
+
.dashboard-header h1 {
|
35
|
+
font-size: 3rem;
|
36
|
+
font-weight: 700;
|
37
|
+
margin-bottom: 10px;
|
38
|
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
39
|
+
}
|
40
|
+
|
41
|
+
.dashboard-header p {
|
42
|
+
font-size: 1.2rem;
|
43
|
+
opacity: 0.9;
|
44
|
+
}
|
45
|
+
|
46
|
+
.metrics-grid {
|
47
|
+
display: grid;
|
48
|
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
49
|
+
gap: 20px;
|
50
|
+
margin-bottom: 40px;
|
51
|
+
}
|
52
|
+
|
53
|
+
.metric-card {
|
54
|
+
background: white;
|
55
|
+
border-radius: 16px;
|
56
|
+
padding: 24px;
|
57
|
+
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
58
|
+
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
59
|
+
border: 1px solid rgba(255,255,255,0.2);
|
60
|
+
}
|
61
|
+
|
62
|
+
.metric-card:hover {
|
63
|
+
transform: translateY(-5px);
|
64
|
+
box-shadow: 0 20px 40px rgba(0,0,0,0.15);
|
65
|
+
}
|
66
|
+
|
67
|
+
.metric-value {
|
68
|
+
font-size: 2.5rem;
|
69
|
+
font-weight: 700;
|
70
|
+
color: #667eea;
|
71
|
+
margin-bottom: 8px;
|
72
|
+
}
|
73
|
+
|
74
|
+
.metric-label {
|
75
|
+
font-size: 0.9rem;
|
76
|
+
color: #666;
|
77
|
+
text-transform: uppercase;
|
78
|
+
letter-spacing: 0.5px;
|
79
|
+
font-weight: 500;
|
80
|
+
}
|
81
|
+
|
82
|
+
.metric-trend {
|
83
|
+
font-size: 0.8rem;
|
84
|
+
color: #28a745;
|
85
|
+
margin-top: 8px;
|
86
|
+
}
|
87
|
+
|
88
|
+
.charts-section {
|
89
|
+
display: grid;
|
90
|
+
grid-template-columns: 1fr 1fr;
|
91
|
+
gap: 30px;
|
92
|
+
margin-bottom: 40px;
|
93
|
+
}
|
94
|
+
|
95
|
+
.chart-container {
|
96
|
+
background: white;
|
97
|
+
border-radius: 16px;
|
98
|
+
padding: 24px;
|
99
|
+
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
100
|
+
}
|
101
|
+
|
102
|
+
.chart-title {
|
103
|
+
font-size: 1.2rem;
|
104
|
+
font-weight: 600;
|
105
|
+
margin-bottom: 20px;
|
106
|
+
color: #333;
|
107
|
+
text-align: center;
|
108
|
+
}
|
109
|
+
|
110
|
+
.recent-healings {
|
111
|
+
background: white;
|
112
|
+
border-radius: 16px;
|
113
|
+
padding: 24px;
|
114
|
+
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
115
|
+
}
|
116
|
+
|
117
|
+
.section-title {
|
118
|
+
font-size: 1.5rem;
|
119
|
+
font-weight: 600;
|
120
|
+
margin-bottom: 20px;
|
121
|
+
color: #333;
|
122
|
+
text-align: center;
|
123
|
+
}
|
124
|
+
|
125
|
+
.healing-item {
|
126
|
+
display: flex;
|
127
|
+
justify-content: space-between;
|
128
|
+
align-items: center;
|
129
|
+
padding: 16px;
|
130
|
+
border-bottom: 1px solid #eee;
|
131
|
+
transition: background-color 0.2s ease;
|
132
|
+
}
|
133
|
+
|
134
|
+
.healing-item:hover {
|
135
|
+
background-color: #f8f9fa;
|
136
|
+
}
|
137
|
+
|
138
|
+
.healing-item:last-child {
|
139
|
+
border-bottom: none;
|
140
|
+
}
|
141
|
+
|
142
|
+
.healing-info h4 {
|
143
|
+
font-size: 1rem;
|
144
|
+
font-weight: 600;
|
145
|
+
color: #333;
|
146
|
+
margin-bottom: 4px;
|
147
|
+
}
|
148
|
+
|
149
|
+
.healing-info p {
|
150
|
+
font-size: 0.9rem;
|
151
|
+
color: #666;
|
152
|
+
margin: 0;
|
153
|
+
}
|
154
|
+
|
155
|
+
.healing-status {
|
156
|
+
padding: 6px 12px;
|
157
|
+
border-radius: 20px;
|
158
|
+
font-size: 0.8rem;
|
159
|
+
font-weight: 500;
|
160
|
+
text-transform: uppercase;
|
161
|
+
}
|
162
|
+
|
163
|
+
.status-success {
|
164
|
+
background-color: #d4edda;
|
165
|
+
color: #155724;
|
166
|
+
}
|
167
|
+
|
168
|
+
.status-failed {
|
169
|
+
background-color: #f8d7da;
|
170
|
+
color: #721c24;
|
171
|
+
}
|
172
|
+
|
173
|
+
.status-pending {
|
174
|
+
background-color: #fff3cd;
|
175
|
+
color: #856404;
|
176
|
+
}
|
177
|
+
|
178
|
+
.view-details-btn {
|
179
|
+
background: #667eea;
|
180
|
+
color: white;
|
181
|
+
padding: 8px 16px;
|
182
|
+
border-radius: 20px;
|
183
|
+
text-decoration: none;
|
184
|
+
font-size: 0.8rem;
|
185
|
+
font-weight: 500;
|
186
|
+
transition: background-color 0.2s ease;
|
187
|
+
}
|
188
|
+
|
189
|
+
.view-details-btn:hover {
|
190
|
+
background: #5a6fd8;
|
191
|
+
}
|
192
|
+
|
193
|
+
@media (max-width: 768px) {
|
194
|
+
.charts-section {
|
195
|
+
grid-template-columns: 1fr;
|
196
|
+
}
|
197
|
+
|
198
|
+
.dashboard-header h1 {
|
199
|
+
font-size: 2rem;
|
200
|
+
}
|
201
|
+
|
202
|
+
.metric-value {
|
203
|
+
font-size: 2rem;
|
204
|
+
}
|
205
|
+
}
|
206
|
+
|
207
|
+
.loading {
|
208
|
+
text-align: center;
|
209
|
+
padding: 40px;
|
210
|
+
color: #666;
|
211
|
+
}
|
212
|
+
|
213
|
+
.error {
|
214
|
+
background: #f8d7da;
|
215
|
+
color: #721c24;
|
216
|
+
padding: 16px;
|
217
|
+
border-radius: 8px;
|
218
|
+
margin: 20px 0;
|
219
|
+
text-align: center;
|
220
|
+
}
|
35
221
|
</style>
|
36
222
|
</head>
|
37
223
|
<body>
|
38
|
-
<div class="container">
|
39
|
-
<div class="header">
|
224
|
+
<div class="dashboard-container">
|
225
|
+
<div class="dashboard-header">
|
40
226
|
<h1>🏥 CodeHealer Dashboard</h1>
|
41
|
-
<p>
|
227
|
+
<p>AI-Powered Code Healing & Self-Repair Analytics</p>
|
42
228
|
</div>
|
43
|
-
|
229
|
+
|
44
230
|
<div class="metrics-grid">
|
45
231
|
<div class="metric-card">
|
46
|
-
<
|
47
|
-
<div class="
|
48
|
-
<div class="
|
232
|
+
<div class="metric-value" id="total-healings">-</div>
|
233
|
+
<div class="metric-label">Total Healings</div>
|
234
|
+
<div class="metric-trend">📈 All time</div>
|
49
235
|
</div>
|
50
236
|
|
51
237
|
<div class="metric-card">
|
52
|
-
<
|
53
|
-
<div class="
|
54
|
-
|
55
|
-
</div>
|
56
|
-
<div class="label">Healing success</div>
|
238
|
+
<div class="metric-value" id="success-rate">-</div>
|
239
|
+
<div class="metric-label">Success Rate</div>
|
240
|
+
<div class="metric-trend">🎯 Performance</div>
|
57
241
|
</div>
|
58
242
|
|
59
243
|
<div class="metric-card">
|
60
|
-
<
|
61
|
-
<div class="
|
62
|
-
<div class="
|
244
|
+
<div class="metric-value" id="healings-today">-</div>
|
245
|
+
<div class="metric-label">Healings Today</div>
|
246
|
+
<div class="metric-trend">📅 Daily</div>
|
63
247
|
</div>
|
64
248
|
|
65
249
|
<div class="metric-card">
|
66
|
-
<
|
67
|
-
<div class="
|
68
|
-
<div class="
|
69
|
-
</div>
|
70
|
-
|
71
|
-
<div class="metric-card">
|
72
|
-
<h3>This Month</h3>
|
73
|
-
<div class="value"><%= @summary[:healings_this_month] %></div>
|
74
|
-
<div class="label">Healings this month</div>
|
75
|
-
</div>
|
76
|
-
|
77
|
-
<div class="metric-card">
|
78
|
-
<h3>Avg Resolution</h3>
|
79
|
-
<div class="value">
|
80
|
-
<%= @summary[:average_resolution_time] ? "#{(@summary[:average_resolution_time] / 1000.0).round(2)}s" : "N/A" %>
|
81
|
-
</div>
|
82
|
-
<div class="label">Average time to fix</div>
|
250
|
+
<div class="metric-value" id="avg-resolution">-</div>
|
251
|
+
<div class="metric-label">Avg Resolution Time</div>
|
252
|
+
<div class="metric-trend">⚡ Speed</div>
|
83
253
|
</div>
|
84
254
|
</div>
|
85
|
-
|
86
|
-
<div class="charts-
|
87
|
-
<div class="chart-
|
88
|
-
<
|
89
|
-
<canvas id="dailyTrendChart"></canvas>
|
255
|
+
|
256
|
+
<div class="charts-section">
|
257
|
+
<div class="chart-container">
|
258
|
+
<div class="chart-title">Daily Healing Trend (Last 7 Days)</div>
|
259
|
+
<canvas id="dailyTrendChart" width="400" height="200"></canvas>
|
90
260
|
</div>
|
91
261
|
|
92
|
-
<div class="chart-
|
93
|
-
<
|
94
|
-
<canvas id="evolutionMethodsChart"></canvas>
|
262
|
+
<div class="chart-container">
|
263
|
+
<div class="chart-title">Evolution Methods Distribution</div>
|
264
|
+
<canvas id="evolutionMethodsChart" width="400" height="200"></canvas>
|
95
265
|
</div>
|
96
266
|
</div>
|
97
|
-
|
267
|
+
|
98
268
|
<div class="recent-healings">
|
99
|
-
<
|
100
|
-
|
101
|
-
<div class="
|
102
|
-
|
103
|
-
<div class="error"><%= healing.error_class %>: <%= healing.error_message&.truncate(100) %></div>
|
104
|
-
<div class="status">
|
105
|
-
<%= healing.success_status %> |
|
106
|
-
<%= healing.evolution_method_display %> |
|
107
|
-
<%= healing.ai_provider_display %>
|
108
|
-
</div>
|
109
|
-
<div class="time"><%= healing.created_at.strftime("%Y-%m-%d %H:%M:%S") %></div>
|
110
|
-
</div>
|
111
|
-
<% end %>
|
269
|
+
<div class="section-title">Recent Healing Operations</div>
|
270
|
+
<div id="recent-healings-list">
|
271
|
+
<div class="loading">Loading recent healings...</div>
|
272
|
+
</div>
|
112
273
|
</div>
|
113
274
|
</div>
|
114
|
-
|
275
|
+
|
115
276
|
<script>
|
116
|
-
//
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
data
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
}]
|
129
|
-
},
|
130
|
-
options: {
|
131
|
-
responsive: true,
|
132
|
-
plugins: {
|
133
|
-
legend: { display: false }
|
134
|
-
},
|
135
|
-
scales: {
|
136
|
-
y: { beginAtZero: true, ticks: { stepSize: 1 } }
|
277
|
+
// Dashboard data loading
|
278
|
+
async function loadDashboardData() {
|
279
|
+
try {
|
280
|
+
const response = await fetch('/code_healer/api/dashboard/summary');
|
281
|
+
const data = await response.json();
|
282
|
+
|
283
|
+
if (response.ok) {
|
284
|
+
updateMetrics(data);
|
285
|
+
updateCharts(data);
|
286
|
+
loadRecentHealings();
|
287
|
+
} else {
|
288
|
+
throw new Error('Failed to load dashboard data');
|
137
289
|
}
|
290
|
+
} catch (error) {
|
291
|
+
console.error('Error loading dashboard data:', error);
|
292
|
+
document.querySelector('.metrics-grid').innerHTML =
|
293
|
+
'<div class="error">Failed to load dashboard data. Please try again later.</div>';
|
138
294
|
}
|
139
|
-
}
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
data
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
295
|
+
}
|
296
|
+
|
297
|
+
function updateMetrics(data) {
|
298
|
+
document.getElementById('total-healings').textContent = data.total_healings || 0;
|
299
|
+
document.getElementById('success-rate').textContent = `${data.success_rate || 0}%`;
|
300
|
+
document.getElementById('healings-today').textContent = data.healings_today || 0;
|
301
|
+
document.getElementById('avg-resolution').textContent = `${data.average_resolution_time || 0}ms`;
|
302
|
+
}
|
303
|
+
|
304
|
+
function updateCharts(data) {
|
305
|
+
// Daily Trend Chart
|
306
|
+
const dailyCtx = document.getElementById('dailyTrendChart').getContext('2d');
|
307
|
+
new Chart(dailyCtx, {
|
308
|
+
type: 'line',
|
309
|
+
data: {
|
310
|
+
labels: Object.keys(data.daily_trend || {}),
|
311
|
+
datasets: [{
|
312
|
+
label: 'Healings',
|
313
|
+
data: Object.values(data.daily_trend || {}),
|
314
|
+
borderColor: '#667eea',
|
315
|
+
backgroundColor: 'rgba(102, 126, 234, 0.1)',
|
316
|
+
tension: 0.4,
|
317
|
+
fill: true
|
318
|
+
}]
|
319
|
+
},
|
320
|
+
options: {
|
321
|
+
responsive: true,
|
322
|
+
plugins: {
|
323
|
+
legend: {
|
324
|
+
display: false
|
325
|
+
}
|
326
|
+
},
|
327
|
+
scales: {
|
328
|
+
y: {
|
329
|
+
beginAtZero: true,
|
330
|
+
ticks: {
|
331
|
+
stepSize: 1
|
332
|
+
}
|
333
|
+
}
|
334
|
+
}
|
335
|
+
}
|
336
|
+
});
|
337
|
+
|
338
|
+
// Evolution Methods Chart
|
339
|
+
const evolutionCtx = document.getElementById('evolutionMethodsChart').getContext('2d');
|
340
|
+
new Chart(evolutionCtx, {
|
341
|
+
type: 'doughnut',
|
342
|
+
data: {
|
343
|
+
labels: Object.keys(data.evolution_methods || {}),
|
344
|
+
datasets: [{
|
345
|
+
data: Object.values(data.evolution_methods || {}),
|
346
|
+
backgroundColor: [
|
347
|
+
'#667eea',
|
348
|
+
'#764ba2',
|
349
|
+
'#f093fb',
|
350
|
+
'#f5576c',
|
351
|
+
'#4facfe',
|
352
|
+
'#00f2fe'
|
353
|
+
]
|
354
|
+
}]
|
355
|
+
},
|
356
|
+
options: {
|
357
|
+
responsive: true,
|
358
|
+
plugins: {
|
359
|
+
legend: {
|
360
|
+
position: 'bottom'
|
361
|
+
}
|
362
|
+
}
|
363
|
+
}
|
364
|
+
});
|
365
|
+
}
|
366
|
+
|
367
|
+
async function loadRecentHealings() {
|
368
|
+
try {
|
369
|
+
const response = await fetch('/code_healer/api/dashboard/metrics?limit=5');
|
370
|
+
const healings = await response.json();
|
371
|
+
|
372
|
+
const container = document.getElementById('recent-healings-list');
|
373
|
+
if (healings && healings.length > 0) {
|
374
|
+
container.innerHTML = healings.map(healing => `
|
375
|
+
<div class="healing-item">
|
376
|
+
<div class="healing-info">
|
377
|
+
<h4>${healing.class_name}#${healing.method_name}</h4>
|
378
|
+
<p>${healing.error_class}: ${healing.error_message}</p>
|
379
|
+
<small>${new Date(healing.created_at).toLocaleString()}</small>
|
380
|
+
</div>
|
381
|
+
<div style="display: flex; align-items: center; gap: 12px;">
|
382
|
+
<div class="healing-status status-${healing.healing_successful ? 'success' : 'failed'}">
|
383
|
+
${healing.healing_successful ? '✅ Success' : '❌ Failed'}
|
384
|
+
</div>
|
385
|
+
<a href="/code_healer/dashboard/healing/${healing.healing_id}" class="view-details-btn">
|
386
|
+
View Details
|
387
|
+
</a>
|
388
|
+
</div>
|
389
|
+
</div>
|
390
|
+
`).join('');
|
391
|
+
} else {
|
392
|
+
container.innerHTML = '<div class="loading">No healing operations found yet.</div>';
|
156
393
|
}
|
394
|
+
} catch (error) {
|
395
|
+
console.error('Error loading recent healings:', error);
|
396
|
+
document.getElementById('recent-healings-list').innerHTML =
|
397
|
+
'<div class="error">Failed to load recent healings.</div>';
|
157
398
|
}
|
158
|
-
}
|
399
|
+
}
|
400
|
+
|
401
|
+
// Initialize dashboard
|
402
|
+
document.addEventListener('DOMContentLoaded', loadDashboardData);
|
403
|
+
|
404
|
+
// Auto-refresh every 30 seconds
|
405
|
+
setInterval(loadDashboardData, 30000);
|
159
406
|
</script>
|
160
407
|
</body>
|
161
408
|
</html>
|
data/lib/code_healer.rb
CHANGED
@@ -72,33 +72,11 @@ if defined?(Rails)
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
-
# Mount the engine to provide dashboard routes
|
75
|
+
# Mount the engine to provide dashboard routes and views
|
76
76
|
initializer "code_healer.mount_engine" do |app|
|
77
77
|
app.routes.prepend do
|
78
78
|
mount CodeHealer::Engine => "/code_healer"
|
79
79
|
end
|
80
80
|
end
|
81
|
-
|
82
|
-
# Add dashboard routes directly to the host app
|
83
|
-
initializer "code_healer.add_dashboard_routes" do |app|
|
84
|
-
app.routes.prepend do
|
85
|
-
namespace :code_healer do
|
86
|
-
get '/dashboard', to: 'dashboard#index'
|
87
|
-
get '/dashboard/metrics', to: 'dashboard#metrics'
|
88
|
-
get '/dashboard/trends', to: 'dashboard#trends'
|
89
|
-
get '/dashboard/performance', to: 'dashboard#performance'
|
90
|
-
get '/dashboard/healing/:healing_id', to: 'dashboard#healing_details'
|
91
|
-
|
92
|
-
# API endpoints (JSON only)
|
93
|
-
namespace :api do
|
94
|
-
get '/dashboard/summary', to: 'dashboard#summary'
|
95
|
-
get '/dashboard/metrics', to: 'dashboard#metrics'
|
96
|
-
get '/dashboard/trends', to: 'dashboard#trends'
|
97
|
-
get '/dashboard/performance', to: 'dashboard#performance'
|
98
|
-
get '/dashboard/healing/:healing_id', to: 'dashboard#healing_details'
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
81
|
end
|
104
82
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: code_healer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Deepan Kumar
|
@@ -350,6 +350,7 @@ files:
|
|
350
350
|
- README.md
|
351
351
|
- code_healer.gemspec
|
352
352
|
- config/code_healer.yml.example
|
353
|
+
- config/routes.rb
|
353
354
|
- docs/INSTALLATION.md
|
354
355
|
- examples/basic_usage.rb
|
355
356
|
- exe/code_healer-setup
|
@@ -388,6 +389,7 @@ files:
|
|
388
389
|
- lib/code_healer/terminal_integration.rb
|
389
390
|
- lib/code_healer/usage_analyzer.rb
|
390
391
|
- lib/code_healer/version.rb
|
392
|
+
- lib/code_healer/views/dashboard/healing_details.html.erb
|
391
393
|
- lib/code_healer/views/dashboard/index.html.erb
|
392
394
|
- lib/generators/code_healer/install_generator.rb
|
393
395
|
- lib/generators/code_healer/templates/create_healing_metrics.rb
|