1
0
الملفات
RestaurantDash/src/components/Home/Inventory/TableView.js

499 أسطر
16 KiB
JavaScript

import React, { useState } from 'react';
import { useTheme } from '@mui/material/styles';
import {
useMediaQuery,
Box,
Typography,
Paper,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Chip,
Pagination,
Button,
Avatar,
TableContainer,
TextField,
IconButton,
} from '@mui/material';
import AssignmentIcon from '@mui/icons-material/Assignment';
import DriveFileRenameOutlineSharpIcon from '@mui/icons-material/DriveFileRenameOutlineSharp';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import CloseIcon from '@mui/icons-material/Close';
import { green } from '@mui/material/colors';
import jsPDF from 'jspdf';
import autoTable from 'jspdf-autotable';
import * as XLSX from 'xlsx';
import { saveAs } from 'file-saver';
import TuneIcon from '@mui/icons-material/Tune';
const TableView = ({ data = [], onAddNewProduct }) => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
const itemsPerPage = 5;
const [currentPage, setCurrentPage] = useState(1);
const [showSearch, setShowSearch] = useState(false);
const [searchTerm, setSearchTerm] = useState('');
// فلترة البيانات بناءً على نص البحث (غير حساس لحالة الأحرف)
const filteredData = data.filter((item) =>
item.product.toLowerCase().includes(searchTerm.toLowerCase())
);
const pageCount = Math.ceil(filteredData.length / itemsPerPage);
const paginatedData = filteredData.slice(
(currentPage - 1) * itemsPerPage,
currentPage * itemsPerPage
);
const handleSearchChange = (e) => {
setSearchTerm(e.target.value);
setCurrentPage(1); // العودة للصفحة الأولى عند تغيير البحث
};
const handleExportPDF = () => {
const doc = new jsPDF();
doc.setFontSize(14);
doc.text('Top Selling Products', 14, 20);
const tableColumn = ['No.', 'Product', 'Sales', 'Amount', 'Price', 'Expiration', 'Status'];
const tableRows = data.map((row, i) => ([
i + 1,
row.product,
row.sales,
`$${row.amount}`,
`$${row.price}`,
row.expiration,
row.status
]));
autoTable(doc, {
startY: 30,
head: [tableColumn],
body: tableRows,
styles: { fontSize: 10 },
headStyles: { fillColor: [240, 240, 240] }
});
doc.save('top_selling_products.pdf');
};
const handleExportSpreadsheet = () => {
const wsData = [
['No.', 'Product', 'Sales', 'Amount', 'Price', 'Expiration', 'Status'],
...data.map((row, i) => [
i + 1,
row.product,
row.sales,
`$${row.amount}`,
`$${row.price}`,
row.expiration,
row.status
])
];
const worksheet = XLSX.utils.aoa_to_sheet(wsData);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Top Selling");
const wbout = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
const blob = new Blob([wbout], { type: 'application/octet-stream' });
saveAs(blob, 'top_selling_products.xlsx');
};
return (
<Paper
sx={{
backgroundColor: '#FFFFFF',
maxWidth: { xs: '90%', md: '100%' },
borderRadius: 2,
height: { xs: 'auto', md: '100vh' },
display: 'flex',
flexDirection: 'column',
overflow: 'hidden',
boxShadow: theme.shadows[1],
}}
>
{/* Header */}
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: { xs: 'flex-start', sm: 'center', md: 'center' },
mb: 3,
pl: { xs: 1, sm: 3 },
pt: 2,
pr: { xs: 1, sm: 3 },
flexDirection: { xs: 'column', sm: 'row', md: 'row' },
gap: { xs: 2, sm: 0 }
}}
>
<Typography variant="h6" sx={{ fontSize: { xs: '1rem', sm: '1.25rem' } }}>
Table View
</Typography>
<Box
display="flex"
gap={1}
flexWrap={{ xs: 'wrap', sm: 'nowrap' }}
width={{ xs: '100%', sm: 'auto' }}
justifyContent={{ xs: 'space-between', sm: 'flex-end' }}
>
{/* زر إضافة منتج جديد */}
<Button
variant="contained"
color="primary"
onClick={onAddNewProduct}
sx={{
textTransform: 'none',
borderRadius: '8px',
height: '40px',
width: { xs: '100%', sm: 'auto' },
fontSize: { xs: '12px', sm: '14px' },
fontWeight: 600,
}}
>
Add New Product
</Button>
{/* زر تصدير جدول (Spreadsheet) */}
<Button
variant="contained"
sx={{
textTransform: 'none',
color: 'white',
backgroundColor: '#5F6868',
boxShadow: 'none',
borderRadius: '8px',
height: '40px',
width: { xs: '48%', sm: '140px', md: '166px' },
fontSize: { xs: '12px', sm: '13px', md: '14px' },
fontWeight: 600,
p: 0,
m: 0,
whiteSpace: 'nowrap',
minWidth: 'unset',
}}
onClick={handleExportSpreadsheet}
>
{isMobile ? 'Export' : 'Export Spreadsheet'}
</Button>
{/* زر PDF */}
<Button
variant="contained"
sx={{
textTransform: 'none',
color: 'white',
backgroundColor: theme.palette.primary.main,
boxShadow: 'none',
borderRadius: '8px',
height: '40px',
width: { xs: '48%', sm: '90px', md: '105px' },
fontSize: { xs: '12px', sm: '13px', md: '14px' },
fontWeight: 600,
p: 0,
m: 0,
whiteSpace: 'nowrap',
minWidth: 'unset',
}}
onClick={handleExportPDF}
>
Export PDF
</Button>
{/* زر الفلاتر */}
<Button
variant="outlined"
sx={{
textTransform: 'none',
color: '#667085',
borderColor: '#e0e0e0',
backgroundColor: '#fff',
borderRadius: '8px',
height: '40px',
width: { xs: '100%', sm: '120px', md: '99px' },
fontSize: { xs: '12px', sm: '13px', md: '14px' },
fontWeight: 600,
p: 0,
m: 0,
whiteSpace: 'nowrap',
minWidth: 'unset',
}}
startIcon={<TuneIcon fontSize={isMobile ? 'small' : 'medium'} />}
>
{isMobile ? '' : 'Filters'}
</Button>
</Box>
</Box>
{/* جدول البيانات */}
<TableContainer
sx={{
flexGrow: 1,
overflowX: 'auto',
maxHeight: { xs: '60vh', md: 'calc(100vh - 160px)' },
'&::-webkit-scrollbar': { height: 4 },
}}
>
<Table
size={isMobile ? 'small' : 'medium'}
sx={{
minWidth: 750,
'& .MuiTableCell-root': {
borderBottom: '1px solid #e0e0e0',
py: { xs: 0.5, sm: 1.5 },
},
'& thead .MuiTableCell-root': {
borderBottom: 'none',
fontWeight: 600,
fontSize: { xs: '11px', sm: '14px' },
px: { xs: 0.5, sm: 2 },
},
}}
>
<TableHead sx={{ backgroundColor: '#F0F1F3' }}>
<TableRow sx={{ height: { xs: '6vh', sm: '10vh' } }}>
<TableCell width="6%">No.</TableCell>
<TableCell width="20%" sx={{ position: 'relative' }}>
<Box
sx={{ display: 'flex', alignItems: 'center', gap: 1, cursor: 'pointer', userSelect: 'none' }}
onClick={() => setShowSearch(prev => !prev)}
>
<Typography>Product Name</Typography>
<ArrowDropDownIcon
fontSize="small"
sx={{
transform: showSearch ? 'rotate(180deg)' : 'rotate(0deg)',
transition: 'transform 0.3s ease',
}}
/>
</Box>
{showSearch && (
<TextField
size="small"
variant="outlined"
placeholder="Search product..."
value={searchTerm}
onChange={handleSearchChange}
sx={{ mt: 1, width: '100%' }}
InputProps={{
endAdornment: searchTerm && (
<IconButton
size="small"
onClick={() => setSearchTerm('')}
aria-label="Clear search"
>
<CloseIcon fontSize="small" />
</IconButton>
),
}}
/>
)}
</TableCell>
{!isMobile && <TableCell width="10%">Barcode</TableCell>}
<TableCell width="10%">Branch</TableCell>
{!isMobile && <TableCell width="10%">Expiration</TableCell>}
{!isMobile && <TableCell width="15%">Stock Quantity </TableCell>}
<TableCell width="15%">Consumption Status</TableCell>
<TableCell width="10%" align="center">
Action
</TableCell>
</TableRow>
</TableHead>
<TableBody
sx={{
'& .MuiTableCell-root': {
px: { xs: 2, sm: 3 },
},
}}
>
{paginatedData.length > 0 ? (
paginatedData.map((row, i) => (
<TableRow key={i} sx={{ height: { xs: '8vh', sm: '13vh' } }}>
<TableCell sx={{ fontSize: { xs: '11px', sm: '14px' } }}>
{(currentPage - 1) * itemsPerPage + i + 1}
</TableCell>
<TableCell>
<Box
sx={{
display: 'flex',
alignItems: 'center',
gap: { xs: 1, sm: 2 },
}}
>
<Avatar
sx={{
bgcolor: green[50],
width: { xs: 28, sm: 40 },
height: { xs: 28, sm: 40 },
}}
variant="rounded"
>
<AssignmentIcon
fontSize={isMobile ? 'small' : 'medium'}
/>
</Avatar>
<Box
sx={{
fontSize: { xs: '11px', sm: '14px' },
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
maxWidth: { xs: '80px', sm: '200px' },
}}
>
{row.product}
</Box>
</Box>
</TableCell>
{!isMobile && (
<TableCell sx={{ fontSize: { xs: '11px', sm: '14px' } }}>
{row.sales}
</TableCell>
)}
<TableCell sx={{ fontSize: { xs: '11px', sm: '14px' } }}>
${row.amount.toLocaleString()}
</TableCell>
{!isMobile && (
<TableCell sx={{ fontSize: { xs: '11px', sm: '14px' } }}>
${row.price}
</TableCell>
)}
{!isMobile && (
<TableCell sx={{ fontSize: { xs: '11px', sm: '14px' } }}>
{row.expiration}
</TableCell>
)}
<TableCell>
<Chip
label={isMobile ? row.status.substring(0, 3) : row.status}
size={isMobile ? 'small' : 'medium'}
sx={{
fontSize: { xs: '10px', sm: '14px' },
fontWeight: 600,
backgroundColor:
row.status === 'Published'
? '#E7F4EE'
: row.status === 'Low Stock'
? '#FDF1E8'
: row.status === 'Out of Stock'
? '#FFCDD2'
: '#E0E0E0',
color:
row.status === 'Published'
? '#0D894F'
: row.status === 'Low Stock'
? '#E46A11'
: row.status === 'Out of Stock'
? '#C62828'
: '#424242',
minWidth: { xs: '50px', sm: '100px' },
}}
/>
</TableCell>
<TableCell align="center">
<Button
variant="text"
size="small"
onClick={() => console.log('Edit', row)}
sx={{ minWidth: 0, padding: 0 }}
>
<DriveFileRenameOutlineSharpIcon
fontSize={isMobile ? 'small' : 'medium'}
sx={{ color: '#5F6868' }}
/>
</Button>
</TableCell>
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={8} align="center" sx={{ py: 5 }}>
No products found.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
{/* Pagination Footer */}
<Box
display="flex"
justifyContent="space-between"
alignItems="center"
p={{ xs: 1, sm: 2 }}
sx={{
position: 'sticky',
bottom: 0,
backgroundColor: theme.palette.background.paper,
borderTop: '1px solid #f0f0f0',
zIndex: 1,
}}
>
<Typography
variant="body2"
color="text.secondary"
sx={{
fontSize: { xs: '11px', sm: '14px' },
whiteSpace: 'nowrap',
}}
>
Showing{' '}
{(currentPage - 1) * itemsPerPage + 1} to{' '}
{Math.min(currentPage * itemsPerPage, filteredData.length)} of{' '}
{filteredData.length}
</Typography>
<Pagination
count={pageCount}
page={currentPage}
onChange={(event, value) => setCurrentPage(value)}
size={isMobile ? 'small' : 'medium'}
sx={{
'& .MuiPaginationItem-root': {
fontSize: { xs: '12px', sm: '14px' },
minWidth: { xs: 24, sm: 32 },
height: { xs: 24, sm: 32 },
backgroundColor: '#FFECE0',
color: theme.palette.primary.main,
borderRadius: '8px',
'&.Mui-selected': {
backgroundColor: theme.palette.primary.main,
color: '#fff',
},
'&:hover': {
backgroundColor: '#FFD6B5',
},
},
}}
/>
</Box>
</Paper>
);
};
export default TableView;