diff --git a/frontend/src/component/menu/routes.js b/frontend/src/component/menu/routes.js
index 21bdc17e8d..7deaacd9c3 100644
--- a/frontend/src/component/menu/routes.js
+++ b/frontend/src/component/menu/routes.js
@@ -31,6 +31,7 @@ import AddonsEdit from '../../page/addons/edit';
import Admin from '../../page/admin';
import AdminApi from '../../page/admin/api';
import AdminUsers from '../../page/admin/users';
+import AdminInvoice from '../../page/admin/invoice';
import AdminAuth from '../../page/admin/auth';
import Reporting from '../../page/reporting';
import Login from '../user/Login';
@@ -327,6 +328,15 @@ export const routes = [
type: 'protected',
layout: 'main',
},
+ {
+ path: '/admin-invoices',
+ title: 'Invoices',
+ icon: 'money',
+ component: AdminInvoice,
+ hidden: true,
+ type: 'protected',
+ layout: 'main',
+ },
{
path: '/admin',
title: 'Admin',
diff --git a/frontend/src/page/admin/invoice/index.js b/frontend/src/page/admin/invoice/index.js
new file mode 100644
index 0000000000..c6e79ad81d
--- /dev/null
+++ b/frontend/src/page/admin/invoice/index.js
@@ -0,0 +1,36 @@
+import { useContext } from 'react';
+import PropTypes from 'prop-types';
+import InvoiceList from './invoice-container';
+import AccessContext from '../../../contexts/AccessContext';
+import { ADMIN } from '../../../component/AccessProvider/permissions';
+import ConditionallyRender from '../../../component/common/ConditionallyRender';
+import { Alert } from '@material-ui/lab';
+
+const InvoiceAdminPage = ({ history }) => {
+ const { hasAccess } = useContext(AccessContext);
+
+ return (
+
+
+ }
+ elseShow={
+
+ You need to be instance admin to access this section.
+
+ }
+
+ />
+
+
+ );
+};
+
+InvoiceAdminPage.propTypes = {
+ match: PropTypes.object.isRequired,
+ history: PropTypes.object.isRequired,
+};
+
+export default InvoiceAdminPage;
diff --git a/frontend/src/page/admin/invoice/invoice-container.js b/frontend/src/page/admin/invoice/invoice-container.js
new file mode 100644
index 0000000000..8064dadc48
--- /dev/null
+++ b/frontend/src/page/admin/invoice/invoice-container.js
@@ -0,0 +1,11 @@
+import { connect } from 'react-redux';
+
+import Component from './invoice-list';
+import { fetchInvoices } from '../../../store/e-admin-invoice/actions';
+export default connect(
+ state => ({
+ location: state.settings.toJS().location || {},
+ invoices: state.invoiceAdmin.toJS(),
+ }),
+ { fetchInvoices }
+)(Component);
diff --git a/frontend/src/page/admin/invoice/invoice-list.jsx b/frontend/src/page/admin/invoice/invoice-list.jsx
new file mode 100644
index 0000000000..c2eaf91a4b
--- /dev/null
+++ b/frontend/src/page/admin/invoice/invoice-list.jsx
@@ -0,0 +1,88 @@
+import { useEffect, useState } from 'react';
+import PropTypes from 'prop-types';
+import {
+ Table,
+ TableHead,
+ TableBody,
+ TableRow,
+ TableCell,
+ Button,
+} from '@material-ui/core';
+import OpenInNew from '@material-ui/icons/OpenInNew';
+import { formatDateWithLocale } from '../../../component/common/util';
+import PageContent from '../../../component/common/PageContent';
+import HeaderTitle from '../../../component/common/HeaderTitle';
+import ConditionallyRender from '../../../component/common/ConditionallyRender';
+import { formatApiPath } from '../../../utils/format-path';
+
+const PORTAL_URL = formatApiPath('api/admin/invoices/portal');
+
+function InvoiceList({
+ location,
+ fetchInvoices,
+ invoices,
+}) {
+
+ const [isLoaded, setLoaded] = useState(false);
+
+ useEffect(() => {
+ fetchInvoices().finally(() => setLoaded(true));
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ return (
+ 0}
+ show={
+ }>
+ Billing portal
+ } />}>
+
+
+
+
+
+ Amount
+ Status
+ Due date
+ PDF
+ Link
+
+
+
+ {invoices.map(item => (
+
+
+ {item.amountFomratted}
+
+
+ {item.status}
+
+
+ {formatDateWithLocale(
+ item.dueDate,
+ location.locale
+ )}
+
+
+ PDF
+
+
+ Payment link
+
+
+ ))}
+
+
+
+ } elseShow={{isLoaded && "No invoices to show."}
} />
+ );
+}
+
+InvoiceList.propTypes = {
+ location: PropTypes.object,
+ fetchInvoices: PropTypes.func.isRequired,
+ invoices: PropTypes.array.isRequired,
+};
+
+export default InvoiceList;
diff --git a/frontend/src/store/e-admin-invoice/actions.js b/frontend/src/store/e-admin-invoice/actions.js
new file mode 100644
index 0000000000..4a7a2cac8b
--- /dev/null
+++ b/frontend/src/store/e-admin-invoice/actions.js
@@ -0,0 +1,20 @@
+import api from './api';
+import { dispatchError } from '../util';
+export const RECEIVE_INVOICES = 'RECEIVE_INVOICES';
+export const ERROR_FETCH_INVOICES = 'ERROR_FETCH_INVOICES';
+
+const debug = require('debug')('unleash:e-admin-invoice-actions');
+
+export function fetchInvoices() {
+ debug('Start fetching invoices for hosted customer');
+ return dispatch =>
+ api
+ .fetchAll()
+ .then(value =>
+ dispatch({
+ type: RECEIVE_INVOICES,
+ invoices: value.invoices,
+ })
+ )
+ .catch(dispatchError(dispatch, ERROR_FETCH_INVOICES));
+}
\ No newline at end of file
diff --git a/frontend/src/store/e-admin-invoice/api.js b/frontend/src/store/e-admin-invoice/api.js
new file mode 100644
index 0000000000..0918ad1d68
--- /dev/null
+++ b/frontend/src/store/e-admin-invoice/api.js
@@ -0,0 +1,14 @@
+import { formatApiPath } from '../../utils/format-path';
+import { throwIfNotSuccess, headers } from '../api-helper';
+
+const URI = formatApiPath('api/admin/invoices');
+
+function fetchAll() {
+ return fetch(URI, { headers, credentials: 'include' })
+ .then(throwIfNotSuccess)
+ .then(response => response.json());
+}
+
+export default {
+ fetchAll
+};
diff --git a/frontend/src/store/e-admin-invoice/index.js b/frontend/src/store/e-admin-invoice/index.js
new file mode 100644
index 0000000000..9def0dc310
--- /dev/null
+++ b/frontend/src/store/e-admin-invoice/index.js
@@ -0,0 +1,13 @@
+import { List } from 'immutable';
+import { RECEIVE_INVOICES } from './actions';
+
+const store = (state = new List(), action) => {
+ switch (action.type) {
+ case RECEIVE_INVOICES:
+ return new List(action.invoices);
+ default:
+ return state;
+ }
+};
+
+export default store;
diff --git a/frontend/src/store/index.js b/frontend/src/store/index.js
index d3726c52a5..230cad2e40 100644
--- a/frontend/src/store/index.js
+++ b/frontend/src/store/index.js
@@ -19,6 +19,7 @@ import addons from './addons';
import apiAdmin from './e-api-admin';
import authAdmin from './e-admin-auth';
import apiCalls from './api-calls';
+import invoiceAdmin from './e-admin-invoice';
const unleashStore = combineReducers({
features,
@@ -41,6 +42,7 @@ const unleashStore = combineReducers({
apiAdmin,
authAdmin,
apiCalls,
+ invoiceAdmin,
});
export default unleashStore;
diff --git a/frontend/src/store/ui-config/__tests__/__snapshots__/ui-config-store.test.js.snap b/frontend/src/store/ui-config/__tests__/__snapshots__/ui-config-store.test.js.snap
index 99f14a0b22..ad585c9125 100644
--- a/frontend/src/store/ui-config/__tests__/__snapshots__/ui-config-store.test.js.snap
+++ b/frontend/src/store/ui-config/__tests__/__snapshots__/ui-config-store.test.js.snap
@@ -7,10 +7,10 @@ Object {
"headerBackground": undefined,
"links": Array [
Object {
- "href": "https://unleash.github.io?source=oss",
+ "href": "https://docs.getunleash.io/docs?source=oss",
"icon": "library_books",
"title": "User documentation",
- "value": "User documentation",
+ "value": "Documentation",
},
Object {
"href": "https://github.com/Unleash",
@@ -32,10 +32,10 @@ Object {
"headerBackground": "red",
"links": Array [
Object {
- "href": "https://unleash.github.io?source=oss",
+ "href": "https://docs.getunleash.io/docs?source=oss",
"icon": "library_books",
"title": "User documentation",
- "value": "User documentation",
+ "value": "Documentation",
},
Object {
"href": "https://github.com/Unleash",
@@ -57,10 +57,10 @@ Object {
"headerBackground": "black",
"links": Array [
Object {
- "href": "https://unleash.github.io?source=oss",
+ "href": "https://docs.getunleash.io/docs?source=oss",
"icon": "library_books",
"title": "User documentation",
- "value": "User documentation",
+ "value": "Documentation",
},
Object {
"href": "https://github.com/Unleash",
diff --git a/frontend/src/store/ui-config/index.js b/frontend/src/store/ui-config/index.js
index 1aa3f6e4ce..f5bd7ebf65 100644
--- a/frontend/src/store/ui-config/index.js
+++ b/frontend/src/store/ui-config/index.js
@@ -20,9 +20,9 @@ const DEFAULT = new $Map({
flags: {},
links: [
{
- value: 'User documentation',
+ value: 'Documentation',
icon: 'library_books',
- href: 'https://unleash.github.io?source=oss',
+ href: 'https://docs.getunleash.io/docs?source=oss',
title: 'User documentation',
},
{