improve signup container

This commit is contained in:
2025-09-10 06:34:27 +03:00
parent b8a8eab87c
commit 8b3e58e833
15 changed files with 92 additions and 40 deletions

View File

@@ -57,10 +57,9 @@ async function SignUpPage({ searchParams }: Props) {
</div> </div>
<SignUpMethodsContainer <SignUpMethodsContainer
providers={authConfig.providers} authConfig={authConfig}
inviteToken={inviteToken} inviteToken={inviteToken}
paths={paths} paths={paths}
displayTermsCheckbox={authConfig.displayTermsCheckbox}
/> />
<div className={'flex justify-center'}> <div className={'flex justify-center'}>

View File

@@ -59,10 +59,10 @@ export function UpdateAccountForm({
const loading = toast.loading(t('updateAccount.updateAccountLoading')); const loading = toast.loading(t('updateAccount.updateAccountLoading'));
try { try {
const response = await onUpdateAccount({ const response = await onUpdateAccount({
firstName: hasFirstName ? firstName : values.firstName, firstName: values.firstName || firstName,
lastName: hasLastName ? lastName : values.lastName, lastName: values.lastName || lastName,
personalCode: hasPersonalCode ? personalCode : values.personalCode, personalCode: values.personalCode || personalCode,
email: hasEmail ? email : values.email, email: values.email || email,
phone: values.phone, phone: values.phone,
weight: ((("weight" in values && values.weight) ?? defaultValues_weight) || null) as number, weight: ((("weight" in values && values.weight) ?? defaultValues_weight) || null) as number,
height: ((("height" in values && values.height) ?? defaultValues_height) || null) as number, height: ((("height" in values && values.height) ?? defaultValues_height) || null) as number,

View File

@@ -7,9 +7,8 @@ export function AuthLayoutShell({
return ( return (
<div <div
className={ className={
'sm:py-auto flex flex-col items-center justify-center py-6' + 'sm:py-auto flex flex-col items-center justify-center py-6 h-screen' +
' bg-background lg:bg-muted/30 gap-y-10 lg:gap-y-8' + ' bg-background lg:bg-muted/30 gap-y-10 lg:gap-y-8'
' animate-in fade-in slide-in-from-top-16 zoom-in-95 duration-1000'
} }
> >
{Logo ? <Logo /> : null} {Logo ? <Logo /> : null}

View File

@@ -113,12 +113,16 @@ export const OauthProviders: React.FC<{
); );
}} }}
> >
{provider === 'keycloak' ? (
<Trans i18nKey={'auth:signInWithKeycloak'} />
) : (
<Trans <Trans
i18nKey={'auth:signInWithProvider'} i18nKey={'auth:signInWithProvider'}
values={{ values={{
provider: getProviderName(provider), provider: getProviderName(provider),
}} }}
/> />
)}
</AuthProviderButton> </AuthProviderButton>
); );
})} })}

View File

@@ -10,9 +10,18 @@ import { useCaptchaToken } from '../captcha/client';
import { usePasswordSignUpFlow } from '../hooks/use-sign-up-flow'; import { usePasswordSignUpFlow } from '../hooks/use-sign-up-flow';
import { AuthErrorAlert } from './auth-error-alert'; import { AuthErrorAlert } from './auth-error-alert';
import { PasswordSignUpForm } from './password-sign-up-form'; import { PasswordSignUpForm } from './password-sign-up-form';
import { Spinner } from '@kit/ui/makerkit/spinner';
interface EmailPasswordSignUpContainerProps { interface EmailPasswordSignUpContainerProps {
displayTermsCheckbox?: boolean; authConfig: {
providers: {
password: boolean;
magicLink: boolean;
oAuth: string[];
};
displayTermsCheckbox: boolean | undefined;
isMailerAutoconfirmEnabled: boolean;
};
defaultValues?: { defaultValues?: {
email: string; email: string;
}; };
@@ -21,10 +30,10 @@ interface EmailPasswordSignUpContainerProps {
} }
export function EmailPasswordSignUpContainer({ export function EmailPasswordSignUpContainer({
authConfig,
defaultValues, defaultValues,
onSignUp, onSignUp,
emailRedirectTo, emailRedirectTo,
displayTermsCheckbox,
}: EmailPasswordSignUpContainerProps) { }: EmailPasswordSignUpContainerProps) {
const { captchaToken, resetCaptchaToken } = useCaptchaToken(); const { captchaToken, resetCaptchaToken } = useCaptchaToken();
@@ -43,7 +52,12 @@ export function EmailPasswordSignUpContainer({
return ( return (
<> <>
<If condition={showVerifyEmailAlert}> <If condition={showVerifyEmailAlert}>
<SuccessAlert /> {authConfig.isMailerAutoconfirmEnabled ? (
<div className="flex justify-center">
<Spinner />
</div>
) : <SuccessAlert />
}
</If> </If>
<If condition={!showVerifyEmailAlert}> <If condition={!showVerifyEmailAlert}>
@@ -53,7 +67,7 @@ export function EmailPasswordSignUpContainer({
onSubmit={onSignupRequested} onSubmit={onSignupRequested}
loading={loading} loading={loading}
defaultValues={defaultValues} defaultValues={defaultValues}
displayTermsCheckbox={displayTermsCheckbox} displayTermsCheckbox={authConfig.displayTermsCheckbox}
/> />
</If> </If>
</> </>

View File

@@ -1,6 +1,7 @@
'use client'; 'use client';
import type { Provider } from '@supabase/supabase-js'; import type { Provider } from '@supabase/supabase-js';
import { useRouter } from 'next/navigation';
import { isBrowser } from '@kit/shared/utils'; import { isBrowser } from '@kit/shared/utils';
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert'; import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
@@ -19,15 +20,20 @@ export function SignUpMethodsContainer(props: {
updateAccount: string; updateAccount: string;
}; };
authConfig: {
providers: { providers: {
password: boolean; password: boolean;
magicLink: boolean; magicLink: boolean;
oAuth: Provider[]; oAuth: Provider[];
}; };
displayTermsCheckbox: boolean | undefined;
isMailerAutoconfirmEnabled: boolean;
};
displayTermsCheckbox?: boolean;
inviteToken?: string; inviteToken?: string;
}) { }) {
const router = useRouter();
const redirectUrl = getCallbackUrl(props); const redirectUrl = getCallbackUrl(props);
const defaultValues = getDefaultValues(); const defaultValues = getDefaultValues();
@@ -37,26 +43,33 @@ export function SignUpMethodsContainer(props: {
<InviteAlert /> <InviteAlert />
</If> </If>
<If condition={props.providers.password}> <If condition={props.authConfig.providers.password}>
<EmailPasswordSignUpContainer <EmailPasswordSignUpContainer
emailRedirectTo={props.paths.callback} emailRedirectTo={props.paths.callback}
defaultValues={defaultValues} defaultValues={defaultValues}
displayTermsCheckbox={props.displayTermsCheckbox} authConfig={props.authConfig}
//onSignUp={() => redirect(redirectUrl)} onSignUp={() => {
if (!props.authConfig.isMailerAutoconfirmEnabled) {
return;
}
setTimeout(() => {
router.replace(props.paths.updateAccount)
}, 2_500);
}}
/> />
</If> </If>
<If condition={props.providers.magicLink}> <If condition={props.authConfig.providers.magicLink}>
<MagicLinkAuthContainer <MagicLinkAuthContainer
inviteToken={props.inviteToken} inviteToken={props.inviteToken}
redirectUrl={redirectUrl} redirectUrl={redirectUrl}
shouldCreateUser={true} shouldCreateUser={true}
defaultValues={defaultValues} defaultValues={defaultValues}
displayTermsCheckbox={props.displayTermsCheckbox} displayTermsCheckbox={props.authConfig.displayTermsCheckbox}
/> />
</If> </If>
<If condition={props.providers.oAuth.length}> <If condition={props.authConfig.providers.oAuth.length}>
<div className="relative"> <div className="relative">
<div className="absolute inset-0 flex items-center"> <div className="absolute inset-0 flex items-center">
<Separator /> <Separator />
@@ -70,7 +83,7 @@ export function SignUpMethodsContainer(props: {
</div> </div>
<OauthProviders <OauthProviders
enabledProviders={props.providers.oAuth} enabledProviders={props.authConfig.providers.oAuth}
inviteToken={props.inviteToken} inviteToken={props.inviteToken}
shouldCreateUser={true} shouldCreateUser={true}
paths={{ paths={{

View File

@@ -23,7 +23,7 @@ export function InfoTooltip({
<TooltipTrigger> <TooltipTrigger>
{icon || <Info className="size-4 cursor-pointer" />} {icon || <Info className="size-4 cursor-pointer" />}
</TooltipTrigger> </TooltipTrigger>
<TooltipContent className='sm:max-w-[400px]'>{content}</TooltipContent> <TooltipContent className='max-w-[90vw] sm:max-w-[400px]'>{content}</TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
); );

View File

@@ -5,6 +5,7 @@ type SupabaseExternalProvider = Provider | 'email';
interface SupabaseAuthSettings { interface SupabaseAuthSettings {
external: Record<SupabaseExternalProvider, boolean>; external: Record<SupabaseExternalProvider, boolean>;
disable_signup: boolean; disable_signup: boolean;
mailer_autoconfirm: boolean;
} }
export class AuthProvidersService { export class AuthProvidersService {
@@ -61,6 +62,10 @@ export class AuthProvidersService {
return process.env.NEXT_PUBLIC_AUTH_PASSWORD === 'true'; return process.env.NEXT_PUBLIC_AUTH_PASSWORD === 'true';
} }
isMailerAutoconfirmEnabled({ settings }: { settings: SupabaseAuthSettings | null }): boolean {
return settings?.mailer_autoconfirm === true;
}
isMagicLinkEnabled(): boolean { isMagicLinkEnabled(): boolean {
return process.env.NEXT_PUBLIC_AUTH_MAGIC_LINK === 'true'; return process.env.NEXT_PUBLIC_AUTH_MAGIC_LINK === 'true';
} }
@@ -105,10 +110,11 @@ export class AuthProvidersService {
async getAuthConfig() { async getAuthConfig() {
const settings = await this.fetchAuthSettings(); const settings = await this.fetchAuthSettings();
const [passwordEnabled, magicLinkEnabled, oAuthProviders] = await Promise.all([ const [passwordEnabled, magicLinkEnabled, oAuthProviders, isMailerAutoconfirmEnabled] = await Promise.all([
this.isPasswordEnabled({ settings }), this.isPasswordEnabled({ settings }),
this.isMagicLinkEnabled(), this.isMagicLinkEnabled(),
this.getEnabledOAuthProviders({ settings }), this.getEnabledOAuthProviders({ settings }),
this.isMailerAutoconfirmEnabled({ settings }),
]); ]);
return { return {
@@ -118,6 +124,7 @@ export class AuthProvidersService {
oAuth: oAuthProviders, oAuth: oAuthProviders,
}, },
displayTermsCheckbox: authConfig.displayTermsCheckbox, displayTermsCheckbox: authConfig.displayTermsCheckbox,
isMailerAutoconfirmEnabled,
}; };
} }

View File

@@ -11,8 +11,19 @@ const DynamicAuthConfigSchema = z.object({
oAuth: providers.array(), oAuth: providers.array(),
}), }),
displayTermsCheckbox: z.boolean().describe('Whether to display the terms checkbox during sign-up.'), displayTermsCheckbox: z.boolean().describe('Whether to display the terms checkbox during sign-up.'),
isMailerAutoconfirmEnabled: z.boolean().describe('Whether Supabase sends confirmation email automatically.'),
}); });
export type DynamicAuthConfig = {
providers: {
password: boolean;
magicLink: boolean;
oAuth: Provider[];
};
displayTermsCheckbox: boolean | undefined;
isMailerAutoconfirmEnabled: boolean;
}
export async function getDynamicAuthConfig() { export async function getDynamicAuthConfig() {
const authService = createAuthProvidersService(); const authService = createAuthProvidersService();
const dynamicProviders = await authService.getAuthConfig(); const dynamicProviders = await authService.getAuthConfig();
@@ -20,6 +31,7 @@ export async function getDynamicAuthConfig() {
const config = { const config = {
providers: dynamicProviders.providers, providers: dynamicProviders.providers,
displayTermsCheckbox: dynamicProviders.displayTermsCheckbox, displayTermsCheckbox: dynamicProviders.displayTermsCheckbox,
isMailerAutoconfirmEnabled: dynamicProviders.isMailerAutoconfirmEnabled,
}; };
return DynamicAuthConfigSchema.parse(config); return DynamicAuthConfigSchema.parse(config);

View File

@@ -8,7 +8,7 @@ import {
createPath, createPath,
getTeamAccountSidebarConfig, getTeamAccountSidebarConfig,
} from './team-account-navigation.config'; } from './team-account-navigation.config';
import { getCachedAuthConfig, getServerAuthConfig } from './dynamic-auth.config'; import { DynamicAuthConfig, getCachedAuthConfig, getServerAuthConfig } from './dynamic-auth.config';
export { export {
appConfig, appConfig,
@@ -21,4 +21,5 @@ export {
personalAccountNavigationConfig, personalAccountNavigationConfig,
getCachedAuthConfig, getCachedAuthConfig,
getServerAuthConfig, getServerAuthConfig,
type DynamicAuthConfig,
}; };

View File

@@ -42,7 +42,7 @@ function PageWithSidebar(props: PageProps) {
> >
{MobileNavigation} {MobileNavigation}
<div className={'bg-background flex flex-1 flex-col px-4 pb-8 lg:px-0'}> <div className={'bg-background flex flex-1 flex-col px-2 pb-8 lg:px-0'}>
{Children} {Children}
</div> </div>
</div> </div>

View File

@@ -34,7 +34,7 @@ const CardHeader: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({
className, className,
...props ...props
}) => ( }) => (
<div className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} /> <div className={cn('flex flex-col space-y-1.5 p-4 sm:p-6', className)} {...props} />
); );
CardHeader.displayName = 'CardHeader'; CardHeader.displayName = 'CardHeader';
@@ -60,14 +60,14 @@ CardDescription.displayName = 'CardDescription';
const CardContent: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({ const CardContent: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({
className, className,
...props ...props
}) => <div className={cn('p-6 pt-0', className)} {...props} />; }) => <div className={cn('p-4 sm:p-6 pt-0', className)} {...props} />;
CardContent.displayName = 'CardContent'; CardContent.displayName = 'CardContent';
const CardFooter: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({ const CardFooter: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({
className, className,
...props ...props
}) => ( }) => (
<div className={cn('flex items-center p-6 pt-0', className)} {...props} /> <div className={cn('flex items-center p-4 sm:p-6 pt-0', className)} {...props} />
); );
CardFooter.displayName = 'CardFooter'; CardFooter.displayName = 'CardFooter';

View File

@@ -22,6 +22,7 @@
"alreadyHaveAccountStatement": "I already have an account, I want to sign in instead", "alreadyHaveAccountStatement": "I already have an account, I want to sign in instead",
"doNotHaveAccountStatement": "I do not have an account, I want to sign up instead", "doNotHaveAccountStatement": "I do not have an account, I want to sign up instead",
"signInWithProvider": "Sign in with {{provider}}", "signInWithProvider": "Sign in with {{provider}}",
"signInWithKeycloak": "Smart-ID/Mobile-ID/ID-card",
"signInWithPhoneNumber": "Sign in with Phone Number", "signInWithPhoneNumber": "Sign in with Phone Number",
"signInWithEmail": "Sign in with Email", "signInWithEmail": "Sign in with Email",
"signUpWithEmail": "Sign up with Email", "signUpWithEmail": "Sign up with Email",

View File

@@ -2,7 +2,7 @@
"signUpHeading": "Loo konto", "signUpHeading": "Loo konto",
"signUp": "Loo konto", "signUp": "Loo konto",
"signUpSubheading": "Täida allolev vorm, et luua konto.", "signUpSubheading": "Täida allolev vorm, et luua konto.",
"signInHeading": "Logi oma kontole sisse", "signInHeading": "Logi sisse",
"signInSubheading": "Tere tulemast tagasi! Palun sisesta oma andmed", "signInSubheading": "Tere tulemast tagasi! Palun sisesta oma andmed",
"signIn": "Logi sisse", "signIn": "Logi sisse",
"getStarted": "Alusta", "getStarted": "Alusta",
@@ -22,6 +22,7 @@
"alreadyHaveAccountStatement": "Mul on juba konto, ma tahan sisse logida", "alreadyHaveAccountStatement": "Mul on juba konto, ma tahan sisse logida",
"doNotHaveAccountStatement": "Mul pole kontot, ma tahan registreeruda", "doNotHaveAccountStatement": "Mul pole kontot, ma tahan registreeruda",
"signInWithProvider": "Logi sisse teenusega {{provider}}", "signInWithProvider": "Logi sisse teenusega {{provider}}",
"signInWithKeycloak": "Smart-ID/Mobiil-ID/ID-kaart",
"signInWithPhoneNumber": "Logi sisse telefoninumbriga", "signInWithPhoneNumber": "Logi sisse telefoninumbriga",
"signInWithEmail": "Logi sisse e-posti aadressiga", "signInWithEmail": "Logi sisse e-posti aadressiga",
"signUpWithEmail": "Registreeru e-posti aadressiga", "signUpWithEmail": "Registreeru e-posti aadressiga",
@@ -68,7 +69,7 @@
"acceptTermsAndConditions": "Ma nõustun <TermsOfServiceLink /> ja <PrivacyPolicyLink />", "acceptTermsAndConditions": "Ma nõustun <TermsOfServiceLink /> ja <PrivacyPolicyLink />",
"termsOfService": "Kasutustingimused", "termsOfService": "Kasutustingimused",
"privacyPolicy": "Privaatsuspoliitika", "privacyPolicy": "Privaatsuspoliitika",
"orContinueWith": "Või jätka koos", "orContinueWith": "Või",
"redirecting": "Oled sees! Palun oota...", "redirecting": "Oled sees! Palun oota...",
"errors": { "errors": {
"Invalid login credentials": "Sisestatud andmed on valed", "Invalid login credentials": "Sisestatud andmed on valed",

View File

@@ -22,6 +22,7 @@
"alreadyHaveAccountStatement": "У меня уже есть аккаунт, я хочу войти", "alreadyHaveAccountStatement": "У меня уже есть аккаунт, я хочу войти",
"doNotHaveAccountStatement": "У меня нет аккаунта, я хочу зарегистрироваться", "doNotHaveAccountStatement": "У меня нет аккаунта, я хочу зарегистрироваться",
"signInWithProvider": "Войти через {{provider}}", "signInWithProvider": "Войти через {{provider}}",
"signInWithKeycloak": "Smart-ID/Mobiil-ID/ID-kaart",
"signInWithPhoneNumber": "Войти по номеру телефона", "signInWithPhoneNumber": "Войти по номеру телефона",
"signInWithEmail": "Войти по Email", "signInWithEmail": "Войти по Email",
"signUpWithEmail": "Зарегистрироваться по Email", "signUpWithEmail": "Зарегистрироваться по Email",