Skip to content

Commit

Permalink
chore(console,experience): remove dev flags add changeset for organiz…
Browse files Browse the repository at this point in the history
…ation updates (#5763)
  • Loading branch information
xiaoyijun authored May 13, 2024
1 parent f85e1b8 commit 6fe6f87
Show file tree
Hide file tree
Showing 71 changed files with 222 additions and 1,970 deletions.
12 changes: 12 additions & 0 deletions .changeset/loud-mice-divide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@logto/console": minor
---

support adding API resource permissions to organization roles and organization permissions in 3rd-party applications

## Updates

- Separated the "Organization template" from the "Organization" page, establishing it as a standalone page for clearer navigation and functionality.
- Enhanced the "Organization template" page by adding functionality that allows users to click on an organization role, which then navigates to the organization role details page where users can view its corresponding permissions and general settings.
- Enabled the assignment of API resource permissions directly from the organization role details page, improving role management and access control.
- Split the permission list for third-party apps into two separate lists: user permissions and organization permissions. Users can now add user profile permissions and API resource permissions for users under user permissions, and add organization permissions and API resource permissions for organizations under organization permissions.
34 changes: 0 additions & 34 deletions packages/console/src/components/OrganizationScopesSelect/index.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion packages/console/src/components/TemplateTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type Props<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValue
readonly errorMessage?: string;
};

export const pageSize = 10;
const pageSize = 10;

/**
* The table component for organization template editing, such as permissions and roles.
Expand Down
2 changes: 1 addition & 1 deletion packages/console/src/consts/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ import { yes } from '@silverhand/essentials';
const isProduction = process.env.NODE_ENV === 'production';
export const isCloud = yes(process.env.IS_CLOUD);
export const adminEndpoint = process.env.ADMIN_ENDPOINT;

// eslint-disable-next-line import/no-unused-modules
export const isDevFeaturesEnabled =
!isProduction || yes(process.env.DEV_FEATURES_ENABLED) || yes(process.env.INTEGRATION_TEST);
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import Role from '@/assets/icons/role.svg';
import SecurityLock from '@/assets/icons/security-lock.svg';
import EnterpriseSso from '@/assets/icons/single-sign-on.svg';
import Web from '@/assets/icons/web.svg';
import { isCloud, isDevFeaturesEnabled } from '@/consts/env';
import { isCloud } from '@/consts/env';

type SidebarItem = {
Icon: FC;
Expand Down Expand Up @@ -103,7 +103,6 @@ export const useSidebarMenuItems = (): {
{
Icon: OrganizationTemplate,
title: 'organization_template',
isHidden: !isDevFeaturesEnabled,
},
],
},
Expand Down
23 changes: 3 additions & 20 deletions packages/console/src/hooks/use-console-routes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import { condArray } from '@silverhand/essentials';
import { useMemo } from 'react';
import { Navigate, type RouteObject } from 'react-router-dom';
import { type RouteObject } from 'react-router-dom';

import { isCloud, isDevFeaturesEnabled } from '@/consts/env';
import { isCloud } from '@/consts/env';
import Dashboard from '@/pages/Dashboard';
import GetStarted from '@/pages/GetStarted';
import Mfa from '@/pages/Mfa';
import NotFound from '@/pages/NotFound';
import OrganizationGuide from '@/pages/Organizations/Guide';
import Introduction from '@/pages/Organizations/Guide/Introduction';
import OrganizationInfo from '@/pages/Organizations/Guide/OrganizationInfo';
import OrganizationPermissions from '@/pages/Organizations/Guide/OrganizationPermissions';
import OrganizationRoles from '@/pages/Organizations/Guide/OrganizationRoles';
import { steps } from '@/pages/Organizations/Guide/const';
import SigningKeys from '@/pages/SigningKeys';

import { apiResources } from './routes/api-resources';
Expand Down Expand Up @@ -48,19 +42,8 @@ export const useConsoleRoutes = () => {
users,
auditLogs,
roles,
isDevFeaturesEnabled && organizationTemplate,
organizationTemplate,
organizations,
!isDevFeaturesEnabled && {
path: 'organization-guide/*',
element: <OrganizationGuide />,
children: [
{ index: true, element: <Navigate replace to={steps.introduction} /> },
{ path: steps.introduction, element: <Introduction /> },
{ path: steps.permissions, element: <OrganizationPermissions /> },
{ path: steps.roles, element: <OrganizationRoles /> },
{ path: steps.organizationInfo, element: <OrganizationInfo /> },
],
},
{ path: 'signing-keys', element: <SigningKeys /> },
isCloud && tenantSettings,
customizeJwt
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { condArray } from '@silverhand/essentials';
import { Navigate, type RouteObject } from 'react-router-dom';

import { isDevFeaturesEnabled } from '@/consts/env';
import OrganizationDetails from '@/pages/OrganizationDetails';
import Members from '@/pages/OrganizationDetails/Members';
import Settings from '@/pages/OrganizationDetails/Settings';
Expand All @@ -13,10 +12,6 @@ export const organizations: RouteObject = {
children: condArray(
{ index: true, element: <Organizations /> },
{ path: 'create', element: <Organizations /> },
!isDevFeaturesEnabled && {
path: 'template',
element: <Organizations tab="template" />,
},
{
path: ':id/*',
element: <OrganizationDetails />,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,6 @@ import { ApplicationUserConsentScopeType } from '@logto/schemas';

import { type PermissionTabType } from './type';

export const allLevelPermissionTabs: PermissionTabType = Object.freeze({
[ApplicationUserConsentScopeType.UserScopes]: {
title: 'application_details.permissions.user_profile',
key: ApplicationUserConsentScopeType.UserScopes,
},
[ApplicationUserConsentScopeType.ResourceScopes]: {
title: 'application_details.permissions.api_permissions',
key: ApplicationUserConsentScopeType.ResourceScopes,
},
[ApplicationUserConsentScopeType.OrganizationScopes]: {
title: 'application_details.permissions.organization',
key: ApplicationUserConsentScopeType.OrganizationScopes,
},
});

export const userLevelPermissionsTabs: PermissionTabType = Object.freeze({
[ApplicationUserConsentScopeType.UserScopes]: {
title: 'application_details.permissions.user_profile',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@ import DataTransferBox from '@/ds-components/DataTransferBox';
import TabNav, { TabNavItem } from '@/ds-components/TabNav';
import TabWrapper from '@/ds-components/TabWrapper';

import {
allLevelPermissionTabs,
organizationLevelPermissionsTab,
userLevelPermissionsTabs,
} from './constants';
import { organizationLevelPermissionsTab, userLevelPermissionsTabs } from './constants';
import { ScopeLevel } from './type';
import useApplicationScopesAssignment from './use-application-scopes-assignment';

Expand Down Expand Up @@ -50,47 +46,33 @@ function ApplicationScopesAssignmentModal({ isOpen, onClose, applicationId, scop
[scopesAssignment]
);

const tabs = useMemo(() => {
const getPermissionTabs = () => {
if (scopeLevel === ScopeLevel.All) {
return allLevelPermissionTabs;
}

return scopeLevel === ScopeLevel.User
? userLevelPermissionsTabs
: organizationLevelPermissionsTab;
};

return Object.values(getPermissionTabs()).map(({ title, key }) => {
const selectedDataCount = scopesAssignment[key].selectedData.length;

return (
<TabNavItem
key={key}
isActive={key === activeTab}
onClick={() => {
setActiveTab(key);
}}
>
{`${String(t(title))}${selectedDataCount ? ` (${selectedDataCount})` : ''}`}
</TabNavItem>
);
});
}, [activeTab, scopeLevel, scopesAssignment, setActiveTab, t]);
const tabs = useMemo(
() =>
Object.values(
scopeLevel === ScopeLevel.User ? userLevelPermissionsTabs : organizationLevelPermissionsTab
).map(({ title, key }) => {
const selectedDataCount = scopesAssignment[key].selectedData.length;

return (
<TabNavItem
key={key}
isActive={key === activeTab}
onClick={() => {
setActiveTab(key);
}}
>
{`${String(t(title))}${selectedDataCount ? ` (${selectedDataCount})` : ''}`}
</TabNavItem>
);
}),
[activeTab, scopeLevel, scopesAssignment, setActiveTab, t]
);

const modalText = useMemo<{
title: AdminConsoleKey;
subtitle: AdminConsoleKey;
saveButton: AdminConsoleKey;
}>(() => {
if (scopeLevel === ScopeLevel.All) {
return {
title: 'application_details.permissions.table_name',
subtitle: 'application_details.permissions.permissions_assignment_description',
saveButton: 'general.save',
};
}

const scopeLevelPhrase = scopeLevel === ScopeLevel.User ? 'user' : 'organization';

return {
Expand All @@ -115,7 +97,7 @@ function ApplicationScopesAssignmentModal({ isOpen, onClose, applicationId, scop
onConfirm={onSubmitHandler}
>
<TabNav>{tabs}</TabNav>
{(scopeLevel === ScopeLevel.All || scopeLevel === ScopeLevel.User) && (
{scopeLevel === ScopeLevel.User && (
<>
<TabWrapper
key={ApplicationUserConsentScopeType.UserScopes}
Expand All @@ -133,25 +115,25 @@ function ApplicationScopesAssignmentModal({ isOpen, onClose, applicationId, scop
</TabWrapper>
</>
)}
{(scopeLevel === ScopeLevel.All || scopeLevel === ScopeLevel.Organization) && (
<TabWrapper
key={ApplicationUserConsentScopeType.OrganizationScopes}
isActive={ApplicationUserConsentScopeType.OrganizationScopes === activeTab}
>
<DataTransferBox
{...scopesAssignment[ApplicationUserConsentScopeType.OrganizationScopes]}
/>
</TabWrapper>
)}
{scopeLevel === ScopeLevel.Organization && (
<TabWrapper
key={ApplicationUserConsentScopeType.OrganizationResourceScopes}
isActive={ApplicationUserConsentScopeType.OrganizationResourceScopes === activeTab}
>
<DataTransferBox
{...scopesAssignment[ApplicationUserConsentScopeType.OrganizationResourceScopes]}
/>
</TabWrapper>
<>
<TabWrapper
key={ApplicationUserConsentScopeType.OrganizationScopes}
isActive={ApplicationUserConsentScopeType.OrganizationScopes === activeTab}
>
<DataTransferBox
{...scopesAssignment[ApplicationUserConsentScopeType.OrganizationScopes]}
/>
</TabWrapper>
<TabWrapper
key={ApplicationUserConsentScopeType.OrganizationResourceScopes}
isActive={ApplicationUserConsentScopeType.OrganizationResourceScopes === activeTab}
>
<DataTransferBox
{...scopesAssignment[ApplicationUserConsentScopeType.OrganizationResourceScopes]}
/>
</TabWrapper>
</>
)}
</ConfirmModal>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,6 @@ export type ScopeAssignmentHook<
export enum ScopeLevel {
User = 'user',
Organization = 'organization',
/**
* Only used when the new organization resource scope feature is not ready.
* Todo @xiaoyijun remove this when the new organization resource scope feature is ready.
*/
All = 'all',
}

export type PermissionTabType = Partial<{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,31 +47,13 @@ function PermissionsCard({ applicationId, scopeLevel }: Props) {
const rowGroups = useMemo(() => {
const { userLevelRowGroups, organizationLevelGroups } = parseRowGroup(data);

if (scopeLevel === ScopeLevel.All) {
return [...userLevelRowGroups, ...organizationLevelGroups];
}

return scopeLevel === ScopeLevel.User ? userLevelRowGroups : organizationLevelGroups;
}, [data, parseRowGroup, scopeLevel]);

const displayTextProps = useMemo<{
formCard: Omit<FormCardProps, 'children'>;
tableName: AdminConsoleKey;
}>(() => {
if (scopeLevel === ScopeLevel.All) {
return {
formCard: {
title: 'application_details.permissions.name',
description: 'application_details.permissions.description',
learnMoreLink: {
href: getDocumentationUrl(logtoThirdPartyAppPermissionsLink),
targetBlank: 'noopener',
},
},
tableName: 'application_details.permissions.table_name',
};
}

const scopeLevelPhrase = scopeLevel === ScopeLevel.User ? 'user' : 'organization';

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ import {
type ApplicationUserConsentScopesResponse,
ApplicationUserConsentScopeType,
} from '@logto/schemas';
import { condArray } from '@silverhand/essentials';
import { useCallback, type ReactNode } from 'react';
import { useTranslation } from 'react-i18next';

import Tip from '@/assets/icons/tip.svg';
import { isDevFeaturesEnabled } from '@/consts/env';
import IconButton from '@/ds-components/IconButton';
import { ToggleTip } from '@/ds-components/Tip';
import useApi from '@/hooks/use-api';
Expand Down Expand Up @@ -143,14 +141,7 @@ const useScopesTable = () => {
organizationLevelGroups: [
// Hide the organization scopes group if there is no organization scopes
...(organizationScopesGroup.data.length > 0 ? [organizationScopesGroup] : []),
...condArray(
/**
* Hide the organization resource scopes group if the organization resource scopes feature is not ready
*/
isDevFeaturesEnabled &&
organizationResourceScopesGroup.length > 0 &&
organizationResourceScopesGroup
),
...(organizationResourceScopesGroup.length > 0 ? organizationResourceScopesGroup : []),
],
};
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { type Application } from '@logto/schemas';

import { isDevFeaturesEnabled } from '@/consts/env';

import PermissionsCard from './PermissionsCard';
import { ScopeLevel } from './PermissionsCard/ApplicationScopesAssignmentModal/type';
import * as styles from './index.module.scss';
Expand All @@ -11,13 +9,9 @@ type Props = {
};

function Permissions({ application }: Props) {
const displayScopeLevels = isDevFeaturesEnabled
? [ScopeLevel.User, ScopeLevel.Organization]
: [ScopeLevel.All];

return (
<div className={styles.container}>
{displayScopeLevels.map((scopeLevel) => (
{[ScopeLevel.User, ScopeLevel.Organization].map((scopeLevel) => (
<PermissionsCard key={scopeLevel} applicationId={application.id} scopeLevel={scopeLevel} />
))}
</div>
Expand Down
Loading

0 comments on commit 6fe6f87

Please sign in to comment.