Skip to content

Commit

Permalink
refactor(console): refactor sso detail pages
Browse files Browse the repository at this point in the history
refactor sso details pages
  • Loading branch information
simeng-li committed Mar 25, 2024
1 parent d8efb8a commit 029fa50
Show file tree
Hide file tree
Showing 18 changed files with 593 additions and 469 deletions.
85 changes: 0 additions & 85 deletions packages/console/src/pages/EnterpriseSso/types.ts

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { type RequestErrorBody } from '@logto/schemas';
import cleanDeep from 'clean-deep';
import { HTTPError } from 'ky';
import { useEffect, useMemo } from 'react';
import { FormProvider, 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 useApi from '@/hooks/use-api';

import { invalidConfigErrorCode } from '../config';
import {
type OidcSsoConnectorWithProviderConfig,
type OidcConnectorConfig,
oidcConnectorConfigGuard,
oidcProviderConfigGuard,
} from '../types/oidc';

import OidcMetadataForm from './OidcMetadataForm';
import OidcConnectorSpInfo from './ServiceProviderInfo/OidcConnectorSpInfo';

type Props = {
isDeleted: boolean;
data: OidcSsoConnectorWithProviderConfig;
onUpdated: (data: OidcSsoConnectorWithProviderConfig) => void;
};

function OidcConnectorForm({ isDeleted, data, onUpdated }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const api = useApi({ hideErrorToast: [invalidConfigErrorCode] });

const methods = useForm<OidcConnectorConfig>();

const {
formState: { isSubmitting, isDirty },
handleSubmit,
reset,
setError,
} = methods;

const { config, providerConfig, providerName, id: connectorId } = data;

// Guard the config data
const oidcConnectorConfig = useMemo(() => {
const result = oidcConnectorConfigGuard.safeParse(config);
const { success } = result;
const guardedConfig = success ? result.data : undefined;

return guardedConfig;
}, [config]);

const oidcProviderConfig = useMemo(() => {
const result = oidcProviderConfigGuard.safeParse(providerConfig);
const { success } = result;
const guardedConfig = success ? result.data : undefined;

return guardedConfig;
}, [providerConfig]);

useEffect(() => {
reset(oidcConnectorConfig);
}, [oidcConnectorConfig, reset]);

const onSubmit = handleSubmit(async (formData) => {
if (isSubmitting) {
return;
}

try {
const result = await api
.patch(`api/sso-connectors/${connectorId}`, {
json: {
config: cleanDeep(formData),
},
})
.json<OidcSsoConnectorWithProviderConfig>();

toast.success(t('general.saved'));

onUpdated(result);

reset(result.config);
} catch (error: unknown) {
if (error instanceof HTTPError) {
const errorBody = await error.response.clone().json<RequestErrorBody>();

// Manually handle the error to show the error message in the form.
if (errorBody.code === invalidConfigErrorCode) {
setError('issuer', {
type: 'custom',
message: errorBody.message,
});
return;
}
}

throw error;
}
});

return (
<FormProvider {...methods}>
<DetailsForm
isDirty={isDirty}
isSubmitting={isSubmitting}
onDiscard={reset}
onSubmit={onSubmit}
>
<FormCard
title="enterprise_sso_details.upload_idp_metadata_title_oidc"
description="enterprise_sso_details.upload_idp_metadata_description_oidc"
>
{/* Can not infer the type by narrowing down the value of `providerName`, so we need to cast it. */}
<OidcMetadataForm
providerName={providerName}
config={oidcConnectorConfig}
providerConfig={oidcProviderConfig}
/>
</FormCard>
<FormCard
title="enterprise_sso_details.service_provider_property_title"
description="enterprise_sso_details.service_provider_property_description"
descriptionInterpolation={{
protocol: 'OIDC',
}}
>
<OidcConnectorSpInfo ssoConnectorId={connectorId} />
</FormCard>
</DetailsForm>
<UnsavedChangesAlertModal hasUnsavedChanges={!isDeleted && isDirty} />
</FormProvider>
);
}

export default OidcConnectorForm;
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { type SsoProviderName } from '@logto/schemas';
import classNames from 'classnames';
import { useTranslation } from 'react-i18next';

import { type ParsedSsoIdentityProviderConfig } from '@/pages/EnterpriseSso/types.js';
import { type OidcProviderConfig } from '@/pages/EnterpriseSsoDetails/types/oidc';

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

type Props = {
providerConfig: ParsedSsoIdentityProviderConfig<SsoProviderName.OIDC>;
providerConfig: OidcProviderConfig;
className?: string;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,16 @@ import CopyToClipboard from '@/ds-components/CopyToClipboard';
import FormField from '@/ds-components/FormField';
import InlineNotification from '@/ds-components/InlineNotification';
import TextInput from '@/ds-components/TextInput';
import {
type ParsedSsoIdentityProviderConfig,
type OidcGuideFormType,
type SsoConnectorConfig,
} from '@/pages/EnterpriseSso/types.js';
import { uriValidator } from '@/utils/validator';

import { type OidcConnectorConfig, type OidcProviderConfig } from '../../types/oidc';

import ParsedConfigPreview from './ParsedConfigPreview';
import * as styles from './index.module.scss';

type Props = {
providerConfig?: ParsedSsoIdentityProviderConfig<SsoProviderName.OIDC>;
config?: SsoConnectorConfig<SsoProviderName.OIDC>;
providerConfig?: OidcProviderConfig;
config?: OidcConnectorConfig;
providerName: SsoProviderName;
};

Expand All @@ -28,7 +25,7 @@ function OidcMetadataForm({ providerConfig, config, providerName }: Props) {
const {
register,
formState: { errors },
} = useFormContext<OidcGuideFormType>();
} = useFormContext<OidcConnectorConfig>();

const isConfigEmpty = !config || Object.keys(config).length === 0;

Expand Down
Loading

0 comments on commit 029fa50

Please sign in to comment.