mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
refactor(console): reorg organization details routes (#5702)
This commit is contained in:
parent
336aa6f49a
commit
077cd85a06
5 changed files with 52 additions and 43 deletions
|
@ -1,8 +1,11 @@
|
||||||
import { condArray } from '@silverhand/essentials';
|
import { condArray } from '@silverhand/essentials';
|
||||||
import { type RouteObject } from 'react-router-dom';
|
import { Navigate, type RouteObject } from 'react-router-dom';
|
||||||
|
|
||||||
import { isDevFeaturesEnabled } from '@/consts/env';
|
import { isDevFeaturesEnabled } from '@/consts/env';
|
||||||
import OrganizationDetails from '@/pages/OrganizationDetails';
|
import OrganizationDetails from '@/pages/OrganizationDetails';
|
||||||
|
import Members from '@/pages/OrganizationDetails/Members';
|
||||||
|
import Settings from '@/pages/OrganizationDetails/Settings';
|
||||||
|
import { OrganizationDetailsTabs } from '@/pages/OrganizationDetails/types';
|
||||||
import Organizations from '@/pages/Organizations';
|
import Organizations from '@/pages/Organizations';
|
||||||
|
|
||||||
export const organizations: RouteObject = {
|
export const organizations: RouteObject = {
|
||||||
|
@ -14,6 +17,14 @@ export const organizations: RouteObject = {
|
||||||
path: 'template',
|
path: 'template',
|
||||||
element: <Organizations tab="template" />,
|
element: <Organizations tab="template" />,
|
||||||
},
|
},
|
||||||
{ path: ':id/*', element: <OrganizationDetails /> }
|
{
|
||||||
|
path: ':id/*',
|
||||||
|
element: <OrganizationDetails />,
|
||||||
|
children: [
|
||||||
|
{ index: true, element: <Navigate replace to={OrganizationDetailsTabs.Settings} /> },
|
||||||
|
{ path: OrganizationDetailsTabs.Settings, element: <Settings /> },
|
||||||
|
{ path: OrganizationDetailsTabs.Members, element: <Members /> },
|
||||||
|
],
|
||||||
|
}
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { type UserWithOrganizationRoles, type Organization } from '@logto/schemas';
|
import { type UserWithOrganizationRoles } from '@logto/schemas';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useOutletContext } from 'react-router-dom';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
|
||||||
import Plus from '@/assets/icons/plus.svg';
|
import Plus from '@/assets/icons/plus.svg';
|
||||||
|
@ -19,17 +20,16 @@ import useActionTranslation from '@/hooks/use-action-translation';
|
||||||
import useApi, { type RequestError } from '@/hooks/use-api';
|
import useApi, { type RequestError } from '@/hooks/use-api';
|
||||||
import { buildUrl } from '@/utils/url';
|
import { buildUrl } from '@/utils/url';
|
||||||
|
|
||||||
|
import { type OrganizationDetailsOutletContext } from '../types';
|
||||||
|
|
||||||
import AddMembersToOrganization from './AddMembersToOrganization';
|
import AddMembersToOrganization from './AddMembersToOrganization';
|
||||||
import EditOrganizationRolesModal from './EditOrganizationRolesModal';
|
import EditOrganizationRolesModal from './EditOrganizationRolesModal';
|
||||||
import * as styles from './index.module.scss';
|
import * as styles from './index.module.scss';
|
||||||
|
|
||||||
const pageSize = defaultPageSize;
|
const pageSize = defaultPageSize;
|
||||||
|
|
||||||
type Props = {
|
function Members() {
|
||||||
organization: Organization;
|
const { data: organization } = useOutletContext<OrganizationDetailsOutletContext>();
|
||||||
};
|
|
||||||
|
|
||||||
function Members({ organization }: Props) {
|
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
const [keyword, setKeyword] = useState('');
|
const [keyword, setKeyword] = useState('');
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { type Organization } from '@logto/schemas';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { toast } from 'react-hot-toast';
|
import { toast } from 'react-hot-toast';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useOutletContext } from 'react-router-dom';
|
||||||
|
|
||||||
import DetailsForm from '@/components/DetailsForm';
|
import DetailsForm from '@/components/DetailsForm';
|
||||||
import FormCard from '@/components/FormCard';
|
import FormCard from '@/components/FormCard';
|
||||||
|
@ -11,17 +12,10 @@ import TextInput from '@/ds-components/TextInput';
|
||||||
import useApi from '@/hooks/use-api';
|
import useApi from '@/hooks/use-api';
|
||||||
import { trySubmitSafe } from '@/utils/form';
|
import { trySubmitSafe } from '@/utils/form';
|
||||||
|
|
||||||
type Props = {
|
import { type OrganizationDetailsOutletContext } from '../types';
|
||||||
/**
|
|
||||||
* Whether the organization is being deleted, this is used to disable the unsaved
|
|
||||||
* changes alert modal.
|
|
||||||
*/
|
|
||||||
isDeleting: boolean;
|
|
||||||
data: Organization;
|
|
||||||
onUpdated: (data: Organization) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
function Settings({ isDeleting, data, onUpdated }: Props) {
|
function Settings() {
|
||||||
|
const { isDeleting, data, onUpdated } = useOutletContext<OrganizationDetailsOutletContext>();
|
||||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import { type Organization } from '@logto/schemas';
|
import { type Organization } from '@logto/schemas';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
// FIXME: @gao
|
import { Outlet, useParams } from 'react-router-dom';
|
||||||
// eslint-disable-next-line no-restricted-imports
|
|
||||||
import { Navigate, Route, Routes, useParams } from 'react-router-dom';
|
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
|
||||||
import Delete from '@/assets/icons/delete.svg';
|
import Delete from '@/assets/icons/delete.svg';
|
||||||
|
@ -23,15 +21,10 @@ import useTenantPathname from '@/hooks/use-tenant-pathname';
|
||||||
|
|
||||||
import Introduction from '../Organizations/Guide/Introduction';
|
import Introduction from '../Organizations/Guide/Introduction';
|
||||||
|
|
||||||
import Members from './Members';
|
|
||||||
import Settings from './Settings';
|
|
||||||
import * as styles from './index.module.scss';
|
import * as styles from './index.module.scss';
|
||||||
|
import { OrganizationDetailsTabs, type OrganizationDetailsOutletContext } from './types';
|
||||||
|
|
||||||
const pathname = '/organizations';
|
const pathname = '/organizations';
|
||||||
const tabs = Object.freeze({
|
|
||||||
settings: 'settings',
|
|
||||||
members: 'members',
|
|
||||||
});
|
|
||||||
|
|
||||||
function OrganizationDetails() {
|
function OrganizationDetails() {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
@ -111,27 +104,22 @@ function OrganizationDetails() {
|
||||||
{t('organization_details.delete_confirmation')}
|
{t('organization_details.delete_confirmation')}
|
||||||
</DeleteConfirmModal>
|
</DeleteConfirmModal>
|
||||||
<TabNav>
|
<TabNav>
|
||||||
<TabNavItem href={`${pathname}/${data.id}/${tabs.settings}`}>
|
<TabNavItem href={`${pathname}/${data.id}/${OrganizationDetailsTabs.Settings}`}>
|
||||||
{t('general.settings_nav')}
|
{t('general.settings_nav')}
|
||||||
</TabNavItem>
|
</TabNavItem>
|
||||||
<TabNavItem href={`${pathname}/${data.id}/${tabs.members}`}>
|
<TabNavItem href={`${pathname}/${data.id}/${OrganizationDetailsTabs.Members}`}>
|
||||||
{t('organizations.members')}
|
{t('organizations.members')}
|
||||||
</TabNavItem>
|
</TabNavItem>
|
||||||
</TabNav>
|
</TabNav>
|
||||||
<Routes>
|
<Outlet
|
||||||
<Route index element={<Navigate replace to={tabs.settings} />} />
|
context={
|
||||||
<Route
|
{
|
||||||
path={tabs.settings}
|
data,
|
||||||
element={
|
isDeleting,
|
||||||
<Settings
|
onUpdated: async (data) => mutate(data),
|
||||||
isDeleting={isDeleting}
|
} satisfies OrganizationDetailsOutletContext
|
||||||
data={data}
|
}
|
||||||
onUpdated={async (data) => mutate(data)}
|
/>
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route path={tabs.members} element={<Members organization={data} />} />
|
|
||||||
</Routes>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</DetailsPage>
|
</DetailsPage>
|
||||||
|
|
16
packages/console/src/pages/OrganizationDetails/types.ts
Normal file
16
packages/console/src/pages/OrganizationDetails/types.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { type Organization } from '@logto/schemas';
|
||||||
|
|
||||||
|
export type OrganizationDetailsOutletContext = {
|
||||||
|
data: Organization;
|
||||||
|
/**
|
||||||
|
* Whether the organization is being deleted, this is used to disable the unsaved
|
||||||
|
* changes alert modal.
|
||||||
|
*/
|
||||||
|
isDeleting: boolean;
|
||||||
|
onUpdated: (data: Organization) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum OrganizationDetailsTabs {
|
||||||
|
Settings = 'settings',
|
||||||
|
Members = 'members',
|
||||||
|
}
|
Loading…
Reference in a new issue