Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bbd44f0107 | |||
| c65a44c2c7 | |||
| 715d0113ca | |||
| 0f15fb82cb | |||
| 320a7dc342 | |||
| 42ba9e88ad |
@@ -1,6 +1,26 @@
|
||||
<a name="readme-top"></a>
|
||||
# Changelog
|
||||
|
||||
# [1.0.0-beta.20](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.19...v1.0.0-beta.20) (2025-02-23)
|
||||
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
* **auth**: Add error handling to form submission and reset Turnstile on failure ([715d011](https://github.com/perfect-panel/ppanel-web/commit/715d011))
|
||||
* **subscribe**: Update subscription domain placeholder to include examples; improve site name retrieval in global store ([c65a44c](https://github.com/perfect-panel/ppanel-web/commit/c65a44c))
|
||||
|
||||
# [1.0.0-beta.19](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.18...v1.0.0-beta.19) (2025-02-23)
|
||||
|
||||
|
||||
### ✨ Features
|
||||
|
||||
* **form**: Make version field optional and set default value; update site domain placeholder for clarity ([42ba9e8](https://github.com/perfect-panel/ppanel-web/commit/42ba9e8))
|
||||
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
* **auth**: Refactor forms to use Turnstile ref for reset functionality ([320a7dc](https://github.com/perfect-panel/ppanel-web/commit/320a7dc))
|
||||
|
||||
# [1.0.0-beta.18](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.17...v1.0.0-beta.18) (2025-02-22)
|
||||
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@ import { Form, FormControl, FormField, FormItem, FormMessage } from '@workspace/
|
||||
import { Input } from '@workspace/ui/components/input';
|
||||
import { Icon } from '@workspace/ui/custom-components/icon';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
import { Dispatch, SetStateAction, useRef } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import CloudFlareTurnstile from '../turnstile';
|
||||
import CloudFlareTurnstile, { TurnstileRef } from '../turnstile';
|
||||
|
||||
export default function LoginForm({
|
||||
loading,
|
||||
@@ -38,10 +38,19 @@ export default function LoginForm({
|
||||
defaultValues: initialValues,
|
||||
});
|
||||
|
||||
const turnstile = useRef<TurnstileRef>(null);
|
||||
const handleSubmit = form.handleSubmit((data) => {
|
||||
try {
|
||||
onSubmit(data);
|
||||
} catch (error) {
|
||||
turnstile.current?.reset();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className='grid gap-6'>
|
||||
<form onSubmit={handleSubmit} className='grid gap-6'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='email'
|
||||
@@ -73,7 +82,7 @@ export default function LoginForm({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<CloudFlareTurnstile id='login' {...field} />
|
||||
<CloudFlareTurnstile id='login' {...field} ref={turnstile} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@@ -6,11 +6,11 @@ import { Input } from '@workspace/ui/components/input';
|
||||
import { Icon } from '@workspace/ui/custom-components/icon';
|
||||
import { Markdown } from '@workspace/ui/custom-components/markdown';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
import { Dispatch, SetStateAction, useRef } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import SendCode from '../send-code';
|
||||
import CloudFlareTurnstile from '../turnstile';
|
||||
import CloudFlareTurnstile, { TurnstileRef } from '../turnstile';
|
||||
|
||||
export default function RegisterForm({
|
||||
loading,
|
||||
@@ -31,11 +31,10 @@ export default function RegisterForm({
|
||||
|
||||
const handleCheckUser = async (email: string) => {
|
||||
try {
|
||||
if (!auth.email.enable_verify) return true;
|
||||
const domain = email.split('@')[1];
|
||||
const isValid =
|
||||
!auth.email.enable_verify ||
|
||||
auth.email?.domain_suffix_list.split('\n').includes(domain || '');
|
||||
return !isValid;
|
||||
const isValid = auth.email?.domain_suffix_list.split('\n').includes(domain || '');
|
||||
return isValid;
|
||||
} catch (error) {
|
||||
console.log('Error checking user:', error);
|
||||
return false;
|
||||
@@ -53,7 +52,7 @@ export default function RegisterForm({
|
||||
password: z.string(),
|
||||
repeat_password: z.string(),
|
||||
code: auth.email.enable_verify ? z.string() : z.string().nullish(),
|
||||
invite: invite.forced_invite ? z.string() : z.string().nullish(),
|
||||
invite: invite.forced_invite ? z.string().min(1) : z.string().nullish(),
|
||||
cf_token:
|
||||
verify.enable_register_verify && verify.turnstile_site_key
|
||||
? z.string()
|
||||
@@ -77,13 +76,22 @@ export default function RegisterForm({
|
||||
},
|
||||
});
|
||||
|
||||
const turnstile = useRef<TurnstileRef>(null);
|
||||
const handleSubmit = form.handleSubmit((data) => {
|
||||
try {
|
||||
onSubmit(data);
|
||||
} catch (error) {
|
||||
turnstile.current?.reset();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{auth.register.stop_register ? (
|
||||
<Markdown>{t('message')}</Markdown>
|
||||
) : (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className='grid gap-6'>
|
||||
<form onSubmit={handleSubmit} className='grid gap-6'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='email'
|
||||
@@ -114,7 +122,12 @@ export default function RegisterForm({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input placeholder='Enter password again...' type='password' {...field} />
|
||||
<Input
|
||||
disabled={loading}
|
||||
placeholder='Enter password again...'
|
||||
type='password'
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -129,6 +142,7 @@ export default function RegisterForm({
|
||||
<FormControl>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Input
|
||||
disabled={loading}
|
||||
placeholder='Enter code...'
|
||||
type='text'
|
||||
{...field}
|
||||
@@ -172,7 +186,7 @@ export default function RegisterForm({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<CloudFlareTurnstile id='register' {...field} />
|
||||
<CloudFlareTurnstile id='register' {...field} ref={turnstile} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@@ -5,11 +5,11 @@ import { Form, FormControl, FormField, FormItem, FormMessage } from '@workspace/
|
||||
import { Input } from '@workspace/ui/components/input';
|
||||
import { Icon } from '@workspace/ui/custom-components/icon';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
import { Dispatch, SetStateAction, useRef } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import SendCode from '../send-code';
|
||||
import CloudFlareTurnstile from '../turnstile';
|
||||
import CloudFlareTurnstile, { TurnstileRef } from '../turnstile';
|
||||
|
||||
export default function ResetForm({
|
||||
loading,
|
||||
@@ -43,10 +43,19 @@ export default function ResetForm({
|
||||
defaultValues: initialValues,
|
||||
});
|
||||
|
||||
const turnstile = useRef<TurnstileRef>(null);
|
||||
const handleSubmit = form.handleSubmit((data) => {
|
||||
try {
|
||||
onSubmit(data);
|
||||
} catch (error) {
|
||||
turnstile.current?.reset();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className='grid gap-6'>
|
||||
<form onSubmit={handleSubmit} className='grid gap-6'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='email'
|
||||
@@ -67,6 +76,7 @@ export default function ResetForm({
|
||||
<FormControl>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Input
|
||||
disabled={loading}
|
||||
placeholder='Enter code...'
|
||||
type='text'
|
||||
{...field}
|
||||
@@ -91,7 +101,7 @@ export default function ResetForm({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input placeholder='Enter your New password...' type='password' {...field} />
|
||||
<Input placeholder='Enter your new password...' type='password' {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -104,7 +114,7 @@ export default function ResetForm({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<CloudFlareTurnstile id='reset' {...field} />
|
||||
<CloudFlareTurnstile id='reset' {...field} ref={turnstile} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@@ -1,26 +1,38 @@
|
||||
'use client';
|
||||
|
||||
import useGlobalStore from '@/config/use-global';
|
||||
import { useLocale } from 'next-intl';
|
||||
import { useTheme } from 'next-themes';
|
||||
import { useEffect } from 'react';
|
||||
import { forwardRef, useEffect, useImperativeHandle } from 'react';
|
||||
import Turnstile, { useTurnstile } from 'react-turnstile';
|
||||
|
||||
export default function CloudFlareTurnstile({
|
||||
id,
|
||||
value,
|
||||
onChange,
|
||||
}: {
|
||||
id?: string;
|
||||
value?: null | string;
|
||||
onChange: (value?: string) => void;
|
||||
}) {
|
||||
import useGlobalStore from '@/config/use-global';
|
||||
|
||||
export type TurnstileRef = {
|
||||
reset: () => void;
|
||||
};
|
||||
|
||||
const CloudFlareTurnstile = forwardRef<
|
||||
TurnstileRef,
|
||||
{
|
||||
id?: string;
|
||||
value?: null | string;
|
||||
onChange: (value?: string) => void;
|
||||
}
|
||||
>(function CloudFlareTurnstile({ id, value, onChange }, ref) {
|
||||
const { common } = useGlobalStore();
|
||||
const { verify } = common;
|
||||
const { resolvedTheme } = useTheme();
|
||||
const locale = useLocale();
|
||||
const turnstile = useTurnstile();
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => ({
|
||||
reset: () => turnstile.reset(),
|
||||
}),
|
||||
[turnstile],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (value === '') {
|
||||
turnstile.reset();
|
||||
@@ -51,4 +63,6 @@ export default function CloudFlareTurnstile({
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default CloudFlareTurnstile;
|
||||
|
||||
@@ -50,7 +50,7 @@ const defaultValues = {
|
||||
|
||||
const versionSchema = z.object({
|
||||
url: z.string(),
|
||||
version: z.string(),
|
||||
version: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
is_default: z.boolean().optional(),
|
||||
});
|
||||
@@ -221,6 +221,7 @@ export default function SubscribeAppForm<
|
||||
name: 'version',
|
||||
type: 'text',
|
||||
placeholder: t('version'),
|
||||
defaultValue: '1.0.0',
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
@@ -251,7 +252,10 @@ export default function SubscribeAppForm<
|
||||
]}
|
||||
value={field.value}
|
||||
onChange={(value) => {
|
||||
form.setValue(field.name, value);
|
||||
form.setValue(
|
||||
field.name,
|
||||
value.filter((item) => item.url),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
@@ -92,7 +92,7 @@ export default function SubscribeConfig() {
|
||||
<TableCell className='text-right'>
|
||||
<Textarea
|
||||
className='h-52'
|
||||
placeholder={t('subscriptionDomainPlaceholder')}
|
||||
placeholder={`${t('subscriptionDomainPlaceholder')}\nexample.com\nwww.example.com`}
|
||||
defaultValue={data?.subscribe_domain}
|
||||
onBlur={(e) => {
|
||||
updateConfig('subscribe_domain', e.target.value);
|
||||
|
||||
@@ -122,7 +122,7 @@ export default function Site() {
|
||||
<TableCell className='text-right'>
|
||||
<Textarea
|
||||
className='h-52'
|
||||
placeholder={t('siteDomainPlaceholder')}
|
||||
placeholder={`${t('siteDomainPlaceholder')}\nexample.com\nwww.example.com`}
|
||||
defaultValue={data?.host}
|
||||
onBlur={(e) => {
|
||||
updateConfig('host', e.target.value);
|
||||
|
||||
@@ -5,10 +5,10 @@ import { Form, FormControl, FormField, FormItem, FormMessage } from '@workspace/
|
||||
import { Input } from '@workspace/ui/components/input';
|
||||
import { Icon } from '@workspace/ui/custom-components/icon';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
import { Dispatch, SetStateAction, useRef } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import CloudFlareTurnstile from '../turnstile';
|
||||
import CloudFlareTurnstile, { TurnstileRef } from '../turnstile';
|
||||
|
||||
export default function LoginForm({
|
||||
loading,
|
||||
@@ -38,10 +38,19 @@ export default function LoginForm({
|
||||
defaultValues: initialValues,
|
||||
});
|
||||
|
||||
const turnstile = useRef<TurnstileRef>(null);
|
||||
const handleSubmit = form.handleSubmit((data) => {
|
||||
try {
|
||||
onSubmit(data);
|
||||
} catch (error) {
|
||||
turnstile.current?.reset();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className='grid gap-6'>
|
||||
<form onSubmit={handleSubmit} className='grid gap-6'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='email'
|
||||
@@ -73,7 +82,7 @@ export default function LoginForm({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<CloudFlareTurnstile id='login' {...field} />
|
||||
<CloudFlareTurnstile id='login' {...field} ref={turnstile} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@@ -6,11 +6,11 @@ import { Input } from '@workspace/ui/components/input';
|
||||
import { Icon } from '@workspace/ui/custom-components/icon';
|
||||
import { Markdown } from '@workspace/ui/custom-components/markdown';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
import { Dispatch, SetStateAction, useRef } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import SendCode from '../send-code';
|
||||
import CloudFlareTurnstile from '../turnstile';
|
||||
import CloudFlareTurnstile, { TurnstileRef } from '../turnstile';
|
||||
|
||||
export default function RegisterForm({
|
||||
loading,
|
||||
@@ -76,13 +76,22 @@ export default function RegisterForm({
|
||||
},
|
||||
});
|
||||
|
||||
const turnstile = useRef<TurnstileRef>(null);
|
||||
const handleSubmit = form.handleSubmit((data) => {
|
||||
try {
|
||||
onSubmit(data);
|
||||
} catch (error) {
|
||||
turnstile.current?.reset();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{auth.register.stop_register ? (
|
||||
<Markdown>{t('message')}</Markdown>
|
||||
) : (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className='grid gap-6'>
|
||||
<form onSubmit={handleSubmit} className='grid gap-6'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='email'
|
||||
@@ -177,7 +186,7 @@ export default function RegisterForm({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<CloudFlareTurnstile id='register' {...field} />
|
||||
<CloudFlareTurnstile id='register' {...field} ref={turnstile} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@@ -5,11 +5,11 @@ import { Form, FormControl, FormField, FormItem, FormMessage } from '@workspace/
|
||||
import { Input } from '@workspace/ui/components/input';
|
||||
import { Icon } from '@workspace/ui/custom-components/icon';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
import { Dispatch, SetStateAction, useRef } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import SendCode from '../send-code';
|
||||
import CloudFlareTurnstile from '../turnstile';
|
||||
import CloudFlareTurnstile, { TurnstileRef } from '../turnstile';
|
||||
|
||||
export default function ResetForm({
|
||||
loading,
|
||||
@@ -43,10 +43,19 @@ export default function ResetForm({
|
||||
defaultValues: initialValues,
|
||||
});
|
||||
|
||||
const turnstile = useRef<TurnstileRef>(null);
|
||||
const handleSubmit = form.handleSubmit((data) => {
|
||||
try {
|
||||
onSubmit(data);
|
||||
} catch (error) {
|
||||
turnstile.current?.reset();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className='grid gap-6'>
|
||||
<form onSubmit={handleSubmit} className='grid gap-6'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='email'
|
||||
@@ -105,7 +114,7 @@ export default function ResetForm({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<CloudFlareTurnstile id='reset' {...field} />
|
||||
<CloudFlareTurnstile id='reset' {...field} ref={turnstile} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@@ -7,11 +7,11 @@ import { AreaCodeSelect } from '@workspace/ui/custom-components/area-code-select
|
||||
import { Icon } from '@workspace/ui/custom-components/icon';
|
||||
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Dispatch, SetStateAction, useState } from 'react';
|
||||
import { Dispatch, SetStateAction, useRef, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import SendCode from '../send-code';
|
||||
import CloudFlareTurnstile from '../turnstile';
|
||||
import CloudFlareTurnstile, { TurnstileRef } from '../turnstile';
|
||||
|
||||
export default function LoginForm({
|
||||
loading,
|
||||
@@ -45,10 +45,19 @@ export default function LoginForm({
|
||||
|
||||
const [mode, setMode] = useState<'password' | 'code'>('password');
|
||||
|
||||
const turnstile = useRef<TurnstileRef>(null);
|
||||
const handleSubmit = form.handleSubmit((data) => {
|
||||
try {
|
||||
onSubmit(data);
|
||||
} catch (error) {
|
||||
turnstile.current?.reset();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className='grid gap-6'>
|
||||
<form onSubmit={handleSubmit} className='grid gap-6'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='telephone'
|
||||
@@ -138,7 +147,7 @@ export default function LoginForm({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<CloudFlareTurnstile id='login' {...field} />
|
||||
<CloudFlareTurnstile id='login' {...field} ref={turnstile} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@@ -7,11 +7,11 @@ import { AreaCodeSelect } from '@workspace/ui/custom-components/area-code-select
|
||||
import { Icon } from '@workspace/ui/custom-components/icon';
|
||||
import { Markdown } from '@workspace/ui/custom-components/markdown';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
import { Dispatch, SetStateAction, useRef } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import SendCode from '../send-code';
|
||||
import CloudFlareTurnstile from '../turnstile';
|
||||
import CloudFlareTurnstile, { TurnstileRef } from '../turnstile';
|
||||
|
||||
export default function RegisterForm({
|
||||
loading,
|
||||
@@ -63,13 +63,22 @@ export default function RegisterForm({
|
||||
},
|
||||
});
|
||||
|
||||
const turnstile = useRef<TurnstileRef>(null);
|
||||
const handleSubmit = form.handleSubmit((data) => {
|
||||
try {
|
||||
onSubmit(data);
|
||||
} catch (error) {
|
||||
turnstile.current?.reset();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{auth.register.stop_register ? (
|
||||
<Markdown>{t('message')}</Markdown>
|
||||
) : (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className='grid gap-6'>
|
||||
<form onSubmit={handleSubmit} className='grid gap-6'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='telephone'
|
||||
@@ -193,7 +202,7 @@ export default function RegisterForm({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<CloudFlareTurnstile id='register' {...field} />
|
||||
<CloudFlareTurnstile id='register' {...field} ref={turnstile} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@@ -6,11 +6,11 @@ import { Input } from '@workspace/ui/components/input';
|
||||
import { AreaCodeSelect } from '@workspace/ui/custom-components/area-code-select';
|
||||
import { Icon } from '@workspace/ui/custom-components/icon';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Dispatch, SetStateAction, useState } from 'react';
|
||||
import { Dispatch, SetStateAction, useRef, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import SendCode from '../send-code';
|
||||
import CloudFlareTurnstile from '../turnstile';
|
||||
import CloudFlareTurnstile, { TurnstileRef } from '../turnstile';
|
||||
|
||||
export default function ResetForm({
|
||||
loading,
|
||||
@@ -46,10 +46,19 @@ export default function ResetForm({
|
||||
defaultValues: initialValues,
|
||||
});
|
||||
|
||||
const turnstile = useRef<TurnstileRef>(null);
|
||||
const handleSubmit = form.handleSubmit((data) => {
|
||||
try {
|
||||
onSubmit(data);
|
||||
} catch (error) {
|
||||
turnstile.current?.reset();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className='grid gap-6'>
|
||||
<form onSubmit={handleSubmit} className='grid gap-6'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='telephone'
|
||||
@@ -136,7 +145,7 @@ export default function ResetForm({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<CloudFlareTurnstile id='reset' {...field} />
|
||||
<CloudFlareTurnstile id='reset' {...field} ref={turnstile} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@@ -2,26 +2,37 @@
|
||||
|
||||
import { useLocale } from 'next-intl';
|
||||
import { useTheme } from 'next-themes';
|
||||
import { useEffect } from 'react';
|
||||
import { forwardRef, useEffect, useImperativeHandle } from 'react';
|
||||
import Turnstile, { useTurnstile } from 'react-turnstile';
|
||||
|
||||
import useGlobalStore from '@/config/use-global';
|
||||
|
||||
export default function CloudFlareTurnstile({
|
||||
id,
|
||||
value,
|
||||
onChange,
|
||||
}: {
|
||||
id?: string;
|
||||
value?: null | string;
|
||||
onChange: (value?: string) => void;
|
||||
}) {
|
||||
export type TurnstileRef = {
|
||||
reset: () => void;
|
||||
};
|
||||
|
||||
const CloudFlareTurnstile = forwardRef<
|
||||
TurnstileRef,
|
||||
{
|
||||
id?: string;
|
||||
value?: null | string;
|
||||
onChange: (value?: string) => void;
|
||||
}
|
||||
>(function CloudFlareTurnstile({ id, value, onChange }, ref) {
|
||||
const { common } = useGlobalStore();
|
||||
const { verify } = common;
|
||||
const { resolvedTheme } = useTheme();
|
||||
const locale = useLocale();
|
||||
const turnstile = useTurnstile();
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => ({
|
||||
reset: () => turnstile.reset(),
|
||||
}),
|
||||
[turnstile],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (value === '') {
|
||||
turnstile.reset();
|
||||
@@ -52,4 +63,6 @@ export default function CloudFlareTurnstile({
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default CloudFlareTurnstile;
|
||||
|
||||
@@ -104,7 +104,7 @@ export const useGlobalStore = create<GlobalStore>((set, get) => ({
|
||||
});
|
||||
},
|
||||
getAppSubLink: (type: string, url: string) => {
|
||||
const name = get().common.site.site_name || '';
|
||||
const name = get().common?.site?.site_name || '';
|
||||
switch (type) {
|
||||
case 'Clash':
|
||||
return `clash://install-config?url=${url}&name=${name}`;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ppanel-web",
|
||||
"version": "1.0.0-beta.18",
|
||||
"version": "1.0.0-beta.20",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/perfect-panel/ppanel-web",
|
||||
"bugs": {
|
||||
|
||||
Reference in New Issue
Block a user