feat: 佣金提现和备注功能
This commit is contained in:
parent
2f0123078b
commit
8241e95508
@ -120,6 +120,7 @@ export default function Page() {
|
|||||||
const { data } = await getTicketList({
|
const { data } = await getTicketList({
|
||||||
...pagination,
|
...pagination,
|
||||||
...filters,
|
...filters,
|
||||||
|
issue_type: 0,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
list: data.data?.list || [],
|
list: data.data?.list || [],
|
||||||
|
|||||||
@ -5,8 +5,16 @@ import { ProTable, ProTableActions } from '@/components/pro-table';
|
|||||||
import { getSubscribeList } from '@/services/admin/subscribe';
|
import { getSubscribeList } from '@/services/admin/subscribe';
|
||||||
import { createUser, deleteUser, getUserList, updateUserBasicInfo } from '@/services/admin/user';
|
import { createUser, deleteUser, getUserList, updateUserBasicInfo } from '@/services/admin/user';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { Badge } from '@workspace/ui/components/badge';
|
|
||||||
import { Button } from '@workspace/ui/components/button';
|
import { Button } from '@workspace/ui/components/button';
|
||||||
|
import { Input } from '@workspace/ui/components/input';
|
||||||
|
import { FilePenLine } from 'lucide-react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverClose,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from '@workspace/ui/components/popover';
|
||||||
import { Switch } from '@workspace/ui/components/switch';
|
import { Switch } from '@workspace/ui/components/switch';
|
||||||
import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
|
import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
|
||||||
import { formatDate } from '@workspace/ui/utils';
|
import { formatDate } from '@workspace/ui/utils';
|
||||||
@ -17,6 +25,37 @@ import { toast } from 'sonner';
|
|||||||
import { UserDetail } from './user-detail';
|
import { UserDetail } from './user-detail';
|
||||||
import UserForm from './user-form';
|
import UserForm from './user-form';
|
||||||
|
|
||||||
|
// 新的子组件,现在管理它自己的备注状态
|
||||||
|
const RemarkForm = ({ onSave, initialRemark, CloseComponent }) => {
|
||||||
|
const [remark, setRemark] = useState(initialRemark);
|
||||||
|
|
||||||
|
const handleInputChange = (event) => {
|
||||||
|
setRemark(event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSaveClick = () => {
|
||||||
|
onSave(remark);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className='mb-2 text-sm font-semibold'>备注</div>
|
||||||
|
<Input
|
||||||
|
type='text'
|
||||||
|
value={remark}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder='在此输入备注...'
|
||||||
|
className='w-full'
|
||||||
|
/>
|
||||||
|
<CloseComponent asChild>
|
||||||
|
<Button onClick={handleSaveClick} variant='default' size={'sm'} className={'mt-2'}>
|
||||||
|
保存
|
||||||
|
</Button>
|
||||||
|
</CloseComponent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const t = useTranslations('user');
|
const t = useTranslations('user');
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@ -32,7 +71,6 @@ export default function Page() {
|
|||||||
return data.data?.list as API.SubscribeGroup[];
|
return data.data?.list as API.SubscribeGroup[];
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProTable<API.User, API.GetUserListParams>
|
<ProTable<API.User, API.GetUserListParams>
|
||||||
action={ref}
|
action={ref}
|
||||||
@ -106,10 +144,42 @@ export default function Page() {
|
|||||||
const method = row.original.auth_methods?.[0];
|
const method = row.original.auth_methods?.[0];
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Badge className='mr-1 uppercase' title={method?.verified ? t('verified') : ''}>
|
<Popover>
|
||||||
{method?.auth_type}
|
<PopoverTrigger>
|
||||||
</Badge>
|
<div className={'flex items-center'}>
|
||||||
{method?.auth_identifier}
|
{method?.auth_identifier}
|
||||||
|
{row.original?.remark ? `(${row.original.remark})` : ''}
|
||||||
|
<FilePenLine size={14} className={'text-primary ml-2'} />
|
||||||
|
</div>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className={'w-64'}>
|
||||||
|
<RemarkForm
|
||||||
|
initialRemark={row.original.remark}
|
||||||
|
CloseComponent={PopoverClose}
|
||||||
|
onSave={async (remark) => {
|
||||||
|
const {
|
||||||
|
auth_methods,
|
||||||
|
user_devices,
|
||||||
|
enable_balance_notify,
|
||||||
|
enable_login_notify,
|
||||||
|
enable_subscribe_notify,
|
||||||
|
enable_trade_notify,
|
||||||
|
updated_at,
|
||||||
|
created_at,
|
||||||
|
id,
|
||||||
|
...rest
|
||||||
|
} = row.original;
|
||||||
|
await updateUserBasicInfo({
|
||||||
|
user_id: id,
|
||||||
|
...rest,
|
||||||
|
remark,
|
||||||
|
} as unknown as API.UpdateUserBasiceInfoRequest);
|
||||||
|
toast.success(t('updateSuccess'));
|
||||||
|
ref.current?.refresh();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
192
apps/admin/app/dashboard/withdraw-ticket/page.tsx
Normal file
192
apps/admin/app/dashboard/withdraw-ticket/page.tsx
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { ProTable, ProTableActions } from '@/components/pro-table';
|
||||||
|
import { getTicket, getTicketList, updateTicketStatus } from '@/services/admin/ticket';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { Button } from '@workspace/ui/components/button';
|
||||||
|
import { Drawer, DrawerContent, DrawerHeader, DrawerTitle } from '@workspace/ui/components/drawer';
|
||||||
|
import { ScrollArea } from '@workspace/ui/components/scroll-area';
|
||||||
|
import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
|
||||||
|
import { cn } from '@workspace/ui/lib/utils';
|
||||||
|
import { formatDate } from '@workspace/ui/utils';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
import NextImage from 'next/legacy/image';
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { toast } from 'sonner';
|
||||||
|
import { UserDetail } from '../user/user-detail';
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
const t = useTranslations('ticket');
|
||||||
|
|
||||||
|
const [ticketId, setTicketId] = useState<any>(null);
|
||||||
|
|
||||||
|
const [message, setMessage] = useState('');
|
||||||
|
|
||||||
|
const { data: ticket, refetch: refetchTicket } = useQuery({
|
||||||
|
queryKey: ['getTicket', ticketId],
|
||||||
|
queryFn: async () => {
|
||||||
|
const { data } = await getTicket({
|
||||||
|
id: ticketId,
|
||||||
|
});
|
||||||
|
return data.data as API.Ticket;
|
||||||
|
},
|
||||||
|
enabled: !!ticketId,
|
||||||
|
refetchInterval: 5000,
|
||||||
|
});
|
||||||
|
|
||||||
|
const scrollRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (scrollRef.current) {
|
||||||
|
scrollRef.current.children[1]?.scrollTo({
|
||||||
|
top: scrollRef.current.children[1].scrollHeight,
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 66);
|
||||||
|
}, [ticket?.follow?.length]);
|
||||||
|
|
||||||
|
const ref = useRef<ProTableActions>(null);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ProTable<API.Ticket, { status: number }>
|
||||||
|
action={ref}
|
||||||
|
header={{
|
||||||
|
title: t('ticketList'),
|
||||||
|
}}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
accessorKey: 'title',
|
||||||
|
header: t('title'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'user_id',
|
||||||
|
header: t('user'),
|
||||||
|
cell: ({ row }) => <UserDetail id={row.original.user_id} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'status',
|
||||||
|
header: t('status.0'),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
'flex items-center gap-2 before:block before:size-1.5 before:animate-pulse before:rounded-full before:ring-2 before:ring-opacity-50',
|
||||||
|
{
|
||||||
|
'before:bg-rose-500 before:ring-rose-500': row.original.status === 1,
|
||||||
|
'before:bg-yellow-500 before:ring-yellow-500': row.original.status === 2,
|
||||||
|
'before:bg-green-500 before:ring-green-500': row.original.status === 3,
|
||||||
|
'before:bg-zinc-500 before:ring-zinc-500': row.original.status === 4,
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{t(`status.${row.original.status}`)}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'updated_at',
|
||||||
|
header: t('updatedAt'),
|
||||||
|
cell: ({ row }) => formatDate(row.getValue('updated_at')),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
params={[
|
||||||
|
{
|
||||||
|
key: 'status',
|
||||||
|
placeholder: t('status.0'),
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: t('close'),
|
||||||
|
value: '4',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
request={async (pagination, filters) => {
|
||||||
|
const { data } = await getTicketList({
|
||||||
|
...pagination,
|
||||||
|
...filters,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
list: data.data?.list || [],
|
||||||
|
total: data.data?.total || 0,
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
actions={{
|
||||||
|
render(row) {
|
||||||
|
if (row.status !== 4) {
|
||||||
|
return [
|
||||||
|
<Button key='reply' size='sm' onClick={() => setTicketId(row.id)}>
|
||||||
|
{t('check')}
|
||||||
|
</Button>,
|
||||||
|
<ConfirmButton
|
||||||
|
key='colse'
|
||||||
|
trigger={
|
||||||
|
<Button size='sm' variant='destructive'>
|
||||||
|
{t('close')}
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
title={t('confirmClose')}
|
||||||
|
description={t('closeWarning')}
|
||||||
|
onConfirm={async () => {
|
||||||
|
await updateTicketStatus({
|
||||||
|
id: row.id,
|
||||||
|
status: 4,
|
||||||
|
});
|
||||||
|
toast.success(t('closeSuccess'));
|
||||||
|
ref.current?.refresh();
|
||||||
|
}}
|
||||||
|
cancelText={t('cancel')}
|
||||||
|
confirmText={t('confirm')}
|
||||||
|
/>,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
<Button key='check' size='sm' onClick={() => setTicketId(row.id)}>
|
||||||
|
{t('check')}
|
||||||
|
</Button>,
|
||||||
|
];
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Drawer
|
||||||
|
open={!!ticketId}
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
if (!open) setTicketId(null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DrawerContent className='container mx-auto h-screen'>
|
||||||
|
<DrawerHeader className='border-b text-left'>
|
||||||
|
<DrawerTitle>{ticket?.title}</DrawerTitle>
|
||||||
|
</DrawerHeader>
|
||||||
|
<ScrollArea className='h-full overflow-hidden' ref={scrollRef}>
|
||||||
|
<div className='flex h-full flex-col gap-4 p-4'>
|
||||||
|
<div className={cn('flex items-center gap-4', {})}>
|
||||||
|
<div className={cn('flex flex-col gap-1', {})}>
|
||||||
|
<div className={cn('bg-accent w-fit rounded-lg p-2 font-medium', {})}>
|
||||||
|
<div> 提现金额:{ticket?.title?.split('-')[1]}</div>
|
||||||
|
<div> 提现方式:{ticket?.description?.split('-')[0]}</div>
|
||||||
|
{ticket?.description?.split('-')[1].includes('data:image') ? (
|
||||||
|
<div>
|
||||||
|
<div>收款码:</div>
|
||||||
|
<NextImage
|
||||||
|
src={ticket?.description?.split('-')[1]}
|
||||||
|
width={300}
|
||||||
|
height={300}
|
||||||
|
className='!size-auto object-cover'
|
||||||
|
alt='image'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>提现地址:{ticket?.description?.split('-')[1]}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -104,6 +104,11 @@ export const navs = [
|
|||||||
url: '/dashboard/ticket',
|
url: '/dashboard/ticket',
|
||||||
icon: 'flat-color-icons:collaboration',
|
icon: 'flat-color-icons:collaboration',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Withdraw Ticket Management',
|
||||||
|
url: '/dashboard/withdraw-ticket',
|
||||||
|
icon: 'flat-color-icons:collaboration',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Document Management',
|
title: 'Document Management',
|
||||||
url: '/dashboard/document',
|
url: '/dashboard/document',
|
||||||
|
|||||||
@ -23,5 +23,6 @@
|
|||||||
"Ticket Management": "Ticket Management",
|
"Ticket Management": "Ticket Management",
|
||||||
"User": "User",
|
"User": "User",
|
||||||
"User Detail": "User Detail",
|
"User Detail": "User Detail",
|
||||||
"User Management": "User Management"
|
"User Management": "User Management",
|
||||||
|
"Withdraw Ticket Management": "Withdraw Management"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,5 +23,6 @@
|
|||||||
"Ticket Management": "工单管理",
|
"Ticket Management": "工单管理",
|
||||||
"User": "用户",
|
"User": "用户",
|
||||||
"User Detail": "用户详情",
|
"User Detail": "用户详情",
|
||||||
"User Management": "用户管理"
|
"User Management": "用户管理",
|
||||||
|
"Withdraw Ticket Management": "提现管理"
|
||||||
}
|
}
|
||||||
|
|||||||
1
apps/admin/services/admin/typings.d.ts
vendored
1
apps/admin/services/admin/typings.d.ts
vendored
@ -1878,6 +1878,7 @@ declare namespace API {
|
|||||||
id: number;
|
id: number;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
balance: number;
|
balance: number;
|
||||||
|
remark: string;
|
||||||
commission: number;
|
commission: number;
|
||||||
gift_amount: number;
|
gift_amount: number;
|
||||||
telegram: number;
|
telegram: number;
|
||||||
|
|||||||
@ -46,10 +46,14 @@ import { Icon } from '@workspace/airo-ui/custom-components/icon';
|
|||||||
import { cn } from '@workspace/airo-ui/lib/utils';
|
import { cn } from '@workspace/airo-ui/lib/utils';
|
||||||
import { formatDate } from '@workspace/airo-ui/utils';
|
import { formatDate } from '@workspace/airo-ui/utils';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
|
import Image from 'next/image';
|
||||||
import NextImage from 'next/legacy/image';
|
import NextImage from 'next/legacy/image';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
|
import { PhotoProvider, PhotoView } from 'react-photo-view';
|
||||||
|
import 'react-photo-view/dist/react-photo-view.css';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const t = useTranslations('ticket');
|
const t = useTranslations('ticket');
|
||||||
|
|
||||||
@ -178,13 +182,13 @@ export default function Page() {
|
|||||||
<CardDescription className='flex gap-2'>
|
<CardDescription className='flex gap-2'>
|
||||||
{item.status !== 4 ? (
|
{item.status !== 4 ? (
|
||||||
<>
|
<>
|
||||||
<AiroButton
|
{item.issue_type === 0 ? (
|
||||||
variant={'primary'}
|
<AiroButton
|
||||||
onClick={() => setTicketId(item.id)}
|
variant={'primary'}
|
||||||
className={'hidden sm:flex'}
|
onClick={() => setTicketId(item.id)}
|
||||||
>
|
className={'hidden sm:flex'}
|
||||||
{t('reply')}
|
/>
|
||||||
</AiroButton>
|
) : null}
|
||||||
<ConfirmButton
|
<ConfirmButton
|
||||||
key='close'
|
key='close'
|
||||||
trigger={
|
trigger={
|
||||||
@ -228,7 +232,28 @@ export default function Page() {
|
|||||||
</li>
|
</li>
|
||||||
<li className='order-2 sm:order-3'>
|
<li className='order-2 sm:order-3'>
|
||||||
<span className='font-normal text-[#225BA9]'>{t('description')}</span>
|
<span className='font-normal text-[#225BA9]'>{t('description')}</span>
|
||||||
<time className={'font-bold'}>{item.description}</time>
|
<time className={'font-bold'}>
|
||||||
|
{item?.description?.includes('data:image') ? (
|
||||||
|
<div>
|
||||||
|
<div>提现方式:{item?.description?.split('-')[0]}</div>
|
||||||
|
<PhotoProvider>
|
||||||
|
<PhotoView src={item?.description?.split('-')[1]}>
|
||||||
|
<Image
|
||||||
|
src={item?.description?.split('-')[1]}
|
||||||
|
height={48}
|
||||||
|
width={48}
|
||||||
|
className={'mx-1 cursor-pointer border'}
|
||||||
|
/>
|
||||||
|
</PhotoView>
|
||||||
|
</PhotoProvider>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<div>提现方式:{item?.description.split('-')[0]}</div>
|
||||||
|
<div>提现地址: {item?.description.split('-')[1]}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</time>
|
||||||
</li>
|
</li>
|
||||||
<li className=''>
|
<li className=''>
|
||||||
<span className='font-normal text-[#225BA9]'>{t('updatedAt')}</span>
|
<span className='font-normal text-[#225BA9]'>{t('updatedAt')}</span>
|
||||||
@ -305,6 +330,7 @@ export default function Page() {
|
|||||||
from: 'User',
|
from: 'User',
|
||||||
type: 1,
|
type: 1,
|
||||||
content: message,
|
content: message,
|
||||||
|
issue_type: 0,
|
||||||
});
|
});
|
||||||
refetchTicket();
|
refetchTicket();
|
||||||
setMessage('');
|
setMessage('');
|
||||||
|
|||||||
@ -0,0 +1,372 @@
|
|||||||
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
import { QueryClient, QueryClientProvider, useMutation } from '@tanstack/react-query';
|
||||||
|
import { AiroButton } from '@workspace/airo-ui/components/AiroButton';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from '@workspace/airo-ui/components/dialog';
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormMessage,
|
||||||
|
} from '@workspace/airo-ui/components/form';
|
||||||
|
import { Input } from '@workspace/airo-ui/components/input';
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@workspace/airo-ui/components/select';
|
||||||
|
import { Icon } from '@workspace/airo-ui/custom-components/icon';
|
||||||
|
import { UploadImage } from '@workspace/airo-ui/custom-components/upload-image';
|
||||||
|
import { FormLabel } from '@workspace/ui/components/form';
|
||||||
|
import { useRef, useState } from 'react';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
// 创建一个 QueryClient 实例
|
||||||
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
|
// 引入实际的 createUserTicket 服务
|
||||||
|
import Modal from '@/components/Modal';
|
||||||
|
import useGlobalStore from '@/config/use-global';
|
||||||
|
import { createUserTicket, getUserTicketList } from '@/services/user/ticket';
|
||||||
|
import { EnhancedInput } from '@workspace/airo-ui/custom-components/enhanced-input';
|
||||||
|
import { Upload } from 'lucide-react';
|
||||||
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
|
interface WalletDialogProps {
|
||||||
|
commission: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const WalletDialog: WalletDialogProps = (props) => {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [pendingData, setPendingData] = useState(null);
|
||||||
|
const ModalRef = useRef(null);
|
||||||
|
const ErrorModalRef = useRef(null);
|
||||||
|
|
||||||
|
const { common } = useGlobalStore();
|
||||||
|
const { currency } = common;
|
||||||
|
|
||||||
|
// 定义支持的账户类型
|
||||||
|
const ACCOUNT_TYPE = ['USDT', '微信', '支付宝'] as const;
|
||||||
|
|
||||||
|
// 根据账户类型定义 Zod 验证模式
|
||||||
|
const formSchema = z
|
||||||
|
.object({
|
||||||
|
type: z.enum(ACCOUNT_TYPE, {
|
||||||
|
required_error: '请选择一个提现方式',
|
||||||
|
}),
|
||||||
|
account: z.string().optional(), // 账号字段变为可选
|
||||||
|
money: z
|
||||||
|
.string()
|
||||||
|
.min(1, '提现金额不能为空')
|
||||||
|
.regex(/^\d+(\.\d+)?$/, '请输入有效的金额')
|
||||||
|
.refine((value) => {
|
||||||
|
const amount = parseFloat(value);
|
||||||
|
return !isNaN(amount) && amount > 0;
|
||||||
|
}, '提现金额必须大于0'),
|
||||||
|
avatar: z.string().optional(), // 图片字段变为可选
|
||||||
|
})
|
||||||
|
.superRefine((data, ctx) => {
|
||||||
|
// 根据提现方式进行条件验证
|
||||||
|
if (data.type === 'USDT') {
|
||||||
|
if (!data.account || data.account.trim().length === 0) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message: 'USDT 账号不能为空',
|
||||||
|
path: ['account'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (data.type === '微信' || data.type === '支付宝') {
|
||||||
|
if (!data.avatar) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message: '请上传图片',
|
||||||
|
path: ['avatar'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
resolver: zodResolver(formSchema),
|
||||||
|
defaultValues: {
|
||||||
|
type: ACCOUNT_TYPE[0],
|
||||||
|
account: '',
|
||||||
|
money: '',
|
||||||
|
avatar: undefined,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 使用 useMutation 来处理表单提交
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationFn: async (data) => {
|
||||||
|
// 构建 title 和 description
|
||||||
|
const title = `提现金额-${data.money}`;
|
||||||
|
let description = '';
|
||||||
|
if (data.type === 'USDT') {
|
||||||
|
description = `${data.type}-${data.account || ''}`;
|
||||||
|
} else if (data.type === '微信' || data.type === '支付宝') {
|
||||||
|
description = `${data.type}-${data.avatar || ''}`;
|
||||||
|
}
|
||||||
|
return createUserTicket({ title, description, issue_type: 1 });
|
||||||
|
},
|
||||||
|
onSuccess: (data) => {
|
||||||
|
console.log('提交成功:', data);
|
||||||
|
toast.success('提交成功,请耐心等待');
|
||||||
|
// 成功后关闭弹窗并重置表单
|
||||||
|
setOpen(false);
|
||||||
|
form.reset();
|
||||||
|
setPendingData(null);
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
console.error('提交失败:', error);
|
||||||
|
// 可以在这里显示一个错误提示
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const currentType = form.watch('type');
|
||||||
|
const money = form.watch('money');
|
||||||
|
const account = form.watch('account');
|
||||||
|
|
||||||
|
// 处理表单提交,先展示确认弹窗或错误弹窗
|
||||||
|
const handleFormSubmit = async (data) => {
|
||||||
|
// 检查提现佣金和当前佣金,如果超过做提示
|
||||||
|
const moneyValue = parseFloat(data.money);
|
||||||
|
if (moneyValue > parseFloat((props.commission / 100).toFixed(2))) {
|
||||||
|
toast.error('提现金额超过佣金总额');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有待处理的工单,如果超过一个则返回
|
||||||
|
const { data: ticketData } = await getUserTicketList({
|
||||||
|
page: 1,
|
||||||
|
size: 1,
|
||||||
|
issue_type: 1,
|
||||||
|
});
|
||||||
|
if (ticketData?.list?.length > 1) {
|
||||||
|
toast.info('已经存在待处理提现,请耐心等待');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moneyValue < 200) {
|
||||||
|
if (ErrorModalRef.current) {
|
||||||
|
ErrorModalRef.current.show();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将数据存储到 state 中,供确认弹窗使用
|
||||||
|
setPendingData(data);
|
||||||
|
if (ModalRef.current) {
|
||||||
|
ModalRef.current.show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 弹窗确认后执行的提交逻辑
|
||||||
|
const handleModalConfirm = () => {
|
||||||
|
if (pendingData) {
|
||||||
|
mutation.mutate(pendingData);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const loading = mutation.isPending;
|
||||||
|
|
||||||
|
// 根据提现方式动态生成弹窗描述
|
||||||
|
const getModalDescription = () => {
|
||||||
|
if (currentType === 'USDT') {
|
||||||
|
const accountInfo = account || '未知地址';
|
||||||
|
return `请确认您的提现地址及金额无误,您将提现${money}RMB至${accountInfo}地址账号。`;
|
||||||
|
} else {
|
||||||
|
return `请确认您的收款码及金额无误,您将提现${money}RMB至对应收款码账户。`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onOpenChange={(newOpen) => {
|
||||||
|
setOpen(newOpen);
|
||||||
|
if (!newOpen) {
|
||||||
|
form.reset();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<AiroButton
|
||||||
|
variant={'link'}
|
||||||
|
className={'min-w-0 px-1 text-sm font-light text-[#225BA9] hover:no-underline'}
|
||||||
|
>
|
||||||
|
提现
|
||||||
|
</AiroButton>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className='sm:w-[675px]'>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className='text-left text-2xl'>佣金提现</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className={'pt-4'}>
|
||||||
|
<div className={'pb-2 text-sm font-semibold text-[#7A7A7A]'}>
|
||||||
|
{currentType === 'USDT'
|
||||||
|
? '将佣金提现至您的个人数字钱包,无手续费'
|
||||||
|
: '该提现方式需10%手续费,该费率由支付平台收取'}
|
||||||
|
</div>
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(handleFormSubmit)} className=''>
|
||||||
|
{/* 提现方式选择 */}
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name='type'
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className={'mb-5'}>
|
||||||
|
<FormLabel className=''>提现方式</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||||
|
<SelectTrigger
|
||||||
|
className={
|
||||||
|
'h-[46px] rounded-full border-4 border-[#225BA9] bg-[#B5C9E2] px-6 focus:ring-0'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectValue placeholder={'提现方式'} />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{ACCOUNT_TYPE.map((type) => (
|
||||||
|
<SelectItem key={type} value={type}>
|
||||||
|
{type}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 提现账号输入(仅当选择USDT时显示) */}
|
||||||
|
{currentType === 'USDT' && (
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name='account'
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className={'mb-2'}>
|
||||||
|
<FormLabel className=''>提现地址</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
className={
|
||||||
|
'h-[46px] rounded-full px-6 shadow-[inset_0_0_7.6px_0_#00000040]'
|
||||||
|
}
|
||||||
|
placeholder='提现地址'
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 提现金额输入 */}
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name='money'
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className={'mb-2'}>
|
||||||
|
<FormLabel className=''>提现金额</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<EnhancedInput
|
||||||
|
type='number'
|
||||||
|
placeholder='不小于200RMB'
|
||||||
|
min={0}
|
||||||
|
value={field.value}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
field.onChange(String(value));
|
||||||
|
}}
|
||||||
|
prefix={currency.currency_symbol}
|
||||||
|
suffix={currency.currency_unit}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 图片上传(仅当选择微信或支付宝时显示) */}
|
||||||
|
{(currentType === '微信' || currentType === '支付宝') && (
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name='avatar'
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className={'mb-2'}>
|
||||||
|
<FormLabel className=''>收款码</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<UploadImage
|
||||||
|
className={`flex h-[46px] items-center justify-center rounded-full border-2 bg-[#EAEAEA] p-4 px-2 text-sm text-[#225BA9] ${field.value ? 'border-[#225BA9]' : 'border-[#EAEAEA]'}`}
|
||||||
|
returnType='base64'
|
||||||
|
onChange={(value) => field.onChange(value)}
|
||||||
|
>
|
||||||
|
点击上传收款码
|
||||||
|
<Upload size={14} className={'ml-3 font-semibold'} />
|
||||||
|
</UploadImage>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 按钮区域 */}
|
||||||
|
<div className='mt-6 flex justify-center gap-2'>
|
||||||
|
<AiroButton
|
||||||
|
type='submit'
|
||||||
|
variant='primary'
|
||||||
|
disabled={loading}
|
||||||
|
className='min-w-[100px]'
|
||||||
|
>
|
||||||
|
{loading && <Icon icon='mdi:loading' className='animate-spin' />}
|
||||||
|
确定
|
||||||
|
</AiroButton>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
<Modal
|
||||||
|
ref={ModalRef}
|
||||||
|
title={'重要提示'}
|
||||||
|
wrapClassName={'w-90'}
|
||||||
|
descriptionClassName={'font-normal text-[#4D4D4D]'}
|
||||||
|
description={getModalDescription()}
|
||||||
|
onConfirm={handleModalConfirm}
|
||||||
|
confirmText={'确认'}
|
||||||
|
cancelText={'取消'}
|
||||||
|
/>
|
||||||
|
<Modal
|
||||||
|
ref={ErrorModalRef}
|
||||||
|
title={'重要提示'}
|
||||||
|
wrapClassName={'w-80'}
|
||||||
|
footerClassName={'hidden'}
|
||||||
|
descriptionClassName={'font-normal text-[#4D4D4D]'}
|
||||||
|
description={'提现仅支持不小于200RMB金额,请重新填写合适金额后再进行提现。'}
|
||||||
|
onConfirm={() => {}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 导出一个包装了 QueryClientProvider 的组件,以确保 useMutation 可用
|
||||||
|
const WrappedWalletDialog = (props) => (
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<WalletDialog {...props} />
|
||||||
|
</QueryClientProvider>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default WrappedWalletDialog;
|
||||||
@ -10,6 +10,7 @@ import Recharge from '@/components/subscribe/recharge';
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import Table from './components/Table/Table';
|
import Table from './components/Table/Table';
|
||||||
import WalletDialog from './components/WalletDialog/WalletDialog';
|
import WalletDialog from './components/WalletDialog/WalletDialog';
|
||||||
|
import WhithdrawDialog from './components/Withdraw/WithdrawDialog';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const t = useTranslations('wallet');
|
const t = useTranslations('wallet');
|
||||||
@ -50,7 +51,10 @@ export default function Page() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className='rounded-[20px] bg-[#EAEAEA] p-4 shadow-sm transition-all duration-300 hover:shadow-md'>
|
<div className='rounded-[20px] bg-[#EAEAEA] p-4 shadow-sm transition-all duration-300 hover:shadow-md'>
|
||||||
<p className='text-sm font-light text-[#666] opacity-80 sm:mb-3'>{t('commission')}</p>
|
<p className='flex items-center justify-between text-sm font-light text-[#666] opacity-80 sm:mb-3'>
|
||||||
|
<span>{t('commission')}</span>
|
||||||
|
<WhithdrawDialog commission={user?.commission} />
|
||||||
|
</p>
|
||||||
<p className='text-xl font-medium text-[#225BA9]'>
|
<p className='text-xl font-medium text-[#225BA9]'>
|
||||||
<Display type='currency' value={user?.commission} />
|
<Display type='currency' value={user?.commission} />
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import useGlobalStore from '@/config/use-global';
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { Button } from '@workspace/airo-ui/components/button';
|
import { Button } from '@workspace/airo-ui/components/button';
|
||||||
import { cn } from '@workspace/airo-ui/lib/utils';
|
import { cn } from '@workspace/airo-ui/lib/utils';
|
||||||
|
import { isBrowser } from '@workspace/airo-ui/utils';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
@ -15,7 +16,7 @@ const CopyShortenedLink = ({ className }: { className?: string }) => {
|
|||||||
const { user } = useGlobalStore();
|
const { user } = useGlobalStore();
|
||||||
|
|
||||||
// 构建长链接,使用用户的唯一标识符作为查询键
|
// 构建长链接,使用用户的唯一标识符作为查询键
|
||||||
const target = `${location?.origin}/?invite=${user?.refer_code}`;
|
const target = isBrowser ? `${location?.origin}/?invite=${user?.refer_code}` : '';
|
||||||
const queryKey = ['short-url', user?.refer_code];
|
const queryKey = ['short-url', user?.refer_code];
|
||||||
|
|
||||||
const { data: shortUrl } = useQuery({
|
const { data: shortUrl } = useQuery({
|
||||||
@ -35,10 +36,9 @@ const CopyShortenedLink = ({ className }: { className?: string }) => {
|
|||||||
|
|
||||||
// 关键步骤:解析 JSON 数据并返回
|
// 关键步骤:解析 JSON 数据并返回
|
||||||
const json = await response.json();
|
const json = await response.json();
|
||||||
console.log('CopyShortened link', json);
|
|
||||||
return json.link ?? null;
|
return json.link ?? null;
|
||||||
},
|
},
|
||||||
enabled: !!user?.refer_code, // 默认不自动执行
|
enabled: !!(user?.refer_code && target), // 默认不自动执行
|
||||||
staleTime: Infinity, // 数据永不过期,除非手动失效
|
staleTime: Infinity, // 数据永不过期,除非手动失效
|
||||||
});
|
});
|
||||||
// 渲染组件
|
// 渲染组件
|
||||||
|
|||||||
@ -340,12 +340,12 @@ const TabContent: React.FC<TabContentProps> = ({
|
|||||||
|
|
||||||
// 使用 useMemo 优化数据处理性能
|
// 使用 useMemo 优化数据处理性能
|
||||||
const yearlyPlans: PlanProps[] = useMemo(
|
const yearlyPlans: PlanProps[] = useMemo(
|
||||||
() => (subscribeData || []).map((item) => processPlanData(item, true)),
|
() => subscribeData?.map((item) => processPlanData(item, true)),
|
||||||
[subscribeData],
|
[subscribeData],
|
||||||
);
|
);
|
||||||
|
|
||||||
const monthlyPlans: PlanProps[] = useMemo(
|
const monthlyPlans: PlanProps[] = useMemo(
|
||||||
() => (subscribeData || []).map((item) => processPlanData(item, false)),
|
() => subscribeData?.map((item) => processPlanData(item, false)),
|
||||||
[subscribeData],
|
[subscribeData],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -42,6 +42,7 @@
|
|||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-copy-to-clipboard": "^5.1.0",
|
"react-copy-to-clipboard": "^5.1.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
|
"react-photo-view": "^1.2.7",
|
||||||
"react-turnstile": "^1.1.4",
|
"react-turnstile": "^1.1.4",
|
||||||
"rtl-detect": "^1.1.2",
|
"rtl-detect": "^1.1.2",
|
||||||
"three": "^0.178.0",
|
"three": "^0.178.0",
|
||||||
|
|||||||
1
apps/user/services/user/typings.d.ts
vendored
1
apps/user/services/user/typings.d.ts
vendored
@ -321,6 +321,7 @@ declare namespace API {
|
|||||||
size: number;
|
size: number;
|
||||||
status?: number;
|
status?: number;
|
||||||
search?: string;
|
search?: string;
|
||||||
|
issue_type?: 0 | 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
type GetUserTicketListRequest = {
|
type GetUserTicketListRequest = {
|
||||||
|
|||||||
3
bun.lock
3
bun.lock
@ -80,6 +80,7 @@
|
|||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-copy-to-clipboard": "^5.1.0",
|
"react-copy-to-clipboard": "^5.1.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
|
"react-photo-view": "^1.2.7",
|
||||||
"react-turnstile": "^1.1.4",
|
"react-turnstile": "^1.1.4",
|
||||||
"rtl-detect": "^1.1.2",
|
"rtl-detect": "^1.1.2",
|
||||||
"three": "^0.178.0",
|
"three": "^0.178.0",
|
||||||
@ -2687,6 +2688,8 @@
|
|||||||
|
|
||||||
"react-markdown": ["react-markdown@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw=="],
|
"react-markdown": ["react-markdown@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw=="],
|
||||||
|
|
||||||
|
"react-photo-view": ["react-photo-view@1.2.7", "", { "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-MfOWVPxuibncRLaycZUNxqYU8D9IA+rbGDDaq6GM8RIoGJal592hEJoRAyRSI7ZxyyJNJTLMUWWL3UIXHJJOpw=="],
|
||||||
|
|
||||||
"react-reconciler": ["react-reconciler@0.31.0", "", { "dependencies": { "scheduler": "^0.25.0" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-7Ob7Z+URmesIsIVRjnLoDGwBEG/tVitidU0nMsqX/eeJaLY89RISO/10ERe0MqmzuKUUB1rmY+h1itMbUHg9BQ=="],
|
"react-reconciler": ["react-reconciler@0.31.0", "", { "dependencies": { "scheduler": "^0.25.0" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-7Ob7Z+URmesIsIVRjnLoDGwBEG/tVitidU0nMsqX/eeJaLY89RISO/10ERe0MqmzuKUUB1rmY+h1itMbUHg9BQ=="],
|
||||||
|
|
||||||
"react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="],
|
"react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="],
|
||||||
|
|||||||
@ -148,7 +148,7 @@ export function EnhancedInput({
|
|||||||
step={0.01}
|
step={0.01}
|
||||||
{...props}
|
{...props}
|
||||||
value={value}
|
value={value}
|
||||||
className='block h-[44px] rounded-full border-none bg-white shadow-[inset_0_0_7.6px_0_rgba(0,0,0,0.25)]'
|
className='block h-[40px] rounded-full border-none bg-white shadow-[inset_0_0_7.6px_0_rgba(0,0,0,0.25)]'
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -10,6 +10,7 @@ const Popover = PopoverPrimitive.Root;
|
|||||||
const PopoverTrigger = PopoverPrimitive.Trigger;
|
const PopoverTrigger = PopoverPrimitive.Trigger;
|
||||||
|
|
||||||
const PopoverAnchor = PopoverPrimitive.Anchor;
|
const PopoverAnchor = PopoverPrimitive.Anchor;
|
||||||
|
const PopoverClose = PopoverPrimitive.Close;
|
||||||
|
|
||||||
const PopoverContent = React.forwardRef<
|
const PopoverContent = React.forwardRef<
|
||||||
React.ElementRef<typeof PopoverPrimitive.Content>,
|
React.ElementRef<typeof PopoverPrimitive.Content>,
|
||||||
@ -30,4 +31,4 @@ const PopoverContent = React.forwardRef<
|
|||||||
));
|
));
|
||||||
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
||||||
|
|
||||||
export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger };
|
export { Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger };
|
||||||
|
|||||||
@ -32,7 +32,6 @@ for ITEM in "${PROJECTS[@]}"; do
|
|||||||
cp -r $PROJECT_PATH/.next/standalone/. $PROJECT_BUILD_DIR/
|
cp -r $PROJECT_PATH/.next/standalone/. $PROJECT_BUILD_DIR/
|
||||||
cp -r $PROJECT_PATH/.next/static $PROJECT_BUILD_DIR/$PROJECT_PATH/.next/
|
cp -r $PROJECT_PATH/.next/static $PROJECT_BUILD_DIR/$PROJECT_PATH/.next/
|
||||||
cp -r $PROJECT_PATH/public $PROJECT_BUILD_DIR/$PROJECT_PATH/
|
cp -r $PROJECT_PATH/public $PROJECT_BUILD_DIR/$PROJECT_PATH/
|
||||||
cp -r $PROJECT_PATH/.env.template $PROJECT_BUILD_DIR/$PROJECT_PATH/.env.template
|
|
||||||
cp -r $PROJECT_PATH/.env $PROJECT_BUILD_DIR/$PROJECT_PATH/.env
|
cp -r $PROJECT_PATH/.env $PROJECT_BUILD_DIR/$PROJECT_PATH/.env
|
||||||
|
|
||||||
# Generate ecosystem.config.js for the project
|
# Generate ecosystem.config.js for the project
|
||||||
|
|||||||
@ -32,7 +32,6 @@ for ITEM in "${PROJECTS[@]}"; do
|
|||||||
cp -r $PROJECT_PATH/.next/standalone/. $PROJECT_BUILD_DIR/
|
cp -r $PROJECT_PATH/.next/standalone/. $PROJECT_BUILD_DIR/
|
||||||
cp -r $PROJECT_PATH/.next/static $PROJECT_BUILD_DIR/$PROJECT_PATH/.next/
|
cp -r $PROJECT_PATH/.next/static $PROJECT_BUILD_DIR/$PROJECT_PATH/.next/
|
||||||
cp -r $PROJECT_PATH/public $PROJECT_BUILD_DIR/$PROJECT_PATH/
|
cp -r $PROJECT_PATH/public $PROJECT_BUILD_DIR/$PROJECT_PATH/
|
||||||
cp -r $PROJECT_PATH/.env.template $PROJECT_BUILD_DIR/$PROJECT_PATH/.env.template
|
|
||||||
cp -f $PROJECT_PATH/.env.prod $PROJECT_BUILD_DIR/$PROJECT_PATH/.env
|
cp -f $PROJECT_PATH/.env.prod $PROJECT_BUILD_DIR/$PROJECT_PATH/.env
|
||||||
|
|
||||||
# Generate ecosystem.config.js for the project
|
# Generate ecosystem.config.js for the project
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user