mirror of
https://github.com/logto-io/logto.git
synced 2025-01-27 21:39:16 -05:00
feat(console): add tenant suspended page (#4473)
This commit is contained in:
parent
c775d6c66f
commit
2337c1a3d9
27 changed files with 91 additions and 12 deletions
|
@ -1,4 +1,4 @@
|
||||||
import Topbar from '@/containers/AppContent/components/Topbar';
|
import Topbar from '@/components/Topbar';
|
||||||
|
|
||||||
import TenantLandingPageContent from './TenantLandingPageContent';
|
import TenantLandingPageContent from './TenantLandingPageContent';
|
||||||
import * as styles from './index.module.scss';
|
import * as styles from './index.module.scss';
|
||||||
|
|
|
@ -10,15 +10,23 @@ type Props = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function TenantStatusTag({ tenantData, tenantPlan, className }: Props) {
|
function TenantStatusTag({ tenantData, tenantPlan, className }: Props) {
|
||||||
const { usage, openInvoices } = tenantData;
|
const { usage, openInvoices, isSuspended } = tenantData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tenant status priority:
|
* Tenant status priority:
|
||||||
* 1. suspend (WIP) @xiaoyijun
|
* 1. suspend
|
||||||
* 2. overdue
|
* 2. overdue
|
||||||
* 3. mau exceeded
|
* 3. mau exceeded
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
if (isSuspended) {
|
||||||
|
return (
|
||||||
|
<Tag className={className}>
|
||||||
|
<DynamicT forKey="user_details.suspended" />
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (openInvoices.length > 0) {
|
if (openInvoices.length > 0) {
|
||||||
return (
|
return (
|
||||||
<Tag className={className}>
|
<Tag className={className}>
|
|
@ -4,11 +4,10 @@ import { useMemo } from 'react';
|
||||||
import Tick from '@/assets/icons/tick.svg';
|
import Tick from '@/assets/icons/tick.svg';
|
||||||
import { type TenantResponse } from '@/cloud/types/router';
|
import { type TenantResponse } from '@/cloud/types/router';
|
||||||
import PlanName from '@/components/PlanName';
|
import PlanName from '@/components/PlanName';
|
||||||
|
import TenantEnvTag from '@/components/TenantEnvTag';
|
||||||
import { DropdownItem } from '@/ds-components/Dropdown';
|
import { DropdownItem } from '@/ds-components/Dropdown';
|
||||||
import useSubscriptionPlans from '@/hooks/use-subscription-plans';
|
import useSubscriptionPlans from '@/hooks/use-subscription-plans';
|
||||||
|
|
||||||
import TenantEnvTag from '../TenantEnvTag';
|
|
||||||
|
|
||||||
import TenantStatusTag from './TenantStatusTag';
|
import TenantStatusTag from './TenantStatusTag';
|
||||||
import * as styles from './index.module.scss';
|
import * as styles from './index.module.scss';
|
||||||
|
|
|
@ -5,6 +5,7 @@ import KeyboardArrowDown from '@/assets/icons/keyboard-arrow-down.svg';
|
||||||
import PlusSign from '@/assets/icons/plus.svg';
|
import PlusSign from '@/assets/icons/plus.svg';
|
||||||
import { type TenantResponse } from '@/cloud/types/router';
|
import { type TenantResponse } from '@/cloud/types/router';
|
||||||
import CreateTenantModal from '@/components/CreateTenantModal';
|
import CreateTenantModal from '@/components/CreateTenantModal';
|
||||||
|
import TenantEnvTag from '@/components/TenantEnvTag';
|
||||||
import { TenantsContext } from '@/contexts/TenantsProvider';
|
import { TenantsContext } from '@/contexts/TenantsProvider';
|
||||||
import Divider from '@/ds-components/Divider';
|
import Divider from '@/ds-components/Divider';
|
||||||
import Dropdown from '@/ds-components/Dropdown';
|
import Dropdown from '@/ds-components/Dropdown';
|
||||||
|
@ -13,7 +14,6 @@ import useUserDefaultTenantId from '@/hooks/use-user-default-tenant-id';
|
||||||
import { onKeyDownHandler } from '@/utils/a11y';
|
import { onKeyDownHandler } from '@/utils/a11y';
|
||||||
|
|
||||||
import TenantDropdownItem from './TenantDropdownItem';
|
import TenantDropdownItem from './TenantDropdownItem';
|
||||||
import TenantEnvTag from './TenantEnvTag';
|
|
||||||
import * as styles from './index.module.scss';
|
import * as styles from './index.module.scss';
|
||||||
|
|
||||||
export default function TenantSelector() {
|
export default function TenantSelector() {
|
|
@ -0,0 +1,29 @@
|
||||||
|
@use '@/scss/underscore' as _;
|
||||||
|
|
||||||
|
.suspendedPage {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.image {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font: var(--font-title-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
font: var(--font-body-2);
|
||||||
|
text-align: center;
|
||||||
|
margin: _.unit(2);
|
||||||
|
width: 470px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.linkButton {
|
||||||
|
margin-top: _.unit(4);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { Theme } from '@logto/schemas';
|
||||||
|
|
||||||
|
import ErrorDark from '@/assets/images/error-dark.svg';
|
||||||
|
import Error from '@/assets/images/error.svg';
|
||||||
|
import { contactEmailLink } from '@/consts';
|
||||||
|
import Button from '@/ds-components/Button';
|
||||||
|
import DynamicT from '@/ds-components/DynamicT';
|
||||||
|
import useTheme from '@/hooks/use-theme';
|
||||||
|
|
||||||
|
import * as styles from './index.module.scss';
|
||||||
|
|
||||||
|
function TenantSuspendedPage() {
|
||||||
|
const theme = useTheme();
|
||||||
|
const ErrorImage = theme === Theme.Light ? Error : ErrorDark;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.suspendedPage}>
|
||||||
|
<ErrorImage className={styles.image} />
|
||||||
|
<div className={styles.title}>
|
||||||
|
<DynamicT forKey="tenants.tenant_suspended_page.title" />
|
||||||
|
</div>
|
||||||
|
<div className={styles.description}>
|
||||||
|
<DynamicT forKey="tenants.tenant_suspended_page.description_1" />
|
||||||
|
</div>
|
||||||
|
<div className={styles.description}>
|
||||||
|
<DynamicT forKey="tenants.tenant_suspended_page.description_2" />
|
||||||
|
</div>
|
||||||
|
<a href={contactEmailLink} className={styles.linkButton} rel="noopener">
|
||||||
|
<Button title="general.contact_us_action" type="outline" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TenantSuspendedPage;
|
|
@ -1,28 +1,33 @@
|
||||||
import { conditional, joinPath } from '@silverhand/essentials';
|
import { conditional, joinPath } from '@silverhand/essentials';
|
||||||
import { useRef } from 'react';
|
import { useContext, useRef } from 'react';
|
||||||
import { Navigate, Outlet, useParams } from 'react-router-dom';
|
import { Navigate, Outlet, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import AppLoading from '@/components/AppLoading';
|
import AppLoading from '@/components/AppLoading';
|
||||||
import MauExceededModal from '@/components/MauExceededModal';
|
import MauExceededModal from '@/components/MauExceededModal';
|
||||||
import PaymentOverdueModal from '@/components/PaymentOverdueModal';
|
import PaymentOverdueModal from '@/components/PaymentOverdueModal';
|
||||||
|
import Topbar from '@/components/Topbar';
|
||||||
import { isCloud } from '@/consts/env';
|
import { isCloud } from '@/consts/env';
|
||||||
|
import { TenantsContext } from '@/contexts/TenantsProvider';
|
||||||
import useScroll from '@/hooks/use-scroll';
|
import useScroll from '@/hooks/use-scroll';
|
||||||
import useUserPreferences from '@/hooks/use-user-preferences';
|
import useUserPreferences from '@/hooks/use-user-preferences';
|
||||||
|
|
||||||
import { getPath } from '../ConsoleContent/Sidebar';
|
import { getPath } from '../ConsoleContent/Sidebar';
|
||||||
import { useSidebarMenuItems } from '../ConsoleContent/Sidebar/hook';
|
import { useSidebarMenuItems } from '../ConsoleContent/Sidebar/hook';
|
||||||
|
|
||||||
import Topbar from './components/Topbar';
|
import TenantSuspendedPage from './TenantSuspendedPage';
|
||||||
import * as styles from './index.module.scss';
|
import * as styles from './index.module.scss';
|
||||||
import { type AppContentOutletContext } from './types';
|
import { type AppContentOutletContext } from './types';
|
||||||
|
|
||||||
export default function AppContent() {
|
export default function AppContent() {
|
||||||
const { isLoading } = useUserPreferences();
|
const { isLoading } = useUserPreferences();
|
||||||
|
const { currentTenant } = useContext(TenantsContext);
|
||||||
|
const isTenantSuspended = isCloud && currentTenant?.isSuspended;
|
||||||
|
const shouldCheckSubscriptionState = isCloud && !currentTenant?.isSuspended;
|
||||||
|
|
||||||
const scrollableContent = useRef<HTMLDivElement>(null);
|
const scrollableContent = useRef<HTMLDivElement>(null);
|
||||||
const { scrollTop } = useScroll(scrollableContent.current);
|
const { scrollTop } = useScroll(scrollableContent.current);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading || !currentTenant) {
|
||||||
return <AppLoading />;
|
return <AppLoading />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,9 +35,12 @@ export default function AppContent() {
|
||||||
<>
|
<>
|
||||||
<div className={styles.app}>
|
<div className={styles.app}>
|
||||||
<Topbar className={conditional(scrollTop && styles.topbarShadow)} />
|
<Topbar className={conditional(scrollTop && styles.topbarShadow)} />
|
||||||
<Outlet context={{ scrollableContent } satisfies AppContentOutletContext} />
|
{isTenantSuspended && <TenantSuspendedPage />}
|
||||||
|
{!isTenantSuspended && (
|
||||||
|
<Outlet context={{ scrollableContent } satisfies AppContentOutletContext} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{isCloud && (
|
{shouldCheckSubscriptionState && (
|
||||||
<>
|
<>
|
||||||
<MauExceededModal />
|
<MauExceededModal />
|
||||||
<PaymentOverdueModal />
|
<PaymentOverdueModal />
|
||||||
|
|
|
@ -2,8 +2,8 @@ import classNames from 'classnames';
|
||||||
import { useTranslation, Trans } from 'react-i18next';
|
import { useTranslation, Trans } from 'react-i18next';
|
||||||
|
|
||||||
import { type TenantResponse } from '@/cloud/types/router';
|
import { type TenantResponse } from '@/cloud/types/router';
|
||||||
|
import { tenantTagMap } from '@/components/TenantEnvTag';
|
||||||
import { contactEmailLink } from '@/consts';
|
import { contactEmailLink } from '@/consts';
|
||||||
import { tenantTagMap } from '@/containers/AppContent/components/Topbar/TenantSelector/TenantEnvTag';
|
|
||||||
import DeleteConfirmModal from '@/ds-components/DeleteConfirmModal';
|
import DeleteConfirmModal from '@/ds-components/DeleteConfirmModal';
|
||||||
import TextLink from '@/ds-components/TextLink';
|
import TextLink from '@/ds-components/TextLink';
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue