From 4606fd68216374cd2bdf57c2d2516805fa9cf4f9 Mon Sep 17 00:00:00 2001 From: Xiao Yijun Date: Thu, 2 Mar 2023 17:06:46 +0800 Subject: [PATCH] feat(console): application table placeholder (#3261) --- .../ApplicationsPlaceholder/index.module.scss | 42 +++++++ .../ApplicationsPlaceholder/index.tsx | 105 ++++++++++++++++++ .../components/CreateForm/index.tsx | 2 +- .../TypeDescription/index.module.scss | 48 +++++--- .../components/TypeDescription/index.tsx | 20 ++-- .../console/src/pages/Applications/index.tsx | 20 +--- .../translation/admin-console/applications.ts | 3 + .../translation/admin-console/applications.ts | 3 + .../translation/admin-console/applications.ts | 3 + .../translation/admin-console/applications.ts | 3 + .../translation/admin-console/applications.ts | 3 + .../translation/admin-console/applications.ts | 3 + .../translation/admin-console/applications.ts | 3 + .../translation/admin-console/applications.ts | 3 + 14 files changed, 215 insertions(+), 46 deletions(-) create mode 100644 packages/console/src/pages/Applications/components/ApplicationsPlaceholder/index.module.scss create mode 100644 packages/console/src/pages/Applications/components/ApplicationsPlaceholder/index.tsx diff --git a/packages/console/src/pages/Applications/components/ApplicationsPlaceholder/index.module.scss b/packages/console/src/pages/Applications/components/ApplicationsPlaceholder/index.module.scss new file mode 100644 index 000000000..f97b6274e --- /dev/null +++ b/packages/console/src/pages/Applications/components/ApplicationsPlaceholder/index.module.scss @@ -0,0 +1,42 @@ +@use '@/scss/underscore' as _; + +.placeholder { + display: flex; + flex-direction: column; + align-items: center; + padding-bottom: _.unit(5); + + .title { + font: var(--font-title-1); + } + + .description { + font: var(--font-body-2); + color: var(--color-text-secondary); + margin-top: _.unit(1); + text-align: center; + max-width: 600px; + } + + .options { + margin-top: _.unit(6); + display: flex; + justify-content: space-between; + align-items: stretch; + max-width: 736px; + gap: _.unit(4); + + .option { + flex: 1; + display: flex; + flex-direction: column; + border: 1px solid var(--color-divider); + border-radius: 12px; + padding: _.unit(3); + + .createButton { + margin-top: _.unit(2.5); + } + } + } +} diff --git a/packages/console/src/pages/Applications/components/ApplicationsPlaceholder/index.tsx b/packages/console/src/pages/Applications/components/ApplicationsPlaceholder/index.tsx new file mode 100644 index 000000000..88bb470d0 --- /dev/null +++ b/packages/console/src/pages/Applications/components/ApplicationsPlaceholder/index.tsx @@ -0,0 +1,105 @@ +import type { Application } from '@logto/schemas'; +import { ApplicationType } from '@logto/schemas'; +import { conditional } from '@silverhand/essentials'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import Modal from 'react-modal'; +import { useNavigate } from 'react-router-dom'; + +import Button from '@/components/Button'; +import useApi from '@/hooks/use-api'; +import useConfigs from '@/hooks/use-configs'; +import * as modalStyles from '@/scss/modal.module.scss'; +import { applicationTypeI18nKey } from '@/types/applications'; + +import Guide from '../Guide'; +import TypeDescription from '../TypeDescription'; +import * as styles from './index.module.scss'; + +const defaultAppName = 'My App'; + +const ApplicationsPlaceholder = () => { + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + const navigate = useNavigate(); + const [isCreating, setIsCreating] = useState(false); + const [createdApplication, setCreatedApplication] = useState(); + const isGetStartedModalOpen = Boolean(createdApplication); + const api = useApi(); + const { updateConfigs } = useConfigs(); + + const handleCreate = async (type: ApplicationType) => { + if (isCreating) { + return; + } + + setIsCreating(true); + const payload = { + type, + name: defaultAppName, + }; + + try { + const createdApp = await api.post('api/applications', { json: payload }).json(); + + setCreatedApplication(createdApp); + + void updateConfigs({ + applicationCreated: true, + ...conditional( + createdApp.type === ApplicationType.MachineToMachine && { m2mApplicationCreated: true } + ), + }); + } finally { + setIsCreating(false); + } + }; + + const closeGuideModal = () => { + if (!createdApplication) { + return; + } + + navigate(`/applications/${createdApplication.id}`); + setCreatedApplication(undefined); + }; + + return ( +
+
{t('applications.placeholder_title')}
+
{t('applications.placeholder_description')}
+
+ {Object.values(ApplicationType).map((type) => ( +
+ +
+ ))} +
+ {createdApplication && ( + + + + )} +
+ ); +}; + +export default ApplicationsPlaceholder; diff --git a/packages/console/src/pages/Applications/components/CreateForm/index.tsx b/packages/console/src/pages/Applications/components/CreateForm/index.tsx index bb135409d..5de5dae6c 100644 --- a/packages/console/src/pages/Applications/components/CreateForm/index.tsx +++ b/packages/console/src/pages/Applications/components/CreateForm/index.tsx @@ -62,7 +62,7 @@ const CreateForm = ({ onClose }: Props) => { void updateConfigs({ applicationCreated: true, ...conditional( - createdApp.type === ApplicationType.MachineToMachine && { applicationM2mCreated: true } + createdApp.type === ApplicationType.MachineToMachine && { m2mApplicationCreated: true } ), }); }); diff --git a/packages/console/src/pages/Applications/components/TypeDescription/index.module.scss b/packages/console/src/pages/Applications/components/TypeDescription/index.module.scss index 935b067c6..b6fbccf1d 100644 --- a/packages/console/src/pages/Applications/components/TypeDescription/index.module.scss +++ b/packages/console/src/pages/Applications/components/TypeDescription/index.module.scss @@ -1,23 +1,37 @@ @use '@/scss/underscore' as _; -.title { - font: var(--font-label-2); - color: var(--color-text); - padding-right: _.unit(6); /* For check mark */ -} +.container { + flex: 1; + display: flex; + flex-direction: column; -.subtitle, -.description { - margin-top: _.unit(3); - flex: 2; -} + .title { + font: var(--font-label-2); + color: var(--color-text); + margin-top: _.unit(2.5); + } -.subtitle { - font: var(--font-body-2); - color: var(--color-text); -} + .subtitle, + .description { + margin-top: _.unit(3); + flex: 2; + font: var(--font-body-2); + } -.description { - font: var(--font-body-2); - color: var(--color-text-secondary); + .subtitle { + color: var(--color-text); + } + + .description { + color: var(--color-text-secondary); + } + + &.small { + .subtitle, + .description { + margin-top: _.unit(1); + flex: 2; + font: var(--font-body-3); + } + } } diff --git a/packages/console/src/pages/Applications/components/TypeDescription/index.tsx b/packages/console/src/pages/Applications/components/TypeDescription/index.tsx index 9376e8d08..62325a136 100644 --- a/packages/console/src/pages/Applications/components/TypeDescription/index.tsx +++ b/packages/console/src/pages/Applications/components/TypeDescription/index.tsx @@ -1,4 +1,5 @@ import type { ApplicationType } from '@logto/schemas'; +import classNames from 'classnames'; import ApplicationIcon from '@/components/ApplicationIcon'; @@ -9,17 +10,16 @@ type Props = { subtitle: string; description: string; type: ApplicationType; + size?: 'large' | 'small'; }; -const TypeDescription = ({ title, subtitle, description, type }: Props) => { - return ( - <> - -
{title}
-
{subtitle}
-
{description}
- - ); -}; +const TypeDescription = ({ title, subtitle, description, type, size = 'large' }: Props) => ( +
+ +
{title}
+
{subtitle}
+
{description}
+
+); export default TypeDescription; diff --git a/packages/console/src/pages/Applications/index.tsx b/packages/console/src/pages/Applications/index.tsx index b0041606c..68588553f 100644 --- a/packages/console/src/pages/Applications/index.tsx +++ b/packages/console/src/pages/Applications/index.tsx @@ -1,5 +1,4 @@ import type { Application } from '@logto/schemas'; -import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; import Modal from 'react-modal'; import { useLocation, useNavigate } from 'react-router-dom'; @@ -10,7 +9,6 @@ import ApplicationIcon from '@/components/ApplicationIcon'; import Button from '@/components/Button'; import CardTitle from '@/components/CardTitle'; import CopyToClipboard from '@/components/CopyToClipboard'; -import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder'; import ItemPreview from '@/components/ItemPreview'; import Pagination from '@/components/Pagination'; import Table from '@/components/Table'; @@ -22,6 +20,7 @@ import * as resourcesStyles from '@/scss/resources.module.scss'; import { applicationTypeI18nKey } from '@/types/applications'; import { buildUrl } from '@/utils/url'; +import ApplicationsPlaceholder from './components/ApplicationsPlaceholder'; import CreateForm from './components/CreateForm'; import * as styles from './index.module.scss'; @@ -81,7 +80,6 @@ const Applications = () => { { if (createdApp) { - toast.success(t('applications.application_created', { name: createdApp.name })); navigate(buildDetailsPathname(createdApp.id), { replace: true }); return; @@ -121,21 +119,7 @@ const Applications = () => { render: ({ id }) => , }, ]} - placeholder={ - <> - -