Files
FastGPT/projects/app/src/pageComponents/account/usage/UsageTable.tsx
Archer 4ada33e7e6 feat: markdown extension (#3663)
* feat: markdown extension

* media cros

* rerank test

* default price

* perf: default model

* fix: cannot custom provider

* fix: default model select

* update bg

* perf: default model selector

* fix: usage export

* i18n

* fix: rerank

* update init extension

* perf: ip limit check

* doubao model order

* web default modle

* perf: tts selector

* perf: tts error

* qrcode package
2025-02-04 17:25:37 +08:00

181 lines
5.8 KiB
TypeScript

import {
Box,
Button,
Flex,
Table,
TableContainer,
Tbody,
Td,
Th,
Thead,
Tr
} from '@chakra-ui/react';
import { formatNumber } from '@fastgpt/global/common/math/tools';
import { UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants';
import { UsageItemType } from '@fastgpt/global/support/wallet/usage/type';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import MyBox from '@fastgpt/web/components/common/MyBox';
import dayjs from 'dayjs';
import { useTranslation } from 'next-i18next';
import React, { useMemo, useState } from 'react';
import Avatar from '@fastgpt/web/components/common/Avatar';
import { usePagination } from '@fastgpt/web/hooks/usePagination';
import { getUserUsages } from '@/web/support/wallet/usage/api';
import { addDays } from 'date-fns';
import dynamic from 'next/dynamic';
import { UsageFilterParams } from './type';
import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { downloadFetch } from '@/web/common/system/utils';
const UsageDetail = dynamic(() => import('./UsageDetail'));
const UsageTableList = ({
filterParams,
Tabs,
Selectors
}: {
Tabs: React.ReactNode;
Selectors: React.ReactNode;
filterParams: UsageFilterParams;
}) => {
const { t } = useTranslation();
const { dateRange, selectTmbIds, isSelectAllTmb, usageSources, isSelectAllSource, projectName } =
filterParams;
const requestParams = useMemo(() => {
return {
dateStart: dateRange.from || new Date(),
dateEnd: addDays(dateRange.to || new Date(), 1),
sources: isSelectAllSource ? undefined : usageSources,
teamMemberIds: isSelectAllTmb ? undefined : selectTmbIds,
projectName
};
}, [
dateRange.from,
dateRange.to,
isSelectAllSource,
isSelectAllTmb,
projectName,
selectTmbIds,
usageSources
]);
const {
data: usages,
isLoading,
Pagination,
total
} = usePagination(getUserUsages, {
pageSize: 20,
params: requestParams,
refreshDeps: [requestParams]
});
const [usageDetail, setUsageDetail] = useState<UsageItemType>();
const { runAsync: exportUsage } = useRequest2(
async () => {
await downloadFetch({
url: `/api/proApi/support/wallet/usage/exportUsage`,
filename: `usage.csv`,
body: {
...requestParams,
appNameMap: {
['core.app.Question Guide']: t('common:core.app.Question Guide'),
['common:support.wallet.usage.Audio Speech']: t(
'common:support.wallet.usage.Audio Speech'
),
['support.wallet.usage.Whisper']: t('common:support.wallet.usage.Whisper'),
['support.wallet.moduleName.index']: t('common:support.wallet.moduleName.index'),
['support.wallet.moduleName.qa']: t('common:support.wallet.moduleName.qa'),
['core.dataset.training.Auto mode']: t('common:core.dataset.training.Auto mode'),
['common:core.module.template.ai_chat']: t('common:core.module.template.ai_chat')
},
sourcesMap: Object.fromEntries(
Object.entries(UsageSourceMap).map(([key, config]) => [
key,
{
label: t(config.label as any)
}
])
),
title: t('account_usage:export_title')
}
});
},
{
refreshDeps: [requestParams]
}
);
return (
<>
<Box>{Tabs}</Box>
<Flex mt={4} w={'100%'}>
<Box>{Selectors}</Box>
<Box flex={'1'} />
<PopoverConfirm
Trigger={<Button size={'md'}>{t('common:Export')}</Button>}
showCancel
content={t('account_usage:export_confirm_tip', { total })}
onConfirm={exportUsage}
/>
</Flex>
<MyBox position={'relative'} overflowY={'auto'} mt={3} flex={1} isLoading={isLoading}>
<TableContainer>
<Table>
<Thead>
<Tr>
<Th>{t('common:user.Time')}</Th>
<Th>{t('account_usage:member')}</Th>
<Th>{t('account_usage:user_type')}</Th>
<Th>{t('account_usage:project_name')}</Th>
<Th>{t('account_usage:total_points')}</Th>
<Th></Th>
</Tr>
</Thead>
<Tbody fontSize={'sm'}>
{usages.map((item) => (
<Tr key={item.id}>
<Td>{dayjs(item.time).format('YYYY/MM/DD HH:mm:ss')}</Td>
<Td>
<Flex alignItems={'center'} color={'myGray.500'}>
<Avatar src={item.sourceMember.avatar} w={'20px'} mr={1} rounded={'full'} />
{item.sourceMember.name}
</Flex>
</Td>
<Td>{t(UsageSourceMap[item.source]?.label as any) || '-'}</Td>
<Td>{t(item.appName as any) || '-'}</Td>
<Td>{formatNumber(item.totalPoints) || 0}</Td>
<Td>
<Button
size={'sm'}
variant={'whitePrimary'}
onClick={() => setUsageDetail(item)}
>
{t('account_usage:details')}
</Button>
</Td>
</Tr>
))}
</Tbody>
</Table>
{!isLoading && usages.length === 0 && (
<EmptyTip text={t('account_usage:no_usage_records')}></EmptyTip>
)}
</TableContainer>
</MyBox>
<Flex mt={3} justifyContent={'center'}>
<Pagination />
</Flex>
{!!usageDetail && (
<UsageDetail usage={usageDetail} onClose={() => setUsageDetail(undefined)} />
)}
</>
);
};
export default React.memo(UsageTableList);