1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

fix: show previous invoices page if UNLEASH_CLOUD is falsy (#1094)

* fix: restore previous invoices page

* fix: show previous invoices page if UNLEASH_CLOUD is falsy

* fix: use correct amountFormatted invoice field name
This commit is contained in:
olav 2022-06-14 15:41:28 +02:00 committed by GitHub
parent 51e5939f68
commit 4fb0be3710
9 changed files with 191 additions and 24 deletions

View File

@ -0,0 +1,19 @@
import { Navigate } from 'react-router-dom';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import InvoiceAdminPage from 'component/admin/invoice/InvoiceAdminPage';
const FlaggedBillingRedirect = () => {
const { uiConfig, loading } = useUiConfig();
if (loading) {
return null;
}
if (!uiConfig.flags.UNLEASH_CLOUD) {
return <InvoiceAdminPage />;
}
return <Navigate to="/admin/billing" replace />;
};
export default FlaggedBillingRedirect;

View File

@ -1,7 +0,0 @@
import { Navigate } from 'react-router-dom';
const RedirectAdminInvoices = () => {
return <Navigate to="/admin/billing" replace />;
};
export default RedirectAdminInvoices;

View File

@ -0,0 +1,25 @@
import { useContext } from 'react';
import InvoiceList from './InvoiceList';
import AccessContext from 'contexts/AccessContext';
import { ADMIN } from 'component/providers/AccessProvider/permissions';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { Alert } from '@mui/material';
const InvoiceAdminPage = () => {
const { hasAccess } = useContext(AccessContext);
return (
<div>
<ConditionallyRender
condition={hasAccess(ADMIN)}
show={<InvoiceList />}
elseShow={
<Alert severity="error">
You need to be instance admin to access this section.
</Alert>
}
/>
</div>
);
};
export default InvoiceAdminPage;

View File

@ -0,0 +1,122 @@
import { useEffect, useState } from 'react';
import {
Table,
TableHead,
TableBody,
TableRow,
TableCell,
Button,
} from '@mui/material';
import OpenInNew from '@mui/icons-material/OpenInNew';
import { PageContent } from 'component/common/PageContent/PageContent';
import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { formatApiPath } from 'utils/formatPath';
import useInvoices from 'hooks/api/getters/useInvoices/useInvoices';
import { IInvoice } from 'interfaces/invoice';
import { useLocationSettings } from 'hooks/useLocationSettings';
import { formatDateYMD } from 'utils/formatDate';
const PORTAL_URL = formatApiPath('api/admin/invoices/portal');
const InvoiceList = () => {
const { refetchInvoices, invoices } = useInvoices();
const [isLoaded, setLoaded] = useState(false);
const { locationSettings } = useLocationSettings();
useEffect(() => {
refetchInvoices();
setLoaded(true);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<ConditionallyRender
condition={invoices.length > 0}
show={
<PageContent
header={
<PageHeader
title="Invoices"
actions={
<Button
href={PORTAL_URL}
rel="noreferrer"
target="_blank"
endIcon={<OpenInNew />}
>
Billing portal
</Button>
}
/>
}
>
<div>
<Table>
<TableHead>
<TableRow>
<TableCell>Amount</TableCell>
<TableCell>Status</TableCell>
<TableCell>Due date</TableCell>
<TableCell>PDF</TableCell>
<TableCell>Link</TableCell>
</TableRow>
</TableHead>
<TableBody>
{invoices.map((item: IInvoice) => (
<TableRow
key={item.invoiceURL}
style={{
backgroundColor:
item.status === 'past-due'
? '#ff9194'
: 'inherit',
}}
>
<TableCell
style={{ textAlign: 'left' }}
>
{item.amountFormatted}
</TableCell>
<TableCell
style={{ textAlign: 'left' }}
>
{item.status}
</TableCell>
<TableCell
style={{ textAlign: 'left' }}
>
{item.dueDate &&
formatDateYMD(
item.dueDate,
locationSettings.locale
)}
</TableCell>
<TableCell
style={{ textAlign: 'left' }}
>
<a href={item.invoicePDF}>PDF</a>
</TableCell>
<TableCell
style={{ textAlign: 'left' }}
>
<a
href={item.invoiceURL}
target="_blank"
rel="noreferrer"
>
Payment link
</a>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</PageContent>
}
elseShow={<div>{isLoaded && 'No invoices to show.'}</div>}
/>
);
};
export default InvoiceList;

View File

@ -23,7 +23,6 @@ import { useAuthPermissions } from 'hooks/api/getters/useAuth/useAuthPermissions
import { useStyles } from './Header.styles'; import { useStyles } from './Header.styles';
import classNames from 'classnames'; import classNames from 'classnames';
import { useId } from 'hooks/useId'; import { useId } from 'hooks/useId';
import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus';
import { IRoute } from 'interfaces/route'; import { IRoute } from 'interfaces/route';
const Header: VFC = () => { const Header: VFC = () => {
@ -37,6 +36,7 @@ const Header: VFC = () => {
const { permissions } = useAuthPermissions(); const { permissions } = useAuthPermissions();
const { const {
uiConfig: { links, name, flags }, uiConfig: { links, name, flags },
isOss,
} = useUiConfig(); } = useUiConfig();
const smallScreen = useMediaQuery(theme.breakpoints.down('md')); const smallScreen = useMediaQuery(theme.breakpoints.down('md'));
const { classes: styles } = useStyles(); const { classes: styles } = useStyles();
@ -57,15 +57,18 @@ const Header: VFC = () => {
} }
}, [permissions]); }, [permissions]);
const { isBilling } = useInstanceStatus();
const routes = getRoutes(); const routes = getRoutes();
const filterByEnterprise = (route: IRoute): boolean => {
return !route.menu.isEnterprise || !isOss();
};
const filteredMainRoutes = { const filteredMainRoutes = {
mainNavRoutes: routes.mainNavRoutes.filter(filterByFlags(flags)), mainNavRoutes: routes.mainNavRoutes.filter(filterByFlags(flags)),
mobileRoutes: routes.mobileRoutes.filter(filterByFlags(flags)), mobileRoutes: routes.mobileRoutes.filter(filterByFlags(flags)),
adminRoutes: routes.adminRoutes adminRoutes: routes.adminRoutes
.filter(filterByFlags(flags)) .filter(filterByFlags(flags))
.filter(filterByBilling(isBilling)), .filter(filterByEnterprise),
}; };
if (smallScreen) { if (smallScreen) {
@ -196,7 +199,4 @@ const Header: VFC = () => {
); );
}; };
export const filterByBilling = (isBilling?: boolean) => (route: IRoute) =>
!route.menu.isBilling || isBilling;
export default Header; export default Header;

View File

@ -411,10 +411,7 @@ exports[`returns all baseRoutes 1`] = `
}, },
{ {
"component": [Function], "component": [Function],
"menu": { "menu": {},
"adminSettings": true,
"isBilling": true,
},
"parent": "/admin", "parent": "/admin",
"path": "/admin/billing", "path": "/admin/billing",
"title": "Billing", "title": "Billing",
@ -422,7 +419,10 @@ exports[`returns all baseRoutes 1`] = `
}, },
{ {
"component": [Function], "component": [Function],
"menu": {}, "menu": {
"adminSettings": true,
"isEnterprise": true,
},
"parent": "/admin", "parent": "/admin",
"path": "/admin-invoices", "path": "/admin-invoices",
"title": "Invoices", "title": "Invoices",

View File

@ -7,7 +7,6 @@ import Admin from 'component/admin';
import AdminApi from 'component/admin/api'; import AdminApi from 'component/admin/api';
import AdminUsers from 'component/admin/users/UsersAdmin'; import AdminUsers from 'component/admin/users/UsersAdmin';
import { AuthSettings } from 'component/admin/auth/AuthSettings'; import { AuthSettings } from 'component/admin/auth/AuthSettings';
import { Billing } from 'component/admin/billing/Billing';
import Login from 'component/user/Login/Login'; import Login from 'component/user/Login/Login';
import { C, EEA, P, RE, SE } from 'component/common/flags'; import { C, EEA, P, RE, SE } from 'component/common/flags';
import { NewUser } from 'component/user/NewUser/NewUser'; import { NewUser } from 'component/user/NewUser/NewUser';
@ -50,8 +49,9 @@ import { EditSegment } from 'component/segments/EditSegment/EditSegment';
import { IRoute } from 'interfaces/route'; import { IRoute } from 'interfaces/route';
import { EnvironmentTable } from 'component/environments/EnvironmentTable/EnvironmentTable'; import { EnvironmentTable } from 'component/environments/EnvironmentTable/EnvironmentTable';
import { SegmentTable } from 'component/segments/SegmentTable/SegmentTable'; import { SegmentTable } from 'component/segments/SegmentTable/SegmentTable';
import RedirectAdminInvoices from 'component/admin/billing/RedirectAdminInvoices/RedirectAdminInvoices'; import FlaggedBillingRedirect from 'component/admin/billing/FlaggedBillingRedirect/FlaggedBillingRedirect';
import { FeaturesArchiveTable } from '../archive/FeaturesArchiveTable'; import { FeaturesArchiveTable } from '../archive/FeaturesArchiveTable';
import { Billing } from 'component/admin/billing/Billing';
export const routes: IRoute[] = [ export const routes: IRoute[] = [
// Splash // Splash
@ -462,15 +462,15 @@ export const routes: IRoute[] = [
title: 'Billing', title: 'Billing',
component: Billing, component: Billing,
type: 'protected', type: 'protected',
menu: { adminSettings: true, isBilling: true }, menu: {},
}, },
{ {
path: '/admin-invoices', path: '/admin-invoices',
parent: '/admin', parent: '/admin',
title: 'Invoices', title: 'Invoices',
component: RedirectAdminInvoices, component: FlaggedBillingRedirect,
type: 'protected', type: 'protected',
menu: {}, menu: { adminSettings: true, isEnterprise: true },
}, },
{ {
path: '/admin', path: '/admin',

View File

@ -0,0 +1,8 @@
export interface IInvoice {
amountFormatted: string;
invoicePDF: string;
invoiceURL: string;
paid: boolean;
status: string;
dueDate?: Date;
}

View File

@ -17,5 +17,5 @@ interface IRouteMenu {
mobile?: boolean; mobile?: boolean;
advanced?: boolean; advanced?: boolean;
adminSettings?: boolean; adminSettings?: boolean;
isBilling?: boolean; isEnterprise?: boolean;
} }