From 0f5347bbb991b3def69d85f93f85429e917e2288 Mon Sep 17 00:00:00 2001 From: Xiao Yijun Date: Tue, 2 Apr 2024 21:10:44 +0800 Subject: [PATCH] feat(console): add org role details general settings page (#5610) --- packages/console/src/consts/page-tabs.ts | 5 + .../src/containers/ConsoleContent/index.tsx | 31 +++-- .../Permissions/index.tsx | 9 ++ .../Settings/index.tsx | 78 +++++++++++ .../pages/OrganizationRoleDetails/index.tsx | 126 ++++++++++++++++++ .../OrganizationRoles/index.tsx | 10 +- .../de/translation/admin-console/index.ts | 2 + .../organization-role-details.ts | 30 +++++ .../en/translation/admin-console/index.ts | 2 + .../organization-role-details.ts | 30 +++++ .../es/translation/admin-console/index.ts | 2 + .../organization-role-details.ts | 30 +++++ .../fr/translation/admin-console/index.ts | 2 + .../organization-role-details.ts | 30 +++++ .../it/translation/admin-console/index.ts | 2 + .../organization-role-details.ts | 30 +++++ .../ja/translation/admin-console/index.ts | 2 + .../organization-role-details.ts | 30 +++++ .../ko/translation/admin-console/index.ts | 2 + .../organization-role-details.ts | 30 +++++ .../pl-pl/translation/admin-console/index.ts | 2 + .../organization-role-details.ts | 30 +++++ .../pt-br/translation/admin-console/index.ts | 2 + .../organization-role-details.ts | 30 +++++ .../pt-pt/translation/admin-console/index.ts | 2 + .../organization-role-details.ts | 30 +++++ .../ru/translation/admin-console/index.ts | 2 + .../organization-role-details.ts | 30 +++++ .../tr-tr/translation/admin-console/index.ts | 2 + .../organization-role-details.ts | 30 +++++ .../zh-cn/translation/admin-console/index.ts | 2 + .../organization-role-details.ts | 29 ++++ .../zh-hk/translation/admin-console/index.ts | 2 + .../organization-role-details.ts | 29 ++++ .../zh-tw/translation/admin-console/index.ts | 2 + .../organization-role-details.ts | 29 ++++ 36 files changed, 721 insertions(+), 15 deletions(-) create mode 100644 packages/console/src/pages/OrganizationRoleDetails/Permissions/index.tsx create mode 100644 packages/console/src/pages/OrganizationRoleDetails/Settings/index.tsx create mode 100644 packages/console/src/pages/OrganizationRoleDetails/index.tsx create mode 100644 packages/phrases/src/locales/de/translation/admin-console/organization-role-details.ts create mode 100644 packages/phrases/src/locales/en/translation/admin-console/organization-role-details.ts create mode 100644 packages/phrases/src/locales/es/translation/admin-console/organization-role-details.ts create mode 100644 packages/phrases/src/locales/fr/translation/admin-console/organization-role-details.ts create mode 100644 packages/phrases/src/locales/it/translation/admin-console/organization-role-details.ts create mode 100644 packages/phrases/src/locales/ja/translation/admin-console/organization-role-details.ts create mode 100644 packages/phrases/src/locales/ko/translation/admin-console/organization-role-details.ts create mode 100644 packages/phrases/src/locales/pl-pl/translation/admin-console/organization-role-details.ts create mode 100644 packages/phrases/src/locales/pt-br/translation/admin-console/organization-role-details.ts create mode 100644 packages/phrases/src/locales/pt-pt/translation/admin-console/organization-role-details.ts create mode 100644 packages/phrases/src/locales/ru/translation/admin-console/organization-role-details.ts create mode 100644 packages/phrases/src/locales/tr-tr/translation/admin-console/organization-role-details.ts create mode 100644 packages/phrases/src/locales/zh-cn/translation/admin-console/organization-role-details.ts create mode 100644 packages/phrases/src/locales/zh-hk/translation/admin-console/organization-role-details.ts create mode 100644 packages/phrases/src/locales/zh-tw/translation/admin-console/organization-role-details.ts diff --git a/packages/console/src/consts/page-tabs.ts b/packages/console/src/consts/page-tabs.ts index e6d593026..890f776c4 100644 --- a/packages/console/src/consts/page-tabs.ts +++ b/packages/console/src/consts/page-tabs.ts @@ -52,3 +52,8 @@ export enum OrganizationTemplateTabs { OrganizationRoles = 'organization-roles', OrganizationPermissions = 'organization-permissions', } + +export enum OrganizationRoleDetailsTabs { + Permissions = 'permissions', + General = 'general', +} diff --git a/packages/console/src/containers/ConsoleContent/index.tsx b/packages/console/src/containers/ConsoleContent/index.tsx index 0c05bc80e..39f1f84c8 100644 --- a/packages/console/src/containers/ConsoleContent/index.tsx +++ b/packages/console/src/containers/ConsoleContent/index.tsx @@ -35,6 +35,7 @@ import GetStarted from '@/pages/GetStarted'; import Mfa from '@/pages/Mfa'; import NotFound from '@/pages/NotFound'; import OrganizationDetails from '@/pages/OrganizationDetails'; +import OrganizationRoleDetails from '@/pages/OrganizationRoleDetails'; import OrganizationTemplate from '@/pages/OrganizationTemplate'; import OrganizationPermissions from '@/pages/OrganizationTemplate/OrganizationPermissions'; import OrganizationRoles from '@/pages/OrganizationTemplate/OrganizationRoles'; @@ -187,20 +188,26 @@ function ConsoleContent() { {isDevFeaturesEnabled && ( - }> + <> + }> + } + /> + } + /> + } + /> + } + path={`organization-template/${OrganizationTemplateTabs.OrganizationRoles}/:id/*`} + element={} /> - } - /> - } - /> - + )} } /> diff --git a/packages/console/src/pages/OrganizationRoleDetails/Permissions/index.tsx b/packages/console/src/pages/OrganizationRoleDetails/Permissions/index.tsx new file mode 100644 index 000000000..1824f1775 --- /dev/null +++ b/packages/console/src/pages/OrganizationRoleDetails/Permissions/index.tsx @@ -0,0 +1,9 @@ +type Props = { + organizationRoleId: string; +}; + +function Permissions({ organizationRoleId }: Props) { + return
TBD
; +} + +export default Permissions; diff --git a/packages/console/src/pages/OrganizationRoleDetails/Settings/index.tsx b/packages/console/src/pages/OrganizationRoleDetails/Settings/index.tsx new file mode 100644 index 000000000..70615c58f --- /dev/null +++ b/packages/console/src/pages/OrganizationRoleDetails/Settings/index.tsx @@ -0,0 +1,78 @@ +import { type OrganizationRole } 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 UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal'; +import { organizationRoleLink } from '@/consts'; +import FormField from '@/ds-components/FormField'; +import TextInput from '@/ds-components/TextInput'; +import useApi from '@/hooks/use-api'; +import useDocumentationUrl from '@/hooks/use-documentation-url'; +import { trySubmitSafe } from '@/utils/form'; + +type Props = { + data: OrganizationRole; + onUpdate: (updatedData: OrganizationRole) => void; +}; + +function Settings({ data, onUpdate }: Props) { + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + const { getDocumentationUrl } = useDocumentationUrl(); + const { + register, + handleSubmit, + reset, + formState: { errors, isDirty, isSubmitting }, + } = useForm({ defaultValues: data }); + + const api = useApi(); + + const onSubmit = handleSubmit( + trySubmitSafe(async (formData) => { + const updatedData = await api + .patch(`api/organization-roles/${data.id}`, { json: formData }) + .json(); + reset(updatedData); + onUpdate(updatedData); + toast.success(t('general.saved')); + }) + ); + + return ( + + + + + + + + + + + + ); +} + +export default Settings; diff --git a/packages/console/src/pages/OrganizationRoleDetails/index.tsx b/packages/console/src/pages/OrganizationRoleDetails/index.tsx new file mode 100644 index 000000000..9abcfcfdd --- /dev/null +++ b/packages/console/src/pages/OrganizationRoleDetails/index.tsx @@ -0,0 +1,126 @@ +import { withAppInsights } from '@logto/app-insights/react/AppInsightsReact'; +import { type OrganizationRole } from '@logto/schemas'; +import { useState } from 'react'; +import { toast } from 'react-hot-toast'; +import { useTranslation } from 'react-i18next'; +import { Navigate, Route, Routes, useParams } from 'react-router-dom'; +import useSWR, { useSWRConfig } from 'swr'; + +import Delete from '@/assets/icons/delete.svg'; +import OrgRoleIcon from '@/assets/icons/role-feature.svg'; +import DetailsPage from '@/components/DetailsPage'; +import DetailsPageHeader from '@/components/DetailsPage/DetailsPageHeader'; +import PageMeta from '@/components/PageMeta'; +import ThemedIcon from '@/components/ThemedIcon'; +import { OrganizationRoleDetailsTabs, OrganizationTemplateTabs } from '@/consts'; +import ConfirmModal from '@/ds-components/ConfirmModal'; +import DynamicT from '@/ds-components/DynamicT'; +import TabNav, { TabNavItem } from '@/ds-components/TabNav'; +import useApi, { type RequestError } from '@/hooks/use-api'; +import useTenantPathname from '@/hooks/use-tenant-pathname'; + +import Permissions from './Permissions'; +import Settings from './Settings'; + +const orgRolesPath = `/organization-template/${OrganizationTemplateTabs.OrganizationRoles}`; + +function OrganizationRoleDetails() { + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + + const { id } = useParams(); + const { navigate } = useTenantPathname(); + + const { data, error, mutate, isLoading } = useSWR( + id && `api/organization-roles/${id}` + ); + const api = useApi(); + const { mutate: mutateGlobal } = useSWRConfig(); + const [isDeletionAlertOpen, setIsDeletionAlertOpen] = useState(false); + const [isDeleting, setIsDeleting] = useState(false); + + const handleDelete = async () => { + if (!data) { + return; + } + + setIsDeleting(true); + + try { + await api.delete(`api/organization-roles/${data.id}`); + toast.success(t('organization_role_details.deleted', { name: data.name })); + await mutateGlobal('api/roles'); + navigate(orgRolesPath, { replace: true }); + } finally { + setIsDeleting(false); + } + }; + + return ( + + + {data && ( + <> + } + title={data.name} + primaryTag={t('organization_role_details.org_role')} + identifier={{ name: 'ID', value: data.id }} + actionMenuItems={[ + { + title: 'general.delete', + icon: , + type: 'danger', + onClick: () => { + setIsDeletionAlertOpen(true); + }, + }, + ]} + /> + { + setIsDeletionAlertOpen(false); + }} + onConfirm={handleDelete} + > + + + + + + + + + + + + } + /> + } + /> + } + /> + + + )} + + ); +} + +export default withAppInsights(OrganizationRoleDetails); diff --git a/packages/console/src/pages/OrganizationTemplate/OrganizationRoles/index.tsx b/packages/console/src/pages/OrganizationTemplate/OrganizationRoles/index.tsx index 46540c604..1cb6360c9 100644 --- a/packages/console/src/pages/OrganizationTemplate/OrganizationRoles/index.tsx +++ b/packages/console/src/pages/OrganizationTemplate/OrganizationRoles/index.tsx @@ -17,13 +17,14 @@ import Tag from '@/ds-components/Tag'; import { type RequestError } from '@/hooks/use-api'; import useDocumentationUrl from '@/hooks/use-documentation-url'; import useSearchParametersWatcher from '@/hooks/use-search-parameters-watcher'; +import useTenantPathname from '@/hooks/use-tenant-pathname'; import { buildUrl } from '@/utils/url'; import * as styles from './index.module.scss'; function OrganizationRoles() { const { getDocumentationUrl } = useDocumentationUrl(); - + const { navigate } = useTenantPathname(); const [{ page }, updateSearchParameters] = useSearchParametersWatcher({ page: 1, }); @@ -49,8 +50,8 @@ function OrganizationRoles() { title: , dataIndex: 'name', colSpan: 4, - render: ({ name }) => { - return } />; + render: ({ id, name }) => { + return } to={id} />; }, }, { @@ -72,6 +73,9 @@ function OrganizationRoles() { }, }, ]} + rowClickHandler={({ id }) => { + navigate(id); + }} filter={