@ai-qa/workflow 2.0.2 → 2.0.3
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.
- package/DESIGN.md +0 -0
- package/install.js +1 -0
- package/package.json +3 -2
- package/qa-dashboard/app.js +52 -0
- package/qa-dashboard/package-lock.json +1002 -0
- package/qa-dashboard/public/css/style.css +1062 -207
- package/qa-dashboard/public/js/main.js +3 -2
- package/qa-dashboard/routes/export.js +35 -16
- package/qa-dashboard/routes/index.js +38 -1
- package/qa-dashboard/routes/runs.js +58 -6
- package/qa-dashboard/routes/stories.js +59 -15
- package/qa-dashboard/services/cli-bridge.js +47 -3
- package/qa-dashboard/services/project-manager.js +26 -0
- package/qa-dashboard/views/analytics.ejs +226 -153
- package/qa-dashboard/views/index.ejs +241 -82
- package/qa-dashboard/views/layouts/main.ejs +18 -0
- package/qa-dashboard/views/project.ejs +49 -29
- package/qa-dashboard/views/projects.ejs +7 -5
- package/qa-dashboard/views/run.ejs +97 -37
- package/qa-dashboard/views/runs.ejs +29 -38
- package/qa-dashboard/views/stories.ejs +23 -25
- package/qa-dashboard/views/story.ejs +94 -15
- package/qa-dashboard/views/test-data.ejs +24 -24
- package/scripts/executor.js +11 -7
- package/specs/us-expense-01-test-plan.md +55 -0
- package/tests/us-expense-01.spec.ts +30 -0
- package/user-story/Demande-alimentation.md +24 -0
|
@@ -1,83 +1,242 @@
|
|
|
1
|
-
<% layout
|
|
2
|
-
<% title
|
|
3
|
-
|
|
4
|
-
<div class="hero">
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
<div class="stat-label">Agentic QA</div>
|
|
21
|
-
</div>
|
|
22
|
-
</div>
|
|
23
|
-
|
|
24
|
-
<div class="pipeline-flow">
|
|
25
|
-
<h2>Pipeline</h2>
|
|
26
|
-
<div class="flow-steps">
|
|
27
|
-
<div class="flow-step">
|
|
28
|
-
<div class="step-icon">📝</div>
|
|
29
|
-
<div class="step-name">User Story</div>
|
|
30
|
-
<div class="step-desc">Input business requirement</div>
|
|
31
|
-
</div>
|
|
32
|
-
<div class="flow-arrow">→</div>
|
|
33
|
-
<div class="flow-step">
|
|
34
|
-
<div class="step-icon">📋</div>
|
|
35
|
-
<div class="step-name">Plan</div>
|
|
36
|
-
<div class="step-desc">AI generates test plan</div>
|
|
37
|
-
</div>
|
|
38
|
-
<div class="flow-arrow">→</div>
|
|
39
|
-
<div class="flow-step">
|
|
40
|
-
<div class="step-icon">🧪</div>
|
|
41
|
-
<div class="step-name">Generate</div>
|
|
42
|
-
<div class="step-desc">AI writes test code</div>
|
|
43
|
-
</div>
|
|
44
|
-
<div class="flow-arrow">→</div>
|
|
45
|
-
<div class="flow-step">
|
|
46
|
-
<div class="step-icon">⚡</div>
|
|
47
|
-
<div class="step-name">Execute</div>
|
|
48
|
-
<div class="step-desc">Playwright runs tests</div>
|
|
49
|
-
</div>
|
|
50
|
-
<div class="flow-arrow">→</div>
|
|
51
|
-
<div class="flow-step">
|
|
52
|
-
<div class="step-icon">🛠️</div>
|
|
53
|
-
<div class="step-name">Heal</div>
|
|
54
|
-
<div class="step-desc">Auto-fix failures</div>
|
|
55
|
-
</div>
|
|
56
|
-
<div class="flow-arrow">→</div>
|
|
57
|
-
<div class="flow-step">
|
|
58
|
-
<div class="step-icon">📊</div>
|
|
59
|
-
<div class="step-name">Report</div>
|
|
60
|
-
<div class="step-desc">Comprehensive results</div>
|
|
1
|
+
<% layout='layouts/main' %>
|
|
2
|
+
<% title='Dashboard' %>
|
|
3
|
+
|
|
4
|
+
<div class="hero">
|
|
5
|
+
<h1>AI QA Pipeline </h1>
|
|
6
|
+
<p>User Story → Test Plan → Test Generation → Execution → Self-Healing → Report</p>
|
|
7
|
+
<% if (project) { %>
|
|
8
|
+
<p class="hero-project">
|
|
9
|
+
Project: <strong>
|
|
10
|
+
<%= project.name %>
|
|
11
|
+
</strong>
|
|
12
|
+
<% if (stats.lastRunStatus==='passed' ) { %><span class="badge badge-pass" style="margin-left:0.5rem;">Last
|
|
13
|
+
Run ✅</span>
|
|
14
|
+
<% } %>
|
|
15
|
+
<% if (stats.lastRunStatus==='failed' ) { %><span class="badge badge-fail"
|
|
16
|
+
style="margin-left:0.5rem;">Last Run ❌</span>
|
|
17
|
+
<% } %>
|
|
18
|
+
</p>
|
|
19
|
+
<% } %>
|
|
61
20
|
</div>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
<div
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
21
|
+
|
|
22
|
+
<% if (!project) { %>
|
|
23
|
+
<!-- ─── No project state ─── -->
|
|
24
|
+
<div class="empty-state">
|
|
25
|
+
<div style="font-size:3rem;margin-bottom:1rem;">🚀</div>
|
|
26
|
+
<h3>Dashboard prêt — aucun projet configuré</h3>
|
|
27
|
+
<p>Ce dashboard est une template. Pointez-le vers votre dossier de test pour voir vos données.</p>
|
|
28
|
+
<a href="/projects/add" class="btn btn-primary" style="margin-top:0.5rem;">Configurer un projet</a>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<% } else { %>
|
|
32
|
+
|
|
33
|
+
<!-- ─── Real stats from project folder ─── -->
|
|
34
|
+
<div class="stats-row">
|
|
35
|
+
<div class="stat-card">
|
|
36
|
+
<div class="stat-number">
|
|
37
|
+
<%= stats.stories %>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="stat-label">User Stories</div>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="stat-card">
|
|
42
|
+
<div class="stat-number">
|
|
43
|
+
<%= stats.plans %>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="stat-label">Test Plans</div>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="stat-card">
|
|
48
|
+
<div class="stat-number">
|
|
49
|
+
<%= stats.specs %>
|
|
50
|
+
</div>
|
|
51
|
+
<div class="stat-label">Test Specs</div>
|
|
52
|
+
</div>
|
|
53
|
+
<div class="stat-card">
|
|
54
|
+
<div class="stat-number">
|
|
55
|
+
<%= stats.runs %>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="stat-label">Executions</div>
|
|
58
|
+
</div>
|
|
59
|
+
<div class="stat-card">
|
|
60
|
+
<div
|
|
61
|
+
class="stat-number <%= stats.passRate >= 80 ? 'text-success' : stats.passRate >= 50 ? 'text-warning' : stats.runs > 0 ? 'text-danger' : '' %>">
|
|
62
|
+
<%= stats.runs> 0 ? stats.passRate + '%' : '—' %>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="stat-label">Pass Rate</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<!-- ─── Pipeline flow ─── -->
|
|
69
|
+
<div class="pipeline-flow">
|
|
70
|
+
<h2>Pipeline</h2>
|
|
71
|
+
<div class="flow-steps">
|
|
72
|
+
<div class="flow-step <%= stats.stories > 0 ? 'step-active' : '' %>">
|
|
73
|
+
<div class="step-icon">📝</div>
|
|
74
|
+
<div class="step-name">User Story</div>
|
|
75
|
+
<div class="step-desc">
|
|
76
|
+
<%= stats.stories %> story<%= stats.stories !==1 ? 's' : '' %>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
<div class="flow-arrow">→</div>
|
|
80
|
+
<div class="flow-step <%= stats.plans > 0 ? 'step-active' : '' %>">
|
|
81
|
+
<div class="step-icon">📋</div>
|
|
82
|
+
<div class="step-name">Plan</div>
|
|
83
|
+
<div class="step-desc">
|
|
84
|
+
<%= stats.plans %> plan<%= stats.plans !==1 ? 's' : '' %>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="flow-arrow">→</div>
|
|
88
|
+
<div class="flow-step <%= stats.specs > 0 ? 'step-active' : '' %>">
|
|
89
|
+
<div class="step-icon">🧪</div>
|
|
90
|
+
<div class="step-name">Generate</div>
|
|
91
|
+
<div class="step-desc">
|
|
92
|
+
<%= stats.specs %> spec<%= stats.specs !==1 ? 's' : '' %>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
<div class="flow-arrow">→</div>
|
|
96
|
+
<div class="flow-step <%= stats.runs > 0 ? 'step-active' : '' %>">
|
|
97
|
+
<div class="step-icon">⚡</div>
|
|
98
|
+
<div class="step-name">Execute</div>
|
|
99
|
+
<div class="step-desc">
|
|
100
|
+
<%= stats.runs %> run<%= stats.runs !==1 ? 's' : '' %>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
<div class="flow-arrow">→</div>
|
|
104
|
+
<div class="flow-step">
|
|
105
|
+
<div class="step-icon">🛠️</div>
|
|
106
|
+
<div class="step-name">Heal</div>
|
|
107
|
+
<div class="step-desc">Auto-fix failures</div>
|
|
108
|
+
</div>
|
|
109
|
+
<div class="flow-arrow">→</div>
|
|
110
|
+
<div class="flow-step">
|
|
111
|
+
<div class="step-icon">📊</div>
|
|
112
|
+
<div class="step-name">Report</div>
|
|
113
|
+
<div class="step-desc">Comprehensive results</div>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<!-- ─── Quick links ─── -->
|
|
119
|
+
<div class="quick-links-row">
|
|
120
|
+
<a href="/stories?project=<%= project.id %>" class="quick-link-card">
|
|
121
|
+
<div class="ql-icon">📝</div>
|
|
122
|
+
<div class="ql-label">Stories <span class="badge badge-project">
|
|
123
|
+
<%= stats.stories %>
|
|
124
|
+
</span></div>
|
|
125
|
+
</a>
|
|
126
|
+
<a href="/runs?project=<%= project.id %>" class="quick-link-card">
|
|
127
|
+
<div class="ql-icon">⚡</div>
|
|
128
|
+
<div class="ql-label">Runs <span class="badge badge-project">
|
|
129
|
+
<%= stats.runs %>
|
|
130
|
+
</span></div>
|
|
131
|
+
</a>
|
|
132
|
+
<a href="/analytics?project=<%= project.id %>" class="quick-link-card">
|
|
133
|
+
<div class="ql-icon">📊</div>
|
|
134
|
+
<div class="ql-label">Analytics</div>
|
|
135
|
+
</a>
|
|
136
|
+
<a href="/projects/<%= project.id %>" class="quick-link-card">
|
|
137
|
+
<div class="ql-icon">⚙️</div>
|
|
138
|
+
<div class="ql-label">Pipeline</div>
|
|
139
|
+
</a>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<% if (stats.stories===0) { %>
|
|
143
|
+
<div class="alert alert-info" style="margin-top:1.5rem;">
|
|
144
|
+
💡 <strong>Aucune user story trouvée</strong> dans <code><%= project.path %>/user-story/</code>.
|
|
145
|
+
Ajoutez un fichier <code>.md</code> pour commencer.
|
|
146
|
+
</div>
|
|
147
|
+
<% } %>
|
|
148
|
+
|
|
149
|
+
<% } %>
|
|
150
|
+
|
|
151
|
+
<style>
|
|
152
|
+
.hero-project {
|
|
153
|
+
color: var(--text-muted);
|
|
154
|
+
font-size: 0.95rem;
|
|
155
|
+
margin-top: 0.5rem;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* Active pipeline step */
|
|
159
|
+
.step-active {
|
|
160
|
+
border-color: var(--primary);
|
|
161
|
+
background: rgba(99, 102, 241, 0.08);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.step-active .step-name {
|
|
165
|
+
color: var(--primary);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* Quick links row */
|
|
169
|
+
.quick-links-row {
|
|
170
|
+
display: flex;
|
|
171
|
+
gap: 1rem;
|
|
172
|
+
flex-wrap: wrap;
|
|
173
|
+
margin: 2rem 0;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.quick-link-card {
|
|
177
|
+
flex: 1;
|
|
178
|
+
min-width: 140px;
|
|
179
|
+
background: var(--surface);
|
|
180
|
+
border: 1px solid var(--border);
|
|
181
|
+
border-radius: var(--radius);
|
|
182
|
+
padding: 1.25rem 1rem;
|
|
183
|
+
text-align: center;
|
|
184
|
+
text-decoration: none;
|
|
185
|
+
color: var(--text);
|
|
186
|
+
transition: border-color 0.2s, transform 0.15s;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.quick-link-card:hover {
|
|
190
|
+
border-color: var(--primary);
|
|
191
|
+
transform: translateY(-2px);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.ql-icon {
|
|
195
|
+
font-size: 1.75rem;
|
|
196
|
+
margin-bottom: 0.4rem;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.ql-label {
|
|
200
|
+
font-size: 0.9rem;
|
|
201
|
+
font-weight: 600;
|
|
202
|
+
display: flex;
|
|
203
|
+
align-items: center;
|
|
204
|
+
justify-content: center;
|
|
205
|
+
gap: 0.4rem;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* Info alert */
|
|
209
|
+
.alert-info {
|
|
210
|
+
background: #1e1b4b;
|
|
211
|
+
border: 1px solid #3730a3;
|
|
212
|
+
color: #a5b4fc;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
@media (max-width: 768px) {
|
|
216
|
+
.stats-row {
|
|
217
|
+
flex-wrap: wrap;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.stat-card {
|
|
221
|
+
flex: 1 1 calc(50% - 0.5rem);
|
|
222
|
+
min-width: unset;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.quick-links-row {
|
|
226
|
+
gap: 0.75rem;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.quick-link-card {
|
|
230
|
+
min-width: calc(50% - 0.375rem);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.flow-steps {
|
|
234
|
+
flex-direction: column;
|
|
235
|
+
align-items: flex-start;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.flow-arrow {
|
|
239
|
+
transform: rotate(90deg);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
</style>
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title><%= title || 'QA Dashboard' %> — AI QA Pipeline</title>
|
|
7
|
+
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@500;600;700&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400&display=swap" rel="stylesheet">
|
|
7
8
|
<link rel="stylesheet" href="/css/style.css">
|
|
8
9
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
|
|
10
|
+
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
9
11
|
</head>
|
|
10
12
|
<body>
|
|
11
13
|
<nav class="navbar">
|
|
@@ -17,6 +19,7 @@
|
|
|
17
19
|
<a href="/runs">Runs</a>
|
|
18
20
|
<a href="/analytics">Analytics</a>
|
|
19
21
|
<a href="/data">Test Data</a>
|
|
22
|
+
<button onclick="location.reload()" class="btn-refresh" title="Refresh">↻</button>
|
|
20
23
|
</div>
|
|
21
24
|
</div>
|
|
22
25
|
</nav>
|
|
@@ -24,5 +27,20 @@
|
|
|
24
27
|
<%- body %>
|
|
25
28
|
</main>
|
|
26
29
|
<script src="/js/main.js"></script>
|
|
30
|
+
<script>
|
|
31
|
+
// Signal the server when the tab is closed
|
|
32
|
+
window.addEventListener('beforeunload', () => {
|
|
33
|
+
navigator.sendBeacon('/shutdown', '{}');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// React-like live reload connection
|
|
37
|
+
const reloadSource = new EventSource('/live-reload');
|
|
38
|
+
reloadSource.onerror = () => {
|
|
39
|
+
// If connection is lost (server restarted), wait 1s and reload browser
|
|
40
|
+
setTimeout(() => {
|
|
41
|
+
location.reload();
|
|
42
|
+
}, 1000);
|
|
43
|
+
};
|
|
44
|
+
</script>
|
|
27
45
|
</body>
|
|
28
46
|
</html>
|
|
@@ -11,39 +11,39 @@
|
|
|
11
11
|
</div>
|
|
12
12
|
</div>
|
|
13
13
|
|
|
14
|
-
<div class="project-detail">
|
|
15
|
-
<p class="project-path"><%= project.path %></p>
|
|
14
|
+
<div class="project-detail" style="margin-bottom:2rem;">
|
|
15
|
+
<p class="project-path" style="font-family:var(--font-mono);color:var(--text-disabled);font-size:0.88rem;"><%= project.path %></p>
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
|
-
<div class="row">
|
|
18
|
+
<div class="row" style="margin-bottom:2rem;">
|
|
19
19
|
<div class="col">
|
|
20
|
-
<div class="card">
|
|
21
|
-
<h3
|
|
20
|
+
<div class="card card-lavender">
|
|
21
|
+
<h3>📝 User Stories</h3>
|
|
22
22
|
<div class="big-number"><%= list.stories.length %></div>
|
|
23
|
-
<a href="/stories?project=<%= project.id %>" class="btn btn-sm">View</a>
|
|
23
|
+
<a href="/stories?project=<%= project.id %>" class="btn btn-sm" style="margin-top:1rem;">View Stories</a>
|
|
24
24
|
</div>
|
|
25
25
|
</div>
|
|
26
26
|
<div class="col">
|
|
27
|
-
<div class="card">
|
|
28
|
-
<h3
|
|
27
|
+
<div class="card card-peach">
|
|
28
|
+
<h3>📋 Test Plans</h3>
|
|
29
29
|
<div class="big-number"><%= list.plans.length %></div>
|
|
30
30
|
</div>
|
|
31
31
|
</div>
|
|
32
32
|
<div class="col">
|
|
33
|
-
<div class="card">
|
|
34
|
-
<h3
|
|
33
|
+
<div class="card card-ochre">
|
|
34
|
+
<h3>🧪 Test Specs</h3>
|
|
35
35
|
<div class="big-number"><%= list.specs.length %></div>
|
|
36
36
|
</div>
|
|
37
37
|
</div>
|
|
38
38
|
</div>
|
|
39
39
|
|
|
40
40
|
<div class="card">
|
|
41
|
-
<h3
|
|
42
|
-
<form id="pipeline-form" data-project="<%= project.id %>">
|
|
41
|
+
<h3>⚡ Pipeline Execution</h3>
|
|
42
|
+
<form id="pipeline-form" data-project="<%= project.id %>" class="form">
|
|
43
43
|
<div class="form-group">
|
|
44
|
-
<label>User Story</label>
|
|
44
|
+
<label>Select User Story to Run</label>
|
|
45
45
|
<select name="storyName" class="form-input">
|
|
46
|
-
<option value="">— Run status check —</option>
|
|
46
|
+
<option value="">— Run status check / full suite —</option>
|
|
47
47
|
<% list.stories.forEach(s => { %>
|
|
48
48
|
<option value="<%= s %>"><%= s %></option>
|
|
49
49
|
<% }) %>
|
|
@@ -51,17 +51,18 @@
|
|
|
51
51
|
</div>
|
|
52
52
|
<button type="submit" class="btn btn-primary">Run Pipeline</button>
|
|
53
53
|
</form>
|
|
54
|
-
<div id="pipeline-output" class="output-box"></div>
|
|
54
|
+
<div id="pipeline-output" class="output-box" style="display:none;"></div>
|
|
55
55
|
</div>
|
|
56
56
|
|
|
57
57
|
<div class="card">
|
|
58
|
-
<h3
|
|
58
|
+
<h3>⚙️ Quick Actions</h3>
|
|
59
|
+
<p style="color:var(--text-muted);font-size:0.9rem;margin-bottom:1rem;">Manage, execute, or build standard reports for this project instantly.</p>
|
|
59
60
|
<div class="action-buttons">
|
|
60
61
|
<button class="btn btn-sm" onclick="runAction('<%= project.id %>', 'status')">Status</button>
|
|
61
62
|
<button class="btn btn-sm" onclick="runAction('<%= project.id %>', 'execute')">Execute All Tests</button>
|
|
62
63
|
<button class="btn btn-sm" onclick="runAction('<%= project.id %>', 'report')">Generate Report</button>
|
|
63
64
|
</div>
|
|
64
|
-
<div id="quick-output" class="output-box"></div>
|
|
65
|
+
<div id="quick-output" class="output-box" style="display:none;"></div>
|
|
65
66
|
</div>
|
|
66
67
|
|
|
67
68
|
<script>
|
|
@@ -71,27 +72,46 @@ document.getElementById('pipeline-form').addEventListener('submit', async (e) =>
|
|
|
71
72
|
const projectId = form.dataset.project;
|
|
72
73
|
const formData = new FormData(form);
|
|
73
74
|
const output = document.getElementById('pipeline-output');
|
|
75
|
+
|
|
76
|
+
output.style.display = 'block';
|
|
74
77
|
output.textContent = 'Running pipeline...';
|
|
75
78
|
output.className = 'output-box output-running';
|
|
76
79
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
try {
|
|
81
|
+
const res = await fetch(`/projects/${projectId}/run-pipeline`, {
|
|
82
|
+
method: 'POST',
|
|
83
|
+
headers: { 'Content-Type': 'application/json' },
|
|
84
|
+
body: JSON.stringify({ storyName: formData.get('storyName') })
|
|
85
|
+
});
|
|
86
|
+
if (!res.ok) throw new Error(`HTTP Error ${res.status}`);
|
|
87
|
+
const data = await res.json();
|
|
88
|
+
output.textContent = data.output || data.error || 'Done';
|
|
89
|
+
output.className = `output-box ${data.success ? 'output-success' : 'output-error'}`;
|
|
90
|
+
} catch (err) {
|
|
91
|
+
output.textContent = 'Network or server error: ' + err.message;
|
|
92
|
+
output.className = 'output-box output-error';
|
|
93
|
+
}
|
|
85
94
|
});
|
|
86
95
|
|
|
87
96
|
async function runAction(projectId, action) {
|
|
88
97
|
const output = document.getElementById('quick-output');
|
|
98
|
+
output.style.display = 'block';
|
|
89
99
|
output.textContent = `Running ${action}...`;
|
|
90
100
|
output.className = 'output-box output-running';
|
|
91
101
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
102
|
+
try {
|
|
103
|
+
const res = await fetch(`/projects/${projectId}/${action}`, {
|
|
104
|
+
method: 'POST',
|
|
105
|
+
headers: { 'Content-Type': 'application/json' },
|
|
106
|
+
body: '{}'
|
|
107
|
+
});
|
|
108
|
+
if (!res.ok) throw new Error(`HTTP Error ${res.status}`);
|
|
109
|
+
const data = await res.json();
|
|
110
|
+
output.textContent = data.output || data.error || 'Done';
|
|
111
|
+
output.className = `output-box ${data.success ? 'output-success' : 'output-error'}`;
|
|
112
|
+
} catch (err) {
|
|
113
|
+
output.textContent = 'Network or server error: ' + err.message;
|
|
114
|
+
output.className = 'output-box output-error';
|
|
115
|
+
}
|
|
96
116
|
}
|
|
97
117
|
</script>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
% layout = 'layouts/main' %>
|
|
2
2
|
<% title = 'Projects' %>
|
|
3
3
|
|
|
4
4
|
<div class="page-header">
|
|
@@ -8,18 +8,20 @@
|
|
|
8
8
|
|
|
9
9
|
<% if (projects.length === 0) { %>
|
|
10
10
|
<div class="empty-state">
|
|
11
|
-
<p>No projects yet. Add your test project
|
|
11
|
+
<p>No projects yet. Add your test project to connect the AI QA pipeline.</p>
|
|
12
|
+
<a href="/projects/add" class="btn btn-primary" style="margin-top:0.5rem;">+ Add Project</a>
|
|
12
13
|
</div>
|
|
13
14
|
<% } %>
|
|
14
15
|
|
|
15
16
|
<div class="project-grid">
|
|
16
|
-
<%
|
|
17
|
-
|
|
17
|
+
<% const colors = ['card-cream', 'card-lavender', 'card-peach', 'card-ochre']; %>
|
|
18
|
+
<% projects.forEach((p, idx) => { %>
|
|
19
|
+
<div class="project-card <%= colors[idx % colors.length] %>">
|
|
18
20
|
<div class="project-name"><%= p.name %></div>
|
|
19
21
|
<div class="project-path"><%= p.path %></div>
|
|
20
22
|
<div class="project-meta">Added: <%= new Date(p.createdAt).toLocaleDateString() %></div>
|
|
21
23
|
<div class="project-actions">
|
|
22
|
-
<a href="/projects/<%= p.id %>" class="btn btn-sm">Open</a>
|
|
24
|
+
<a href="/projects/<%= p.id %>" class="btn btn-sm btn-primary">Open</a>
|
|
23
25
|
<form action="/projects/<%= p.id %>/remove" method="POST" style="display:inline">
|
|
24
26
|
<button class="btn btn-sm btn-danger">Remove</button>
|
|
25
27
|
</form>
|