mirror of
https://github.com/logto-io/logto.git
synced 2025-01-06 20:40:08 -05:00
Merge pull request #808 from logto-io/charles-log-2416-hide-get-started-page
feat(console): hide get-started page on clicking 'Hide this' button
This commit is contained in:
commit
58dba811d8
10 changed files with 220 additions and 107 deletions
|
@ -7,8 +7,10 @@ import './scss/normalized.scss';
|
|||
|
||||
import * as styles from './App.module.scss';
|
||||
import AppContent from './components/AppContent';
|
||||
import { getPath, sections } from './components/AppContent/components/Sidebar';
|
||||
import { getPath } from './components/AppContent/components/Sidebar';
|
||||
import { useSidebarMenuItems } from './components/AppContent/components/Sidebar/hook';
|
||||
import ErrorBoundary from './components/ErrorBoundary';
|
||||
import LogtoLoading from './components/LogtoLoading';
|
||||
import Toast from './components/Toast';
|
||||
import { themeStorageKey, logtoApiResource } from './consts';
|
||||
import { RequestError } from './hooks/use-api';
|
||||
|
@ -45,6 +47,8 @@ const Main = () => {
|
|||
settingsFetcher
|
||||
);
|
||||
|
||||
const sections = useSidebarMenuItems();
|
||||
|
||||
useEffect(() => {
|
||||
const theme = data?.adminConsole.appearanceMode ?? defaultTheme;
|
||||
const isFollowSystem = theme === AppearanceMode.SyncWithSystem;
|
||||
|
@ -69,9 +73,13 @@ const Main = () => {
|
|||
|
||||
useEffect(() => {
|
||||
if (location.pathname === '/') {
|
||||
navigate(getPath(sections[0]?.items[0]?.title ?? ''));
|
||||
navigate(getPath(sections?.[0]?.items[0]?.title ?? ''));
|
||||
}
|
||||
}, [location.pathname, navigate, sections]);
|
||||
|
||||
if (sections?.length === 0) {
|
||||
return <LogtoLoading message="general.loading" />;
|
||||
}
|
||||
}, [location.pathname, navigate]);
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
import React, { FC, ReactNode } from 'react';
|
||||
import { TFuncKey } from 'react-i18next';
|
||||
|
||||
import Contact from './components/Contact';
|
||||
import BarGraph from './icons/BarGraph';
|
||||
import Bolt from './icons/Bolt';
|
||||
import Box from './icons/Box';
|
||||
import Cloud from './icons/Cloud';
|
||||
import Connection from './icons/Connection';
|
||||
import ContactIcon from './icons/Contact';
|
||||
import Document from './icons/Document';
|
||||
import List from './icons/List';
|
||||
import UserProfile from './icons/UserProfile';
|
||||
import Web from './icons/Web';
|
||||
|
||||
type SidebarItem = {
|
||||
Icon: FC;
|
||||
title: TFuncKey<'translation', 'admin_console.tabs'>;
|
||||
modal?: (isOpen: boolean, onCancel: () => void) => ReactNode;
|
||||
};
|
||||
|
||||
type SidebarSection = {
|
||||
title: TFuncKey<'translation', 'admin_console.tab_sections'>;
|
||||
items: SidebarItem[];
|
||||
};
|
||||
|
||||
export const sections: SidebarSection[] = [
|
||||
{
|
||||
title: 'overview',
|
||||
items: [
|
||||
{
|
||||
Icon: Bolt,
|
||||
title: 'get_started',
|
||||
},
|
||||
{
|
||||
Icon: BarGraph,
|
||||
title: 'dashboard',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'resource_management',
|
||||
items: [
|
||||
{
|
||||
Icon: Box,
|
||||
title: 'applications',
|
||||
},
|
||||
{
|
||||
Icon: Cloud,
|
||||
title: 'api_resources',
|
||||
},
|
||||
{
|
||||
Icon: Web,
|
||||
title: 'sign_in_experience',
|
||||
},
|
||||
{
|
||||
Icon: Connection,
|
||||
title: 'connectors',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'user_management',
|
||||
items: [
|
||||
{
|
||||
Icon: UserProfile,
|
||||
title: 'users',
|
||||
},
|
||||
{
|
||||
Icon: List,
|
||||
title: 'audit_logs',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'help_and_support',
|
||||
items: [
|
||||
{
|
||||
Icon: ContactIcon,
|
||||
title: 'contact_us',
|
||||
modal: (isOpen, onCancel) => <Contact isOpen={isOpen} onCancel={onCancel} />,
|
||||
},
|
||||
{
|
||||
Icon: Document,
|
||||
title: 'documentation',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
|
@ -0,0 +1,101 @@
|
|||
import React, { FC, ReactNode } from 'react';
|
||||
import { TFuncKey } from 'react-i18next';
|
||||
|
||||
import useAdminConsoleConfigs from '@/hooks/use-configs';
|
||||
|
||||
import Contact from './components/Contact';
|
||||
import BarGraph from './icons/BarGraph';
|
||||
import Bolt from './icons/Bolt';
|
||||
import Box from './icons/Box';
|
||||
import Cloud from './icons/Cloud';
|
||||
import Connection from './icons/Connection';
|
||||
import ContactIcon from './icons/Contact';
|
||||
import Document from './icons/Document';
|
||||
import List from './icons/List';
|
||||
import UserProfile from './icons/UserProfile';
|
||||
import Web from './icons/Web';
|
||||
|
||||
type SidebarItem = {
|
||||
Icon: FC;
|
||||
title: TFuncKey<'translation', 'admin_console.tabs'>;
|
||||
isHidden?: boolean;
|
||||
modal?: (isOpen: boolean, onCancel: () => void) => ReactNode;
|
||||
};
|
||||
|
||||
type SidebarSection = {
|
||||
title: TFuncKey<'translation', 'admin_console.tab_sections'>;
|
||||
items: SidebarItem[];
|
||||
};
|
||||
|
||||
export const useSidebarMenuItems = (): SidebarSection[] | undefined => {
|
||||
const { configs } = useAdminConsoleConfigs();
|
||||
|
||||
if (!configs) {
|
||||
return;
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
title: 'overview',
|
||||
items: [
|
||||
{
|
||||
Icon: Bolt,
|
||||
title: 'get_started',
|
||||
isHidden: configs.hideGetStarted,
|
||||
},
|
||||
{
|
||||
Icon: BarGraph,
|
||||
title: 'dashboard',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'resource_management',
|
||||
items: [
|
||||
{
|
||||
Icon: Box,
|
||||
title: 'applications',
|
||||
},
|
||||
{
|
||||
Icon: Cloud,
|
||||
title: 'api_resources',
|
||||
},
|
||||
{
|
||||
Icon: Web,
|
||||
title: 'sign_in_experience',
|
||||
},
|
||||
{
|
||||
Icon: Connection,
|
||||
title: 'connectors',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'user_management',
|
||||
items: [
|
||||
{
|
||||
Icon: UserProfile,
|
||||
title: 'users',
|
||||
},
|
||||
{
|
||||
Icon: List,
|
||||
title: 'audit_logs',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'help_and_support',
|
||||
items: [
|
||||
{
|
||||
Icon: ContactIcon,
|
||||
title: 'contact_us',
|
||||
modal: (isOpen, onCancel) => <Contact isOpen={isOpen} onCancel={onCancel} />,
|
||||
},
|
||||
{
|
||||
Icon: Document,
|
||||
title: 'documentation',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
};
|
|
@ -4,7 +4,7 @@ import { useLocation } from 'react-router-dom';
|
|||
|
||||
import Item from './components/Item';
|
||||
import Section from './components/Section';
|
||||
import { sections } from './consts';
|
||||
import { useSidebarMenuItems } from './hook';
|
||||
import Gear from './icons/Gear';
|
||||
import * as styles from './index.module.scss';
|
||||
import { getPath } from './utils';
|
||||
|
@ -14,12 +14,15 @@ const Sidebar = () => {
|
|||
keyPrefix: 'admin_console.tab_sections',
|
||||
});
|
||||
const location = useLocation();
|
||||
const sections = useSidebarMenuItems();
|
||||
|
||||
return (
|
||||
<div className={styles.sidebar}>
|
||||
{sections.map(({ title, items }) => (
|
||||
{sections?.map(({ title, items }) => (
|
||||
<Section key={title} title={t(title)}>
|
||||
{items.map(({ title, Icon, modal }) => (
|
||||
{items.map(
|
||||
({ title, Icon, isHidden, modal }) =>
|
||||
!isHidden && (
|
||||
<Item
|
||||
key={title}
|
||||
titleKey={title}
|
||||
|
@ -27,7 +30,8 @@ const Sidebar = () => {
|
|||
isActive={location.pathname.startsWith(getPath(title))}
|
||||
modal={modal}
|
||||
/>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</Section>
|
||||
))}
|
||||
<div className={styles.spacer} />
|
||||
|
@ -42,5 +46,4 @@ const Sidebar = () => {
|
|||
|
||||
export default Sidebar;
|
||||
|
||||
export * from './consts';
|
||||
export * from './utils';
|
||||
|
|
55
packages/console/src/components/ConfirmModal/index.tsx
Normal file
55
packages/console/src/components/ConfirmModal/index.tsx
Normal file
|
@ -0,0 +1,55 @@
|
|||
import { AdminConsoleKey, I18nKey } from '@logto/phrases';
|
||||
import React from 'react';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import * as modalStyles from '@/scss/modal.module.scss';
|
||||
|
||||
import Button from '../Button';
|
||||
import ModalLayout from '../ModalLayout';
|
||||
|
||||
type Props = {
|
||||
title: AdminConsoleKey;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
confirmButtonText?: I18nKey;
|
||||
cancelButtonText?: I18nKey;
|
||||
isOpen: boolean;
|
||||
isPending?: boolean;
|
||||
onConfirm: () => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
|
||||
const ConfirmModal = ({
|
||||
title,
|
||||
children,
|
||||
className,
|
||||
confirmButtonText = 'general.confirm',
|
||||
cancelButtonText = 'general.cancel',
|
||||
isOpen,
|
||||
isPending,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
}: Props) => (
|
||||
<Modal isOpen={isOpen} className={modalStyles.content} overlayClassName={modalStyles.overlay}>
|
||||
<ModalLayout
|
||||
title={title}
|
||||
footer={
|
||||
<>
|
||||
<Button type="outline" title={cancelButtonText} onClick={onCancel} />
|
||||
<Button
|
||||
isLoading={isPending}
|
||||
type="primary"
|
||||
title={confirmButtonText}
|
||||
onClick={onConfirm}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
className={className}
|
||||
onClose={onCancel}
|
||||
>
|
||||
{children}
|
||||
</ModalLayout>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
export default ConfirmModal;
|
|
@ -17,7 +17,7 @@ const GetStartedProgress = () => {
|
|||
const [showDropDown, setShowDropdown] = useState(false);
|
||||
const { data, completedCount, totalCount } = useGetStartedMetadata();
|
||||
|
||||
if (!configs) {
|
||||
if (!configs || configs.hideGetStarted) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,29 @@
|
|||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import completeIndicator from '@/assets/images/circle-tick.svg';
|
||||
import Button from '@/components/Button';
|
||||
import Card from '@/components/Card';
|
||||
import ConfirmModal from '@/components/ConfirmModal';
|
||||
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 navigate = useNavigate();
|
||||
const { data } = useGetStartedMetadata();
|
||||
const { updateConfigs } = useAdminConsoleConfigs();
|
||||
const [showConfirmModal, setShowConfirmModal] = useState(false);
|
||||
|
||||
const hideGetStarted = () => {
|
||||
void updateConfigs({ hideGetStarted: true });
|
||||
// Navigate to next menu item
|
||||
navigate('/dashboard');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
|
@ -22,7 +34,14 @@ const GetStarted = () => {
|
|||
<Spacer />
|
||||
<span>
|
||||
{t('get_started.subtitle_part2')}
|
||||
<span className={styles.hideButton}>{t('get_started.hide_this')}</span>
|
||||
<span
|
||||
className={styles.hideButton}
|
||||
onClick={() => {
|
||||
setShowConfirmModal(true);
|
||||
}}
|
||||
>
|
||||
{t('get_started.hide_this')}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -43,6 +62,17 @@ const GetStarted = () => {
|
|||
/>
|
||||
</Card>
|
||||
))}
|
||||
<ConfirmModal
|
||||
title="get_started.confirm"
|
||||
isOpen={showConfirmModal}
|
||||
confirmButtonText="admin_console.get_started.hide_this"
|
||||
onConfirm={hideGetStarted}
|
||||
onCancel={() => {
|
||||
setShowConfirmModal(false);
|
||||
}}
|
||||
>
|
||||
{t('get_started.confirm_message')}
|
||||
</ConfirmModal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -280,6 +280,8 @@ const translation = {
|
|||
subtitle_part1: 'Here are the following things you can do',
|
||||
subtitle_part2: 'I’m done with this set up. ',
|
||||
hide_this: 'Hide this',
|
||||
confirm: 'Reminder',
|
||||
confirm_message: 'Are you sure you want to hide this page? This action cannot be undone.',
|
||||
card1_title: 'Logto is a XXX for customer identity. Check out our demo',
|
||||
card1_subtitle:
|
||||
'Setup a mobile, single page or traditional application to use Logto for Authentication.',
|
||||
|
|
|
@ -277,6 +277,8 @@ const translation = {
|
|||
subtitle_part1: '下列是一些适合您快速上手的事情',
|
||||
subtitle_part2: '这配置页面我要看吐了,',
|
||||
hide_this: '退下!',
|
||||
confirm: '确认提醒',
|
||||
confirm_message: '您确认要隐藏该页面吗? 本操作将无法恢复。',
|
||||
card1_title: 'Logto 是您没有使用过的全新身份管理工具,来看看我们的 Demo 吧',
|
||||
card1_subtitle:
|
||||
'无论是 mobile 应用,SPA web 还是传统 web 应用,您都可以使用 Logto,快速创建和管理身份,实现登录验证服务。',
|
||||
|
|
|
@ -145,6 +145,7 @@ export const adminConsoleConfigGuard = z.object({
|
|||
appearanceMode: z.nativeEnum(AppearanceMode),
|
||||
experienceNoticeConfirmed: z.boolean().optional(),
|
||||
experienceGuideDone: z.boolean().optional(),
|
||||
hideGetStarted: z.boolean().optional(),
|
||||
// Get started challenges
|
||||
checkDemo: z.boolean(),
|
||||
createApplication: z.boolean(),
|
||||
|
|
Loading…
Reference in a new issue