0
Fork 0
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:
Xiao Yijun 2023-01-05 16:34:01 +08:00 committed by GitHub
parent 5786d8156f
commit 6230775a4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 307 additions and 0 deletions

View file

@ -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>

View file

@ -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;

View 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);
}
}
}

View 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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;