Files
new-api/web/src/components/table/redemptions/RedemptionsColumnDefs.jsx
T

241 lines
6.2 KiB
React

/*
Copyright (C) 2025 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import React from 'react';
import { Tag, Button, Space, Popover, Dropdown } from '@douyinfe/semi-ui';
import { IconMore } from '@douyinfe/semi-icons';
import { renderQuota, timestamp2string } from '../../../helpers';
import {
REDEMPTION_STATUS,
REDEMPTION_STATUS_MAP,
REDEMPTION_ACTIONS,
REDEMPTION_REDEEM_TYPE,
REDEMPTION_REDEEM_TYPE_MAP,
} from '../../../constants/redemption.constants';
export const isExpired = (record) => {
return (
record.status === REDEMPTION_STATUS.UNUSED &&
record.expired_time !== 0 &&
record.expired_time < Math.floor(Date.now() / 1000)
);
};
const renderTimestamp = (timestamp) => {
return <>{timestamp2string(timestamp)}</>;
};
const renderStatus = (status, record, t) => {
if (isExpired(record)) {
return (
<Tag color='orange' shape='circle'>
{t('Expired')}
</Tag>
);
}
const statusConfig = REDEMPTION_STATUS_MAP[status];
if (statusConfig) {
return (
<Tag color={statusConfig.color} shape='circle'>
{t(statusConfig.text)}
</Tag>
);
}
return (
<Tag color='black' shape='circle'>
{t('Unknown')}
</Tag>
);
};
const renderRedeemType = (redeemType, t) => {
const normalizedType =
redeemType === REDEMPTION_REDEEM_TYPE.SUBSCRIPTION
? REDEMPTION_REDEEM_TYPE.SUBSCRIPTION
: REDEMPTION_REDEEM_TYPE.QUOTA;
const typeConfig = REDEMPTION_REDEEM_TYPE_MAP[normalizedType];
return (
<Tag color={typeConfig.color} shape='circle'>
{t(typeConfig.text)}
</Tag>
);
};
const renderRedeemTarget = (record, t) => {
const redeemType =
record.redeem_type === REDEMPTION_REDEEM_TYPE.SUBSCRIPTION
? REDEMPTION_REDEEM_TYPE.SUBSCRIPTION
: REDEMPTION_REDEEM_TYPE.QUOTA;
if (redeemType === REDEMPTION_REDEEM_TYPE.SUBSCRIPTION) {
const label = record.plan_title || `ID ${record.plan_id || '-'}`;
return (
<Tag color='light-green' shape='circle'>
{`${t('Plan')}: ${label}`}
</Tag>
);
}
return (
<Tag color='grey' shape='circle'>
{renderQuota(parseInt(record.quota))}
</Tag>
);
};
export const getRedemptionsColumns = ({
t,
manageRedemption,
copyText,
setEditingRedemption,
setShowEdit,
showDeleteRedemptionModal,
}) => {
return [
{
title: t('ID'),
dataIndex: 'id',
},
{
title: t('Name'),
dataIndex: 'name',
},
{
title: t('Type'),
dataIndex: 'redeem_type',
render: (text) => <div>{renderRedeemType(text, t)}</div>,
},
{
title: t('Status'),
dataIndex: 'status',
key: 'status',
render: (text, record) => {
return <div>{renderStatus(text, record, t)}</div>;
},
},
{
title: t('Redeem target'),
dataIndex: 'quota',
render: (_, record) => {
return <div>{renderRedeemTarget(record, t)}</div>;
},
},
{
title: t('Created at'),
dataIndex: 'created_time',
render: (text) => {
return <div>{renderTimestamp(text)}</div>;
},
},
{
title: t('Expires at'),
dataIndex: 'expired_time',
render: (text) => {
return <div>{text === 0 ? t('Never') : renderTimestamp(text)}</div>;
},
},
{
title: t('Redeemed by'),
dataIndex: 'used_user_id',
render: (text) => {
return <div>{text === 0 ? t('N/A') : text}</div>;
},
},
{
title: '',
dataIndex: 'operate',
fixed: 'right',
width: 205,
render: (text, record) => {
const moreMenuItems = [
{
node: 'item',
name: t('Delete'),
type: 'danger',
onClick: () => {
showDeleteRedemptionModal(record);
},
},
];
if (record.status === REDEMPTION_STATUS.UNUSED && !isExpired(record)) {
moreMenuItems.push({
node: 'item',
name: t('Disable'),
type: 'warning',
onClick: () => {
manageRedemption(record.id, REDEMPTION_ACTIONS.DISABLE, record);
},
});
} else if (!isExpired(record)) {
moreMenuItems.push({
node: 'item',
name: t('Enable'),
type: 'secondary',
onClick: () => {
manageRedemption(record.id, REDEMPTION_ACTIONS.ENABLE, record);
},
disabled: record.status === REDEMPTION_STATUS.USED,
});
}
return (
<Space>
<Popover
content={record.key}
style={{ padding: 20 }}
position='top'
>
<Button type='tertiary' size='small'>
{t('View')}
</Button>
</Popover>
<Button
size='small'
onClick={async () => {
await copyText(record.key);
}}
>
{t('Copy')}
</Button>
<Button
type='tertiary'
size='small'
onClick={() => {
setEditingRedemption(record);
setShowEdit(true);
}}
disabled={record.status !== REDEMPTION_STATUS.UNUSED}
>
{t('Edit')}
</Button>
<Dropdown
trigger='click'
position='bottomRight'
menu={moreMenuItems}
>
<Button type='tertiary' size='small' icon={<IconMore />} />
</Dropdown>
</Space>
);
},
},
];
};