1
0

Initial commit - restaurant dashboard

هذا الالتزام موجود في:
RaghadMAlkous
2025-09-04 01:17:15 +03:00
الأصل 13891b47fd
التزام 7b2f8840cb
136 ملفات معدلة مع 16638 إضافات و6033 حذوفات

عرض الملف

@@ -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;