@cat-factory/app 0.145.2 → 0.146.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/components/environments/EnvSetupStepper.vue +70 -0
- package/app/components/environments/EnvironmentSetupWizard.vue +9 -59
- package/app/modular/journeys/environmentSetup.logic.spec.ts +46 -15
- package/app/modular/journeys/environmentSetup.logic.ts +102 -35
- package/app/modular/journeys/environmentSetup.ts +17 -68
- package/i18n/locales/de.json +1 -0
- package/i18n/locales/en.json +1 -0
- package/i18n/locales/es.json +1 -0
- package/i18n/locales/fr.json +1 -0
- package/i18n/locales/he.json +1 -0
- package/i18n/locales/it.json +1 -0
- package/i18n/locales/ja.json +1 -0
- package/i18n/locales/pl.json +1 -0
- package/i18n/locales/tr.json +1 -0
- package/i18n/locales/uk.json +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// The environment-setup wizard's stepper header, driven entirely by
|
|
3
|
+
// `useJourneyProgress` (modular-react#83, production-feedback item 4). The ordered
|
|
4
|
+
// steps, the live position, and the "Step X of N" total all come from
|
|
5
|
+
// `resolveStepSequence` walking the journey's transition graph — there is no
|
|
6
|
+
// hand-maintained step-order array to keep in sync (the old `ENV_STEP_ORDER` +
|
|
7
|
+
// `crumbState` is gone). Rendered inside `<JourneyHost>`, which hands it the live
|
|
8
|
+
// `instanceId`; each step's label is the i18n key carried in the journey's `steps`
|
|
9
|
+
// metadata.
|
|
10
|
+
import type { InstanceId } from '@modular-vue/journeys'
|
|
11
|
+
import { useJourneyProgress } from '@modular-vue/journeys'
|
|
12
|
+
import { environmentSetupJourney } from '~/modular/journeys/environmentSetup'
|
|
13
|
+
import type { EnvSetupInput } from '~/modular/journeys/environmentSetup.logic'
|
|
14
|
+
|
|
15
|
+
const props = defineProps<{
|
|
16
|
+
/** The live journey instance from `<JourneyHost>`, or null before it starts. */
|
|
17
|
+
instanceId: InstanceId | null
|
|
18
|
+
/** The launch input; frozen for the host's lifetime, so the resolved spine is
|
|
19
|
+
* stable (`useJourneyProgress` reads `sequence.input` once at setup). */
|
|
20
|
+
input: EnvSetupInput
|
|
21
|
+
}>()
|
|
22
|
+
|
|
23
|
+
const { t, te } = useI18n()
|
|
24
|
+
|
|
25
|
+
const { index, total, steps } = useJourneyProgress(
|
|
26
|
+
() => props.instanceId,
|
|
27
|
+
environmentSetupJourney,
|
|
28
|
+
{ sequence: { input: props.input } },
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
/** Resolve a step's progress-label key (from the journey's `steps` metadata)
|
|
32
|
+
* through i18n, tolerating a locale that omits it rather than leaking the key. */
|
|
33
|
+
function stepLabel(key: string | undefined): string {
|
|
34
|
+
return key && te(key) ? t(key) : ''
|
|
35
|
+
}
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<template>
|
|
39
|
+
<div class="space-y-2" data-testid="env-setup-stepper">
|
|
40
|
+
<p v-if="total" class="text-[11px] font-medium text-slate-400" data-testid="env-setup-progress">
|
|
41
|
+
{{ t('environmentWizard.progress', { index: index + 1, total }) }}
|
|
42
|
+
</p>
|
|
43
|
+
<ol class="flex items-center gap-2 text-[11px]">
|
|
44
|
+
<li
|
|
45
|
+
v-for="(step, i) in steps"
|
|
46
|
+
:key="step.entry"
|
|
47
|
+
class="flex items-center gap-2"
|
|
48
|
+
:data-testid="`env-setup-crumb-${step.entry}`"
|
|
49
|
+
>
|
|
50
|
+
<span
|
|
51
|
+
class="flex h-5 w-5 items-center justify-center rounded-full text-[10px] font-semibold"
|
|
52
|
+
:class="{
|
|
53
|
+
'bg-primary-500 text-white': i === index,
|
|
54
|
+
'bg-emerald-600/70 text-white': i < index,
|
|
55
|
+
'bg-slate-700 text-slate-300': i > index,
|
|
56
|
+
}"
|
|
57
|
+
>{{ i + 1 }}</span
|
|
58
|
+
>
|
|
59
|
+
<span :class="i === index ? 'text-slate-100' : 'text-slate-500'">
|
|
60
|
+
{{ stepLabel(step.progressLabel) }}
|
|
61
|
+
</span>
|
|
62
|
+
<UIcon
|
|
63
|
+
v-if="i < steps.length - 1"
|
|
64
|
+
name="i-lucide-chevron-right"
|
|
65
|
+
class="h-3 w-3 text-slate-600"
|
|
66
|
+
/>
|
|
67
|
+
</li>
|
|
68
|
+
</ol>
|
|
69
|
+
</div>
|
|
70
|
+
</template>
|
|
@@ -8,14 +8,15 @@
|
|
|
8
8
|
// journey (`~/modular/journeys/environmentSetup`), hosted here by `<JourneyHost>`:
|
|
9
9
|
// it starts the journey on open (RESUMING the in-flight instance for the same
|
|
10
10
|
// frame when one was left mid-flow, via the Pinia persistence adapter), renders
|
|
11
|
-
// the current step through `<JourneyOutlet>`, and abandons it on close.
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
11
|
+
// the current step through `<JourneyOutlet>`, and abandons it on close. The
|
|
12
|
+
// stepper header + "Step X of N" are derived from the journey's transition graph
|
|
13
|
+
// by `<EnvSetupStepper>` (via `useJourneyProgress`), not a hand-maintained step
|
|
14
|
+
// list. Each step component drives the `environmentWizard` store for its
|
|
15
|
+
// data/actions and fires the journey's exits to advance. On `complete` (the save
|
|
16
|
+
// step's Done) the journey clears its persisted blob and we close the modal.
|
|
15
17
|
import { computed } from 'vue'
|
|
16
18
|
import { JourneyHost, JourneyOutlet } from '@modular-vue/journeys'
|
|
17
19
|
import { environmentSetupHandle } from '~/modular/journeys/environmentSetup'
|
|
18
|
-
import { ENV_STEP_ORDER, type EnvStep } from '~/modular/journeys/environmentSetup.logic'
|
|
19
20
|
|
|
20
21
|
const ui = useUiStore()
|
|
21
22
|
const { t } = useI18n()
|
|
@@ -30,23 +31,6 @@ const open = computed({
|
|
|
30
31
|
// Read once at journey start; keyed for resume. Frozen for the host's lifetime,
|
|
31
32
|
// so the modal remounts the host per open (it renders under `v-if` upstream).
|
|
32
33
|
const input = computed(() => ({ frameId: ui.environmentWizardFrameId }))
|
|
33
|
-
|
|
34
|
-
const STEP_LABEL = computed<Record<EnvStep, string>>(() => ({
|
|
35
|
-
pick: t('environmentWizard.steps.pick'),
|
|
36
|
-
review: t('environmentWizard.steps.review'),
|
|
37
|
-
preflight: t('environmentWizard.steps.preflight'),
|
|
38
|
-
save: t('environmentWizard.steps.save'),
|
|
39
|
-
}))
|
|
40
|
-
|
|
41
|
-
/** The crumb index of a step, relative to the current journey step. */
|
|
42
|
-
function crumbState(
|
|
43
|
-
entry: EnvStep,
|
|
44
|
-
currentEntry: EnvStep | undefined,
|
|
45
|
-
): 'current' | 'done' | 'todo' {
|
|
46
|
-
if (!currentEntry) return 'todo'
|
|
47
|
-
if (entry === currentEntry) return 'current'
|
|
48
|
-
return ENV_STEP_ORDER.indexOf(entry) < ENV_STEP_ORDER.indexOf(currentEntry) ? 'done' : 'todo'
|
|
49
|
-
}
|
|
50
34
|
</script>
|
|
51
35
|
|
|
52
36
|
<template>
|
|
@@ -63,43 +47,9 @@ function crumbState(
|
|
|
63
47
|
:input="input"
|
|
64
48
|
@finished="ui.closeEnvironmentSetup()"
|
|
65
49
|
>
|
|
66
|
-
<template #default="{ instanceId
|
|
67
|
-
<!-- stepper header,
|
|
68
|
-
<
|
|
69
|
-
<li
|
|
70
|
-
v-for="(s, i) in ENV_STEP_ORDER"
|
|
71
|
-
:key="s"
|
|
72
|
-
class="flex items-center gap-2"
|
|
73
|
-
:data-testid="`env-setup-crumb-${s}`"
|
|
74
|
-
>
|
|
75
|
-
<span
|
|
76
|
-
class="flex h-5 w-5 items-center justify-center rounded-full text-[10px] font-semibold"
|
|
77
|
-
:class="{
|
|
78
|
-
'bg-primary-500 text-white':
|
|
79
|
-
crumbState(s, (instance?.step?.entry as EnvStep) ?? undefined) === 'current',
|
|
80
|
-
'bg-emerald-600/70 text-white':
|
|
81
|
-
crumbState(s, (instance?.step?.entry as EnvStep) ?? undefined) === 'done',
|
|
82
|
-
'bg-slate-700 text-slate-300':
|
|
83
|
-
crumbState(s, (instance?.step?.entry as EnvStep) ?? undefined) === 'todo',
|
|
84
|
-
}"
|
|
85
|
-
>{{ i + 1 }}</span
|
|
86
|
-
>
|
|
87
|
-
<span
|
|
88
|
-
:class="
|
|
89
|
-
crumbState(s, (instance?.step?.entry as EnvStep) ?? undefined) === 'current'
|
|
90
|
-
? 'text-slate-100'
|
|
91
|
-
: 'text-slate-500'
|
|
92
|
-
"
|
|
93
|
-
>
|
|
94
|
-
{{ STEP_LABEL[s] }}
|
|
95
|
-
</span>
|
|
96
|
-
<UIcon
|
|
97
|
-
v-if="i < ENV_STEP_ORDER.length - 1"
|
|
98
|
-
name="i-lucide-chevron-right"
|
|
99
|
-
class="h-3 w-3 text-slate-600"
|
|
100
|
-
/>
|
|
101
|
-
</li>
|
|
102
|
-
</ol>
|
|
50
|
+
<template #default="{ instanceId }">
|
|
51
|
+
<!-- stepper header, derived from the journey graph -->
|
|
52
|
+
<EnvSetupStepper :instance-id="instanceId" :input="input" />
|
|
103
53
|
|
|
104
54
|
<!-- the current step (pick / review / preflight / save) -->
|
|
105
55
|
<div class="mt-4">
|
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { resolveStepSequence } from '@modular-vue/journeys'
|
|
2
3
|
import {
|
|
3
|
-
|
|
4
|
+
ENV_MODULE_ID,
|
|
5
|
+
environmentSetupJourney,
|
|
4
6
|
envInitialState,
|
|
5
|
-
envNextAfter,
|
|
6
7
|
envStartStep,
|
|
7
8
|
} from './environmentSetup.logic'
|
|
9
|
+
import en from '../../../i18n/locales/en.json'
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
/** Dot-path lookup into the real `en.json`, mirroring which keys actually ship. */
|
|
12
|
+
function hasKey(path: string): boolean {
|
|
13
|
+
return (
|
|
14
|
+
path.split('.').reduce<unknown>((node, seg) => {
|
|
15
|
+
return node && typeof node === 'object' ? (node as Record<string, unknown>)[seg] : undefined
|
|
16
|
+
}, en) !== undefined
|
|
17
|
+
)
|
|
18
|
+
}
|
|
17
19
|
|
|
20
|
+
// Pure navigation graph for the environment-setup journey (slice 3). The step
|
|
21
|
+
// order is derived from the annotated transition graph (production-feedback item
|
|
22
|
+
// 4), so `resolveStepSequenceResult` walking the real journey definition is the
|
|
23
|
+
// guard against a silent reorder or dropped step — no hand-maintained order array
|
|
24
|
+
// to pin separately.
|
|
25
|
+
describe('environment-setup journey logic', () => {
|
|
18
26
|
it('seeds state from the launch input', () => {
|
|
19
27
|
expect(envInitialState({ frameId: 'blk_1' })).toEqual({ frameId: 'blk_1' })
|
|
20
28
|
expect(envInitialState({ frameId: null })).toEqual({ frameId: null })
|
|
@@ -25,10 +33,33 @@ describe('environment-setup journey logic', () => {
|
|
|
25
33
|
expect(envStartStep({ frameId: null }, { frameId: null })).toBe('pick')
|
|
26
34
|
})
|
|
27
35
|
|
|
28
|
-
it('
|
|
29
|
-
|
|
30
|
-
expect(
|
|
31
|
-
expect(
|
|
32
|
-
expect(
|
|
36
|
+
it('derives the pick → review → preflight → save order from the transition graph', () => {
|
|
37
|
+
const steps = resolveStepSequence(environmentSetupJourney, { input: { frameId: null } })
|
|
38
|
+
expect(steps.map((s) => s.entry)).toEqual(['pick', 'review', 'preflight', 'save'])
|
|
39
|
+
expect(steps.every((s) => s.module === ENV_MODULE_ID)).toBe(true)
|
|
40
|
+
expect(steps.map((s) => s.progressLabel)).toEqual([
|
|
41
|
+
'environmentWizard.steps.pick',
|
|
42
|
+
'environmentWizard.steps.review',
|
|
43
|
+
'environmentWizard.steps.preflight',
|
|
44
|
+
'environmentWizard.steps.save',
|
|
45
|
+
])
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('starts the derived sequence at review when a frame is preselected', () => {
|
|
49
|
+
const steps = resolveStepSequence(environmentSetupJourney, { input: { frameId: 'blk_1' } })
|
|
50
|
+
expect(steps.map((s) => s.entry)).toEqual(['review', 'preflight', 'save'])
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// The stepper resolves each label through a VARIABLE key (`t(step.progressLabel)`), so
|
|
54
|
+
// the tier-1 typed-message-keys check + `vue-i18n-extract` can no longer see these keys
|
|
55
|
+
// as used and can't catch a typo or a dropped key. Pin the missing-key guard here instead:
|
|
56
|
+
// every `progressLabel` the real definition emits must resolve to a shipping `en.json` key.
|
|
57
|
+
it('carries a shipping en.json label key for every derived step', () => {
|
|
58
|
+
const steps = resolveStepSequence(environmentSetupJourney, { input: { frameId: null } })
|
|
59
|
+
for (const step of steps) {
|
|
60
|
+
const key = step.progressLabel
|
|
61
|
+
expect(key, `missing i18n key for step "${step.entry}"`).toBeTruthy()
|
|
62
|
+
expect(hasKey(key ?? ''), `key "${key}" not in en.json`).toBe(true)
|
|
63
|
+
}
|
|
33
64
|
})
|
|
34
65
|
})
|
|
@@ -10,15 +10,26 @@
|
|
|
10
10
|
* trial) stay in that Pinia store, which the step components still drive; the
|
|
11
11
|
* journey just decides WHICH step shows and threads the target frame id.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
* transition graph
|
|
15
|
-
*
|
|
16
|
-
* `
|
|
13
|
+
* The step ORDER is no longer spelled out a second time as an ordered array: it
|
|
14
|
+
* is DERIVED from the annotated transition graph below (production-feedback item
|
|
15
|
+
* 4 in modular-react#83). Each transition wraps its handler with
|
|
16
|
+
* `defineTransition({ targets, handle })` so the static `targets` declare where
|
|
17
|
+
* the flow can go; `resolveStepSequence` / `useJourneyProgress` walk that graph
|
|
18
|
+
* to produce the ordered step list and the wizard's "Step X of N" progress from
|
|
19
|
+
* the one place the flow is encoded. `steps` carries each step's progress-label
|
|
20
|
+
* key beside the transitions.
|
|
21
|
+
*
|
|
22
|
+
* This definition lives in the logic file (not the SFC-importing
|
|
23
|
+
* `environmentSetup.ts`) so the graph stays free of `.vue` imports and is
|
|
24
|
+
* unit-tested directly (`environmentSetup.logic.spec.ts`). It references the step
|
|
25
|
+
* module only as a TYPE (`import type`), which is erased at runtime, so importing
|
|
26
|
+
* this file pulls no components. `environmentSetup.ts` supplies the module (with
|
|
27
|
+
* its step components) and re-exports the journey + the launch handle + the
|
|
28
|
+
* persistence adapter.
|
|
17
29
|
*/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export type EnvStep = (typeof ENV_STEP_ORDER)[number]
|
|
30
|
+
import type { ExitCtx } from '@modular-vue/journeys'
|
|
31
|
+
import { defineJourney, defineTransition } from '@modular-vue/journeys'
|
|
32
|
+
import type { environmentSetupModule } from '~/modular/journeys/environmentSetup'
|
|
22
33
|
|
|
23
34
|
/** The single module id every step entry lives under. */
|
|
24
35
|
export const ENV_MODULE_ID = 'cat-factory:environment-setup' as const
|
|
@@ -52,35 +63,91 @@ export function envInitialState(input: EnvSetupInput): EnvSetupState {
|
|
|
52
63
|
}
|
|
53
64
|
|
|
54
65
|
/** First step: skip the picker when the launcher already chose a frame. */
|
|
55
|
-
export function envStartStep(_state: EnvSetupState, input: EnvSetupInput):
|
|
66
|
+
export function envStartStep(_state: EnvSetupState, input: EnvSetupInput): 'pick' | 'review' {
|
|
56
67
|
return input.frameId ? 'review' : 'pick'
|
|
57
68
|
}
|
|
58
69
|
|
|
59
70
|
/**
|
|
60
|
-
* The
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* definition (`environmentSetup.ts`) derives its `advance` transitions' target
|
|
66
|
-
* entries from it, so pinning this function in `environmentSetup.logic.spec.ts`
|
|
67
|
-
* genuinely guards the wired graph against a silent reorder/drop. The overloads
|
|
68
|
-
* narrow the return to a concrete entry (or `'done'`) so a transition handler can
|
|
69
|
-
* feed the result straight into a `StepSpec` without a widened `EnvStep | 'done'`.
|
|
71
|
+
* The journey's module type map — one module, referenced by its `typeof` so the
|
|
72
|
+
* transition + step maps resolve its literal entry/exit vocabulary with no casts.
|
|
73
|
+
* This is the case production-feedback item 3 (modular-react#83) fixed: before it,
|
|
74
|
+
* `defineModule` widened the descriptor, so `typeof environmentSetupModule` could
|
|
75
|
+
* not stand in as a `TModules` member without re-declaring the entry names by hand.
|
|
70
76
|
*/
|
|
71
|
-
export
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
77
|
+
export type EnvModules = { [ENV_MODULE_ID]: typeof environmentSetupModule }
|
|
78
|
+
|
|
79
|
+
/** Binder threading the journey generics into each annotated transition handler. */
|
|
80
|
+
const transition = defineTransition<EnvModules, EnvSetupState>()
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The journey definition. `start` skips the picker when a frame was preselected.
|
|
84
|
+
* The forward chain is the annotated transitions' `targets`: pick →(select)→
|
|
85
|
+
* review →(advance)→ preflight →(advance)→ save →(advance)→ complete. Back
|
|
86
|
+
* navigation is handled by the entries' `allowBack` (declared on the module in
|
|
87
|
+
* `environmentSetup.ts`), not explicit transitions.
|
|
88
|
+
*/
|
|
89
|
+
export const environmentSetupJourney = defineJourney<EnvModules, EnvSetupState>()({
|
|
90
|
+
id: 'environment-setup',
|
|
91
|
+
version: '1.0.0',
|
|
92
|
+
initialState: (input: EnvSetupInput) => envInitialState(input),
|
|
93
|
+
start: (state, input: EnvSetupInput) =>
|
|
94
|
+
envStartStep(state, input) === 'review'
|
|
95
|
+
? { module: ENV_MODULE_ID, entry: 'review', input: { frameId: state.frameId } }
|
|
96
|
+
: { module: ENV_MODULE_ID, entry: 'pick', input: { frameId: state.frameId } },
|
|
97
|
+
steps: {
|
|
98
|
+
[ENV_MODULE_ID]: {
|
|
99
|
+
pick: { progressLabel: 'environmentWizard.steps.pick' },
|
|
100
|
+
review: { progressLabel: 'environmentWizard.steps.review' },
|
|
101
|
+
preflight: { progressLabel: 'environmentWizard.steps.preflight' },
|
|
102
|
+
save: { progressLabel: 'environmentWizard.steps.save' },
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
transitions: {
|
|
106
|
+
[ENV_MODULE_ID]: {
|
|
107
|
+
pick: {
|
|
108
|
+
[ENV_SELECT_EXIT]: transition({
|
|
109
|
+
targets: [{ module: ENV_MODULE_ID, entry: 'review' }],
|
|
110
|
+
handle: (ctx: ExitCtx<EnvSetupState, EnvSelectOutput, unknown>) => ({
|
|
111
|
+
next: {
|
|
112
|
+
module: ENV_MODULE_ID,
|
|
113
|
+
entry: 'review',
|
|
114
|
+
input: { frameId: ctx.output.frameId },
|
|
115
|
+
},
|
|
116
|
+
state: { frameId: ctx.output.frameId },
|
|
117
|
+
}),
|
|
118
|
+
}),
|
|
119
|
+
},
|
|
120
|
+
review: {
|
|
121
|
+
[ENV_ADVANCE_EXIT]: transition({
|
|
122
|
+
targets: [{ module: ENV_MODULE_ID, entry: 'preflight' }],
|
|
123
|
+
handle: (ctx) => ({
|
|
124
|
+
next: {
|
|
125
|
+
module: ENV_MODULE_ID,
|
|
126
|
+
entry: 'preflight',
|
|
127
|
+
input: { frameId: ctx.state.frameId },
|
|
128
|
+
},
|
|
129
|
+
}),
|
|
130
|
+
}),
|
|
131
|
+
},
|
|
132
|
+
preflight: {
|
|
133
|
+
[ENV_ADVANCE_EXIT]: transition({
|
|
134
|
+
targets: [{ module: ENV_MODULE_ID, entry: 'save' }],
|
|
135
|
+
handle: (ctx) => ({
|
|
136
|
+
next: {
|
|
137
|
+
module: ENV_MODULE_ID,
|
|
138
|
+
entry: 'save',
|
|
139
|
+
input: { frameId: ctx.state.frameId },
|
|
140
|
+
},
|
|
141
|
+
}),
|
|
142
|
+
}),
|
|
143
|
+
},
|
|
144
|
+
// `save` advances to journey completion.
|
|
145
|
+
save: {
|
|
146
|
+
[ENV_ADVANCE_EXIT]: transition({
|
|
147
|
+
targets: ['complete'],
|
|
148
|
+
handle: () => ({ complete: undefined }),
|
|
149
|
+
}),
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineModule } from '@modular-vue/core'
|
|
2
2
|
import { defineExit } from '@modular-frontend/core'
|
|
3
|
-
import {
|
|
3
|
+
import { defineJourneyHandle } from '@modular-vue/journeys'
|
|
4
4
|
import EnvPickStep from '~/components/environments/steps/EnvPickStep.vue'
|
|
5
5
|
import EnvReviewStep from '~/components/environments/steps/EnvReviewStep.vue'
|
|
6
6
|
import EnvPreflightStep from '~/components/environments/steps/EnvPreflightStep.vue'
|
|
@@ -13,9 +13,7 @@ import {
|
|
|
13
13
|
type EnvSelectOutput,
|
|
14
14
|
type EnvSetupInput,
|
|
15
15
|
type EnvSetupState,
|
|
16
|
-
|
|
17
|
-
envNextAfter,
|
|
18
|
-
envStartStep,
|
|
16
|
+
environmentSetupJourney,
|
|
19
17
|
} from '~/modular/journeys/environmentSetup.logic'
|
|
20
18
|
|
|
21
19
|
/**
|
|
@@ -25,6 +23,13 @@ import {
|
|
|
25
23
|
* with a typed, back/rewind-capable, resumable journey; the per-step data + async
|
|
26
24
|
* actions stay in that Pinia store, driven by the step components below.
|
|
27
25
|
*
|
|
26
|
+
* This file supplies the step MODULE (it imports the step `.vue` components, so —
|
|
27
|
+
* like `result-views.ts` — it's registered from the client plugin, keeping the
|
|
28
|
+
* unit-tested `registry.ts` / `*.logic.ts` import graph free of SFCs) and re-exports
|
|
29
|
+
* the JOURNEY, whose transition graph + step metadata live in the SFC-free
|
|
30
|
+
* `environmentSetup.logic.ts` (so `resolveStepSequence` / `useJourneyProgress` and
|
|
31
|
+
* the ordering test have no `.vue` dependency).
|
|
32
|
+
*
|
|
28
33
|
* Authoring notes (co-evolution): `defineModule` comes from the `@modular-vue/core`
|
|
29
34
|
* binding, but the exit helper `defineExit` is only exported from the neutral
|
|
30
35
|
* `@modular-frontend/core` engine (a direct dep) — the Vue binding doesn't re-export
|
|
@@ -36,10 +41,6 @@ import {
|
|
|
36
41
|
* pick step and never changes mid-flow, so the snapshot input a transition places is
|
|
37
42
|
* always current — even on `preserve-state` back-navigation — which keeps the module
|
|
38
43
|
* type simple (no `buildInput`-optionality quirk) and the flow fully explicit.
|
|
39
|
-
*
|
|
40
|
-
* This file imports the step `.vue` components, so — like `result-views.ts` — it's
|
|
41
|
-
* registered from the client plugin, keeping the unit-tested `registry.ts` /
|
|
42
|
-
* `*.logic.ts` import graph free of SFCs.
|
|
43
44
|
*/
|
|
44
45
|
|
|
45
46
|
/**
|
|
@@ -52,20 +53,20 @@ export const environmentSetupModule = defineModule({
|
|
|
52
53
|
id: ENV_MODULE_ID,
|
|
53
54
|
version: '1.0.0',
|
|
54
55
|
entryPoints: {
|
|
55
|
-
pick: { mountKinds: ['journey']
|
|
56
|
+
pick: { mountKinds: ['journey'], component: EnvPickStep },
|
|
56
57
|
review: {
|
|
57
|
-
mountKinds: ['journey']
|
|
58
|
-
allowBack: 'preserve-state'
|
|
58
|
+
mountKinds: ['journey'],
|
|
59
|
+
allowBack: 'preserve-state',
|
|
59
60
|
component: EnvReviewStep,
|
|
60
61
|
},
|
|
61
62
|
preflight: {
|
|
62
|
-
mountKinds: ['journey']
|
|
63
|
-
allowBack: 'preserve-state'
|
|
63
|
+
mountKinds: ['journey'],
|
|
64
|
+
allowBack: 'preserve-state',
|
|
64
65
|
component: EnvPreflightStep,
|
|
65
66
|
},
|
|
66
67
|
save: {
|
|
67
|
-
mountKinds: ['journey']
|
|
68
|
-
allowBack: 'preserve-state'
|
|
68
|
+
mountKinds: ['journey'],
|
|
69
|
+
allowBack: 'preserve-state',
|
|
69
70
|
component: EnvSaveStep,
|
|
70
71
|
},
|
|
71
72
|
},
|
|
@@ -75,59 +76,7 @@ export const environmentSetupModule = defineModule({
|
|
|
75
76
|
},
|
|
76
77
|
})
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* The journey definition. `start` skips the picker when a frame was preselected;
|
|
82
|
-
* transitions are the linear pick → review → preflight → save chain, terminating
|
|
83
|
-
* with `complete` off the save step's `advance`. Back-navigation is handled by the
|
|
84
|
-
* entries' `allowBack`, not explicit transitions.
|
|
85
|
-
*/
|
|
86
|
-
export const environmentSetupJourney = defineJourney<EnvModules, EnvSetupState>()({
|
|
87
|
-
id: 'environment-setup',
|
|
88
|
-
version: '1.0.0',
|
|
89
|
-
initialState: (input: EnvSetupInput) => envInitialState(input),
|
|
90
|
-
start: (state, input: EnvSetupInput) =>
|
|
91
|
-
envStartStep(state, input) === 'review'
|
|
92
|
-
? ({ module: ENV_MODULE_ID, entry: 'review', input: { frameId: state.frameId } } as const)
|
|
93
|
-
: ({ module: ENV_MODULE_ID, entry: 'pick', input: { frameId: state.frameId } } as const),
|
|
94
|
-
transitions: {
|
|
95
|
-
[ENV_MODULE_ID]: {
|
|
96
|
-
pick: {
|
|
97
|
-
[ENV_SELECT_EXIT]: (ctx) => ({
|
|
98
|
-
next: {
|
|
99
|
-
module: ENV_MODULE_ID,
|
|
100
|
-
entry: 'review',
|
|
101
|
-
input: { frameId: ctx.output.frameId },
|
|
102
|
-
} as const,
|
|
103
|
-
state: { frameId: ctx.output.frameId },
|
|
104
|
-
}),
|
|
105
|
-
},
|
|
106
|
-
review: {
|
|
107
|
-
[ENV_ADVANCE_EXIT]: (ctx) => ({
|
|
108
|
-
next: {
|
|
109
|
-
module: ENV_MODULE_ID,
|
|
110
|
-
entry: envNextAfter('review'),
|
|
111
|
-
input: { frameId: ctx.state.frameId },
|
|
112
|
-
} as const,
|
|
113
|
-
}),
|
|
114
|
-
},
|
|
115
|
-
preflight: {
|
|
116
|
-
[ENV_ADVANCE_EXIT]: (ctx) => ({
|
|
117
|
-
next: {
|
|
118
|
-
module: ENV_MODULE_ID,
|
|
119
|
-
entry: envNextAfter('preflight'),
|
|
120
|
-
input: { frameId: ctx.state.frameId },
|
|
121
|
-
} as const,
|
|
122
|
-
}),
|
|
123
|
-
},
|
|
124
|
-
// `save` advances to `envNextAfter('save') === 'done'`, i.e. journey completion.
|
|
125
|
-
save: {
|
|
126
|
-
[ENV_ADVANCE_EXIT]: () => ({ complete: undefined }),
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
})
|
|
79
|
+
export { environmentSetupJourney }
|
|
131
80
|
|
|
132
81
|
/** Typed launch handle — callers `runtime.start(handle, { frameId })`. */
|
|
133
82
|
export const environmentSetupHandle = defineJourneyHandle(environmentSetupJourney)
|
package/i18n/locales/de.json
CHANGED
|
@@ -4808,6 +4808,7 @@
|
|
|
4808
4808
|
}
|
|
4809
4809
|
},
|
|
4810
4810
|
"environmentWizard": {
|
|
4811
|
+
"progress": "Schritt {index} von {total}",
|
|
4811
4812
|
"title": "Eine Testumgebung einrichten",
|
|
4812
4813
|
"subtitle": "Erkenne, prüfe und speichere ein Docker-Compose-Rezept, damit der Deployer es bereitstellt.",
|
|
4813
4814
|
"recommended": "empfohlen",
|
package/i18n/locales/en.json
CHANGED
|
@@ -4941,6 +4941,7 @@
|
|
|
4941
4941
|
}
|
|
4942
4942
|
},
|
|
4943
4943
|
"environmentWizard": {
|
|
4944
|
+
"progress": "Step {index} of {total}",
|
|
4944
4945
|
"title": "Set up a test environment",
|
|
4945
4946
|
"subtitle": "Detect, review, and save a Docker Compose recipe so the Deployer provisions it.",
|
|
4946
4947
|
"recommended": "recommended",
|
package/i18n/locales/es.json
CHANGED
|
@@ -4800,6 +4800,7 @@
|
|
|
4800
4800
|
}
|
|
4801
4801
|
},
|
|
4802
4802
|
"environmentWizard": {
|
|
4803
|
+
"progress": "Paso {index} de {total}",
|
|
4803
4804
|
"title": "Configurar un entorno de pruebas",
|
|
4804
4805
|
"subtitle": "Detecta, revisa y guarda una receta de Docker Compose para que el Deployer la aprovisione.",
|
|
4805
4806
|
"recommended": "recomendado",
|
package/i18n/locales/fr.json
CHANGED
|
@@ -4800,6 +4800,7 @@
|
|
|
4800
4800
|
}
|
|
4801
4801
|
},
|
|
4802
4802
|
"environmentWizard": {
|
|
4803
|
+
"progress": "Étape {index} sur {total}",
|
|
4803
4804
|
"title": "Configurer un environnement de test",
|
|
4804
4805
|
"subtitle": "Détectez, vérifiez et enregistrez une recette Docker Compose pour que le Deployer la provisionne.",
|
|
4805
4806
|
"recommended": "recommandé",
|
package/i18n/locales/he.json
CHANGED
package/i18n/locales/it.json
CHANGED
|
@@ -4808,6 +4808,7 @@
|
|
|
4808
4808
|
}
|
|
4809
4809
|
},
|
|
4810
4810
|
"environmentWizard": {
|
|
4811
|
+
"progress": "Passaggio {index} di {total}",
|
|
4811
4812
|
"title": "Configura un ambiente di test",
|
|
4812
4813
|
"subtitle": "Rileva, rivedi e salva una ricetta Docker Compose in modo che il Deployer la esegua.",
|
|
4813
4814
|
"recommended": "consigliato",
|
package/i18n/locales/ja.json
CHANGED
package/i18n/locales/pl.json
CHANGED
|
@@ -4800,6 +4800,7 @@
|
|
|
4800
4800
|
}
|
|
4801
4801
|
},
|
|
4802
4802
|
"environmentWizard": {
|
|
4803
|
+
"progress": "Krok {index} z {total}",
|
|
4803
4804
|
"title": "Skonfiguruj środowisko testowe",
|
|
4804
4805
|
"subtitle": "Wykryj, przejrzyj i zapisz przepis Docker Compose, aby Deployer go udostępnił.",
|
|
4805
4806
|
"recommended": "zalecane",
|
package/i18n/locales/tr.json
CHANGED
|
@@ -4812,6 +4812,7 @@
|
|
|
4812
4812
|
}
|
|
4813
4813
|
},
|
|
4814
4814
|
"environmentWizard": {
|
|
4815
|
+
"progress": "Adım {index} / {total}",
|
|
4815
4816
|
"title": "Bir test ortamı kur",
|
|
4816
4817
|
"subtitle": "Deployer sağlasın diye bir Docker Compose tarifini algıla, gözden geçir ve kaydet.",
|
|
4817
4818
|
"recommended": "önerilen",
|
package/i18n/locales/uk.json
CHANGED
|
@@ -4800,6 +4800,7 @@
|
|
|
4800
4800
|
}
|
|
4801
4801
|
},
|
|
4802
4802
|
"environmentWizard": {
|
|
4803
|
+
"progress": "Крок {index} з {total}",
|
|
4803
4804
|
"title": "Налаштувати тестове середовище",
|
|
4804
4805
|
"subtitle": "Виявіть, перегляньте та збережіть рецепт Docker Compose, щоб Deployer його забезпечив.",
|
|
4805
4806
|
"recommended": "рекомендовано",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.146.0",
|
|
4
4
|
"description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
"access": "public"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@modular-frontend/core": "^0.
|
|
22
|
-
"@modular-vue/core": "^1.
|
|
23
|
-
"@modular-vue/journeys": "^1.
|
|
21
|
+
"@modular-frontend/core": "^0.6.0",
|
|
22
|
+
"@modular-vue/core": "^1.5.0",
|
|
23
|
+
"@modular-vue/journeys": "^1.4.0",
|
|
24
24
|
"@modular-vue/nuxt": "^0.4.0",
|
|
25
25
|
"@modular-vue/runtime": "^1.4.1",
|
|
26
26
|
"@modular-vue/vue": "^1.4.0",
|