diff --git a/apps/user/app/(main)/(content)/register/page.tsx b/apps/user/app/(main)/(content)/register/page.tsx index 5d9d79c..e4efa85 100644 --- a/apps/user/app/(main)/(content)/register/page.tsx +++ b/apps/user/app/(main)/(content)/register/page.tsx @@ -1,10 +1,10 @@ 'use client'; -import EmailAuthForm2 from '@/app/auth/email2/auth-form'; +import EmailAuthForm1 from '@/app/auth/email2/auth-form'; export default function RegisterPage() { return (
- +
); } diff --git a/apps/user/app/(main)/(content)/register2/page.tsx b/apps/user/app/(main)/(content)/register2/page.tsx new file mode 100644 index 0000000..e6e6eff --- /dev/null +++ b/apps/user/app/(main)/(content)/register2/page.tsx @@ -0,0 +1,45 @@ +/*import { GlobalMap } from '@/components/main/global-map'; +import { Hero } from '@/components/main/hero'; +import { ProductShowcase } from '@/components/main/product-showcase/index'; +import { Stats } from '@/components/main/stats';*/ +import Header from '@/components/Header/Header'; +import { queryUserInfo } from '@/services/user/user'; +import { cookies } from 'next/headers'; + +import EmailAuthForm2 from '@/app/auth/email2/auth-form'; +import { LoginDialogProvider } from '@/app/auth/LoginDialogContext'; +import FooterCopyright from '@/components/main/FooterCopyright'; +import FullScreenVideoBackground from '@/components/main/FullScreenVideoBackground'; + +export default async function Home() { + const Authorization = (await cookies()).get('Authorization')?.value; + + if (Authorization) { + let user = null; + try { + user = await queryUserInfo({ + skipErrorHandler: true, + Authorization, + }).then((res) => res.data.data); + } catch (error) { + console.log('Token validation failed:', error); + } + + if (user) { + // redirect('/dashboard'); + } + } + + return ( + + +
+
+
+ +
+
+ + + ); +} diff --git a/apps/user/app/auth/email1/auth-form.tsx b/apps/user/app/auth/email1/auth-form.tsx new file mode 100644 index 0000000..01431c2 --- /dev/null +++ b/apps/user/app/auth/email1/auth-form.tsx @@ -0,0 +1,111 @@ +'use client'; + +import { resetPassword, userLogin, userRegister } from '@/services/common/auth'; +import { useTranslations } from 'next-intl'; +import { useRouter } from 'next/navigation'; +import { ReactNode, useState, useTransition } from 'react'; +import { toast } from 'sonner'; + +import { + NEXT_PUBLIC_DEFAULT_USER_EMAIL, + NEXT_PUBLIC_DEFAULT_USER_PASSWORD, +} from '@/config/constants'; +import useGlobalStore from '@/config/use-global'; +import { getRedirectUrl, setAuthorization } from '@/utils/common'; +import LoginForm from './login-form'; +import RegisterForm from './register-form'; +import ResetForm from './reset-form'; + +export default function EmailAuthForm(props: { isRedirect: boolean }) { + const t = useTranslations('auth'); + const router = useRouter(); + const [type, setType] = useState<'login' | 'register' | 'reset'>('register'); + const [loading, startTransition] = useTransition(); + const [initialValues, setInitialValues] = useState<{ + email?: string; + password?: string; + }>({ + email: NEXT_PUBLIC_DEFAULT_USER_EMAIL, + password: NEXT_PUBLIC_DEFAULT_USER_PASSWORD, + }); + const { getUserInfo } = useGlobalStore(); + const handleFormSubmit = async (params: any) => { + const onLogin = async (token?: string) => { + if (!token) return; + setAuthorization(token); + console.log('props.isRedirect', token); + console.log('props.isRedirect', props.isRedirect); + console.log('props.isRedirect ', getRedirectUrl()); + if (props.isRedirect) { + router.replace(getRedirectUrl()); + router.refresh(); + } else { + await getUserInfo(); + } + }; + startTransition(async () => { + try { + switch (type) { + case 'login': { + const login = await userLogin(params); + toast.success(t('login.success')); + onLogin(login.data.data?.token); + break; + } + case 'register': { + const create = await userRegister(params); + toast.success(t('register.success')); + onLogin(create.data.data?.token); + break; + } + case 'reset': + await resetPassword(params); + toast.success(t('reset.success')); + setType('login'); + break; + } + } catch (error) { + /* empty */ + } + }); + }; + + let UserForm: ReactNode = null; + switch (type) { + case 'login': + UserForm = ( + + ); + break; + case 'register': + UserForm = ( + + ); + break; + case 'reset': + UserForm = ( + + ); + break; + } + + return UserForm; +} diff --git a/apps/user/app/auth/email1/login-form.tsx b/apps/user/app/auth/email1/login-form.tsx new file mode 100644 index 0000000..859f1a6 --- /dev/null +++ b/apps/user/app/auth/email1/login-form.tsx @@ -0,0 +1,147 @@ +import useGlobalStore from '@/config/use-global'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { AiroButton } from '@workspace/airo-ui/components/AiroButton'; +import { Button } from '@workspace/airo-ui/components/button'; +import { + Form, + FormControl, + FormField, + FormItem, + FormMessage, +} from '@workspace/airo-ui/components/form'; +import { Input } from '@workspace/airo-ui/components/input'; +import { Icon } from '@workspace/airo-ui/custom-components/icon'; +import { useTranslations } from 'next-intl'; +import { Dispatch, SetStateAction, useRef } from 'react'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import CloudFlareTurnstile, { TurnstileRef } from '../turnstile'; + +export default function LoginForm({ + loading, + onSubmit, + initialValues, + setInitialValues, + onSwitchForm, +}: { + loading?: boolean; + onSubmit: (data: any) => void; + initialValues: any; + setInitialValues: Dispatch>; + onSwitchForm: Dispatch>; +}) { + const t = useTranslations('auth.login'); + const { common } = useGlobalStore(); + const { verify } = common; + + const formSchema = z.object({ + email: z.string().email(t('email')), + password: z.string(), + cf_token: + verify.enable_login_verify && verify.turnstile_site_key ? z.string() : z.string().optional(), + }); + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: initialValues, + }); + + const turnstile = useRef(null); + const handleSubmit = form.handleSubmit((data) => { + try { + onSubmit(data); + } catch (error) { + turnstile.current?.reset(); + } + }); + + return ( + <> +
账户验证
+
+ + ( + + + + + + + )} + /> + ( + + + + + + + )} + /> + {verify.enable_login_verify && ( + ( + + + + + + + )} + /> + )} +
+ + +
+ +
+ + {loading && } + {t('title')} + +
+ + + + ); +} diff --git a/apps/user/app/auth/email1/register-form.tsx b/apps/user/app/auth/email1/register-form.tsx new file mode 100644 index 0000000..674ec1b --- /dev/null +++ b/apps/user/app/auth/email1/register-form.tsx @@ -0,0 +1,218 @@ +'use client'; +import useGlobalStore from '@/config/use-global'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { AiroButton } from '@workspace/airo-ui/components/AiroButton'; +import { Button } from '@workspace/airo-ui/components/button'; + +import { + Form, + FormControl, + FormField, + FormItem, + FormMessage, +} from '@workspace/airo-ui/components/form'; +import { Input } from '@workspace/airo-ui/components/input'; +import { Icon } from '@workspace/airo-ui/custom-components/icon'; +import { useRouter } from 'next/navigation'; +import { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import SendCode from '../send-code'; +import { TurnstileRef } from '../turnstile'; + +export default function RegisterForm({ + loading, + onSubmit, + initialValues, + setInitialValues, + onSwitchForm, +}: { + loading?: boolean; + onSubmit: (data: any) => void; + initialValues: any; + setInitialValues: Dispatch>; + onSwitchForm: Dispatch>; +}) { + const { common } = useGlobalStore(); + const router = useRouter(); + + const formSchema = z + .object({ + email: z.string().email('请输入有效的电子邮件地址。'), + password: z.string().min(1, '请输入密码'), // 必填提示 + repeat_password: z.string().min(1, '请重复输入密码'), // 必填 + code: z.string().min(1, '请输入验证码'), // 必填, + invite: z.string().nullish(), + }) + .superRefine(({ password, repeat_password }, ctx) => { + if (password !== repeat_password) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: '两次密码输入不一致', + path: ['repeat_password'], + }); + } + }); + + const [inviteDefault, setInviteDefault] = useState(''); + + useEffect(() => { + const invite = localStorage.getItem('invite') || ''; + setInviteDefault(invite); + }, []); + + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + ...initialValues, + invite: inviteDefault, + }, + }); + + const turnstile = useRef(null); + const handleSubmit = form.handleSubmit((data) => { + try { + onSubmit(data); + } catch (error) { + turnstile.current?.reset(); + } + }); + + return ( + <> +
线路优化
+ +
+ +
+ ( + + + + + + + )} + /> + ( + + + + + + + )} + /> + ( + + + + + + + )} + /> + { + ( + + +
+ + +
+
+ +
+ )} + /> + } + ( + + + + + + + )} + /> +
+ +
+ 已有账户?  + +
+
+ + {loading && } + 注册 + +
+
+ + + ); +} diff --git a/apps/user/app/auth/email1/reset-form.tsx b/apps/user/app/auth/email1/reset-form.tsx new file mode 100644 index 0000000..5cadaa8 --- /dev/null +++ b/apps/user/app/auth/email1/reset-form.tsx @@ -0,0 +1,174 @@ +import useGlobalStore from '@/config/use-global'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { AiroButton } from '@workspace/airo-ui/components/AiroButton'; +import { Button } from '@workspace/airo-ui/components/button'; +import { + Form, + FormControl, + FormField, + FormItem, + FormMessage, +} from '@workspace/airo-ui/components/form'; +import { Input } from '@workspace/airo-ui/components/input'; +import { Icon } from '@workspace/airo-ui/custom-components/icon'; +import { useTranslations } from 'next-intl'; +import { Dispatch, SetStateAction, useRef } from 'react'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import SendCode from '../send-code'; +import CloudFlareTurnstile, { TurnstileRef } from '../turnstile'; + +export default function ResetForm({ + loading, + onSubmit, + initialValues, + setInitialValues, + onSwitchForm, +}: { + loading?: boolean; + onSubmit: (data: any) => void; + initialValues: any; + setInitialValues: Dispatch>; + onSwitchForm: Dispatch>; +}) { + const t = useTranslations('auth.reset'); + + const { common } = useGlobalStore(); + const { verify, auth } = common; + + const formSchema = z.object({ + email: z.string().email(t('email')), + password: z.string(), + code: auth?.email?.enable_verify ? z.string() : z.string().nullish(), + cf_token: + verify.enable_register_verify && verify.turnstile_site_key + ? z.string() + : z.string().nullish(), + }); + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: initialValues, + }); + + const turnstile = useRef(null); + const handleSubmit = form.handleSubmit((data) => { + try { + onSubmit(data); + } catch (error) { + turnstile.current?.reset(); + } + }); + + return ( + <> +
找回账户
+
+ +
+ ( + + + + + + + )} + /> + ( + + +
+ + +
+
+ +
+ )} + /> + ( + + + + + + + )} + /> + {verify.enable_reset_password_verify && ( + ( + + + + + + + )} + /> + )} +
+
+ {t('existingAccount')}  + +
+
+ + {loading && } + {t('title')} + +
+
+ + + ); +} diff --git a/apps/user/app/auth/email2/register-form.tsx b/apps/user/app/auth/email2/register-form.tsx index 674ec1b..1f35ff9 100644 --- a/apps/user/app/auth/email2/register-form.tsx +++ b/apps/user/app/auth/email2/register-form.tsx @@ -4,6 +4,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { AiroButton } from '@workspace/airo-ui/components/AiroButton'; import { Button } from '@workspace/airo-ui/components/button'; +import { useLoginDialog } from '@/app/auth/LoginDialogContext'; import { Form, FormControl, @@ -55,6 +56,7 @@ export default function RegisterForm({ }); const [inviteDefault, setInviteDefault] = useState(''); + const { openLoginDialog } = useLoginDialog(); useEffect(() => { const invite = localStorage.getItem('invite') || ''; @@ -80,7 +82,7 @@ export default function RegisterForm({ return ( <> -
线路优化
+
线路优化
@@ -92,7 +94,9 @@ export default function RegisterForm({ { - router.replace('/'); + openLoginDialog(); }} > 登录 @@ -204,8 +216,9 @@ export default function RegisterForm({
{loading && } 注册 diff --git a/apps/user/app/auth/send-code.tsx b/apps/user/app/auth/send-code.tsx index 4c2c26d..4cc9d4f 100644 --- a/apps/user/app/auth/send-code.tsx +++ b/apps/user/app/auth/send-code.tsx @@ -84,16 +84,14 @@ export default function SendCode({ type, params, form }: SendCodeProps) { await getPhoneCode(); } }; - const disabled = - seconds > 0 || - (type === 'email' ? !params.email : !params.telephone || !params.telephone_area_code); + const disabled = seconds > 0; return (
diff --git a/apps/user/components/providers.tsx b/apps/user/components/providers.tsx index a15d624..3e6fade 100644 --- a/apps/user/components/providers.tsx +++ b/apps/user/components/providers.tsx @@ -33,7 +33,7 @@ export default function Providers({ const { setCommon, setUser } = useGlobalStore(); const pathname = usePathname(); - const whiteList = ['/', '/register']; + const whiteList = ['/', '/register', '/register2']; const isWhite = whiteList.includes(pathname); useEffect(() => { diff --git a/apps/user/utils/common.ts b/apps/user/utils/common.ts index d017e05..5f0a607 100644 --- a/apps/user/utils/common.ts +++ b/apps/user/utils/common.ts @@ -53,7 +53,7 @@ export function Logout() { Crisp.session.reset(); // 2. Unbind the current session const pathname = location.pathname; if ( - !['', '/', '/auth', '/tos', '/privacy-policy', '/register'].includes(pathname) && + !['', '/', '/auth', '/tos', '/privacy-policy', '/register', '/register2'].includes(pathname) && !pathname.startsWith('/purchasing') && !pathname.startsWith('/oauth/') ) {