222 lines
6.5 KiB
TypeScript
222 lines
6.5 KiB
TypeScript
import type { IInvoiceItem } from 'src/types/invoice';
|
|
|
|
import { useState, useCallback } from 'react';
|
|
|
|
import Box from '@mui/material/Box';
|
|
import Card from '@mui/material/Card';
|
|
import Table from '@mui/material/Table';
|
|
import Stack from '@mui/material/Stack';
|
|
import Divider from '@mui/material/Divider';
|
|
import TableRow from '@mui/material/TableRow';
|
|
import TableHead from '@mui/material/TableHead';
|
|
import TableBody from '@mui/material/TableBody';
|
|
import TableCell from '@mui/material/TableCell';
|
|
import Typography from '@mui/material/Typography';
|
|
|
|
import { fDate } from 'src/utils/format-time';
|
|
import { fCurrency } from 'src/utils/format-number';
|
|
|
|
import { INVOICE_STATUS_OPTIONS } from 'src/_mock';
|
|
|
|
import { Label } from 'src/components/label';
|
|
import { Scrollbar } from 'src/components/scrollbar';
|
|
|
|
import { InvoiceToolbar } from './invoice-toolbar';
|
|
import { InvoiceTotalSummary } from './invoice-total-summary';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { changeStatus } from 'src/actions/invoice';
|
|
import { toast } from 'src/components/snackbar';
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
type Props = {
|
|
invoice: IInvoiceItem;
|
|
};
|
|
|
|
export function InvoiceDetails({ invoice }: Props) {
|
|
const { t } = useTranslation();
|
|
const [currentStatus, setCurrentStatus] = useState(invoice?.status);
|
|
|
|
const handleChangeStatus = useCallback(
|
|
(event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
|
// setCurrentStatus(event.target.value);
|
|
|
|
try {
|
|
changeStatus(invoice.id, event.target.value);
|
|
setCurrentStatus(event.target.value);
|
|
|
|
toast.success('status changed!');
|
|
} catch (error) {
|
|
console.error(error);
|
|
toast.warning('error during changing status');
|
|
}
|
|
},
|
|
[]
|
|
);
|
|
|
|
const renderFooter = () => (
|
|
<Box
|
|
sx={{
|
|
py: 3,
|
|
gap: 2,
|
|
display: 'flex',
|
|
flexWrap: 'wrap',
|
|
alignItems: 'center',
|
|
}}
|
|
>
|
|
<div>
|
|
<Typography variant="subtitle2" sx={{ mb: 0.5 }}>
|
|
NOTES
|
|
</Typography>
|
|
<Typography variant="body2">
|
|
We appreciate your business. Should you need us to add VAT or extra notes let us know!
|
|
</Typography>
|
|
</div>
|
|
|
|
<Box sx={{ flexGrow: { md: 1 }, textAlign: { md: 'right' } }}>
|
|
<Typography variant="subtitle2" sx={{ mb: 0.5 }}>
|
|
Have a question?
|
|
</Typography>
|
|
<Typography variant="body2">support@minimals.cc</Typography>
|
|
</Box>
|
|
</Box>
|
|
);
|
|
|
|
const renderList = () => (
|
|
<Scrollbar sx={{ mt: 5 }}>
|
|
<Table sx={{ minWidth: 960 }}>
|
|
<TableHead>
|
|
<TableRow>
|
|
<TableCell width={40}>#</TableCell>
|
|
<TableCell sx={{ typography: 'subtitle2' }}>Description</TableCell>
|
|
<TableCell>Qty</TableCell>
|
|
<TableCell align="right">Unit price</TableCell>
|
|
<TableCell align="right">Total</TableCell>
|
|
</TableRow>
|
|
</TableHead>
|
|
|
|
<TableBody>
|
|
{invoice?.items.map((row, index) => (
|
|
<TableRow key={index}>
|
|
<TableCell>{index + 1}</TableCell>
|
|
|
|
<TableCell>
|
|
<Box sx={{ maxWidth: 560 }}>
|
|
<Typography variant="subtitle2">{row.title}</Typography>
|
|
|
|
<Typography variant="body2" sx={{ color: 'text.secondary' }} noWrap>
|
|
{row.description}
|
|
</Typography>
|
|
</Box>
|
|
</TableCell>
|
|
|
|
<TableCell>{row.quantity}</TableCell>
|
|
<TableCell align="right">{fCurrency(row.price)}</TableCell>
|
|
<TableCell align="right">{fCurrency(row.price * row.quantity)}</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</Scrollbar>
|
|
);
|
|
|
|
return (
|
|
<>
|
|
<InvoiceToolbar
|
|
invoice={invoice}
|
|
currentStatus={currentStatus || ''}
|
|
onChangeStatus={handleChangeStatus}
|
|
statusOptions={INVOICE_STATUS_OPTIONS}
|
|
/>
|
|
|
|
<Card sx={{ pt: 5, px: 5 }}>
|
|
<Box
|
|
sx={{
|
|
rowGap: 5,
|
|
display: 'grid',
|
|
alignItems: 'center',
|
|
gridTemplateColumns: { xs: 'repeat(1, 1fr)', sm: 'repeat(2, 1fr)' },
|
|
}}
|
|
>
|
|
<Box
|
|
component="img"
|
|
alt="Invoice logo"
|
|
src="/logo/logo-single.svg"
|
|
sx={{ width: 48, height: 48 }}
|
|
/>
|
|
|
|
<Stack spacing={1} sx={{ alignItems: { xs: 'flex-start', md: 'flex-end' } }}>
|
|
<Label
|
|
variant="soft"
|
|
color={
|
|
(currentStatus === 'paid' && 'success') ||
|
|
(currentStatus === 'pending' && 'warning') ||
|
|
(currentStatus === 'overdue' && 'error') ||
|
|
'default'
|
|
}
|
|
>
|
|
{currentStatus}
|
|
</Label>
|
|
|
|
<Typography variant="h6">{invoice?.invoiceNumber}</Typography>
|
|
</Stack>
|
|
|
|
<Stack sx={{ typography: 'body2' }}>
|
|
<Typography variant="subtitle2" sx={{ mb: 1 }}>
|
|
Invoice from
|
|
</Typography>
|
|
{invoice?.invoiceFrom.name}
|
|
<br />
|
|
{invoice?.invoiceFrom.fullAddress}
|
|
<br />
|
|
Phone: {invoice?.invoiceFrom.phoneNumber}
|
|
<br />
|
|
</Stack>
|
|
|
|
<Stack sx={{ typography: 'body2' }}>
|
|
<Typography variant="subtitle2" sx={{ mb: 1 }}>
|
|
Invoice to
|
|
</Typography>
|
|
{invoice?.invoiceTo.name}
|
|
<br />
|
|
{invoice?.invoiceTo.fullAddress}
|
|
<br />
|
|
Phone: {invoice?.invoiceTo.phoneNumber}
|
|
<br />
|
|
</Stack>
|
|
|
|
<Stack sx={{ typography: 'body2' }}>
|
|
<Typography variant="subtitle2" sx={{ mb: 1 }}>
|
|
Date create
|
|
</Typography>
|
|
{fDate(invoice?.createDate)}
|
|
</Stack>
|
|
|
|
<Stack sx={{ typography: 'body2' }}>
|
|
<Typography variant="subtitle2" sx={{ mb: 1 }}>
|
|
Due date
|
|
</Typography>
|
|
{fDate(invoice?.dueDate)}
|
|
</Stack>
|
|
</Box>
|
|
|
|
{renderList()}
|
|
|
|
<Divider sx={{ borderStyle: 'dashed' }} />
|
|
|
|
<InvoiceTotalSummary
|
|
taxes={invoice?.taxes}
|
|
subtotal={invoice?.subtotal}
|
|
discount={invoice?.discount}
|
|
shipping={invoice?.shipping}
|
|
totalAmount={invoice?.totalAmount}
|
|
/>
|
|
|
|
<Divider sx={{ mt: 5, borderStyle: 'dashed' }} />
|
|
|
|
{renderFooter()}
|
|
</Card>
|
|
</>
|
|
);
|
|
}
|