181 lines
9.0 KiB
JavaScript
181 lines
9.0 KiB
JavaScript
import { useState } from 'react';
|
|
import { useNavigate, useParams } from 'react-router-dom';
|
|
import {
|
|
Box, Card, Stack, Button, Typography, Chip, Divider, IconButton,
|
|
Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Grid,
|
|
Dialog, DialogTitle, DialogContent, DialogActions, TextField
|
|
} from '@mui/material';
|
|
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
|
import PrintOutlinedIcon from '@mui/icons-material/PrintOutlined';
|
|
import PaymentsOutlinedIcon from '@mui/icons-material/PaymentsOutlined';
|
|
|
|
import PageHeader from '@/components/PageHeader';
|
|
import Logo from '@/components/Logo';
|
|
import { invoices, invoiceLineItems, tenants } from '@/data/mock';
|
|
import { inr } from '@/utils/format';
|
|
|
|
export default function InvoicePreview() {
|
|
const navigate = useNavigate();
|
|
const { id } = useParams();
|
|
const [payOpen, setPayOpen] = useState(false);
|
|
|
|
const invoice = invoices.find((i) => String(i.id) === String(id)) || invoices[0];
|
|
const tenant = tenants[0];
|
|
|
|
const subTotal = invoiceLineItems.reduce((a, l) => a + l.amount, 0);
|
|
const discount = Math.round(subTotal * 0.05);
|
|
const taxable = subTotal - discount;
|
|
const tax = Math.round(taxable * 0.18);
|
|
const grandTotal = taxable + tax;
|
|
|
|
return (
|
|
<>
|
|
<PageHeader
|
|
title="Invoice Details"
|
|
breadcrumbs={[{ label: 'Invoice', to: '/invoice' }, { label: 'Details' }]}
|
|
action={
|
|
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={1.5} alignItems="center">
|
|
<IconButton onClick={() => navigate('/invoice')} sx={{ border: 1, borderColor: 'grey.300' }}>
|
|
<ArrowBackIcon fontSize="small" />
|
|
</IconButton>
|
|
<Chip label={invoice.invoiceId} sx={{ bgcolor: 'warning.lighter', color: 'warning.dark', fontWeight: 600 }} />
|
|
<Button variant="outlined" startIcon={<PaymentsOutlinedIcon />} onClick={() => setPayOpen(true)}>
|
|
Update Payment
|
|
</Button>
|
|
<Button variant="contained" startIcon={<PrintOutlinedIcon />} onClick={() => window.print()}>
|
|
Print
|
|
</Button>
|
|
</Stack>
|
|
}
|
|
/>
|
|
|
|
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
|
|
<Card sx={{ width: '100%', maxWidth: 860, p: { xs: 3, sm: 5 } }}>
|
|
{/* Top band */}
|
|
<Stack direction={{ xs: 'column', sm: 'row' }} justifyContent="space-between" spacing={3}>
|
|
<Box>
|
|
<Logo />
|
|
<Typography variant="subtitle2" sx={{ mt: 2, fontWeight: 700, color: 'grey.800' }}>From</Typography>
|
|
<Typography variant="body2" sx={{ fontWeight: 600 }}>Doormile Logistics Pvt. Ltd.</Typography>
|
|
<Typography variant="body2" color="text.secondary">No. 7, Brigade Road</Typography>
|
|
<Typography variant="body2" color="text.secondary">Bengaluru, Karnataka 560001</Typography>
|
|
<Typography variant="body2" color="text.secondary">GSTIN: 29ABCDE1234F1Z5</Typography>
|
|
<Typography variant="body2" color="text.secondary">billing@doormile.in</Typography>
|
|
</Box>
|
|
<Box sx={{ textAlign: { sm: 'right' } }}>
|
|
<Typography variant="h4" sx={{ fontWeight: 800, color: 'primary.main' }}>INVOICE</Typography>
|
|
<Typography variant="subtitle2" sx={{ mt: 2, fontWeight: 700, color: 'grey.800' }}>To</Typography>
|
|
<Typography variant="body2" sx={{ fontWeight: 600 }}>{tenant.name}</Typography>
|
|
<Typography variant="body2" color="text.secondary">{tenant.contact}</Typography>
|
|
<Typography variant="body2" color="text.secondary">{tenant.address}</Typography>
|
|
<Typography variant="body2" color="text.secondary">{tenant.city} {tenant.postcode}</Typography>
|
|
<Typography variant="body2" color="text.secondary">{tenant.email}</Typography>
|
|
</Box>
|
|
</Stack>
|
|
|
|
<Divider sx={{ my: 3 }} />
|
|
|
|
{/* Invoice meta */}
|
|
<Grid container spacing={2}>
|
|
<Grid item xs={6} sm={3}>
|
|
<Typography variant="caption" color="text.secondary">Invoice No</Typography>
|
|
<Typography variant="body2" sx={{ fontWeight: 600 }}>{invoice.invoiceId}</Typography>
|
|
</Grid>
|
|
<Grid item xs={6} sm={3}>
|
|
<Typography variant="caption" color="text.secondary">Date</Typography>
|
|
<Typography variant="body2" sx={{ fontWeight: 600 }}>{invoice.invoiceDate}</Typography>
|
|
</Grid>
|
|
<Grid item xs={6} sm={3}>
|
|
<Typography variant="caption" color="text.secondary">Due Date</Typography>
|
|
<Typography variant="body2" sx={{ fontWeight: 600 }}>{invoice.dueDate}</Typography>
|
|
</Grid>
|
|
<Grid item xs={6} sm={3}>
|
|
<Typography variant="caption" color="text.secondary">Period</Typography>
|
|
<Typography variant="body2" sx={{ fontWeight: 600 }}>{invoice.period}</Typography>
|
|
</Grid>
|
|
</Grid>
|
|
|
|
{/* Line items */}
|
|
<TableContainer sx={{ mt: 3 }}>
|
|
<Table size="small">
|
|
<TableHead>
|
|
<TableRow sx={{ '& th': { bgcolor: 'grey.50', fontWeight: 700 } }}>
|
|
<TableCell>S.No</TableCell>
|
|
<TableCell>Particulars</TableCell>
|
|
<TableCell>Unit</TableCell>
|
|
<TableCell align="center">Quantity</TableCell>
|
|
<TableCell align="right">Rate</TableCell>
|
|
<TableCell align="right">Other Charges</TableCell>
|
|
<TableCell align="right">Amount</TableCell>
|
|
</TableRow>
|
|
</TableHead>
|
|
<TableBody>
|
|
{invoiceLineItems.map((l, idx) => (
|
|
<TableRow key={idx}>
|
|
<TableCell>{idx + 1}</TableCell>
|
|
<TableCell>{l.particulars}</TableCell>
|
|
<TableCell>{l.unit}</TableCell>
|
|
<TableCell align="center">{l.qty}</TableCell>
|
|
<TableCell align="right">{inr(l.rate)}</TableCell>
|
|
<TableCell align="right">{inr(l.other)}</TableCell>
|
|
<TableCell align="right" sx={{ fontWeight: 600 }}>{inr(l.amount)}</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</TableContainer>
|
|
|
|
{/* Totals */}
|
|
<Stack alignItems="flex-end" sx={{ mt: 3 }}>
|
|
<Box sx={{ width: { xs: '100%', sm: 320 } }}>
|
|
<Stack direction="row" justifyContent="space-between" sx={{ py: 0.75 }}>
|
|
<Typography variant="body2" color="text.secondary">Sub Total</Typography>
|
|
<Typography variant="body2">{inr(subTotal)}</Typography>
|
|
</Stack>
|
|
<Stack direction="row" justifyContent="space-between" sx={{ py: 0.75 }}>
|
|
<Typography variant="body2" color="text.secondary">Discount (5%)</Typography>
|
|
<Typography variant="body2">- {inr(discount)}</Typography>
|
|
</Stack>
|
|
<Stack direction="row" justifyContent="space-between" sx={{ py: 0.75 }}>
|
|
<Typography variant="body2" color="text.secondary">Tax (18% GST)</Typography>
|
|
<Typography variant="body2">{inr(tax)}</Typography>
|
|
</Stack>
|
|
<Divider sx={{ my: 1 }} />
|
|
<Stack direction="row" justifyContent="space-between" sx={{ py: 0.5 }}>
|
|
<Typography variant="subtitle1" sx={{ fontWeight: 700 }}>Grand Total</Typography>
|
|
<Typography variant="subtitle1" sx={{ fontWeight: 800, color: 'primary.main' }}>{inr(grandTotal)}</Typography>
|
|
</Stack>
|
|
</Box>
|
|
</Stack>
|
|
|
|
{/* Notes + accent */}
|
|
<Box sx={{ mt: 4 }}>
|
|
<Typography variant="subtitle2" sx={{ fontWeight: 700, color: 'grey.800' }}>Notes & Terms</Typography>
|
|
<Typography variant="body2" color="text.secondary" sx={{ mt: 0.5 }}>
|
|
Payment is due within 15 days of the invoice date. Please make payments via NEFT/RTGS to the
|
|
registered account. A 1.5% monthly interest applies to overdue balances. This is a
|
|
computer-generated invoice and does not require a signature.
|
|
</Typography>
|
|
</Box>
|
|
<Box sx={{ mt: 3, height: 4, borderRadius: 2, bgcolor: 'primary.main' }} />
|
|
</Card>
|
|
</Box>
|
|
|
|
{/* Update Payment Dialog */}
|
|
<Dialog open={payOpen} onClose={() => setPayOpen(false)} maxWidth="xs" fullWidth>
|
|
<DialogTitle>Update Payment</DialogTitle>
|
|
<DialogContent>
|
|
<Stack spacing={2.5} sx={{ mt: 1 }}>
|
|
<TextField label="Reference No" type="number" fullWidth placeholder="Enter transaction reference" />
|
|
<TextField label="Remarks" fullWidth multiline minRows={3} placeholder="Add a remark for this payment" />
|
|
</Stack>
|
|
</DialogContent>
|
|
<DialogActions sx={{ px: 3, pb: 2 }}>
|
|
<Button onClick={() => setPayOpen(false)} color="inherit">Cancel</Button>
|
|
<Button variant="contained" onClick={() => setPayOpen(false)}>Update</Button>
|
|
</DialogActions>
|
|
</Dialog>
|
|
</>
|
|
);
|
|
}
|