updated the ui for the mobile view
This commit is contained in:
@@ -21,8 +21,10 @@ import {
|
||||
TablePagination,
|
||||
TableRow,
|
||||
Tooltip,
|
||||
Typography
|
||||
Typography,
|
||||
useMediaQuery
|
||||
} from '@mui/material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import {
|
||||
MdReceiptLong,
|
||||
MdDashboard,
|
||||
@@ -40,6 +42,7 @@ import { fetchinvoiceinsight, fetchdeliverylist } from 'pages/api/api';
|
||||
import Loader from 'components/Loader';
|
||||
import DebounceSearchBar from 'components/nearle_components/DebounceSearchBar';
|
||||
import { OrdersTableSkeleton } from '../orders/OrdersTableSkeleton';
|
||||
import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard';
|
||||
|
||||
// ============================================================================
|
||||
// Design tokens — shared with deliveries / tenants / customers / pricing /
|
||||
@@ -101,6 +104,8 @@ function formatNumberToRupees(value) {
|
||||
|
||||
const Invoice = () => {
|
||||
const navigate = useNavigate();
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
||||
const [page, setPage] = useState(0);
|
||||
const [rowsPerPage, setRowsPerPage] = useState(10);
|
||||
const [billStatus, setBillStatus] = useState(0);
|
||||
@@ -523,19 +528,183 @@ const Invoice = () => {
|
||||
background: '#fff'
|
||||
}}
|
||||
>
|
||||
<TableContainer
|
||||
sx={{
|
||||
maxHeight: { xs: 'calc(100vh - 220px)', md: 'calc(100vh - 190px)' },
|
||||
'&::-webkit-scrollbar': { width: 10, height: 10 },
|
||||
'&::-webkit-scrollbar-thumb': {
|
||||
backgroundColor: edge(BRAND),
|
||||
borderRadius: 8,
|
||||
'&:hover': { backgroundColor: BRAND }
|
||||
},
|
||||
'&::-webkit-scrollbar-track': { backgroundColor: DT.surfaceAlt }
|
||||
}}
|
||||
>
|
||||
<Table stickyHeader sx={{ minWidth: { xs: 880, md: 1060 } }}>
|
||||
{isMobile ? (
|
||||
<>
|
||||
{isDeliveryLoading ? (
|
||||
<Box sx={{ p: 1.5 }}>
|
||||
<OrdersTableSkeleton col={4} />
|
||||
</Box>
|
||||
) : pagedList.length === 0 ? (
|
||||
<Stack alignItems="center" spacing={1.5} sx={{ py: 6, px: 2 }}>
|
||||
<Avatar sx={{ width: 64, height: 64, bgcolor: soft('#94a3b8'), color: DT.textMuted }}>
|
||||
<MdReceiptLong size={28} />
|
||||
</Avatar>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 700, color: DT.textPrimary }}>
|
||||
No invoices to show
|
||||
</Typography>
|
||||
<Typography variant="caption" sx={{ color: DT.textSecondary, textAlign: 'center' }}>
|
||||
{searchword
|
||||
? 'Try a different keyword.'
|
||||
: `No ${activeMeta.label.toLowerCase()} invoices for this filter.`}
|
||||
</Typography>
|
||||
</Stack>
|
||||
) : (
|
||||
<MobileCardList>
|
||||
{pagedList.map((item, index) => {
|
||||
const overdue =
|
||||
billStatus === 2 ||
|
||||
(item.duedate && dayjs(item.duedate).isBefore(dayjs(), 'day') && billStatus !== 3);
|
||||
return (
|
||||
<MobileCard
|
||||
key={item.invoiceno || index}
|
||||
accent={BRAND}
|
||||
header={
|
||||
<Stack direction="row" alignItems="flex-start" justifyContent="space-between" spacing={1}>
|
||||
<Stack direction="row" alignItems="center" spacing={1} sx={{ minWidth: 0 }}>
|
||||
<AccentAvatar color={BRAND} size={36}>
|
||||
<MdGroups size={18} />
|
||||
</AccentAvatar>
|
||||
<Stack spacing={0.25} sx={{ minWidth: 0 }}>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 700, color: DT.textPrimary }} noWrap>
|
||||
{item.tenantname || '—'}
|
||||
</Typography>
|
||||
{item.contactperson && (
|
||||
<Typography variant="caption" sx={{ color: DT.textSecondary }} noWrap>
|
||||
{item.contactperson}
|
||||
</Typography>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Tooltip title="Preview invoice" placement="left">
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => {
|
||||
setIsLoader(true);
|
||||
setTimeout(() => {
|
||||
setIsLoader(false);
|
||||
navigate('/nearle/invoice/preview', { state: item });
|
||||
}, 500);
|
||||
}}
|
||||
sx={{
|
||||
flexShrink: 0,
|
||||
bgcolor: soft(BRAND),
|
||||
color: BRAND,
|
||||
border: `1px solid ${edge(BRAND)}`,
|
||||
'&:hover': { bgcolor: BRAND, color: '#fff' }
|
||||
}}
|
||||
>
|
||||
<MdVisibility size={16} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
}
|
||||
>
|
||||
<MobileFieldGrid>
|
||||
<MobileField label="Invoice ID">
|
||||
<Box
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: 0.5,
|
||||
px: 1,
|
||||
py: 0.375,
|
||||
borderRadius: 999,
|
||||
bgcolor: tint('#0ea5e9'),
|
||||
border: `1px solid ${edge('#0ea5e9')}`,
|
||||
color: '#0ea5e9',
|
||||
fontSize: 11,
|
||||
fontWeight: 800
|
||||
}}
|
||||
>
|
||||
<MdReceiptLong size={12} /> {item.invoiceno || '—'}
|
||||
</Box>
|
||||
</MobileField>
|
||||
<MobileField label="Amount" align="right">
|
||||
<Box
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: 0.5,
|
||||
px: 1,
|
||||
py: 0.375,
|
||||
borderRadius: 999,
|
||||
bgcolor: tint(BRAND),
|
||||
border: `1px solid ${edge(BRAND)}`,
|
||||
color: BRAND,
|
||||
fontSize: 11,
|
||||
fontWeight: 800,
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
<MdCurrencyRupee size={11} />
|
||||
{formatNumberToRupees(item.totalamount).replace('₹', '').trim()}
|
||||
</Box>
|
||||
</MobileField>
|
||||
<MobileField label="Invoice Date">
|
||||
<Stack direction="row" alignItems="center" spacing={0.5}>
|
||||
<MdEventNote size={12} color={DT.textMuted} />
|
||||
<Typography variant="caption" sx={{ fontWeight: 700, color: DT.textPrimary }} noWrap>
|
||||
{item.transactiondate ? dayjs(item.transactiondate).format('DD/MM/YYYY') : '—'}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</MobileField>
|
||||
<MobileField label="Due Date">
|
||||
<Stack direction="row" alignItems="center" spacing={0.5}>
|
||||
<MdEventNote size={12} color={overdue && billStatus !== 3 ? '#ef4444' : DT.textMuted} />
|
||||
<Typography
|
||||
variant="caption"
|
||||
sx={{
|
||||
fontWeight: 700,
|
||||
color: overdue && billStatus !== 3 ? '#ef4444' : DT.textPrimary
|
||||
}}
|
||||
noWrap
|
||||
>
|
||||
{item.duedate ? dayjs(item.duedate).format('DD/MM/YYYY') : '—'}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</MobileField>
|
||||
<MobileField label="Items">
|
||||
<Box
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: 0.5,
|
||||
px: 0.875,
|
||||
py: 0.25,
|
||||
borderRadius: 999,
|
||||
bgcolor: tint('#14b8a6'),
|
||||
border: `1px solid ${edge('#14b8a6')}`,
|
||||
color: '#14b8a6',
|
||||
fontSize: 11,
|
||||
fontWeight: 800,
|
||||
minWidth: 44,
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
<MdInventory2 size={11} /> {item.itemcount ?? 0}
|
||||
</Box>
|
||||
</MobileField>
|
||||
</MobileFieldGrid>
|
||||
</MobileCard>
|
||||
);
|
||||
})}
|
||||
</MobileCardList>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<TableContainer
|
||||
sx={{
|
||||
maxHeight: { xs: 'calc(100vh - 220px)', md: 'calc(100vh - 190px)' },
|
||||
'&::-webkit-scrollbar': { width: 10, height: 10 },
|
||||
'&::-webkit-scrollbar-thumb': {
|
||||
backgroundColor: edge(BRAND),
|
||||
borderRadius: 8,
|
||||
'&:hover': { backgroundColor: BRAND }
|
||||
},
|
||||
'&::-webkit-scrollbar-track': { backgroundColor: DT.surfaceAlt }
|
||||
}}
|
||||
>
|
||||
<Table stickyHeader sx={{ minWidth: { xs: 880, md: 1060 } }}>
|
||||
<TableHead>
|
||||
<TableRow
|
||||
sx={{
|
||||
@@ -754,9 +923,10 @@ const Invoice = () => {
|
||||
);
|
||||
})
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
)}
|
||||
|
||||
<Divider />
|
||||
<Stack
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useRef, useState, useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
// import nearleLogo from '../../../assets/images/nearleLogo.png';
|
||||
import logo_nearle1 from '../../../assets/images/logo-nearle1.png';
|
||||
import axios from 'axios';
|
||||
@@ -56,6 +57,7 @@ const InvoicePreview = () => {
|
||||
const [refnumber, setRefnumber] = useState('');
|
||||
const [remarks, setRemarks] = useState('');
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
||||
useEffect(() => {
|
||||
setselected(location.state);
|
||||
}, []);
|
||||
@@ -96,7 +98,13 @@ const InvoicePreview = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack direction="row" justifyContent="Space-between" alignItems={'center'} spacing={2} sx={{ px: 2.5, py: 1, bgcolor: '#eeeeee' }}>
|
||||
<Stack
|
||||
direction={{ xs: 'column', md: 'row' }}
|
||||
justifyContent="Space-between"
|
||||
alignItems={{ xs: 'stretch', md: 'center' }}
|
||||
spacing={2}
|
||||
sx={{ px: { xs: 1.5, md: 2.5 }, py: 1, bgcolor: '#eeeeee' }}
|
||||
>
|
||||
<Stack direction={'row'} alignItems={'center'} spacing={2}>
|
||||
<Tooltip title="back">
|
||||
<IconButton
|
||||
@@ -123,10 +131,11 @@ const InvoicePreview = () => {
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<Stack direction={'row'} spacing={2}>
|
||||
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2} sx={{ width: { xs: '100%', md: 'auto' } }}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
fullWidth={isMobile}
|
||||
sx={{
|
||||
'&:hover': {
|
||||
backgroundColor: 'primary.main',
|
||||
@@ -148,6 +157,7 @@ const InvoicePreview = () => {
|
||||
startIcon={<PrinterFilled />}
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
fullWidth={isMobile}
|
||||
sx={{
|
||||
'&:hover': {
|
||||
backgroundColor: 'primary.main',
|
||||
@@ -162,8 +172,12 @@ const InvoicePreview = () => {
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Box sx={{ pb: 2.5, border: '1px solid #eee' }}>
|
||||
<div ref={componentRef} style={{ width: '100%' }}>
|
||||
<Box sx={{ pb: 2.5, border: '1px solid #eee', overflowX: { xs: 'auto', md: 'visible' } }}>
|
||||
{/* minWidth keeps the invoice at a legible fixed layout on phones —
|
||||
the parent's overflowX:auto then lets it scroll horizontally
|
||||
instead of squishing the header into vertical slivers. 720px sits
|
||||
within the print page width, so printing is unaffected. */}
|
||||
<div ref={componentRef} style={{ width: '100%', minWidth: 720 }}>
|
||||
<Box id="print" sx={{ p: 2.5 }}>
|
||||
<Box sx={{ pb: 2.5 }}>
|
||||
<Stack
|
||||
|
||||
Reference in New Issue
Block a user