نسخ من RaghadAlkhous/RestaurantDash
Initial commit - restaurant dashboard
هذا الالتزام موجود في:
389
src/components/Home/Cart/contect/Orders.js
Normal file
389
src/components/Home/Cart/contect/Orders.js
Normal file
@@ -0,0 +1,389 @@
|
||||
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;
|
||||
المرجع في مشكلة جديدة
حظر مستخدم