mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
refactor: update title in main flow and report pv for console sign-in (#3651)
This commit is contained in:
parent
1d4662ebc1
commit
0e49e43245
41 changed files with 163 additions and 51 deletions
|
@ -9,11 +9,9 @@
|
|||
"lib"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./lib/index.js"
|
||||
},
|
||||
"./*": {
|
||||
"import": "./lib/*"
|
||||
"import": "./lib/*.js",
|
||||
"types": "./lib/*.d.ts"
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
|
|
|
@ -6,7 +6,7 @@ import { findUp } from 'find-up';
|
|||
|
||||
dotenv.config({ path: await findUp('.env', {}) });
|
||||
|
||||
const { appInsights } = await import('@logto/app-insights/node.js');
|
||||
const { appInsights } = await import('@logto/app-insights/node');
|
||||
|
||||
if (appInsights.setup('logto-cloud')) {
|
||||
console.debug('Initialized ApplicationInsights');
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { appInsights } from '@logto/app-insights/node.js';
|
||||
import { appInsights } from '@logto/app-insights/node';
|
||||
import { tryThat } from '@silverhand/essentials';
|
||||
import type { BaseContext, NextFunction } from '@withtyped/server';
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import TenantsProvider, { TenantsContext } from './contexts/TenantsProvider';
|
|||
if (appInsightsReact.setup()) {
|
||||
console.debug('Initialized ApplicationInsights');
|
||||
}
|
||||
|
||||
void initI18n();
|
||||
|
||||
function Content() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import fs from 'node:fs/promises';
|
||||
import http2 from 'node:http2';
|
||||
|
||||
import { appInsights } from '@logto/app-insights/node.js';
|
||||
import { appInsights } from '@logto/app-insights/node';
|
||||
import { toTitle, trySafe } from '@silverhand/essentials';
|
||||
import chalk from 'chalk';
|
||||
import type Koa from 'koa';
|
||||
|
|
|
@ -8,7 +8,7 @@ import SystemContext from './tenants/SystemContext.js';
|
|||
|
||||
dotenv.config({ path: await findUp('.env', {}) });
|
||||
|
||||
const { appInsights } = await import('@logto/app-insights/node.js');
|
||||
const { appInsights } = await import('@logto/app-insights/node');
|
||||
|
||||
if (appInsights.setup('logto')) {
|
||||
console.debug('Initialized ApplicationInsights');
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { appInsights } from '@logto/app-insights/node.js';
|
||||
import { appInsights } from '@logto/app-insights/node';
|
||||
import type { RequestErrorBody } from '@logto/schemas';
|
||||
import type { Middleware } from 'koa';
|
||||
import { HttpError } from 'koa';
|
||||
|
|
|
@ -4,7 +4,13 @@ import { readFileSync } from 'node:fs';
|
|||
|
||||
import { userClaims } from '@logto/core-kit';
|
||||
import type { I18nKey } from '@logto/phrases';
|
||||
import { CustomClientMetadataKey, demoAppApplicationId } from '@logto/schemas';
|
||||
import {
|
||||
CustomClientMetadataKey,
|
||||
demoAppApplicationId,
|
||||
logtoCookieKey,
|
||||
type LogtoUiCookie,
|
||||
} from '@logto/schemas';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import i18next from 'i18next';
|
||||
import Provider, { errors, type ResourceServer } from 'oidc-provider';
|
||||
import snakecaseKeys from 'snakecase-keys';
|
||||
|
@ -145,15 +151,23 @@ export default function initOidc(
|
|||
},
|
||||
},
|
||||
interactions: {
|
||||
url: (ctx, interaction) => {
|
||||
const isDemoApp = interaction.params.client_id === demoAppApplicationId;
|
||||
url: (ctx, { params: { client_id: appId }, prompt }) => {
|
||||
const isDemoApp = appId === demoAppApplicationId;
|
||||
|
||||
ctx.cookies.set(
|
||||
logtoCookieKey,
|
||||
JSON.stringify({
|
||||
appId: conditional(Boolean(appId) && String(appId)),
|
||||
} satisfies LogtoUiCookie),
|
||||
{ sameSite: 'lax', overwrite: true, httpOnly: false }
|
||||
);
|
||||
|
||||
const appendParameters = (path: string) => {
|
||||
// `notification` is for showing a text banner on the homepage
|
||||
return isDemoApp ? path + `?notification=demo_app.notification&no_cache` : path;
|
||||
};
|
||||
|
||||
switch (interaction.prompt.name) {
|
||||
switch (prompt.name) {
|
||||
case 'login': {
|
||||
const isSignUp =
|
||||
ctx.oidc.params?.[OIDCExtraParametersKey.InteractionMode] === InteractionMode.signUp;
|
||||
|
@ -166,7 +180,7 @@ export default function initOidc(
|
|||
}
|
||||
|
||||
default: {
|
||||
throw new Error(`Prompt not supported: ${interaction.prompt.name}`);
|
||||
throw new Error(`Prompt not supported: ${prompt.name}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -60,6 +60,8 @@ const description = {
|
|||
create_your_account: 'Erstelle dein Konto',
|
||||
sign_in_to_your_account: 'Melde dich in deinem Konto an',
|
||||
no_region_code_found: 'Kein Regionencode gefunden',
|
||||
verify_email: 'Bestätige deine E-Mail-Adresse',
|
||||
verify_phone: 'Bestätige deine Telefonnummer',
|
||||
};
|
||||
|
||||
export default description;
|
||||
|
|
|
@ -57,6 +57,8 @@ const description = {
|
|||
create_your_account: 'Create your account',
|
||||
sign_in_to_your_account: 'Sign in to your account',
|
||||
no_region_code_found: 'No region code found',
|
||||
verify_email: 'Verify your email',
|
||||
verify_phone: 'Verify your phone number',
|
||||
};
|
||||
|
||||
export default description;
|
||||
|
|
|
@ -58,6 +58,8 @@ const description = {
|
|||
create_your_account: 'Cree su cuenta',
|
||||
sign_in_to_your_account: 'Inicie sesión en su cuenta',
|
||||
no_region_code_found: 'No se encontró código de región',
|
||||
verify_email: 'Verificar su correo electrónico',
|
||||
verify_phone: 'Verificar su número de teléfono',
|
||||
};
|
||||
|
||||
export default description;
|
||||
|
|
|
@ -60,6 +60,8 @@ const description = {
|
|||
create_your_account: 'Créer votre compte',
|
||||
sign_in_to_your_account: 'Connecte-toi à ton compte',
|
||||
no_region_code_found: 'Aucun code de région trouvé',
|
||||
verify_email: 'Vérifiez votre e-mail',
|
||||
verify_phone: 'Vérifiez votre numéro de téléphone',
|
||||
};
|
||||
|
||||
export default description;
|
||||
|
|
|
@ -56,6 +56,8 @@ const description = {
|
|||
create_your_account: 'Crea il tuo account',
|
||||
sign_in_to_your_account: 'Accedi al tuo account',
|
||||
no_region_code_found: 'Nessun codice di regione trovato',
|
||||
verify_email: 'Verifica la tua email',
|
||||
verify_phone: 'Verifica il tuo numero di telefono',
|
||||
};
|
||||
|
||||
export default description;
|
||||
|
|
|
@ -58,6 +58,8 @@ const description = {
|
|||
create_your_account: 'アカウントを作成する',
|
||||
sign_in_to_your_account: 'アカウントにサインインする',
|
||||
no_region_code_found: '地域コードが見つかりません',
|
||||
verify_email: 'Eメールを確認する',
|
||||
verify_phone: '電話番号を確認する',
|
||||
};
|
||||
|
||||
export default description;
|
||||
|
|
|
@ -53,6 +53,8 @@ const description = {
|
|||
create_your_account: '계정 생성하기',
|
||||
sign_in_to_your_account: '계정에 로그인하세요',
|
||||
no_region_code_found: '지역 코드를 찾을 수 없습니다.',
|
||||
verify_email: '이메일 인증',
|
||||
verify_phone: '휴대전화번호 인증',
|
||||
};
|
||||
|
||||
export default description;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const opis = {
|
||||
const description = {
|
||||
email: 'adres email',
|
||||
phone_number: 'numer telefonu',
|
||||
username: 'nazwa użytkownika',
|
||||
|
@ -29,7 +29,7 @@ const opis = {
|
|||
social_link_email: 'Możesz połączyć kolejny adres email',
|
||||
social_link_phone: 'Możesz połączyć kolejny numer telefonu',
|
||||
social_link_email_or_phone: 'Możesz połączyć kolejny adres email lub numer telefonu',
|
||||
social_bind_with_existing: 'Znaleźliśmy powiązane konto, możesz go połączyć bezpośrednio.',
|
||||
social_bind_with_existing: 'Znaleźliśmy powiązane konto, możesz je połączyć bezpośrednio.',
|
||||
reset_password: 'Zresetuj hasło',
|
||||
reset_password_description:
|
||||
'Wpisz {{types, lista(type: złączonych;)}} związanego z twoim kontem, a wyślemy ci kod weryfikacyjny do zresetowania hasła.',
|
||||
|
@ -56,6 +56,8 @@ const opis = {
|
|||
create_your_account: 'Utwórz konto',
|
||||
sign_in_to_your_account: 'Zaloguj się do swojego konta',
|
||||
no_region_code_found: 'Nie znaleziono kodu regionu',
|
||||
verify_email: 'Potwierdź swój email',
|
||||
verify_phone: 'Potwierdź swój numer telefonu',
|
||||
};
|
||||
|
||||
export default opis;
|
||||
export default description;
|
||||
|
|
|
@ -55,6 +55,8 @@ const description = {
|
|||
create_your_account: 'Crie sua conta',
|
||||
sign_in_to_your_account: 'Faça login na sua conta',
|
||||
no_region_code_found: 'Não foi possível encontrar o código de região do seu telefone.',
|
||||
verify_email: 'Verificar e-mail',
|
||||
verify_phone: 'Verificar número de telefone',
|
||||
};
|
||||
|
||||
export default description;
|
||||
|
|
|
@ -55,6 +55,8 @@ const description = {
|
|||
create_your_account: 'Crie a sua conta',
|
||||
sign_in_to_your_account: 'Inicie sessão na sua conta',
|
||||
no_region_code_found: 'Não foi possível encontrar o código de região do seu telefone.',
|
||||
verify_email: 'Verifique o seu email',
|
||||
verify_phone: 'Verifique o seu número de telefone',
|
||||
};
|
||||
|
||||
export default description;
|
||||
|
|
|
@ -59,6 +59,8 @@ const description = {
|
|||
create_your_account: 'Создайте свой аккаунт',
|
||||
sign_in_to_your_account: 'Войди в свой аккаунт',
|
||||
no_region_code_found: 'Не удалось определить код региона',
|
||||
verify_email: 'Подтвердите Ваш электронный адрес',
|
||||
verify_phone: 'Подтвердите свой номер телефона',
|
||||
};
|
||||
|
||||
export default description;
|
||||
|
|
|
@ -56,6 +56,8 @@ const description = {
|
|||
create_your_account: 'Hesabını oluştur',
|
||||
sign_in_to_your_account: 'Hesabına giriş yap',
|
||||
no_region_code_found: 'Bölge kodu bulunamadı',
|
||||
verify_email: 'E-postanızın doğrulanması',
|
||||
verify_phone: 'Telefon numaranızın doğrulanması',
|
||||
};
|
||||
|
||||
export default description;
|
||||
|
|
|
@ -49,6 +49,8 @@ const description = {
|
|||
create_your_account: '注册你的账号',
|
||||
sign_in_to_your_account: '登录你的账号',
|
||||
no_region_code_found: '没有找到区域码',
|
||||
verify_email: '验证你的邮箱',
|
||||
verify_phone: '验证你的手机号',
|
||||
};
|
||||
|
||||
export default description;
|
||||
|
|
|
@ -49,6 +49,8 @@ const description = {
|
|||
create_your_account: '註冊你的帳號',
|
||||
sign_in_to_your_account: '登錄你的帳號',
|
||||
no_region_code_found: '沒有找到區域碼',
|
||||
verify_email: '驗證你的郵箱',
|
||||
verify_phone: '驗證你的手機號',
|
||||
};
|
||||
|
||||
export default description;
|
||||
|
|
|
@ -49,6 +49,8 @@ const description = {
|
|||
create_your_account: '註冊你的帳號',
|
||||
sign_in_to_your_account: '登錄你的帳號',
|
||||
no_region_code_found: '沒有找到區域碼',
|
||||
verify_email: '驗證你的郵箱',
|
||||
verify_phone: '驗證你的手機號碼',
|
||||
};
|
||||
|
||||
export default description;
|
||||
|
|
1
packages/schemas/src/consts/cookie.ts
Normal file
1
packages/schemas/src/consts/cookie.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export const logtoCookieKey = '_logto';
|
|
@ -1,2 +1,3 @@
|
|||
export * from './cookie.js';
|
||||
export * from './system.js';
|
||||
export * from './oidc.js';
|
||||
|
|
5
packages/schemas/src/types/cookie.ts
Normal file
5
packages/schemas/src/types/cookie.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
export const logtoUiCookieGuard = z.object({ appId: z.string() }).partial();
|
||||
|
||||
export type LogtoUiCookie = z.infer<typeof logtoUiCookieGuard>;
|
|
@ -16,3 +16,4 @@ export * from './user-assets.js';
|
|||
export * from './hook.js';
|
||||
export * from './service-log.js';
|
||||
export * from './theme.js';
|
||||
export * from './cookie.js';
|
||||
|
|
|
@ -9,6 +9,9 @@ const config: Config.InitialOptions = {
|
|||
'\\.(svg)$': 'jest-transformer-svg',
|
||||
'\\.(png)$': 'jest-transform-stub',
|
||||
},
|
||||
moduleNameMapper: {
|
||||
'^@logto/app-insights/(.*)$': '<rootDir>/node_modules/@logto/app-insights/$1',
|
||||
},
|
||||
}),
|
||||
// Will update common config soon
|
||||
transformIgnorePatterns: ['node_modules/(?!(.*(nanoid|jose|ky|@logto|@silverhand))/)'],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
"test": "jest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@logto/app-insights": "workspace:^",
|
||||
"@logto/connector-kit": "workspace:^",
|
||||
"@logto/core-kit": "workspace:^",
|
||||
"@logto/language-kit": "workspace:^",
|
||||
|
@ -42,6 +43,7 @@
|
|||
"@types/jest": "^29.4.0",
|
||||
"@types/react": "^18.0.31",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@types/react-helmet": "^6.1.6",
|
||||
"@types/react-modal": "^3.13.1",
|
||||
"@types/react-router-dom": "^5.3.2",
|
||||
"camelcase-keys": "^8.0.0",
|
||||
|
@ -56,7 +58,7 @@
|
|||
"jest-environment-jsdom": "^29.0.0",
|
||||
"jest-transform-stub": "^2.0.0",
|
||||
"jest-transformer-svg": "^2.0.0",
|
||||
"js-base64": "^3.7.2",
|
||||
"js-base64": "^3.7.5",
|
||||
"ky": "^0.33.0",
|
||||
"libphonenumber-js": "^1.9.49",
|
||||
"lint-staged": "^13.0.0",
|
||||
|
@ -68,6 +70,7 @@
|
|||
"react": "^18.0.0",
|
||||
"react-device-detect": "^2.2.2",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-hook-form": "^7.34.0",
|
||||
"react-i18next": "^11.18.3",
|
||||
"react-modal": "^3.15.1",
|
||||
|
@ -77,6 +80,7 @@
|
|||
"react-top-loading-bar": "^2.3.1",
|
||||
"stylelint": "^15.0.0",
|
||||
"superstruct": "^0.16.0",
|
||||
"tiny-cookie": "^2.4.1",
|
||||
"ts-jest": "^29.0.5",
|
||||
"typescript": "^5.0.0",
|
||||
"use-debounced-loader": "^0.1.1",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { appInsightsReact } from '@logto/app-insights/lib/react';
|
||||
import { Route, Routes, BrowserRouter } from 'react-router-dom';
|
||||
|
||||
import AppLayout from './Layout/AppLayout';
|
||||
|
@ -20,10 +21,15 @@ import SocialLinkAccount from './pages/SocialLinkAccount';
|
|||
import SocialSignIn from './pages/SocialSignInCallback';
|
||||
import Springboard from './pages/Springboard';
|
||||
import VerificationCode from './pages/VerificationCode';
|
||||
import { shouldTrack } from './utils/cookies';
|
||||
import { handleSearchParametersData } from './utils/search-parameters';
|
||||
|
||||
import './scss/normalized.scss';
|
||||
|
||||
if (shouldTrack && appInsightsReact.setup()) {
|
||||
console.debug('Initialized ApplicationInsights');
|
||||
}
|
||||
|
||||
handleSearchParametersData();
|
||||
|
||||
const App = () => {
|
||||
|
|
|
@ -1,28 +1,15 @@
|
|||
import classNames from 'classnames';
|
||||
import { useEffect } from 'react';
|
||||
import { Outlet, useLocation } from 'react-router-dom';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
import LogtoSignature from '@/components/LogtoSignature';
|
||||
import usePlatform from '@/hooks/use-platform';
|
||||
import { layoutClassNames } from '@/utils/consts';
|
||||
import { parseHtmlTitle } from '@/utils/sign-in-experience';
|
||||
|
||||
import CustomContent from './CustomContent';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
const AppLayout = () => {
|
||||
const { isMobile } = usePlatform();
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
const { pathname } = location;
|
||||
const title = parseHtmlTitle(pathname);
|
||||
|
||||
if (title) {
|
||||
// eslint-disable-next-line @silverhand/fp/no-mutation
|
||||
document.title = title;
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
return (
|
||||
<div className={styles.viewBox}>
|
||||
|
|
|
@ -5,6 +5,7 @@ import type { TFuncKey } from 'react-i18next';
|
|||
|
||||
import PageContext from '@/Providers/PageContextProvider/PageContext';
|
||||
import BrandingHeader from '@/components/BrandingHeader';
|
||||
import PageMeta from '@/components/PageMeta';
|
||||
import { layoutClassNames } from '@/utils/consts';
|
||||
import { getBrandingLogoUrl } from '@/utils/logo';
|
||||
|
||||
|
@ -15,7 +16,7 @@ import * as styles from './index.module.scss';
|
|||
type Props = {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
title?: TFuncKey;
|
||||
title: TFuncKey;
|
||||
};
|
||||
|
||||
const LandingPageLayout = ({ children, className, title }: Props) => {
|
||||
|
@ -32,6 +33,7 @@ const LandingPageLayout = ({ children, className, title }: Props) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<PageMeta titleKey={title} />
|
||||
{platform === 'web' && <div className={styles.placeholderTop} />}
|
||||
<div className={classNames(styles.wrapper, className)}>
|
||||
<BrandingHeader
|
||||
|
|
|
@ -2,6 +2,7 @@ import { useTranslation } from 'react-i18next';
|
|||
import type { TFuncKey } from 'react-i18next';
|
||||
|
||||
import NavBar from '@/components/NavBar';
|
||||
import PageMeta from '@/components/PageMeta';
|
||||
import usePlatform from '@/hooks/use-platform';
|
||||
|
||||
import { InlineNotification } from '../../components/Notification';
|
||||
|
@ -9,7 +10,7 @@ import { InlineNotification } from '../../components/Notification';
|
|||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = {
|
||||
title?: TFuncKey;
|
||||
title: TFuncKey;
|
||||
description?: TFuncKey;
|
||||
titleProps?: Record<string, unknown>;
|
||||
descriptionProps?: Record<string, unknown>;
|
||||
|
@ -30,13 +31,14 @@ const SecondaryPageLayout = ({
|
|||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<PageMeta titleKey={title} />
|
||||
<NavBar />
|
||||
{isMobile && notification && (
|
||||
<InlineNotification message={notification} className={styles.notification} />
|
||||
)}
|
||||
<div className={styles.container}>
|
||||
<div className={styles.header}>
|
||||
{title && <div className={styles.title}>{t(title, titleProps)}</div>}
|
||||
<div className={styles.title}>{t(title, titleProps)}</div>
|
||||
{description && (
|
||||
<div className={styles.description}>{t(description, descriptionProps)}</div>
|
||||
)}
|
||||
|
|
32
packages/ui/src/components/PageMeta/index.tsx
Normal file
32
packages/ui/src/components/PageMeta/index.tsx
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { appInsightsReact } from '@logto/app-insights/lib/react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { type TFuncKey, useTranslation } from 'react-i18next';
|
||||
|
||||
import { shouldTrack } from '@/utils/cookies';
|
||||
|
||||
type Props = {
|
||||
titleKey: TFuncKey | TFuncKey[];
|
||||
// eslint-disable-next-line react/boolean-prop-naming
|
||||
trackPageView?: boolean;
|
||||
};
|
||||
|
||||
const PageMeta = ({ titleKey, trackPageView = true }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const [pageViewTracked, setPageViewTracked] = useState(false);
|
||||
const keys = typeof titleKey === 'string' ? [titleKey] : titleKey;
|
||||
const rawTitle = keys.map((key) => t(key, { lng: 'en' })).join(' - ');
|
||||
const title = keys.map((key) => t(key)).join(' - ');
|
||||
|
||||
useEffect(() => {
|
||||
// Only track once for the same page
|
||||
if (shouldTrack && trackPageView && !pageViewTracked) {
|
||||
appInsightsReact.trackPageView?.({ name: [rawTitle, 'SIE'].join(' - ') });
|
||||
setPageViewTracked(true);
|
||||
}
|
||||
}, [pageViewTracked, rawTitle, trackPageView]);
|
||||
|
||||
return <Helmet title={title} />;
|
||||
};
|
||||
|
||||
export default PageMeta;
|
|
@ -4,7 +4,7 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>Logto</title>
|
||||
<title></title>
|
||||
<link rel="preload" href="/api/.well-known/sign-in-exp" as="fetch" crossorigin="anonymous">
|
||||
<link rel="preload" href="/api/.well-known/phrases" as="fetch" crossorigin="anonymous">
|
||||
</head>
|
||||
|
|
|
@ -8,6 +8,7 @@ import PageContext from '@/Providers/PageContextProvider/PageContext';
|
|||
import EmptyStateDark from '@/assets/icons/empty-state-dark.svg';
|
||||
import EmptyState from '@/assets/icons/empty-state.svg';
|
||||
import NavBar from '@/components/NavBar';
|
||||
import PageMeta from '@/components/PageMeta';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
|
@ -26,6 +27,7 @@ const ErrorPage = ({ title = 'description.not_found', message, rawMessage }: Pro
|
|||
|
||||
return (
|
||||
<StaticPageLayout>
|
||||
<PageMeta titleKey={title} />
|
||||
{history.length > 1 && <NavBar />}
|
||||
<div className={styles.container}>
|
||||
{theme === Theme.Light ? <EmptyState /> : <EmptyStateDark />}
|
||||
|
|
|
@ -8,8 +8,6 @@ import { generateState, storeState } from '@/utils/social-connectors';
|
|||
|
||||
import SocialCallback from '.';
|
||||
|
||||
const origin = 'http://localhost:3000';
|
||||
|
||||
jest.mock('i18next', () => ({
|
||||
...jest.requireActual('i18next'),
|
||||
language: 'en',
|
||||
|
|
|
@ -23,7 +23,7 @@ describe('VerificationCode Page', () => {
|
|||
{ initialEntries: ['/sign-in/verification-code'] }
|
||||
);
|
||||
|
||||
expect(queryByText('action.enter_passcode')).not.toBeNull();
|
||||
expect(queryByText('description.verify_email')).not.toBeNull();
|
||||
expect(queryByText('description.enter_passcode')).not.toBeNull();
|
||||
});
|
||||
|
||||
|
|
|
@ -41,9 +41,10 @@ const VerificationCode = () => {
|
|||
|
||||
return (
|
||||
<SecondaryPageLayout
|
||||
title="action.enter_passcode"
|
||||
title={`description.verify_${identifier}`}
|
||||
description="description.enter_passcode"
|
||||
descriptionProps={{
|
||||
// TODO: @simeng consider align the phrase key to 'phone'
|
||||
address: t(`description.${identifier === 'email' ? 'email' : 'phone_number'}`),
|
||||
target: identifier === 'phone' ? formatPhoneNumberWithCountryCallingCode(value) : value,
|
||||
}}
|
||||
|
|
8
packages/ui/src/utils/cookies.ts
Normal file
8
packages/ui/src/utils/cookies.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { adminConsoleApplicationId, logtoCookieKey, logtoUiCookieGuard } from '@logto/schemas';
|
||||
import { trySafe } from '@silverhand/essentials';
|
||||
import { getCookie } from 'tiny-cookie';
|
||||
|
||||
export const logtoCookies =
|
||||
trySafe(() => logtoUiCookieGuard.parse(getCookie(logtoCookieKey, JSON.parse))) ?? {};
|
||||
|
||||
export const shouldTrack = logtoCookies.appId === adminConsoleApplicationId;
|
|
@ -1342,6 +1342,9 @@ importers:
|
|||
|
||||
packages/ui:
|
||||
devDependencies:
|
||||
'@logto/app-insights':
|
||||
specifier: workspace:^
|
||||
version: link:../app-insights
|
||||
'@logto/connector-kit':
|
||||
specifier: workspace:^
|
||||
version: link:../toolkit/connector-kit
|
||||
|
@ -1417,6 +1420,9 @@ importers:
|
|||
'@types/react-dom':
|
||||
specifier: ^18.0.0
|
||||
version: 18.0.6
|
||||
'@types/react-helmet':
|
||||
specifier: ^6.1.6
|
||||
version: 6.1.6
|
||||
'@types/react-modal':
|
||||
specifier: ^3.13.1
|
||||
version: 3.13.1
|
||||
|
@ -1460,8 +1466,8 @@ importers:
|
|||
specifier: ^2.0.0
|
||||
version: 2.0.0(jest@29.5.0)(react@18.2.0)
|
||||
js-base64:
|
||||
specifier: ^3.7.2
|
||||
version: 3.7.2
|
||||
specifier: ^3.7.5
|
||||
version: 3.7.5
|
||||
ky:
|
||||
specifier: ^0.33.0
|
||||
version: 0.33.0
|
||||
|
@ -1495,6 +1501,9 @@ importers:
|
|||
react-dom:
|
||||
specifier: ^18.0.0
|
||||
version: 18.2.0(react@18.2.0)
|
||||
react-helmet:
|
||||
specifier: ^6.1.0
|
||||
version: 6.1.0(react@18.2.0)
|
||||
react-hook-form:
|
||||
specifier: ^7.34.0
|
||||
version: 7.34.0(react@18.2.0)
|
||||
|
@ -1522,6 +1531,9 @@ importers:
|
|||
superstruct:
|
||||
specifier: ^0.16.0
|
||||
version: 0.16.0
|
||||
tiny-cookie:
|
||||
specifier: ^2.4.1
|
||||
version: 2.4.1
|
||||
ts-jest:
|
||||
specifier: ^29.0.5
|
||||
version: 29.0.5(@babel/core@7.20.2)(@jest/types@29.3.1)(jest@29.5.0)(typescript@5.0.2)
|
||||
|
@ -2716,7 +2728,7 @@ packages:
|
|||
'@types/node': 18.11.18
|
||||
jest-message-util: 29.4.1
|
||||
jest-mock: 29.4.1
|
||||
jest-util: 29.4.1
|
||||
jest-util: 29.5.0
|
||||
dev: true
|
||||
|
||||
/@jest/fake-timers@29.5.0:
|
||||
|
@ -3025,7 +3037,7 @@ packages:
|
|||
dependencies:
|
||||
'@logto/client': 1.1.0
|
||||
'@silverhand/essentials': 1.3.0
|
||||
js-base64: 3.7.4
|
||||
js-base64: 3.7.5
|
||||
dev: true
|
||||
|
||||
/@logto/client@1.1.0:
|
||||
|
@ -9639,7 +9651,7 @@ packages:
|
|||
'@jest/types': 29.5.0
|
||||
'@types/node': 18.11.18
|
||||
jest-mock: 29.4.1
|
||||
jest-util: 29.4.1
|
||||
jest-util: 29.5.0
|
||||
dev: true
|
||||
|
||||
/jest-environment-node@29.5.0:
|
||||
|
@ -9782,7 +9794,7 @@ packages:
|
|||
dependencies:
|
||||
'@jest/types': 29.5.0
|
||||
'@types/node': 18.11.18
|
||||
jest-util: 29.4.1
|
||||
jest-util: 29.5.0
|
||||
dev: true
|
||||
|
||||
/jest-mock@29.5.0:
|
||||
|
@ -10080,14 +10092,14 @@ packages:
|
|||
/jose@4.11.1:
|
||||
resolution: {integrity: sha512-YRv4Tk/Wlug8qicwqFNFVEZSdbROCHRAC6qu/i0dyNKr5JQdoa2pIGoS04lLO/jXQX7Z9omoNewYIVIxqZBd9Q==}
|
||||
|
||||
/js-base64@3.7.2:
|
||||
resolution: {integrity: sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==}
|
||||
dev: true
|
||||
|
||||
/js-base64@3.7.4:
|
||||
resolution: {integrity: sha512-wpM/wi20Tl+3ifTyi0RdDckS4YTD4Lf953mBRrpG8547T7hInHNPEj8+ck4gB8VDcGyeAWFK++Wb/fU1BeavKQ==}
|
||||
dev: true
|
||||
|
||||
/js-base64@3.7.5:
|
||||
resolution: {integrity: sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA==}
|
||||
dev: true
|
||||
|
||||
/js-sdsl@4.3.0:
|
||||
resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==}
|
||||
dev: true
|
||||
|
@ -11670,7 +11682,7 @@ packages:
|
|||
dev: true
|
||||
|
||||
/object-assign@4.1.1:
|
||||
resolution: {integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=}
|
||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
|
@ -14628,6 +14640,10 @@ packages:
|
|||
resolution: {integrity: sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==}
|
||||
dev: true
|
||||
|
||||
/tiny-cookie@2.4.1:
|
||||
resolution: {integrity: sha512-h8ueaMyvUd/9ZfRqCfa1t+0tXqfVFhdK8WpLHz8VXMqsiaj3Sqg64AOCH/xevLQGZk0ZV+/75ouITdkvp3taVA==}
|
||||
dev: true
|
||||
|
||||
/tiny-glob@0.2.9:
|
||||
resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==}
|
||||
dependencies:
|
||||
|
|
Loading…
Reference in a new issue