0
Fork 0
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:
Charles Zhao 2022-05-13 17:12:45 +08:00 committed by GitHub
commit 58dba811d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 220 additions and 107 deletions

View file

@ -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]);
}, [location.pathname, navigate, sections]);
if (sections?.length === 0) {
return <LogtoLoading message="general.loading" />;
}
return (
<ErrorBoundary>

View file

@ -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',
},
],
},
];

View file

@ -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',
},
],
},
];
};

View file

@ -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,20 +14,24 @@ 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 }) => (
<Item
key={title}
titleKey={title}
icon={<Icon />}
isActive={location.pathname.startsWith(getPath(title))}
modal={modal}
/>
))}
{items.map(
({ title, Icon, isHidden, modal }) =>
!isHidden && (
<Item
key={title}
titleKey={title}
icon={<Icon />}
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';

View 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;

View file

@ -17,7 +17,7 @@ const GetStartedProgress = () => {
const [showDropDown, setShowDropdown] = useState(false);
const { data, completedCount, totalCount } = useGetStartedMetadata();
if (!configs) {
if (!configs || configs.hideGetStarted) {
return null;
}

View file

@ -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>
);
};

View file

@ -280,6 +280,8 @@ const translation = {
subtitle_part1: 'Here are the following things you can do',
subtitle_part2: 'Im 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.',

View file

@ -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快速创建和管理身份实现登录验证服务。',

View file

@ -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(),