From 2e7ff9eda848e14aa250e2199afe53fad37268e8 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 21 Mar 2022 00:40:45 +0100 Subject: [PATCH] Add project invoices to project billing page --- .../interfaces/Billing/Invoices.tsx | 165 ++++++++++++++++++ .../Billing/Subscription/Subscription.tsx | 4 +- studio/components/interfaces/Billing/index.ts | 3 + .../InvoicesSettings/InvoicesSettings.tsx | 4 + .../project/[ref]/settings/billing/index.tsx | 13 +- 5 files changed, 183 insertions(+), 6 deletions(-) create mode 100644 studio/components/interfaces/Billing/Invoices.tsx diff --git a/studio/components/interfaces/Billing/Invoices.tsx b/studio/components/interfaces/Billing/Invoices.tsx new file mode 100644 index 00000000000..e74b7fdc82b --- /dev/null +++ b/studio/components/interfaces/Billing/Invoices.tsx @@ -0,0 +1,165 @@ +import { FC, useState, useEffect } from 'react' +import { + Button, + Loading, + Typography, + IconFileText, + IconDownload, + IconChevronLeft, + IconChevronRight, +} from '@supabase/ui' + +import { useStore } from 'hooks' +import { API_URL } from 'lib/constants' +import { get, head } from 'lib/common/fetch' +import Table from 'components/to-be-cleaned/Table' + +const PAGE_LIMIT = 10 + +interface Props { + projectRef: string +} + +const Invoices: FC = ({ projectRef }) => { + const { ui } = useStore() + const [loading, setLoading] = useState(false) + + const [page, setPage] = useState(1) + const [count, setCount] = useState(0) + const [invoices, setInvoices] = useState([]) + + const offset = (page - 1) * PAGE_LIMIT + + useEffect(() => { + let cancel = false + const page = 1 + + const fetchInvoiceCount = async () => { + const res = await head(`${API_URL}/projects/${projectRef}/invoices`, ['X-Total-Count']) + if (!cancel) { + if (res.error) { + ui.setNotification({ category: 'error', message: res.error.message }) + } else { + setCount(res['X-Total-Count']) + } + } + } + + setPage(page) + fetchInvoices(page) + fetchInvoiceCount() + + return () => { + cancel = true + } + }, []) + + const fetchInvoices = async (page: number) => { + setLoading(true) + setPage(page) + + const offset = (page - 1) * PAGE_LIMIT + const invoices = await get( + `${API_URL}/projects/${projectRef}/invoices?offset=${offset}&limit=${PAGE_LIMIT}` + ) + + if (invoices.error) { + ui.setNotification({ category: 'error', message: invoices.error.message }) + } else { + setInvoices(invoices) + } + + setLoading(false) + } + + return ( +
+ + , + Date, + Amount due, + Invoice number, + , + ]} + body={ + invoices.length === 0 ? ( + + + + {loading ? 'Checking for invoices' : 'No invoices for this project yet'} + + + + ) : ( + <> + {invoices.map((x: any) => { + return ( + + + + + + + {new Date(x.period_end * 1000).toLocaleString()} + + + + + {x.subtotal >= 0 + ? `$${x.subtotal / 100}` + : `-$${Math.abs(x.subtotal / 100)}`} + + + + {x.number} + + +
+ + + +
+
+
+
+ ) + })} + + +
+

+ Showing {offset + 1} to {offset + invoices.length} out of {count} invoices +

+
+
+
+
+
+ + ) + } + /> + + + ) +} + +export default Invoices diff --git a/studio/components/interfaces/Billing/Subscription/Subscription.tsx b/studio/components/interfaces/Billing/Subscription/Subscription.tsx index 77a2fe7bdec..01c3a93c39b 100644 --- a/studio/components/interfaces/Billing/Subscription/Subscription.tsx +++ b/studio/components/interfaces/Billing/Subscription/Subscription.tsx @@ -56,7 +56,9 @@ const Subscription: FC = ({
-

{showProjectName ? project.name : 'Current subscription'}

+

+ {showProjectName ? project.name : 'Current subscription'} +

{subscription?.tier.name ?? '-'}

{/* */} diff --git a/studio/components/interfaces/Billing/index.ts b/studio/components/interfaces/Billing/index.ts index 27ff4817fd4..6b24fcfe902 100644 --- a/studio/components/interfaces/Billing/index.ts +++ b/studio/components/interfaces/Billing/index.ts @@ -10,6 +10,8 @@ import ComputeSizeSelection from './AddOns/ComputeSizeSelection' import AddNewPaymentMethodModal from './AddNewPaymentMethodModal' import UpdateSuccess from './UpdateSuccess' +import Invoices from './Invoices' + export * from './Subscription/Subscription.types' export * from './PAYGUsage/PAYGUsage.constants' export { @@ -23,4 +25,5 @@ export { ComputeSizeSelection, AddNewPaymentMethodModal, UpdateSuccess, + Invoices, } diff --git a/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx b/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx index 6f37fc272c7..98a89f779ed 100644 --- a/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx +++ b/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx @@ -16,6 +16,10 @@ import Table from 'components/to-be-cleaned/Table' const PAGE_LIMIT = 10 +/** + * Eventually deprecate this - as we move on to show invoices by project on the organization billing page + */ + interface Props { organization: any } diff --git a/studio/pages/project/[ref]/settings/billing/index.tsx b/studio/pages/project/[ref]/settings/billing/index.tsx index 3fbdd3c89d8..0214685bea1 100644 --- a/studio/pages/project/[ref]/settings/billing/index.tsx +++ b/studio/pages/project/[ref]/settings/billing/index.tsx @@ -1,14 +1,14 @@ import dayjs from 'dayjs' import { FC, useEffect, useState } from 'react' import { observer } from 'mobx-react-lite' -import { Typography, Loading, IconArrowRight, Button } from '@supabase/ui' +import { Typography, Loading, IconArrowRight } from '@supabase/ui' import { get as _get, maxBy } from 'lodash' import { Dictionary } from '@supabase/grid' -import { API_URL } from 'lib/constants' -import { useStore, useSubscriptionStats, withAuth } from 'hooks' +import { useStore, withAuth } from 'hooks' import { post, get } from 'lib/common/fetch' import { + API_URL, DATE_FORMAT, STRIPE_PRODUCT_IDS, TIME_PERIODS_REPORTS, @@ -21,6 +21,7 @@ import { Subscription, StripeSubscription, chargeableProducts, + Invoices, } from 'components/interfaces/Billing' import ProjectUsage from 'components/to-be-cleaned/Usage' @@ -51,8 +52,6 @@ const Settings: FC = ({ project }) => { const { ui } = useStore() const projectRef = ui.selectedProject?.ref - const subscriptionStats = useSubscriptionStats() - const [loading, setLoading] = useState(true) const [subscription, setSubscription] = useState() const [paygStats, setPaygStats] = useState>() @@ -163,6 +162,10 @@ const Settings: FC = ({ project }) => { ) : ( )} +
+

Invoices

+ +
) }