Add project invoices to project billing page

This commit is contained in:
Joshen Lim committed 2022-03-21 00:40:45 +01:00
1 parent 9cdc753bdf
commit 2e7ff9eda8
5 files changed
+183 -6

No files matched your search

@@ -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<Props> = ({ projectRef }) => {
const { ui } = useStore()
const [loading, setLoading] = useState<any>(false)
const [page, setPage] = useState(1)
const [count, setCount] = useState(0)
const [invoices, setInvoices] = useState<any>([])
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 (
<div className="my-4 container max-w-4xl space-y-1">
<Loading active={loading}>
<Table
head={[
<Table.th key="header-icon"></Table.th>,
<Table.th key="header-date">Date</Table.th>,
<Table.th key="header-amount">Amount due</Table.th>,
<Table.th key="header-invoice">Invoice number</Table.th>,
<Table.th key="header-download" className="text-right"></Table.th>,
]}
body={
invoices.length === 0 ? (
<Table.tr>
<Table.td colSpan={5} className="p-3 py-12 text-center">
<Typography.Text type="secondary">
{loading ? 'Checking for invoices' : 'No invoices for this project yet'}
</Typography.Text>
</Table.td>
</Table.tr>
) : (
<>
{invoices.map((x: any) => {
return (
<Table.tr key={x.id}>
<Table.td>
<IconFileText size="xxl" />
</Table.td>
<Table.td>
<Typography.Text>
{new Date(x.period_end * 1000).toLocaleString()}
</Typography.Text>
</Table.td>
<Table.td>
<Typography.Text>
{x.subtotal >= 0
? `$${x.subtotal / 100}`
: `-$${Math.abs(x.subtotal / 100)}`}
</Typography.Text>
</Table.td>
<Table.td>
<Typography.Text>{x.number}</Typography.Text>
</Table.td>
<Table.td className="align-right">
<div className="flex items-center space-x-2 justify-end">
<a href={x.hosted_invoice_url} target="_blank">
<Button type="outline">View invoice</Button>
</a>
<form method="get" action={x.invoice_pdf}>
<Button type="outline" icon={<IconDownload />} htmlType="submit" />
</form>
</div>
</Table.td>
</Table.tr>
)
})}
<Table.tr key="navigation">
<Table.td colSpan={5}>
<div className="flex items-center justify-between">
<p className="text-sm opacity-50">
Showing {offset + 1} to {offset + invoices.length} out of {count} invoices
</p>
<div className="flex items-center space-x-2">
<Button
icon={<IconChevronLeft />}
type="default"
size="tiny"
disabled={page === 1}
onClick={async () => await fetchInvoices(page - 1)}
/>
<Button
icon={<IconChevronRight />}
type="default"
size="tiny"
disabled={page * PAGE_LIMIT >= count}
onClick={async () => await fetchInvoices(page + 1)}
/>
</div>
</div>
</Table.td>
</Table.tr>
</>
)
}
/>
</Loading>
</div>
)
}
export default Invoices
@@ -56,7 +56,9 @@ const Subscription: FC<Props> = ({
<div className="bg-panel-body-light dark:bg-panel-body-dark">
<div className="px-6 pt-4 flex items-center justify-between">
<div className="flex flex-col">
<p className="text-sm">{showProjectName ? project.name : 'Current subscription'}</p>
<p className="text-scale-1100 text-sm">
{showProjectName ? project.name : 'Current subscription'}
</p>
<h3 className="text-xl mb-0">{subscription?.tier.name ?? '-'}</h3>
</div>
{/* <UpgradeButton paid={paid} projectRef={project.ref} /> */}
@@ -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,
}
@@ -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
}
@@ -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<SettingsProps> = ({ project }) => {
const { ui } = useStore()
const projectRef = ui.selectedProject?.ref
const subscriptionStats = useSubscriptionStats()
const [loading, setLoading] = useState<boolean>(true)
const [subscription, setSubscription] = useState<StripeSubscription>()
const [paygStats, setPaygStats] = useState<Dictionary<number>>()
@@ -163,6 +162,10 @@ const Settings: FC<SettingsProps> = ({ project }) => {
) : (
<ProjectUsage projectRef={project.ref} subscription_id={project.subscription_id} />
)}
<div className="space-y-2">
<h4 className="text-lg">Invoices</h4>
<Invoices projectRef={ui.selectedProject?.ref ?? ''} />
</div>
</div>
)
}