@autocode-cli/autocode 0.1.4 → 0.1.6
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/README.md +28 -24
- package/dist/cli/commands/init.js +1 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/serve.d.ts.map +1 -1
- package/dist/cli/commands/serve.js +27 -0
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/sync.d.ts +9 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +91 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/parser.d.ts.map +1 -1
- package/dist/cli/parser.js +2 -0
- package/dist/cli/parser.js.map +1 -1
- package/dist/core/catalog.d.ts +52 -0
- package/dist/core/catalog.d.ts.map +1 -0
- package/dist/core/catalog.js +101 -0
- package/dist/core/catalog.js.map +1 -0
- package/dist/core/column.d.ts +2 -1
- package/dist/core/column.d.ts.map +1 -1
- package/dist/core/column.js +23 -11
- package/dist/core/column.js.map +1 -1
- package/dist/core/pipeline.d.ts +70 -0
- package/dist/core/pipeline.d.ts.map +1 -0
- package/dist/core/pipeline.js +199 -0
- package/dist/core/pipeline.js.map +1 -0
- package/dist/core/sync.d.ts +41 -0
- package/dist/core/sync.d.ts.map +1 -0
- package/dist/core/sync.js +269 -0
- package/dist/core/sync.js.map +1 -0
- package/dist/server/api.d.ts.map +1 -1
- package/dist/server/api.js +300 -0
- package/dist/server/api.js.map +1 -1
- package/dist/server/dashboard/pages/index.d.ts +1 -0
- package/dist/server/dashboard/pages/index.d.ts.map +1 -1
- package/dist/server/dashboard/pages/index.js +1 -0
- package/dist/server/dashboard/pages/index.js.map +1 -1
- package/dist/server/dashboard/pages/main-dashboard.d.ts.map +1 -1
- package/dist/server/dashboard/pages/main-dashboard.js +2 -1
- package/dist/server/dashboard/pages/main-dashboard.js.map +1 -1
- package/dist/server/dashboard/pages/pipeline-configurator.d.ts +8 -0
- package/dist/server/dashboard/pages/pipeline-configurator.d.ts.map +1 -0
- package/dist/server/dashboard/pages/pipeline-configurator.js +1531 -0
- package/dist/server/dashboard/pages/pipeline-configurator.js.map +1 -0
- package/dist/server/dashboard/pages/water-quality-form.d.ts +10 -0
- package/dist/server/dashboard/pages/water-quality-form.d.ts.map +1 -0
- package/dist/server/dashboard/pages/water-quality-form.js +910 -0
- package/dist/server/dashboard/pages/water-quality-form.js.map +1 -0
- package/dist/server/dashboard/styles/base.d.ts.map +1 -1
- package/dist/server/dashboard/styles/base.js +11 -0
- package/dist/server/dashboard/styles/base.js.map +1 -1
- package/dist/server/dashboard.d.ts +1 -1
- package/dist/server/dashboard.d.ts.map +1 -1
- package/dist/server/dashboard.js +1 -1
- package/dist/server/dashboard.js.map +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +67 -1
- package/dist/server/index.js.map +1 -1
- package/dist/services/claude.d.ts +2 -1
- package/dist/services/claude.d.ts.map +1 -1
- package/dist/services/claude.js +8 -1
- package/dist/services/claude.js.map +1 -1
- package/dist/types/index.d.ts +67 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/config.d.ts +28 -20
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +12 -10
- package/dist/utils/config.js.map +1 -1
- package/package.json +2 -2
- package/templates/catalog.yaml +100 -0
- package/templates/{columns/00_backlog.en.md → prompts/backlog.en.md} +1 -1
- package/templates/{columns/00_backlog.fr.md → prompts/backlog.fr.md} +1 -1
- package/templates/prompts/changelog.en.md +31 -0
- package/templates/prompts/changelog.fr.md +31 -0
- package/templates/prompts/deploy-prod.en.md +32 -0
- package/templates/prompts/deploy-prod.fr.md +32 -0
- package/templates/{columns/10_deploy-staging.en.md → prompts/deploy-staging.en.md} +1 -1
- package/templates/{columns/10_deploy-staging.fr.md → prompts/deploy-staging.fr.md} +1 -1
- package/templates/prompts/design.en.md +30 -0
- package/templates/prompts/design.fr.md +30 -0
- package/templates/prompts/dev.en.md +31 -0
- package/templates/prompts/dev.fr.md +31 -0
- package/templates/{columns/12_done.en.md → prompts/done.en.md} +1 -1
- package/templates/{columns/12_done.fr.md → prompts/done.fr.md} +1 -1
- package/templates/prompts/git-commit.en.md +35 -0
- package/templates/prompts/git-commit.fr.md +35 -0
- package/templates/prompts/git-push.en.md +31 -0
- package/templates/prompts/git-push.fr.md +31 -0
- package/templates/prompts/git-tag.en.md +32 -0
- package/templates/prompts/git-tag.fr.md +32 -0
- package/templates/{columns/02_in-progress.en.md → prompts/in-progress.en.md} +1 -1
- package/templates/{columns/02_in-progress.fr.md → prompts/in-progress.fr.md} +1 -1
- package/templates/prompts/qualification.en.md +30 -0
- package/templates/prompts/qualification.fr.md +30 -0
- package/templates/{columns/01_ready.en.md → prompts/ready.en.md} +1 -1
- package/templates/{columns/01_ready.fr.md → prompts/ready.fr.md} +1 -1
- package/templates/prompts/retest-cypress.en.md +29 -0
- package/templates/prompts/retest-cypress.fr.md +29 -0
- package/templates/prompts/retest-playwright.en.md +30 -0
- package/templates/prompts/retest-playwright.fr.md +30 -0
- package/templates/prompts/retest.en.md +31 -0
- package/templates/prompts/retest.fr.md +31 -0
- package/templates/{columns/03_review-best-practices.en.md → prompts/review-best-practices.en.md} +1 -1
- package/templates/{columns/03_review-best-practices.fr.md → prompts/review-best-practices.fr.md} +1 -1
- package/templates/prompts/review-code.en.md +31 -0
- package/templates/prompts/review-code.fr.md +31 -0
- package/templates/{columns/05_review-consistency.en.md → prompts/review-consistency.en.md} +1 -1
- package/templates/{columns/05_review-consistency.fr.md → prompts/review-consistency.fr.md} +1 -1
- package/templates/{columns/04_review-no-duplication.en.md → prompts/review-no-duplication.en.md} +1 -1
- package/templates/{columns/04_review-no-duplication.fr.md → prompts/review-no-duplication.fr.md} +1 -1
- package/templates/{columns/06_review-security.en.md → prompts/review-security.en.md} +1 -1
- package/templates/{columns/06_review-security.fr.md → prompts/review-security.fr.md} +1 -1
- package/templates/prompts/specification.en.md +30 -0
- package/templates/prompts/specification.fr.md +30 -0
- package/templates/{columns/08_testing-cypress.en.md → prompts/testing-cypress.en.md} +1 -1
- package/templates/{columns/08_testing-cypress.fr.md → prompts/testing-cypress.fr.md} +1 -1
- package/templates/prompts/testing-integration.en.md +32 -0
- package/templates/prompts/testing-integration.fr.md +32 -0
- package/templates/{columns/07_testing-playwright.en.md → prompts/testing-playwright.en.md} +1 -1
- package/templates/{columns/07_testing-playwright.fr.md → prompts/testing-playwright.fr.md} +1 -1
- package/templates/prompts/testing-unit.en.md +32 -0
- package/templates/prompts/testing-unit.fr.md +32 -0
- package/templates/{columns/09_update-docs.en.md → prompts/update-docs.en.md} +1 -1
- package/templates/{columns/09_update-docs.fr.md → prompts/update-docs.fr.md} +1 -1
- package/templates/{columns/11_validate-staging.en.md → prompts/validate-staging.en.md} +1 -1
- package/templates/{columns/11_validate-staging.fr.md → prompts/validate-staging.fr.md} +1 -1
|
@@ -0,0 +1,1531 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline configurator page generator
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Generate the pipeline configurator page
|
|
6
|
+
*/
|
|
7
|
+
export function generatePipelineConfiguratorPage(lang) {
|
|
8
|
+
return `<!DOCTYPE html>
|
|
9
|
+
<html lang="${lang}">
|
|
10
|
+
<head>
|
|
11
|
+
<meta charset="UTF-8">
|
|
12
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
13
|
+
<title>Pipeline Configurator - AutoCode</title>
|
|
14
|
+
<style>
|
|
15
|
+
:root {
|
|
16
|
+
--bg: #0d1117;
|
|
17
|
+
--bg-card: #161b22;
|
|
18
|
+
--bg-hover: #1f2937;
|
|
19
|
+
--border: #30363d;
|
|
20
|
+
--fg: #c9d1d9;
|
|
21
|
+
--muted: #8b949e;
|
|
22
|
+
--accent: #7c3aed;
|
|
23
|
+
--blue: #4dabf7;
|
|
24
|
+
--green: #22c55e;
|
|
25
|
+
--red: #ef4444;
|
|
26
|
+
--yellow: #eab308;
|
|
27
|
+
--segment-definition: #3b82f6;
|
|
28
|
+
--segment-action: #f97316;
|
|
29
|
+
--segment-finish: #22c55e;
|
|
30
|
+
}
|
|
31
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
32
|
+
body {
|
|
33
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
34
|
+
background: var(--bg);
|
|
35
|
+
color: var(--fg);
|
|
36
|
+
min-height: 100vh;
|
|
37
|
+
}
|
|
38
|
+
.header {
|
|
39
|
+
background: var(--bg-card);
|
|
40
|
+
border-bottom: 1px solid var(--border);
|
|
41
|
+
padding: 16px 24px;
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
gap: 16px;
|
|
45
|
+
position: sticky;
|
|
46
|
+
top: 0;
|
|
47
|
+
z-index: 100;
|
|
48
|
+
}
|
|
49
|
+
.back-btn {
|
|
50
|
+
background: none;
|
|
51
|
+
border: 1px solid var(--border);
|
|
52
|
+
color: var(--fg);
|
|
53
|
+
padding: 8px 12px;
|
|
54
|
+
border-radius: 6px;
|
|
55
|
+
cursor: pointer;
|
|
56
|
+
font-size: 14px;
|
|
57
|
+
text-decoration: none;
|
|
58
|
+
}
|
|
59
|
+
.back-btn:hover { border-color: var(--accent); color: var(--accent); }
|
|
60
|
+
.title { flex: 1; font-size: 18px; font-weight: 600; }
|
|
61
|
+
.title span { color: var(--accent); }
|
|
62
|
+
.btn {
|
|
63
|
+
padding: 8px 16px;
|
|
64
|
+
border-radius: 6px;
|
|
65
|
+
font-size: 14px;
|
|
66
|
+
font-weight: 500;
|
|
67
|
+
cursor: pointer;
|
|
68
|
+
border: none;
|
|
69
|
+
display: inline-flex;
|
|
70
|
+
align-items: center;
|
|
71
|
+
gap: 6px;
|
|
72
|
+
}
|
|
73
|
+
.btn-primary { background: var(--accent); color: white; }
|
|
74
|
+
.btn-primary:hover { opacity: 0.9; }
|
|
75
|
+
.btn-success { background: var(--green); color: white; }
|
|
76
|
+
.btn-success:hover { opacity: 0.9; }
|
|
77
|
+
.btn-secondary { background: var(--bg); border: 1px solid var(--border); color: var(--fg); }
|
|
78
|
+
.btn-secondary:hover { border-color: var(--accent); }
|
|
79
|
+
.btn-danger { background: var(--red); color: white; }
|
|
80
|
+
.btn-danger:hover { opacity: 0.9; }
|
|
81
|
+
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
82
|
+
|
|
83
|
+
.container {
|
|
84
|
+
display: grid;
|
|
85
|
+
grid-template-columns: 280px 1fr;
|
|
86
|
+
gap: 24px;
|
|
87
|
+
padding: 24px;
|
|
88
|
+
max-width: 1600px;
|
|
89
|
+
margin: 0 auto;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Sidebar - Pipeline List */
|
|
93
|
+
.sidebar {
|
|
94
|
+
background: var(--bg-card);
|
|
95
|
+
border: 1px solid var(--border);
|
|
96
|
+
border-radius: 8px;
|
|
97
|
+
padding: 16px;
|
|
98
|
+
}
|
|
99
|
+
.sidebar h2 {
|
|
100
|
+
font-size: 14px;
|
|
101
|
+
text-transform: uppercase;
|
|
102
|
+
color: var(--muted);
|
|
103
|
+
margin-bottom: 12px;
|
|
104
|
+
letter-spacing: 0.5px;
|
|
105
|
+
}
|
|
106
|
+
.pipeline-list {
|
|
107
|
+
display: flex;
|
|
108
|
+
flex-direction: column;
|
|
109
|
+
gap: 8px;
|
|
110
|
+
margin-bottom: 16px;
|
|
111
|
+
}
|
|
112
|
+
.pipeline-item {
|
|
113
|
+
padding: 12px;
|
|
114
|
+
background: var(--bg);
|
|
115
|
+
border: 1px solid var(--border);
|
|
116
|
+
border-radius: 6px;
|
|
117
|
+
cursor: pointer;
|
|
118
|
+
transition: all 0.2s;
|
|
119
|
+
}
|
|
120
|
+
.pipeline-item:hover { border-color: var(--accent); }
|
|
121
|
+
.pipeline-item.active { border-color: var(--accent); background: rgba(124, 58, 237, 0.1); }
|
|
122
|
+
.pipeline-item .name { font-weight: 500; margin-bottom: 4px; }
|
|
123
|
+
.pipeline-item .meta { font-size: 12px; color: var(--muted); }
|
|
124
|
+
.pipeline-item .badge {
|
|
125
|
+
display: inline-block;
|
|
126
|
+
background: var(--green);
|
|
127
|
+
color: white;
|
|
128
|
+
font-size: 10px;
|
|
129
|
+
padding: 2px 6px;
|
|
130
|
+
border-radius: 4px;
|
|
131
|
+
margin-left: 8px;
|
|
132
|
+
}
|
|
133
|
+
.new-pipeline-btn {
|
|
134
|
+
width: 100%;
|
|
135
|
+
padding: 12px;
|
|
136
|
+
background: var(--bg);
|
|
137
|
+
border: 1px dashed var(--border);
|
|
138
|
+
border-radius: 6px;
|
|
139
|
+
color: var(--muted);
|
|
140
|
+
cursor: pointer;
|
|
141
|
+
font-size: 14px;
|
|
142
|
+
}
|
|
143
|
+
.new-pipeline-btn:hover { border-color: var(--accent); color: var(--accent); }
|
|
144
|
+
|
|
145
|
+
/* Main area */
|
|
146
|
+
.main {
|
|
147
|
+
display: flex;
|
|
148
|
+
flex-direction: column;
|
|
149
|
+
gap: 24px;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* Pipeline details */
|
|
153
|
+
.pipeline-header {
|
|
154
|
+
background: var(--bg-card);
|
|
155
|
+
border: 1px solid var(--border);
|
|
156
|
+
border-radius: 8px;
|
|
157
|
+
padding: 20px;
|
|
158
|
+
}
|
|
159
|
+
.pipeline-form {
|
|
160
|
+
display: grid;
|
|
161
|
+
grid-template-columns: 1fr 1fr auto;
|
|
162
|
+
gap: 16px;
|
|
163
|
+
align-items: end;
|
|
164
|
+
}
|
|
165
|
+
.form-group {
|
|
166
|
+
display: flex;
|
|
167
|
+
flex-direction: column;
|
|
168
|
+
gap: 6px;
|
|
169
|
+
}
|
|
170
|
+
.form-group label {
|
|
171
|
+
font-size: 12px;
|
|
172
|
+
color: var(--muted);
|
|
173
|
+
text-transform: uppercase;
|
|
174
|
+
}
|
|
175
|
+
.form-group input, .form-group textarea {
|
|
176
|
+
background: var(--bg);
|
|
177
|
+
border: 1px solid var(--border);
|
|
178
|
+
border-radius: 6px;
|
|
179
|
+
padding: 10px 12px;
|
|
180
|
+
color: var(--fg);
|
|
181
|
+
font-size: 14px;
|
|
182
|
+
}
|
|
183
|
+
.form-group input:focus, .form-group textarea:focus {
|
|
184
|
+
outline: none;
|
|
185
|
+
border-color: var(--accent);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* Segments editor */
|
|
189
|
+
.segments-container {
|
|
190
|
+
display: grid;
|
|
191
|
+
grid-template-columns: repeat(3, 1fr);
|
|
192
|
+
gap: 16px;
|
|
193
|
+
}
|
|
194
|
+
.segment {
|
|
195
|
+
background: var(--bg-card);
|
|
196
|
+
border: 1px solid var(--border);
|
|
197
|
+
border-radius: 8px;
|
|
198
|
+
padding: 16px;
|
|
199
|
+
min-height: 300px;
|
|
200
|
+
}
|
|
201
|
+
.segment.definition { border-top: 3px solid var(--segment-definition); }
|
|
202
|
+
.segment.action { border-top: 3px solid var(--segment-action); }
|
|
203
|
+
.segment.finish { border-top: 3px solid var(--segment-finish); }
|
|
204
|
+
.segment-header {
|
|
205
|
+
display: flex;
|
|
206
|
+
align-items: center;
|
|
207
|
+
justify-content: space-between;
|
|
208
|
+
margin-bottom: 12px;
|
|
209
|
+
}
|
|
210
|
+
.segment-title {
|
|
211
|
+
font-weight: 600;
|
|
212
|
+
font-size: 14px;
|
|
213
|
+
text-transform: uppercase;
|
|
214
|
+
}
|
|
215
|
+
.segment.definition .segment-title { color: var(--segment-definition); }
|
|
216
|
+
.segment.action .segment-title { color: var(--segment-action); }
|
|
217
|
+
.segment.finish .segment-title { color: var(--segment-finish); }
|
|
218
|
+
.segment-desc {
|
|
219
|
+
font-size: 12px;
|
|
220
|
+
color: var(--muted);
|
|
221
|
+
margin-bottom: 16px;
|
|
222
|
+
}
|
|
223
|
+
.segment-columns {
|
|
224
|
+
display: flex;
|
|
225
|
+
flex-direction: column;
|
|
226
|
+
gap: 8px;
|
|
227
|
+
min-height: 200px;
|
|
228
|
+
padding: 8px;
|
|
229
|
+
background: var(--bg);
|
|
230
|
+
border-radius: 6px;
|
|
231
|
+
border: 2px dashed var(--border);
|
|
232
|
+
}
|
|
233
|
+
.segment-columns.dragover {
|
|
234
|
+
border-color: var(--accent);
|
|
235
|
+
background: rgba(124, 58, 237, 0.1);
|
|
236
|
+
}
|
|
237
|
+
.segment-columns.dragover-invalid {
|
|
238
|
+
border-color: var(--red);
|
|
239
|
+
background: rgba(239, 68, 68, 0.1);
|
|
240
|
+
cursor: not-allowed;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/* Column items */
|
|
244
|
+
.column-item {
|
|
245
|
+
padding: 10px 12px;
|
|
246
|
+
background: var(--bg-card);
|
|
247
|
+
border: 1px solid var(--border);
|
|
248
|
+
border-radius: 6px;
|
|
249
|
+
cursor: grab;
|
|
250
|
+
display: flex;
|
|
251
|
+
align-items: center;
|
|
252
|
+
gap: 8px;
|
|
253
|
+
transition: all 0.2s;
|
|
254
|
+
}
|
|
255
|
+
.column-item:hover { border-color: var(--accent); }
|
|
256
|
+
.column-item.dragging { opacity: 0.5; }
|
|
257
|
+
.column-item .drag-handle {
|
|
258
|
+
color: var(--muted);
|
|
259
|
+
font-size: 12px;
|
|
260
|
+
}
|
|
261
|
+
.column-item .name { flex: 1; font-size: 14px; }
|
|
262
|
+
.column-item .remove-btn {
|
|
263
|
+
background: none;
|
|
264
|
+
border: none;
|
|
265
|
+
color: var(--muted);
|
|
266
|
+
cursor: pointer;
|
|
267
|
+
padding: 4px;
|
|
268
|
+
border-radius: 4px;
|
|
269
|
+
}
|
|
270
|
+
.column-item .remove-btn:hover { color: var(--red); background: rgba(239, 68, 68, 0.1); }
|
|
271
|
+
|
|
272
|
+
/* Catalog */
|
|
273
|
+
.catalog {
|
|
274
|
+
background: var(--bg-card);
|
|
275
|
+
border: 1px solid var(--border);
|
|
276
|
+
border-radius: 8px;
|
|
277
|
+
padding: 16px;
|
|
278
|
+
}
|
|
279
|
+
.catalog h2 {
|
|
280
|
+
font-size: 14px;
|
|
281
|
+
text-transform: uppercase;
|
|
282
|
+
color: var(--muted);
|
|
283
|
+
margin-bottom: 16px;
|
|
284
|
+
display: flex;
|
|
285
|
+
align-items: center;
|
|
286
|
+
gap: 8px;
|
|
287
|
+
}
|
|
288
|
+
.catalog-grid {
|
|
289
|
+
display: grid;
|
|
290
|
+
grid-template-columns: repeat(3, 1fr);
|
|
291
|
+
gap: 16px;
|
|
292
|
+
}
|
|
293
|
+
.catalog-segment {
|
|
294
|
+
display: flex;
|
|
295
|
+
flex-direction: column;
|
|
296
|
+
}
|
|
297
|
+
.catalog-segment-header {
|
|
298
|
+
display: flex;
|
|
299
|
+
flex-direction: column;
|
|
300
|
+
align-items: center;
|
|
301
|
+
gap: 8px;
|
|
302
|
+
margin-bottom: 12px;
|
|
303
|
+
}
|
|
304
|
+
.catalog-segment-arrow {
|
|
305
|
+
font-size: 24px;
|
|
306
|
+
color: var(--muted);
|
|
307
|
+
opacity: 0.5;
|
|
308
|
+
animation: bounce 2s infinite;
|
|
309
|
+
}
|
|
310
|
+
@keyframes bounce {
|
|
311
|
+
0%, 100% { transform: translateY(0); }
|
|
312
|
+
50% { transform: translateY(-5px); }
|
|
313
|
+
}
|
|
314
|
+
.catalog-segment.definition .catalog-segment-arrow { color: var(--segment-definition); }
|
|
315
|
+
.catalog-segment.action .catalog-segment-arrow { color: var(--segment-action); }
|
|
316
|
+
.catalog-segment.finish .catalog-segment-arrow { color: var(--segment-finish); }
|
|
317
|
+
.catalog-segment-title {
|
|
318
|
+
font-size: 12px;
|
|
319
|
+
font-weight: 600;
|
|
320
|
+
padding: 4px 8px;
|
|
321
|
+
border-radius: 4px;
|
|
322
|
+
display: inline-block;
|
|
323
|
+
}
|
|
324
|
+
.catalog-segment.definition .catalog-segment-title { background: rgba(59, 130, 246, 0.2); color: var(--segment-definition); }
|
|
325
|
+
.catalog-segment.action .catalog-segment-title { background: rgba(249, 115, 22, 0.2); color: var(--segment-action); }
|
|
326
|
+
.catalog-segment.finish .catalog-segment-title { background: rgba(34, 197, 94, 0.2); color: var(--segment-finish); }
|
|
327
|
+
.catalog-columns {
|
|
328
|
+
display: flex;
|
|
329
|
+
flex-direction: column;
|
|
330
|
+
gap: 6px;
|
|
331
|
+
flex: 1;
|
|
332
|
+
}
|
|
333
|
+
.catalog-item {
|
|
334
|
+
padding: 8px 12px;
|
|
335
|
+
background: var(--bg);
|
|
336
|
+
border: 1px solid var(--border);
|
|
337
|
+
border-radius: 6px;
|
|
338
|
+
cursor: pointer;
|
|
339
|
+
font-size: 13px;
|
|
340
|
+
transition: all 0.2s;
|
|
341
|
+
display: flex;
|
|
342
|
+
align-items: center;
|
|
343
|
+
gap: 6px;
|
|
344
|
+
}
|
|
345
|
+
.catalog-item:hover { border-color: var(--accent); }
|
|
346
|
+
.catalog-item.dragging { opacity: 0.5; cursor: grabbing; }
|
|
347
|
+
.catalog-item-name { flex: 1; }
|
|
348
|
+
.catalog-item-eye {
|
|
349
|
+
opacity: 0.3;
|
|
350
|
+
font-size: 11px;
|
|
351
|
+
transition: opacity 0.2s;
|
|
352
|
+
}
|
|
353
|
+
.catalog-item:hover .catalog-item-eye { opacity: 0.8; }
|
|
354
|
+
|
|
355
|
+
/* Lang toggle */
|
|
356
|
+
.lang-toggle {
|
|
357
|
+
display: flex;
|
|
358
|
+
gap: 4px;
|
|
359
|
+
}
|
|
360
|
+
.lang-toggle .lang-btn {
|
|
361
|
+
padding: 4px 10px;
|
|
362
|
+
font-size: 12px;
|
|
363
|
+
}
|
|
364
|
+
.lang-toggle .lang-btn.active {
|
|
365
|
+
background: var(--accent);
|
|
366
|
+
border-color: var(--accent);
|
|
367
|
+
color: white;
|
|
368
|
+
}
|
|
369
|
+
.catalog-item[title]:hover::after {
|
|
370
|
+
content: attr(title);
|
|
371
|
+
position: absolute;
|
|
372
|
+
background: var(--bg-card);
|
|
373
|
+
border: 1px solid var(--border);
|
|
374
|
+
padding: 6px 10px;
|
|
375
|
+
border-radius: 4px;
|
|
376
|
+
font-size: 12px;
|
|
377
|
+
white-space: nowrap;
|
|
378
|
+
z-index: 100;
|
|
379
|
+
margin-top: 30px;
|
|
380
|
+
margin-left: -50px;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/* Sync preview */
|
|
384
|
+
.sync-preview {
|
|
385
|
+
background: var(--bg-card);
|
|
386
|
+
border: 1px solid var(--border);
|
|
387
|
+
border-radius: 8px;
|
|
388
|
+
padding: 16px;
|
|
389
|
+
}
|
|
390
|
+
.sync-preview h3 {
|
|
391
|
+
font-size: 14px;
|
|
392
|
+
margin-bottom: 12px;
|
|
393
|
+
display: flex;
|
|
394
|
+
align-items: center;
|
|
395
|
+
gap: 8px;
|
|
396
|
+
}
|
|
397
|
+
.sync-changes {
|
|
398
|
+
display: grid;
|
|
399
|
+
grid-template-columns: repeat(3, 1fr);
|
|
400
|
+
gap: 12px;
|
|
401
|
+
}
|
|
402
|
+
.sync-group {
|
|
403
|
+
padding: 12px;
|
|
404
|
+
border-radius: 6px;
|
|
405
|
+
background: var(--bg);
|
|
406
|
+
}
|
|
407
|
+
.sync-group.added { border-left: 3px solid var(--green); }
|
|
408
|
+
.sync-group.removed { border-left: 3px solid var(--red); }
|
|
409
|
+
.sync-group.renamed { border-left: 3px solid var(--yellow); }
|
|
410
|
+
.sync-group h4 {
|
|
411
|
+
font-size: 12px;
|
|
412
|
+
margin-bottom: 8px;
|
|
413
|
+
color: var(--muted);
|
|
414
|
+
}
|
|
415
|
+
.sync-group ul {
|
|
416
|
+
list-style: none;
|
|
417
|
+
font-size: 13px;
|
|
418
|
+
}
|
|
419
|
+
.sync-group li { padding: 4px 0; }
|
|
420
|
+
|
|
421
|
+
/* Notification */
|
|
422
|
+
.notification {
|
|
423
|
+
position: fixed;
|
|
424
|
+
bottom: 24px;
|
|
425
|
+
right: 24px;
|
|
426
|
+
background: var(--bg-card);
|
|
427
|
+
border: 1px solid var(--border);
|
|
428
|
+
border-left: 3px solid var(--green);
|
|
429
|
+
padding: 12px 16px;
|
|
430
|
+
border-radius: 6px;
|
|
431
|
+
font-size: 14px;
|
|
432
|
+
opacity: 0;
|
|
433
|
+
transform: translateY(10px);
|
|
434
|
+
transition: all 0.3s;
|
|
435
|
+
z-index: 1000;
|
|
436
|
+
}
|
|
437
|
+
.notification.show { opacity: 1; transform: translateY(0); }
|
|
438
|
+
.notification.error { border-left-color: var(--red); }
|
|
439
|
+
.notification.warning { border-left-color: var(--yellow); }
|
|
440
|
+
|
|
441
|
+
/* Modal */
|
|
442
|
+
.modal-overlay {
|
|
443
|
+
position: fixed;
|
|
444
|
+
inset: 0;
|
|
445
|
+
background: rgba(0,0,0,0.7);
|
|
446
|
+
display: none;
|
|
447
|
+
align-items: center;
|
|
448
|
+
justify-content: center;
|
|
449
|
+
z-index: 1000;
|
|
450
|
+
}
|
|
451
|
+
.modal-overlay.show { display: flex; }
|
|
452
|
+
.modal {
|
|
453
|
+
background: var(--bg-card);
|
|
454
|
+
border: 1px solid var(--border);
|
|
455
|
+
border-radius: 12px;
|
|
456
|
+
padding: 24px;
|
|
457
|
+
min-width: 400px;
|
|
458
|
+
max-width: 500px;
|
|
459
|
+
}
|
|
460
|
+
.modal h3 {
|
|
461
|
+
margin-bottom: 16px;
|
|
462
|
+
font-size: 18px;
|
|
463
|
+
}
|
|
464
|
+
.modal-actions {
|
|
465
|
+
display: flex;
|
|
466
|
+
justify-content: flex-end;
|
|
467
|
+
gap: 8px;
|
|
468
|
+
margin-top: 20px;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/* Empty state */
|
|
472
|
+
.empty-state {
|
|
473
|
+
text-align: center;
|
|
474
|
+
padding: 40px;
|
|
475
|
+
color: var(--muted);
|
|
476
|
+
}
|
|
477
|
+
.empty-state h3 { font-size: 18px; margin-bottom: 8px; color: var(--fg); }
|
|
478
|
+
.empty-state p { margin-bottom: 20px; }
|
|
479
|
+
|
|
480
|
+
/* Lang switcher */
|
|
481
|
+
.lang-switcher {
|
|
482
|
+
display: flex;
|
|
483
|
+
gap: 4px;
|
|
484
|
+
margin-left: auto;
|
|
485
|
+
margin-right: 16px;
|
|
486
|
+
}
|
|
487
|
+
.lang-switcher .lang-btn {
|
|
488
|
+
background: var(--bg);
|
|
489
|
+
border: 1px solid var(--border);
|
|
490
|
+
color: var(--muted);
|
|
491
|
+
padding: 6px 12px;
|
|
492
|
+
border-radius: 4px;
|
|
493
|
+
cursor: pointer;
|
|
494
|
+
font-size: 13px;
|
|
495
|
+
font-weight: 600;
|
|
496
|
+
transition: all 0.2s;
|
|
497
|
+
}
|
|
498
|
+
.lang-switcher .lang-btn:hover:not(.active) {
|
|
499
|
+
border-color: var(--accent);
|
|
500
|
+
color: var(--fg);
|
|
501
|
+
}
|
|
502
|
+
.lang-switcher .lang-btn.active {
|
|
503
|
+
background: var(--accent);
|
|
504
|
+
color: white;
|
|
505
|
+
border-color: var(--accent);
|
|
506
|
+
}
|
|
507
|
+
</style>
|
|
508
|
+
</head>
|
|
509
|
+
<body>
|
|
510
|
+
<header class="header">
|
|
511
|
+
<a href="/" class="back-btn">← Dashboard</a>
|
|
512
|
+
<h1 class="title"><span>Pipeline</span> Configurator</h1>
|
|
513
|
+
<div class="lang-switcher" id="lang-switcher">
|
|
514
|
+
<button class="lang-btn" data-lang="en">EN</button>
|
|
515
|
+
<button class="lang-btn" data-lang="fr">FR</button>
|
|
516
|
+
</div>
|
|
517
|
+
<button class="btn btn-success" id="savePipelineBtn" disabled>Save Pipeline</button>
|
|
518
|
+
<button class="btn btn-primary" id="activateBtn" disabled>Activate & Sync</button>
|
|
519
|
+
</header>
|
|
520
|
+
|
|
521
|
+
<div class="container">
|
|
522
|
+
<!-- Sidebar: Pipeline list -->
|
|
523
|
+
<aside class="sidebar">
|
|
524
|
+
<h2>Pipelines</h2>
|
|
525
|
+
<div class="pipeline-list" id="pipelineList">
|
|
526
|
+
<!-- Populated by JS -->
|
|
527
|
+
</div>
|
|
528
|
+
<button class="new-pipeline-btn" id="newPipelineBtn">+ New Pipeline</button>
|
|
529
|
+
</aside>
|
|
530
|
+
|
|
531
|
+
<!-- Main content -->
|
|
532
|
+
<main class="main" id="mainContent">
|
|
533
|
+
<div class="empty-state" id="emptyState">
|
|
534
|
+
<h3>No Pipeline Selected</h3>
|
|
535
|
+
<p>Select a pipeline from the list or create a new one.</p>
|
|
536
|
+
<button class="btn btn-primary" id="createFirstBtn">Create First Pipeline</button>
|
|
537
|
+
</div>
|
|
538
|
+
|
|
539
|
+
<!-- Pipeline editor (hidden initially) -->
|
|
540
|
+
<div id="pipelineEditor" style="display:none;">
|
|
541
|
+
<!-- Pipeline details -->
|
|
542
|
+
<div class="pipeline-header">
|
|
543
|
+
<div class="pipeline-form">
|
|
544
|
+
<div class="form-group">
|
|
545
|
+
<label>Pipeline Name</label>
|
|
546
|
+
<input type="text" id="pipelineName" placeholder="e.g., Default Pipeline">
|
|
547
|
+
</div>
|
|
548
|
+
<div class="form-group">
|
|
549
|
+
<label>Version</label>
|
|
550
|
+
<input type="text" id="pipelineVersion" placeholder="e.g., 1.0.0" value="1.0.0">
|
|
551
|
+
</div>
|
|
552
|
+
<button class="btn btn-danger" id="deletePipelineBtn">Delete</button>
|
|
553
|
+
</div>
|
|
554
|
+
<div class="form-group" style="margin-top: 12px;">
|
|
555
|
+
<label>Description (optional)</label>
|
|
556
|
+
<input type="text" id="pipelineDescription" placeholder="Short description of this pipeline">
|
|
557
|
+
</div>
|
|
558
|
+
</div>
|
|
559
|
+
|
|
560
|
+
<!-- Segments -->
|
|
561
|
+
<div class="segments-container">
|
|
562
|
+
<div class="segment definition">
|
|
563
|
+
<div class="segment-header">
|
|
564
|
+
<span class="segment-title">Definition</span>
|
|
565
|
+
</div>
|
|
566
|
+
<div class="segment-desc">Qualification and preparation columns</div>
|
|
567
|
+
<div class="segment-columns" id="definitionColumns" data-segment="definition">
|
|
568
|
+
<!-- Columns added here -->
|
|
569
|
+
</div>
|
|
570
|
+
</div>
|
|
571
|
+
|
|
572
|
+
<div class="segment action">
|
|
573
|
+
<div class="segment-header">
|
|
574
|
+
<span class="segment-title">Action</span>
|
|
575
|
+
</div>
|
|
576
|
+
<div class="segment-desc">Implementation and validation columns</div>
|
|
577
|
+
<div class="segment-columns" id="actionColumns" data-segment="action">
|
|
578
|
+
<!-- Columns added here -->
|
|
579
|
+
</div>
|
|
580
|
+
</div>
|
|
581
|
+
|
|
582
|
+
<div class="segment finish">
|
|
583
|
+
<div class="segment-header">
|
|
584
|
+
<span class="segment-title">Finish</span>
|
|
585
|
+
</div>
|
|
586
|
+
<div class="segment-desc">Finalization and deployment columns</div>
|
|
587
|
+
<div class="segment-columns" id="finishColumns" data-segment="finish">
|
|
588
|
+
<!-- Columns added here -->
|
|
589
|
+
</div>
|
|
590
|
+
</div>
|
|
591
|
+
</div>
|
|
592
|
+
|
|
593
|
+
<!-- Catalog -->
|
|
594
|
+
<div class="catalog">
|
|
595
|
+
<h2>
|
|
596
|
+
Column Catalog <span style="font-weight: normal; font-size: 12px;">(drag columns to segments above)</span>
|
|
597
|
+
<button class="btn btn-secondary" style="margin-left: auto; font-size: 12px;" onclick="openAddColumnModal()">+ Add Column</button>
|
|
598
|
+
</h2>
|
|
599
|
+
<div id="catalogContent">
|
|
600
|
+
<!-- Populated by JS -->
|
|
601
|
+
</div>
|
|
602
|
+
</div>
|
|
603
|
+
|
|
604
|
+
<!-- Sync preview -->
|
|
605
|
+
<div class="sync-preview" id="syncPreview" style="display:none;">
|
|
606
|
+
<h3>Sync Preview</h3>
|
|
607
|
+
<div class="sync-changes" id="syncChanges">
|
|
608
|
+
<!-- Populated by JS -->
|
|
609
|
+
</div>
|
|
610
|
+
</div>
|
|
611
|
+
</div>
|
|
612
|
+
</main>
|
|
613
|
+
</div>
|
|
614
|
+
|
|
615
|
+
<!-- New Pipeline Modal -->
|
|
616
|
+
<div class="modal-overlay" id="newPipelineModal">
|
|
617
|
+
<div class="modal">
|
|
618
|
+
<h3>Create New Pipeline</h3>
|
|
619
|
+
<div class="form-group">
|
|
620
|
+
<label>Pipeline Name</label>
|
|
621
|
+
<input type="text" id="newPipelineName" placeholder="e.g., Default Pipeline">
|
|
622
|
+
<small style="color: var(--muted); margin-top: 4px; display: block;">Key: <code id="newPipelineKeyPreview">-</code></small>
|
|
623
|
+
</div>
|
|
624
|
+
<div class="modal-actions">
|
|
625
|
+
<button class="btn btn-secondary" onclick="closeNewPipelineModal()">Cancel</button>
|
|
626
|
+
<button class="btn btn-primary" onclick="confirmNewPipeline()">Create</button>
|
|
627
|
+
</div>
|
|
628
|
+
</div>
|
|
629
|
+
</div>
|
|
630
|
+
|
|
631
|
+
<div class="notification" id="notification"></div>
|
|
632
|
+
|
|
633
|
+
<!-- Add Column Modal -->
|
|
634
|
+
<div class="modal-overlay" id="addColumnModal">
|
|
635
|
+
<div class="modal" style="max-width: 500px;">
|
|
636
|
+
<h3>Add New Column to Catalog</h3>
|
|
637
|
+
<p style="color: var(--muted); font-size: 13px; margin-bottom: 16px;">Describe your idea and Claude will generate the prompt in EN/FR.</p>
|
|
638
|
+
<div class="form-group">
|
|
639
|
+
<label>Column Name</label>
|
|
640
|
+
<input type="text" id="newColumnName" placeholder="e.g., Code Formatting">
|
|
641
|
+
</div>
|
|
642
|
+
<div class="form-group" style="margin-top: 12px;">
|
|
643
|
+
<label>Segment</label>
|
|
644
|
+
<select id="newColumnSegment">
|
|
645
|
+
<option value="definition">Definition (preparation)</option>
|
|
646
|
+
<option value="action" selected>Action (implementation)</option>
|
|
647
|
+
<option value="finish">Finish (deployment)</option>
|
|
648
|
+
</select>
|
|
649
|
+
</div>
|
|
650
|
+
<div class="form-group" style="margin-top: 12px;">
|
|
651
|
+
<label>Describe what this column should do</label>
|
|
652
|
+
<textarea id="newColumnDescription" rows="4" placeholder="e.g., Check code formatting with Prettier, fix any issues, ensure consistent style across the project..."></textarea>
|
|
653
|
+
</div>
|
|
654
|
+
<div class="modal-actions">
|
|
655
|
+
<button class="btn btn-secondary" onclick="closeAddColumnModal()">Cancel</button>
|
|
656
|
+
<button class="btn btn-primary" id="generateColumnBtn" onclick="generateColumn()">
|
|
657
|
+
<span id="generateColumnText">Generate with Claude</span>
|
|
658
|
+
<span id="generateColumnSpinner" style="display:none;">Generating...</span>
|
|
659
|
+
</button>
|
|
660
|
+
</div>
|
|
661
|
+
</div>
|
|
662
|
+
</div>
|
|
663
|
+
|
|
664
|
+
<!-- Prompt Preview Modal -->
|
|
665
|
+
<div class="modal-overlay" id="promptPreviewModal">
|
|
666
|
+
<div class="modal" style="max-width: 700px; width: 90%;">
|
|
667
|
+
<div class="modal-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
|
|
668
|
+
<h3 id="promptPreviewTitle" style="margin: 0;">Column Prompt</h3>
|
|
669
|
+
<div style="display: flex; gap: 8px; align-items: center;">
|
|
670
|
+
<div class="lang-toggle">
|
|
671
|
+
<button class="btn btn-secondary lang-btn active" data-lang="en" onclick="switchPromptLang('en')">EN</button>
|
|
672
|
+
<button class="btn btn-secondary lang-btn" data-lang="fr" onclick="switchPromptLang('fr')">FR</button>
|
|
673
|
+
</div>
|
|
674
|
+
<button class="btn btn-secondary" onclick="closePromptPreview()" style="padding: 4px 10px;">✕</button>
|
|
675
|
+
</div>
|
|
676
|
+
</div>
|
|
677
|
+
<div id="promptPreviewContent" style="background: var(--bg); border: 1px solid var(--border); border-radius: 6px; padding: 16px; max-height: 60vh; overflow-y: auto; white-space: pre-wrap; font-family: monospace; font-size: 13px; line-height: 1.5;"></div>
|
|
678
|
+
</div>
|
|
679
|
+
</div>
|
|
680
|
+
|
|
681
|
+
<script>
|
|
682
|
+
// Translations
|
|
683
|
+
const translations = {
|
|
684
|
+
en: {
|
|
685
|
+
backBtn: '← Dashboard',
|
|
686
|
+
title: 'Configurator',
|
|
687
|
+
titleSpan: 'Pipeline',
|
|
688
|
+
savePipeline: 'Save Pipeline',
|
|
689
|
+
activateSync: 'Activate & Sync',
|
|
690
|
+
pipelines: 'Pipelines',
|
|
691
|
+
newPipeline: '+ New Pipeline',
|
|
692
|
+
noSelected: 'No Pipeline Selected',
|
|
693
|
+
noSelectedDesc: 'Select a pipeline from the list or create a new one.',
|
|
694
|
+
createFirst: 'Create First Pipeline',
|
|
695
|
+
pipelineName: 'Pipeline Name',
|
|
696
|
+
version: 'Version',
|
|
697
|
+
delete: 'Delete',
|
|
698
|
+
description: 'Description (optional)',
|
|
699
|
+
descPlaceholder: 'Short description of this pipeline',
|
|
700
|
+
definition: 'DEFINITION',
|
|
701
|
+
definitionDesc: 'Qualification and preparation columns',
|
|
702
|
+
action: 'ACTION',
|
|
703
|
+
actionDesc: 'Implementation and validation columns',
|
|
704
|
+
finish: 'FINISH',
|
|
705
|
+
finishDesc: 'Finalization and deployment columns',
|
|
706
|
+
catalog: 'Column Catalog',
|
|
707
|
+
catalogHint: '(drag columns to segments above)',
|
|
708
|
+
addColumn: '+ Add Column',
|
|
709
|
+
syncPreview: 'Sync Preview',
|
|
710
|
+
createNewPipeline: 'Create New Pipeline',
|
|
711
|
+
create: 'Create',
|
|
712
|
+
cancel: 'Cancel',
|
|
713
|
+
addColumnTitle: 'Add New Column to Catalog',
|
|
714
|
+
addColumnDesc: 'Describe your idea and Claude will generate the prompt in EN/FR.',
|
|
715
|
+
columnName: 'Column Name',
|
|
716
|
+
segment: 'Segment',
|
|
717
|
+
segmentDefinition: 'Definition (preparation)',
|
|
718
|
+
segmentAction: 'Action (implementation)',
|
|
719
|
+
segmentFinish: 'Finish (deployment)',
|
|
720
|
+
describeColumn: 'Describe what this column should do',
|
|
721
|
+
generateClaude: 'Generate with Claude',
|
|
722
|
+
generating: 'Generating...',
|
|
723
|
+
promptPreview: 'Column Prompt',
|
|
724
|
+
loading: 'Loading...',
|
|
725
|
+
noPrompt: '(No prompt available for this column)',
|
|
726
|
+
columnBelongsTo: 'This column belongs to the',
|
|
727
|
+
segmentLabel: 'segment',
|
|
728
|
+
cannotMoveBetween: 'Columns cannot be moved between segments',
|
|
729
|
+
columnExists: 'Column already exists in this segment',
|
|
730
|
+
pipelineSaved: 'Pipeline saved!',
|
|
731
|
+
pipelineCreated: 'Pipeline created!',
|
|
732
|
+
pipelineDeleted: 'Pipeline deleted',
|
|
733
|
+
pipelineActivated: 'Pipeline activated and synced!',
|
|
734
|
+
fillAllFields: 'Please fill in all fields',
|
|
735
|
+
enterPipelineName: 'Please enter a pipeline name',
|
|
736
|
+
keyExists: 'A pipeline with this key already exists',
|
|
737
|
+
confirmDelete: 'Are you sure you want to delete this pipeline?',
|
|
738
|
+
columnAdded: 'Column added to catalog!',
|
|
739
|
+
},
|
|
740
|
+
fr: {
|
|
741
|
+
backBtn: '← Tableau de bord',
|
|
742
|
+
title: 'Configurateur',
|
|
743
|
+
titleSpan: 'Pipeline',
|
|
744
|
+
savePipeline: 'Sauvegarder',
|
|
745
|
+
activateSync: 'Activer & Sync',
|
|
746
|
+
pipelines: 'Pipelines',
|
|
747
|
+
newPipeline: '+ Nouveau Pipeline',
|
|
748
|
+
noSelected: 'Aucun Pipeline Sélectionné',
|
|
749
|
+
noSelectedDesc: 'Sélectionnez un pipeline dans la liste ou créez-en un nouveau.',
|
|
750
|
+
createFirst: 'Créer le Premier Pipeline',
|
|
751
|
+
pipelineName: 'Nom du Pipeline',
|
|
752
|
+
version: 'Version',
|
|
753
|
+
delete: 'Supprimer',
|
|
754
|
+
description: 'Description (optionnel)',
|
|
755
|
+
descPlaceholder: 'Brève description de ce pipeline',
|
|
756
|
+
definition: 'DÉFINITION',
|
|
757
|
+
definitionDesc: 'Colonnes de qualification et préparation',
|
|
758
|
+
action: 'ACTION',
|
|
759
|
+
actionDesc: "Colonnes d'implémentation et validation",
|
|
760
|
+
finish: 'FINALISATION',
|
|
761
|
+
finishDesc: 'Colonnes de finalisation et déploiement',
|
|
762
|
+
catalog: 'Catalogue de Colonnes',
|
|
763
|
+
catalogHint: '(glissez les colonnes vers les segments ci-dessus)',
|
|
764
|
+
addColumn: '+ Ajouter Colonne',
|
|
765
|
+
syncPreview: 'Aperçu Sync',
|
|
766
|
+
createNewPipeline: 'Créer un Nouveau Pipeline',
|
|
767
|
+
create: 'Créer',
|
|
768
|
+
cancel: 'Annuler',
|
|
769
|
+
addColumnTitle: 'Ajouter une Nouvelle Colonne au Catalogue',
|
|
770
|
+
addColumnDesc: 'Décrivez votre idée et Claude générera le prompt en EN/FR.',
|
|
771
|
+
columnName: 'Nom de la Colonne',
|
|
772
|
+
segment: 'Segment',
|
|
773
|
+
segmentDefinition: 'Définition (préparation)',
|
|
774
|
+
segmentAction: 'Action (implémentation)',
|
|
775
|
+
segmentFinish: 'Finalisation (déploiement)',
|
|
776
|
+
describeColumn: 'Décrivez ce que cette colonne doit faire',
|
|
777
|
+
generateClaude: 'Générer avec Claude',
|
|
778
|
+
generating: 'Génération...',
|
|
779
|
+
promptPreview: 'Prompt de la Colonne',
|
|
780
|
+
loading: 'Chargement...',
|
|
781
|
+
noPrompt: '(Aucun prompt disponible pour cette colonne)',
|
|
782
|
+
columnBelongsTo: 'Cette colonne appartient au segment',
|
|
783
|
+
segmentLabel: '',
|
|
784
|
+
cannotMoveBetween: 'Les colonnes ne peuvent pas être déplacées entre segments',
|
|
785
|
+
columnExists: 'La colonne existe déjà dans ce segment',
|
|
786
|
+
pipelineSaved: 'Pipeline sauvegardé !',
|
|
787
|
+
pipelineCreated: 'Pipeline créé !',
|
|
788
|
+
pipelineDeleted: 'Pipeline supprimé',
|
|
789
|
+
pipelineActivated: 'Pipeline activé et synchronisé !',
|
|
790
|
+
fillAllFields: 'Veuillez remplir tous les champs',
|
|
791
|
+
enterPipelineName: 'Veuillez entrer un nom de pipeline',
|
|
792
|
+
keyExists: 'Un pipeline avec cette clé existe déjà',
|
|
793
|
+
confirmDelete: 'Êtes-vous sûr de vouloir supprimer ce pipeline ?',
|
|
794
|
+
columnAdded: 'Colonne ajoutée au catalogue !',
|
|
795
|
+
}
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
function t(key) {
|
|
799
|
+
return translations[currentLang]?.[key] || translations['en'][key] || key;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
function updateUILanguage() {
|
|
803
|
+
// Header
|
|
804
|
+
document.querySelector('.back-btn').textContent = t('backBtn');
|
|
805
|
+
document.querySelector('.title span').textContent = t('titleSpan');
|
|
806
|
+
document.querySelector('.title').childNodes[2].textContent = ' ' + t('title');
|
|
807
|
+
document.getElementById('savePipelineBtn').textContent = t('savePipeline');
|
|
808
|
+
document.getElementById('activateBtn').textContent = t('activateSync');
|
|
809
|
+
|
|
810
|
+
// Sidebar
|
|
811
|
+
document.querySelector('.sidebar h2').textContent = t('pipelines');
|
|
812
|
+
document.getElementById('newPipelineBtn').textContent = t('newPipeline');
|
|
813
|
+
|
|
814
|
+
// Empty state
|
|
815
|
+
document.querySelector('#emptyState h3').textContent = t('noSelected');
|
|
816
|
+
document.querySelector('#emptyState p').textContent = t('noSelectedDesc');
|
|
817
|
+
document.getElementById('createFirstBtn').textContent = t('createFirst');
|
|
818
|
+
|
|
819
|
+
// Pipeline form
|
|
820
|
+
document.querySelector('label[for="pipelineName"]')?.setAttribute('data-text', t('pipelineName'));
|
|
821
|
+
document.querySelectorAll('.pipeline-header .form-group label')[0].textContent = t('pipelineName');
|
|
822
|
+
document.querySelectorAll('.pipeline-header .form-group label')[1].textContent = t('version');
|
|
823
|
+
document.getElementById('deletePipelineBtn').textContent = t('delete');
|
|
824
|
+
document.querySelectorAll('.pipeline-header .form-group label')[2].textContent = t('description');
|
|
825
|
+
document.getElementById('pipelineDescription').placeholder = t('descPlaceholder');
|
|
826
|
+
|
|
827
|
+
// Segments
|
|
828
|
+
document.querySelectorAll('.segment.definition .segment-title')[0].textContent = t('definition');
|
|
829
|
+
document.querySelectorAll('.segment.definition .segment-desc')[0].textContent = t('definitionDesc');
|
|
830
|
+
document.querySelectorAll('.segment.action .segment-title')[0].textContent = t('action');
|
|
831
|
+
document.querySelectorAll('.segment.action .segment-desc')[0].textContent = t('actionDesc');
|
|
832
|
+
document.querySelectorAll('.segment.finish .segment-title')[0].textContent = t('finish');
|
|
833
|
+
document.querySelectorAll('.segment.finish .segment-desc')[0].textContent = t('finishDesc');
|
|
834
|
+
|
|
835
|
+
// Catalog
|
|
836
|
+
const catalogH2 = document.querySelector('.catalog h2');
|
|
837
|
+
catalogH2.childNodes[0].textContent = t('catalog') + ' ';
|
|
838
|
+
catalogH2.querySelector('span').textContent = t('catalogHint');
|
|
839
|
+
catalogH2.querySelector('button').textContent = t('addColumn');
|
|
840
|
+
|
|
841
|
+
// Sync preview
|
|
842
|
+
document.querySelector('#syncPreview h3').textContent = t('syncPreview');
|
|
843
|
+
|
|
844
|
+
// New pipeline modal
|
|
845
|
+
document.querySelector('#newPipelineModal h3').textContent = t('createNewPipeline');
|
|
846
|
+
document.querySelector('#newPipelineModal label').textContent = t('pipelineName');
|
|
847
|
+
document.querySelectorAll('#newPipelineModal .btn-secondary')[0].textContent = t('cancel');
|
|
848
|
+
document.querySelectorAll('#newPipelineModal .btn-primary')[0].textContent = t('create');
|
|
849
|
+
|
|
850
|
+
// Add column modal
|
|
851
|
+
document.querySelector('#addColumnModal h3').textContent = t('addColumnTitle');
|
|
852
|
+
document.querySelector('#addColumnModal p').textContent = t('addColumnDesc');
|
|
853
|
+
document.querySelectorAll('#addColumnModal label')[0].textContent = t('columnName');
|
|
854
|
+
document.querySelectorAll('#addColumnModal label')[1].textContent = t('segment');
|
|
855
|
+
document.querySelectorAll('#addColumnModal label')[2].textContent = t('describeColumn');
|
|
856
|
+
document.querySelectorAll('#addColumnModal select option')[0].textContent = t('segmentDefinition');
|
|
857
|
+
document.querySelectorAll('#addColumnModal select option')[1].textContent = t('segmentAction');
|
|
858
|
+
document.querySelectorAll('#addColumnModal select option')[2].textContent = t('segmentFinish');
|
|
859
|
+
document.querySelectorAll('#addColumnModal .btn-secondary')[0].textContent = t('cancel');
|
|
860
|
+
document.getElementById('generateColumnText').textContent = t('generateClaude');
|
|
861
|
+
document.getElementById('generateColumnSpinner').textContent = t('generating');
|
|
862
|
+
|
|
863
|
+
// Prompt preview modal
|
|
864
|
+
document.getElementById('promptPreviewTitle').textContent = t('promptPreview');
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// State
|
|
868
|
+
let catalog = null;
|
|
869
|
+
let pipelines = [];
|
|
870
|
+
let currentPipelineKey = null;
|
|
871
|
+
let currentPipeline = null;
|
|
872
|
+
let hasChanges = false;
|
|
873
|
+
let currentPromptSlug = null;
|
|
874
|
+
let currentPromptLang = 'en';
|
|
875
|
+
let currentLang = localStorage.getItem('autocode-lang') || 'en';
|
|
876
|
+
|
|
877
|
+
// Elements
|
|
878
|
+
const pipelineList = document.getElementById('pipelineList');
|
|
879
|
+
const mainContent = document.getElementById('mainContent');
|
|
880
|
+
const emptyState = document.getElementById('emptyState');
|
|
881
|
+
const pipelineEditor = document.getElementById('pipelineEditor');
|
|
882
|
+
const savePipelineBtn = document.getElementById('savePipelineBtn');
|
|
883
|
+
const activateBtn = document.getElementById('activateBtn');
|
|
884
|
+
const notification = document.getElementById('notification');
|
|
885
|
+
|
|
886
|
+
// Load initial data
|
|
887
|
+
async function init() {
|
|
888
|
+
await Promise.all([loadCatalog(), loadPipelines()]);
|
|
889
|
+
renderCatalog();
|
|
890
|
+
renderPipelineList();
|
|
891
|
+
setupDragAndDrop();
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// Load catalog
|
|
895
|
+
async function loadCatalog() {
|
|
896
|
+
try {
|
|
897
|
+
const res = await fetch('/api/catalog');
|
|
898
|
+
const data = await res.json();
|
|
899
|
+
if (data.success) {
|
|
900
|
+
catalog = data.data;
|
|
901
|
+
}
|
|
902
|
+
} catch (e) {
|
|
903
|
+
showNotification('Failed to load catalog: ' + e.message, 'error');
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// Load pipelines
|
|
908
|
+
async function loadPipelines() {
|
|
909
|
+
try {
|
|
910
|
+
const res = await fetch('/api/pipelines');
|
|
911
|
+
const data = await res.json();
|
|
912
|
+
if (data.success) {
|
|
913
|
+
pipelines = data.data;
|
|
914
|
+
}
|
|
915
|
+
} catch (e) {
|
|
916
|
+
showNotification('Failed to load pipelines: ' + e.message, 'error');
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// Render catalog
|
|
921
|
+
function renderCatalog() {
|
|
922
|
+
if (!catalog) return;
|
|
923
|
+
const container = document.getElementById('catalogContent');
|
|
924
|
+
container.innerHTML = '<div class="catalog-grid" id="catalogGrid"></div>';
|
|
925
|
+
const grid = document.getElementById('catalogGrid');
|
|
926
|
+
|
|
927
|
+
for (const [segment, data] of Object.entries(catalog.segments)) {
|
|
928
|
+
const segmentDiv = document.createElement('div');
|
|
929
|
+
segmentDiv.className = 'catalog-segment ' + segment;
|
|
930
|
+
|
|
931
|
+
// Header with arrow and title
|
|
932
|
+
const headerDiv = document.createElement('div');
|
|
933
|
+
headerDiv.className = 'catalog-segment-header';
|
|
934
|
+
headerDiv.innerHTML = '<span class="catalog-segment-arrow">↑</span><span class="catalog-segment-title">' + segment.toUpperCase() + '</span>';
|
|
935
|
+
segmentDiv.appendChild(headerDiv);
|
|
936
|
+
|
|
937
|
+
const columnsDiv = document.createElement('div');
|
|
938
|
+
columnsDiv.className = 'catalog-columns';
|
|
939
|
+
|
|
940
|
+
for (const col of data.columns) {
|
|
941
|
+
const item = document.createElement('div');
|
|
942
|
+
item.className = 'catalog-item';
|
|
943
|
+
item.draggable = true;
|
|
944
|
+
item.dataset.slug = col.slug;
|
|
945
|
+
item.dataset.name = col.name;
|
|
946
|
+
item.dataset.segment = segment;
|
|
947
|
+
item.title = col.description + ' (click to preview prompt)';
|
|
948
|
+
item.innerHTML = '<span class="catalog-item-name">' + escapeHtml(col.name) + '</span><span class="catalog-item-eye">👁</span>';
|
|
949
|
+
item.onclick = (e) => {
|
|
950
|
+
if (!e.target.classList.contains('dragging')) {
|
|
951
|
+
openPromptPreview(col.slug, col.name);
|
|
952
|
+
}
|
|
953
|
+
};
|
|
954
|
+
columnsDiv.appendChild(item);
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
segmentDiv.appendChild(columnsDiv);
|
|
958
|
+
grid.appendChild(segmentDiv);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// Render pipeline list
|
|
963
|
+
function renderPipelineList() {
|
|
964
|
+
pipelineList.innerHTML = '';
|
|
965
|
+
for (const p of pipelines) {
|
|
966
|
+
const item = document.createElement('div');
|
|
967
|
+
item.className = 'pipeline-item' + (p.name === currentPipelineKey ? ' active' : '');
|
|
968
|
+
item.innerHTML = '<div class="name">' + escapeHtml(p.pipeline.name) +
|
|
969
|
+
(p.isActive ? '<span class="badge">Active</span>' : '') +
|
|
970
|
+
'</div><div class="meta">v' + escapeHtml(p.pipeline.version) + '</div>';
|
|
971
|
+
item.onclick = () => selectPipeline(p.name);
|
|
972
|
+
pipelineList.appendChild(item);
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
if (pipelines.length === 0) {
|
|
976
|
+
emptyState.style.display = '';
|
|
977
|
+
pipelineEditor.style.display = 'none';
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// Select a pipeline
|
|
982
|
+
function selectPipeline(key) {
|
|
983
|
+
const p = pipelines.find(p => p.name === key);
|
|
984
|
+
if (!p) return;
|
|
985
|
+
|
|
986
|
+
currentPipelineKey = key;
|
|
987
|
+
currentPipeline = JSON.parse(JSON.stringify(p.pipeline)); // Deep copy
|
|
988
|
+
|
|
989
|
+
emptyState.style.display = 'none';
|
|
990
|
+
pipelineEditor.style.display = '';
|
|
991
|
+
|
|
992
|
+
document.getElementById('pipelineName').value = currentPipeline.name;
|
|
993
|
+
document.getElementById('pipelineVersion').value = currentPipeline.version;
|
|
994
|
+
document.getElementById('pipelineDescription').value = currentPipeline.description || '';
|
|
995
|
+
|
|
996
|
+
renderSegmentColumns();
|
|
997
|
+
renderPipelineList();
|
|
998
|
+
|
|
999
|
+
hasChanges = false;
|
|
1000
|
+
updateButtons();
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// Render segment columns
|
|
1004
|
+
function renderSegmentColumns() {
|
|
1005
|
+
['definition', 'action', 'finish'].forEach(segment => {
|
|
1006
|
+
const container = document.getElementById(segment + 'Columns');
|
|
1007
|
+
container.innerHTML = '';
|
|
1008
|
+
|
|
1009
|
+
const columns = currentPipeline[segment] || [];
|
|
1010
|
+
columns.forEach((col, index) => {
|
|
1011
|
+
const item = createColumnItem(col, segment, index);
|
|
1012
|
+
container.appendChild(item);
|
|
1013
|
+
});
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
// Create a column item element
|
|
1018
|
+
function createColumnItem(col, segment, index) {
|
|
1019
|
+
const item = document.createElement('div');
|
|
1020
|
+
item.className = 'column-item';
|
|
1021
|
+
item.draggable = true;
|
|
1022
|
+
item.dataset.segment = segment;
|
|
1023
|
+
item.dataset.index = index;
|
|
1024
|
+
item.innerHTML =
|
|
1025
|
+
'<span class="drag-handle">⋮⋮</span>' +
|
|
1026
|
+
'<span class="name">' + escapeHtml(col.name) + '</span>' +
|
|
1027
|
+
'<button class="remove-btn" title="Remove">✕</button>';
|
|
1028
|
+
|
|
1029
|
+
item.querySelector('.remove-btn').onclick = (e) => {
|
|
1030
|
+
e.stopPropagation();
|
|
1031
|
+
removeColumn(segment, index);
|
|
1032
|
+
};
|
|
1033
|
+
|
|
1034
|
+
return item;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// Add column to segment
|
|
1038
|
+
function addColumn(segment, slug, name) {
|
|
1039
|
+
if (!currentPipeline) return;
|
|
1040
|
+
if (!currentPipeline[segment]) currentPipeline[segment] = [];
|
|
1041
|
+
|
|
1042
|
+
// Check if already exists
|
|
1043
|
+
if (currentPipeline[segment].some(c => c.slug.includes(slug))) {
|
|
1044
|
+
showNotification('Column already exists in this segment', 'warning');
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
currentPipeline[segment].push({ slug, name });
|
|
1049
|
+
renderSegmentColumns();
|
|
1050
|
+
hasChanges = true;
|
|
1051
|
+
updateButtons();
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// Remove column from segment
|
|
1055
|
+
function removeColumn(segment, index) {
|
|
1056
|
+
if (!currentPipeline || !currentPipeline[segment]) return;
|
|
1057
|
+
currentPipeline[segment].splice(index, 1);
|
|
1058
|
+
renderSegmentColumns();
|
|
1059
|
+
hasChanges = true;
|
|
1060
|
+
updateButtons();
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// Setup drag and drop
|
|
1064
|
+
function setupDragAndDrop() {
|
|
1065
|
+
document.addEventListener('dragstart', (e) => {
|
|
1066
|
+
if (e.target.classList.contains('catalog-item') || e.target.classList.contains('column-item')) {
|
|
1067
|
+
e.target.classList.add('dragging');
|
|
1068
|
+
window.currentDragSegment = e.target.dataset.segment;
|
|
1069
|
+
e.dataTransfer.setData('text/plain', JSON.stringify({
|
|
1070
|
+
slug: e.target.dataset.slug,
|
|
1071
|
+
name: e.target.dataset.name,
|
|
1072
|
+
segment: e.target.dataset.segment,
|
|
1073
|
+
index: e.target.dataset.index
|
|
1074
|
+
}));
|
|
1075
|
+
}
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
document.addEventListener('dragend', (e) => {
|
|
1079
|
+
e.target.classList.remove('dragging');
|
|
1080
|
+
window.currentDragSegment = null;
|
|
1081
|
+
document.querySelectorAll('.dragover').forEach(el => el.classList.remove('dragover'));
|
|
1082
|
+
document.querySelectorAll('.dragover-invalid').forEach(el => el.classList.remove('dragover-invalid'));
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
document.querySelectorAll('.segment-columns').forEach(container => {
|
|
1086
|
+
container.addEventListener('dragover', (e) => {
|
|
1087
|
+
e.preventDefault();
|
|
1088
|
+
// Only show dragover if segment is compatible
|
|
1089
|
+
const draggedSegment = window.currentDragSegment;
|
|
1090
|
+
const targetSegment = container.dataset.segment;
|
|
1091
|
+
if (!draggedSegment || draggedSegment === targetSegment) {
|
|
1092
|
+
container.classList.add('dragover');
|
|
1093
|
+
} else {
|
|
1094
|
+
container.classList.add('dragover-invalid');
|
|
1095
|
+
}
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1098
|
+
container.addEventListener('dragleave', () => {
|
|
1099
|
+
container.classList.remove('dragover');
|
|
1100
|
+
container.classList.remove('dragover-invalid');
|
|
1101
|
+
});
|
|
1102
|
+
|
|
1103
|
+
container.addEventListener('drop', (e) => {
|
|
1104
|
+
e.preventDefault();
|
|
1105
|
+
container.classList.remove('dragover');
|
|
1106
|
+
container.classList.remove('dragover-invalid');
|
|
1107
|
+
|
|
1108
|
+
const data = JSON.parse(e.dataTransfer.getData('text/plain'));
|
|
1109
|
+
const targetSegment = container.dataset.segment;
|
|
1110
|
+
|
|
1111
|
+
// Check if it's from catalog or from another segment
|
|
1112
|
+
if (data.index === undefined) {
|
|
1113
|
+
// From catalog - check segment compatibility
|
|
1114
|
+
if (data.segment !== targetSegment) {
|
|
1115
|
+
showNotification('This column belongs to the "' + data.segment + '" segment', 'warning');
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
addColumn(targetSegment, data.slug, data.name);
|
|
1119
|
+
} else {
|
|
1120
|
+
// Reordering within same segment only
|
|
1121
|
+
const fromSegment = data.segment;
|
|
1122
|
+
const fromIndex = parseInt(data.index);
|
|
1123
|
+
|
|
1124
|
+
if (fromSegment !== targetSegment) {
|
|
1125
|
+
showNotification('Columns cannot be moved between segments', 'warning');
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
// Reorder within same segment
|
|
1130
|
+
const items = currentPipeline[fromSegment];
|
|
1131
|
+
const [removed] = items.splice(fromIndex, 1);
|
|
1132
|
+
// Find drop position
|
|
1133
|
+
const dropIndex = getDropIndex(e, container);
|
|
1134
|
+
items.splice(dropIndex, 0, removed);
|
|
1135
|
+
|
|
1136
|
+
renderSegmentColumns();
|
|
1137
|
+
hasChanges = true;
|
|
1138
|
+
updateButtons();
|
|
1139
|
+
}
|
|
1140
|
+
});
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
function getDropIndex(e, container) {
|
|
1145
|
+
const items = [...container.querySelectorAll('.column-item:not(.dragging)')];
|
|
1146
|
+
const y = e.clientY;
|
|
1147
|
+
let insertIndex = items.length;
|
|
1148
|
+
|
|
1149
|
+
for (let i = 0; i < items.length; i++) {
|
|
1150
|
+
const rect = items[i].getBoundingClientRect();
|
|
1151
|
+
if (y < rect.top + rect.height / 2) {
|
|
1152
|
+
insertIndex = i;
|
|
1153
|
+
break;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
return insertIndex;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
// Update buttons state
|
|
1161
|
+
function updateButtons() {
|
|
1162
|
+
savePipelineBtn.disabled = !hasChanges || !currentPipeline;
|
|
1163
|
+
activateBtn.disabled = !currentPipeline || hasChanges;
|
|
1164
|
+
|
|
1165
|
+
// Validate pipeline
|
|
1166
|
+
if (currentPipeline) {
|
|
1167
|
+
const valid = validatePipeline();
|
|
1168
|
+
savePipelineBtn.disabled = savePipelineBtn.disabled || !valid;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
// Validate pipeline
|
|
1173
|
+
function validatePipeline() {
|
|
1174
|
+
if (!currentPipeline) return false;
|
|
1175
|
+
if (!currentPipeline.definition || currentPipeline.definition.length === 0) return false;
|
|
1176
|
+
if (!currentPipeline.action || currentPipeline.action.length === 0) return false;
|
|
1177
|
+
if (!currentPipeline.finish || currentPipeline.finish.length === 0) return false;
|
|
1178
|
+
return true;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// Save pipeline
|
|
1182
|
+
async function savePipeline() {
|
|
1183
|
+
if (!currentPipelineKey || !currentPipeline) return;
|
|
1184
|
+
|
|
1185
|
+
// Update from form
|
|
1186
|
+
currentPipeline.name = document.getElementById('pipelineName').value;
|
|
1187
|
+
currentPipeline.version = document.getElementById('pipelineVersion').value;
|
|
1188
|
+
currentPipeline.description = document.getElementById('pipelineDescription').value;
|
|
1189
|
+
|
|
1190
|
+
// Renumber columns
|
|
1191
|
+
let index = 0;
|
|
1192
|
+
['definition', 'action', 'finish'].forEach(segment => {
|
|
1193
|
+
currentPipeline[segment] = currentPipeline[segment].map(col => ({
|
|
1194
|
+
slug: String(index++).padStart(2, '0') + '_' + col.slug.replace(/^\\d{2}_/, ''),
|
|
1195
|
+
name: col.name
|
|
1196
|
+
}));
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
try {
|
|
1200
|
+
const res = await fetch('/api/pipelines', {
|
|
1201
|
+
method: 'POST',
|
|
1202
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1203
|
+
body: JSON.stringify({
|
|
1204
|
+
name: currentPipelineKey,
|
|
1205
|
+
pipeline: currentPipeline,
|
|
1206
|
+
setActive: false
|
|
1207
|
+
})
|
|
1208
|
+
});
|
|
1209
|
+
const data = await res.json();
|
|
1210
|
+
if (data.success) {
|
|
1211
|
+
showNotification('Pipeline saved!');
|
|
1212
|
+
hasChanges = false;
|
|
1213
|
+
await loadPipelines();
|
|
1214
|
+
renderPipelineList();
|
|
1215
|
+
updateButtons();
|
|
1216
|
+
} else {
|
|
1217
|
+
showNotification('Error: ' + data.error, 'error');
|
|
1218
|
+
}
|
|
1219
|
+
} catch (e) {
|
|
1220
|
+
showNotification('Error: ' + e.message, 'error');
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
// Activate pipeline
|
|
1225
|
+
async function activatePipeline() {
|
|
1226
|
+
if (!currentPipelineKey) return;
|
|
1227
|
+
|
|
1228
|
+
try {
|
|
1229
|
+
const res = await fetch('/api/pipelines/' + encodeURIComponent(currentPipelineKey) + '/activate', {
|
|
1230
|
+
method: 'PUT'
|
|
1231
|
+
});
|
|
1232
|
+
const data = await res.json();
|
|
1233
|
+
if (data.success) {
|
|
1234
|
+
showNotification('Pipeline activated and synced!');
|
|
1235
|
+
await loadPipelines();
|
|
1236
|
+
renderPipelineList();
|
|
1237
|
+
|
|
1238
|
+
// Show sync results if any
|
|
1239
|
+
if (data.data && (data.data.added.length || data.data.removed.length || data.data.transitioned.length)) {
|
|
1240
|
+
showSyncResults(data.data);
|
|
1241
|
+
}
|
|
1242
|
+
} else {
|
|
1243
|
+
showNotification('Error: ' + data.error, 'error');
|
|
1244
|
+
}
|
|
1245
|
+
} catch (e) {
|
|
1246
|
+
showNotification('Error: ' + e.message, 'error');
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
// Show sync results
|
|
1251
|
+
function showSyncResults(result) {
|
|
1252
|
+
const preview = document.getElementById('syncPreview');
|
|
1253
|
+
const changes = document.getElementById('syncChanges');
|
|
1254
|
+
|
|
1255
|
+
changes.innerHTML = '';
|
|
1256
|
+
|
|
1257
|
+
if (result.added.length) {
|
|
1258
|
+
changes.innerHTML += '<div class="sync-group added"><h4>Added</h4><ul>' +
|
|
1259
|
+
result.added.map(s => '<li>+ ' + escapeHtml(s) + '</li>').join('') + '</ul></div>';
|
|
1260
|
+
}
|
|
1261
|
+
if (result.removed.length) {
|
|
1262
|
+
changes.innerHTML += '<div class="sync-group removed"><h4>Removed</h4><ul>' +
|
|
1263
|
+
result.removed.map(s => '<li>- ' + escapeHtml(s) + '</li>').join('') + '</ul></div>';
|
|
1264
|
+
}
|
|
1265
|
+
if (result.transitioned.length) {
|
|
1266
|
+
changes.innerHTML += '<div class="sync-group renamed"><h4>Tickets in Transition</h4><ul>' +
|
|
1267
|
+
result.transitioned.map(s => '<li>' + escapeHtml(s) + '</li>').join('') + '</ul></div>';
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
preview.style.display = changes.innerHTML ? '' : 'none';
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
// Delete pipeline
|
|
1274
|
+
async function deletePipeline() {
|
|
1275
|
+
if (!currentPipelineKey) return;
|
|
1276
|
+
if (!confirm('Are you sure you want to delete this pipeline?')) return;
|
|
1277
|
+
|
|
1278
|
+
try {
|
|
1279
|
+
const res = await fetch('/api/pipelines/' + encodeURIComponent(currentPipelineKey), {
|
|
1280
|
+
method: 'DELETE'
|
|
1281
|
+
});
|
|
1282
|
+
const data = await res.json();
|
|
1283
|
+
if (data.success) {
|
|
1284
|
+
showNotification('Pipeline deleted');
|
|
1285
|
+
currentPipelineKey = null;
|
|
1286
|
+
currentPipeline = null;
|
|
1287
|
+
await loadPipelines();
|
|
1288
|
+
renderPipelineList();
|
|
1289
|
+
emptyState.style.display = '';
|
|
1290
|
+
pipelineEditor.style.display = 'none';
|
|
1291
|
+
} else {
|
|
1292
|
+
showNotification('Error: ' + data.error, 'error');
|
|
1293
|
+
}
|
|
1294
|
+
} catch (e) {
|
|
1295
|
+
showNotification('Error: ' + e.message, 'error');
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
// Convert to kebab-case
|
|
1300
|
+
function toKebabCase(str) {
|
|
1301
|
+
return str
|
|
1302
|
+
.toLowerCase()
|
|
1303
|
+
.normalize('NFD').replace(/[\u0300-\u036f]/g, '') // Remove accents
|
|
1304
|
+
.replace(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric with dashes
|
|
1305
|
+
.replace(/^-+|-+$/g, '') // Trim dashes
|
|
1306
|
+
.replace(/-+/g, '-'); // Collapse multiple dashes
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
// New pipeline modal
|
|
1310
|
+
function openNewPipelineModal() {
|
|
1311
|
+
document.getElementById('newPipelineModal').classList.add('show');
|
|
1312
|
+
document.getElementById('newPipelineName').value = '';
|
|
1313
|
+
document.getElementById('newPipelineKeyPreview').textContent = '-';
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
function closeNewPipelineModal() {
|
|
1317
|
+
document.getElementById('newPipelineModal').classList.remove('show');
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
async function confirmNewPipeline() {
|
|
1321
|
+
const name = document.getElementById('newPipelineName').value.trim();
|
|
1322
|
+
const key = toKebabCase(name);
|
|
1323
|
+
|
|
1324
|
+
if (!name || !key) {
|
|
1325
|
+
showNotification('Please enter a pipeline name', 'warning');
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
// Check if key already exists
|
|
1330
|
+
if (pipelines.some(p => p.name === key)) {
|
|
1331
|
+
showNotification('A pipeline with this key already exists', 'warning');
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
// Create empty pipeline
|
|
1336
|
+
const newPipeline = {
|
|
1337
|
+
name: name,
|
|
1338
|
+
version: '1.0.0',
|
|
1339
|
+
description: '',
|
|
1340
|
+
definition: [{ slug: 'backlog', name: 'Backlog' }],
|
|
1341
|
+
action: [{ slug: 'in-progress', name: 'In Progress' }],
|
|
1342
|
+
finish: [{ slug: 'done', name: 'Done' }]
|
|
1343
|
+
};
|
|
1344
|
+
|
|
1345
|
+
try {
|
|
1346
|
+
const res = await fetch('/api/pipelines', {
|
|
1347
|
+
method: 'POST',
|
|
1348
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1349
|
+
body: JSON.stringify({
|
|
1350
|
+
name: key,
|
|
1351
|
+
pipeline: newPipeline,
|
|
1352
|
+
setActive: pipelines.length === 0
|
|
1353
|
+
})
|
|
1354
|
+
});
|
|
1355
|
+
const data = await res.json();
|
|
1356
|
+
if (data.success) {
|
|
1357
|
+
closeNewPipelineModal();
|
|
1358
|
+
await loadPipelines();
|
|
1359
|
+
renderPipelineList();
|
|
1360
|
+
selectPipeline(key);
|
|
1361
|
+
showNotification('Pipeline created!');
|
|
1362
|
+
} else {
|
|
1363
|
+
showNotification('Error: ' + data.error, 'error');
|
|
1364
|
+
}
|
|
1365
|
+
} catch (e) {
|
|
1366
|
+
showNotification('Error: ' + e.message, 'error');
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
// Show notification
|
|
1371
|
+
function showNotification(msg, type = 'success') {
|
|
1372
|
+
notification.textContent = msg;
|
|
1373
|
+
notification.className = 'notification show' + (type !== 'success' ? ' ' + type : '');
|
|
1374
|
+
setTimeout(() => notification.className = 'notification', 3000);
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
// Prompt preview
|
|
1378
|
+
async function openPromptPreview(slug, name) {
|
|
1379
|
+
currentPromptSlug = slug;
|
|
1380
|
+
currentPromptLang = currentLang || 'en';
|
|
1381
|
+
document.getElementById('promptPreviewTitle').textContent = name;
|
|
1382
|
+
document.getElementById('promptPreviewModal').classList.add('show');
|
|
1383
|
+
updatePromptLangButtons();
|
|
1384
|
+
await loadPromptContent();
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
function closePromptPreview() {
|
|
1388
|
+
document.getElementById('promptPreviewModal').classList.remove('show');
|
|
1389
|
+
currentPromptSlug = null;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
async function switchPromptLang(lang) {
|
|
1393
|
+
currentPromptLang = lang;
|
|
1394
|
+
updatePromptLangButtons();
|
|
1395
|
+
await loadPromptContent();
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
function updatePromptLangButtons() {
|
|
1399
|
+
document.querySelectorAll('#promptPreviewModal .lang-btn').forEach(btn => {
|
|
1400
|
+
btn.classList.toggle('active', btn.dataset.lang === currentPromptLang);
|
|
1401
|
+
});
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
async function loadPromptContent() {
|
|
1405
|
+
const content = document.getElementById('promptPreviewContent');
|
|
1406
|
+
content.textContent = 'Loading...';
|
|
1407
|
+
|
|
1408
|
+
try {
|
|
1409
|
+
const res = await fetch('/api/prompts/' + encodeURIComponent(currentPromptSlug) + '?lang=' + currentPromptLang);
|
|
1410
|
+
const data = await res.json();
|
|
1411
|
+
if (data.success && data.data) {
|
|
1412
|
+
content.textContent = data.data;
|
|
1413
|
+
} else {
|
|
1414
|
+
content.textContent = '(No prompt available for this column in ' + currentPromptLang.toUpperCase() + ')';
|
|
1415
|
+
}
|
|
1416
|
+
} catch (e) {
|
|
1417
|
+
content.textContent = 'Error loading prompt: ' + e.message;
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
// Add Column Modal
|
|
1422
|
+
function openAddColumnModal() {
|
|
1423
|
+
document.getElementById('addColumnModal').classList.add('show');
|
|
1424
|
+
document.getElementById('newColumnName').value = '';
|
|
1425
|
+
document.getElementById('newColumnDescription').value = '';
|
|
1426
|
+
document.getElementById('newColumnSegment').value = 'action';
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
function closeAddColumnModal() {
|
|
1430
|
+
document.getElementById('addColumnModal').classList.remove('show');
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
async function generateColumn() {
|
|
1434
|
+
const name = document.getElementById('newColumnName').value.trim();
|
|
1435
|
+
const segment = document.getElementById('newColumnSegment').value;
|
|
1436
|
+
const description = document.getElementById('newColumnDescription').value.trim();
|
|
1437
|
+
|
|
1438
|
+
if (!name || !description) {
|
|
1439
|
+
showNotification('Please fill in all fields', 'warning');
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
// Show loading state
|
|
1444
|
+
document.getElementById('generateColumnText').style.display = 'none';
|
|
1445
|
+
document.getElementById('generateColumnSpinner').style.display = 'inline';
|
|
1446
|
+
document.getElementById('generateColumnBtn').disabled = true;
|
|
1447
|
+
|
|
1448
|
+
try {
|
|
1449
|
+
const res = await fetch('/api/catalog/generate', {
|
|
1450
|
+
method: 'POST',
|
|
1451
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1452
|
+
body: JSON.stringify({ name, segment, description })
|
|
1453
|
+
});
|
|
1454
|
+
const data = await res.json();
|
|
1455
|
+
|
|
1456
|
+
if (data.success) {
|
|
1457
|
+
showNotification('Column "' + name + '" added to catalog!');
|
|
1458
|
+
closeAddColumnModal();
|
|
1459
|
+
// Reload catalog
|
|
1460
|
+
await loadCatalog();
|
|
1461
|
+
renderCatalog();
|
|
1462
|
+
} else {
|
|
1463
|
+
showNotification('Error: ' + data.error, 'error');
|
|
1464
|
+
}
|
|
1465
|
+
} catch (e) {
|
|
1466
|
+
showNotification('Error: ' + e.message, 'error');
|
|
1467
|
+
} finally {
|
|
1468
|
+
document.getElementById('generateColumnText').style.display = 'inline';
|
|
1469
|
+
document.getElementById('generateColumnSpinner').style.display = 'none';
|
|
1470
|
+
document.getElementById('generateColumnBtn').disabled = false;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
// Escape HTML
|
|
1475
|
+
function escapeHtml(str) {
|
|
1476
|
+
const div = document.createElement('div');
|
|
1477
|
+
div.textContent = str || '';
|
|
1478
|
+
return div.innerHTML;
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
// Event listeners
|
|
1482
|
+
document.getElementById('newPipelineBtn').onclick = openNewPipelineModal;
|
|
1483
|
+
document.getElementById('createFirstBtn').onclick = openNewPipelineModal;
|
|
1484
|
+
document.getElementById('savePipelineBtn').onclick = savePipeline;
|
|
1485
|
+
document.getElementById('activateBtn').onclick = activatePipeline;
|
|
1486
|
+
document.getElementById('deletePipelineBtn').onclick = deletePipeline;
|
|
1487
|
+
|
|
1488
|
+
// Track form changes
|
|
1489
|
+
['pipelineName', 'pipelineVersion', 'pipelineDescription'].forEach(id => {
|
|
1490
|
+
document.getElementById(id).addEventListener('input', () => {
|
|
1491
|
+
hasChanges = true;
|
|
1492
|
+
updateButtons();
|
|
1493
|
+
});
|
|
1494
|
+
});
|
|
1495
|
+
|
|
1496
|
+
// Live update key preview in new pipeline modal
|
|
1497
|
+
document.getElementById('newPipelineName').addEventListener('input', (e) => {
|
|
1498
|
+
const name = e.target.value.trim();
|
|
1499
|
+
const key = toKebabCase(name);
|
|
1500
|
+
document.getElementById('newPipelineKeyPreview').textContent = key || '-';
|
|
1501
|
+
});
|
|
1502
|
+
|
|
1503
|
+
// Language switcher
|
|
1504
|
+
function initLangSwitcher() {
|
|
1505
|
+
const buttons = document.querySelectorAll('#lang-switcher .lang-btn');
|
|
1506
|
+
buttons.forEach(btn => {
|
|
1507
|
+
if (btn.dataset.lang === currentLang) {
|
|
1508
|
+
btn.classList.add('active');
|
|
1509
|
+
}
|
|
1510
|
+
btn.addEventListener('click', () => {
|
|
1511
|
+
currentLang = btn.dataset.lang;
|
|
1512
|
+
localStorage.setItem('autocode-lang', currentLang);
|
|
1513
|
+
buttons.forEach(b => b.classList.remove('active'));
|
|
1514
|
+
btn.classList.add('active');
|
|
1515
|
+
// Update default prompt language
|
|
1516
|
+
currentPromptLang = currentLang;
|
|
1517
|
+
// Update all UI text
|
|
1518
|
+
updateUILanguage();
|
|
1519
|
+
});
|
|
1520
|
+
});
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
// Initialize
|
|
1524
|
+
init();
|
|
1525
|
+
initLangSwitcher();
|
|
1526
|
+
updateUILanguage();
|
|
1527
|
+
</script>
|
|
1528
|
+
</body>
|
|
1529
|
+
</html>`;
|
|
1530
|
+
}
|
|
1531
|
+
//# sourceMappingURL=pipeline-configurator.js.map
|