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

refactor(console): user details (#2467)

This commit is contained in:
Xiao Yijun 2022-11-21 10:33:08 +08:00 committed by GitHub
parent 9d02a3d3f4
commit e56238e6b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 97 additions and 106 deletions

View file

@ -0,0 +1,9 @@
@use '@/scss/underscore' as _;
.logs {
margin-bottom: _.unit(6);
>:not(:first-child) {
margin-top: _.unit(3);
}
}

View file

@ -1,6 +1,7 @@
import AuditLogTable from '@/components/AuditLogTable';
import Card from '@/components/Card';
import * as styles from '../index.module.scss';
import * as styles from './UserLogs.module.scss';
type Props = {
userId: string;
@ -8,9 +9,9 @@ type Props = {
const UserLogs = ({ userId }: Props) => {
return (
<div className={styles.logs}>
<Card className={styles.logs}>
<AuditLogTable userId={userId} />
</div>
</Card>
);
};

View file

@ -6,17 +6,16 @@ import { useForm, useController } from 'react-hook-form';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
import Button from '@/components/Button';
import CodeEditor from '@/components/CodeEditor';
import DetailsForm from '@/components/DetailsForm';
import FormCard from '@/components/FormCard';
import FormField from '@/components/FormField';
import TextInput from '@/components/TextInput';
import UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal';
import useApi from '@/hooks/use-api';
import * as detailsStyles from '@/scss/details.module.scss';
import { safeParseJson } from '@/utilities/json';
import { uriValidator } from '@/utilities/validator';
import * as styles from '../index.module.scss';
import UserConnectors from './UserConnectors';
type FormData = {
@ -96,67 +95,63 @@ const UserSettings = ({ userData, userFormData, isDeleted, onUserUpdated }: Prop
});
return (
<form className={styles.form} onSubmit={onSubmit}>
<div className={styles.fields}>
{getValues('primaryEmail') && (
<FormField title="user_details.field_email" className={styles.textField}>
<TextInput readOnly {...register('primaryEmail')} />
<>
<DetailsForm
isSubmitting={isSubmitting}
isDirty={isDirty}
onSubmit={onSubmit}
onDiscard={reset}
>
<FormCard title="user_details.settings" description="user_details.settings_description">
{getValues('primaryEmail') && (
<FormField title="user_details.field_email">
<TextInput readOnly {...register('primaryEmail')} />
</FormField>
)}
{getValues('primaryPhone') && (
<FormField title="user_details.field_phone">
<TextInput readOnly {...register('primaryPhone')} />
</FormField>
)}
{getValues('username') && (
<FormField title="user_details.field_username">
<TextInput readOnly {...register('username')} />
</FormField>
)}
<FormField title="user_details.field_name">
<TextInput {...register('name')} />
</FormField>
)}
{getValues('primaryPhone') && (
<FormField title="user_details.field_phone" className={styles.textField}>
<TextInput readOnly {...register('primaryPhone')} />
<FormField title="user_details.field_avatar">
<TextInput
{...register('avatar', {
validate: (value) =>
!value || uriValidator(value) || t('errors.invalid_uri_format'),
})}
hasError={Boolean(errors.avatar)}
errorMessage={errors.avatar?.message}
placeholder={t('user_details.field_avatar_placeholder')}
/>
</FormField>
)}
{getValues('username') && (
<FormField title="user_details.field_username" className={styles.textField}>
<TextInput readOnly {...register('username')} />
<FormField title="user_details.field_connectors">
<UserConnectors
userId={userData.id}
connectors={userData.identities}
onDelete={() => {
onUserUpdated();
}}
/>
</FormField>
)}
<FormField title="user_details.field_name" className={styles.textField}>
<TextInput {...register('name')} />
</FormField>
<FormField title="user_details.field_avatar" className={styles.textField}>
<TextInput
{...register('avatar', {
validate: (value) => !value || uriValidator(value) || t('errors.invalid_uri_format'),
})}
hasError={Boolean(errors.avatar)}
errorMessage={errors.avatar?.message}
placeholder={t('user_details.field_avatar_placeholder')}
/>
</FormField>
<FormField title="user_details.field_connectors" className={styles.textField}>
<UserConnectors
userId={userData.id}
connectors={userData.identities}
onDelete={() => {
onUserUpdated();
}}
/>
</FormField>
<FormField
isRequired
title="user_details.field_custom_data"
className={styles.textField}
tooltip="user_details.field_custom_data_tip"
>
<CodeEditor language="json" value={value} onChange={onChange} />
</FormField>
</div>
<div className={detailsStyles.footer}>
<div className={detailsStyles.footerMain}>
<Button
isLoading={isSubmitting}
htmlType="submit"
type="primary"
title="general.save_changes"
size="large"
/>
</div>
</div>
<FormField
isRequired
title="user_details.field_custom_data"
tooltip="user_details.field_custom_data_tip"
>
<CodeEditor language="json" value={value} onChange={onChange} />
</FormField>
</FormCard>
</DetailsForm>
<UnsavedChangesAlertModal hasUnsavedChanges={!isDeleted && isDirty} />
</form>
</>
);
};

View file

@ -68,31 +68,6 @@
}
}
.body {
> :first-child {
margin-top: 0;
}
.form {
margin-top: _.unit(8);
flex: 1;
display: flex;
flex-direction: column;
}
.fields {
padding-bottom: _.unit(10);
flex: 1;
}
.textField {
@include _.form-text-field;
}
.logs {
padding: _.unit(6) 0;
}
}
.resetIcon {
color: var(--color-text-secondary);

View file

@ -1,5 +1,4 @@
import type { User } from '@logto/schemas';
import classNames from 'classnames';
import { useMemo, useState } from 'react';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
@ -170,23 +169,21 @@ const UserDetails = () => {
</DeleteConfirmModal>
</div>
</Card>
<Card className={classNames(styles.body, detailsStyles.body)}>
<TabNav>
<TabNavItem href={`/users/${userId}`}>{t('general.settings_nav')}</TabNavItem>
<TabNavItem href={`/users/${userId}/logs`}>{t('user_details.tab_logs')}</TabNavItem>
</TabNav>
{isLogs && <UserLogs userId={data.id} />}
{!isLogs && userFormData && (
<UserSettings
userData={data}
userFormData={userFormData}
isDeleted={isDeleted}
onUserUpdated={(user) => {
void mutate(user);
}}
/>
)}
</Card>
<TabNav>
<TabNavItem href={`/users/${userId}`}>{t('general.settings_nav')}</TabNavItem>
<TabNavItem href={`/users/${userId}/logs`}>{t('user_details.tab_logs')}</TabNavItem>
</TabNav>
{isLogs && <UserLogs userId={data.id} />}
{!isLogs && userFormData && (
<UserSettings
userData={data}
userFormData={userFormData}
isDeleted={isDeleted}
onUserUpdated={(user) => {
void mutate(user);
}}
/>
)}
</>
)}
{data && password && (

View file

@ -17,6 +17,8 @@ const user_details = {
new_password: 'Neues Passwort:',
},
tab_logs: 'Benutzer-Logs',
settings: 'Settings', // UNTRANSLATED
settings_description: 'It real sent your at. Amounted all shy set why followed declared.', // UNTRANSLATED
field_email: 'Primäre E-Mail',
field_phone: 'Primäre Telefonnummer',
field_username: 'Benutzername',

View file

@ -15,6 +15,8 @@ const user_details = {
new_password: 'New password:',
},
tab_logs: 'User logs',
settings: 'Settings', // UNTRANSLATED
settings_description: 'It real sent your at. Amounted all shy set why followed declared.', // UNTRANSLATED
field_email: 'Primary email',
field_phone: 'Primary phone',
field_username: 'Username',

View file

@ -17,6 +17,8 @@ const user_details = {
new_password: 'Nouveau mot de passe :',
},
tab_logs: "Journaux de l'utilisateur",
settings: 'Settings', // UNTRANSLATED
settings_description: 'It real sent your at. Amounted all shy set why followed declared.', // UNTRANSLATED
field_email: 'Email principale',
field_phone: 'Téléphone principal',
field_username: "Nom d'utilisateur",

View file

@ -15,6 +15,8 @@ const user_details = {
new_password: '새로운 비밀번호:',
},
tab_logs: '사용자 기록',
settings: 'Settings', // UNTRANSLATED
settings_description: 'It real sent your at. Amounted all shy set why followed declared.', // UNTRANSLATED
field_email: '메인 이메일',
field_phone: '메인 휴대전화번호',
field_username: '사용자 이름',

View file

@ -17,6 +17,8 @@ const user_details = {
new_password: 'Nova password:',
},
tab_logs: 'Registros do utilizador',
settings: 'Settings', // UNTRANSLATED
settings_description: 'It real sent your at. Amounted all shy set why followed declared.', // UNTRANSLATED
field_email: 'Email',
field_phone: 'Telefone',
field_username: 'Nome de utilizador',

View file

@ -15,6 +15,8 @@ const user_details = {
new_password: 'Yeni şifre:',
},
tab_logs: 'Kullanıcı kayıtları',
settings: 'Settings', // UNTRANSLATED
settings_description: 'It real sent your at. Amounted all shy set why followed declared.', // UNTRANSLATED
field_email: 'Öncelikli e-posta adresi',
field_phone: 'Öncelikli telefon',
field_username: 'Kullanıcı Adı',

View file

@ -15,6 +15,8 @@ const user_details = {
new_password: '新密码:',
},
tab_logs: '用户日志',
settings: 'Settings', // UNTRANSLATED
settings_description: 'It real sent your at. Amounted all shy set why followed declared.', // UNTRANSLATED
field_email: '主要邮箱',
field_phone: '主要手机号码',
field_username: '用户名',