parent
5abd820237
commit
87cb5f0c67
5 changed files with 164 additions and 0 deletions
@ -0,0 +1,111 @@ |
||||
import React, { useState, useEffect } from 'react'; |
||||
import Link from 'next/link'; |
||||
import Head from 'next/head'; |
||||
import BackButton from '../components/BackButton.js'; |
||||
import ErrorAlert from '../components/ErrorAlert.js'; |
||||
import * as API from '../api.js'; |
||||
import { useRouter } from 'next/router'; |
||||
import { wildcardCheck, wildcardMatches } from '../util.js'; |
||||
|
||||
const statusColors = { |
||||
'cancelled': 'secondary', |
||||
'pending': 'primary', |
||||
'paid': 'success', |
||||
'unpaid': 'warning', |
||||
'overdue': 'danger', |
||||
'other': 'info', |
||||
}; |
||||
|
||||
export default function Billing(props) { |
||||
|
||||
const router = useRouter(); |
||||
const [state, dispatch] = useState(props); |
||||
const [error, setError] = useState(); |
||||
|
||||
useEffect(() => { |
||||
if (!state.invoices) { |
||||
API.getBilling(dispatch, setError, router); |
||||
} |
||||
}, []); |
||||
|
||||
if (!state.invoices) { |
||||
return ( |
||||
<div className='d-flex flex-column'> |
||||
{error && <ErrorAlert error={error} />} |
||||
<div className='text-center mb-4'> |
||||
<div className='spinner-border mt-5' role='status'> |
||||
<span className='visually-hidden'>Loading...</span> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
const { invoices } = state; |
||||
|
||||
return ( |
||||
<> |
||||
|
||||
<Head> |
||||
<title>Billing</title> |
||||
</Head> |
||||
|
||||
<h5 className='fw-bold'> |
||||
Invoices: |
||||
</h5> |
||||
|
||||
{/* Domains table */} |
||||
<div className='table-responsive round-shadow'> |
||||
<table className='table text-nowrap'> |
||||
<tbody> |
||||
|
||||
<tr className='align-middle'> |
||||
<th> |
||||
Description |
||||
</th> |
||||
<th> |
||||
Date |
||||
</th> |
||||
<th> |
||||
Amount |
||||
</th> |
||||
<th> |
||||
Status |
||||
</th> |
||||
</tr> |
||||
|
||||
{invoices.map(inv => (<tr key={inv._id} className='align-middle'> |
||||
<td> |
||||
{inv.description} |
||||
</td> |
||||
<td suppressHydrationWarning={true}> |
||||
{new Date(inv.date).toLocaleString()} |
||||
</td> |
||||
<td> |
||||
${(inv.amount/100).toFixed(2)} |
||||
</td> |
||||
<td> |
||||
<span className={`badge rounded-pill text-bg-${statusColors[inv.status]} text-uppercase`}> |
||||
{inv.status} |
||||
</span> |
||||
</td> |
||||
</tr>))} |
||||
|
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
|
||||
{error && <span className='mx-2'><ErrorAlert error={error} /></span>} |
||||
|
||||
{/* back to account */} |
||||
<BackButton to='/account' /> |
||||
|
||||
</> |
||||
); |
||||
|
||||
} |
||||
|
||||
export async function getServerSideProps({ req, res, query, resolvedUrl, locale, locales, defaultLocale}) { |
||||
return { props: res.locals.data }; |
||||
} |
||||
|
Loading…
Reference in new issue