feat: 修改代码结构
This commit is contained in:
parent
5524d25d9a
commit
525e305f1d
17
apps/admin/.env.prod
Normal file
17
apps/admin/.env.prod
Normal file
@ -0,0 +1,17 @@
|
||||
# Default Language
|
||||
NEXT_PUBLIC_DEFAULT_LANGUAGE=zh-CN
|
||||
|
||||
# Site URL and API URL
|
||||
NEXT_PUBLIC_SITE_URL=https://admin.ppanel.dev
|
||||
NEXT_PUBLIC_API_URL=https://api.airoport.co
|
||||
|
||||
# Default Login User
|
||||
NEXT_PUBLIC_DEFAULT_USER_EMAIL=
|
||||
NEXT_PUBLIC_DEFAULT_USER_PASSWORD=
|
||||
|
||||
# Please put in the .env file, otherwise the i18n command will not work
|
||||
# OpenAI API key and proxy URL required for i18n command (optional)
|
||||
OPENAI_API_KEY=
|
||||
OPENAI_PROXY_URL=
|
||||
|
||||
|
||||
32
apps/user/.env.prod
Normal file
32
apps/user/.env.prod
Normal file
@ -0,0 +1,32 @@
|
||||
# Default Language
|
||||
NEXT_PUBLIC_DEFAULT_LANGUAGE=zh-CN
|
||||
|
||||
# Site URL and API URL
|
||||
NEXT_PUBLIC_SITE_URL=https://user.ppanel.dev
|
||||
NEXT_PUBLIC_API_URL=https://api.airoport.co
|
||||
NEXT_PUBLIC_CDN_URL=https://cdn.jsdelivr.net
|
||||
|
||||
# Home Page Settings
|
||||
NEXT_PUBLIC_HOME_USER_COUNT=999
|
||||
NEXT_PUBLIC_HOME_SERVER_COUNT=999
|
||||
NEXT_PUBLIC_HOME_LOCATION_COUNT=999
|
||||
|
||||
# Contact Email
|
||||
NEXT_PUBLIC_EMAIL=support@ppanel.dev
|
||||
# Community Links
|
||||
NEXT_PUBLIC_TELEGRAM_LINK=https://t.me/ppanel
|
||||
NEXT_PUBLIC_TWITTER_LINK=https://github.com/perfect-panel/ppanel-web
|
||||
NEXT_PUBLIC_DISCORD_LINK=https://github.com/perfect-panel/ppanel-web
|
||||
NEXT_PUBLIC_INSTAGRAM_LINK=https://github.com/perfect-panel/ppanel-web
|
||||
NEXT_PUBLIC_LINKEDIN_LINK=https://github.com/perfect-panel/ppanel-web
|
||||
NEXT_PUBLIC_FACEBOOK_LINK=https://github.com/perfect-panel/ppanel-web
|
||||
NEXT_PUBLIC_GITHUB_LINK=https://github.com/perfect-panel/ppanel-web
|
||||
|
||||
# Default Login User
|
||||
NEXT_PUBLIC_DEFAULT_USER_EMAIL=
|
||||
NEXT_PUBLIC_DEFAULT_USER_PASSWORD=
|
||||
|
||||
# Please put in the .env file, otherwise the i18n command will not work
|
||||
# OpenAI API key and proxy URL required for i18n command (optional)
|
||||
OPENAI_API_KEY=
|
||||
OPENAI_PROXY_URL=
|
||||
@ -18,8 +18,8 @@ export function Header() {
|
||||
const pathname = usePathname();
|
||||
const items = useMemo(() => findNavByUrl(pathname), [pathname]);
|
||||
return (
|
||||
<header className='box-content flex h-[84px] items-center justify-between gap-2 px-4 pt-4 md:hidden'>
|
||||
<SidebarTrigger />
|
||||
<header className='flex h-[84px] w-full items-center justify-end gap-2 px-4 pt-4 md:hidden'>
|
||||
<SidebarTrigger className={'fixed left-4'} />
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList className={'text-[36px] font-semibold'}>
|
||||
{items.map((item, index) => {
|
||||
|
||||
@ -75,8 +75,8 @@ export default function Content() {
|
||||
const { data: orderData } = useQuery({
|
||||
queryKey: ['orderData'],
|
||||
queryFn: async () => {
|
||||
const { data } = await queryOrderList({ status: 5, page: 1, size: 10 });
|
||||
return data?.[0] ?? {};
|
||||
const { data } = await queryOrderList({ status: 5, page: 1, size: 1 });
|
||||
return data?.data?.list?.[0] ?? {};
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -13,8 +13,8 @@ export default async function DashboardLayout({ children }: { children: React.Re
|
||||
<SidebarLeft className='w-[288px] border-r-0 bg-transparent lg:flex' />
|
||||
<SidebarInset className='relative flex-grow overflow-hidden'>
|
||||
{/*<LanguageSwitch />*/}
|
||||
<Header />
|
||||
<div className='h-[calc(100vh-84px)] flex-grow gap-4 overflow-auto p-4 pt-0 sm:pt-9'>
|
||||
<div className='h-screen flex-grow gap-4 overflow-auto p-4 sm:pt-9'>
|
||||
<Header />
|
||||
{children}
|
||||
</div>
|
||||
</SidebarInset>
|
||||
|
||||
@ -5,6 +5,7 @@ import { updateUserNotify } from '@/services/user/user';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Button } from '@workspace/airo-ui/components/button';
|
||||
import { Card } from '@workspace/airo-ui/components/card';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { AiroButton } from '@workspace/airo-ui/components/AiroButton';
|
||||
import {
|
||||
@ -39,6 +40,17 @@ export default function NotifySettings() {
|
||||
enable_trade_notify: user?.enable_trade_notify ?? false,
|
||||
},
|
||||
});
|
||||
// 监听 user 变化,更新表单
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
form.reset({
|
||||
enable_balance_notify: user.enable_balance_notify ?? false,
|
||||
enable_login_notify: user.enable_login_notify ?? false,
|
||||
enable_subscribe_notify: user.enable_subscribe_notify ?? false,
|
||||
enable_trade_notify: user.enable_trade_notify ?? false,
|
||||
});
|
||||
}
|
||||
}, [user, form]);
|
||||
|
||||
async function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||
await updateUserNotify(data);
|
||||
|
||||
@ -0,0 +1,60 @@
|
||||
import { Display } from '@/components/display';
|
||||
import { Empty } from '@/components/empty';
|
||||
import { ProList, ProListActions } from '@/components/pro-list';
|
||||
import { queryUserBalanceLog } from '@/services/user/user';
|
||||
import { Card, CardContent } from '@workspace/airo-ui/components/card';
|
||||
import { formatDate } from '@workspace/airo-ui/utils';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useRef } from 'react';
|
||||
|
||||
const Table: React.FC<{}> = () => {
|
||||
const ref = useRef<ProListActions>(null);
|
||||
const t = useTranslations('wallet');
|
||||
|
||||
return (
|
||||
<ProList<API.UserBalanceLog, Record<string, unknown>>
|
||||
action={ref}
|
||||
request={async (pagination, filter) => {
|
||||
const response = await queryUserBalanceLog({ ...pagination, ...filter });
|
||||
return {
|
||||
list: response.data.data?.list || [],
|
||||
total: response.data.data?.total || 0,
|
||||
};
|
||||
}}
|
||||
renderItem={(item) => {
|
||||
return (
|
||||
<Card className='rounded-[32px] px-[20px] sm:px-[55px]'>
|
||||
<CardContent className='px-0 py-3 text-[10px] sm:p-3 sm:text-sm'>
|
||||
<ul className='grid grid-cols-4 gap-3 *:flex *:flex-col'>
|
||||
<li className='font-semibold'>
|
||||
<span className='text-[#225BA9]'>{t('createdAt')}</span>
|
||||
<time>{formatDate(item.created_at)}</time>
|
||||
</li>
|
||||
<li className='font-semibold'>
|
||||
<span className='text-[#225BA9]'>{t('type.0')}</span>
|
||||
<span>{t(`type.${item.type}`)}</span>
|
||||
</li>
|
||||
<li className='font-semibold'>
|
||||
<span className='text-[#225BA9]'>{t('amount')}</span>
|
||||
<span>
|
||||
<Display type='currency' value={item.amount} />
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li className='font-semibold'>
|
||||
<span className='text-[#225BA9]'>{t('balance')}</span>
|
||||
<span>
|
||||
<Display type='currency' value={item.balance} />
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}}
|
||||
empty={<Empty />}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Table;
|
||||
@ -0,0 +1,106 @@
|
||||
import { queryUserBalanceLog } from '@/services/user/user';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { AiroButton } from '@workspace/airo-ui/components/AiroButton';
|
||||
import { Card, CardContent } from '@workspace/airo-ui/components/card';
|
||||
import {
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@workspace/airo-ui/components/dialog';
|
||||
import { default as Airo_Empty } from '@workspace/airo-ui/custom-components/empty';
|
||||
import { formatDate } from '@workspace/airo-ui/utils';
|
||||
import { Dialog } from '@workspace/ui/components/dialog';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Display } from '@/components/display';
|
||||
import Table from '../Table/Table';
|
||||
|
||||
const WalletDialog: React.FC<{}> = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const t = useTranslations('affiliate');
|
||||
const tWallet = useTranslations('wallet');
|
||||
|
||||
const { data } = useQuery({
|
||||
queryKey: ['walletDialogList'],
|
||||
queryFn: async () => {
|
||||
const data = await queryUserBalanceLog({
|
||||
page: 1,
|
||||
size: 4,
|
||||
} as API.QueryUserAffiliateListRequest);
|
||||
console.log('data', data?.data.data);
|
||||
return data?.data?.data?.list || [];
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Card className='order-2 rounded-[20px] border border-[#EAEAEA] bg-gradient-to-b from-white to-[#EAEAEA] p-6 md:order-none'>
|
||||
<div className='mb-4 flex items-center justify-between'>
|
||||
<h3 className='font-medium text-[#666666] sm:text-xl'>{tWallet('title')}</h3>
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<AiroButton variant={'dangerLink'} className={'min-w-0 px-1 text-sm text-[#225BA9]'}>
|
||||
{t('more')}
|
||||
</AiroButton>
|
||||
</DialogTrigger>
|
||||
<DialogContent className='sm:w-[675px]'>
|
||||
<DialogHeader>
|
||||
<DialogTitle className='text-left text-2xl sm:text-4xl'>
|
||||
{tWallet('title')}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<Table />
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
|
||||
<div className='space-y-2 sm:space-y-4'>
|
||||
{data?.length ? (
|
||||
<div className='relative space-y-3'>
|
||||
<div
|
||||
className={
|
||||
'absolute bottom-0 left-0 right-0 h-[60px] bg-white/30 backdrop-blur-[1px]'
|
||||
}
|
||||
></div>
|
||||
{data?.map((item) => {
|
||||
return (
|
||||
<Card className='rounded-[15px] px-[20px] sm:rounded-[32px] sm:px-[55px]'>
|
||||
<CardContent className='px-0 py-3 text-[10px] sm:p-3 sm:text-sm'>
|
||||
<ul className='grid grid-cols-4 gap-3 *:flex *:flex-col'>
|
||||
<li className='font-semibold'>
|
||||
<span className='text-[#225BA9]'>{tWallet('createdAt')}</span>
|
||||
<time>{formatDate(item.created_at)}</time>
|
||||
</li>
|
||||
<li className='font-semibold'>
|
||||
<span className='text-[#225BA9]'>{tWallet('type.0')}</span>
|
||||
<span>{tWallet(`type.${item.type}`)}</span>
|
||||
</li>
|
||||
<li className='font-semibold'>
|
||||
<span className='text-[#225BA9]'>{tWallet('amount')}</span>
|
||||
<span>
|
||||
<Display type='currency' value={item.amount} />
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li className='font-semibold'>
|
||||
<span className='text-[#225BA9]'>{tWallet('balance')}</span>
|
||||
<span>
|
||||
<Display type='currency' value={item.balance} />
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<Airo_Empty className={'py-0'} description={t('noInvitationRecords')} />
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default WalletDialog;
|
||||
@ -1,27 +1,23 @@
|
||||
'use client';
|
||||
|
||||
import { Display } from '@/components/display';
|
||||
import { ProList, ProListActions } from '@/components/pro-list';
|
||||
import useGlobalStore from '@/config/use-global';
|
||||
import { queryUserBalanceLog } from '@/services/user/user';
|
||||
import { Card, CardContent } from '@workspace/airo-ui/components/card';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useRef } from 'react';
|
||||
|
||||
import { Empty } from '@/components/empty';
|
||||
import Recharge from '@/components/subscribe/recharge';
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
import { Button } from '@workspace/airo-ui/components/button';
|
||||
import { formatDate } from '@workspace/airo-ui/utils';
|
||||
import Link from 'next/link';
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
import { toast } from 'sonner';
|
||||
import Table from './components/Table/Table';
|
||||
import WalletDialog from './components/WalletDialog/WalletDialog';
|
||||
|
||||
export default function Page() {
|
||||
const t = useTranslations('wallet');
|
||||
const dashboardT = useTranslations('dashboard');
|
||||
const { user } = useGlobalStore();
|
||||
const ref = useRef<ProListActions>(null);
|
||||
const totalAssets = (user?.balance || 0) + (user?.commission || 0) + (user?.gift_amount || 0);
|
||||
return (
|
||||
<>
|
||||
@ -92,48 +88,12 @@ export default function Page() {
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<ProList<API.UserBalanceLog, Record<string, unknown>>
|
||||
action={ref}
|
||||
request={async (pagination, filter) => {
|
||||
const response = await queryUserBalanceLog({ ...pagination, ...filter });
|
||||
return {
|
||||
list: response.data.data?.list || [],
|
||||
total: response.data.data?.total || 0,
|
||||
};
|
||||
}}
|
||||
renderItem={(item) => {
|
||||
return (
|
||||
<Card className='rounded-[32px] px-[20px] sm:px-[55px]'>
|
||||
<CardContent className='px-0 py-3 text-[10px] sm:p-3 sm:text-sm'>
|
||||
<ul className='grid grid-cols-4 gap-3 *:flex *:flex-col'>
|
||||
<li className='font-semibold'>
|
||||
<span className='text-[#225BA9]'>{t('createdAt')}</span>
|
||||
<time>{formatDate(item.created_at)}</time>
|
||||
</li>
|
||||
<li className='font-semibold'>
|
||||
<span className='text-[#225BA9]'>{t('type.0')}</span>
|
||||
<span>{t(`type.${item.type}`)}</span>
|
||||
</li>
|
||||
<li className='font-semibold'>
|
||||
<span className='text-[#225BA9]'>{t('amount')}</span>
|
||||
<span>
|
||||
<Display type='currency' value={item.amount} />
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li className='font-semibold'>
|
||||
<span className='text-[#225BA9]'>{t('balance')}</span>
|
||||
<span>
|
||||
<Display type='currency' value={item.balance} />
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}}
|
||||
empty={<Empty />}
|
||||
/>
|
||||
<div className={'hidden sm:block'}>
|
||||
<Table />
|
||||
</div>
|
||||
<div className={'mt-2.5 sm:hidden'}>
|
||||
<WalletDialog />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ export default function LanguageSwitch() {
|
||||
}}
|
||||
>
|
||||
<SvgIcon
|
||||
name={locale === 'en-US' ? 'language' : 'language-zh'}
|
||||
name={locale === 'en-US' ? 'language-zh' : 'language'}
|
||||
className={'cursor-pointer text-[#EAEAEA] hover:text-[#B5C9E2]'}
|
||||
/>
|
||||
<span className='sr-only'>{languages[locale as keyof typeof languages]}</span>
|
||||
|
||||
@ -75,7 +75,6 @@ const Purchase = forwardRef<PurchaseDialogRef, PurchaseProps>((props, ref) => {
|
||||
queryKey: ['preCreateOrder', subscribe?.id, params.quantity],
|
||||
queryFn: async () => {
|
||||
try {
|
||||
console.log('123123', subscribe);
|
||||
const { data } = await preCreateOrder({
|
||||
...params,
|
||||
subscribe_id: subscribe?.id as number,
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
"giftAmount": "Girt Amount",
|
||||
"referralCode": "Referral Code",
|
||||
"referralDetails": "Referral Details",
|
||||
"title": "Invite Records",
|
||||
"totalAssets": "Total Assets",
|
||||
"type": {
|
||||
"0": "Type",
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
"giftAmount": "赠送金额",
|
||||
"referralCode": "返佣邀请码",
|
||||
"referralDetails": "返佣详情",
|
||||
"title": "邀请记录",
|
||||
"totalAssets": "资产概览",
|
||||
"type": {
|
||||
"0": "类型",
|
||||
|
||||
@ -32,11 +32,17 @@ export async function queryUserAffiliateList(
|
||||
}
|
||||
|
||||
/** Query User Balance Log GET /v1/public/user/balance_log */
|
||||
export async function queryUserBalanceLog(options?: { [key: string]: any }) {
|
||||
export async function queryUserBalanceLog(
|
||||
params: API.QueryUserAffiliateListParams,
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
return request<API.Response & { data?: API.QueryUserBalanceLogListResponse }>(
|
||||
'/v1/public/user/balance_log',
|
||||
{
|
||||
method: 'GET',
|
||||
params: {
|
||||
...params,
|
||||
},
|
||||
...(options || {}),
|
||||
},
|
||||
);
|
||||
|
||||
@ -7,7 +7,6 @@ import { getAuthorization, Logout } from './common';
|
||||
|
||||
async function handleError(response: any) {
|
||||
const code = response.data?.code;
|
||||
console.log(1111111, code);
|
||||
if ([40002, 40003, 40004, 40005].includes(code)) {
|
||||
if (isBrowser()) {
|
||||
const t = await getTranslations('common');
|
||||
|
||||
@ -14,10 +14,10 @@ PROJECTS=(
|
||||
)
|
||||
|
||||
# Step 1: Install dependencies
|
||||
#bun install || {
|
||||
# echo "Dependency installation failed"
|
||||
# exit 1
|
||||
#}
|
||||
bun install || {
|
||||
echo "Dependency installation failed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Step 2: Build each project using Turbo
|
||||
for ITEM in "${PROJECTS[@]}"; do
|
||||
|
||||
@ -33,7 +33,7 @@ for ITEM in "${PROJECTS[@]}"; do
|
||||
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/.env.template $PROJECT_BUILD_DIR/$PROJECT_PATH/.env.template
|
||||
cp -r $PROJECT_PATH/.env $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
|
||||
ECOSYSTEM_CONFIG="$PROJECT_BUILD_DIR/ecosystem.config.js"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user