diff --git a/packages/console/src/assets/images/cake.svg b/packages/console/src/assets/images/cake.svg deleted file mode 100644 index c63814846..000000000 --- a/packages/console/src/assets/images/cake.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/console/src/assets/images/check-demo.svg b/packages/console/src/assets/images/check-demo.svg new file mode 100644 index 000000000..afcb36ba0 --- /dev/null +++ b/packages/console/src/assets/images/check-demo.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/console/src/assets/images/circle-tick.svg b/packages/console/src/assets/images/circle-tick.svg new file mode 100644 index 000000000..d8e08b62a --- /dev/null +++ b/packages/console/src/assets/images/circle-tick.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/console/src/assets/images/crab.svg b/packages/console/src/assets/images/crab.svg deleted file mode 100644 index ad688cefa..000000000 --- a/packages/console/src/assets/images/crab.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/console/src/assets/images/create-app.svg b/packages/console/src/assets/images/create-app.svg new file mode 100644 index 000000000..287c529b3 --- /dev/null +++ b/packages/console/src/assets/images/create-app.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/console/src/assets/images/customize.svg b/packages/console/src/assets/images/customize.svg new file mode 100644 index 000000000..1ba5f81eb --- /dev/null +++ b/packages/console/src/assets/images/customize.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/console/src/assets/images/drink.svg b/packages/console/src/assets/images/drink.svg deleted file mode 100644 index 9886db5e2..000000000 --- a/packages/console/src/assets/images/drink.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/console/src/assets/images/frog.svg b/packages/console/src/assets/images/frog.svg deleted file mode 100644 index adbb4260a..000000000 --- a/packages/console/src/assets/images/frog.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/packages/console/src/assets/images/further-readings.svg b/packages/console/src/assets/images/further-readings.svg new file mode 100644 index 000000000..1f10daddd --- /dev/null +++ b/packages/console/src/assets/images/further-readings.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/console/src/assets/images/grinning-face.svg b/packages/console/src/assets/images/grinning-face.svg deleted file mode 100644 index 48b050ccf..000000000 --- a/packages/console/src/assets/images/grinning-face.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/packages/console/src/assets/images/one-click.svg b/packages/console/src/assets/images/one-click.svg new file mode 100644 index 000000000..bd9b1a311 --- /dev/null +++ b/packages/console/src/assets/images/one-click.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/packages/console/src/assets/images/owl.svg b/packages/console/src/assets/images/owl.svg deleted file mode 100644 index 9b1b58bc5..000000000 --- a/packages/console/src/assets/images/owl.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/packages/console/src/assets/images/passwordless.svg b/packages/console/src/assets/images/passwordless.svg new file mode 100644 index 000000000..355d14aac --- /dev/null +++ b/packages/console/src/assets/images/passwordless.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/console/src/assets/images/tada.svg b/packages/console/src/assets/images/tada.svg index 596ec2011..330a9136e 100644 --- a/packages/console/src/assets/images/tada.svg +++ b/packages/console/src/assets/images/tada.svg @@ -1,23 +1,29 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/packages/console/src/components/AppContent/components/Topbar/index.module.scss b/packages/console/src/components/AppContent/components/Topbar/index.module.scss index 251746f30..982c1a1a8 100644 --- a/packages/console/src/components/AppContent/components/Topbar/index.module.scss +++ b/packages/console/src/components/AppContent/components/Topbar/index.module.scss @@ -3,7 +3,7 @@ .topbar { flex: 0 0 64px; width: 100%; - padding: _.unit(4) _.unit(6); + padding: 0 _.unit(6); display: flex; align-items: center; diff --git a/packages/console/src/components/AppContent/components/Topbar/index.tsx b/packages/console/src/components/AppContent/components/Topbar/index.tsx index cf03db23c..478ddb22e 100644 --- a/packages/console/src/components/AppContent/components/Topbar/index.tsx +++ b/packages/console/src/components/AppContent/components/Topbar/index.tsx @@ -1,7 +1,9 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; +import Spacer from '@/components/Spacer'; import Logo from '@/icons/Logo'; +import GetStartedProgress from '@/pages/GetStarted/components/GetStartedProgress'; import * as styles from './index.module.scss'; @@ -13,6 +15,8 @@ const Topbar = () => {
{t('admin_console.title')}
+ +
); }; diff --git a/packages/console/src/components/AppContent/index.module.scss b/packages/console/src/components/AppContent/index.module.scss index 8bee59453..163a21ebe 100644 --- a/packages/console/src/components/AppContent/index.module.scss +++ b/packages/console/src/components/AppContent/index.module.scss @@ -10,7 +10,7 @@ .content { flex-grow: 1; display: flex; - padding-right: _.unit(5); + padding-right: _.unit(6); margin-bottom: _.unit(6); overflow: hidden; diff --git a/packages/console/src/components/Dropdown/DropdownItem.tsx b/packages/console/src/components/Dropdown/DropdownItem.tsx index 159f4d42c..824ae30d0 100644 --- a/packages/console/src/components/Dropdown/DropdownItem.tsx +++ b/packages/console/src/components/Dropdown/DropdownItem.tsx @@ -5,19 +5,23 @@ import * as styles from './DropdownItem.module.scss'; type Props = { onClick?: () => void; + className?: string; children: React.ReactNode; icon?: React.ReactNode; + iconClassName?: string; type?: 'default' | 'danger'; }; -const DropdownItem = ({ onClick, children, icon, type = 'default' }: Props) => ( -
  • { - onClick?.(); - }} - > - {icon && {icon}} +const DropdownItem = ({ + onClick, + className, + children, + icon, + iconClassName, + type = 'default', +}: Props) => ( +
  • + {icon && {icon}} {children}
  • ); diff --git a/packages/console/src/components/Dropdown/index.module.scss b/packages/console/src/components/Dropdown/index.module.scss index 7e1eb30e2..91c8bae54 100644 --- a/packages/console/src/components/Dropdown/index.module.scss +++ b/packages/console/src/components/Dropdown/index.module.scss @@ -13,12 +13,12 @@ &:focus { outline: none; } +} - .title { - padding: _.unit(4) _.unit(3) 0 _.unit(3); - font: var(--font-subhead-cap-small); - color: var(--color-caption); - } +.title { + padding: _.unit(4) _.unit(3) 0 _.unit(3); + font: var(--font-subhead-cap-small); + color: var(--color-caption); } .overlay { @@ -27,7 +27,7 @@ inset: 0; } -ul.list { +.list { margin: 0; padding: _.unit(2) _.unit(1); } diff --git a/packages/console/src/components/Dropdown/index.tsx b/packages/console/src/components/Dropdown/index.tsx index 5654e927f..eb56b5c10 100644 --- a/packages/console/src/components/Dropdown/index.tsx +++ b/packages/console/src/components/Dropdown/index.tsx @@ -3,7 +3,7 @@ import React, { ReactNode, RefObject, useRef } from 'react'; import ReactModal from 'react-modal'; import * as styles from './index.module.scss'; -import usePosition from './use-position'; +import usePosition, { HorizontalAlignment } from './use-position'; export { default as DropdownItem } from './DropdownItem'; @@ -15,6 +15,8 @@ type Props = { anchorRef: RefObject; isFullWidth?: boolean; className?: string; + titleClassName?: string; + horizontalAlign?: HorizontalAlignment; }; const Dropdown = ({ @@ -25,10 +27,12 @@ const Dropdown = ({ anchorRef, isFullWidth, className, + titleClassName, + horizontalAlign, }: Props) => { const overlayRef = useRef(null); - const { position, mutate } = usePosition(anchorRef, overlayRef); + const { position, mutate } = usePosition(anchorRef, overlayRef, horizontalAlign); return (
    - {title &&
    {title}
    } -
      + {title &&
      {title}
      } +
        {children}
    diff --git a/packages/console/src/components/Dropdown/use-position.ts b/packages/console/src/components/Dropdown/use-position.ts index 209f58af2..6f8fd21e6 100644 --- a/packages/console/src/components/Dropdown/use-position.ts +++ b/packages/console/src/components/Dropdown/use-position.ts @@ -7,6 +7,8 @@ type Position = { isOnTop?: boolean; }; +export type HorizontalAlignment = 'left' | 'right'; + // Leave space for box-shadow effect. const safePadding = 12; // The distance to anchor @@ -14,9 +16,11 @@ const distance = 4; export default function usePosition( anchorRef: RefObject, - overlayRef: RefObject + overlayRef: RefObject, + horizontalAlign: HorizontalAlignment = 'left' ) { const [position, setPosition] = useState(); + const isRightAligned = horizontalAlign === 'right'; const updatePosition = useCallback(() => { if (anchorRef.current && overlayRef.current) { @@ -24,13 +28,13 @@ export default function usePosition( const overlay = overlayRef.current.getBoundingClientRect(); const isOnTop = anchor.y + anchor.height + overlay.height > window.innerHeight - safePadding; const isOnLeft = anchor.x + overlay.width > window.innerWidth - safePadding; - const left = isOnLeft ? anchor.x + anchor.width - overlay.width : anchor.x; + const left = isOnLeft || isRightAligned ? anchor.x + anchor.width - overlay.width : anchor.x; const top = isOnTop ? anchor.y - overlay.height - distance : anchor.y + anchor.height + distance; setPosition({ left, top, width: anchor.width, isOnTop }); } - }, [anchorRef, overlayRef]); + }, [anchorRef, isRightAligned, overlayRef]); useLayoutEffect(() => { updatePosition(); diff --git a/packages/console/src/components/Index/index.module.scss b/packages/console/src/components/Index/index.module.scss new file mode 100644 index 000000000..b51e0cde1 --- /dev/null +++ b/packages/console/src/components/Index/index.module.scss @@ -0,0 +1,26 @@ +@use '@/scss/underscore' as _; + +.container { + display: flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + border-radius: 50%; + color: var(--color-primary); + background: var(--color-surface-5); + font: var(--font-title-medium); + + &.active { + color: var(--color-on-primary); + background: var(--color-primary); + } + + &.completed { + background: var(--color-primary); + + > svg { + fill: var(--color-on-primary); + } + } +} diff --git a/packages/console/src/components/Index/index.tsx b/packages/console/src/components/Index/index.tsx new file mode 100644 index 000000000..b06e1fb8d --- /dev/null +++ b/packages/console/src/components/Index/index.tsx @@ -0,0 +1,28 @@ +import classNames from 'classnames'; +import React from 'react'; + +import Tick from '@/icons/Tick'; + +import * as styles from './index.module.scss'; + +type Props = { + className?: string; + index: number; + isActive?: boolean; + isComplete?: boolean; +}; + +const Index = ({ className, index, isActive, isComplete }: Props) => ( +
    + {isComplete ? : index} +
    +); + +export default Index; diff --git a/packages/console/src/mdx-components/Step/index.module.scss b/packages/console/src/mdx-components/Step/index.module.scss index d958275f5..748b1701c 100644 --- a/packages/console/src/mdx-components/Step/index.module.scss +++ b/packages/console/src/mdx-components/Step/index.module.scss @@ -17,29 +17,7 @@ } .index { - display: flex; - align-items: center; - justify-content: center; - width: 28px; - height: 28px; - border-radius: 50%; - color: var(--color-primary); - background: var(--color-surface-5); - font: var(--font-title-medium); margin-right: _.unit(4); - - &.active { - color: var(--color-on-primary); - background: var(--color-primary); - } - - &.completed { - background: var(--color-primary); - - > svg { - fill: var(--color-on-primary); - } - } } } diff --git a/packages/console/src/mdx-components/Step/index.tsx b/packages/console/src/mdx-components/Step/index.tsx index d77e6b789..1baf8eae6 100644 --- a/packages/console/src/mdx-components/Step/index.tsx +++ b/packages/console/src/mdx-components/Step/index.tsx @@ -8,9 +8,9 @@ import Card from '@/components/Card'; import CardTitle from '@/components/CardTitle'; import DangerousRaw from '@/components/DangerousRaw'; import IconButton from '@/components/IconButton'; +import Index from '@/components/Index'; import Spacer from '@/components/Spacer'; import { ArrowDown, ArrowUp } from '@/icons/Arrow'; -import Tick from '@/icons/Tick'; import * as styles from './index.module.scss'; @@ -62,15 +62,12 @@ const Step = ({ setIsExpanded(!isExpanded); }} > -
    - {isComplete ? : index + 1} -
    + {title}} diff --git a/packages/console/src/pages/GetStarted/components/GetStartedProgress/index.module.scss b/packages/console/src/pages/GetStarted/components/GetStartedProgress/index.module.scss new file mode 100644 index 000000000..207a2393b --- /dev/null +++ b/packages/console/src/pages/GetStarted/components/GetStartedProgress/index.module.scss @@ -0,0 +1,51 @@ +@use '@/scss/underscore' as _; + +.container { + display: flex; + align-items: center; + padding: _.unit(2); + border-radius: 8px; + transition: background-color 0.2s ease-in-out; + user-select: none; + cursor: pointer; + + &:hover, + &.active { + background-color: var(--color-surface-5); + } + + img { + width: 24px; + height: 24px; + margin-right: 8px; + } + + span { + font: var(--font-subhead-2); + color: var(--color-text); + } +} + +.dropdownTitle { + padding: _.unit(5) _.unit(5) _.unit(3) _.unit(4); + font: var(--font-subhead-cap); + text-transform: uppercase; + letter-spacing: 0.1em; +} + +.dropdown { + padding: 0; +} + +.index { + width: 20px; + height: 20px; + font: var(--font-subhead-cap); +} + +.dropdownItem { + height: 44px; + padding: 0 _.unit(5) 0 _.unit(4); + border-top: 1px solid var(--color-neutral-95); + border-radius: unset; +} diff --git a/packages/console/src/pages/GetStarted/components/GetStartedProgress/index.tsx b/packages/console/src/pages/GetStarted/components/GetStartedProgress/index.tsx new file mode 100644 index 000000000..e149bcd99 --- /dev/null +++ b/packages/console/src/pages/GetStarted/components/GetStartedProgress/index.tsx @@ -0,0 +1,67 @@ +import classNames from 'classnames'; +import React, { useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import icon from '@/assets/images/tada.svg'; +import Dropdown, { DropdownItem } from '@/components/Dropdown'; +import Index from '@/components/Index'; +import useConfigs from '@/hooks/use-configs'; + +import useGetStartedMetadata from '../../hook'; +import * as styles from './index.module.scss'; + +const GetStartedProgress = () => { + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + const { configs } = useConfigs(); + const anchorRef = useRef(null); + const [showDropDown, setShowDropdown] = useState(false); + const { data, completedCount, totalCount } = useGetStartedMetadata(); + + if (!configs) { + return null; + } + + return ( + <> +
    { + setShowDropdown(true); + }} + > + + + {t('get_started.progress', { + completed: completedCount, + total: totalCount, + })} + +
    + { + setShowDropdown(false); + }} + > + {data.map(({ id, title, isComplete, onClick }, index) => ( + } + onClick={onClick} + > + {t(title)} + + ))} + + + ); +}; + +export default GetStartedProgress; diff --git a/packages/console/src/pages/GetStarted/hook.ts b/packages/console/src/pages/GetStarted/hook.ts new file mode 100644 index 000000000..37d2981f3 --- /dev/null +++ b/packages/console/src/pages/GetStarted/hook.ts @@ -0,0 +1,103 @@ +import { AdminConsoleKey, I18nKey } from '@logto/phrases'; +import { useNavigate } from 'react-router-dom'; + +import checkDemoIcon from '@/assets/images/check-demo.svg'; +import createAppIcon from '@/assets/images/create-app.svg'; +import customizeIcon from '@/assets/images/customize.svg'; +import furtherReadingsIcon from '@/assets/images/further-readings.svg'; +import oneClickIcon from '@/assets/images/one-click.svg'; +import passwordlessIcon from '@/assets/images/passwordless.svg'; +import useAdminConsoleConfigs from '@/hooks/use-configs'; + +type GetStartedMetadata = { + id: string; + title: AdminConsoleKey; + subtitle: AdminConsoleKey; + icon: string; + buttonText: I18nKey; + isComplete?: boolean; + onClick: () => void; +}; + +const useGetStartedMetadata = () => { + const { configs, updateConfigs } = useAdminConsoleConfigs(); + const navigate = useNavigate(); + + const data: GetStartedMetadata[] = [ + { + id: 'checkDemo', + title: 'get_started.card1_title', + subtitle: 'get_started.card1_subtitle', + icon: checkDemoIcon, + buttonText: 'general.check_out', + isComplete: configs?.checkDemo, + onClick: async () => { + void updateConfigs({ checkDemo: true }); + window.open('https://logto.io/', '_blank'); + }, + }, + { + id: 'createApplication', + title: 'get_started.card2_title', + subtitle: 'get_started.card2_subtitle', + icon: createAppIcon, + buttonText: 'general.create', + isComplete: configs?.createApplication, + onClick: () => { + navigate('/applications'); + }, + }, + { + id: 'configurePasswordless', + title: 'get_started.card3_title', + subtitle: 'get_started.card3_subtitle', + icon: passwordlessIcon, + buttonText: 'general.create', + isComplete: configs?.configurePasswordless, + onClick: () => { + navigate('/connectors'); + }, + }, + { + id: 'configureSocialSignIn', + title: 'get_started.card4_title', + subtitle: 'get_started.card4_subtitle', + icon: oneClickIcon, + buttonText: 'general.set_up', + onClick: () => { + navigate('/connectors/social'); + }, + }, + { + id: 'customizeSignInExperience', + title: 'get_started.card5_title', + subtitle: 'get_started.card5_subtitle', + icon: customizeIcon, + buttonText: 'general.customize', + isComplete: configs?.customizeSignInExperience, + onClick: () => { + navigate('/sign-in-experience'); + }, + }, + { + id: 'checkFurtherReadings', + title: 'get_started.card6_title', + subtitle: 'get_started.card6_subtitle', + icon: furtherReadingsIcon, + buttonText: 'general.check_out', + isComplete: configs?.checkFurtherReadings, + onClick: () => { + void updateConfigs({ checkFurtherReadings: true }); + window.open('https://docs.logto.io/', '_blank'); + }, + }, + ]; + + return { + data, + completedCount: data.filter(({ isComplete }) => isComplete).length, + totalCount: data.length, + }; +}; + +export default useGetStartedMetadata; diff --git a/packages/console/src/pages/GetStarted/index.module.scss b/packages/console/src/pages/GetStarted/index.module.scss index 146dd5080..b86a4b322 100644 --- a/packages/console/src/pages/GetStarted/index.module.scss +++ b/packages/console/src/pages/GetStarted/index.module.scss @@ -32,6 +32,7 @@ .card { display: flex; padding: _.unit(6) _.unit(8); + position: relative; .icon { width: 48px; @@ -39,6 +40,12 @@ margin-right: _.unit(6); } + .completeIndicator { + position: absolute; + top: 53px; + left: 70px; + } + .wrapper { flex: 1; display: flex; diff --git a/packages/console/src/pages/GetStarted/index.tsx b/packages/console/src/pages/GetStarted/index.tsx index ef5572eb5..8e2d6f258 100644 --- a/packages/console/src/pages/GetStarted/index.tsx +++ b/packages/console/src/pages/GetStarted/index.tsx @@ -1,90 +1,17 @@ -import { AdminConsoleKey, I18nKey } from '@logto/phrases'; import React from 'react'; import { useTranslation } from 'react-i18next'; -import { useNavigate } from 'react-router-dom'; -import cakeIcon from '@/assets/images/cake.svg'; -import crabIcon from '@/assets/images/crab.svg'; -import drinkIcon from '@/assets/images/drink.svg'; -import frogIcon from '@/assets/images/frog.svg'; -import grinningFaceIcon from '@/assets/images/grinning-face.svg'; -import owlIcon from '@/assets/images/owl.svg'; +import completeIndicator from '@/assets/images/circle-tick.svg'; import Button from '@/components/Button'; import Card from '@/components/Card'; import Spacer from '@/components/Spacer'; -import useAdminConsoleConfigs from '@/hooks/use-configs'; +import useGetStartedMetadata from './hook'; import * as styles from './index.module.scss'; const GetStarted = () => { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - const { updateConfigs } = useAdminConsoleConfigs(); - const navigate = useNavigate(); - - const data: Array<{ - title: AdminConsoleKey; - subtitle: AdminConsoleKey; - icon: string; - buttonText: I18nKey; - onClick: () => void; - }> = [ - { - title: 'get_started.card1_title', - subtitle: 'get_started.card1_subtitle', - icon: grinningFaceIcon, - buttonText: 'general.check_out', - onClick: async () => { - void updateConfigs({ checkDemo: true }); - window.open('https://fake.demo.com', '_blank'); - }, - }, - { - title: 'get_started.card2_title', - subtitle: 'get_started.card2_subtitle', - icon: cakeIcon, - buttonText: 'general.create', - onClick: () => { - navigate('/applications'); - }, - }, - { - title: 'get_started.card3_title', - subtitle: 'get_started.card3_subtitle', - icon: drinkIcon, - buttonText: 'general.create', - onClick: () => { - navigate('/connectors'); - }, - }, - { - title: 'get_started.card4_title', - subtitle: 'get_started.card4_subtitle', - icon: crabIcon, - buttonText: 'general.set_up', - onClick: () => { - navigate('/connectors/social'); - }, - }, - { - title: 'get_started.card5_title', - subtitle: 'get_started.card5_subtitle', - icon: owlIcon, - buttonText: 'general.customize', - onClick: () => { - navigate('/sign-in-experience'); - }, - }, - { - title: 'get_started.card6_title', - subtitle: 'get_started.card6_subtitle', - icon: frogIcon, - buttonText: 'general.check_out', - onClick: () => { - void updateConfigs({ checkFurtherReadings: true }); - window.open('https://further.readings.com', '_blank'); - }, - }, - ]; + const { data } = useGetStartedMetadata(); return (
    @@ -99,9 +26,10 @@ const GetStarted = () => {
    - {data.map(({ title, subtitle, icon, buttonText, onClick }) => ( - + {data.map(({ id, title, subtitle, icon, isComplete, buttonText, onClick }) => ( + + {isComplete && }
    {t(title)}
    {t(subtitle)}
    diff --git a/packages/phrases/src/locales/en.ts b/packages/phrases/src/locales/en.ts index 5f8b17915..02feeca43 100644 --- a/packages/phrases/src/locales/en.ts +++ b/packages/phrases/src/locales/en.ts @@ -273,6 +273,8 @@ const translation = { connector_deleted: 'The connector has been deleted.', }, get_started: { + progress: 'Get started guide: {{completed}}/{{total}}', + progress_dropdown_title: 'A few things you can do...', title: 'How do you want to get started with Logto?', subtitle_part1: 'Here are the following things you can do', subtitle_part2: 'I’m done with this set up. ', diff --git a/packages/phrases/src/locales/zh-cn.ts b/packages/phrases/src/locales/zh-cn.ts index 8b72a1f18..f1ad67655 100644 --- a/packages/phrases/src/locales/zh-cn.ts +++ b/packages/phrases/src/locales/zh-cn.ts @@ -270,6 +270,8 @@ const translation = { connector_deleted: '成功删除连接器。', }, get_started: { + progress: '开始使用: {{completed}}/{{total}}', + progress_dropdown_title: '快速上手', title: '还不知道如何使用 Logto?', subtitle_part1: '下列是一些适合您快速上手的事情', subtitle_part2: '这配置页面我要看吐了,',