feat(affiliate): Affiliate Detail

This commit is contained in:
web@ppanel 2024-11-21 14:48:24 +07:00
parent 98c1c3047f
commit a782c17a59
14 changed files with 135 additions and 28 deletions

View File

@ -0,0 +1,76 @@
'use client';
import { Display } from '@/components/display';
import { ProList } from '@/components/pro-list';
import useGlobalStore from '@/config/use-global';
import { queryUserAffiliate } from '@/services/user/user';
import { formatDate } from '@repo/ui/utils';
import { Button } from '@shadcn/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@shadcn/ui/card';
import { toast } from '@shadcn/ui/lib/sonner';
import { useTranslations } from 'next-intl';
import { useState } from 'react';
export default function Page() {
const t = useTranslations('affiliate');
const { user } = useGlobalStore();
const [sum, setSum] = useState<number>();
return (
<div className='flex flex-col gap-4'>
<Card>
<CardHeader>
<CardTitle>{t('totalCommission')}</CardTitle>
<CardDescription>{t('commissionInfo')}</CardDescription>
</CardHeader>
<CardContent className='text-2xl font-bold'>
<Display type='currency' value={sum} />
</CardContent>
</Card>
<Card>
<CardHeader className='flex flex-row items-center justify-between space-y-0'>
<CardTitle>{t('inviteCode')}</CardTitle>
<Button
onClick={() => {
navigator.clipboard.writeText(`${location.origin}/auth?invite=${user?.refer_code}`);
toast.success(t('copySuccess'));
}}
>
{t('copyInviteLink')}
</Button>
</CardHeader>
<CardContent className='text-2xl font-bold'>{user?.refer_code}</CardContent>
</Card>
<ProList<API.UserAffiliate, Record<string, unknown>>
request={async (pagination, filter) => {
const response = await queryUserAffiliate({ ...pagination, ...filter });
setSum(response.data.data?.sum);
return {
list: response.data.data?.list || [],
total: response.data.data?.total || 0,
};
}}
header={{
title: t('inviteRecords'),
}}
renderItem={(item) => {
return (
<Card className='overflow-hidden'>
<CardContent className='p-3 text-sm'>
<ul className='grid grid-cols-2 gap-3 *:flex *:flex-col'>
<li className='font-semibold'>
<span className='text-muted-foreground'>{t('userEmail')}</span>
<span>{item.email}</span>
</li>
<li className='font-semibold'>
<span className='text-muted-foreground'>{t('registrationTime')}</span>
<time>{formatDate(item.registered_at)}</time>
</li>
</ul>
</CardContent>
</Card>
);
}}
/>
</div>
);
}

View File

@ -25,20 +25,6 @@ export function SidebarRight({ ...props }: React.ComponentProps<typeof Sidebar>)
<Display type='currency' value={user?.balance} />
</CardContent>
</Card>
<Card>
<CardHeader className='flex flex-row items-center justify-between space-y-0 p-3 pb-2'>
<CardTitle className='text-sm font-medium'>{t('totalCommission')}</CardTitle>
<Icon icon='mdi:money' className='text-muted-foreground text-2xl' />
</CardHeader>
<CardContent className='p-3 text-2xl font-bold'>0.00</CardContent>
</Card>
<Card>
<CardHeader className='flex flex-row items-center justify-between space-y-0 p-3 pb-2'>
<CardTitle className='text-sm font-medium'>{t('invitees')}</CardTitle>
<Icon icon='mdi:users' className='text-muted-foreground text-2xl' />
</CardHeader>
<CardContent className='p-3 text-2xl font-bold'>0</CardContent>
</Card>
<Card>
<CardHeader className='flex flex-row items-center justify-between space-y-0 p-3 pb-2'>
<CardTitle className='text-sm font-medium'>{t('inviteCode')}</CardTitle>

View File

@ -37,6 +37,11 @@ export const navs = [
icon: 'uil:wallet',
title: 'wallet',
},
{
url: '/affiliate',
icon: 'uil:users-alt',
title: 'affiliate',
},
],
},
{

View File

@ -0,0 +1,10 @@
{
"commissionInfo": "Statistics of the commission, automatically transferred to balance",
"copyInviteLink": "Copy Invite Link",
"copySuccess": "Copied Successfully",
"inviteCode": "Invite Code",
"inviteRecords": "Invite Records",
"registrationTime": "Registration Time",
"totalCommission": "Total Commission",
"userEmail": "User Email"
}

View File

@ -3,7 +3,5 @@
"copyInviteLink": "Copy Invite Link",
"copySuccess": "Invite Link Copied Successfully",
"inviteCode": "Invite Code",
"invitees": "Invitees",
"recharge": "Recharge",
"totalCommission": "Total Commission"
"recharge": "Recharge"
}

View File

@ -1,4 +1,5 @@
{
"affiliate": "My Invitation",
"announcement": "Announcement List",
"dashboard": "Dashboard",
"document": "Documentation",

View File

@ -23,6 +23,7 @@ export default getRequestConfig(async () => {
wallet: (await import(`./${locale}/wallet.json`)).default,
ticket: (await import(`./${locale}/ticket.json`)).default,
document: (await import(`./${locale}/document.json`)).default,
affiliate: (await import(`./${locale}/affiliate.json`)).default,
};
return {

View File

@ -0,0 +1,10 @@
{
"commissionInfo": "统计金额,邀请佣金自动转入余额",
"copyInviteLink": "复制邀请链接",
"copySuccess": "复制成功",
"inviteCode": "邀请码",
"inviteRecords": "邀请记录",
"registrationTime": "注册时间",
"totalCommission": "佣金总额",
"userEmail": "用户邮箱"
}

View File

@ -3,7 +3,5 @@
"copyInviteLink": "复制邀请链接",
"copySuccess": "邀请链接复制成功",
"inviteCode": "邀请码",
"invitees": "邀请人数",
"recharge": "充值",
"totalCommission": "佣金总额"
"recharge": "充值"
}

View File

@ -1,4 +1,5 @@
{
"affiliate": "我的邀请",
"announcement": "公告列表",
"dashboard": "主页",
"document": "使用文档",

View File

@ -21,3 +21,11 @@ export async function getGlobalConfig(options?: { [key: string]: any }) {
...(options || {}),
});
}
/** Get Tos Content GET /v1/common/site/tos */
export async function getTos(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.GetTosResponse }>('/v1/common/site/tos', {
method: 'GET',
...(options || {}),
});
}

View File

@ -25,6 +25,10 @@ declare namespace API {
subscribe: SubscribeConfig;
};
type GetTosResponse = {
tos_content: string;
};
type InviteConfig = {
forced_invite: boolean;
};

View File

@ -212,6 +212,7 @@ declare namespace API {
};
type QueryUserAffiliateResponse = {
sum: number;
list: UserAffiliate[];
total: number;
};
@ -353,7 +354,6 @@ declare namespace API {
type UserAffiliate = {
email: string;
avatar: string;
telegram: number;
registered_at: number;
enable: boolean;
};

View File

@ -3,6 +3,7 @@
import { Alert, AlertDescription, AlertTitle } from '@shadcn/ui/alert';
import { Button } from '@shadcn/ui/button';
import { Checkbox } from '@shadcn/ui/checkbox';
import { cn } from '@shadcn/ui/lib/utils';
import {
ColumnFiltersState,
getCoreRowModel,
@ -142,12 +143,16 @@ export function ProList<TData, TValue extends Record<string, unknown>>({
)}
</div>
<div className='flex flex-1 items-center justify-end gap-2'>
{params && params?.length > 0 && (
<>
<Button variant='outline' className='h-8 w-8 p-2' onClick={fetchData}>
<RefreshCcw className='h-4 w-4' />
</Button>
<Button variant='outline' className='h-8 w-8 p-2' onClick={reset}>
<ListRestart className='h-4 w-4' />
</Button>
</>
)}
{header?.toolbar}
</div>
</div>
@ -161,7 +166,11 @@ export function ProList<TData, TValue extends Record<string, unknown>>({
</Alert>
)}
<div className='relative overflow-x-auto'>
<div
className={cn('relative overflow-x-auto', {
'rounded-xl border': data.length === 0,
})}
>
<div className='grid grid-cols-1 gap-4'>
{data.length ? (
data.map((item, index) => {