From 25ca7b721687e3d1c744287c3739391a30e7ac86 Mon Sep 17 00:00:00 2001 From: Youssef Khedher Date: Fri, 4 Feb 2022 13:41:13 +0100 Subject: [PATCH] refactor: add useInvoices hook (#656) * refactor: add useInvoices hook * refactor: invoiceList and InvoiceAdminPage * fix: declare path outside of the hook Co-authored-by: Fredrik Strand Oseberg --- .../api-token/ApiTokenList/ApiTokenList.tsx | 2 +- .../{index.js => InvoiceAdminPage.tsx} | 11 +- .../component/admin/invoice/InvoiceList.tsx | 122 ++++++++++++++++++ .../admin/invoice/invoice-container.js | 11 -- .../component/admin/invoice/invoice-list.jsx | 88 ------------- frontend/src/component/menu/routes.js | 2 +- .../api/getters/useInvoices/useInvoices.ts | 37 ++++++ frontend/src/interfaces/invoice.ts | 8 ++ 8 files changed, 171 insertions(+), 110 deletions(-) rename frontend/src/component/admin/invoice/{index.js => InvoiceAdminPage.tsx} (75%) create mode 100644 frontend/src/component/admin/invoice/InvoiceList.tsx delete mode 100644 frontend/src/component/admin/invoice/invoice-container.js delete mode 100644 frontend/src/component/admin/invoice/invoice-list.jsx create mode 100644 frontend/src/hooks/api/getters/useInvoices/useInvoices.ts create mode 100644 frontend/src/interfaces/invoice.ts diff --git a/frontend/src/component/admin/api-token/ApiTokenList/ApiTokenList.tsx b/frontend/src/component/admin/api-token/ApiTokenList/ApiTokenList.tsx index cbdfc0a8e7..146ea60f05 100644 --- a/frontend/src/component/admin/api-token/ApiTokenList/ApiTokenList.tsx +++ b/frontend/src/component/admin/api-token/ApiTokenList/ApiTokenList.tsx @@ -86,7 +86,7 @@ const ApiTokenList = ({ location }: IApiTokenList) => { refetch(); setToastData({ type: 'success', - show: true, + title: 'Deleted successfully', text: 'Successfully deleted API token.', }); }; diff --git a/frontend/src/component/admin/invoice/index.js b/frontend/src/component/admin/invoice/InvoiceAdminPage.tsx similarity index 75% rename from frontend/src/component/admin/invoice/index.js rename to frontend/src/component/admin/invoice/InvoiceAdminPage.tsx index 53f3a37bcf..0eca248673 100644 --- a/frontend/src/component/admin/invoice/index.js +++ b/frontend/src/component/admin/invoice/InvoiceAdminPage.tsx @@ -1,14 +1,12 @@ import { useContext } from 'react'; -import PropTypes from 'prop-types'; -import InvoiceList from './invoice-container'; +import InvoiceList from './InvoiceList'; import AccessContext from '../../../contexts/AccessContext'; import { ADMIN } from '../../providers/AccessProvider/permissions'; import ConditionallyRender from '../../common/ConditionallyRender'; import { Alert } from '@material-ui/lab'; -const InvoiceAdminPage = ({ history }) => { +const InvoiceAdminPage = () => { const { hasAccess } = useContext(AccessContext); - return (
{ ); }; -InvoiceAdminPage.propTypes = { - match: PropTypes.object.isRequired, - history: PropTypes.object.isRequired, -}; - 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..f9a8c78c8d --- /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 '@material-ui/core'; +import OpenInNew from '@material-ui/icons/OpenInNew'; +import { formatDateWithLocale } from '../../common/util'; +import PageContent from '../../common/PageContent'; +import HeaderTitle from '../../common/HeaderTitle'; +import ConditionallyRender from '../../common/ConditionallyRender'; +import { formatApiPath } from '../../../utils/format-path'; +import useInvoices from '../../../hooks/api/getters/useInvoices/useInvoices'; +import { useLocation } from 'react-router-dom'; +import { IInvoice } from '../../../interfaces/invoice'; + +const PORTAL_URL = formatApiPath('api/admin/invoices/portal'); + +const InvoiceList = () => { + const { refetchInvoices, invoices } = useInvoices(); + const [isLoaded, setLoaded] = useState(false); + const location = useLocation(); + + 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.amountFomratted} + + + {item.status} + + + {item.dueDate && + formatDateWithLocale( + item.dueDate, + location.locale + )} + + + PDF + + + + Payment link + + + + ))} + +
+
+
+ } + elseShow={
{isLoaded && 'No invoices to show.'}
} + /> + ); +}; +export default InvoiceList; diff --git a/frontend/src/component/admin/invoice/invoice-container.js b/frontend/src/component/admin/invoice/invoice-container.js deleted file mode 100644 index 8064dadc48..0000000000 --- a/frontend/src/component/admin/invoice/invoice-container.js +++ /dev/null @@ -1,11 +0,0 @@ -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/component/admin/invoice/invoice-list.jsx b/frontend/src/component/admin/invoice/invoice-list.jsx deleted file mode 100644 index 76b51f60de..0000000000 --- a/frontend/src/component/admin/invoice/invoice-list.jsx +++ /dev/null @@ -1,88 +0,0 @@ -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 '../../common/util'; -import PageContent from '../../common/PageContent'; -import HeaderTitle from '../../common/HeaderTitle'; -import ConditionallyRender from '../../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} - - - { item.dueDate && 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/component/menu/routes.js b/frontend/src/component/menu/routes.js index 9f48947b11..3560476aa8 100644 --- a/frontend/src/component/menu/routes.js +++ b/frontend/src/component/menu/routes.js @@ -14,8 +14,8 @@ import AddonsCreate from '../../page/addons/create'; import AddonsEdit from '../../page/addons/edit'; import Admin from '../admin'; import AdminApi from '../admin/api'; +import AdminInvoice from '../admin/invoice/InvoiceAdminPage'; import AdminUsers from '../admin/users/UsersAdmin'; -import AdminInvoice from '../admin/invoice'; import AdminAuth from '../admin/auth'; import Login from '../user/Login/Login'; import { P, C, E, EEA, RE } from '../common/flags'; diff --git a/frontend/src/hooks/api/getters/useInvoices/useInvoices.ts b/frontend/src/hooks/api/getters/useInvoices/useInvoices.ts new file mode 100644 index 0000000000..4f12c5a850 --- /dev/null +++ b/frontend/src/hooks/api/getters/useInvoices/useInvoices.ts @@ -0,0 +1,37 @@ +import useSWR, { mutate, SWRConfiguration } from 'swr'; +import { useState, useEffect } from 'react'; +import { formatApiPath } from '../../../../utils/format-path'; +import handleErrorResponses from '../httpErrorResponseHandler'; + +const KEY = `api/admin/invoices`; +const path = formatApiPath(KEY); + +const useInvoices = (options: SWRConfiguration = {}) => { + const fetcher = () => { + return fetch(path, { + method: 'GET', + }) + .then(handleErrorResponses('Invoices')) + .then(res => res.json()); + }; + + const { data, error } = useSWR(KEY, fetcher, options); + const [loading, setLoading] = useState(!error && !data); + + const refetchInvoices = () => { + mutate(KEY); + }; + + useEffect(() => { + setLoading(!error && !data); + }, [data, error]); + + return { + invoices: data?.invoices || [], + error, + loading, + refetchInvoices, + }; +}; + +export default useInvoices; diff --git a/frontend/src/interfaces/invoice.ts b/frontend/src/interfaces/invoice.ts new file mode 100644 index 0000000000..db49e34d9b --- /dev/null +++ b/frontend/src/interfaces/invoice.ts @@ -0,0 +1,8 @@ +export interface IInvoice { + amountFomratted: string; + invoicePDF: string; + invoiceURL: string; + paid: boolean; + status: string; + dueDate?: Date; +}