fix(preferences): fix preferences users page.

This commit is contained in:
a.bouhuolia
2021-03-22 19:23:36 +02:00
parent d79be910f9
commit a0f4947138
20 changed files with 451 additions and 307 deletions

View File

@@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import { Switch, Route } from 'react-router'; import { Switch, Route } from 'react-router';
import { useQuery } from 'react-query';
import 'style/pages/Dashboard/Dashboard.scss'; import 'style/pages/Dashboard/Dashboard.scss';

View File

@@ -4,7 +4,7 @@ import {Switch, Route, useRouteMatch} from 'react-router-dom';
export default function PreferencesSubContent({ preferenceTab }) { export default function PreferencesSubContent({ preferenceTab }) {
const routes = preferencesTabs[preferenceTab]; const routes = preferencesTabs[preferenceTab];
const {path} = useRouteMatch(); const { path } = useRouteMatch();
if (routes.length <= 0) { return null; } if (routes.length <= 0) { return null; }

View File

@@ -0,0 +1,6 @@
function UserActivateAlert() {
}

View File

@@ -0,0 +1,76 @@
import React from 'react';
import { Intent, Alert } from '@blueprintjs/core';
import { FormattedMessage as T, useIntl } from 'react-intl';
import { useDeleteUser } from 'hooks/query';
import { AppToaster } from 'components';
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
import withAlertActions from 'containers/Alert/withAlertActions';
import { compose } from 'redux';
/**
* User delete alert.
*/
function UserDeleteAlert({
// #ownProps
name,
// #withAlertStoreConnect
isOpen,
payload: { userId },
// #withAlertActions
closeAlert,
}) {
const { formatMessage } = useIntl();
const { mutateAsync: deleteUserMutate, isLoading } = useDeleteUser();
const handleCancelUserDelete = () => {
closeAlert(name);
};
const handleConfirmUserDelete = () => {
deleteUserMutate(userId)
.then((response) => {
AppToaster.show({
message: formatMessage({
id: 'the_user_has_been_deleted_successfully',
}),
intent: Intent.SUCCESS,
});
})
.catch(({ response: { data: { errors } } }) => {
if (errors.find(e => e.type === 'CANNOT_DELETE_LAST_USER')) {
AppToaster.show({
message: 'Cannot delete the last user in the system.',
intent: Intent.DANGER,
});
}
closeAlert(name);
});
};
return (
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'delete'} />}
intent={Intent.DANGER}
isOpen={isOpen}
onCancel={handleCancelUserDelete}
onConfirm={handleConfirmUserDelete}
loading={isLoading}
>
<p>
Once you delete this user, you won't be able to restore it later. Are
you sure you want to delete ?
</p>
</Alert>
);
}
export default compose(
withAlertStoreConnect(),
withAlertActions,
)(UserDeleteAlert);

View File

@@ -0,0 +1,68 @@
import React from 'react';
import { FormattedMessage as T, useIntl } from 'react-intl';
import { Alert, Intent } from '@blueprintjs/core';
import { AppToaster } from 'components';
import { useInactivateUser } from 'hooks/query';
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
import withAlertActions from 'containers/Alert/withAlertActions';
import { compose } from 'utils';
/**
* User inactivate alert.
*/
function UserInactivateAlert({
// #ownProps
name,
// #withAlertStoreConnect
isOpen,
payload: { userId },
// #withAlertActions
closeAlert,
}) {
const { formatMessage } = useIntl();
const { mutateAsync: userInactivateMutate } = useInactivateUser();
const handleConfirmInactivate = () => {
userInactivateMutate(userId)
.then(() => {
AppToaster.show({
message: formatMessage({
id: 'the_user_has_been_inactivated_successfully',
}),
intent: Intent.SUCCESS,
});
})
.catch((error) => {
});
};
const handleCancel = () => {
closeAlert(name);
};
return (
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'inactivate'} />}
intent={Intent.WARNING}
isOpen={isOpen}
onCancel={handleCancel}
onConfirm={handleConfirmInactivate}
>
<p>
<T id={'are_sure_to_inactive_this_account'} />
</p>
</Alert>
);
}
export default compose(
withAlertStoreConnect(),
withAlertActions,
)(UserInactivateAlert);

View File

@@ -1,19 +1,14 @@
import React, { useMemo, useCallback } from 'react'; import React from 'react';
import { import {
Button,
Classes, Classes,
FormGroup, FormGroup,
InputGroup, InputGroup,
Intent,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { Form, useFormikContext, FastField } from 'formik'; import { FastField } from 'formik';
import { FormattedMessage as T, useIntl } from 'react-intl'; import { FormattedMessage as T } from 'react-intl';
import { pick } from 'lodash';
import { import {
ErrorMessage, ErrorMessage,
AppToaster,
FieldRequiredHint, FieldRequiredHint,
DialogContent,
} from 'components'; } from 'components';
import { useAutofocus } from 'hooks'; import { useAutofocus } from 'hooks';
@@ -33,7 +28,7 @@ export default function CurrencyFormFields() {
className={'form-group--currency-name'} className={'form-group--currency-name'}
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="currency_name" />} helperText={<ErrorMessage name="currency_name" />}
inline={true} // inline={true}
> >
<InputGroup <InputGroup
inputRef={(ref) => (currencyNameFieldRef.current = ref)} inputRef={(ref) => (currencyNameFieldRef.current = ref)}
@@ -51,7 +46,7 @@ export default function CurrencyFormFields() {
className={'form-group--currency-code'} className={'form-group--currency-code'}
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="currency_code" />} helperText={<ErrorMessage name="currency_code" />}
inline={true} // inline={true}
> >
<InputGroup {...field} /> <InputGroup {...field} />
</FormGroup> </FormGroup>

View File

@@ -14,7 +14,7 @@ function CurrencyFormProvider({ isEditMode, currency, dialogName, ...props }) {
const { mutateAsync: editCurrencyMutate } = useEditCurrency(); const { mutateAsync: editCurrencyMutate } = useEditCurrency();
// fetch Currencies list. // fetch Currencies list.
const { data: currencies, isFetching: isCurrenciesLoading } = useCurrencies(); const { data: currencies, isLoading: isCurrenciesLoading } = useCurrencies();
// Provider state. // Provider state.
const provider = { const provider = {

View File

@@ -1,5 +1,5 @@
import React, { lazy } from 'react'; import React, { lazy } from 'react';
import { FormattedMessage as T, useIntl } from 'react-intl'; import { FormattedMessage as T } from 'react-intl';
import { Dialog, DialogSuspense } from 'components'; import { Dialog, DialogSuspense } from 'components';
import withDialogRedux from 'components/DialogReduxConnect'; import withDialogRedux from 'components/DialogReduxConnect';
import { compose } from 'utils'; import { compose } from 'utils';
@@ -30,6 +30,7 @@ function CurrencyFormDialog({
isOpen={isOpen} isOpen={isOpen}
autoFocus={true} autoFocus={true}
canEscapeKeyClose={true} canEscapeKeyClose={true}
style={{ width: '450px' }}
> >
<DialogSuspense> <DialogSuspense>
<CurrencyFormDialogContent <CurrencyFormDialogContent

View File

@@ -12,7 +12,7 @@ function InviteUserFormProvider({ userId, isEditMode, dialogName, ...props }) {
const { mutateAsync: inviteUserMutate } = useCreateInviteUser(); const { mutateAsync: inviteUserMutate } = useCreateInviteUser();
// fetch users list. // fetch users list.
const { isFetching: isUsersLoading } = useUsers(); const { isLoading: isUsersLoading } = useUsers();
// Provider state. // Provider state.
const provider = { const provider = {

View File

@@ -3,8 +3,8 @@ import { formatMessage } from 'services/intl';
export const transformApiErrors = (errors) => { export const transformApiErrors = (errors) => {
const fields = {}; const fields = {};
if (errors.find(error => error.type === 'EMAIL.ALREADY.INVITED')) { if (errors.find((error) => error.type === 'EMAIL.ALREADY.INVITED')) {
fields.email = formatMessage({ id: 'email_is_already_used' }); fields.email = formatMessage({ id: 'email_is_already_used' });
} }
return fields; return fields;
} };

View File

@@ -8,7 +8,7 @@ const CurrenciesContext = createContext();
*/ */
function CurrenciesProvider({ ...props }) { function CurrenciesProvider({ ...props }) {
// fetches the currencies list. // fetches the currencies list.
const { data: currencies, isFetching: isCurrenciesLoading } = useCurrencies(); const { data: currencies, isLoading: isCurrenciesLoading } = useCurrencies();
const state = { const state = {
currencies, currencies,
@@ -16,9 +16,7 @@ function CurrenciesProvider({ ...props }) {
}; };
return ( return (
<>
<CurrenciesContext.Provider value={state} {...props} /> <CurrenciesContext.Provider value={state} {...props} />
</>
); );
} }

View File

@@ -2,10 +2,16 @@ import React from 'react';
import { Tabs, Tab } from '@blueprintjs/core'; import { Tabs, Tab } from '@blueprintjs/core';
import classNames from 'classnames'; import classNames from 'classnames';
import 'style/pages/Preferences/Users.scss'
import { CLASSES } from 'common/classes'; import { CLASSES } from 'common/classes';
import PreferencesSubContent from 'components/Preferences/PreferencesSubContent'; import PreferencesSubContent from 'components/Preferences/PreferencesSubContent';
import withUserPreferences from 'containers/Preferences/Users/withUserPreferences'; import withUserPreferences from 'containers/Preferences/Users/withUserPreferences';
/**
* Preferences page - Users page.
*/
function UsersPreferences({ openDialog }) { function UsersPreferences({ openDialog }) {
const onChangeTabs = (currentTabId) => {}; const onChangeTabs = (currentTabId) => {};

View File

@@ -0,0 +1,14 @@
import React from 'react';
import UserDeleteAlert from 'containers/Alerts/Users/UserDeleteAlert';
import UserInactivateAlert from 'containers/Alerts/Users/UserInactivateAlert';
// import UserActivateAlert from 'containers/Alerts/UserActivateAlert';
export default function UsersAlerts() {
return (
<>
<UserDeleteAlert name={'user-delete'} />
<UserInactivateAlert name={'user-inactivate'} />
{/* <UserActivateAlert name={'user-activate'} /> */}
</>
);
}

View File

@@ -1,178 +1,78 @@
import React, { useCallback, useState, useMemo } from 'react'; import React, { useCallback } from 'react';
import {
Intent,
Button,
Popover,
Menu,
MenuDivider,
Tag,
MenuItem,
Position,
} from '@blueprintjs/core';
import { withRouter } from 'react-router';
import { snakeCase } from 'lodash';
import { FormattedMessage as T, useIntl } from 'react-intl'; import { compose } from 'utils';
import { compose, firstLettersArgs } from 'utils'; import { DataTable } from 'components';
import { DataTable, Icon, If } from 'components';
import withDialogActions from 'containers/Dialog/withDialogActions'; import withDialogActions from 'containers/Dialog/withDialogActions';
import withUsers from 'containers/Users/withUsers'; import withAlertActions from 'containers/Alert/withAlertActions';
const AvatarCell = (row) => { import { ActionsMenu, useUsersListColumns } from './components';
return <span className={'avatar'}>{ firstLettersArgs(row.email) }</span>; import { useUsersListContext } from './UsersProvider';
}
/**
* Users datatable.
*/
function UsersDataTable({ function UsersDataTable({
// #withDialogActions // #withDialogActions
openDialog, openDialog,
// #withUsers // #withAlertActions
usersList, openAlert,
usersLoading,
// #ownProps
loading,
onFetchData,
onInactiveUser,
onDeleteUser,
onSelectedRowsChange,
}) { }) {
const { formatMessage } = useIntl(); // Handle edit user action.
const handleEditUserAction = useCallback(
const onEditUser = useCallback( (user) => {
(user) => () => { openDialog('userList-form', { action: 'edit', userId: user.id });
const form = Object.keys(user).reduce((obj, key) => {
const camelKey = snakeCase(key);
obj[camelKey] = user[key];
return obj;
}, {});
openDialog('userList-form', { action: 'edit', user: form });
}, },
[openDialog], [openDialog],
); );
// Handle inactivate user action.
const actionMenuList = useCallback( const handleInactivateUser = useCallback(
(user) => ( (user) => {
<Menu> openAlert('user-inactivate', { userId: user.id });
<If condition={user.invite_accepted_at}> },
<MenuItem [openAlert]
icon={<Icon icon="pen-18" />}
text={formatMessage({ id: 'edit_user' })}
onClick={onEditUser(user)}
/>
<MenuDivider />
<MenuItem
text={formatMessage({ id: 'inactivate_user' })}
onClick={() => onInactiveUser(user)}
/>
</If>
<MenuItem
icon={<Icon icon="trash-16" iconSize={16} />}
text={formatMessage({ id: 'delete_user' })}
onClick={() => onDeleteUser(user)}
intent={Intent.DANGER}
/>
</Menu>
),
[onInactiveUser, onDeleteUser, onEditUser, formatMessage],
); );
const onRowContextMenu = useCallback( // Handle activate user action.
(cell) => { const handleActivateuser = useCallback(
return actionMenuList(cell.row.original); (user) => {
openAlert('user-activate', { userId: user.id });
}, },
[actionMenuList], [openAlert]
); );
// Handle delete user action.
const handleDeleteUser = useCallback(
(user) => {
openAlert('user-delete', { userId: user.id });
},
[openAlert]
);
// Users list columns.
const columns = useUsersListColumns();
const columns = useMemo( // Users list context.
() => [ const { users, isUsersLoading, isUsersFetching } = useUsersListContext();
{
id: 'avatar',
Header: '',
accessor: AvatarCell,
width: 100,
},
{
id: 'full_name',
Header: formatMessage({ id: 'full_name' }),
accessor: 'full_name',
width: 150,
},
{
id: 'email',
Header: formatMessage({ id: 'email' }),
accessor: 'email',
width: 150,
},
{
id: 'phone_number',
Header: formatMessage({ id: 'phone_number' }),
accessor: 'phone_number',
width: 120,
},
{
id: 'status',
Header: 'Status',
accessor: (user) =>
!user.invite_accepted_at ? (
<Tag minimal={true}>
<T id={'inviting'} />
</Tag>
) : user.active ? (
<Tag intent={Intent.SUCCESS} minimal={true}>
<T id={'activate'} />
</Tag>
) : (
<Tag intent={Intent.WARNING} minimal={true}>
<T id={'inactivate'} />
</Tag>
),
width: 80,
className: 'status',
},
{
id: 'actions',
Header: '',
Cell: ({ cell }) => (
<Popover
content={actionMenuList(cell.row.original)}
position={Position.RIGHT_BOTTOM}
>
<Button icon={<Icon icon="more-h-16" iconSize={16} />} />
</Popover>
),
className: 'actions',
width: 50,
disableResizing: true,
},
],
[actionMenuList, formatMessage],
);
const handelDataTableFetchData = useCallback(
(...args) => {
onFetchData && onFetchData(...args);
},
[onFetchData],
);
return ( return (
<DataTable <DataTable
columns={columns} columns={columns}
data={usersList} data={users}
loading={loading} loading={isUsersLoading}
onFetchData={handelDataTableFetchData} headerLoading={isUsersLoading}
progressBarLoading={isUsersFetching}
noInitialFetch={true} noInitialFetch={true}
rowContextMenu={onRowContextMenu} ContextMenu={ActionsMenu}
payload={{
onEdit: handleEditUserAction,
onActivate: handleInactivateUser,
onInactivate: handleActivateuser,
onDelete: handleDeleteUser
}}
/> />
); );
} }
export default compose( export default compose(
withRouter,
withDialogActions, withDialogActions,
withUsers(({ usersList, usersLoading }) => ({ usersList, usersLoading })), withAlertActions
)(UsersDataTable); )(UsersDataTable);

View File

@@ -1,140 +1,34 @@
import React, { useState, useCallback, useEffect } from 'react'; import React, { useEffect } from 'react';
import { queryCache, useQuery } from 'react-query'; import { useIntl } from 'react-intl';
import { Alert, Intent } from '@blueprintjs/core';
import {UsersListProvider } from './UsersProvider';
import withDashboardActions from 'containers/Dashboard/withDashboardActions'; import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withUsersActions from 'containers/Users/withUsersActions';
import UsersDataTable from './UsersDataTable'; import UsersDataTable from './UsersDataTable';
import { import UsersAlerts from './UsersAlerts';
FormattedMessage as T,
FormattedHTMLMessage,
useIntl,
} from 'react-intl';
import AppToaster from 'components/AppToaster';
import { compose } from 'utils'; import { compose } from 'utils';
/**
* Users list.
*/
function UsersListPreferences({ function UsersListPreferences({
// #withDashboardActions // #withDashboardActions
changePreferencesPageTitle, changePreferencesPageTitle,
// #withUsersActions
requestDeleteUser,
requestInactiveUser,
requestFetchUsers,
}) { }) {
const [deleteUserState, setDeleteUserState] = useState(false);
const [inactiveUserState, setInactiveUserState] = useState(false);
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const fetchUsers = useQuery('users-table', () => requestFetchUsers());
useEffect(() => { useEffect(() => {
changePreferencesPageTitle(formatMessage({ id: 'users' })); changePreferencesPageTitle(formatMessage({ id: 'users' }));
}, [changePreferencesPageTitle, formatMessage]); }, [changePreferencesPageTitle, formatMessage]);
// Handle cancel/confirm user inactive.
const handleInactiveUser = useCallback((user) => {
setInactiveUserState(user);
}, []);
// Handle cancel inactive user alert
const handleCancelInactiveUser = useCallback(() => {
setInactiveUserState(false);
}, []);
// handel confirm user activation
const handleConfirmUserActive = useCallback(() => {
requestInactiveUser(inactiveUserState.id)
.then(() => {
setInactiveUserState(false);
AppToaster.show({
message: formatMessage({
id: 'the_user_has_been_inactivated_successfully',
}),
intent: Intent.SUCCESS,
});
queryCache.invalidateQueries('users-table');
})
.catch((error) => {
setInactiveUserState(false);
});
}, [inactiveUserState, requestInactiveUser, formatMessage]);
// Handle click and cancel/confirm user delete
const handleDeleteUser = useCallback((user) => {
setDeleteUserState(user);
}, []);
// handle cancel delete user alert.
const handleCancelUserDelete = () => {
setDeleteUserState(false);
};
const handleEditUser = useCallback(() => {}, []);
// Handle confirm User delete
const handleConfirmUserDelete = useCallback(() => {
if (!deleteUserState) {
return;
}
requestDeleteUser(deleteUserState.id)
.then((response) => {
setDeleteUserState(false);
AppToaster.show({
message: formatMessage({
id: 'the_user_has_been_deleted_successfully',
}),
intent: Intent.SUCCESS,
});
queryCache.invalidateQueries('users-table');
})
.catch((errors) => {
setDeleteUserState(false);
});
}, [deleteUserState, requestDeleteUser, formatMessage]);
return ( return (
<> <UsersListProvider>
<UsersDataTable <UsersDataTable />
loading={fetchUsers.isFetching} <UsersAlerts />
onDeleteUser={handleDeleteUser} </UsersListProvider>
onInactiveUser={handleInactiveUser}
onEditUser={handleEditUser}
/>
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'delete'} />}
intent={Intent.DANGER}
isOpen={deleteUserState}
onCancel={handleCancelUserDelete}
onConfirm={handleConfirmUserDelete}
>
<p>
Once you delete this user, you won't be able to restore it later. Are you sure you want to delete ?
</p>
</Alert>
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'inactivate'} />}
intent={Intent.WARNING}
isOpen={inactiveUserState}
onCancel={handleCancelInactiveUser}
onConfirm={handleConfirmUserActive}
>
<p>
<T id={'are_sure_to_inactive_this_account'} />
</p>
</Alert>
</>
); );
} }
export default compose( export default compose(
withDashboardActions, withDashboardActions,
withUsersActions,
)(UsersListPreferences); )(UsersListPreferences);

View File

@@ -0,0 +1,25 @@
import React, { createContext } from 'react';
import { useUsers } from 'hooks/query';
const UsersListContext = createContext();
/**
* Users list provider.
*/
function UsersListProvider(props) {
const { data: users, isLoading, isFetching } = useUsers();
const state = {
isUsersLoading: isLoading,
isUsersFetching: isFetching,
users,
};
return (
<UsersListContext.Provider value={state} {...props} />
);
}
const useUsersListContext = () => React.useContext(UsersListContext);
export { UsersListProvider, useUsersListContext };

View File

@@ -0,0 +1,143 @@
import React from 'react';
import { FormattedMessage as T, useIntl } from 'react-intl';
import {
Intent,
Button,
Popover,
Menu,
MenuDivider,
Tag,
MenuItem,
Position,
} from '@blueprintjs/core';
import { safeCallback, firstLettersArgs } from 'utils';
import { Icon, If } from 'components';
/**
* Avatar cell.
*/
function AvatarCell(row) {
return <span className={'avatar'}>{firstLettersArgs(row.email)}</span>;
}
/**
* Users table actions menu.
*/
export function ActionsMenu({
row: { original },
payload: {
onEdit,
onInactivate,
onActivate,
onDelete
}
}) {
const { formatMessage } = useIntl();
return (
<Menu>
<If condition={original.invite_accepted_at}>
<MenuItem
icon={<Icon icon="pen-18" />}
text={formatMessage({ id: 'edit_user' })}
onClick={safeCallback(onEdit, original)}
/>
<MenuDivider />
<MenuItem
text={formatMessage({ id: 'inactivate_user' })}
onClick={safeCallback(onInactivate, original)}
/>
</If>
<MenuItem
icon={<Icon icon="trash-16" iconSize={16} />}
text={formatMessage({ id: 'delete_user' })}
onClick={safeCallback(onDelete, original)}
intent={Intent.DANGER}
/>
</Menu>
);
}
/**
* Status accessor.
*/
function StatusAccessor(user) {
return !user.invite_accepted_at ? (
<Tag minimal={true}>
<T id={'inviting'} />
</Tag>
) : user.active ? (
<Tag intent={Intent.SUCCESS} minimal={true}>
<T id={'activate'} />
</Tag>
) : (
<Tag intent={Intent.WARNING} minimal={true}>
<T id={'inactivate'} />
</Tag>
);
}
/**
* Actions cell.
*/
function ActionsCell(props) {
return (
<Popover
content={<ActionsMenu {...props} />}
position={Position.RIGHT_BOTTOM}
>
<Button icon={<Icon icon="more-h-16" iconSize={16} />} />
</Popover>
);
}
export const useUsersListColumns = () => {
const { formatMessage } = useIntl();
return React.useMemo(
() => [
{
id: 'avatar',
Header: '',
accessor: AvatarCell,
width: 40,
},
{
id: 'full_name',
Header: formatMessage({ id: 'full_name' }),
accessor: 'full_name',
width: 150,
},
{
id: 'email',
Header: formatMessage({ id: 'email' }),
accessor: 'email',
width: 150,
},
{
id: 'phone_number',
Header: formatMessage({ id: 'phone_number' }),
accessor: 'phone_number',
width: 120,
},
{
id: 'status',
Header: 'Status',
accessor: StatusAccessor,
width: 80,
className: 'status',
},
{
id: 'actions',
Header: '',
Cell: ActionsCell,
className: 'actions',
width: 50,
disableResizing: true,
},
],
[formatMessage],
);
};

View File

@@ -1,6 +1,5 @@
import { useMutation, useQueryClient } from 'react-query'; import { useMutation, useQueryClient } from 'react-query';
import { defaultTo } from 'lodash'; import { useQueryTenant, useRequestQuery } from '../useQueryRequest';
import { useQueryTenant } from '../useQueryRequest';
import useApiRequest from '../useRequest'; import useApiRequest from '../useRequest';
import t from './types'; import t from './types';
@@ -43,6 +42,24 @@ export function useEditUser(props) {
}); });
} }
export function useInactivateUser(props) {
const apiRequest = useApiRequest();
const queryClient = useQueryClient();
return useMutation(
([id, values]) => apiRequest.post(`users/${id}/inactivate`, values),
{
onSuccess: (res, [id, values]) => {
queryClient.invalidateQueries([t.USER, id]);
// Common invalidate queries.
commonInvalidateQueries(queryClient);
},
...props,
},
);
}
/** /**
* Deletes the given user. * Deletes the given user.
*/ */
@@ -65,18 +82,18 @@ export function useDeleteUser(props) {
* Retrieves users list. * Retrieves users list.
*/ */
export function useUsers(props) { export function useUsers(props) {
const apiRequest = useApiRequest(); return useRequestQuery(
const result = useQueryTenant(
[t.USERS], [t.USERS],
() => apiRequest.get(`USERS`).then((response) => response.data.users), {
props, method: 'get',
url: 'users',
},
{
select: (res) => res.data.users,
defaultData: [],
...props,
},
); );
return {
...result,
data: defaultTo(result.data, {}),
};
} }
/** /**

View File

@@ -41,6 +41,7 @@
align-items: center; align-items: center;
height: 60px; height: 60px;
padding: 0 22px; padding: 0 22px;
border-bottom: 1px solid #c9d9de;
h2 { h2 {
font-size: 22px; font-size: 22px;

View File

@@ -3,10 +3,11 @@
// --------------------------------- // ---------------------------------
.preferences-page__inside-content--users{ .preferences-page__inside-content--users{
.bigcapital-datatable { .bigcapital-datatable {
.td{ .td{
.avatar{ .avatar{
display: block;
height: 28px; height: 28px;
width: 28px; width: 28px;
text-align: center; text-align: center;