RedemptionsColumnDefs.jsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /*
  2. Copyright (C) 2025 QuantumNous
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <https://www.gnu.org/licenses/>.
  13. For commercial licensing, please contact support@quantumnous.com
  14. */
  15. import React from 'react';
  16. import { Tag, Button, Space, Popover, Dropdown } from '@douyinfe/semi-ui';
  17. import { IconMore } from '@douyinfe/semi-icons';
  18. import { renderQuota, timestamp2string } from '../../../helpers';
  19. import {
  20. REDEMPTION_STATUS,
  21. REDEMPTION_STATUS_MAP,
  22. REDEMPTION_ACTIONS,
  23. } from '../../../constants/redemption.constants';
  24. /**
  25. * Check if redemption code is expired
  26. */
  27. export const isExpired = (record) => {
  28. return (
  29. record.status === REDEMPTION_STATUS.UNUSED &&
  30. record.expired_time !== 0 &&
  31. record.expired_time < Math.floor(Date.now() / 1000)
  32. );
  33. };
  34. /**
  35. * Render timestamp
  36. */
  37. const renderTimestamp = (timestamp) => {
  38. return <>{timestamp2string(timestamp)}</>;
  39. };
  40. /**
  41. * Render redemption code status
  42. */
  43. const renderStatus = (status, record, t) => {
  44. if (isExpired(record)) {
  45. return (
  46. <Tag color='orange' shape='circle'>
  47. {t('已过期')}
  48. </Tag>
  49. );
  50. }
  51. const statusConfig = REDEMPTION_STATUS_MAP[status];
  52. if (statusConfig) {
  53. return (
  54. <Tag color={statusConfig.color} shape='circle'>
  55. {t(statusConfig.text)}
  56. </Tag>
  57. );
  58. }
  59. return (
  60. <Tag color='black' shape='circle'>
  61. {t('未知状态')}
  62. </Tag>
  63. );
  64. };
  65. /**
  66. * Get redemption code table column definitions
  67. */
  68. export const getRedemptionsColumns = ({
  69. t,
  70. manageRedemption,
  71. copyText,
  72. setEditingRedemption,
  73. setShowEdit,
  74. refresh,
  75. redemptions,
  76. activePage,
  77. showDeleteRedemptionModal,
  78. }) => {
  79. return [
  80. {
  81. title: t('ID'),
  82. dataIndex: 'id',
  83. },
  84. {
  85. title: t('名称'),
  86. dataIndex: 'name',
  87. },
  88. {
  89. title: t('状态'),
  90. dataIndex: 'status',
  91. key: 'status',
  92. render: (text, record) => {
  93. return <div>{renderStatus(text, record, t)}</div>;
  94. },
  95. },
  96. {
  97. title: t('额度'),
  98. dataIndex: 'quota',
  99. render: (text) => {
  100. return (
  101. <div>
  102. <Tag color='grey' shape='circle'>
  103. {renderQuota(parseInt(text))}
  104. </Tag>
  105. </div>
  106. );
  107. },
  108. },
  109. {
  110. title: t('创建时间'),
  111. dataIndex: 'created_time',
  112. render: (text) => {
  113. return <div>{renderTimestamp(text)}</div>;
  114. },
  115. },
  116. {
  117. title: t('过期时间'),
  118. dataIndex: 'expired_time',
  119. render: (text) => {
  120. return <div>{text === 0 ? t('永不过期') : renderTimestamp(text)}</div>;
  121. },
  122. },
  123. {
  124. title: t('兑换人ID'),
  125. dataIndex: 'used_user_id',
  126. render: (text) => {
  127. return <div>{text === 0 ? t('无') : text}</div>;
  128. },
  129. },
  130. {
  131. title: '',
  132. dataIndex: 'operate',
  133. fixed: 'right',
  134. width: 205,
  135. render: (text, record) => {
  136. // Create dropdown menu items for more operations
  137. const moreMenuItems = [
  138. {
  139. node: 'item',
  140. name: t('删除'),
  141. type: 'danger',
  142. onClick: () => {
  143. showDeleteRedemptionModal(record);
  144. },
  145. },
  146. ];
  147. if (record.status === REDEMPTION_STATUS.UNUSED && !isExpired(record)) {
  148. moreMenuItems.push({
  149. node: 'item',
  150. name: t('禁用'),
  151. type: 'warning',
  152. onClick: () => {
  153. manageRedemption(record.id, REDEMPTION_ACTIONS.DISABLE, record);
  154. },
  155. });
  156. } else if (!isExpired(record)) {
  157. moreMenuItems.push({
  158. node: 'item',
  159. name: t('启用'),
  160. type: 'secondary',
  161. onClick: () => {
  162. manageRedemption(record.id, REDEMPTION_ACTIONS.ENABLE, record);
  163. },
  164. disabled: record.status === REDEMPTION_STATUS.USED,
  165. });
  166. }
  167. return (
  168. <Space>
  169. <Popover
  170. content={record.key}
  171. style={{ padding: 20 }}
  172. position='top'
  173. >
  174. <Button type='tertiary' size='small'>
  175. {t('查看')}
  176. </Button>
  177. </Popover>
  178. <Button
  179. size='small'
  180. onClick={async () => {
  181. await copyText(record.key);
  182. }}
  183. >
  184. {t('复制')}
  185. </Button>
  186. <Button
  187. type='tertiary'
  188. size='small'
  189. onClick={() => {
  190. setEditingRedemption(record);
  191. setShowEdit(true);
  192. }}
  193. disabled={record.status !== REDEMPTION_STATUS.UNUSED}
  194. >
  195. {t('编辑')}
  196. </Button>
  197. <Dropdown
  198. trigger='click'
  199. position='bottomRight'
  200. menu={moreMenuItems}
  201. >
  202. <Button type='tertiary' size='small' icon={<IconMore />} />
  203. </Dropdown>
  204. </Space>
  205. );
  206. },
  207. },
  208. ];
  209. };