| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- /*
- 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 { useState, useEffect } from 'react';
- import { useTranslation } from 'react-i18next';
- import { API, showError, showSuccess } from '../../helpers';
- import { ITEMS_PER_PAGE } from '../../constants';
- import { useTableCompactMode } from '../common/useTableCompactMode';
- export const useUsersData = () => {
- const { t } = useTranslation();
- const [compactMode, setCompactMode] = useTableCompactMode('users');
- // State management
- const [users, setUsers] = useState([]);
- const [loading, setLoading] = useState(true);
- const [activePage, setActivePage] = useState(1);
- const [pageSize, setPageSize] = useState(ITEMS_PER_PAGE);
- const [searching, setSearching] = useState(false);
- const [groupOptions, setGroupOptions] = useState([]);
- const [userCount, setUserCount] = useState(0);
- // Modal states
- const [showAddUser, setShowAddUser] = useState(false);
- const [showEditUser, setShowEditUser] = useState(false);
- const [editingUser, setEditingUser] = useState({
- id: undefined,
- });
- // Form initial values
- const formInitValues = {
- searchKeyword: '',
- searchGroup: '',
- };
- // Form API reference
- const [formApi, setFormApi] = useState(null);
- // Get form values helper function
- const getFormValues = () => {
- const formValues = formApi ? formApi.getValues() : {};
- return {
- searchKeyword: formValues.searchKeyword || '',
- searchGroup: formValues.searchGroup || '',
- };
- };
- // Set user format with key field
- const setUserFormat = (users) => {
- for (let i = 0; i < users.length; i++) {
- users[i].key = users[i].id;
- }
- setUsers(users);
- };
- // 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) {
- const newPageData = data.items;
- setActivePage(data.page);
- setUserCount(data.total);
- setUserFormat(newPageData);
- } else {
- showError(message);
- }
- setLoading(false);
- };
- // Search users with keyword and group
- const searchUsers = async (
- startIdx,
- pageSize,
- searchKeyword = null,
- searchGroup = null,
- ) => {
- // If no parameters passed, get values from form
- if (searchKeyword === null || searchGroup === null) {
- const formValues = getFormValues();
- searchKeyword = formValues.searchKeyword;
- searchGroup = formValues.searchGroup;
- }
- if (searchKeyword === '' && searchGroup === '') {
- // If keyword is blank, load files instead
- await loadUsers(startIdx, pageSize);
- return;
- }
- setSearching(true);
- const res = await API.get(
- `/api/user/search?keyword=${searchKeyword}&group=${searchGroup}&p=${startIdx}&page_size=${pageSize}`,
- );
- const { success, message, data } = res.data;
- if (success) {
- const newPageData = data.items;
- setActivePage(data.page);
- setUserCount(data.total);
- setUserFormat(newPageData);
- } else {
- showError(message);
- }
- setSearching(false);
- };
- // Manage user operations (promote, demote, enable, disable, delete)
- const manageUser = async (userId, action, record) => {
- // Trigger loading state to force table re-render
- setLoading(true);
- const res = await API.post('/api/user/manage', {
- id: userId,
- action,
- });
- const { success, message } = res.data;
- if (success) {
- showSuccess('操作成功完成!');
- const user = res.data.data;
- // Create a new array and new object to ensure React detects changes
- const newUsers = users.map((u) => {
- if (u.id === userId) {
- if (action === 'delete') {
- return { ...u, DeletedAt: new Date() };
- }
- return { ...u, status: user.status, role: user.role };
- }
- return u;
- });
- setUsers(newUsers);
- } else {
- showError(message);
- }
- setLoading(false);
- };
- const resetUserPasskey = async (user) => {
- if (!user) {
- return;
- }
- try {
- const res = await API.delete(`/api/user/${user.id}/reset_passkey`);
- const { success, message } = res.data;
- if (success) {
- showSuccess(t('Passkey 已重置'));
- } else {
- showError(message || t('操作失败,请重试'));
- }
- } catch (error) {
- showError(t('操作失败,请重试'));
- }
- };
- const resetUserTwoFA = async (user) => {
- if (!user) {
- return;
- }
- try {
- const res = await API.delete(`/api/user/${user.id}/2fa`);
- const { success, message } = res.data;
- if (success) {
- showSuccess(t('二步验证已重置'));
- } else {
- showError(message || t('操作失败,请重试'));
- }
- } catch (error) {
- showError(t('操作失败,请重试'));
- }
- };
- // Handle page change
- const handlePageChange = (page) => {
- setActivePage(page);
- const { searchKeyword, searchGroup } = getFormValues();
- if (searchKeyword === '' && searchGroup === '') {
- loadUsers(page, pageSize).then();
- } else {
- searchUsers(page, pageSize, searchKeyword, searchGroup).then();
- }
- };
- // Handle page size change
- const handlePageSizeChange = async (size) => {
- localStorage.setItem('page-size', size + '');
- setPageSize(size);
- setActivePage(1);
- loadUsers(activePage, size)
- .then()
- .catch((reason) => {
- showError(reason);
- });
- };
- // Handle table row styling for disabled/deleted users
- const handleRow = (record, index) => {
- if (record.DeletedAt !== null || record.status !== 1) {
- return {
- style: {
- background: 'var(--semi-color-disabled-border)',
- },
- };
- } else {
- return {};
- }
- };
- // Refresh data
- const refresh = async (page = activePage) => {
- const { searchKeyword, searchGroup } = getFormValues();
- if (searchKeyword === '' && searchGroup === '') {
- await loadUsers(page, pageSize);
- } else {
- await searchUsers(page, pageSize, searchKeyword, searchGroup);
- }
- };
- // Fetch groups data
- const fetchGroups = async () => {
- try {
- let res = await API.get(`/api/group/`);
- if (res === undefined) {
- return;
- }
- setGroupOptions(
- res.data.data.map((group) => ({
- label: group,
- value: group,
- })),
- );
- } catch (error) {
- showError(error.message);
- }
- };
- // Modal control functions
- const closeAddUser = () => {
- setShowAddUser(false);
- };
- const closeEditUser = () => {
- setShowEditUser(false);
- setEditingUser({
- id: undefined,
- });
- };
- // Initialize data on component mount
- useEffect(() => {
- loadUsers(0, pageSize)
- .then()
- .catch((reason) => {
- showError(reason);
- });
- fetchGroups().then();
- }, []);
- return {
- // Data state
- users,
- loading,
- activePage,
- pageSize,
- userCount,
- searching,
- groupOptions,
- // Modal state
- showAddUser,
- showEditUser,
- editingUser,
- setShowAddUser,
- setShowEditUser,
- setEditingUser,
- // Form state
- formInitValues,
- formApi,
- setFormApi,
- // UI state
- compactMode,
- setCompactMode,
- // Actions
- loadUsers,
- searchUsers,
- manageUser,
- resetUserPasskey,
- resetUserTwoFA,
- handlePageChange,
- handlePageSizeChange,
- handleRow,
- refresh,
- closeAddUser,
- closeEditUser,
- getFormValues,
- // Translation
- t,
- };
- };
|