* 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>
186 lines
7.5 KiB
Vue
186 lines
7.5 KiB
Vue
<script setup lang="ts">
|
|
import { Form, Head, usePage } from '@inertiajs/vue3';
|
|
import { IconEye, IconEyeOff } from '@tabler/icons-vue';
|
|
import { computed, ref } from 'vue';
|
|
|
|
import SocialLogin from '@/components/auth/SocialLogin.vue';
|
|
import InputError from '@/components/InputError.vue';
|
|
import TextLink from '@/components/TextLink.vue';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Checkbox } from '@/components/ui/checkbox';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Label } from '@/components/ui/label';
|
|
import { Spinner } from '@/components/ui/spinner';
|
|
import {
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipProvider,
|
|
TooltipTrigger,
|
|
} from '@/components/ui/tooltip';
|
|
import AuthBase from '@/layouts/AuthLayout.vue';
|
|
import { register } from '@/routes';
|
|
import { store } from '@/routes/login';
|
|
import { request } from '@/routes/password';
|
|
|
|
defineProps<{
|
|
status?: string;
|
|
email?: string | null;
|
|
redirect?: string | null;
|
|
}>();
|
|
|
|
const showPassword = ref(false);
|
|
|
|
const page = usePage();
|
|
const isSelfHosted = computed(() => Boolean(page.props.selfHosted));
|
|
</script>
|
|
|
|
<template>
|
|
<AuthBase
|
|
:title="$t('auth.login.title')"
|
|
:description="$t('auth.login.description')"
|
|
>
|
|
<Head :title="$t('auth.login.page_title')" />
|
|
|
|
<div
|
|
v-if="status"
|
|
class="mb-4 text-center text-sm font-medium text-green-600"
|
|
>
|
|
{{ status }}
|
|
</div>
|
|
|
|
<div class="flex flex-col gap-6">
|
|
<SocialLogin mode="login" />
|
|
|
|
<Form
|
|
v-bind="store.form()"
|
|
:reset-on-success="['password']"
|
|
v-slot="{ errors, processing }"
|
|
class="flex flex-col gap-6"
|
|
>
|
|
<input
|
|
v-if="redirect"
|
|
type="hidden"
|
|
name="redirect"
|
|
:value="redirect"
|
|
/>
|
|
<div class="grid gap-6">
|
|
<div class="grid gap-2">
|
|
<Label for="email">{{ $t('auth.login.email') }}</Label>
|
|
<Input
|
|
id="email"
|
|
type="email"
|
|
name="email"
|
|
autofocus
|
|
:tabindex="1"
|
|
autocomplete="email"
|
|
placeholder="email@example.com"
|
|
:default-value="email ?? ''"
|
|
/>
|
|
<InputError :message="errors.email" />
|
|
</div>
|
|
|
|
<div class="grid gap-2">
|
|
<div class="flex items-center justify-between">
|
|
<Label for="password">{{
|
|
$t('auth.login.password')
|
|
}}</Label>
|
|
<TextLink
|
|
:href="request()"
|
|
class="text-sm"
|
|
:tabindex="5"
|
|
>
|
|
{{ $t('auth.login.forgot_password') }}
|
|
</TextLink>
|
|
</div>
|
|
<div class="relative">
|
|
<Input
|
|
id="password"
|
|
:type="showPassword ? 'text' : 'password'"
|
|
name="password"
|
|
:tabindex="2"
|
|
autocomplete="current-password"
|
|
:placeholder="$t('auth.login.password')"
|
|
/>
|
|
<div
|
|
class="absolute inset-y-0 end-0 flex items-center pe-3"
|
|
>
|
|
<TooltipProvider>
|
|
<Tooltip>
|
|
<TooltipTrigger as-child>
|
|
<button
|
|
type="button"
|
|
:tabindex="-1"
|
|
class="cursor-pointer text-muted-foreground hover:text-foreground"
|
|
@click="
|
|
showPassword = !showPassword
|
|
"
|
|
>
|
|
<IconEyeOff
|
|
v-if="showPassword"
|
|
class="size-4"
|
|
/>
|
|
<IconEye
|
|
v-else
|
|
class="size-4"
|
|
/>
|
|
</button>
|
|
</TooltipTrigger>
|
|
<TooltipContent>
|
|
<p>
|
|
{{
|
|
showPassword
|
|
? $t(
|
|
'auth.login.hide_password',
|
|
)
|
|
: $t(
|
|
'auth.login.show_password',
|
|
)
|
|
}}
|
|
</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
</div>
|
|
</div>
|
|
<InputError :message="errors.password" />
|
|
</div>
|
|
|
|
<div class="flex items-center justify-between">
|
|
<Label
|
|
for="remember"
|
|
class="flex items-center space-x-3"
|
|
>
|
|
<Checkbox
|
|
id="remember"
|
|
name="remember"
|
|
:tabindex="3"
|
|
/>
|
|
<span>{{ $t('auth.login.remember_me') }}</span>
|
|
</Label>
|
|
</div>
|
|
|
|
<Button
|
|
type="submit"
|
|
class="mt-4 w-full"
|
|
:tabindex="4"
|
|
:disabled="processing"
|
|
data-test="login-button"
|
|
>
|
|
<Spinner v-if="processing" />
|
|
{{ $t('auth.login.submit') }}
|
|
</Button>
|
|
</div>
|
|
|
|
<div
|
|
v-if="!isSelfHosted"
|
|
class="text-center text-sm text-muted-foreground"
|
|
>
|
|
{{ $t('auth.login.no_account') }}
|
|
<TextLink :href="register()" :tabindex="5">{{
|
|
$t('auth.login.sign_up')
|
|
}}</TextLink>
|
|
</div>
|
|
</Form>
|
|
</div>
|
|
</AuthBase>
|
|
</template>
|