* Rename pre-subscription onboarding funnel to Welcome. Move the ICP steps to /welcome, drop the social-connect checkout gate, hold unpaid members on a subscription-required screen, and keep legacy /onboarding URLs working until the post-subscription checklist lands. Closes #237 Co-authored-by: Cursor <cursoragent@cursor.com> * Drop legacy /onboarding ICP URL aliases. Unfinished users re-enter Welcome via EnsureAccountReady on next login; /onboarding stays free for the post-subscription checklist. Co-authored-by: Cursor <cursoragent@cursor.com> * Simplify Welcome PostHog event names. Use welcome.persona/goals/referral and drop the unused checkout case — begin checkout stays on the frontend as checkout.started / begin_checkout. Co-authored-by: Cursor <cursoragent@cursor.com> * Slim welcome goal options to match the #204 set. Drop team_collaboration, automate_api, and track_performance so the goals step stays at nine choices. Co-authored-by: Cursor <cursoragent@cursor.com> * Split Welcome AI goals into TryPost AI and MCP assistants. Rewrite ai_content for in-app generation and add use_mcp so Claude/ChatGPT/Cursor intent is captured separately across locales. Co-authored-by: Cursor <cursoragent@cursor.com> * Harden Welcome goals gate and drop dead checkout UI. Treat removed goal values as incomplete so mid-funnel users re-select, remove the unused canCheckout branch, and fix the pt-BR welcome progress label. Co-authored-by: Cursor <cursoragent@cursor.com> * Add password visibility toggle to the login form. Match the register eye control so users can reveal their password while signing in. Co-authored-by: Cursor <cursoragent@cursor.com> * Point the sidebar community link to Discord. Replace the X stay-updated entry with Join Discord and the trypost.it/discord invite. Co-authored-by: Cursor <cursoragent@cursor.com> * Rename the sidebar Discord link to Discord community. Softer label that matches the other support nav items. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix broken Turkish Discord community translation. An unescaped apostrophe left a parse error in lang/tr/sidebar.php. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
169 lines
4.8 KiB
Vue
169 lines
4.8 KiB
Vue
<script setup lang="ts">
|
|
import { Head, useForm } from '@inertiajs/vue3';
|
|
import {
|
|
IconBriefcase,
|
|
IconBuildingSkyscraper,
|
|
IconBuildingStore,
|
|
IconCheck,
|
|
IconCode,
|
|
IconDots,
|
|
IconRocket,
|
|
IconShoppingBag,
|
|
IconSpeakerphone,
|
|
IconUser,
|
|
} from '@tabler/icons-vue';
|
|
import { trans } from 'laravel-vue-i18n';
|
|
import type { FunctionalComponent } from 'vue';
|
|
|
|
import InputError from '@/components/InputError.vue';
|
|
import { Button } from '@/components/ui/button';
|
|
import WelcomeLayout from '@/layouts/WelcomeLayout.vue';
|
|
import { store } from '@/routes/app/welcome/persona';
|
|
|
|
const props = defineProps<{
|
|
personas: string[];
|
|
selected?: string | null;
|
|
}>();
|
|
|
|
const form = useForm({ persona: props.selected ?? '' });
|
|
|
|
const personaMeta: Record<
|
|
string,
|
|
{ icon: FunctionalComponent; iconClass: string; badge: string }
|
|
> = {
|
|
creator: {
|
|
icon: IconUser,
|
|
iconClass: 'text-rose-700',
|
|
badge: 'bg-rose-100',
|
|
},
|
|
freelancer: {
|
|
icon: IconBriefcase,
|
|
iconClass: 'text-amber-700',
|
|
badge: 'bg-amber-100',
|
|
},
|
|
developer: {
|
|
icon: IconCode,
|
|
iconClass: 'text-cyan-700',
|
|
badge: 'bg-cyan-100',
|
|
},
|
|
startup: {
|
|
icon: IconRocket,
|
|
iconClass: 'text-violet-700',
|
|
badge: 'bg-violet-100',
|
|
},
|
|
agency: {
|
|
icon: IconBuildingSkyscraper,
|
|
iconClass: 'text-blue-700',
|
|
badge: 'bg-blue-100',
|
|
},
|
|
small_business: {
|
|
icon: IconBuildingStore,
|
|
iconClass: 'text-emerald-700',
|
|
badge: 'bg-emerald-100',
|
|
},
|
|
marketer: {
|
|
icon: IconSpeakerphone,
|
|
iconClass: 'text-fuchsia-700',
|
|
badge: 'bg-fuchsia-100',
|
|
},
|
|
online_store: {
|
|
icon: IconShoppingBag,
|
|
iconClass: 'text-teal-700',
|
|
badge: 'bg-teal-100',
|
|
},
|
|
other: {
|
|
icon: IconDots,
|
|
iconClass: 'text-foreground',
|
|
badge: 'bg-muted',
|
|
},
|
|
};
|
|
|
|
const metaFor = (value: string) =>
|
|
personaMeta[value] ?? {
|
|
icon: IconDots,
|
|
iconClass: 'text-foreground',
|
|
badge: 'bg-muted',
|
|
};
|
|
|
|
const personaLabel = (value: string): string =>
|
|
trans(`welcome.personas.${value}`);
|
|
|
|
const select = (value: string): void => {
|
|
form.persona = value;
|
|
};
|
|
|
|
const submit = (): void => {
|
|
if (!form.persona || form.processing) {
|
|
return;
|
|
}
|
|
|
|
form.submit(store());
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<Head :title="$t('welcome.title')" />
|
|
|
|
<WelcomeLayout
|
|
:title="$t('welcome.title')"
|
|
:description="$t('welcome.description')"
|
|
:step="1"
|
|
wide
|
|
>
|
|
<div class="flex flex-wrap justify-center gap-2.5">
|
|
<button
|
|
v-for="persona in personas"
|
|
:key="persona"
|
|
type="button"
|
|
:aria-pressed="form.persona === persona"
|
|
:data-testid="`welcome-persona-${persona}`"
|
|
:class="[
|
|
'inline-flex cursor-pointer items-center gap-3 rounded-full border-2 border-foreground py-2.5 ps-2.5 pe-5 text-start shadow-2xs transition-shadow hover:shadow-md',
|
|
form.persona === persona ? 'bg-violet-100' : 'bg-card',
|
|
]"
|
|
@click="select(persona)"
|
|
>
|
|
<span
|
|
:class="[
|
|
'inline-flex size-11 shrink-0 items-center justify-center rounded-full border-2 border-foreground shadow-2xs',
|
|
metaFor(persona).badge,
|
|
]"
|
|
>
|
|
<component
|
|
:is="metaFor(persona).icon"
|
|
:class="[metaFor(persona).iconClass, 'size-6']"
|
|
stroke-width="2"
|
|
/>
|
|
</span>
|
|
<span
|
|
class="text-sm font-bold tracking-tight text-foreground sm:text-base"
|
|
>
|
|
{{ personaLabel(persona) }}
|
|
</span>
|
|
<span
|
|
v-if="form.persona === persona"
|
|
class="inline-flex size-5 shrink-0 items-center justify-center rounded-full border-2 border-foreground bg-foreground"
|
|
>
|
|
<IconCheck
|
|
class="size-3 text-background"
|
|
stroke-width="3"
|
|
/>
|
|
</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="mx-auto flex w-full max-w-sm flex-col items-center gap-3">
|
|
<InputError :message="form.errors.persona" />
|
|
<Button
|
|
type="button"
|
|
size="lg"
|
|
class="w-full rounded-full"
|
|
:disabled="!form.persona || form.processing"
|
|
data-testid="welcome-persona-continue"
|
|
@click="submit"
|
|
>
|
|
{{ $t('welcome.continue') }}
|
|
</Button>
|
|
</div>
|
|
</WelcomeLayout>
|
|
</template>
|