Files
doormile_app_web/src/pages/invoice/InvoicePreview.jsx
2026-06-05 17:28:05 +05:30

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 &amp; 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>
</>
);
}