Skip to content

Commit

Permalink
refactor: filter SAML apps from API and update types in console
Browse files Browse the repository at this point in the history
  • Loading branch information
darcyYe committed Nov 18, 2024
1 parent f604112 commit d5c8533
Show file tree
Hide file tree
Showing 33 changed files with 148 additions and 80 deletions.
5 changes: 3 additions & 2 deletions packages/console/src/assets/docs/guides/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { type ApplicationType } from '@logto/schemas';
import { type MDXProps } from 'mdx/types';
import { type LazyExoticComponent, type ComponentType, type SVGProps } from 'react';

import { type ApplicationTypeWithoutSaml } from '@/types/applications';

/**
* The guide metadata type. The directory name that the metadata is in will be the
* unique identifier of the guide.
Expand All @@ -17,7 +18,7 @@ export type GuideMetadata = {
* For example, if the guide is for application creation, the target should be `ApplicationType`,
* and an application of the target type should be created.
*/
target: ApplicationType | 'API';
target: ApplicationTypeWithoutSaml | 'API';
/** The related sample information of the guide. */
sample?: {
/** The GitHub repository of the `logto-io` organization that the sample is in. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ import Button from '@/ds-components/Button';
import TextLink from '@/ds-components/TextLink';
import useApplicationsUsage from '@/hooks/use-applications-usage';
import useUserPreferences from '@/hooks/use-user-preferences';
import { type ApplicationTypeWithoutSaml } from '@/types/applications';
import { isPaidPlan } from '@/utils/subscription';

import styles from './index.module.scss';

type Props = {
readonly selectedType?: ApplicationType;
readonly selectedType?: ApplicationTypeWithoutSaml;
readonly isLoading: boolean;
readonly isThirdParty?: boolean;
readonly onClickCreate: () => void;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { type AdminConsoleKey } from '@logto/phrases';
import type { Application } from '@logto/schemas';
import { ApplicationType, ReservedPlanId } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
import { type ReactElement, useContext, useMemo } from 'react';
Expand All @@ -21,23 +20,27 @@ import useApplicationsUsage from '@/hooks/use-applications-usage';
import useCurrentUser from '@/hooks/use-current-user';
import TypeDescription from '@/pages/Applications/components/TypeDescription';
import modalStyles from '@/scss/modal.module.scss';
import { applicationTypeI18nKey } from '@/types/applications';
import {
applicationTypeI18nKey,
type ApplicationTypeWithoutSaml,
type Application,
} from '@/types/applications';
import { trySubmitSafe } from '@/utils/form';
import { isPaidPlan } from '@/utils/subscription';

import Footer from './Footer';
import styles from './index.module.scss';

type FormData = {
type: ApplicationType;
type: ApplicationTypeWithoutSaml;
name: string;
description?: string;
isThirdParty?: boolean;
};

export type Props = {
readonly isDefaultCreateThirdParty?: boolean;
readonly defaultCreateType?: ApplicationType;
readonly defaultCreateType?: ApplicationTypeWithoutSaml;
readonly defaultCreateFrameworkName?: string;
readonly onClose?: (createdApp?: Application) => void;
};
Expand Down Expand Up @@ -161,7 +164,7 @@ function CreateForm({
>
{Object.values(ApplicationType)
// Other application types (e.g. "Protected") should not show up in the creation modal
.filter((value): value is Exclude<ApplicationType, ApplicationType.SAML> =>
.filter((value): value is ApplicationTypeWithoutSaml =>
[
ApplicationType.Native,
ApplicationType.SPA,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { ApplicationType, RoleType, type Application } from '@logto/schemas';
import { ApplicationType, RoleType } from '@logto/schemas';
import { useCallback, useState } from 'react';

import RoleAssignmentModal from '@/components/RoleAssignmentModal';
import { type Application } from '@/types/applications';

import CreateForm, { type Props as CreateApplicationFormProps } from './CreateForm';

Expand Down
14 changes: 9 additions & 5 deletions packages/console/src/components/ApplicationIcon/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ApplicationType, Theme } from '@logto/schemas';
import { Theme } from '@logto/schemas';

import {
darkModeApplicationIconMap,
Expand All @@ -7,16 +7,20 @@ import {
thirdPartyApplicationIconDark,
} from '@/consts';
import useTheme from '@/hooks/use-theme';
import { type ApplicationTypeWithoutSaml } from '@/types/applications';

type Props = {
readonly type: ApplicationType;
readonly type: ApplicationTypeWithoutSaml;
readonly className?: string;
readonly isThirdParty?: boolean;
};

const getIcon = (type: ApplicationType, isLightMode: boolean, isThirdParty?: boolean) => {
// We have ensured that SAML applications are always third party in DB schema, we use `??` here to make TypeScript happy.
if (isThirdParty ?? type === ApplicationType.SAML) {
const getIcon = (
type: ApplicationTypeWithoutSaml,
isLightMode: boolean,
isThirdParty?: boolean
) => {
if (isThirdParty) {
return isLightMode ? thirdPartyApplicationIcon : thirdPartyApplicationIconDark;
}

Expand Down
3 changes: 2 additions & 1 deletion packages/console/src/components/AuditLogTable/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Log, ApplicationResponse } from '@logto/schemas';
import type { Log } from '@logto/schemas';
import { LogResult, ApplicationType } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
import { useTranslation } from 'react-i18next';
Expand All @@ -12,6 +12,7 @@ import type { Column } from '@/ds-components/Table/types';
import type { RequestError } from '@/hooks/use-api';
import useSearchParametersWatcher from '@/hooks/use-search-parameters-watcher';
import useTenantPathname from '@/hooks/use-tenant-pathname';
import { type ApplicationResponse } from '@/types/applications';
import { buildUrl } from '@/utils/url';

import EmptyDataPlaceholder from '../EmptyDataPlaceholder';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { type Application, type User } from '@logto/schemas';
import { type User } from '@logto/schemas';

import ApplicationIcon from '@/components/ApplicationIcon';
import UserAvatar from '@/components/UserAvatar';
import SuspendedTag from '@/pages/Users/components/SuspendedTag';
import { type Application } from '@/types/applications';
import { getUserTitle } from '@/utils/user';

import styles from './index.module.scss';
Expand Down
4 changes: 1 addition & 3 deletions packages/console/src/components/Guide/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ApplicationType } from '@logto/schemas';
import { useCallback, useMemo } from 'react';

import { guides } from '@/assets/docs/guides';
Expand Down Expand Up @@ -99,8 +98,7 @@ export const useAppGuideMetadata = (): {
return accumulated;
}

// We have ensured that SAML applications are always third party in DB schema, we use `||` here to make TypeScript happy.
if (target === ApplicationType.SAML || isThirdParty) {
if (isThirdParty) {
return {
...accumulated,
[thirdPartyAppCategory]: [...accumulated[thirdPartyAppCategory], guide],
Expand Down
2 changes: 1 addition & 1 deletion packages/console/src/components/Guide/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { type ApplicationResponse } from '@logto/schemas';
import classNames from 'classnames';
import {
type ComponentType,
Expand All @@ -16,6 +15,7 @@ import OverlayScrollbar from '@/ds-components/OverlayScrollbar';
import MdxProvider from '@/mdx-components/MdxProvider';
import { type ApplicationSecretRow } from '@/pages/ApplicationDetails/ApplicationDetailsContent/EndpointsAndCredentials';
import NotFound from '@/pages/NotFound';
import type { ApplicationResponse } from '@/types/applications';

import StepsSkeleton from './StepsSkeleton';
import styles from './index.module.scss';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type Application, ApplicationType } from '@logto/schemas';
import { useTranslation } from 'react-i18next';

import ApplicationIcon from '@/components/ApplicationIcon';
import { type Application } from '@/types/applications';
import { applicationTypeI18nKey } from '@/types/applications';

import ItemPreview from '.';
Expand All @@ -21,8 +21,7 @@ function ApplicationPreview({ data: { id, name, isThirdParty, type } }: Props) {
<ItemPreview
title={name}
subtitle={
// We have ensured that SAML applications are always third party in DB schema, we use `||` here to make TypeScript happy.
isThirdParty || type === ApplicationType.SAML
isThirdParty
? t(`${applicationTypeI18nKey.thirdParty}.title`)
: t(`${applicationTypeI18nKey[type]}.title`)
}
Expand Down
4 changes: 2 additions & 2 deletions packages/console/src/consts/applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import SinglePageAppDark from '@/assets/icons/single-page-app-dark.svg?react';
import SinglePageApp from '@/assets/icons/single-page-app.svg?react';
import TraditionalWebAppDark from '@/assets/icons/traditional-web-app-dark.svg?react';
import TraditionalWebApp from '@/assets/icons/traditional-web-app.svg?react';
import { type ApplicationTypeWithoutSaml } from '@/types/applications';

type ApplicationIconMap = {
// TODO: Add SAML icon when we support SAML application in console
[key in Exclude<ApplicationType, ApplicationType.SAML>]: SvgComponent;
[key in ApplicationTypeWithoutSaml]: SvgComponent;
};

export const lightModeApplicationIconMap: ApplicationIconMap = Object.freeze({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ApplicationType, type ApplicationResponse } from '@logto/schemas';
import { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';

Expand All @@ -9,6 +8,7 @@ import GuideCardGroup from '@/components/Guide/GuideCardGroup';
import { useAppGuideMetadata } from '@/components/Guide/hooks';
import IconButton from '@/ds-components/IconButton';
import Spacer from '@/ds-components/Spacer';
import { type ApplicationResponse } from '@/types/applications';

import AppGuide from '../../components/AppGuide';
import { type ApplicationSecretRow } from '../EndpointsAndCredentials';
Expand All @@ -26,30 +26,24 @@ function GuideDrawer({ app, secrets, onClose }: Props) {
const { getStructuredAppGuideMetadata } = useAppGuideMetadata();
const [selectedGuide, setSelectedGuide] = useState<SelectedGuide>();

const appType = useMemo(
// SAML application is actually a Traditional application, the same as OIDC applications.
() => (app.type === ApplicationType.SAML ? ApplicationType.Traditional : app.type),
[app.type]
);

const structuredMetadata = useMemo(
() => getStructuredAppGuideMetadata({ categories: [appType] }),
[getStructuredAppGuideMetadata, appType]
() => getStructuredAppGuideMetadata({ categories: [app.type] }),
[getStructuredAppGuideMetadata, app.type]
);

const hasSingleGuide = useMemo(() => {
return structuredMetadata[appType].length === 1;
}, [appType, structuredMetadata]);
return structuredMetadata[app.type].length === 1;
}, [app.type, structuredMetadata]);

useEffect(() => {
if (hasSingleGuide) {
const guide = structuredMetadata[appType][0];
const guide = structuredMetadata[app.type][0];
if (guide) {
const { id, metadata } = guide;
setSelectedGuide({ id, metadata });
}
}
}, [hasSingleGuide, appType, structuredMetadata]);
}, [hasSingleGuide, app.type, structuredMetadata]);

return (
<div className={styles.drawerContainer}>
Expand Down Expand Up @@ -81,8 +75,8 @@ function GuideDrawer({ app, secrets, onClose }: Props) {
{!selectedGuide && (
<GuideCardGroup
className={styles.cardGroup}
categoryName={t(`categories.${appType}`)}
guides={structuredMetadata[appType]}
categoryName={t(`categories.${app.type}`)}
guides={structuredMetadata[app.type]}
onClickGuide={(guide) => {
setSelectedGuide(guide);
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type Application, ApplicationType, customClientMetadataGuard } from '@logto/schemas';
import { ApplicationType, customClientMetadataGuard } from '@logto/schemas';
import { useFormContext } from 'react-hook-form';
import { Trans, useTranslation } from 'react-i18next';

Expand All @@ -7,6 +7,7 @@ import FormField from '@/ds-components/FormField';
import Switch from '@/ds-components/Switch';
import TextInput from '@/ds-components/TextInput';
import TextLink from '@/ds-components/TextLink';
import { type Application } from '@/types/applications';

type Props = {
readonly data: Application;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { validateRedirectUrl } from '@logto/core-kit';
import type { Application } from '@logto/schemas';
import { ApplicationType } from '@logto/schemas';
import { Controller, useFormContext } from 'react-hook-form';
import { Trans, useTranslation } from 'react-i18next';
Expand All @@ -16,6 +15,7 @@ import {
import TextInput from '@/ds-components/TextInput';
import TextLink from '@/ds-components/TextLink';
import useDocumentationUrl from '@/hooks/use-documentation-url';
import type { Application } from '@/types/applications';
import { isJsonObject } from '@/utils/json';

import ProtectedAppSettings from './ProtectedAppSettings';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
ApplicationType,
type ApplicationResponse,
type SnakeCaseOidcConfig,
} from '@logto/schemas';
import { ApplicationType, type SnakeCaseOidcConfig } from '@logto/schemas';
import { useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
Expand All @@ -28,6 +24,7 @@ import { organizations } from '@/hooks/use-console-routes/routes/organizations';
import useDocumentationUrl from '@/hooks/use-documentation-url';
import useTenantPathname from '@/hooks/use-tenant-pathname';
import { applicationTypeI18nKey } from '@/types/applications';
import { type ApplicationResponse } from '@/types/applications';
import { trySubmitSafe } from '@/utils/form';

import BackchannelLogout from './BackchannelLogout';
Expand Down Expand Up @@ -123,8 +120,7 @@ function ApplicationDetailsContent({ data, secrets, oidcConfig, onApplicationUpd
icon={<ApplicationIcon type={data.type} isThirdParty={data.isThirdParty} />}
title={data.name}
primaryTag={
// We have ensured that SAML applications are always third party in DB schema, we use `||` here to make TypeScript happy.
data.isThirdParty || data.type === ApplicationType.SAML
data.isThirdParty
? t(`${applicationTypeI18nKey.thirdParty}.title`)
: t(`${applicationTypeI18nKey[data.type]}.title`)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { ApplicationResponse } from '@logto/schemas';
import Modal from 'react-modal';

import ModalHeader from '@/components/Guide/ModalHeader';
import modalStyles from '@/scss/modal.module.scss';
import { type ApplicationResponse } from '@/types/applications';

import { type ApplicationSecretRow } from '../ApplicationDetailsContent/EndpointsAndCredentials';
import AppGuide from '../components/AppGuide';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { type ApplicationResponse } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
import { useContext, useMemo } from 'react';

import { guides } from '@/assets/docs/guides';
import Guide, { GuideContext, type GuideContextType } from '@/components/Guide';
import { AppDataContext } from '@/contexts/AppDataProvider';
import useCustomDomain from '@/hooks/use-custom-domain';
import { type ApplicationResponse } from '@/types/applications';

import { type ApplicationSecretRow } from '../../ApplicationDetailsContent/EndpointsAndCredentials';

Expand Down
3 changes: 2 additions & 1 deletion packages/console/src/pages/ApplicationDetails/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type ApplicationResponse, type SnakeCaseOidcConfig } from '@logto/schemas';
import { type SnakeCaseOidcConfig } from '@logto/schemas';
import { useParams } from 'react-router-dom';
import useSWR from 'swr';

Expand All @@ -8,6 +8,7 @@ import { openIdProviderConfigPath } from '@/consts/oidc';
import { Daisy } from '@/ds-components/Spinner';
import type { RequestError } from '@/hooks/use-api';
import useTenantPathname from '@/hooks/use-tenant-pathname';
import { type ApplicationResponse } from '@/types/applications';

import ApplicationDetailsContent from './ApplicationDetailsContent';
import { type ApplicationSecretRow } from './ApplicationDetailsContent/EndpointsAndCredentials';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isLocalhost, validateUriOrigin } from '@logto/core-kit';
import { ApplicationType, type Application, type RequestErrorBody } from '@logto/schemas';
import { ApplicationType, type RequestErrorBody } from '@logto/schemas';
import { isValidSubdomain } from '@logto/shared/universal';
import { condString, conditional } from '@silverhand/essentials';
import classNames from 'classnames';
Expand All @@ -22,6 +22,7 @@ import TextLink from '@/ds-components/TextLink';
import useApi from '@/hooks/use-api';
import useApplicationsUsage from '@/hooks/use-applications-usage';
import useTenantPathname from '@/hooks/use-tenant-pathname';
import type { Application } from '@/types/applications';

import styles from './index.module.scss';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { type ApplicationType } from '@logto/schemas';
import classNames from 'classnames';

import ApplicationIcon from '@/components/ApplicationIcon';
import { type ApplicationTypeWithoutSaml } from '@/types/applications';

import styles from './index.module.scss';

type Props = {
readonly title: string;
readonly subtitle: string;
readonly description: string;
readonly type: ApplicationType;
readonly type: ApplicationTypeWithoutSaml;
readonly size?: 'large' | 'small';
};

Expand Down
Loading

0 comments on commit d5c8533

Please sign in to comment.