mirror of
https://github.com/perfect-panel/ppanel-web.git
synced 2026-02-06 03:30:25 -05:00
244 lines
9.0 KiB
TypeScript
244 lines
9.0 KiB
TypeScript
'use client';
|
|
|
|
import { Display } from '@/components/display';
|
|
import { ProTable } from '@/components/pro-table';
|
|
import { queryQuotaTaskList } from '@/services/admin/marketing';
|
|
import { getSubscribeList } from '@/services/admin/subscribe';
|
|
import { formatDate } from '@/utils/common';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import { Badge } from '@workspace/ui/components/badge';
|
|
import { ScrollArea } from '@workspace/ui/components/scroll-area';
|
|
import {
|
|
Sheet,
|
|
SheetContent,
|
|
SheetHeader,
|
|
SheetTitle,
|
|
SheetTrigger,
|
|
} from '@workspace/ui/components/sheet';
|
|
import { Icon } from '@workspace/ui/custom-components/icon';
|
|
import { useTranslations } from 'next-intl';
|
|
import { useState } from 'react';
|
|
|
|
export default function QuotaTaskManager() {
|
|
const t = useTranslations('marketing');
|
|
const [open, setOpen] = useState(false);
|
|
|
|
// Get subscribe list to show subscription names
|
|
const { data: subscribeList } = useQuery({
|
|
queryKey: ['getSubscribeList', 'all'],
|
|
queryFn: async () => {
|
|
const { data } = await getSubscribeList({
|
|
page: 1,
|
|
size: 999999999,
|
|
});
|
|
return data.data?.list as API.SubscribeItem[];
|
|
},
|
|
});
|
|
|
|
// Create a map for quick lookup of subscription names
|
|
const subscribeMap =
|
|
subscribeList?.reduce(
|
|
(acc, subscribe) => {
|
|
acc[subscribe.id!] = subscribe.name!;
|
|
return acc;
|
|
},
|
|
{} as Record<number, string>,
|
|
) || {};
|
|
|
|
const getStatusBadge = (status: number) => {
|
|
const statusConfig = {
|
|
0: { label: t('notStarted'), variant: 'secondary' as const },
|
|
1: { label: t('inProgress'), variant: 'default' as const },
|
|
2: { label: t('completed'), variant: 'default' as const },
|
|
};
|
|
|
|
const config = statusConfig[status as keyof typeof statusConfig] || {
|
|
label: `${t('status')} ${status}`,
|
|
variant: 'secondary' as const,
|
|
};
|
|
|
|
return <Badge variant={config.variant}>{config.label}</Badge>;
|
|
};
|
|
|
|
return (
|
|
<Sheet open={open} onOpenChange={setOpen}>
|
|
<SheetTrigger asChild>
|
|
<div className='flex cursor-pointer items-center justify-between transition-colors'>
|
|
<div className='flex items-center gap-3'>
|
|
<div className='bg-primary/10 flex h-10 w-10 items-center justify-center rounded-lg'>
|
|
<Icon icon='mdi:database-plus' className='text-primary h-5 w-5' />
|
|
</div>
|
|
<div className='flex-1'>
|
|
<p className='font-medium'>{t('quotaTaskManager')}</p>
|
|
<p className='text-muted-foreground text-sm'>{t('viewAndManageQuotaTasks')}</p>
|
|
</div>
|
|
</div>
|
|
<Icon icon='mdi:chevron-right' className='size-6' />
|
|
</div>
|
|
</SheetTrigger>
|
|
<SheetContent className='w-[1000px] max-w-full md:max-w-screen-lg'>
|
|
<SheetHeader>
|
|
<SheetTitle>{t('quotaTasks')}</SheetTitle>
|
|
</SheetHeader>
|
|
<ScrollArea className='-mx-6 h-[calc(100dvh-48px-36px-env(safe-area-inset-top))] px-6'>
|
|
<div className='mt-4 space-y-4'>
|
|
{open && (
|
|
<ProTable<API.QuotaTask, API.QueryQuotaTaskListParams>
|
|
columns={[
|
|
{
|
|
accessorKey: 'subscribers',
|
|
header: t('subscribers'),
|
|
size: 200,
|
|
cell: ({ row }) => {
|
|
const subscribers = row.getValue('subscribers') as number[];
|
|
const subscriptionNames =
|
|
subscribers?.map((id) => subscribeMap[id]).filter(Boolean) || [];
|
|
|
|
if (subscriptionNames.length === 0) {
|
|
return (
|
|
<span className='text-muted-foreground text-sm'>
|
|
{t('noSubscriptions')}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className='flex flex-wrap gap-1'>
|
|
{subscriptionNames.map((name, index) => (
|
|
<span key={index} className='bg-muted rounded px-2 py-1 text-xs'>
|
|
{name}
|
|
</span>
|
|
))}
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
accessorKey: 'is_active',
|
|
header: t('validOnly'),
|
|
size: 120,
|
|
cell: ({ row }) => {
|
|
const isActive = row.getValue('is_active') as boolean;
|
|
return <span className='text-sm'>{isActive ? t('yes') : t('no')}</span>;
|
|
},
|
|
},
|
|
{
|
|
accessorKey: 'reset_traffic',
|
|
header: t('resetTraffic'),
|
|
size: 120,
|
|
cell: ({ row }) => {
|
|
const resetTraffic = row.getValue('reset_traffic') as boolean;
|
|
return <span className='text-sm'>{resetTraffic ? t('yes') : t('no')}</span>;
|
|
},
|
|
},
|
|
{
|
|
accessorKey: 'gift_value',
|
|
header: t('giftAmount'),
|
|
size: 120,
|
|
cell: ({ row }) => {
|
|
const giftValue = row.getValue('gift_value') as number;
|
|
const task = row.original as API.QuotaTask;
|
|
const giftType = task.gift_type;
|
|
|
|
return (
|
|
<div className='text-sm font-medium'>
|
|
{giftType === 1 ? (
|
|
<Display type='currency' value={giftValue} />
|
|
) : (
|
|
`${giftValue}%`
|
|
)}
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
accessorKey: 'days',
|
|
header: t('quotaDays'),
|
|
size: 100,
|
|
cell: ({ row }) => {
|
|
const days = row.getValue('days') as number;
|
|
return (
|
|
<span className='font-medium'>
|
|
{days} {t('days')}
|
|
</span>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
accessorKey: 'time_range',
|
|
header: t('timeRange'),
|
|
size: 180,
|
|
cell: ({ row }) => {
|
|
const task = row.original as API.QuotaTask;
|
|
const startTime = task.start_time;
|
|
const endTime = task.end_time;
|
|
|
|
if (!startTime && !endTime) {
|
|
return (
|
|
<span className='text-muted-foreground text-sm'>{t('noTimeLimit')}</span>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className='space-y-1 text-xs'>
|
|
{startTime && (
|
|
<div>
|
|
{t('startTime')}: {formatDate(startTime)}
|
|
</div>
|
|
)}
|
|
{endTime && (
|
|
<div>
|
|
{t('endTime')}: {formatDate(endTime)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
accessorKey: 'status',
|
|
header: t('status'),
|
|
size: 100,
|
|
cell: ({ row }) => getStatusBadge(row.getValue('status') as number),
|
|
},
|
|
{
|
|
accessorKey: 'created_at',
|
|
header: t('createdAt'),
|
|
size: 150,
|
|
cell: ({ row }) => {
|
|
const createdAt = row.getValue('created_at') as number;
|
|
return formatDate(createdAt);
|
|
},
|
|
},
|
|
]}
|
|
request={async (pagination, filters) => {
|
|
const response = await queryQuotaTaskList({
|
|
...filters,
|
|
page: pagination.page,
|
|
size: pagination.size,
|
|
});
|
|
return {
|
|
list: response.data?.data?.list || [],
|
|
total: response.data?.data?.total || 0,
|
|
};
|
|
}}
|
|
params={[
|
|
{
|
|
key: 'status',
|
|
placeholder: t('status'),
|
|
options: [
|
|
{ label: t('notStarted'), value: '0' },
|
|
{ label: t('inProgress'), value: '1' },
|
|
{ label: t('completed'), value: '2' },
|
|
],
|
|
},
|
|
]}
|
|
/>
|
|
)}
|
|
</div>
|
|
</ScrollArea>
|
|
</SheetContent>
|
|
</Sheet>
|
|
);
|
|
}
|