feat(client): add KYC for event joining
Signed-off-by: Noa Virellia <noa@requiem.garden>
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
import type { KycSubmission } from './kyc.types';
|
||||
import { useForm } from '@tanstack/react-form';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import z from 'zod';
|
||||
import {
|
||||
Field,
|
||||
FieldError,
|
||||
FieldLabel,
|
||||
} from '@/components/ui/field';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Button } from '../../ui/button';
|
||||
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '../../ui/dialog';
|
||||
|
||||
const CnridSchema = z.object({
|
||||
cnrid: z.string().min(18, '身份证号应为18位').max(18, '身份证号应为18位'),
|
||||
name: z.string().min(2, '姓名应至少2个字符').max(10, '姓名应不超过10个字符'),
|
||||
});
|
||||
|
||||
function CnridForm({ onSubmit }: { onSubmit: OnSubmit }) {
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
cnrid: '',
|
||||
name: '',
|
||||
},
|
||||
validators: {
|
||||
onSubmit: CnridSchema,
|
||||
},
|
||||
onSubmit: async (values) => {
|
||||
await onSubmit({
|
||||
method: 'cnrid',
|
||||
...values.value,
|
||||
}).catch(() => {
|
||||
toast('认证失败,请稍后再试');
|
||||
});
|
||||
},
|
||||
});
|
||||
return (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
void form.handleSubmit();
|
||||
}}
|
||||
className="flex flex-col gap-4"
|
||||
>
|
||||
<form.Field name="name">
|
||||
{field => (
|
||||
<Field>
|
||||
<FieldLabel htmlFor="name">姓名</FieldLabel>
|
||||
<Input
|
||||
id="name"
|
||||
name="name"
|
||||
value={field.state.value}
|
||||
onBlur={field.handleBlur}
|
||||
onChange={e => field.handleChange(e.target.value)}
|
||||
/>
|
||||
<FieldError errors={field.state.meta.errors} />
|
||||
</Field>
|
||||
)}
|
||||
</form.Field>
|
||||
<form.Field name="cnrid">
|
||||
{field => (
|
||||
<Field>
|
||||
<FieldLabel htmlFor="cnrid">身份证号</FieldLabel>
|
||||
<Input
|
||||
id="cnrid"
|
||||
name="cnrid"
|
||||
value={field.state.value}
|
||||
onBlur={field.handleBlur}
|
||||
onChange={e => field.handleChange(e.target.value)}
|
||||
/>
|
||||
<FieldError errors={field.state.meta.errors} />
|
||||
</Field>
|
||||
)}
|
||||
</form.Field>
|
||||
<DialogFooter>
|
||||
<form.Subscribe
|
||||
selector={state => [state.canSubmit, state.isPristine, state.isSubmitting]}
|
||||
children={([canSubmit, isPristine, isSubmitting]) => (
|
||||
<Button type="submit" disabled={!canSubmit || isPristine || isSubmitting}>{isSubmitting ? '...' : '开始认证'}</Button>
|
||||
)}
|
||||
/>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
const PassportSchema = z.object({
|
||||
passportId: z.string().min(9, '护照号应为9个字符').max(9, '护照号应为9个字符'),
|
||||
});
|
||||
|
||||
function PassportForm({ onSubmit }: { onSubmit: OnSubmit }) {
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
passportId: '',
|
||||
},
|
||||
validators: {
|
||||
onSubmit: PassportSchema,
|
||||
},
|
||||
onSubmit: async (values) => {
|
||||
await onSubmit({
|
||||
method: 'passport',
|
||||
...values.value,
|
||||
}).catch(() => {
|
||||
toast('认证失败,请稍后再试');
|
||||
});
|
||||
},
|
||||
});
|
||||
return (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
void form.handleSubmit();
|
||||
}}
|
||||
className="flex flex-col gap-4"
|
||||
>
|
||||
<form.Field name="passportId">
|
||||
{field => (
|
||||
<Field>
|
||||
<FieldLabel htmlFor="passportId">护照号</FieldLabel>
|
||||
<Input
|
||||
id="passportId"
|
||||
name="passportId"
|
||||
value={field.state.value}
|
||||
onBlur={field.handleBlur}
|
||||
onChange={e => field.handleChange(e.target.value)}
|
||||
/>
|
||||
<FieldError errors={field.state.meta.errors} />
|
||||
</Field>
|
||||
)}
|
||||
</form.Field>
|
||||
<DialogFooter>
|
||||
<form.Subscribe
|
||||
selector={state => [state.canSubmit, state.isPristine, state.isSubmitting]}
|
||||
children={([canSubmit, isPristine, isSubmitting]) => (
|
||||
<Button type="submit" disabled={!canSubmit || isPristine || isSubmitting}>{isSubmitting ? '...' : '开始认证'}</Button>
|
||||
)}
|
||||
/>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
type OnSubmit = (submission: KycSubmission) => Promise<void>;
|
||||
|
||||
export function KycMethodSelectionDialogView({ onSubmit }: { onSubmit: OnSubmit }) {
|
||||
const [kycMethod, setKycMethod] = useState<string | null>(null);
|
||||
|
||||
return (
|
||||
<DialogContent className="max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>选择身份认证模式</DialogTitle>
|
||||
<DialogDescription className="prose">
|
||||
<p>我们支持身份证和护照认证。</p>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Label htmlFor="selection">身份认证模式</Label>
|
||||
<Select onValueChange={setKycMethod}>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue placeholder="请选择..." />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup id="selection">
|
||||
<SelectItem value="cnrid">身份证</SelectItem>
|
||||
<SelectItem value="passport">护照</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{kycMethod === 'cnrid' && <CnridForm onSubmit={onSubmit} />}
|
||||
{kycMethod === 'passport' && <PassportForm onSubmit={onSubmit} />}
|
||||
</DialogContent>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user