diff --git a/frontend/src/component/admin/billing/FlaggedBillingRedirect/FlaggedBillingRedirect.tsx b/frontend/src/component/admin/billing/FlaggedBillingRedirect/FlaggedBillingRedirect.tsx
new file mode 100644
index 0000000000..0a9d17683e
--- /dev/null
+++ b/frontend/src/component/admin/billing/FlaggedBillingRedirect/FlaggedBillingRedirect.tsx
@@ -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 ;
+ }
+
+ return ;
+};
+
+export default FlaggedBillingRedirect;
diff --git a/frontend/src/component/admin/billing/RedirectAdminInvoices/RedirectAdminInvoices.tsx b/frontend/src/component/admin/billing/RedirectAdminInvoices/RedirectAdminInvoices.tsx
deleted file mode 100644
index 07111c962e..0000000000
--- a/frontend/src/component/admin/billing/RedirectAdminInvoices/RedirectAdminInvoices.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import { Navigate } from 'react-router-dom';
-
-const RedirectAdminInvoices = () => {
- return ;
-};
-
-export default RedirectAdminInvoices;
diff --git a/frontend/src/component/admin/invoice/InvoiceAdminPage.tsx b/frontend/src/component/admin/invoice/InvoiceAdminPage.tsx
new file mode 100644
index 0000000000..f9197c0df6
--- /dev/null
+++ b/frontend/src/component/admin/invoice/InvoiceAdminPage.tsx
@@ -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 (
+
+
}
+ elseShow={
+
+ You need to be instance admin to access this section.
+
+ }
+ />
+
+ );
+};
+
+export default InvoiceAdminPage;
diff --git a/frontend/src/component/admin/invoice/InvoiceList.tsx b/frontend/src/component/admin/invoice/InvoiceList.tsx
new file mode 100644
index 0000000000..136706ca32
--- /dev/null
+++ b/frontend/src/component/admin/invoice/InvoiceList.tsx
@@ -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 (
+ 0}
+ show={
+ }
+ >
+ Billing portal
+
+ }
+ />
+ }
+ >
+
+
+
+
+ Amount
+ Status
+ Due date
+ PDF
+ Link
+
+
+
+ {invoices.map((item: IInvoice) => (
+
+
+ {item.amountFormatted}
+
+
+ {item.status}
+
+
+ {item.dueDate &&
+ formatDateYMD(
+ item.dueDate,
+ locationSettings.locale
+ )}
+
+
+ PDF
+
+
+
+ Payment link
+
+
+
+ ))}
+
+
+
+
+ }
+ elseShow={{isLoaded && 'No invoices to show.'}
}
+ />
+ );
+};
+export default InvoiceList;
diff --git a/frontend/src/component/menu/Header/Header.tsx b/frontend/src/component/menu/Header/Header.tsx
index 00f6da9574..facdb236ce 100644
--- a/frontend/src/component/menu/Header/Header.tsx
+++ b/frontend/src/component/menu/Header/Header.tsx
@@ -23,7 +23,6 @@ import { useAuthPermissions } from 'hooks/api/getters/useAuth/useAuthPermissions
import { useStyles } from './Header.styles';
import classNames from 'classnames';
import { useId } from 'hooks/useId';
-import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus';
import { IRoute } from 'interfaces/route';
const Header: VFC = () => {
@@ -37,6 +36,7 @@ const Header: VFC = () => {
const { permissions } = useAuthPermissions();
const {
uiConfig: { links, name, flags },
+ isOss,
} = useUiConfig();
const smallScreen = useMediaQuery(theme.breakpoints.down('md'));
const { classes: styles } = useStyles();
@@ -57,15 +57,18 @@ const Header: VFC = () => {
}
}, [permissions]);
- const { isBilling } = useInstanceStatus();
const routes = getRoutes();
+ const filterByEnterprise = (route: IRoute): boolean => {
+ return !route.menu.isEnterprise || !isOss();
+ };
+
const filteredMainRoutes = {
mainNavRoutes: routes.mainNavRoutes.filter(filterByFlags(flags)),
mobileRoutes: routes.mobileRoutes.filter(filterByFlags(flags)),
adminRoutes: routes.adminRoutes
.filter(filterByFlags(flags))
- .filter(filterByBilling(isBilling)),
+ .filter(filterByEnterprise),
};
if (smallScreen) {
@@ -196,7 +199,4 @@ const Header: VFC = () => {
);
};
-export const filterByBilling = (isBilling?: boolean) => (route: IRoute) =>
- !route.menu.isBilling || isBilling;
-
export default Header;
diff --git a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap
index 6c49570e44..6b5c45ae25 100644
--- a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap
+++ b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap
@@ -411,10 +411,7 @@ exports[`returns all baseRoutes 1`] = `
},
{
"component": [Function],
- "menu": {
- "adminSettings": true,
- "isBilling": true,
- },
+ "menu": {},
"parent": "/admin",
"path": "/admin/billing",
"title": "Billing",
@@ -422,7 +419,10 @@ exports[`returns all baseRoutes 1`] = `
},
{
"component": [Function],
- "menu": {},
+ "menu": {
+ "adminSettings": true,
+ "isEnterprise": true,
+ },
"parent": "/admin",
"path": "/admin-invoices",
"title": "Invoices",
diff --git a/frontend/src/component/menu/routes.ts b/frontend/src/component/menu/routes.ts
index 1e51189519..3189b20848 100644
--- a/frontend/src/component/menu/routes.ts
+++ b/frontend/src/component/menu/routes.ts
@@ -7,7 +7,6 @@ import Admin from 'component/admin';
import AdminApi from 'component/admin/api';
import AdminUsers from 'component/admin/users/UsersAdmin';
import { AuthSettings } from 'component/admin/auth/AuthSettings';
-import { Billing } from 'component/admin/billing/Billing';
import Login from 'component/user/Login/Login';
import { C, EEA, P, RE, SE } from 'component/common/flags';
import { NewUser } from 'component/user/NewUser/NewUser';
@@ -50,8 +49,9 @@ import { EditSegment } from 'component/segments/EditSegment/EditSegment';
import { IRoute } from 'interfaces/route';
import { EnvironmentTable } from 'component/environments/EnvironmentTable/EnvironmentTable';
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 { Billing } from 'component/admin/billing/Billing';
export const routes: IRoute[] = [
// Splash
@@ -462,15 +462,15 @@ export const routes: IRoute[] = [
title: 'Billing',
component: Billing,
type: 'protected',
- menu: { adminSettings: true, isBilling: true },
+ menu: {},
},
{
path: '/admin-invoices',
parent: '/admin',
title: 'Invoices',
- component: RedirectAdminInvoices,
+ component: FlaggedBillingRedirect,
type: 'protected',
- menu: {},
+ menu: { adminSettings: true, isEnterprise: true },
},
{
path: '/admin',
diff --git a/frontend/src/interfaces/invoice.ts b/frontend/src/interfaces/invoice.ts
new file mode 100644
index 0000000000..82790c14cd
--- /dev/null
+++ b/frontend/src/interfaces/invoice.ts
@@ -0,0 +1,8 @@
+export interface IInvoice {
+ amountFormatted: string;
+ invoicePDF: string;
+ invoiceURL: string;
+ paid: boolean;
+ status: string;
+ dueDate?: Date;
+}
diff --git a/frontend/src/interfaces/route.ts b/frontend/src/interfaces/route.ts
index 213bec4a51..6c94cb1a80 100644
--- a/frontend/src/interfaces/route.ts
+++ b/frontend/src/interfaces/route.ts
@@ -17,5 +17,5 @@ interface IRouteMenu {
mobile?: boolean;
advanced?: boolean;
adminSettings?: boolean;
- isBilling?: boolean;
+ isEnterprise?: boolean;
}