0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-03 22:15:32 -05:00

chore(console): remove settings page and its sidebar menu item (#3291)

This commit is contained in:
Charles Zhao 2023-03-06 10:09:57 +08:00 committed by GitHub
parent 6aab01e56c
commit 1477751e30
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 66 additions and 490 deletions

View file

@ -0,0 +1,13 @@
---
"@logto/console": minor
"@logto/phrases": minor
"@logto/core": minor
"@logto/ui": minor
---
New feature: User account settings page
- We have removed the previous settings page and moved it to the account settings page. You can access to the new settings menu by clicking the user avatar in the top right corner.
- You can directly change the language or theme from the popover menu, and explore more account settings by clicking the "Profile" menu item.
- You can update your avatar, name and username in the profile page, and also changing your password.
- [Cloud] Cloud users can also link their email address and social accounts (Google and GitHub at first launch).

View file

@ -4,7 +4,6 @@ import { useLocation } from 'react-router-dom';
import Item from './components/Item';
import Section from './components/Section';
import { useSidebarMenuItems } from './hook';
import Gear from './icons/Gear';
import * as styles from './index.module.scss';
import { getPath } from './utils';
@ -34,12 +33,6 @@ const Sidebar = () => {
)}
</Section>
))}
<div className={styles.spacer} />
<Item
titleKey="settings"
icon={<Gear />}
isActive={location.pathname.startsWith(getPath('settings'))}
/>
</div>
);
};

View file

@ -113,19 +113,19 @@ const UserInfo = () => {
<SubMenu
className={styles.dropdownItem}
icon={<Palette className={styles.icon} />}
title="menu.appearance"
title="menu.appearance.label"
options={[
{
value: AppearanceMode.SyncWithSystem,
title: t('settings.appearance_system'),
title: t('menu.appearance.system'),
},
{
value: AppearanceMode.LightMode,
title: t('settings.appearance_light'),
title: t('menu.appearance.light'),
},
{
value: AppearanceMode.DarkMode,
title: t('settings.appearance_dark'),
title: t('menu.appearance.dark'),
},
]}
selectedOption={appearanceMode}

View file

@ -37,7 +37,6 @@ import RolePermissions from '@/pages/RoleDetails/RolePermissions';
import RoleSettings from '@/pages/RoleDetails/RoleSettings';
import RoleUsers from '@/pages/RoleDetails/RoleUsers';
import Roles from '@/pages/Roles';
import Settings from '@/pages/Settings';
import SignInExperience from '@/pages/SignInExperience';
import UserDetails from '@/pages/UserDetails';
import UserLogs from '@/pages/UserDetails/UserLogs';
@ -136,7 +135,6 @@ const Main = () => {
<Route path={RoleDetailsTabs.Users} element={<RoleUsers />} />
</Route>
</Route>
<Route path="settings" element={<Settings />} />
<Route path="profile">
<Route index element={<Profile />} />
<Route path="verify-password" element={<VerifyPasswordModal />} />

View file

@ -54,7 +54,7 @@ const ChangePasswordModal = () => {
clearErrors();
void handleSubmit(async ({ newPassword }) => {
await api.post(`me/password`, { json: { password: newPassword } });
toast.success(t('settings.password_changed'));
toast.success(t('profile.password_changed'));
reset();
onClose();
})();

View file

@ -1,17 +0,0 @@
@use '@/scss/underscore' as _;
.changePassword {
border: 1px solid var(--color-divider);
border-radius: 8px;
padding: _.unit(4);
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
.description {
font: var(--font-body-2);
color: var(--color-text);
margin-right: _.unit(4);
}
}

View file

@ -1,101 +0,0 @@
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
import ReactModal from 'react-modal';
import Button from '@/components/Button';
import FormField from '@/components/FormField';
import ModalLayout from '@/components/ModalLayout';
import TextInput from '@/components/TextInput';
import { adminTenantEndpoint, meApi } from '@/consts';
import { useStaticApi } from '@/hooks/use-api';
import useLogtoUserId from '@/hooks/use-logto-user-id';
import * as modalStyles from '@/scss/modal.module.scss';
import * as styles from './ChangePassword.module.scss';
type FormFields = {
password: string;
confirmPassword: string;
};
const ChangePassword = () => {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const [isOpen, setIsOpen] = useState(false);
const { watch, register, reset } = useForm<FormFields>();
const [isLoading, setIsLoading] = useState(false);
const userId = useLogtoUserId();
const api = useStaticApi({ prefixUrl: adminTenantEndpoint, resourceIndicator: meApi.indicator });
const password = watch('password');
const confirmPassword = watch('confirmPassword');
const isDisabled = !password || password !== confirmPassword;
const onSubmit = async () => {
if (!userId) {
toast.error(t('errors.unexpected_error'));
return;
}
setIsLoading(true);
await api.post(`me/password`, { json: { password } }).json();
setIsLoading(false);
setIsOpen(false);
toast.success(t('settings.password_changed'));
reset({});
};
return (
<>
<FormField title="settings.change_password">
<div className={styles.changePassword}>
<div className={styles.description}>{t('settings.change_password_description')}</div>
<Button
title="settings.change_password"
type="default"
onClick={() => {
setIsOpen(true);
}}
/>
</div>
</FormField>
<ReactModal
shouldCloseOnEsc
isOpen={isOpen}
className={modalStyles.content}
overlayClassName={modalStyles.overlay}
onRequestClose={() => {
setIsOpen(false);
}}
>
<ModalLayout
title="settings.change_modal_title"
subtitle="settings.change_modal_description"
footer={
<Button
type="primary"
title="general.confirm"
disabled={isDisabled || isLoading}
onClick={onSubmit}
/>
}
onClose={() => {
setIsOpen(false);
}}
>
<div>
<FormField title="settings.new_password">
<TextInput {...register('password', { required: true })} type="password" />
</FormField>
<FormField title="settings.confirm_password">
<TextInput {...register('confirmPassword', { required: true })} type="password" />
</FormField>
</div>
</ModalLayout>
</ReactModal>
</>
);
};
export default ChangePassword;

View file

@ -1,15 +0,0 @@
@use '@/scss/underscore' as _;
.container {
height: 100%;
display: flex;
flex-direction: column;
.cardTitle {
flex-shrink: 0;
}
>:not(:first-child) {
margin-top: _.unit(4);
}
}

View file

@ -1,123 +0,0 @@
import {
builtInLanguageOptions as consoleBuiltInLanguageOptions,
getDefaultLanguageTag,
} from '@logto/phrases';
import { AppearanceMode } from '@logto/schemas';
import { Controller, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
import { useSWRConfig } from 'swr';
import CardTitle from '@/components/CardTitle';
import DetailsForm from '@/components/DetailsForm';
import FormCard from '@/components/FormCard';
import FormField from '@/components/FormField';
import RequestDataError from '@/components/RequestDataError';
import Select from '@/components/Select';
import UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal';
import type { UserPreferences } from '@/hooks/use-user-preferences';
import useUserPreferences from '@/hooks/use-user-preferences';
import ChangePassword from './components/ChangePassword';
import * as styles from './index.module.scss';
const Settings = () => {
const {
t,
i18n: { language },
} = useTranslation(undefined, { keyPrefix: 'admin_console' });
const { mutate: mutateGlobal } = useSWRConfig();
const defaultLanguage = getDefaultLanguageTag(language);
const { data, error, update, isLoading, isLoaded } = useUserPreferences();
const {
handleSubmit,
control,
reset,
formState: { isSubmitting, isDirty },
} = useForm<UserPreferences>({ defaultValues: data });
const onSubmit = handleSubmit(async (formData) => {
if (isSubmitting) {
return;
}
await update(formData);
reset(formData);
toast.success(t('general.saved'));
});
return (
<div className={styles.container}>
<CardTitle
title="settings.title"
subtitle="settings.description"
className={styles.cardTitle}
/>
{isLoading && <div>loading</div>}
{error && (
<RequestDataError
error={error}
onRetry={() => {
void mutateGlobal('api/me/custom-data');
}}
/>
)}
{isLoaded && (
<DetailsForm
isSubmitting={isSubmitting}
isDirty={isDirty}
onSubmit={onSubmit}
onDiscard={reset}
>
<FormCard title="settings.settings">
<FormField title="settings.language">
<Controller
name="language"
control={control}
render={({ field: { value, onChange } }) => (
<Select
value={value ?? defaultLanguage}
options={consoleBuiltInLanguageOptions}
onChange={onChange}
/>
)}
/>
</FormField>
<FormField title="settings.appearance">
<Controller
name="appearanceMode"
control={control}
render={({ field: { value, onChange } }) => (
<Select
value={value}
options={[
{
value: AppearanceMode.SyncWithSystem,
title: t('settings.appearance_system'),
},
{
value: AppearanceMode.LightMode,
title: t('settings.appearance_light'),
},
{
value: AppearanceMode.DarkMode,
title: t('settings.appearance_dark'),
},
]}
onChange={onChange}
/>
)}
/>
</FormField>
<ChangePassword />
</FormCard>
</DetailsForm>
)}
<UnsavedChangesAlertModal hasUnsavedChanges={isDirty} />
</div>
);
};
export default Settings;

View file

@ -18,7 +18,6 @@ import profile from './profile.js';
import role_details from './role-details.js';
import roles from './roles.js';
import session_expired from './session-expired.js';
import settings from './settings.js';
import sign_in_exp from './sign-in-exp.js';
import tab_sections from './tab-sections.js';
import tabs from './tabs.js';
@ -46,7 +45,6 @@ const admin_console = {
user_details,
contact,
sign_in_exp,
settings,
dashboard,
logs,
log_details,

View file

@ -1,7 +1,12 @@
const menu = {
profile: 'Profil',
language: 'Sprache',
appearance: 'Darstellung',
appearance: {
label: 'Darstellung',
light: 'Hell',
dark: 'Dunkel',
system: 'Synchonisiere mit Systemeinstellungen',
},
sign_out: 'Abmelden',
};

View file

@ -1,25 +0,0 @@
const settings = {
title: 'Einstellungen',
description: 'Verwalte die globalen Einstellungen',
settings: 'Einstellungen',
custom_domain: 'Benutzerdefinierte Domain',
language: 'Sprache',
appearance: 'Darstellung',
appearance_system: 'Synchonisiere mit Systemeinstellungen',
appearance_light: 'Hell',
appearance_dark: 'Dunkel',
saved: 'Gespeichert!',
change_password: 'Passwort ändern',
change_password_description:
'Du kannst das Passwort für dieses Konto ändern. Du verwendest den aktuellen Benutzernamen mit dem neuen Passwort, um dich in der Admin Konsole anzumelden.',
change_modal_title: 'Account Password ändern',
change_modal_description:
'Du verwendest den aktuellen Benutzernamen mit dem neuen Passwort, um dich in der Admin Konsole anzumelden.',
new_password: 'Neues Passwort',
new_password_placeholder: 'Gib ein neues Passwort ein',
confirm_password: 'Passwort bestätigen',
confirm_password_placeholder: 'Bestätige das neue Passwort',
password_changed: 'Passwort geändert!',
};
export default settings;

View file

@ -18,7 +18,6 @@ import profile from './profile.js';
import role_details from './role-details.js';
import roles from './roles.js';
import session_expired from './session-expired.js';
import settings from './settings.js';
import sign_in_exp from './sign-in-exp.js';
import tab_sections from './tab-sections.js';
import tabs from './tabs.js';
@ -46,7 +45,6 @@ const admin_console = {
user_details,
contact,
sign_in_exp,
settings,
dashboard,
logs,
log_details,

View file

@ -1,7 +1,12 @@
const menu = {
profile: 'Profile',
language: 'Language',
appearance: 'Appearance',
appearance: {
label: 'Appearance',
light: 'Light mode',
dark: 'Dark mode',
system: 'Sync with system',
},
sign_out: 'Sign Out',
};

View file

@ -1,25 +0,0 @@
const settings = {
title: 'Settings',
description: 'Manage the global settings',
settings: 'Settings',
custom_domain: 'Custom domain',
language: 'Language',
appearance: 'Appearance',
appearance_system: 'Sync with system',
appearance_light: 'Light mode',
appearance_dark: 'Dark mode',
saved: 'Saved!',
change_password: 'Change Password',
change_password_description:
'You can change password for this account. You will use current username with new password to sign in Admin Console.',
change_modal_title: 'Change Account Password',
change_modal_description:
'You will use current username with new password to sign in Admin Console.',
new_password: 'New password',
new_password_placeholder: 'Enter your password',
confirm_password: 'Confirm password',
confirm_password_placeholder: 'Confirm your password',
password_changed: 'Password changed!',
};
export default settings;

View file

@ -18,7 +18,6 @@ import profile from './profile.js';
import role_details from './role-details.js';
import roles from './roles.js';
import session_expired from './session-expired.js';
import settings from './settings.js';
import sign_in_exp from './sign-in-exp.js';
import tab_sections from './tab-sections.js';
import tabs from './tabs.js';
@ -46,7 +45,6 @@ const admin_console = {
user_details,
contact,
sign_in_exp,
settings,
dashboard,
logs,
log_details,

View file

@ -1,7 +1,12 @@
const menu = {
profile: 'Profile',
language: 'Langue',
appearance: 'Apparence',
appearance: {
label: 'Apparence',
light: 'Mode clair',
dark: 'Mode sombre',
system: 'Synchronisation avec le système',
},
sign_out: 'Sign Out',
};

View file

@ -1,25 +0,0 @@
const settings = {
title: 'Paramètres',
description: 'Gérer les paramètres globaux',
settings: 'Paramètres',
custom_domain: 'Domaine personnalisé',
language: 'Langue',
appearance: 'Apparence',
appearance_system: 'Synchronisation avec le système',
appearance_light: 'Mode clair',
appearance_dark: 'Mode sombre',
saved: 'Sauvegardé !',
change_password: 'Changer le mot de passe',
change_password_description:
"Vous pouvez changer le mot de passe de ce compte. Vous utiliserez le nom d'utilisateur actuel avec le nouveau mot de passe pour vous connecter à la console d'administration.",
change_modal_title: 'Changer le mot de passe du compte',
change_modal_description:
"Vous utiliserez votre nom d'utilisateur actuel et votre nouveau mot de passe pour vous connecter à la console d'administration.",
new_password: 'Nouveau mot de passe',
new_password_placeholder: 'Entrez votre mot de passe',
confirm_password: 'Confirmez votre mot de passe',
confirm_password_placeholder: 'Confirmez votre mot de passe',
password_changed: 'Mot de passe changé !',
};
export default settings;

View file

@ -18,7 +18,6 @@ import profile from './profile.js';
import role_details from './role-details.js';
import roles from './roles.js';
import session_expired from './session-expired.js';
import settings from './settings.js';
import sign_in_exp from './sign-in-exp.js';
import tab_sections from './tab-sections.js';
import tabs from './tabs.js';
@ -46,7 +45,6 @@ const admin_console = {
user_details,
contact,
sign_in_exp,
settings,
dashboard,
logs,
log_details,

View file

@ -1,7 +1,12 @@
const menu = {
profile: '프로필',
language: '언어',
appearance: '모습',
appearance: {
label: '모습',
light: '라이트 모드',
dark: '다크 모드',
system: '시스템과 동기화',
},
sign_out: '로그아웃',
};

View file

@ -1,23 +0,0 @@
const settings = {
title: '설정',
description: '전체 설정을 관리해 보세요.',
settings: '설정',
custom_domain: '커스텀 도메인',
language: '언어',
appearance: '모습',
appearance_system: '시스템과 동기화',
appearance_light: '라이트 모드',
appearance_dark: '다크 모드',
saved: '저장되었어요!',
change_password: '비밀번호 변경',
change_password_description: '현재 계정의 비밀번호를 변경할 수 있어요.',
change_modal_title: '계정 비밀번호 변경',
change_modal_description: '새로 변경된 비밀번호로 로그인해야 해요.',
new_password: '새로운 비밀번호',
new_password_placeholder: '새로운 비밀번호를 입력해 주세요.',
confirm_password: '비밀번호 확인',
confirm_password_placeholder: '비밀번호를 다시 입력해 주세요.',
password_changed: '비밀번호가 변경되었어요!',
};
export default settings;

View file

@ -18,7 +18,6 @@ import profile from './profile.js';
import role_details from './role-details.js';
import roles from './roles.js';
import session_expired from './session-expired.js';
import settings from './settings.js';
import sign_in_exp from './sign-in-exp.js';
import tab_sections from './tab-sections.js';
import tabs from './tabs.js';
@ -46,7 +45,6 @@ const admin_console = {
user_details,
contact,
sign_in_exp,
settings,
dashboard,
logs,
log_details,

View file

@ -1,7 +1,12 @@
const menu = {
profile: 'Perfil',
language: 'Idioma',
appearance: 'Aparência',
appearance: {
label: 'Aparência',
light: 'Modo claro',
dark: 'Modo escuro',
system: 'Sincronizar com o sistema',
},
sign_out: 'Sair',
};

View file

@ -1,25 +0,0 @@
const settings = {
title: 'Configurações',
description: 'Gerenciar as configurações globais',
settings: 'Configurações',
custom_domain: 'Domínio personalizado',
language: 'Idioma',
appearance: 'Aparência',
appearance_system: 'Sincronizar com o sistema',
appearance_light: 'Modo claro',
appearance_dark: 'Modo escuro',
saved: 'Salvou!',
change_password: 'Mudar senha',
change_password_description:
'Você pode alterar a senha desta conta. Você usará o nome de usuário atual com a nova senha para entrar no Admin Console.',
change_modal_title: 'Modificar senha da conta',
change_modal_description:
'Você usará o nome de usuário atual com a nova senha para entrar no Admin Console.',
new_password: 'Nova senha',
new_password_placeholder: 'Digite sua senha',
confirm_password: 'Confirme a senha',
confirm_password_placeholder: 'Confirme sua senha',
password_changed: 'Senha alterada!',
};
export default settings;

View file

@ -18,7 +18,6 @@ import profile from './profile.js';
import role_details from './role-details.js';
import roles from './roles.js';
import session_expired from './session-expired.js';
import settings from './settings.js';
import sign_in_exp from './sign-in-exp.js';
import tab_sections from './tab-sections.js';
import tabs from './tabs.js';
@ -46,7 +45,6 @@ const admin_console = {
user_details,
contact,
sign_in_exp,
settings,
dashboard,
logs,
log_details,

View file

@ -1,7 +1,12 @@
const menu = {
profile: 'Perfil',
language: 'Linguagem',
appearance: 'Aparência',
appearance: {
label: 'Aparência',
light: 'Claro',
dark: 'Escuro',
system: 'Sincronizar com o sistemam',
},
sign_out: 'Terminar sessão',
};

View file

@ -1,25 +0,0 @@
const settings = {
title: 'Definições',
description: 'Gerenciar as configurações globais',
settings: 'Definições',
custom_domain: 'Domínio personalizado',
language: 'Linguagem',
appearance: 'Aparência',
appearance_system: 'Sincronizar com o sistema',
appearance_light: 'Claro',
appearance_dark: 'Escuro',
saved: 'Guardado!',
change_password: 'Mudar password',
change_password_description:
'Pode alterar a senha desta conta. Usará o nome de utilizador atual com a nova password para fazer login na consola.',
change_modal_title: 'Alterar password da conta',
change_modal_description:
'Usará o nome de utilizador atual com a nova password para fazer login na consola.',
new_password: 'Nova Password',
new_password_placeholder: 'Introduza a password',
confirm_password: 'Confirme a password',
confirm_password_placeholder: 'Confirme a password',
password_changed: 'Password alterada!',
};
export default settings;

View file

@ -18,7 +18,6 @@ import profile from './profile.js';
import role_details from './role-details.js';
import roles from './roles.js';
import session_expired from './session-expired.js';
import settings from './settings.js';
import sign_in_exp from './sign-in-exp.js';
import tab_sections from './tab-sections.js';
import tabs from './tabs.js';
@ -46,7 +45,6 @@ const admin_console = {
user_details,
contact,
sign_in_exp,
settings,
dashboard,
logs,
log_details,

View file

@ -1,7 +1,12 @@
const menu = {
profile: 'Profil',
language: 'Dil',
appearance: 'Görünüm',
appearance: {
label: 'Görünüm',
light: 'Açık mod',
dark: 'Koyu mod',
system: 'Sistemle senkronize et',
},
sign_out: ıkış Yap',
};

View file

@ -1,25 +0,0 @@
const settings = {
title: 'Ayarlar',
description: 'Genel ayarları yönet',
settings: 'Ayarlar',
custom_domain: 'Özel alan',
language: 'Dil',
appearance: 'Görünüm',
appearance_system: 'Sistemle senkronize et',
appearance_light: 'Açık mod',
appearance_dark: 'Koyu mod',
saved: 'Kaydedildi!',
change_password: 'Şifreyi Değiştir',
change_password_description:
'Bu hesabın şifresini değiştirebilirsiniz. Yönetici Panelinde oturum açmak için mevcut kullanıcı adını yeni şifreyle kullanacaksınız.',
change_modal_title: 'Hesap Şifresini Değiştir',
change_modal_description:
'Yönetici Panelinde oturum açmak için mevcut kullanıcı adını yeni şifreyle kullanacaksınız.',
new_password: 'Yeni şifre',
new_password_placeholder: 'Şifre giriniz',
confirm_password: 'Şifre onayla',
confirm_password_placeholder: 'Şifrenizi onaylayınız',
password_changed: 'Şifre değiştirildi!',
};
export default settings;

View file

@ -18,7 +18,6 @@ import profile from './profile.js';
import role_details from './role-details.js';
import roles from './roles.js';
import session_expired from './session-expired.js';
import settings from './settings.js';
import sign_in_exp from './sign-in-exp.js';
import tab_sections from './tab-sections.js';
import tabs from './tabs.js';
@ -46,7 +45,6 @@ const admin_console = {
user_details,
contact,
sign_in_exp,
settings,
dashboard,
logs,
log_details,

View file

@ -1,7 +1,12 @@
const menu = {
profile: '帐户管理',
language: '语言',
appearance: '主题',
appearance: {
label: '主题',
light: '浅色模式',
dark: '深色模式',
system: '跟随系统',
},
sign_out: '退出登录',
};

View file

@ -1,23 +0,0 @@
const settings = {
title: '设置',
description: '管理全局设置',
settings: '设置',
custom_domain: '自定义域名',
language: '语言',
appearance: '外观',
appearance_system: '跟随系统',
appearance_light: '浅色模式',
appearance_dark: '深色模式',
saved: '已保存!',
change_password: '修改密码',
change_password_description: '修改本帐号密码。生效后使用当前用户名和新密码登录管理控制台。',
change_modal_title: '修改帐号密码',
change_modal_description: '生效后使用当前用户名和新密码登录管理控制台。',
new_password: '新密码',
new_password_placeholder: '输入密码',
confirm_password: '确认密码',
confirm_password_placeholder: '确认密码',
password_changed: '密码已修改!',
};
export default settings;