@pellux/goodvibes-tui 0.19.27 → 0.19.29
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/CHANGELOG.md +11 -0
- package/README.md +3 -1
- package/docs/foundation-artifacts/operator-contract.json +1 -1
- package/package.json +2 -2
- package/src/cli/bundle-command.ts +3 -2
- package/src/cli/entrypoint.ts +2 -2
- package/src/cli/help.ts +1 -1
- package/src/cli/status.ts +9 -9
- package/src/cli/surface-command.ts +46 -11
- package/src/cli/tui-startup.ts +4 -4
- package/src/daemon/cli.ts +7 -0
- package/src/input/handler-interactions.ts +14 -1
- package/src/input/handler-onboarding.ts +161 -118
- package/src/input/handler.ts +1 -1
- package/src/input/onboarding/handler-onboarding-routes.ts +35 -15
- package/src/input/onboarding/onboarding-wizard-apply.ts +35 -25
- package/src/input/onboarding/onboarding-wizard-constants.ts +4 -5
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +93 -5
- package/src/input/onboarding/onboarding-wizard-helpers.ts +2 -3
- package/src/input/onboarding/onboarding-wizard-rules.ts +40 -8
- package/src/input/onboarding/onboarding-wizard-state.ts +19 -8
- package/src/input/onboarding/onboarding-wizard-steps.ts +226 -93
- package/src/input/onboarding/onboarding-wizard-types.ts +15 -0
- package/src/input/onboarding/onboarding-wizard.ts +123 -6
- package/src/input/settings-modal-types.ts +2 -1
- package/src/input/settings-modal.ts +4 -0
- package/src/main.ts +35 -27
- package/src/renderer/compositor.ts +3 -3
- package/src/renderer/onboarding/onboarding-wizard.ts +141 -57
- package/src/renderer/settings-modal-helpers.ts +9 -0
- package/src/renderer/settings-modal.ts +3 -0
- package/src/runtime/bootstrap.ts +15 -0
- package/src/runtime/onboarding/apply.ts +45 -90
- package/src/runtime/onboarding/derivation.ts +7 -7
- package/src/runtime/onboarding/markers.ts +41 -55
- package/src/runtime/onboarding/snapshot.ts +1 -0
- package/src/runtime/onboarding/state.ts +6 -6
- package/src/runtime/onboarding/types.ts +24 -27
- package/src/runtime/onboarding/verify.ts +3 -65
- package/src/runtime/surface-feature-flags.ts +67 -0
- package/src/version.ts +1 -1
|
@@ -17,19 +17,19 @@ export const DEFAULT_CAPABILITIES: readonly OnboardingStep1CapabilityItem[] = [
|
|
|
17
17
|
id: 'local-tui-only',
|
|
18
18
|
label: 'Local TUI Only (No Servers)',
|
|
19
19
|
selected: true,
|
|
20
|
-
detail: '
|
|
20
|
+
detail: 'Use GoodVibes only in this terminal. No browser access, background service, HTTP listener, external app surface, or network setup.',
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
id: 'browser-access',
|
|
24
24
|
label: 'Open GoodVibes in a Browser',
|
|
25
25
|
selected: false,
|
|
26
|
-
detail: '
|
|
26
|
+
detail: 'Run the background service and web UI. GoodVibes will use the local network by default; you can restrict or customize it next.',
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
id: 'network-access',
|
|
30
30
|
label: 'Let other devices use GoodVibes',
|
|
31
31
|
selected: false,
|
|
32
|
-
detail: '
|
|
32
|
+
detail: 'Make enabled GoodVibes services reachable from other devices on your LAN. Local authentication is required.',
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
id: 'webhook-events',
|
|
@@ -41,7 +41,7 @@ export const DEFAULT_CAPABILITIES: readonly OnboardingStep1CapabilityItem[] = [
|
|
|
41
41
|
id: 'external-integrations',
|
|
42
42
|
label: 'Connect GoodVibes to external apps and services',
|
|
43
43
|
selected: false,
|
|
44
|
-
detail: '
|
|
44
|
+
detail: 'Enable setup screens for Slack, Discord, Telegram, Teams, Matrix, and other app surfaces you choose.',
|
|
45
45
|
},
|
|
46
46
|
];
|
|
47
47
|
|
|
@@ -115,7 +115,6 @@ export const REQUIRED_EXTERNAL_SETUP_FIELD_IDS = new Set<string>([
|
|
|
115
115
|
'external-services.discord.public-key',
|
|
116
116
|
'external-services.telegram.bot-token',
|
|
117
117
|
'external-services.telegram.webhook-secret',
|
|
118
|
-
'external-services.ntfy.topic',
|
|
119
118
|
'external-services.webhook.default-target',
|
|
120
119
|
'external-services.google-chat.webhook-url',
|
|
121
120
|
'external-services.google-chat.verification-token',
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
GOODVIBES_NTFY_AGENT_TOPIC,
|
|
3
|
+
GOODVIBES_NTFY_CHAT_TOPIC,
|
|
4
|
+
GOODVIBES_NTFY_REMOTE_TOPIC,
|
|
5
|
+
resolveGoodVibesNtfyTopics,
|
|
6
|
+
} from '@pellux/goodvibes-sdk/platform/integrations/ntfy';
|
|
7
|
+
import { DEFAULT_CONFIG, type ConfigKey } from '../../config/index.ts';
|
|
2
8
|
import type { OnboardingSnapshotState } from '../../runtime/onboarding/index.ts';
|
|
3
9
|
import { TELEGRAM_MODE_OPTIONS, WHATSAPP_PROVIDER_OPTIONS } from './onboarding-wizard-constants.ts';
|
|
4
10
|
import type { OnboardingWizardRadioOption } from './onboarding-wizard-types.ts';
|
|
@@ -24,10 +30,65 @@ export interface ExternalSurfaceSpec {
|
|
|
24
30
|
readonly enabledConfigKey: ConfigKey;
|
|
25
31
|
readonly label: string;
|
|
26
32
|
readonly hint: string;
|
|
33
|
+
/**
|
|
34
|
+
* Existing SDK config key. In onboarding this maps to the per-surface
|
|
35
|
+
* auto-start choice, not to whether setup fields are shown.
|
|
36
|
+
*/
|
|
27
37
|
readonly defaultEnabled: (snapshot: OnboardingSnapshotState | null) => boolean;
|
|
28
38
|
readonly fields: readonly ExternalSurfaceSetupFieldSpec[];
|
|
29
39
|
}
|
|
30
40
|
|
|
41
|
+
function normalizeConfigValue(value: unknown): string {
|
|
42
|
+
if (value === null || value === undefined) return '';
|
|
43
|
+
return String(value).trim();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getDefaultConfigValue(key: ConfigKey): unknown {
|
|
47
|
+
return key.split('.').reduce<unknown>((cursor, part) => (
|
|
48
|
+
typeof cursor === 'object' && cursor !== null && part in cursor
|
|
49
|
+
? (cursor as Record<string, unknown>)[part]
|
|
50
|
+
: undefined
|
|
51
|
+
), DEFAULT_CONFIG);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function getExternalSurfaceAutoStartFieldId(surface: ExternalSurfaceSpec): string {
|
|
55
|
+
return `${surface.enabledFieldId}.auto-start`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function getExternalSurfaceAutoStartDefaultValue(
|
|
59
|
+
surface: ExternalSurfaceSpec,
|
|
60
|
+
snapshot: OnboardingSnapshotState | null,
|
|
61
|
+
): 'yes' | 'no' {
|
|
62
|
+
return surface.defaultEnabled(snapshot) ? 'yes' : 'no';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function isExternalSurfaceSelectedByDefault(
|
|
66
|
+
surface: ExternalSurfaceSpec,
|
|
67
|
+
snapshot: OnboardingSnapshotState | null,
|
|
68
|
+
): boolean {
|
|
69
|
+
if (surface.defaultEnabled(snapshot)) return true;
|
|
70
|
+
if (!snapshot) return false;
|
|
71
|
+
|
|
72
|
+
return surface.fields.some((field) => {
|
|
73
|
+
const current = normalizeConfigValue(field.defaultValue(snapshot));
|
|
74
|
+
const defaultValue = normalizeConfigValue(getDefaultConfigValue(field.configKey));
|
|
75
|
+
return current.length > 0 && current !== defaultValue;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function getNtfyProtocolTopicLines(snapshot: OnboardingSnapshotState | null): readonly string[] {
|
|
80
|
+
const topics = resolveGoodVibesNtfyTopics({
|
|
81
|
+
chatTopic: snapshot?.config.surfaces.ntfy.chatTopic,
|
|
82
|
+
agentTopic: snapshot?.config.surfaces.ntfy.agentTopic,
|
|
83
|
+
remoteTopic: snapshot?.config.surfaces.ntfy.remoteTopic,
|
|
84
|
+
});
|
|
85
|
+
return [
|
|
86
|
+
`Chat topic: ${topics.chatTopic}`,
|
|
87
|
+
`Agent topic: ${topics.agentTopic}`,
|
|
88
|
+
`Daemon-only remote topic: ${topics.remoteTopic}`,
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
|
|
31
92
|
export const EXTERNAL_SURFACE_SPECS: readonly ExternalSurfaceSpec[] = [
|
|
32
93
|
{
|
|
33
94
|
id: 'slack',
|
|
@@ -200,7 +261,7 @@ export const EXTERNAL_SURFACE_SPECS: readonly ExternalSurfaceSpec[] = [
|
|
|
200
261
|
enabledFieldId: 'external-services.ntfy',
|
|
201
262
|
enabledConfigKey: 'surfaces.ntfy.enabled',
|
|
202
263
|
label: 'ntfy surface',
|
|
203
|
-
hint: '
|
|
264
|
+
hint: 'Configure ntfy chat, agent, remote-session, and notification delivery topics.',
|
|
204
265
|
defaultEnabled: (snapshot) => snapshot?.config.surfaces.ntfy.enabled ?? false,
|
|
205
266
|
fields: [
|
|
206
267
|
{
|
|
@@ -212,12 +273,39 @@ export const EXTERNAL_SURFACE_SPECS: readonly ExternalSurfaceSpec[] = [
|
|
|
212
273
|
placeholder: 'https://ntfy.sh',
|
|
213
274
|
defaultValue: (snapshot) => snapshot?.config.surfaces.ntfy.baseUrl ?? 'https://ntfy.sh',
|
|
214
275
|
},
|
|
276
|
+
{
|
|
277
|
+
id: 'external-services.ntfy.chat-topic',
|
|
278
|
+
configKey: 'surfaces.ntfy.chatTopic',
|
|
279
|
+
kind: 'text',
|
|
280
|
+
label: 'ntfy chat topic',
|
|
281
|
+
hint: 'Messages sent here attach to the active terminal TUI session and reply back to ntfy.',
|
|
282
|
+
placeholder: GOODVIBES_NTFY_CHAT_TOPIC,
|
|
283
|
+
defaultValue: (snapshot) => snapshot?.config.surfaces.ntfy.chatTopic ?? GOODVIBES_NTFY_CHAT_TOPIC,
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
id: 'external-services.ntfy.agent-topic',
|
|
287
|
+
configKey: 'surfaces.ntfy.agentTopic',
|
|
288
|
+
kind: 'text',
|
|
289
|
+
label: 'ntfy agent topic',
|
|
290
|
+
hint: 'Messages sent here start agent work attached to the active TUI session.',
|
|
291
|
+
placeholder: GOODVIBES_NTFY_AGENT_TOPIC,
|
|
292
|
+
defaultValue: (snapshot) => snapshot?.config.surfaces.ntfy.agentTopic ?? GOODVIBES_NTFY_AGENT_TOPIC,
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
id: 'external-services.ntfy.remote-topic',
|
|
296
|
+
configKey: 'surfaces.ntfy.remoteTopic',
|
|
297
|
+
kind: 'text',
|
|
298
|
+
label: 'ntfy daemon-only remote topic',
|
|
299
|
+
hint: 'Messages sent here start an ntfy remote session in the daemon and do not appear in the TUI.',
|
|
300
|
+
placeholder: GOODVIBES_NTFY_REMOTE_TOPIC,
|
|
301
|
+
defaultValue: (snapshot) => snapshot?.config.surfaces.ntfy.remoteTopic ?? GOODVIBES_NTFY_REMOTE_TOPIC,
|
|
302
|
+
},
|
|
215
303
|
{
|
|
216
304
|
id: 'external-services.ntfy.topic',
|
|
217
305
|
configKey: 'surfaces.ntfy.topic',
|
|
218
306
|
kind: 'text',
|
|
219
|
-
label: 'ntfy topic',
|
|
220
|
-
hint: '
|
|
307
|
+
label: 'ntfy default delivery topic',
|
|
308
|
+
hint: 'Optional outbound notification topic. It does not control chat, agent, or daemon-only remote routing.',
|
|
221
309
|
placeholder: 'goodvibes',
|
|
222
310
|
defaultValue: (snapshot) => snapshot?.config.surfaces.ntfy.topic ?? '',
|
|
223
311
|
},
|
|
@@ -227,7 +315,7 @@ export const EXTERNAL_SURFACE_SPECS: readonly ExternalSurfaceSpec[] = [
|
|
|
227
315
|
kind: 'masked',
|
|
228
316
|
label: 'ntfy token',
|
|
229
317
|
hint: 'Optional token for authenticated ntfy servers.',
|
|
230
|
-
placeholder: '
|
|
318
|
+
placeholder: 'empty for anonymous ntfy',
|
|
231
319
|
defaultValue: (snapshot) => snapshot?.config.surfaces.ntfy.token ?? '',
|
|
232
320
|
},
|
|
233
321
|
{
|
|
@@ -209,10 +209,9 @@ export function getRuntimeDerivedState(hydration: OnboardingWizardRuntimeHydrati
|
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
export function getOnboardingWizardBodyRows(viewportHeight: number): number {
|
|
212
|
-
return Math.max(
|
|
212
|
+
return Math.max(6, viewportHeight - 5);
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
export function getOnboardingWizardVisibleFieldCount(viewportHeight: number): number {
|
|
216
|
-
return Math.max(1,
|
|
216
|
+
return Math.max(1, getOnboardingWizardBodyRows(viewportHeight) - 6);
|
|
217
217
|
}
|
|
218
|
-
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { OnboardingStep1CapabilityId } from '../../runtime/onboarding/index.ts';
|
|
2
2
|
import { INBOUND_EXTERNAL_SURFACE_IDS, REQUIRED_EXTERNAL_SETUP_FIELD_IDS } from './onboarding-wizard-constants.ts';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
EXTERNAL_SURFACE_SPECS,
|
|
5
|
+
getExternalSurfaceAutoStartDefaultValue,
|
|
6
|
+
getExternalSurfaceAutoStartFieldId,
|
|
7
|
+
isExternalSurfaceSelectedByDefault,
|
|
8
|
+
} from './onboarding-wizard-external-surfaces.ts';
|
|
4
9
|
import { getExternalSurfaceSpecByFieldId, normalizeText, uniqueNonEmpty } from './onboarding-wizard-helpers.ts';
|
|
5
10
|
import type { OnboardingWizardController } from './onboarding-wizard.ts';
|
|
6
11
|
|
|
@@ -30,10 +35,6 @@ export function getSharedIpHostDefault(
|
|
|
30
35
|
return hosts[0] ?? '0.0.0.0';
|
|
31
36
|
}
|
|
32
37
|
|
|
33
|
-
export function defaultReviewUserMarker(controller: OnboardingWizardController): boolean {
|
|
34
|
-
return controller.mode === 'new';
|
|
35
|
-
}
|
|
36
|
-
|
|
37
38
|
export function toggleCapability(controller: OnboardingWizardController, capabilityId: OnboardingStep1CapabilityId): void {
|
|
38
39
|
if (capabilityId === 'local-tui-only') {
|
|
39
40
|
for (const capability of controller.getCurrentCapabilities()) {
|
|
@@ -69,6 +70,18 @@ export function selectLocalTuiOnly(controller: OnboardingWizardController): void
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
export function selectAllExternalSurfaces(controller: OnboardingWizardController): void {
|
|
74
|
+
for (const surface of EXTERNAL_SURFACE_SPECS) {
|
|
75
|
+
controller.toggleState.set(surface.enabledFieldId, true);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function clearExternalSurfaces(controller: OnboardingWizardController): void {
|
|
80
|
+
for (const surface of EXTERNAL_SURFACE_SPECS) {
|
|
81
|
+
controller.toggleState.set(surface.enabledFieldId, false);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
72
85
|
export function setCapabilityValue(controller: OnboardingWizardController, capabilityId: OnboardingStep1CapabilityId, selected: boolean): void {
|
|
73
86
|
if (capabilityId === 'local-tui-only') {
|
|
74
87
|
if (selected) {
|
|
@@ -116,7 +129,14 @@ export function hasSelectedInboundExternalSurface(controller: OnboardingWizardCo
|
|
|
116
129
|
if (!controller.isCapabilitySelected('external-integrations')) return false;
|
|
117
130
|
return EXTERNAL_SURFACE_SPECS.some((surface) => (
|
|
118
131
|
INBOUND_EXTERNAL_SURFACE_IDS.has(surface.id)
|
|
119
|
-
&& controller.getBooleanFieldValue(
|
|
132
|
+
&& controller.getBooleanFieldValue(
|
|
133
|
+
surface.enabledFieldId,
|
|
134
|
+
isExternalSurfaceSelectedByDefault(surface, controller.runtimeSnapshot),
|
|
135
|
+
)
|
|
136
|
+
&& controller.getStringFieldValue(
|
|
137
|
+
getExternalSurfaceAutoStartFieldId(surface),
|
|
138
|
+
getExternalSurfaceAutoStartDefaultValue(surface, controller.runtimeSnapshot),
|
|
139
|
+
) === 'yes'
|
|
120
140
|
));
|
|
121
141
|
}
|
|
122
142
|
|
|
@@ -125,7 +145,10 @@ export function isRequiredExternalSetupField(controller: OnboardingWizardControl
|
|
|
125
145
|
const surface = getExternalSurfaceSpecByFieldId(fieldId);
|
|
126
146
|
if (!surface) return false;
|
|
127
147
|
if (!controller.isCapabilitySelected('external-integrations')
|
|
128
|
-
|| !controller.getBooleanFieldValue(
|
|
148
|
+
|| !controller.getBooleanFieldValue(
|
|
149
|
+
surface.enabledFieldId,
|
|
150
|
+
isExternalSurfaceSelectedByDefault(surface, controller.runtimeSnapshot),
|
|
151
|
+
)) {
|
|
129
152
|
return false;
|
|
130
153
|
}
|
|
131
154
|
if (fieldId === 'external-services.telegram.webhook-secret') {
|
|
@@ -172,7 +195,7 @@ export function shouldExposeControlPlaneNetwork(controller: OnboardingWizardCont
|
|
|
172
195
|
|
|
173
196
|
export function requiresAuthBootstrap(controller: OnboardingWizardController): boolean {
|
|
174
197
|
return controller.hasServerCapabilitiesSelected()
|
|
175
|
-
&& (!controller.
|
|
198
|
+
&& (!controller.hasLocalAuthUser() || controller.hasBootstrapCredentialPresent());
|
|
176
199
|
}
|
|
177
200
|
|
|
178
201
|
export function hasAdminAuthUser(controller: OnboardingWizardController): boolean {
|
|
@@ -180,12 +203,21 @@ export function hasAdminAuthUser(controller: OnboardingWizardController): boolea
|
|
|
180
203
|
.some((user) => user.roles.includes('admin'));
|
|
181
204
|
}
|
|
182
205
|
|
|
206
|
+
export function hasLocalAuthUser(controller: OnboardingWizardController): boolean {
|
|
207
|
+
return (controller.runtimeSnapshot?.auth.snapshot.userCount ?? 0) > 0
|
|
208
|
+
|| (controller.runtimeSnapshot?.auth.snapshot.users ?? []).length > 0;
|
|
209
|
+
}
|
|
210
|
+
|
|
183
211
|
export function hasBootstrapCredentialPresent(controller: OnboardingWizardController): boolean {
|
|
184
212
|
return controller.runtimeSnapshot?.auth.snapshot.bootstrapCredentialPresent === true;
|
|
185
213
|
}
|
|
186
214
|
|
|
187
215
|
export function getDefaultAdminUsername(controller: OnboardingWizardController): string {
|
|
188
216
|
const users = controller.runtimeSnapshot?.auth.snapshot.users ?? [];
|
|
217
|
+
if (controller.hasBootstrapCredentialPresent()) {
|
|
218
|
+
const existingAdmin = users.find((user) => user.roles.includes('admin'));
|
|
219
|
+
if (existingAdmin) return existingAdmin.username;
|
|
220
|
+
}
|
|
189
221
|
const candidates = controller.hasBootstrapCredentialPresent()
|
|
190
222
|
? ['goodvibes-admin', 'admin']
|
|
191
223
|
: ['admin', 'goodvibes-admin'];
|
|
@@ -72,21 +72,26 @@ export function getFieldValidationError(
|
|
|
72
72
|
if (field.kind !== 'text' && field.kind !== 'masked') return null;
|
|
73
73
|
|
|
74
74
|
const value = normalizeText(controller.getFieldValue(field) as string);
|
|
75
|
-
const required = field.required === true
|
|
75
|
+
const required = field.required === true;
|
|
76
76
|
if (required && value.length === 0) {
|
|
77
77
|
return `${step.shortLabel}: ${field.label} is required.`;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
if (field.id === 'accounts.admin-username') {
|
|
81
|
-
const
|
|
82
|
-
if (
|
|
83
|
-
return `${step.shortLabel}: ${field.label}
|
|
81
|
+
const password = normalizeText(controller.getStringFieldValue('accounts.admin-password', ''));
|
|
82
|
+
if (!required && password.length > 0 && value.length === 0) {
|
|
83
|
+
return `${step.shortLabel}: ${field.label} is required when setting a local auth password.`;
|
|
84
84
|
}
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
const existing = controller.runtimeSnapshot?.auth.snapshot.users.find((user) => user.username === value);
|
|
86
|
+
if ((required || password.length > 0) && existing && !existing.roles.includes('admin')) {
|
|
87
|
+
return `${step.shortLabel}: ${field.label} must be an existing admin user or a new username.`;
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
|
|
91
|
+
if (field.id === 'accounts.admin-password' && value.length > 0 && value.length < 8) {
|
|
92
|
+
return `${step.shortLabel}: ${field.label} must be at least 8 characters.`;
|
|
93
|
+
}
|
|
94
|
+
|
|
90
95
|
if (field.kind === 'masked' && isMalformedGoodVibesSecretReferenceValue(value)) {
|
|
91
96
|
return `${step.shortLabel}: ${field.label} must be a secret value or a goodvibes://secrets/... reference.`;
|
|
92
97
|
}
|
|
@@ -313,14 +318,20 @@ export function isFieldDirtyByDefinition(controller: OnboardingWizardController,
|
|
|
313
318
|
}
|
|
314
319
|
|
|
315
320
|
export function isFieldSatisfied(controller: OnboardingWizardController, field: OnboardingWizardFieldDefinition): boolean {
|
|
316
|
-
if (field.kind === 'checklist'
|
|
317
|
-
|
|
321
|
+
if (field.kind === 'checklist') {
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (field.kind === 'acknowledgement') {
|
|
326
|
+
if (!field.required) return true;
|
|
318
327
|
return Boolean(controller.getFieldValue(field));
|
|
319
328
|
}
|
|
320
329
|
|
|
321
330
|
if (field.kind === 'radio') return true;
|
|
322
331
|
|
|
323
332
|
if (field.kind === 'text' || field.kind === 'masked') {
|
|
333
|
+
const required = field.required === true;
|
|
334
|
+
if (!required) return true;
|
|
324
335
|
return normalizeText(controller.getFieldValue(field) as string).length > 0;
|
|
325
336
|
}
|
|
326
337
|
|