@cat-factory/app 0.47.9 → 0.47.11
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/app/components/brainstorm/BrainstormWindow.vue +96 -59
- package/app/components/clarity/ClarityReviewWindow.vue +84 -59
- package/app/components/consensus/ConsensusSessionWindow.vue +72 -34
- package/app/components/focus/BlockFocusView.vue +14 -10
- package/app/components/followUp/FollowUpWindow.vue +51 -26
- package/app/components/github/AddServiceFromRepoModal.vue +76 -23
- package/app/components/humanTest/HumanTestWindow.vue +70 -35
- package/app/components/requirements/RequirementsReviewWindow.vue +130 -82
- package/app/components/spec/ServiceSpecWindow.vue +54 -28
- package/app/components/testing/TestReportWindow.vue +82 -39
- package/app/components/visualConfirm/VisualConfirmationWindow.vue +49 -32
- package/i18n/locales/en.json +587 -3
- package/i18n/locales/es.json +551 -3
- package/i18n/locales/fr.json +551 -3
- package/i18n/locales/pl.json +551 -3
- package/i18n/locales/uk.json +551 -3
- package/package.json +1 -1
|
@@ -5,12 +5,20 @@
|
|
|
5
5
|
// stream), surfaces the ephemeral environment URL, and drives the human actions: confirm
|
|
6
6
|
// (pass + tear down + advance), request a fix from findings (the Tester's fixer), pull latest
|
|
7
7
|
// main into the branch + redeploy (conflict → conflict-resolver), recreate, or destroy the env.
|
|
8
|
-
import type {
|
|
8
|
+
import type {
|
|
9
|
+
HumanTestEnvironmentStatus,
|
|
10
|
+
HumanTestRound,
|
|
11
|
+
HumanTestStepState,
|
|
12
|
+
} from '~/types/execution'
|
|
13
|
+
|
|
14
|
+
// The round outcome union (contracts state it inline, so derive it off the round type).
|
|
15
|
+
type HumanTestRoundOutcome = NonNullable<HumanTestRound['outcome']>
|
|
9
16
|
import StepRunMeta from '~/components/panels/StepRunMeta.vue'
|
|
10
17
|
|
|
11
18
|
const board = useBoardStore()
|
|
12
19
|
const execution = useExecutionStore()
|
|
13
20
|
const humanTest = useHumanTestStore()
|
|
21
|
+
const { t, d } = useI18n()
|
|
14
22
|
|
|
15
23
|
// Shared seam contract (open/blockId/close + Escape). No `onOpen` loader: the gate state
|
|
16
24
|
// rides on the execution step, pushed over the stream.
|
|
@@ -38,21 +46,36 @@ const working = computed(
|
|
|
38
46
|
phase.value === 'resolving_conflicts',
|
|
39
47
|
)
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
// Exhaustive enum→label maps of literal keys (keeps the typed-key drift guard live vs a
|
|
50
|
+
// runtime-built `humanTest.envStatus.${status}`). The colour stays a component constant.
|
|
51
|
+
const ENV_STATUS_LABEL: Record<HumanTestEnvironmentStatus, string> = {
|
|
52
|
+
provisioning: 'humanTest.envStatus.provisioning',
|
|
53
|
+
ready: 'humanTest.envStatus.ready',
|
|
54
|
+
failed: 'humanTest.envStatus.failed',
|
|
55
|
+
expired: 'humanTest.envStatus.expired',
|
|
56
|
+
tearing_down: 'humanTest.envStatus.tearingDown',
|
|
57
|
+
torn_down: 'humanTest.envStatus.tornDown',
|
|
58
|
+
}
|
|
59
|
+
const ENV_STATUS_COLOR: Record<HumanTestEnvironmentStatus, string> = {
|
|
60
|
+
provisioning: 'text-amber-300',
|
|
61
|
+
ready: 'text-emerald-300',
|
|
62
|
+
failed: 'text-rose-300',
|
|
63
|
+
expired: 'text-slate-400',
|
|
64
|
+
tearing_down: 'text-slate-400',
|
|
65
|
+
torn_down: 'text-slate-400',
|
|
48
66
|
}
|
|
49
67
|
|
|
50
68
|
const PHASE_LABEL: Record<NonNullable<HumanTestStepState['phase']>, string> = {
|
|
51
|
-
provisioning: '
|
|
52
|
-
awaiting_human: '
|
|
53
|
-
fixing: '
|
|
54
|
-
resolving_conflicts: '
|
|
55
|
-
passed: '
|
|
69
|
+
provisioning: 'humanTest.phase.provisioning',
|
|
70
|
+
awaiting_human: 'humanTest.phase.awaitingHuman',
|
|
71
|
+
fixing: 'humanTest.phase.fixing',
|
|
72
|
+
resolving_conflicts: 'humanTest.phase.resolvingConflicts',
|
|
73
|
+
passed: 'humanTest.phase.passed',
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const ROUND_OUTCOME_LABEL: Record<HumanTestRoundOutcome, string> = {
|
|
77
|
+
completed: 'humanTest.roundOutcome.completed',
|
|
78
|
+
failed: 'humanTest.roundOutcome.failed',
|
|
56
79
|
}
|
|
57
80
|
|
|
58
81
|
const findings = ref('')
|
|
@@ -115,10 +138,12 @@ const canDestroy = computed(
|
|
|
115
138
|
</span>
|
|
116
139
|
<div class="min-w-0 flex-1">
|
|
117
140
|
<h2 class="truncate text-sm font-semibold text-slate-100">
|
|
118
|
-
|
|
141
|
+
{{
|
|
142
|
+
block ? t('humanTest.titleWithBlock', { title: block.title }) : t('humanTest.title')
|
|
143
|
+
}}
|
|
119
144
|
</h2>
|
|
120
145
|
<p class="truncate text-[11px] text-slate-400">
|
|
121
|
-
{{ phase ? PHASE_LABEL[phase] : '
|
|
146
|
+
{{ phase ? t(PHASE_LABEL[phase]) : t('humanTest.subtitle') }}
|
|
122
147
|
</p>
|
|
123
148
|
</div>
|
|
124
149
|
<button
|
|
@@ -135,24 +160,24 @@ const canDestroy = computed(
|
|
|
135
160
|
class="flex flex-col items-center justify-center gap-2 py-10 text-center text-slate-400"
|
|
136
161
|
>
|
|
137
162
|
<UIcon name="i-lucide-user-check" class="h-8 w-8 opacity-40" />
|
|
138
|
-
<p class="text-sm">
|
|
163
|
+
<p class="text-sm">{{ t('humanTest.notStarted') }}</p>
|
|
139
164
|
</div>
|
|
140
165
|
|
|
141
166
|
<template v-else>
|
|
142
167
|
<!-- Environment -->
|
|
143
168
|
<section class="rounded-lg border border-slate-800 bg-slate-900/60 p-4">
|
|
144
169
|
<h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
145
|
-
|
|
170
|
+
{{ t('humanTest.environment.heading') }}
|
|
146
171
|
</h3>
|
|
147
172
|
<div v-if="env" class="space-y-2">
|
|
148
173
|
<div class="flex items-center gap-2 text-[13px]">
|
|
149
174
|
<UIcon
|
|
150
175
|
name="i-lucide-circle-dot"
|
|
151
176
|
class="h-3.5 w-3.5"
|
|
152
|
-
:class="
|
|
177
|
+
:class="ENV_STATUS_COLOR[env.status]"
|
|
153
178
|
/>
|
|
154
|
-
<span :class="
|
|
155
|
-
|
|
179
|
+
<span :class="ENV_STATUS_COLOR[env.status]">{{
|
|
180
|
+
t(ENV_STATUS_LABEL[env.status])
|
|
156
181
|
}}</span>
|
|
157
182
|
</div>
|
|
158
183
|
<a
|
|
@@ -165,13 +190,17 @@ const canDestroy = computed(
|
|
|
165
190
|
<UIcon name="i-lucide-external-link" class="h-3.5 w-3.5 shrink-0" />
|
|
166
191
|
{{ env.url }}
|
|
167
192
|
</a>
|
|
168
|
-
<p v-else class="text-[12px] italic text-slate-500">
|
|
193
|
+
<p v-else class="text-[12px] italic text-slate-500">
|
|
194
|
+
{{ t('humanTest.environment.noUrl') }}
|
|
195
|
+
</p>
|
|
169
196
|
<p v-if="env.expiresAt" class="text-[11px] text-slate-500">
|
|
170
|
-
|
|
197
|
+
{{
|
|
198
|
+
t('humanTest.environment.expires', { date: d(new Date(env.expiresAt), 'long') })
|
|
199
|
+
}}
|
|
171
200
|
</p>
|
|
172
201
|
</div>
|
|
173
202
|
<p v-else class="text-[12px] text-amber-300/90">
|
|
174
|
-
{{ ht.degradedReason ?? '
|
|
203
|
+
{{ ht.degradedReason ?? t('humanTest.environment.none') }}
|
|
175
204
|
</p>
|
|
176
205
|
<p v-if="env && ht.degradedReason" class="mt-2 text-[12px] text-amber-300/90">
|
|
177
206
|
{{ ht.degradedReason }}
|
|
@@ -188,7 +217,7 @@ const canDestroy = computed(
|
|
|
188
217
|
:disabled="busy || !canManageEnv"
|
|
189
218
|
@click="recreate"
|
|
190
219
|
>
|
|
191
|
-
|
|
220
|
+
{{ t('humanTest.actions.recreate') }}
|
|
192
221
|
</UButton>
|
|
193
222
|
<UButton
|
|
194
223
|
size="xs"
|
|
@@ -198,7 +227,7 @@ const canDestroy = computed(
|
|
|
198
227
|
:disabled="busy || !canDestroy"
|
|
199
228
|
@click="destroy"
|
|
200
229
|
>
|
|
201
|
-
|
|
230
|
+
{{ t('humanTest.actions.destroy') }}
|
|
202
231
|
</UButton>
|
|
203
232
|
<UButton
|
|
204
233
|
size="xs"
|
|
@@ -209,7 +238,7 @@ const canDestroy = computed(
|
|
|
209
238
|
:disabled="busy || !canManageEnv"
|
|
210
239
|
@click="pullMain"
|
|
211
240
|
>
|
|
212
|
-
|
|
241
|
+
{{ t('humanTest.actions.pullMain') }}
|
|
213
242
|
</UButton>
|
|
214
243
|
</div>
|
|
215
244
|
</section>
|
|
@@ -220,7 +249,7 @@ const canDestroy = computed(
|
|
|
220
249
|
class="flex items-center gap-2 rounded-lg border border-slate-800 bg-slate-950/40 px-3 py-2 text-[12px] text-slate-300"
|
|
221
250
|
>
|
|
222
251
|
<UIcon name="i-lucide-loader" class="h-3.5 w-3.5 animate-spin text-amber-300" />
|
|
223
|
-
{{ phase ? PHASE_LABEL[phase] : '' }}
|
|
252
|
+
{{ phase ? t(PHASE_LABEL[phase]) : '' }}
|
|
224
253
|
</p>
|
|
225
254
|
|
|
226
255
|
<!-- Findings / fix -->
|
|
@@ -230,20 +259,20 @@ const canDestroy = computed(
|
|
|
230
259
|
>
|
|
231
260
|
<div class="flex items-center justify-between">
|
|
232
261
|
<h3 class="text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
233
|
-
|
|
262
|
+
{{ t('humanTest.fix.heading') }}
|
|
234
263
|
</h3>
|
|
235
264
|
<button
|
|
236
265
|
class="text-[12px] text-slate-400 hover:text-slate-200"
|
|
237
266
|
@click="showFindings = !showFindings"
|
|
238
267
|
>
|
|
239
|
-
{{ showFindings ? '
|
|
268
|
+
{{ showFindings ? t('humanTest.fix.cancel') : t('humanTest.fix.requestFix') }}
|
|
240
269
|
</button>
|
|
241
270
|
</div>
|
|
242
271
|
<div v-if="showFindings" class="mt-2 space-y-2">
|
|
243
272
|
<textarea
|
|
244
273
|
v-model="findings"
|
|
245
274
|
rows="4"
|
|
246
|
-
placeholder="
|
|
275
|
+
:placeholder="t('humanTest.fix.placeholder')"
|
|
247
276
|
class="w-full rounded-md border border-slate-700 bg-slate-950 px-3 py-2 text-[13px] text-slate-200 placeholder:text-slate-600 focus:border-amber-500 focus:outline-none"
|
|
248
277
|
/>
|
|
249
278
|
<UButton
|
|
@@ -254,7 +283,7 @@ const canDestroy = computed(
|
|
|
254
283
|
:disabled="busy || !findings.trim()"
|
|
255
284
|
@click="submitFix"
|
|
256
285
|
>
|
|
257
|
-
|
|
286
|
+
{{ t('humanTest.fix.send') }}
|
|
258
287
|
</UButton>
|
|
259
288
|
</div>
|
|
260
289
|
</section>
|
|
@@ -265,7 +294,7 @@ const canDestroy = computed(
|
|
|
265
294
|
class="rounded-lg border border-slate-800 bg-slate-900/60 p-4"
|
|
266
295
|
>
|
|
267
296
|
<h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
268
|
-
|
|
297
|
+
{{ t('humanTest.history.heading', { count: ht.attempts }, ht.attempts) }}
|
|
269
298
|
</h3>
|
|
270
299
|
<ol class="space-y-2">
|
|
271
300
|
<li v-for="(r, i) in ht.rounds" :key="i" class="flex items-start gap-2 text-[12px]">
|
|
@@ -275,7 +304,9 @@ const canDestroy = computed(
|
|
|
275
304
|
/>
|
|
276
305
|
<div class="min-w-0 flex-1">
|
|
277
306
|
<span class="text-slate-200">{{
|
|
278
|
-
r.kind === 'fix'
|
|
307
|
+
r.kind === 'fix'
|
|
308
|
+
? t('humanTest.history.fixRequested')
|
|
309
|
+
: t('humanTest.history.pulledMain')
|
|
279
310
|
}}</span>
|
|
280
311
|
<span
|
|
281
312
|
class="ml-1.5 rounded px-1 text-[10px] uppercase"
|
|
@@ -287,7 +318,11 @@ const canDestroy = computed(
|
|
|
287
318
|
: 'bg-slate-500/15 text-slate-300'
|
|
288
319
|
"
|
|
289
320
|
>
|
|
290
|
-
{{
|
|
321
|
+
{{
|
|
322
|
+
r.outcome
|
|
323
|
+
? t(ROUND_OUTCOME_LABEL[r.outcome])
|
|
324
|
+
: t('humanTest.roundOutcome.inProgress')
|
|
325
|
+
}}
|
|
291
326
|
</span>
|
|
292
327
|
<p v-if="r.findings" class="leading-snug text-slate-400">{{ r.findings }}</p>
|
|
293
328
|
</div>
|
|
@@ -318,7 +353,7 @@ const canDestroy = computed(
|
|
|
318
353
|
:disabled="busy || !awaitingHuman"
|
|
319
354
|
@click="confirm"
|
|
320
355
|
>
|
|
321
|
-
|
|
356
|
+
{{ t('humanTest.confirm') }}
|
|
322
357
|
</UButton>
|
|
323
358
|
</footer>
|
|
324
359
|
</div>
|