mirror of
https://github.com/logto-io/logto.git
synced 2025-01-06 20:40:08 -05:00
feat(console): role details settings page (#2821)
This commit is contained in:
parent
5786d8156f
commit
6230775a4b
20 changed files with 307 additions and 0 deletions
|
@ -25,6 +25,7 @@ import Connectors from '@/pages/Connectors';
|
|||
import Dashboard from '@/pages/Dashboard';
|
||||
import GetStarted from '@/pages/GetStarted';
|
||||
import NotFound from '@/pages/NotFound';
|
||||
import RoleDetails from '@/pages/RoleDetails';
|
||||
import Roles from '@/pages/Roles';
|
||||
import Settings from '@/pages/Settings';
|
||||
import SignInExperience from '@/pages/SignInExperience';
|
||||
|
@ -85,6 +86,7 @@ const Main = () => {
|
|||
</Route>
|
||||
<Route path="roles">
|
||||
<Route index element={<Roles />} />
|
||||
<Route path=":id" element={<RoleDetails />} />
|
||||
</Route>
|
||||
<Route path="settings" element={<Settings />} />
|
||||
</Route>
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
import type { Role } from '@logto/schemas';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
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';
|
||||
|
||||
type Props = {
|
||||
data: Role;
|
||||
onRoleUpdated: (data: Role) => void;
|
||||
};
|
||||
|
||||
const RoleSettings = ({ data, onRoleUpdated }: Props) => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const {
|
||||
handleSubmit,
|
||||
register,
|
||||
reset,
|
||||
formState: { isDirty, isSubmitting, errors },
|
||||
} = useForm<Role>({
|
||||
defaultValues: data,
|
||||
});
|
||||
|
||||
const api = useApi();
|
||||
|
||||
const onSubmit = handleSubmit(async (formData) => {
|
||||
if (isSubmitting) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedRole = await api.patch(`/api/roles/${data.id}`, { json: formData }).json<Role>();
|
||||
reset(updatedRole);
|
||||
onRoleUpdated(updatedRole);
|
||||
toast.success(t('general.saved'));
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<DetailsForm
|
||||
isDirty={isDirty}
|
||||
isSubmitting={isSubmitting}
|
||||
onDiscard={reset}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<FormCard title="role_details.settings" description="role_details.settings_description">
|
||||
<FormField isRequired title="role_details.field_name">
|
||||
<TextInput {...register('name', { required: true })} hasError={Boolean(errors.name)} />
|
||||
</FormField>
|
||||
<FormField title="role_details.field_description">
|
||||
<TextInput {...register('description')} hasError={Boolean(errors.description)} />
|
||||
</FormField>
|
||||
</FormCard>
|
||||
</DetailsForm>
|
||||
<UnsavedChangesAlertModal hasUnsavedChanges={isDirty} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default RoleSettings;
|
38
packages/console/src/pages/RoleDetails/index.module.scss
Normal file
38
packages/console/src/pages/RoleDetails/index.module.scss
Normal file
|
@ -0,0 +1,38 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.container {
|
||||
.backLink {
|
||||
margin: _.unit(1) 0 0 _.unit(1);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: _.unit(6) _.unit(8);
|
||||
|
||||
.info {
|
||||
.name {
|
||||
font: var(--font-title-large);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.idText {
|
||||
font: var(--font-label-large);
|
||||
color: var(--color-text-secondary);
|
||||
margin-right: _.unit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.moreIcon {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
75
packages/console/src/pages/RoleDetails/index.tsx
Normal file
75
packages/console/src/pages/RoleDetails/index.tsx
Normal file
|
@ -0,0 +1,75 @@
|
|||
import type { Role } from '@logto/schemas';
|
||||
import classNames from 'classnames';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import Back from '@/assets/images/back.svg';
|
||||
import Delete from '@/assets/images/delete.svg';
|
||||
import More from '@/assets/images/more.svg';
|
||||
import ActionMenu, { ActionMenuItem } from '@/components/ActionMenu';
|
||||
import Card from '@/components/Card';
|
||||
import CopyToClipboard from '@/components/CopyToClipboard';
|
||||
import DetailsSkeleton from '@/components/DetailsSkeleton';
|
||||
import TabNav, { TabNavItem } from '@/components/TabNav';
|
||||
import TextLink from '@/components/TextLink';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
import * as detailsStyles from '@/scss/details.module.scss';
|
||||
|
||||
import RoleSettings from './RoleSettings';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
const RoleDetails = () => {
|
||||
const { id } = useParams();
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { data, error, mutate } = useSWR<Role, RequestError>(id && `/api/roles/${id}`);
|
||||
const isLoading = !data && !error;
|
||||
|
||||
return (
|
||||
<div className={classNames(detailsStyles.container, styles.container)}>
|
||||
<TextLink to="/roles" icon={<Back />} className={styles.backLink}>
|
||||
{t('role_details.back_to_roles')}
|
||||
</TextLink>
|
||||
{isLoading && <DetailsSkeleton />}
|
||||
{!data && error && <div>{`error occurred: ${error.body?.message ?? error.message}`}</div>}
|
||||
{data && (
|
||||
<>
|
||||
<Card className={styles.header}>
|
||||
<div className={styles.info}>
|
||||
<div className={styles.name}>{data.name}</div>
|
||||
<div className={styles.meta}>
|
||||
<div className={styles.idText}>{t('role_details.identifier')}</div>
|
||||
<CopyToClipboard value={data.id} size="small" />
|
||||
</div>
|
||||
</div>
|
||||
<ActionMenu
|
||||
buttonProps={{ icon: <More className={styles.moreIcon} />, size: 'large' }}
|
||||
title={t('general.more_options')}
|
||||
>
|
||||
<ActionMenuItem
|
||||
icon={<Delete />}
|
||||
type="danger"
|
||||
onClick={() => {
|
||||
// Todo:@xiaoyijun handle delete
|
||||
}}
|
||||
>
|
||||
{t('general.delete')}
|
||||
</ActionMenuItem>
|
||||
</ActionMenu>
|
||||
</Card>
|
||||
<TabNav>
|
||||
<TabNavItem href={`/roles/${data.id}`}>{t('role_details.settings_tab')}</TabNavItem>
|
||||
</TabNav>
|
||||
<RoleSettings
|
||||
data={data}
|
||||
onRoleUpdated={(data) => {
|
||||
void mutate(data);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RoleDetails;
|
|
@ -11,6 +11,7 @@ import general from './general.js';
|
|||
import get_started from './get-started.js';
|
||||
import log_details from './log-details.js';
|
||||
import logs from './logs.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';
|
||||
|
@ -49,6 +50,7 @@ const admin_console = {
|
|||
session_expired,
|
||||
welcome,
|
||||
roles,
|
||||
role_details,
|
||||
};
|
||||
|
||||
export default admin_console;
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
const role_details = {
|
||||
back_to_roles: 'Back to Roles', // UNTRANSLATED
|
||||
identifier: 'Identifier', // UNTRANSLATED
|
||||
settings_tab: 'Settings', // UNTRANSLATED
|
||||
users_tab: 'Users', // UNTRANSLATED
|
||||
permissions_tab: 'Permissions', // UNTRANSLATED
|
||||
settings: 'Settings', // UNTRANSLATED
|
||||
settings_description:
|
||||
'It real sent your at. Amounted all shy set why followed declared. Repeated of endeavor mr position kindness offering ignorant so up. Simplicity are melancholy preference considered saw companions.', // UNTRANSLATED
|
||||
field_name: 'Name', // UNTRANSLATED
|
||||
field_description: 'Description', // UNTRANSLATED
|
||||
};
|
||||
|
||||
export default role_details;
|
|
@ -11,6 +11,7 @@ import general from './general.js';
|
|||
import get_started from './get-started.js';
|
||||
import log_details from './log-details.js';
|
||||
import logs from './logs.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';
|
||||
|
@ -49,6 +50,7 @@ const admin_console = {
|
|||
session_expired,
|
||||
welcome,
|
||||
roles,
|
||||
role_details,
|
||||
};
|
||||
|
||||
export default admin_console;
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
const role_details = {
|
||||
back_to_roles: 'Back to Roles', // UNTRANSLATED
|
||||
identifier: 'Identifier', // UNTRANSLATED
|
||||
settings_tab: 'Settings', // UNTRANSLATED
|
||||
users_tab: 'Users', // UNTRANSLATED
|
||||
permissions_tab: 'Permissions', // UNTRANSLATED
|
||||
settings: 'Settings', // UNTRANSLATED
|
||||
settings_description:
|
||||
'It real sent your at. Amounted all shy set why followed declared. Repeated of endeavor mr position kindness offering ignorant so up. Simplicity are melancholy preference considered saw companions.', // UNTRANSLATED
|
||||
field_name: 'Name', // UNTRANSLATED
|
||||
field_description: 'Description', // UNTRANSLATED
|
||||
};
|
||||
|
||||
export default role_details;
|
|
@ -11,6 +11,7 @@ import general from './general.js';
|
|||
import get_started from './get-started.js';
|
||||
import log_details from './log-details.js';
|
||||
import logs from './logs.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';
|
||||
|
@ -49,6 +50,7 @@ const admin_console = {
|
|||
session_expired,
|
||||
welcome,
|
||||
roles,
|
||||
role_details,
|
||||
};
|
||||
|
||||
export default admin_console;
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
const role_details = {
|
||||
back_to_roles: 'Back to Roles', // UNTRANSLATED
|
||||
identifier: 'Identifier', // UNTRANSLATED
|
||||
settings_tab: 'Settings', // UNTRANSLATED
|
||||
users_tab: 'Users', // UNTRANSLATED
|
||||
permissions_tab: 'Permissions', // UNTRANSLATED
|
||||
settings: 'Settings', // UNTRANSLATED
|
||||
settings_description:
|
||||
'It real sent your at. Amounted all shy set why followed declared. Repeated of endeavor mr position kindness offering ignorant so up. Simplicity are melancholy preference considered saw companions.', // UNTRANSLATED
|
||||
field_name: 'Name', // UNTRANSLATED
|
||||
field_description: 'Description', // UNTRANSLATED
|
||||
};
|
||||
|
||||
export default role_details;
|
|
@ -11,6 +11,7 @@ import general from './general.js';
|
|||
import get_started from './get-started.js';
|
||||
import log_details from './log-details.js';
|
||||
import logs from './logs.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';
|
||||
|
@ -49,6 +50,7 @@ const admin_console = {
|
|||
session_expired,
|
||||
welcome,
|
||||
roles,
|
||||
role_details,
|
||||
};
|
||||
|
||||
export default admin_console;
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
const role_details = {
|
||||
back_to_roles: 'Back to Roles', // UNTRANSLATED
|
||||
identifier: 'Identifier', // UNTRANSLATED
|
||||
settings_tab: 'Settings', // UNTRANSLATED
|
||||
users_tab: 'Users', // UNTRANSLATED
|
||||
permissions_tab: 'Permissions', // UNTRANSLATED
|
||||
settings: 'Settings', // UNTRANSLATED
|
||||
settings_description:
|
||||
'It real sent your at. Amounted all shy set why followed declared. Repeated of endeavor mr position kindness offering ignorant so up. Simplicity are melancholy preference considered saw companions.', // UNTRANSLATED
|
||||
field_name: 'Name', // UNTRANSLATED
|
||||
field_description: 'Description', // UNTRANSLATED
|
||||
};
|
||||
|
||||
export default role_details;
|
|
@ -11,6 +11,7 @@ import general from './general.js';
|
|||
import get_started from './get-started.js';
|
||||
import log_details from './log-details.js';
|
||||
import logs from './logs.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';
|
||||
|
@ -49,6 +50,7 @@ const admin_console = {
|
|||
session_expired,
|
||||
welcome,
|
||||
roles,
|
||||
role_details,
|
||||
};
|
||||
|
||||
export default admin_console;
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
const role_details = {
|
||||
back_to_roles: 'Back to Roles', // UNTRANSLATED
|
||||
identifier: 'Identifier', // UNTRANSLATED
|
||||
settings_tab: 'Settings', // UNTRANSLATED
|
||||
users_tab: 'Users', // UNTRANSLATED
|
||||
permissions_tab: 'Permissions', // UNTRANSLATED
|
||||
settings: 'Settings', // UNTRANSLATED
|
||||
settings_description:
|
||||
'It real sent your at. Amounted all shy set why followed declared. Repeated of endeavor mr position kindness offering ignorant so up. Simplicity are melancholy preference considered saw companions.', // UNTRANSLATED
|
||||
field_name: 'Name', // UNTRANSLATED
|
||||
field_description: 'Description', // UNTRANSLATED
|
||||
};
|
||||
|
||||
export default role_details;
|
|
@ -11,6 +11,7 @@ import general from './general.js';
|
|||
import get_started from './get-started.js';
|
||||
import log_details from './log-details.js';
|
||||
import logs from './logs.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';
|
||||
|
@ -49,6 +50,7 @@ const admin_console = {
|
|||
session_expired,
|
||||
welcome,
|
||||
roles,
|
||||
role_details,
|
||||
};
|
||||
|
||||
export default admin_console;
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
const role_details = {
|
||||
back_to_roles: 'Back to Roles', // UNTRANSLATED
|
||||
identifier: 'Identifier', // UNTRANSLATED
|
||||
settings_tab: 'Settings', // UNTRANSLATED
|
||||
users_tab: 'Users', // UNTRANSLATED
|
||||
permissions_tab: 'Permissions', // UNTRANSLATED
|
||||
settings: 'Settings', // UNTRANSLATED
|
||||
settings_description:
|
||||
'It real sent your at. Amounted all shy set why followed declared. Repeated of endeavor mr position kindness offering ignorant so up. Simplicity are melancholy preference considered saw companions.', // UNTRANSLATED
|
||||
field_name: 'Name', // UNTRANSLATED
|
||||
field_description: 'Description', // UNTRANSLATED
|
||||
};
|
||||
|
||||
export default role_details;
|
|
@ -11,6 +11,7 @@ import general from './general.js';
|
|||
import get_started from './get-started.js';
|
||||
import log_details from './log-details.js';
|
||||
import logs from './logs.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';
|
||||
|
@ -49,6 +50,7 @@ const admin_console = {
|
|||
session_expired,
|
||||
welcome,
|
||||
roles,
|
||||
role_details,
|
||||
};
|
||||
|
||||
export default admin_console;
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
const role_details = {
|
||||
back_to_roles: 'Back to Roles', // UNTRANSLATED
|
||||
identifier: 'Identifier', // UNTRANSLATED
|
||||
settings_tab: 'Settings', // UNTRANSLATED
|
||||
users_tab: 'Users', // UNTRANSLATED
|
||||
permissions_tab: 'Permissions', // UNTRANSLATED
|
||||
settings: 'Settings', // UNTRANSLATED
|
||||
settings_description:
|
||||
'It real sent your at. Amounted all shy set why followed declared. Repeated of endeavor mr position kindness offering ignorant so up. Simplicity are melancholy preference considered saw companions.', // UNTRANSLATED
|
||||
field_name: 'Name', // UNTRANSLATED
|
||||
field_description: 'Description', // UNTRANSLATED
|
||||
};
|
||||
|
||||
export default role_details;
|
|
@ -11,6 +11,7 @@ import general from './general.js';
|
|||
import get_started from './get-started.js';
|
||||
import log_details from './log-details.js';
|
||||
import logs from './logs.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';
|
||||
|
@ -49,6 +50,7 @@ const admin_console = {
|
|||
session_expired,
|
||||
welcome,
|
||||
roles,
|
||||
role_details,
|
||||
};
|
||||
|
||||
export default admin_console;
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
const role_details = {
|
||||
back_to_roles: 'Back to Roles',
|
||||
identifier: 'Identifier',
|
||||
settings_tab: 'Settings',
|
||||
users_tab: 'Users',
|
||||
permissions_tab: 'Permissions',
|
||||
settings: 'Settings',
|
||||
settings_description:
|
||||
'It real sent your at. Amounted all shy set why followed declared. Repeated of endeavor mr position kindness offering ignorant so up. Simplicity are melancholy preference considered saw companions.', // UNTRANSLATED
|
||||
field_name: 'Name',
|
||||
field_description: 'Description',
|
||||
};
|
||||
|
||||
export default role_details;
|
Loading…
Reference in a new issue