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:
parent
9d02a3d3f4
commit
e56238e6b6
12 changed files with 97 additions and 106 deletions
|
@ -0,0 +1,9 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.logs {
|
||||
margin-bottom: _.unit(6);
|
||||
|
||||
>:not(:first-child) {
|
||||
margin-top: _.unit(3);
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 && (
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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: '사용자 이름',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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ı',
|
||||
|
|
|
@ -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: '用户名',
|
||||
|
|
Loading…
Reference in a new issue