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

217 lines
5.5 KiB
React
Raw Normal View History

/*
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
*/
♻️ refactor(components): restructure RedemptionsTable to modular architecture Refactor the monolithic RedemptionsTable component (614 lines) into a clean, modular structure following the established tokens component pattern. ### Changes Made: **New Components:** - `RedemptionsColumnDefs.js` - Extract table column definitions and render logic - `RedemptionsActions.jsx` - Extract action buttons (add, batch copy, clear invalid) - `RedemptionsFilters.jsx` - Extract search and filter form components - `RedemptionsDescription.jsx` - Extract description area component - `redemptions/index.jsx` - Main container component managing state and composition **New Hook:** - `useRedemptionsData.js` - Extract all data management, CRUD operations, and business logic **New Constants:** - `redemption.constants.js` - Extract redemption status, actions, and form constants **Architecture Changes:** - Transform RedemptionsTable.jsx into pure table rendering component - Move state management and component composition to index.jsx - Implement consistent prop drilling pattern matching tokens module - Add memoization for performance optimization - Centralize translation function distribution ### Benefits: - **Maintainability**: Each component has single responsibility - **Reusability**: Components and hooks can be used elsewhere - **Testability**: Individual modules can be unit tested - **Team Collaboration**: Multiple developers can work on different modules - **Consistency**: Follows established architectural patterns ### File Structure: ``` redemptions/ ├── index.jsx # Main container (state + composition) ├── RedemptionsTable.jsx # Pure table component ├── RedemptionsActions.jsx # Action buttons ├── RedemptionsFilters.jsx # Search/filter form ├── RedemptionsDescription.jsx # Description area └── RedemptionsColumnDefs.js # Column definitions
2025-07-19 00:12:04 +08:00
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 } from '../../../constants/redemption.constants';
/**
* Check if redemption code is expired
*/
export const isExpired = (record) => {
return record.status === REDEMPTION_STATUS.UNUSED &&
record.expired_time !== 0 &&
record.expired_time < Math.floor(Date.now() / 1000);
};
/**
* Render timestamp
*/
const renderTimestamp = (timestamp) => {
return <>{timestamp2string(timestamp)}</>;
};
/**
* Render redemption code status
*/
const renderStatus = (status, record, t) => {
if (isExpired(record)) {
return (
<Tag color='orange' shape='circle'>{t('已过期')}</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('未知状态')}
</Tag>
);
};
/**
* Get redemption code table column definitions
*/
export const getRedemptionsColumns = ({
t,
manageRedemption,
copyText,
setEditingRedemption,
setShowEdit,
refresh,
redemptions,
activePage,
showDeleteRedemptionModal
}) => {
return [
{
title: t('ID'),
dataIndex: 'id',
},
{
title: t('名称'),
dataIndex: 'name',
},
{
title: t('状态'),
dataIndex: 'status',
key: 'status',
render: (text, record) => {
return <div>{renderStatus(text, record, t)}</div>;
},
},
{
title: t('额度'),
dataIndex: 'quota',
render: (text) => {
return (
<div>
<Tag color='grey' shape='circle'>
{renderQuota(parseInt(text))}
</Tag>
</div>
);
},
},
{
title: t('创建时间'),
dataIndex: 'created_time',
render: (text) => {
return <div>{renderTimestamp(text)}</div>;
},
},
{
title: t('过期时间'),
dataIndex: 'expired_time',
render: (text) => {
return <div>{text === 0 ? t('永不过期') : renderTimestamp(text)}</div>;
},
},
{
title: t('兑换人ID'),
dataIndex: 'used_user_id',
render: (text) => {
return <div>{text === 0 ? t('无') : text}</div>;
},
},
{
title: '',
dataIndex: 'operate',
fixed: 'right',
width: 205,
render: (text, record) => {
// Create dropdown menu items for more operations
const moreMenuItems = [
{
node: 'item',
name: t('删除'),
type: 'danger',
onClick: () => {
showDeleteRedemptionModal(record);
},
}
];
if (record.status === REDEMPTION_STATUS.UNUSED && !isExpired(record)) {
moreMenuItems.push({
node: 'item',
name: t('禁用'),
type: 'warning',
onClick: () => {
manageRedemption(record.id, REDEMPTION_ACTIONS.DISABLE, record);
},
});
} else if (!isExpired(record)) {
moreMenuItems.push({
node: 'item',
name: t('启用'),
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('查看')}
</Button>
</Popover>
<Button
size="small"
onClick={async () => {
await copyText(record.key);
}}
>
{t('复制')}
</Button>
<Button
type='tertiary'
size="small"
onClick={() => {
setEditingRedemption(record);
setShowEdit(true);
}}
disabled={record.status !== REDEMPTION_STATUS.UNUSED}
>
{t('编辑')}
</Button>
<Dropdown
trigger='click'
position='bottomRight'
menu={moreMenuItems}
>
<Button
type='tertiary'
size="small"
icon={<IconMore />}
/>
</Dropdown>
</Space>
);
},
},
];
};