Skip to content

Commit

Permalink
feat(console): support editing permissions from org role details page
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoyijun committed Apr 10, 2024
1 parent b99fb0d commit ec31b18
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type ScopeResponse } from '@logto/schemas';
import { type AdminConsoleKey } from '@logto/phrases';
import { type Nullable } from '@silverhand/essentials';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import ReactModal from 'react-modal';
Expand All @@ -10,24 +11,43 @@ import TextInput from '@/ds-components/TextInput';
import * as modalStyles from '@/scss/modal.module.scss';
import { trySubmitSafe } from '@/utils/form';

export type EditScopeData = {
/** Only `description` is editable for all kinds of scopes */
description: Nullable<string>;
};

type Props = {
data: ScopeResponse;
/** The scope name displayed in the name input field */
scopeName: string;
/** The data to edit */
data: EditScopeData;
/** Determines the translation keys for texts in the editor modal */
text: {
/** The translation key of the modal title */
title: AdminConsoleKey;
/** The field name translation key for the name input */
nameField: AdminConsoleKey;
/** The field name translation key for the description input */
descriptionField: AdminConsoleKey;
/** The placeholder translation key for the description input */
descriptionPlaceholder: AdminConsoleKey;
};
onSubmit: (editedData: EditScopeData) => Promise<void>;
onClose: () => void;
onSubmit: (scope: ScopeResponse) => Promise<void>;
};

function EditPermissionModal({ data, onClose, onSubmit }: Props) {
function EditScopeModal({ scopeName, data, text, onClose, onSubmit }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });

const {
handleSubmit,
register,
formState: { isSubmitting },
} = useForm<ScopeResponse>({ defaultValues: data });
} = useForm<EditScopeData>({ defaultValues: data });

const onSubmitHandler = handleSubmit(
trySubmitSafe(async (formData) => {
await onSubmit({ ...data, ...formData });
await onSubmit(formData);
onClose();
})
);
Expand All @@ -43,7 +63,7 @@ function EditPermissionModal({ data, onClose, onSubmit }: Props) {
}}
>
<ModalLayout
title="permissions.edit_title"
title={text.title}
footer={
<>
<Button isLoading={isSubmitting} title="general.cancel" onClick={onClose} />
Expand All @@ -59,14 +79,14 @@ function EditPermissionModal({ data, onClose, onSubmit }: Props) {
onClose={onClose}
>
<form>
<FormField title="api_resource_details.permission.name">
<TextInput readOnly value={data.name} />
<FormField title={text.nameField}>
<TextInput readOnly value={scopeName} />
</FormField>
<FormField title="api_resource_details.permission.description">
<FormField title={text.descriptionField}>
<TextInput
// eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus
placeholder={t('api_resource_details.permission.description_placeholder')}
placeholder={String(t(text.descriptionPlaceholder))}
{...register('description')}
/>
</FormField>
Expand All @@ -76,4 +96,4 @@ function EditPermissionModal({ data, onClose, onSubmit }: Props) {
);
}

export default EditPermissionModal;
export default EditScopeModal;
19 changes: 14 additions & 5 deletions packages/console/src/components/PermissionsTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import useApi from '@/hooks/use-api';
import useDocumentationUrl from '@/hooks/use-documentation-url';

import ActionsButton from '../ActionsButton';
import EditScopeModal, { type EditScopeData } from '../EditScopeModal';
import EmptyDataPlaceholder from '../EmptyDataPlaceholder';

import EditPermissionModal from './EditPermissionModal';
import * as styles from './index.module.scss';

type SearchProps = {
Expand Down Expand Up @@ -98,9 +98,9 @@ function PermissionsTable({

const api = useApi();

const handleEdit = async (scope: ScopeResponse) => {
const handleEdit = async (scope: ScopeResponse, editedData: EditScopeData) => {
const patchApiEndpoint = `api/resources/${scope.resourceId}/scopes/${scope.id}`;
await api.patch(patchApiEndpoint, { json: scope });
await api.patch(patchApiEndpoint, { json: editedData });
toast.success(t('permissions.updated'));
onPermissionUpdated();
};
Expand Down Expand Up @@ -236,12 +236,21 @@ function PermissionsTable({
onRetry={retryHandler}
/>
{editingScope && (
<EditPermissionModal
<EditScopeModal
scopeName={editingScope.name}
data={editingScope}
text={{
title: 'permissions.edit_title',
nameField: 'api_resource_details.permission.name',
descriptionField: 'api_resource_details.permission.description',
descriptionPlaceholder: 'api_resource_details.permission.description_placeholder',
}}
onSubmit={async (editedData) => {
await handleEdit(editingScope, editedData);
}}
onClose={() => {
setEditingScope(undefined);
}}
onSubmit={handleEdit}
/>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { useTranslation } from 'react-i18next';
import Plus from '@/assets/icons/plus.svg';
import ActionsButton from '@/components/ActionsButton';
import Breakable from '@/components/Breakable';
import EditScopeModal, { type EditScopeData } from '@/components/EditScopeModal';
import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder';
import ManageOrganizationPermissionModal from '@/components/ManageOrganizationPermissionModal';
import Button from '@/ds-components/Button';
import DynamicT from '@/ds-components/DynamicT';
import Search from '@/ds-components/Search';
Expand Down Expand Up @@ -53,7 +53,7 @@ function Permissions({ organizationRoleId }: Props) {
[filterScopes, keyword, organizationScopes, resourceScopes]
);

const [editOrganizationScope, setEditOrganizationScope] = useState<OrganizationScope>();
const [editingScope, setEditingScope] = useState<OrganizationScope>();

const removeScopeHandler = useCallback(
(scopeToRemove: OrganizationRoleScope) => async () => {
Expand All @@ -68,18 +68,14 @@ function Permissions({ organizationRoleId }: Props) {
[api, mutate, organizationRolePath, t]
);

const editScopeHandler = useCallback(
(scopeToEdit: OrganizationRoleScope) => async () => {
if (isResourceScope(scopeToEdit)) {
// Todo @xiaoyijun support resource scope editing

return;
}

setEditOrganizationScope(scopeToEdit);
},
[]
);
const handleEdit = async (scope: OrganizationRoleScope, editedData: EditScopeData) => {
const patchApiEndpoint = isResourceScope(scope)
? `api/resources/${scope.resourceId}/scopes/${scope.id}`
: `api/organization-scopes/${scope.id}`;
await api.patch(patchApiEndpoint, { json: editedData });
toast.success(t('permissions.updated'));
mutate();
};

return (
<>
Expand Down Expand Up @@ -136,7 +132,9 @@ function Permissions({ organizationRoleId }: Props) {
delete: 'organization_role_details.permissions.remove_permission',
deleteConfirmation: 'general.remove',
}}
onEdit={editScopeHandler(scope)}
onEdit={() => {
setEditingScope(scope);
}}
onDelete={removeScopeHandler(scope)}
/>
),
Expand Down Expand Up @@ -173,12 +171,31 @@ function Permissions({ organizationRoleId }: Props) {
errorMessage={error?.body?.message ?? error?.message}
onRetry={mutate}
/>
{editOrganizationScope && (
<ManageOrganizationPermissionModal
data={editOrganizationScope}
{editingScope && (
<EditScopeModal
scopeName={editingScope.name}
data={editingScope}
text={
isResourceScope(editingScope)
? {
title: 'permissions.edit_title',
nameField: 'api_resource_details.permission.name',
descriptionField: 'api_resource_details.permission.description',
descriptionPlaceholder: 'api_resource_details.permission.description_placeholder',
}
: {
title: 'organization_template.permissions.edit_title',
nameField: 'organization_template.permissions.permission_field_name',
descriptionField: 'organization_template.permissions.description_field_name',
descriptionPlaceholder:
'organization_template.permissions.description_field_placeholder',
}
}
onSubmit={async (editedData) => {
await handleEdit(editingScope, editedData);
}}
onClose={() => {
setEditOrganizationScope(undefined);
mutate();
setEditingScope(undefined);
}}
/>
)}
Expand Down

0 comments on commit ec31b18

Please sign in to comment.