0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-02-17 22:04:19 -05:00

feat(console,phrases): add permissions tab to the application details page 4-3 (#5213)

* feat(console,phrases): implement the third-party app permissions table

implement the third-party app permissions table

* fix(console,phrases): update content

update content

* fix(console): add permission table classname

add permission table classname
This commit is contained in:
simeng-li 2024-01-16 15:40:51 +08:00 committed by GitHub
parent 2d41cbca8f
commit 400ee914d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 426 additions and 3 deletions

View file

@ -17,6 +17,7 @@ type Props<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValue
* The optional table name. The value must be a valid phrase singular key with plural support.
*/
name?: AdminConsoleKey;
className?: string;
rowIndexKey: TName;
rowGroups: Array<RowGroup<TFieldValues>>;
columns: Array<Column<TFieldValues>>;
@ -40,6 +41,7 @@ function TemplateTable<
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
name,
className,
rowIndexKey,
rowGroups,
columns,
@ -75,6 +77,7 @@ function TemplateTable<
<Table
hasBorder
isRowHoverEffectDisabled
className={className}
isLoading={isLoading}
rowGroups={rowGroups}
columns={columns}

View file

@ -123,10 +123,10 @@ function Table<
<TableEmptyWrapper columns={columns.length}>{placeholder}</TableEmptyWrapper>
)}
{isLoaded &&
rowGroups.map(({ key, label, labelClassName, data }) => (
rowGroups.map(({ key, label, labelRowClassName, labelClassName, data }) => (
<Fragment key={key}>
{label && (
<tr>
<tr className={labelRowClassName}>
<td colSpan={totalColspan} className={labelClassName}>
{label}
</td>

View file

@ -12,6 +12,7 @@ export type Column<TFieldValues extends FieldValues = FieldValues> = {
export type RowGroup<TFieldValues extends FieldValues = FieldValues> = {
key: Key;
label?: ReactNode;
labelRowClassName?: string;
labelClassName?: string;
data?: TFieldValues[];
};

View file

@ -0,0 +1,12 @@
@use '@/scss/underscore' as _;
// Override the Table component styles
.permissionsModal [class*='tableContainer'] [class*='bodyTable'] tr.sectionTitleRow {
height: _.unit(9);
td {
color: var(--color-text-secondary);
background-color: var(--color-layer-light);
}
}

View file

@ -0,0 +1,92 @@
import { type Application, type ApplicationUserConsentScopesResponse } from '@logto/schemas';
import { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import useSWR from 'swr';
import ActionsButton from '@/components/ActionsButton';
import Breakable from '@/components/Breakable';
import FormCard from '@/components/FormCard';
import TemplateTable from '@/components/TemplateTable';
import Tag from '@/ds-components/Tag';
import { type RequestError } from '@/hooks/use-api';
import * as styles from './index.module.scss';
import usePermissionsTable from './use-permissions-table';
type Props = {
application: Application;
};
function Permissions({ application }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const [isAssignScopesModalOpen, setIsAssignScopesModalOpen] = useState(false);
const { parseRowGroup, deletePermission } = usePermissionsTable();
const { data, mutate, isLoading } = useSWR<ApplicationUserConsentScopesResponse, RequestError>(
`api/applications/${application.id}/user-consent-scopes`
);
const rowGroups = useMemo(() => parseRowGroup(data), [data, parseRowGroup]);
return (
<FormCard
title="application_details.permissions.name"
description="application_details.permissions.description"
>
<TemplateTable
className={styles.permissionsModal}
name="application_details.permissions.table_name"
rowIndexKey="id"
isLoading={isLoading}
rowGroups={rowGroups}
columns={[
{
title: t('application_details.permissions.field_name'),
dataIndex: 'name',
colSpan: 5,
render: ({ name }) => (
<Tag variant="cell">
<Breakable>{name}</Breakable>
</Tag>
),
},
{
title: `${t('general.description')} (${t(
'application_details.permissions.field_description'
)})`,
dataIndex: 'description',
colSpan: 5,
render: ({ description }) => <Breakable>{description ?? '-'}</Breakable>,
},
{
title: null,
dataIndex: 'delete',
render: (data) => (
<ActionsButton
fieldName="application_details.permissions.name"
deleteConfirmation="application_details.permissions.permission_delete_confirm"
textOverrides={{
delete: 'application_details.permissions.delete_text',
deleteConfirmation: 'general.remove',
}}
onEdit={() => {
// TODO: Implement edit permission
}}
onDelete={async () => {
await deletePermission(data, application.id);
void mutate();
}}
/>
),
},
]}
onAdd={() => {
setIsAssignScopesModalOpen(true);
}}
/>
</FormCard>
);
}
export default Permissions;

View file

@ -0,0 +1,96 @@
import {
type ApplicationUserConsentScopesResponse,
ApplicationUserConsentScopeType,
} from '@logto/schemas';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import useApi from '@/hooks/use-api';
import * as styles from './index.module.scss';
type PermissionsTableRowDataType = {
type: ApplicationUserConsentScopeType;
id: string;
name: string;
description?: string;
};
type PermissionsTableFieldGroupType = {
key: string;
label: string;
labelRowClassName?: string;
data: PermissionsTableRowDataType[];
};
/**
* - parseRowGroup: parse the application user consent scopes response data to table field group data
*/
const usePermissionsTable = () => {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const api = useApi();
const parseRowGroup = useCallback(
(data?: ApplicationUserConsentScopesResponse): PermissionsTableFieldGroupType[] => {
if (!data) {
return [];
}
const { organizationScopes, userScopes, resourceScopes } = data;
const userScopesGroup: PermissionsTableFieldGroupType = {
key: ApplicationUserConsentScopeType.UserScopes,
label: t('application_details.permissions.user_permissions'),
labelRowClassName: styles.sectionTitleRow,
data: userScopes.map((scope) => ({
type: ApplicationUserConsentScopeType.UserScopes,
id: scope,
name: scope,
// TODO: @simeng-li add user profile scopes description
})),
};
const organizationScopesGroup: PermissionsTableFieldGroupType = {
key: ApplicationUserConsentScopeType.OrganizationScopes,
label: t('application_details.permissions.organization_permissions'),
labelRowClassName: styles.sectionTitleRow,
data: organizationScopes.map(({ id, name, description }) => ({
type: ApplicationUserConsentScopeType.OrganizationScopes,
id,
name,
description: description ?? undefined,
})),
};
const resourceScopesGroups = resourceScopes.map<PermissionsTableFieldGroupType>(
({ resource, scopes }) => ({
key: resource.indicator,
label: resource.name,
labelRowClassName: styles.sectionTitleRow,
data: scopes.map(({ id, name, description }) => ({
type: ApplicationUserConsentScopeType.ResourceScopes,
id,
name,
description,
})),
})
);
return [userScopesGroup, ...resourceScopesGroups, organizationScopesGroup];
},
[t]
);
const deletePermission = useCallback(
async (scope: PermissionsTableRowDataType, applicationId: string) =>
api.delete(`api/applications/${applicationId}/user-consent-scopes/${scope.type}/${scope.id}`),
[api]
);
return {
parseRowGroup,
deletePermission,
};
};
export default usePermissionsTable;

View file

@ -40,6 +40,7 @@ import GuideDrawer from './components/GuideDrawer';
import GuideModal from './components/GuideModal';
import MachineLogs from './components/MachineLogs';
import MachineToMachineApplicationRoles from './components/MachineToMachineApplicationRoles';
import Permissions from './components/Permissions';
import RefreshTokenSettings from './components/RefreshTokenSettings';
import Settings from './components/Settings';
import * as styles from './index.module.scss';
@ -286,7 +287,7 @@ function ApplicationDetails() {
isActive={tab === ApplicationDetailsTabs.Permissions}
className={styles.tabContainer}
>
<div>Permissions</div>
<Permissions application={data} />
</TabWrapper>
<TabWrapper
isActive={tab === ApplicationDetailsTabs.Branding}

View file

@ -93,6 +93,21 @@ const application_details = {
/** UNTRANSLATED */
description:
'Select the permissions that the third-party application requires for user authorization to access specific data types.',
/** UNTRANSLATED */
user_permissions: 'Personal user information',
/** UNTRANSLATED */
organization_permissions: 'Organization access',
/** UNTRANSLATED */
table_name: 'Grant permissions',
/** UNTRANSLATED */
field_name: 'Permission',
/** UNTRANSLATED */
field_description: 'Displayed in the consent screen',
/** UNTRANSLATED */
delete_text: 'Remove permission',
/** UNTRANSLATED */
permission_delete_confirm:
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
},
roles: {
name_column: 'Rolle',

View file

@ -76,6 +76,14 @@ const application_details = {
name: 'Permissions',
description:
'Select the permissions that the third-party application requires for user authorization to access specific data types.',
user_permissions: 'Personal user information',
organization_permissions: 'Organization access',
table_name: 'Grant permissions',
field_name: 'Permission',
field_description: 'Displayed in the consent screen',
delete_text: 'Remove permission',
permission_delete_confirm:
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
},
roles: {
name_column: 'Role',

View file

@ -93,6 +93,21 @@ const application_details = {
/** UNTRANSLATED */
description:
'Select the permissions that the third-party application requires for user authorization to access specific data types.',
/** UNTRANSLATED */
user_permissions: 'Personal user information',
/** UNTRANSLATED */
organization_permissions: 'Organization access',
/** UNTRANSLATED */
table_name: 'Grant permissions',
/** UNTRANSLATED */
field_name: 'Permission',
/** UNTRANSLATED */
field_description: 'Displayed in the consent screen',
/** UNTRANSLATED */
delete_text: 'Remove permission',
/** UNTRANSLATED */
permission_delete_confirm:
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
},
roles: {
name_column: 'Rol',

View file

@ -93,6 +93,21 @@ const application_details = {
/** UNTRANSLATED */
description:
'Select the permissions that the third-party application requires for user authorization to access specific data types.',
/** UNTRANSLATED */
user_permissions: 'Personal user information',
/** UNTRANSLATED */
organization_permissions: 'Organization access',
/** UNTRANSLATED */
table_name: 'Grant permissions',
/** UNTRANSLATED */
field_name: 'Permission',
/** UNTRANSLATED */
field_description: 'Displayed in the consent screen',
/** UNTRANSLATED */
delete_text: 'Remove permission',
/** UNTRANSLATED */
permission_delete_confirm:
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
},
roles: {
name_column: 'Rôle',

View file

@ -93,6 +93,21 @@ const application_details = {
/** UNTRANSLATED */
description:
'Select the permissions that the third-party application requires for user authorization to access specific data types.',
/** UNTRANSLATED */
user_permissions: 'Personal user information',
/** UNTRANSLATED */
organization_permissions: 'Organization access',
/** UNTRANSLATED */
table_name: 'Grant permissions',
/** UNTRANSLATED */
field_name: 'Permission',
/** UNTRANSLATED */
field_description: 'Displayed in the consent screen',
/** UNTRANSLATED */
delete_text: 'Remove permission',
/** UNTRANSLATED */
permission_delete_confirm:
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
},
roles: {
name_column: 'Ruolo',

View file

@ -93,6 +93,21 @@ const application_details = {
/** UNTRANSLATED */
description:
'Select the permissions that the third-party application requires for user authorization to access specific data types.',
/** UNTRANSLATED */
user_permissions: 'Personal user information',
/** UNTRANSLATED */
organization_permissions: 'Organization access',
/** UNTRANSLATED */
table_name: 'Grant permissions',
/** UNTRANSLATED */
field_name: 'Permission',
/** UNTRANSLATED */
field_description: 'Displayed in the consent screen',
/** UNTRANSLATED */
delete_text: 'Remove permission',
/** UNTRANSLATED */
permission_delete_confirm:
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
},
roles: {
name_column: '役割',

View file

@ -93,6 +93,21 @@ const application_details = {
/** UNTRANSLATED */
description:
'Select the permissions that the third-party application requires for user authorization to access specific data types.',
/** UNTRANSLATED */
user_permissions: 'Personal user information',
/** UNTRANSLATED */
organization_permissions: 'Organization access',
/** UNTRANSLATED */
table_name: 'Grant permissions',
/** UNTRANSLATED */
field_name: 'Permission',
/** UNTRANSLATED */
field_description: 'Displayed in the consent screen',
/** UNTRANSLATED */
delete_text: 'Remove permission',
/** UNTRANSLATED */
permission_delete_confirm:
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
},
roles: {
name_column: '역할',

View file

@ -93,6 +93,21 @@ const application_details = {
/** UNTRANSLATED */
description:
'Select the permissions that the third-party application requires for user authorization to access specific data types.',
/** UNTRANSLATED */
user_permissions: 'Personal user information',
/** UNTRANSLATED */
organization_permissions: 'Organization access',
/** UNTRANSLATED */
table_name: 'Grant permissions',
/** UNTRANSLATED */
field_name: 'Permission',
/** UNTRANSLATED */
field_description: 'Displayed in the consent screen',
/** UNTRANSLATED */
delete_text: 'Remove permission',
/** UNTRANSLATED */
permission_delete_confirm:
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
},
roles: {
name_column: 'Role',

View file

@ -93,6 +93,21 @@ const application_details = {
/** UNTRANSLATED */
description:
'Select the permissions that the third-party application requires for user authorization to access specific data types.',
/** UNTRANSLATED */
user_permissions: 'Personal user information',
/** UNTRANSLATED */
organization_permissions: 'Organization access',
/** UNTRANSLATED */
table_name: 'Grant permissions',
/** UNTRANSLATED */
field_name: 'Permission',
/** UNTRANSLATED */
field_description: 'Displayed in the consent screen',
/** UNTRANSLATED */
delete_text: 'Remove permission',
/** UNTRANSLATED */
permission_delete_confirm:
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
},
roles: {
name_column: 'Função',

View file

@ -93,6 +93,21 @@ const application_details = {
/** UNTRANSLATED */
description:
'Select the permissions that the third-party application requires for user authorization to access specific data types.',
/** UNTRANSLATED */
user_permissions: 'Personal user information',
/** UNTRANSLATED */
organization_permissions: 'Organization access',
/** UNTRANSLATED */
table_name: 'Grant permissions',
/** UNTRANSLATED */
field_name: 'Permission',
/** UNTRANSLATED */
field_description: 'Displayed in the consent screen',
/** UNTRANSLATED */
delete_text: 'Remove permission',
/** UNTRANSLATED */
permission_delete_confirm:
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
},
roles: {
name_column: 'Nome da função',

View file

@ -93,6 +93,21 @@ const application_details = {
/** UNTRANSLATED */
description:
'Select the permissions that the third-party application requires for user authorization to access specific data types.',
/** UNTRANSLATED */
user_permissions: 'Personal user information',
/** UNTRANSLATED */
organization_permissions: 'Organization access',
/** UNTRANSLATED */
table_name: 'Grant permissions',
/** UNTRANSLATED */
field_name: 'Permission',
/** UNTRANSLATED */
field_description: 'Displayed in the consent screen',
/** UNTRANSLATED */
delete_text: 'Remove permission',
/** UNTRANSLATED */
permission_delete_confirm:
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
},
roles: {
name_column: 'Роль',

View file

@ -93,6 +93,21 @@ const application_details = {
/** UNTRANSLATED */
description:
'Select the permissions that the third-party application requires for user authorization to access specific data types.',
/** UNTRANSLATED */
user_permissions: 'Personal user information',
/** UNTRANSLATED */
organization_permissions: 'Organization access',
/** UNTRANSLATED */
table_name: 'Grant permissions',
/** UNTRANSLATED */
field_name: 'Permission',
/** UNTRANSLATED */
field_description: 'Displayed in the consent screen',
/** UNTRANSLATED */
delete_text: 'Remove permission',
/** UNTRANSLATED */
permission_delete_confirm:
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
},
roles: {
name_column: 'Rol',

View file

@ -90,6 +90,21 @@ const application_details = {
/** UNTRANSLATED */
description:
'Select the permissions that the third-party application requires for user authorization to access specific data types.',
/** UNTRANSLATED */
user_permissions: 'Personal user information',
/** UNTRANSLATED */
organization_permissions: 'Organization access',
/** UNTRANSLATED */
table_name: 'Grant permissions',
/** UNTRANSLATED */
field_name: 'Permission',
/** UNTRANSLATED */
field_description: 'Displayed in the consent screen',
/** UNTRANSLATED */
delete_text: 'Remove permission',
/** UNTRANSLATED */
permission_delete_confirm:
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
},
roles: {
name_column: '角色',

View file

@ -90,6 +90,21 @@ const application_details = {
/** UNTRANSLATED */
description:
'Select the permissions that the third-party application requires for user authorization to access specific data types.',
/** UNTRANSLATED */
user_permissions: 'Personal user information',
/** UNTRANSLATED */
organization_permissions: 'Organization access',
/** UNTRANSLATED */
table_name: 'Grant permissions',
/** UNTRANSLATED */
field_name: 'Permission',
/** UNTRANSLATED */
field_description: 'Displayed in the consent screen',
/** UNTRANSLATED */
delete_text: 'Remove permission',
/** UNTRANSLATED */
permission_delete_confirm:
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
},
roles: {
name_column: '角色',

View file

@ -91,6 +91,21 @@ const application_details = {
/** UNTRANSLATED */
description:
'Select the permissions that the third-party application requires for user authorization to access specific data types.',
/** UNTRANSLATED */
user_permissions: 'Personal user information',
/** UNTRANSLATED */
organization_permissions: 'Organization access',
/** UNTRANSLATED */
table_name: 'Grant permissions',
/** UNTRANSLATED */
field_name: 'Permission',
/** UNTRANSLATED */
field_description: 'Displayed in the consent screen',
/** UNTRANSLATED */
delete_text: 'Remove permission',
/** UNTRANSLATED */
permission_delete_confirm:
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
},
roles: {
name_column: '角色',