🚀 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:
@@ -128,13 +128,13 @@ const SiderBar = ({ onNavigate = () => { } }) => {
|
|||||||
const adminItems = useMemo(
|
const adminItems = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
text: t('渠道'),
|
text: t('渠道管理'),
|
||||||
itemKey: 'channel',
|
itemKey: 'channel',
|
||||||
to: '/channel',
|
to: '/channel',
|
||||||
className: isAdmin() ? '' : 'tableHiddle',
|
className: isAdmin() ? '' : 'tableHiddle',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: t('兑换码'),
|
text: t('兑换码管理'),
|
||||||
itemKey: 'redemption',
|
itemKey: 'redemption',
|
||||||
to: '/redemption',
|
to: '/redemption',
|
||||||
className: isAdmin() ? '' : 'tableHiddle',
|
className: isAdmin() ? '' : 'tableHiddle',
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
For commercial licensing, please contact support@quantumnous.com
|
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 { Form, Button } from '@douyinfe/semi-ui';
|
||||||
import { IconSearch } from '@douyinfe/semi-icons';
|
import { IconSearch } from '@douyinfe/semi-icons';
|
||||||
|
|
||||||
@@ -31,20 +31,23 @@ const RedemptionsFilters = ({
|
|||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
// Handle form reset and immediate search
|
// Handle form reset and immediate search
|
||||||
const handleReset = (formApi) => {
|
const formApiRef = useRef(null);
|
||||||
if (formApi) {
|
|
||||||
formApi.reset();
|
const handleReset = () => {
|
||||||
// Reset and search immediately
|
if (!formApiRef.current) return;
|
||||||
setTimeout(() => {
|
formApiRef.current.reset();
|
||||||
searchRedemptions();
|
setTimeout(() => {
|
||||||
}, 100);
|
searchRedemptions();
|
||||||
}
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
initValues={formInitValues}
|
initValues={formInitValues}
|
||||||
getFormApi={(api) => setFormApi(api)}
|
getFormApi={(api) => {
|
||||||
|
setFormApi(api);
|
||||||
|
formApiRef.current = api;
|
||||||
|
}}
|
||||||
onSubmit={searchRedemptions}
|
onSubmit={searchRedemptions}
|
||||||
allowEmpty={true}
|
allowEmpty={true}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
@@ -76,7 +79,7 @@ const RedemptionsFilters = ({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="tertiary"
|
type="tertiary"
|
||||||
onClick={(_, formApi) => handleReset(formApi)}
|
onClick={handleReset}
|
||||||
className="flex-1 md:flex-initial md:w-auto"
|
className="flex-1 md:flex-initial md:w-auto"
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
For commercial licensing, please contact support@quantumnous.com
|
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 { Form, Button } from '@douyinfe/semi-ui';
|
||||||
import { IconSearch } from '@douyinfe/semi-icons';
|
import { IconSearch } from '@douyinfe/semi-icons';
|
||||||
|
|
||||||
@@ -30,20 +30,23 @@ const TokensFilters = ({
|
|||||||
t,
|
t,
|
||||||
}) => {
|
}) => {
|
||||||
// Handle form reset and immediate search
|
// Handle form reset and immediate search
|
||||||
const handleReset = (formApi) => {
|
const formApiRef = useRef(null);
|
||||||
if (formApi) {
|
|
||||||
formApi.reset();
|
const handleReset = () => {
|
||||||
// Reset and search immediately
|
if (!formApiRef.current) return;
|
||||||
setTimeout(() => {
|
formApiRef.current.reset();
|
||||||
searchTokens();
|
setTimeout(() => {
|
||||||
}, 100);
|
searchTokens();
|
||||||
}
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
initValues={formInitValues}
|
initValues={formInitValues}
|
||||||
getFormApi={(api) => setFormApi(api)}
|
getFormApi={(api) => {
|
||||||
|
setFormApi(api);
|
||||||
|
formApiRef.current = api;
|
||||||
|
}}
|
||||||
onSubmit={searchTokens}
|
onSubmit={searchTokens}
|
||||||
allowEmpty={true}
|
allowEmpty={true}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
@@ -88,7 +91,7 @@ const TokensFilters = ({
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
type='tertiary'
|
type='tertiary'
|
||||||
onClick={(_, formApi) => handleReset(formApi)}
|
onClick={handleReset}
|
||||||
className="flex-1 md:flex-initial md:w-auto"
|
className="flex-1 md:flex-initial md:w-auto"
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
For commercial licensing, please contact support@quantumnous.com
|
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 { Form, Button } from '@douyinfe/semi-ui';
|
||||||
import { IconSearch } from '@douyinfe/semi-icons';
|
import { IconSearch } from '@douyinfe/semi-icons';
|
||||||
|
|
||||||
@@ -34,21 +34,23 @@ const UsersFilters = ({
|
|||||||
t
|
t
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
// Handle form reset and immediate search
|
const formApiRef = useRef(null);
|
||||||
const handleReset = (formApi) => {
|
|
||||||
if (formApi) {
|
const handleReset = () => {
|
||||||
formApi.reset();
|
if (!formApiRef.current) return;
|
||||||
// Reset and search immediately
|
formApiRef.current.reset();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
loadUsers(1, pageSize);
|
loadUsers(1, pageSize);
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
initValues={formInitValues}
|
initValues={formInitValues}
|
||||||
getFormApi={(api) => setFormApi(api)}
|
getFormApi={(api) => {
|
||||||
|
setFormApi(api);
|
||||||
|
formApiRef.current = api;
|
||||||
|
}}
|
||||||
onSubmit={() => {
|
onSubmit={() => {
|
||||||
searchUsers(1, pageSize);
|
searchUsers(1, pageSize);
|
||||||
}}
|
}}
|
||||||
@@ -99,7 +101,7 @@ const UsersFilters = ({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type='tertiary'
|
type='tertiary'
|
||||||
onClick={(_, formApi) => handleReset(formApi)}
|
onClick={handleReset}
|
||||||
className="flex-1 md:flex-initial md:w-auto"
|
className="flex-1 md:flex-initial md:w-auto"
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ export const useUsersData = () => {
|
|||||||
|
|
||||||
// Load users data
|
// Load users data
|
||||||
const loadUsers = async (startIdx, pageSize) => {
|
const loadUsers = async (startIdx, pageSize) => {
|
||||||
|
setLoading(true);
|
||||||
const res = await API.get(`/api/user/?p=${startIdx}&page_size=${pageSize}`);
|
const res = await API.get(`/api/user/?p=${startIdx}&page_size=${pageSize}`);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
|
|||||||
@@ -165,7 +165,8 @@
|
|||||||
"操作失败,重定向至登录界面中...": "Operation failed, redirecting to login page...",
|
"操作失败,重定向至登录界面中...": "Operation failed, redirecting to login page...",
|
||||||
"出现错误,第 ${count} 次重试中...": "Error occurred, retry attempt ${count}...",
|
"出现错误,第 ${count} 次重试中...": "Error occurred, retry attempt ${count}...",
|
||||||
"首页": "Home",
|
"首页": "Home",
|
||||||
"渠道": "Channels",
|
"渠道": "Channel",
|
||||||
|
"渠道管理": "Channels",
|
||||||
"令牌": "Tokens",
|
"令牌": "Tokens",
|
||||||
"兑换": "Redeem",
|
"兑换": "Redeem",
|
||||||
"充值": "Recharge",
|
"充值": "Recharge",
|
||||||
@@ -1487,7 +1488,7 @@
|
|||||||
"收益": "Earnings",
|
"收益": "Earnings",
|
||||||
"无邀请人": "No Inviter",
|
"无邀请人": "No Inviter",
|
||||||
"邀请人": "Inviter",
|
"邀请人": "Inviter",
|
||||||
"兑换码管理": "Redemption Code Management",
|
"兑换码管理": "Redemption Code",
|
||||||
"设置兑换码的基本信息": "Set redemption code basic information",
|
"设置兑换码的基本信息": "Set redemption code basic information",
|
||||||
"设置兑换码的额度和数量": "Set redemption code quota and quantity",
|
"设置兑换码的额度和数量": "Set redemption code quota and quantity",
|
||||||
"编辑用户": "Edit User",
|
"编辑用户": "Edit User",
|
||||||
|
|||||||
Reference in New Issue
Block a user