diff --git a/messages/en.context.json b/messages/en.context.json index 88bd92be9f..17124acdc8 100644 --- a/messages/en.context.json +++ b/messages/en.context.json @@ -3098,6 +3098,7 @@ "app-onboarding-name-step-title": "Used in app onboarding details wizard. Role: step title. Translate for UI; keep Capgo product names, code, and placeholders unchanged.", "app-onboarding-remove-icon": "Used in app onboarding details wizard. Role: button label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.", "app-onboarding-skip-app-id": "Used in app onboarding details wizard. Role: button label that accepts Capgo's generated App ID. Translate for UI; keep Capgo product names, code, and placeholders unchanged.", + "app-onboarding-skip-icon": "Used in app onboarding details wizard. Role: button label that continues without an optional app icon. Translate for UI; keep Capgo product names, code, and placeholders unchanged.", "app-onboarding-toast-store-icon-error": "Used in app onboarding details wizard. Role: error toast. Translate for UI; keep Capgo product names, code, and placeholders unchanged.", "app-onboarding-use-different-icon": "Used in app onboarding details wizard. Role: section label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.", "app-onboarding-use-imported-icon": "Used in app onboarding details wizard. Role: button label. Translate for UI; keep Capgo product names, code, and placeholders unchanged.", diff --git a/messages/en.json b/messages/en.json index 14375a1d4a..458c0e15a5 100644 --- a/messages/en.json +++ b/messages/en.json @@ -518,6 +518,7 @@ "app-onboarding-store-link-placeholder": "https://apps.apple.com/... or https://play.google.com/store/apps/details?id=com.example.app", "app-onboarding-store-screenshot-alt": "Store screenshot preview", "app-onboarding-skip-app-id": "Skip", + "app-onboarding-skip-icon": "Skip", "app-onboarding-subtitle": "The app is created immediately in Capgo. From there you can either connect your real project in the CLI or explore the dashboard with temporary demo data.", "app-onboarding-summary-method": "Setup method", "app-onboarding-summary-source": "Starting point", diff --git a/src/components/dashboard/AppOnboardingFlow.vue b/src/components/dashboard/AppOnboardingFlow.vue index 1f94bcbc47..7914c4656e 100644 --- a/src/components/dashboard/AppOnboardingFlow.vue +++ b/src/components/dashboard/AppOnboardingFlow.vue @@ -304,6 +304,15 @@ const suggestedAppId = computed(() => { }) const generatedAppId = computed(() => createdApp.value?.app_id || manualAppId.value.trim() || suggestedAppId.value) const hasProvidedAppId = computed(() => Boolean(manualAppId.value.trim() || importedStoreAppId.value.trim())) +const appDetailsPrimaryActionLabel = computed(() => { + if (appDetailsStep.value === 'icon') + return iconPreview.value ? t('app-onboarding-continue') : t('app-onboarding-skip-icon') + + if (appDetailsStep.value === 'app_id' && !hasProvidedAppId.value) + return t('app-onboarding-skip-app-id') + + return t('app-onboarding-continue') +}) const appNameInitial = computed(() => Array.from(appName.value.trim())[0]?.toLocaleUpperCase() ?? '') const selectedAppIdSource = computed>(() => { if (manualAppId.value.trim()) @@ -2446,13 +2455,7 @@ defineExpose({ @click="continueFromCurrentAppDetailsStep" > - {{ appDetailsStep === 'icon' - ? props.preOrg - ? t('app-onboarding-continue') - : t('app-onboarding-finish-details') - : appDetailsStep === 'app_id' && !hasProvidedAppId - ? t('app-onboarding-skip-app-id') - : t('app-onboarding-continue') }} + {{ appDetailsPrimaryActionLabel }} diff --git a/src/components/dashboard/AppOnboardingWelcome.vue b/src/components/dashboard/AppOnboardingWelcome.vue index 9bf3b137f7..8ca18b85ca 100644 --- a/src/components/dashboard/AppOnboardingWelcome.vue +++ b/src/components/dashboard/AppOnboardingWelcome.vue @@ -59,8 +59,8 @@ const { t } = useI18n() .onboarding-welcome { isolation: isolate; background: - radial-gradient(circle at 50% 40%, rgb(59 130 246 / 12%), transparent 29rem), - linear-gradient(180deg, #fff 0%, #f8fafc 58%, #f1f5f9 100%); + radial-gradient(circle at 50% 40%, rgb(59 130 246 / 14%), transparent 29rem), + linear-gradient(180deg, var(--color-base-100) 0%, var(--color-base-200) 58%, var(--color-base-300) 100%); } .onboarding-welcome::before, @@ -177,12 +177,6 @@ const { t } = useI18n() z-index: 1; } -:global(.dark) .onboarding-welcome { - background: - radial-gradient(circle at 50% 40%, rgb(59 130 246 / 18%), transparent 29rem), - linear-gradient(180deg, #020617 0%, #0f172a 58%, #111827 100%); -} - @keyframes onboarding-status-pulse { 0%, 34% { diff --git a/tests/app-onboarding-v3.unit.test.ts b/tests/app-onboarding-v3.unit.test.ts index fbbc3eb27f..e750524442 100644 --- a/tests/app-onboarding-v3.unit.test.ts +++ b/tests/app-onboarding-v3.unit.test.ts @@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest' const onboardingSource = readFileSync(new URL('../src/components/dashboard/AppOnboardingFlow.vue', import.meta.url), 'utf8') const iconInputSource = readFileSync(new URL('../src/components/dashboard/AppOnboardingIconInput.vue', import.meta.url), 'utf8') +const messages = JSON.parse(readFileSync(new URL('../messages/en.json', import.meta.url), 'utf8')) as Record function sliceBetween(source: string, startMarker: string, endMarker: string) { const start = source.indexOf(startMarker) @@ -152,9 +153,17 @@ describe('pre-organization onboarding v3', () => { expect(onboardingSource.match(/:disabled="isAppDetailsNavigationPending"/g)).toHaveLength(2) }) - it.concurrent('continues to organization creation after the pre-org icon step', () => { - expect(onboardingSource).toContain("t('app-onboarding-continue')") - expect(onboardingSource).toContain("t('app-onboarding-finish-details')") + it.concurrent('labels the icon action as skip until an icon is provided', () => { + const iconAction = sliceBetween(onboardingSource, ':data-test="appDetailsStep === \'app_id\'', ' {', 'const appNameInitial = computed') + + expect(messages['app-onboarding-skip-icon']).toBe('Skip') + expect(primaryActionLabel).toContain("if (appDetailsStep.value === 'icon')") + expect(primaryActionLabel).toContain("return iconPreview.value ? t('app-onboarding-continue') : t('app-onboarding-skip-icon')") + expect(primaryActionLabel).toContain("if (appDetailsStep.value === 'app_id' && !hasProvidedAppId.value)") + expect(primaryActionLabel).toContain("return t('app-onboarding-skip-app-id')") + expect(iconAction).toContain('{{ appDetailsPrimaryActionLabel }}') + expect(iconAction).not.toContain("t('app-onboarding-finish-details')") }) it.concurrent('does not send raw onboarding field values to analytics', () => { diff --git a/tests/app-onboarding-welcome.unit.test.ts b/tests/app-onboarding-welcome.unit.test.ts index 7bcbff09cf..b30afde7be 100644 --- a/tests/app-onboarding-welcome.unit.test.ts +++ b/tests/app-onboarding-welcome.unit.test.ts @@ -25,6 +25,17 @@ describe('app onboarding welcome', () => { expect(source).not.toContain('Takes about') }) + it.concurrent('uses DaisyUI theme colors for the welcome canvas', async () => { + const source = await readFile(new URL('../src/components/dashboard/AppOnboardingWelcome.vue', import.meta.url), 'utf8') + const welcomeRule = source.match(/\.onboarding-welcome\s*\{([\s\S]*?)\n\}/)?.[1] ?? '' + const backgroundDeclaration = welcomeRule.match(/background:\s*([\s\S]*?);/)?.[1] ?? '' + + expect(backgroundDeclaration).toContain('var(--color-base-100)') + expect(backgroundDeclaration).toContain('var(--color-base-200)') + expect(backgroundDeclaration).toContain('var(--color-base-300)') + expect(source).not.toContain(':global(.dark) .onboarding-welcome') + }) + it.concurrent('shows only for fresh or restarted desktop pre-organization onboarding', async () => { const source = await readFile(new URL('../src/components/dashboard/AppOnboardingFlow.vue', import.meta.url), 'utf8') const resumeFlow = source.slice(