0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

refactor(console): create user process (#2698)

This commit is contained in:
Xiao Yijun 2022-12-22 11:15:39 +08:00 committed by GitHub
parent 3daf30835a
commit 5743515804
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 78 additions and 80 deletions

View file

@ -4,4 +4,3 @@ export * from './logs';
export const themeStorageKey = 'logto:admin_console:theme';
export const requestTimeout = 20_000;
export const generatedPasswordStorageKey = 'logto:admin_console:generated_password';

View file

@ -19,7 +19,6 @@ import DetailsSkeleton from '@/components/DetailsSkeleton';
import TabNav, { TabNavItem } from '@/components/TabNav';
import TextLink from '@/components/TextLink';
import UserAvatar from '@/components/UserAvatar';
import { generatedPasswordStorageKey } from '@/consts';
import type { RequestError } from '@/hooks/use-api';
import useApi from '@/hooks/use-api';
import * as detailsStyles from '@/scss/details.module.scss';
@ -42,7 +41,6 @@ const UserDetails = () => {
const [isDeleted, setIsDeleted] = useState(false);
const [isResetPasswordFormOpen, setIsResetPasswordFormOpen] = useState(false);
const [resetResult, setResetResult] = useState<string>();
const [password, setPassword] = useState(sessionStorage.getItem(generatedPasswordStorageKey));
const { data, error, mutate } = useSWR<User, RequestError>(userId && `/api/users/${userId}`);
const isLoading = !data && !error;
@ -176,17 +174,6 @@ const UserDetails = () => {
)}
</>
)}
{data && password && (
<CreateSuccess
title="user_details.created_title"
username={data.username ?? '-'}
password={password}
onClose={() => {
setPassword(null);
sessionStorage.removeItem(generatedPasswordStorageKey);
}}
/>
)}
{data && resetResult && (
<CreateSuccess
title="user_details.reset_password.congratulations"

View file

@ -1,31 +1,44 @@
import { usernameRegEx } from '@logto/core-kit';
import type { User } from '@logto/schemas';
import { nanoid } from 'nanoid';
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import Modal from 'react-modal';
import { useNavigate } from 'react-router-dom';
import Button from '@/components/Button';
import FormField from '@/components/FormField';
import ModalLayout from '@/components/ModalLayout';
import TextInput from '@/components/TextInput';
import useApi from '@/hooks/use-api';
import CreateSuccess from '@/pages/UserDetails/components/CreateSuccess';
import * as modalStyles from '@/scss/modal.module.scss';
type FormData = {
username: string;
name: string;
};
type CreatedUserInfo = {
user: User;
password: string;
};
type Props = {
onClose?: (createdUser?: User, password?: string) => void;
onClose: () => void;
};
const CreateForm = ({ onClose }: Props) => {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const navigate = useNavigate();
const [createdUserInfo, setCreatedUserInfo] = useState<CreatedUserInfo>();
const {
handleSubmit,
register,
formState: { isSubmitting, errors },
} = useForm<FormData>();
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const api = useApi();
@ -37,49 +50,70 @@ const CreateForm = ({ onClose }: Props) => {
const password = nanoid(8);
const createdUser = await api.post('/api/users', { json: { ...data, password } }).json<User>();
onClose?.(createdUser, password);
setCreatedUserInfo({
user: createdUser,
password,
});
});
return (
<ModalLayout
title="users.create"
footer={
<Button
disabled={isSubmitting}
htmlType="submit"
title="users.create"
size="large"
type="primary"
onClick={onSubmit}
/>
}
onClose={onClose}
return createdUserInfo ? (
<CreateSuccess
title="user_details.created_title"
username={createdUserInfo.user.username ?? '-'}
password={createdUserInfo.password}
onClose={() => {
navigate(`/users/${createdUserInfo.user.id}`, { replace: true });
}}
/>
) : (
<Modal
shouldCloseOnEsc
isOpen
className={modalStyles.content}
overlayClassName={modalStyles.overlay}
onRequestClose={onClose}
>
<form>
<FormField isRequired title="users.create_form_username">
<TextInput
// eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus
{...register('username', {
required: true,
pattern: {
value: usernameRegEx,
message: t('errors.username_pattern_error'),
},
})}
hasError={Boolean(errors.username)}
errorMessage={errors.username?.message}
<ModalLayout
title="users.create"
footer={
<Button
disabled={isSubmitting}
htmlType="submit"
title="users.create"
size="large"
type="primary"
onClick={onSubmit}
/>
</FormField>
<FormField title="users.create_form_name">
<TextInput
{...register('name')}
hasError={Boolean(errors.name)}
errorMessage={errors.name?.message}
/>
</FormField>
</form>
</ModalLayout>
}
onClose={onClose}
>
<form>
<FormField isRequired title="users.create_form_username">
<TextInput
// eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus
{...register('username', {
required: true,
pattern: {
value: usernameRegEx,
message: t('errors.username_pattern_error'),
},
})}
hasError={Boolean(errors.username)}
errorMessage={errors.username?.message}
/>
</FormField>
<FormField title="users.create_form_name">
<TextInput
{...register('name')}
hasError={Boolean(errors.name)}
errorMessage={errors.name?.message}
/>
</FormField>
</form>
</ModalLayout>
</Modal>
);
};

View file

@ -2,7 +2,6 @@ import type { User } from '@logto/schemas';
import { conditional, conditionalString } from '@silverhand/essentials';
import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import Modal from 'react-modal';
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
import useSWR from 'swr';
@ -18,9 +17,7 @@ import TableEmpty from '@/components/Table/TableEmpty';
import TableError from '@/components/Table/TableError';
import TableLoading from '@/components/Table/TableLoading';
import UserAvatar from '@/components/UserAvatar';
import { generatedPasswordStorageKey } from '@/consts';
import type { RequestError } from '@/hooks/use-api';
import * as modalStyles from '@/scss/modal.module.scss';
import * as resourcesStyles from '@/scss/resources.module.scss';
import * as tableStyles from '@/scss/table.module.scss';
@ -68,36 +65,17 @@ const Users = () => {
});
}}
/>
<Modal
shouldCloseOnEsc
isOpen={isCreateNew}
className={modalStyles.content}
overlayClassName={modalStyles.overlay}
onRequestClose={() => {
navigate({
pathname: usersPathname,
search,
});
}}
>
{isCreateNew && (
<CreateForm
onClose={(createdUser, password) => {
if (createdUser && password) {
sessionStorage.setItem(generatedPasswordStorageKey, password);
navigate(buildDetailsPathname(createdUser.id), { replace: true });
return;
}
onClose={() => {
navigate({
pathname: usersPathname,
search,
});
}}
/>
</Modal>
)}
</div>
<div className={classNames(resourcesStyles.table, styles.tableLayout)}>
<div className={styles.filter}>
<Search