نسخ من RaghadAlkhous/RestaurantDash
390 أسطر
17 KiB
JavaScript
390 أسطر
17 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
|
import {
|
|
useMediaQuery,
|
|
Box,
|
|
Typography,
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableContainer,
|
|
TableHead,
|
|
TableRow,
|
|
Paper,
|
|
IconButton,
|
|
Skeleton,
|
|
CircularProgress,
|
|
Button
|
|
} from '@mui/material';
|
|
import { useTheme } from '@mui/material/styles';
|
|
import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew';
|
|
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
|
|
import authService from '../../../../services/authService';
|
|
import { useRestaurant } from "../../../../contexts/RestaurantContext";
|
|
import CartDetails from './CartDetails';
|
|
import CartView from './CartView';
|
|
|
|
|
|
|
|
|
|
const SimplePagination = ({ currentPage, pageCount, onChange }) => {
|
|
const theme = useTheme();
|
|
const handlePrev = () => { if (currentPage > 1) onChange(currentPage - 1); };
|
|
const handleNext = () => { if (currentPage < pageCount) onChange(currentPage + 1); };
|
|
const { restaurantId } = useRestaurant();
|
|
return (
|
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
<IconButton
|
|
size="small"
|
|
onClick={handlePrev}
|
|
disabled={currentPage <= 1}
|
|
sx={{
|
|
borderRadius: '8px',
|
|
backgroundColor: '#FFECE0',
|
|
'&:hover': { backgroundColor: '#FFD6B5' },
|
|
color: theme.palette.primary.main,
|
|
'&.Mui-disabled': { color: '#ccc', backgroundColor: '#FFF5E6' },
|
|
}}
|
|
>
|
|
<ArrowBackIosNewIcon fontSize="small" />
|
|
</IconButton>
|
|
|
|
<Box
|
|
sx={{
|
|
width: 32,
|
|
height: 32,
|
|
borderRadius: '8px',
|
|
backgroundColor: theme.palette.primary.main,
|
|
color: '#fff',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
fontWeight: 600,
|
|
fontSize: 14,
|
|
userSelect: 'none',
|
|
boxShadow: `0 0 0 1px ${theme.palette.primary.main}`,
|
|
}}
|
|
>
|
|
{currentPage}
|
|
</Box>
|
|
|
|
<IconButton
|
|
size="small"
|
|
onClick={handleNext}
|
|
disabled={currentPage >= pageCount}
|
|
sx={{
|
|
borderRadius: '8px',
|
|
backgroundColor: '#FFECE0',
|
|
'&:hover': { backgroundColor: '#FFD6B5' },
|
|
color: theme.palette.primary.main,
|
|
'&.Mui-disabled': { color: '#ccc', backgroundColor: '#FFF5E6' },
|
|
}}
|
|
>
|
|
<ArrowForwardIosIcon fontSize="small" />
|
|
</IconButton>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
const Orders = ({ adminId }) => {
|
|
const theme = useTheme();
|
|
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
|
|
const { restaurantId } = useRestaurant();
|
|
const [ordersData, setOrdersData] = useState([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [selectedCart, setSelectedCart] = useState(null);
|
|
const [cartLoading, setCartLoading] = useState(false);
|
|
const [showCartView, setShowCartView] = useState(false);
|
|
|
|
const [currentPage, setCurrentPage] = useState(1);
|
|
const itemsPerPage = 6;
|
|
|
|
// ────────────── Fetch Orders ──────────────
|
|
useEffect(() => {
|
|
const fetchOrders = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const result = await authService.getCart();
|
|
if (result.success) {
|
|
console.log(Array.isArray(result.data) ? result.data : []);
|
|
setOrdersData(Array.isArray(result.data) ? result.data : []);
|
|
} else {
|
|
console.error(result.message);
|
|
setOrdersData([]);
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to fetch orders:', error);
|
|
setOrdersData([]);
|
|
}
|
|
setLoading(false);
|
|
};
|
|
fetchOrders();
|
|
}, []);
|
|
|
|
// ────────────── Row Click ──────────────
|
|
const handleRowClick = async (cartId) => {
|
|
setCartLoading(true);
|
|
try {
|
|
const result = await authService.getCartById(cartId);
|
|
if (result.success) setSelectedCart(result.data);
|
|
else console.error(result.message);
|
|
} catch (error) {
|
|
console.error('Failed to fetch cart details:', error);
|
|
}
|
|
setCartLoading(false);
|
|
};
|
|
|
|
const handleCartUpdated = (updatedCart) => {
|
|
setOrdersData(prev =>
|
|
prev.map(cart => cart.id === updatedCart.id ? updatedCart : cart)
|
|
);
|
|
setSelectedCart(null);
|
|
};
|
|
|
|
const handleCloseCartDetails = () => { setSelectedCart(null); };
|
|
|
|
const handleAddOrder = (newOrder) => {
|
|
setOrdersData(prev => [newOrder, ...prev]);
|
|
};
|
|
|
|
const pageCount = Math.ceil(ordersData.length / itemsPerPage);
|
|
const paginatedOrders = ordersData.slice(
|
|
(currentPage - 1) * itemsPerPage,
|
|
currentPage * itemsPerPage
|
|
);
|
|
|
|
if (cartLoading) {
|
|
return (
|
|
<Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
|
|
<CircularProgress />
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
if (selectedCart) {
|
|
return (
|
|
<CartDetails
|
|
cart={selectedCart}
|
|
onClose={handleCloseCartDetails}
|
|
onUpdated={handleCartUpdated}
|
|
/>
|
|
);
|
|
}
|
|
|
|
if (showCartView) {
|
|
return (
|
|
<CartView
|
|
onClose={() => setShowCartView(false)}
|
|
onSend={handleAddOrder}
|
|
onCartCreated={(newCart) => {
|
|
setOrdersData(prev => [newCart, ...prev]);
|
|
setShowCartView(false);
|
|
}}
|
|
adminId={adminId}
|
|
/>
|
|
);
|
|
}
|
|
|
|
// ────────────── Main Orders Table ──────────────
|
|
return (
|
|
<Box
|
|
sx={{
|
|
width: { xs: '100%', sm: '93.5%' },
|
|
p: { xs: 2, sm: 3 },
|
|
backgroundColor: 'white',
|
|
borderRadius: 2,
|
|
}}
|
|
>
|
|
<Box
|
|
sx={{
|
|
display: 'flex',
|
|
flexDirection: { xs: 'column', sm: 'row' },
|
|
justifyContent: 'space-between',
|
|
alignItems: { xs: 'stretch', sm: 'center' },
|
|
mb: 2,
|
|
gap: { xs: 1, sm: 0 },
|
|
}}
|
|
>
|
|
<Typography
|
|
variant="h5"
|
|
sx={{
|
|
fontWeight: 600,
|
|
fontSize: { xs: '18px', sm: '20px' },
|
|
mb: { xs: 1, sm: 0 },
|
|
textAlign: { xs: 'center', sm: 'left' },
|
|
}}
|
|
>
|
|
Cart
|
|
</Typography>
|
|
|
|
<Button
|
|
onClick={() => setShowCartView(true)}
|
|
sx={{
|
|
color: 'white',
|
|
borderRadius: '8px',
|
|
fontWeight: 600,
|
|
fontSize: '14px',
|
|
height: '40px',
|
|
width: { xs: '100%', sm: '135px' },
|
|
textTransform: 'none',
|
|
minWidth: { xs: 'unset', sm: '135px' },
|
|
}}
|
|
variant="contained"
|
|
size="small"
|
|
>
|
|
Current Cart
|
|
</Button>
|
|
</Box>
|
|
|
|
<TableContainer
|
|
component={Paper}
|
|
sx={{
|
|
boxShadow: 'none',
|
|
border: '1px solid #e0e0e0',
|
|
borderRadius: 2,
|
|
minWidth: 320,
|
|
height: '380px',
|
|
}}
|
|
>
|
|
<Table
|
|
sx={{ minWidth: { sm: 550, md: 650 }, tableLayout: 'auto' }}
|
|
aria-label="orders table"
|
|
size={isMobile ? 'small' : 'medium'}
|
|
>
|
|
<TableHead sx={{ backgroundColor: '#f5f5f5', color: '#61677F' }}>
|
|
<TableRow sx={{ '& th': { borderBottom: 'none' } }}>
|
|
<TableCell sx={{ fontWeight: 500, fontSize: '16px', color: '#61677F' }}>Cart ID</TableCell>
|
|
<TableCell sx={{ fontWeight: 500, fontSize: '16px', color: '#61677F' }}>Total Price</TableCell>
|
|
<TableCell sx={{ fontWeight: 500, fontSize: '16px', color: '#61677F' }}>Created At</TableCell>
|
|
<TableCell sx={{ fontWeight: 500, fontSize: '16px', color: '#61677F' }}>Items Count</TableCell>
|
|
<TableCell sx={{ fontWeight: 500, fontSize: '16px', color: '#61677F' }}>Action</TableCell>
|
|
</TableRow>
|
|
</TableHead>
|
|
<TableBody>
|
|
{loading
|
|
? Array.from({ length: itemsPerPage }).map((_, idx) => (
|
|
<TableRow key={`skeleton-${idx}`}>
|
|
<TableCell><Skeleton variant="text" /></TableCell>
|
|
<TableCell><Skeleton variant="text" /></TableCell>
|
|
<TableCell><Skeleton variant="text" /></TableCell>
|
|
<TableCell><Skeleton variant="text" /></TableCell>
|
|
<TableCell><Skeleton variant="text" /></TableCell>
|
|
</TableRow>
|
|
))
|
|
: (
|
|
<>
|
|
{paginatedOrders.map(order => {
|
|
const attributes = order?.attributes || {};
|
|
const relationships = order?.relationships || {};
|
|
const cartItems = relationships?.cartItems || relationships?.cart_items || [];
|
|
return (
|
|
<TableRow
|
|
key={order?.id}
|
|
hover
|
|
sx={{
|
|
cursor: 'pointer',
|
|
transition: 'background-color 0.3s',
|
|
'&:hover': {
|
|
backgroundColor: '#ffe4d0ff !important',
|
|
'& td': { color: '#000000ff' },
|
|
},
|
|
}}
|
|
onClick={() => handleRowClick(order.id)}
|
|
>
|
|
<TableCell>{order?.id || '--'}</TableCell>
|
|
<TableCell>{attributes?.totalPrice ?? attributes?.total_price ?? '0.00'}</TableCell>
|
|
<TableCell>{attributes?.createdAt ? new Date(attributes.createdAt).toLocaleString() : '--'}</TableCell>
|
|
<TableCell>{cartItems.length}</TableCell>
|
|
<TableCell>
|
|
<Button
|
|
variant="contained"
|
|
color="primary"
|
|
size="small"
|
|
onClick={ async (e) => {
|
|
e.stopPropagation();
|
|
const cart = order;
|
|
if (!cart) return;
|
|
|
|
const orderData = {
|
|
data: {
|
|
type: "order",
|
|
attributes: {
|
|
totalPrice: parseFloat(cart.attributes.total_price) || 0,
|
|
},
|
|
relationships: {
|
|
restaurant: {
|
|
data: {
|
|
id: restaurantId,
|
|
},
|
|
},
|
|
orderItems: (cart.relationships.cart_items || []).map((item) => ({
|
|
attributes: {
|
|
quantity: item.attributes.quantity,
|
|
},
|
|
relationships: {
|
|
product: {
|
|
data: {
|
|
id: item.relationships.supplier_product.id,
|
|
},
|
|
},
|
|
},
|
|
})),
|
|
},
|
|
},
|
|
};
|
|
authService.confirmOrder(orderData);
|
|
|
|
const result =await authService.confirmOrder(orderData);
|
|
if (result.success) {
|
|
setOrdersData(prev => prev.filter(c => c.id !== cart.id));
|
|
} else {
|
|
console.error(result.message);
|
|
}
|
|
}}>
|
|
Confirm
|
|
</Button>
|
|
</TableCell>
|
|
</TableRow>
|
|
);
|
|
})}
|
|
|
|
{paginatedOrders.length < itemsPerPage &&
|
|
Array.from({ length: itemsPerPage - paginatedOrders.length }).map((_, idx) => (
|
|
<TableRow key={`empty-${idx}`} sx={{ height: 53 }}>
|
|
<TableCell colSpan={4} sx={{ borderBottom: 'none' }} />
|
|
</TableRow>
|
|
))
|
|
}
|
|
</>
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
</TableContainer>
|
|
|
|
<Box
|
|
display="flex"
|
|
justifyContent="space-between"
|
|
alignItems="center"
|
|
pt={{ xs: 1, sm: 2 }}
|
|
sx={{
|
|
position: 'sticky',
|
|
bottom: 0,
|
|
backgroundColor: theme.palette.background.paper,
|
|
borderTop: '1px solid #f0f0f0',
|
|
}}
|
|
>
|
|
<Typography
|
|
variant="body2"
|
|
color="text.secondary"
|
|
sx={{ fontSize: { xs: '11px', sm: '14px' }, whiteSpace: 'nowrap' }}
|
|
>
|
|
Showing {(currentPage - 1) * itemsPerPage + 1} - {Math.min(currentPage * itemsPerPage, ordersData.length)} of {ordersData.length}
|
|
</Typography>
|
|
|
|
<SimplePagination currentPage={currentPage} pageCount={pageCount} onChange={setCurrentPage} />
|
|
</Box>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export default Orders;
|