🚀 feat: Enhance table UX & fix reset actions across Users / Tokens / Redemptions

Users table (UsersColumnDefs.js)
• Merged “Status” into the “Statistics” tag: unified text-color logic, removed duplicate renderStatus / renderOverallStatus helpers.
• Switch now disabled for deleted users.
• Replaced dropdown “More” menu with explicit action buttons (Edit / Promote / Demote / Delete) and set column width to 200 px.
• Deleted unused Dropdown & IconMore imports and tidied redundant code.

Users filters & hooks
• UsersFilters.jsx – store formApi in a ref; reset button clears form then reloads data after 100 ms.
• useUsersData.js – call setLoading(true) at the start of loadUsers so the Query button shows loading on reset / reload.

TokensFilters.jsx & RedemptionsFilters.jsx
• Same ref-based reset pattern with 100 ms debounce to restore working “Reset” buttons.

Other clean-ups
• Removed repeated status strings and unused helper functions.
• Updated import lists to reflect component changes.

Result
– Reset buttons now reliably clear filters and reload data with proper loading feedback.
– Users table shows concise status information and all operation buttons without extra clicks.
This commit is contained in:
t0ng7u
2025-07-20 01:21:06 +08:00
parent 39079e7aff
commit 252fddf3de
6 changed files with 48 additions and 38 deletions

View File

@@ -128,13 +128,13 @@ const SiderBar = ({ onNavigate = () => { } }) => {
const adminItems = useMemo(
() => [
{
text: t('渠道'),
text: t('渠道管理'),
itemKey: 'channel',
to: '/channel',
className: isAdmin() ? '' : 'tableHiddle',
},
{
text: t('兑换码'),
text: t('兑换码管理'),
itemKey: 'redemption',
to: '/redemption',
className: isAdmin() ? '' : 'tableHiddle',

View File

@@ -17,7 +17,7 @@ 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 React, { useRef } from 'react';
import { Form, Button } from '@douyinfe/semi-ui';
import { IconSearch } from '@douyinfe/semi-icons';
@@ -31,20 +31,23 @@ const RedemptionsFilters = ({
}) => {
// Handle form reset and immediate search
const handleReset = (formApi) => {
if (formApi) {
formApi.reset();
// Reset and search immediately
setTimeout(() => {
searchRedemptions();
}, 100);
}
const formApiRef = useRef(null);
const handleReset = () => {
if (!formApiRef.current) return;
formApiRef.current.reset();
setTimeout(() => {
searchRedemptions();
}, 100);
};
return (
<Form
initValues={formInitValues}
getFormApi={(api) => setFormApi(api)}
getFormApi={(api) => {
setFormApi(api);
formApiRef.current = api;
}}
onSubmit={searchRedemptions}
allowEmpty={true}
autoComplete="off"
@@ -76,7 +79,7 @@ const RedemptionsFilters = ({
</Button>
<Button
type="tertiary"
onClick={(_, formApi) => handleReset(formApi)}
onClick={handleReset}
className="flex-1 md:flex-initial md:w-auto"
size="small"
>

View File

@@ -17,7 +17,7 @@ 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 React, { useRef } from 'react';
import { Form, Button } from '@douyinfe/semi-ui';
import { IconSearch } from '@douyinfe/semi-icons';
@@ -30,20 +30,23 @@ const TokensFilters = ({
t,
}) => {
// Handle form reset and immediate search
const handleReset = (formApi) => {
if (formApi) {
formApi.reset();
// Reset and search immediately
setTimeout(() => {
searchTokens();
}, 100);
}
const formApiRef = useRef(null);
const handleReset = () => {
if (!formApiRef.current) return;
formApiRef.current.reset();
setTimeout(() => {
searchTokens();
}, 100);
};
return (
<Form
initValues={formInitValues}
getFormApi={(api) => setFormApi(api)}
getFormApi={(api) => {
setFormApi(api);
formApiRef.current = api;
}}
onSubmit={searchTokens}
allowEmpty={true}
autoComplete="off"
@@ -88,7 +91,7 @@ const TokensFilters = ({
<Button
type='tertiary'
onClick={(_, formApi) => handleReset(formApi)}
onClick={handleReset}
className="flex-1 md:flex-initial md:w-auto"
size="small"
>

View File

@@ -17,7 +17,7 @@ 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 React, { useRef } from 'react';
import { Form, Button } from '@douyinfe/semi-ui';
import { IconSearch } from '@douyinfe/semi-icons';
@@ -34,21 +34,23 @@ const UsersFilters = ({
t
}) => {
// Handle form reset and immediate search
const handleReset = (formApi) => {
if (formApi) {
formApi.reset();
// Reset and search immediately
setTimeout(() => {
loadUsers(1, pageSize);
}, 100);
}
const formApiRef = useRef(null);
const handleReset = () => {
if (!formApiRef.current) return;
formApiRef.current.reset();
setTimeout(() => {
loadUsers(1, pageSize);
}, 100);
};
return (
<Form
initValues={formInitValues}
getFormApi={(api) => setFormApi(api)}
getFormApi={(api) => {
setFormApi(api);
formApiRef.current = api;
}}
onSubmit={() => {
searchUsers(1, pageSize);
}}
@@ -99,7 +101,7 @@ const UsersFilters = ({
</Button>
<Button
type='tertiary'
onClick={(_, formApi) => handleReset(formApi)}
onClick={handleReset}
className="flex-1 md:flex-initial md:w-auto"
size="small"
>

View File

@@ -71,6 +71,7 @@ export const useUsersData = () => {
// Load users data
const loadUsers = async (startIdx, pageSize) => {
setLoading(true);
const res = await API.get(`/api/user/?p=${startIdx}&page_size=${pageSize}`);
const { success, message, data } = res.data;
if (success) {

View File

@@ -165,7 +165,8 @@
"操作失败,重定向至登录界面中...": "Operation failed, redirecting to login page...",
"出现错误,第 ${count} 次重试中...": "Error occurred, retry attempt ${count}...",
"首页": "Home",
"渠道": "Channels",
"渠道": "Channel",
"渠道管理": "Channels",
"令牌": "Tokens",
"兑换": "Redeem",
"充值": "Recharge",
@@ -1487,7 +1488,7 @@
"收益": "Earnings",
"无邀请人": "No Inviter",
"邀请人": "Inviter",
"兑换码管理": "Redemption Code Management",
"兑换码管理": "Redemption Code",
"设置兑换码的基本信息": "Set redemption code basic information",
"设置兑换码的额度和数量": "Set redemption code quota and quantity",
"编辑用户": "Edit User",