0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

fix(console): invalid guide url should show 404 not found (#4367)

This commit is contained in:
Charles Zhao 2023-08-20 02:05:29 -05:00 committed by GitHub
parent 4a532a358d
commit 5b1039267f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 70 additions and 54 deletions

View file

@ -55,6 +55,14 @@
margin-top: _.unit(6); margin-top: _.unit(6);
} }
.notFound {
width: 100%;
svg {
margin-top: 10%;
}
}
.actionBar { .actionBar {
position: absolute; position: absolute;
inset: auto 0 0 0; inset: auto 0 0 0;

View file

@ -12,6 +12,7 @@ import CodeEditor from '@/ds-components/CodeEditor';
import TextLink from '@/ds-components/TextLink'; import TextLink from '@/ds-components/TextLink';
import useCustomDomain from '@/hooks/use-custom-domain'; import useCustomDomain from '@/hooks/use-custom-domain';
import DetailsSummary from '@/mdx-components/DetailsSummary'; import DetailsSummary from '@/mdx-components/DetailsSummary';
import NotFound from '@/pages/NotFound';
import GuideHeader from '../GuideHeader'; import GuideHeader from '../GuideHeader';
import StepsSkeleton from '../StepsSkeleton'; import StepsSkeleton from '../StepsSkeleton';
@ -50,7 +51,7 @@ export const GuideContext = createContext<GuideContextType>({
type Props = { type Props = {
className?: string; className?: string;
guideId: string; guideId: string;
app?: ApplicationResponse; app: ApplicationResponse;
isCompact?: boolean; isCompact?: boolean;
onClose: () => void; onClose: () => void;
}; };
@ -61,28 +62,26 @@ function Guide({ className, guideId, app, isCompact, onClose }: Props) {
const isCustomDomainActive = customDomain?.status === DomainStatus.Active; const isCustomDomainActive = customDomain?.status === DomainStatus.Active;
const guide = guides.find(({ id }) => id === guideId); const guide = guides.find(({ id }) => id === guideId);
if (!app || !guide) { const GuideComponent = guide?.Component;
throw new Error('Invalid app or guide');
}
const GuideComponent = guide.Component;
const memorizedContext = useMemo( const memorizedContext = useMemo(
() => () =>
({ conditional(
metadata: guide.metadata, !!guide && {
Logo: guide.Logo, metadata: guide.metadata,
app, Logo: guide.Logo,
endpoint: tenantEndpoint?.toString() ?? '', app,
alternativeEndpoint: conditional(isCustomDomainActive && tenantEndpoint?.toString()), endpoint: tenantEndpoint?.toString() ?? '',
redirectUris: app.oidcClientMetadata.redirectUris, alternativeEndpoint: conditional(isCustomDomainActive && tenantEndpoint?.toString()),
postLogoutRedirectUris: app.oidcClientMetadata.postLogoutRedirectUris, redirectUris: app.oidcClientMetadata.redirectUris,
isCompact: Boolean(isCompact), postLogoutRedirectUris: app.oidcClientMetadata.postLogoutRedirectUris,
sampleUrls: { isCompact: Boolean(isCompact),
origin: 'http://localhost:3001/', sampleUrls: {
callback: 'http://localhost:3001/callback', origin: 'http://localhost:3001/',
}, callback: 'http://localhost:3001/callback',
}) satisfies GuideContextType, },
}
) satisfies GuideContextType | undefined,
[guide, app, tenantEndpoint, isCustomDomainActive, isCompact] [guide, app, tenantEndpoint, isCustomDomainActive, isCompact]
); );
@ -90,39 +89,43 @@ function Guide({ className, guideId, app, isCompact, onClose }: Props) {
<div className={classNames(styles.container, className)}> <div className={classNames(styles.container, className)}>
{!isCompact && <GuideHeader onClose={onClose} />} {!isCompact && <GuideHeader onClose={onClose} />}
<div className={styles.content}> <div className={styles.content}>
<GuideContext.Provider value={memorizedContext}> {memorizedContext ? (
<MDXProvider <GuideContext.Provider value={memorizedContext}>
components={{ <MDXProvider
code: ({ className, children }) => { components={{
const [, language] = /language-(\w+)/.exec(String(className ?? '')) ?? []; code: ({ className, children }) => {
const [, language] = /language-(\w+)/.exec(String(className ?? '')) ?? [];
return language ? ( return language ? (
<CodeEditor <CodeEditor
isReadonly isReadonly
// We need to transform `ts` to `typescript` for prismjs, and // We need to transform `ts` to `typescript` for prismjs, and
// it's weird since it worked in the original Guide component. // it's weird since it worked in the original Guide component.
// To be investigated. // To be investigated.
language={language === 'ts' ? 'typescript' : language} language={language === 'ts' ? 'typescript' : language}
value={String(children).trimEnd()} value={String(children).trimEnd()}
/> />
) : ( ) : (
<code>{String(children).trimEnd()}</code> <code>{String(children).trimEnd()}</code>
); );
}, },
a: ({ children, ...props }) => ( a: ({ children, ...props }) => (
<TextLink {...props} target="_blank" rel="noopener noreferrer"> <TextLink {...props} target="_blank" rel="noopener noreferrer">
{children} {children}
</TextLink> </TextLink>
), ),
details: DetailsSummary, details: DetailsSummary,
}} }}
> >
<Suspense fallback={<StepsSkeleton />}> <Suspense fallback={<StepsSkeleton />}>
{tenantEndpoint && <GuideComponent {...memorizedContext} />} {tenantEndpoint && GuideComponent && <GuideComponent {...memorizedContext} />}
</Suspense> </Suspense>
</MDXProvider> </MDXProvider>
</GuideContext.Provider> </GuideContext.Provider>
{!isCompact && ( ) : (
<NotFound className={styles.notFound} />
)}
{!isCompact && memorizedContext && (
<nav className={styles.actionBar}> <nav className={styles.actionBar}>
<div className={styles.layout}> <div className={styles.layout}>
<Button <Button

View file

@ -1,4 +1,5 @@
import { Theme } from '@logto/schemas'; import { Theme } from '@logto/schemas';
import classNames from 'classnames';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
@ -10,13 +11,17 @@ import useTheme from '@/hooks/use-theme';
import * as styles from './index.module.scss'; import * as styles from './index.module.scss';
function NotFound() { type Props = {
className?: string;
};
function NotFound({ className }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const theme = useTheme(); const theme = useTheme();
const location = useLocation(); const location = useLocation();
return ( return (
<div className={styles.container}> <div className={classNames(styles.container, className)}>
{/* Don't track "not found" for the root path as it will be redirected. */} {/* Don't track "not found" for the root path as it will be redirected. */}
<PageMeta titleKey="errors.page_not_found" trackPageView={location.pathname !== '/'} /> <PageMeta titleKey="errors.page_not_found" trackPageView={location.pathname !== '/'} />
<Card className={styles.content}> <Card className={styles.content}>