initial commit

This commit is contained in:
RaghadAlkhous
2026-05-06 15:08:57 +03:00
commit 1ccca905f3
215 changed files with 61375 additions and 0 deletions
+230
View File
@@ -0,0 +1,230 @@
import React, { useState, useEffect } from "react";
import { Box, useTheme, useMediaQuery } from "@mui/material";
import KitchPlusAppBar from "../AppBar";
import Sidebar from "../SideHome";
import AccountSettings from "./contect/AccountSettings";
import AllProducts from "./contect/AllProducts";
import PopularProductSection from "./contect/PopularProductSection";
import ProductDetail from "./contect/ProductDetail";
import api from "../../../services/authService";
const IMAGE_URL = "http://127.0.0.1:8000/storage/";
const drawerWidth = 230;
const Supplier = () => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
const [sidebarOpen, setSidebarOpen] = useState(!isMobile);
const [categories, setCategories] = useState([]);
const [products, setProducts] = useState([]);
const [selectedCategory, setSelectedCategory] = useState(null);
const [selectedCategoryName, setSelectedCategoryName] = useState(null);
const [selectedProduct, setSelectedProduct] = useState(null);
const [showAllPopular, setShowAllPopular] = useState(false);
const [showAllTopItems, setShowAllTopItems] = useState(false);
useEffect(() => {
setSidebarOpen(window.innerWidth >= theme.breakpoints.values.md);
}, [theme.breakpoints.values.md]);
useEffect(() => {
const handleResize = () => {
setSidebarOpen(window.innerWidth >= theme.breakpoints.values.md);
};
handleResize();
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, [theme.breakpoints.values.md]);
useEffect(() => {
const fetchCategories = async () => {
try {
const res = await api.getAllCategory();
if (res.success) {
const formatted = res.data.map((category) => ({
id: category.id,
name: category.name,
icon: category.image
? IMAGE_URL + category.image
: "/images/default-product.png",
}));
setCategories(formatted);
}
} catch (err) {
console.error("❌ Error fetching categories:", err);
}
};
fetchCategories();
}, []);
useEffect(() => {
const fetchProducts = async () => {
if (!selectedCategory) return;
try {
const res = await api.getProductsByCategory(selectedCategory, 1);
if (res.success) {
const formatted = res.data.map((product) => ({
id: product.id,
name: product.name,
category: product.supplier_category_id,
price: product.price,
unit: "Kg",
image: product.image
? IMAGE_URL + product.image
: "/images/default-product.png",
}));
setProducts(formatted);
const categoryName = categories.find(
(cat) => cat.id === selectedCategory
)?.name;
setSelectedCategoryName(categoryName || "");
}
} catch (err) {
console.error("Error fetching products:", err);
}
};
fetchProducts();
}, [selectedCategory, categories]);
const handleDrawerToggle = () => {
setSidebarOpen(!sidebarOpen);
};
const toggleShowAllPopular = () => {
setShowAllPopular((prev) => !prev);
};
const toggleShowAllTopItems = () => {
setShowAllTopItems((prev) => !prev);
};
return (
<Box
sx={{
display: "flex",
height: "100vh",
backgroundColor: "#F6F6F6",
overflow: "hidden",
}}
>
<Sidebar
open={sidebarOpen}
onClose={handleDrawerToggle}
isMobile={isMobile}
drawerWidth={drawerWidth}
/>
<Box
sx={{
flexGrow: 1,
display: "flex",
flexDirection: "column",
width: "10%",
marginLeft: { xs: 0, sm: sidebarOpen ? `${drawerWidth}px` : 0, md: 0 },
transition: theme.transitions.create(["width"], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
}}
>
<KitchPlusAppBar
onDrawerToggle={handleDrawerToggle}
sidebarOpen={sidebarOpen}
isMobile={isMobile}
/>
<Box
sx={{
flexGrow: 1,
width: sidebarOpen ? "calc(100% - 40px)" : "100%",
pt: { xs: 2, sm: 3 },
overflowY: "auto",
pl: { xs: 1, sm: 2 },
pr: { xs: 1, sm: 2 },
scrollbarWidth: "none",
"&::-webkit-scrollbar": { display: "none" },
}}
>
{selectedProduct ? (
<ProductDetail
product={selectedProduct}
onBack={() => setSelectedProduct(null)}
/>
) : (
<>
<AccountSettings
selectedCategory={selectedCategory}
setSelectedCategory={setSelectedCategory}
categories={categories}
/>
{selectedCategory ? (
<Box sx={{ mt: 3 }}>
<AllProducts
products={products}
showAll={true}
onToggleShowAll={() => setSelectedCategory(null)}
onProductClick={setSelectedProduct}
categoryName={selectedCategoryName}
/>
</Box>
) : !showAllPopular && !showAllTopItems ? (
<Box
sx={{
mt: 3,
display: "flex",
flexDirection: { xs: "column", md: "row" },
alignItems: "stretch",
gap: 2,
}}
>
<Box
sx={{
flexBasis: { xs: "100%", md: "100%" },
display: "flex",
flexDirection: "column",
pb: { xs: 4, md: 4 },
pr: { xs: 1, md: 0 },
mb: 10,
}}
>
<PopularProductSection
products={products}
showAll={showAllPopular}
onToggleShowAll={toggleShowAllPopular}
onProductClick={setSelectedProduct}
/>
</Box>
</Box>
) : showAllPopular ? (
<Box sx={{ mt: 3, width: "100%" }}>
<PopularProductSection
products={products}
showAll={showAllPopular}
onToggleShowAll={toggleShowAllPopular}
onProductClick={setSelectedProduct}
/>
</Box>
) : showAllTopItems ? (
<Box sx={{ mt: 3, width: "100%" }}>
</Box>
) : null}
</>
)}
</Box>
</Box>
</Box>
);
};
export default Supplier;
+247
View File
@@ -0,0 +1,247 @@
import React, { useState, useEffect } from 'react';
import { Box, useTheme, useMediaQuery } from '@mui/material';
import KitchPlusAppBar from '../AppBar';
import Sidebar from '../SideHome';
// بدل هذه المكونات عند عرض التفاصيل
import AccountSettings from './contect/AccountSettings';
import AllProducts from './contect/AllProducts';
import PopularProductSection from './contect/PopularProductSection';
import TopItems from './contect/TopItems';
import OrderList from './contect/OrderList';
import ProductDetail from './contect/ProductDetail'; // 👈 مكون التفاصيل
//import auth servise
import api from '../../../services/authService';
const IMAGE_URL = 'http://127.0.0.1:8000/storage/' ;
const categoriesInit0 = await api.getAllCategory();
const categoriesInit = categoriesInit0.data.map(category => ({
id: category.id,
name: category.name,
icon: category.image || "/images/default-product.png",
}));
const productList0 = await api.getAllProduct();
const productList = productList0.data.map(product => ({
id: product.id,
name: product.name,
category: product.supplier_category_id,
price: product.price,
unit: "Kg",
image: IMAGE_URL + product.image || "/images/default-product.png",
}));
// const productList = [
// { id: 1, name: 'Cabbage 1', category: 'Vegetable', price: '15.10', unit: '/kg', image: '/images/waitress2.png' },
// { id: 2, name: 'vegetables 2', category: 'Vegetable', price: '8.34', unit: '/kg', image: '/images/waitress2.png' },
// { id: 3, name: 'Brocoly 3', category: 'Vegetable', price: '5.60', unit: '/kg', image: '/images/waitress2.png' },
// { id: 4, name: 'Onion 4', category: 'Vegetable', price: '6.45', unit: '/kg', image: '/images/waitress2.png' },
// { id: 5, name: 'Bread 5', category: 'Bread', price: '3.50', unit: '/pcs', image: '/images/waitress2.png' },
// { id: 6, name: 'Meat 6', category: 'Meat', price: '20.00', unit: '/kg', image: '/images/waitress2.png' },
// { id: 7, name: 'Cabbage', category: 'Vegetable', price: '15.10', unit: '/kg', image: '/images/waitress2.png' },
// { id: 8, name: 'vegetables', category: 'Vegetable', price: '8.34', unit: '/kg', image: '/images/waitress2.png' },
// { id: 9, name: 'Brocoly', category: 'Vegetable', price: '5.60', unit: '/kg', image: '/images/waitress2.png' },
// { id: 10, name: 'Onion', category: 'Vegetable', price: '6.45', unit: '/kg', image: '/images/waitress2.png' },
// { id: 11, name: 'Bread', category: 'Bread', price: '3.50', unit: '/pcs', image: '/images/waitress2.png' },
// { id: 12, name: 'Meat', category: 'Meat', price: '20.00', unit: '/kg', image: '/images/waitress2.png' },
// { id: 13, name: 'Cabbage', category: 'Vegetable', price: '15.10', unit: '/kg', image: '/images/waitress2.png' },
// { id: 14, name: 'vegetables', category: 'Vegetable', price: '8.34', unit: '/kg', image: '/images/waitress2.png' },
// { id: 15, name: 'Brocoly', category: 'Vegetable', price: '5.60', unit: '/kg', image: '/images/waitress2.png' },
// { id: 16, name: 'Onion', category: 'Vegetable', price: '6.45', unit: '/kg', image: '/images/waitress2.png' },
// { id: 17, name: 'Bread', category: 'Bread', price: '3.50', unit: '/pcs', image: '/images/waitress2.png' },
// { id: 18, name: 'Meat', category: 'Meat', price: '20.00', unit: '/kg', image: '/images/waitress2.png' },
// ];
const item = [
{ name: 'Cabbage', price: '15.10', unit: '/kg', image: '/images/cabbage.png' },
{ name: 'Kale vegetables', price: '8.34', unit: '/kg', image: '/images/kale.png' },
{ name: 'Brocoly', price: '5.60', unit: '/kg', image: '/images/broccoli.png' },
{ name: 'Celery', price: '4.80', unit: '/kg', image: '/images/celery.png' },
{ name: 'Onion', price: '6.45', unit: '/kg', image: '/images/onion.png' },
{ name: 'Garlic', price: '9.90', unit: '/kg', image: '/images/garlic.png' },
];
const drawerWidth = 230;
const Supplier = () => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
const [sidebarOpen, setSidebarOpen] = useState(!isMobile);
// حالات العرض
const [showAllPopular, setShowAllPopular] = useState(false);
const [showAllTopItems, setShowAllTopItems] = useState(false);
const [selectedCategory, setSelectedCategory] = useState(null);
// ✅ المنتج المختار
const [selectedProduct, setSelectedProduct] = useState(null);
useEffect(() => {
setSidebarOpen(window.innerWidth >= theme.breakpoints.values.md);
}, [theme.breakpoints.values.md]);
useEffect(() => {
const handleResize = () => {
setSidebarOpen(window.innerWidth >= theme.breakpoints.values.md);
};
handleResize();
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, [theme.breakpoints.values.md]);
const handleDrawerToggle = () => {
setSidebarOpen(!sidebarOpen);
};
const toggleShowAllPopular = () => {
setShowAllPopular(prev => !prev);
};
const toggleShowAllTopItems = () => {
setShowAllTopItems(prev => !prev);
};
// اسم الفئة
const selectedCategoryName = selectedCategory
? categoriesInit.find(cat => cat.id === selectedCategory)?.name
: null;
return (
<Box sx={{ display: 'flex', height: '100vh', backgroundColor: '#F6F6F6', overflow: 'hidden' }}>
<Sidebar
open={sidebarOpen}
onClose={handleDrawerToggle}
isMobile={isMobile}
drawerWidth={drawerWidth}
/>
<Box
sx={{
flexGrow: 1,
display: 'flex',
flexDirection: 'column',
width: '10%',
marginLeft: { xs: 0, sm: sidebarOpen ? `${drawerWidth}px` : 0, md: 0 },
transition: theme.transitions.create(['width'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
}}
>
<KitchPlusAppBar onDrawerToggle={handleDrawerToggle} sidebarOpen={sidebarOpen} isMobile={isMobile} />
<Box
sx={{
flexGrow: 1,
width: sidebarOpen ? 'calc(100% - 40px)' : '100%',
pt: { xs: 2, sm: 3 },
overflowY: 'auto',
pl: { xs: 1, sm: 2 },
pr: { xs: 1, sm: 2 },
scrollbarWidth: 'none',
'&::-webkit-scrollbar': { display: 'none' },
}}
>
{/* ✅ إذا تم اختيار منتج نظهر تفاصيله فقط */}
{selectedProduct ? (
<ProductDetail product={selectedProduct} onBack={() => setSelectedProduct(null)} />
) : (
<>
<AccountSettings
selectedCategory={selectedCategory}
setSelectedCategory={setSelectedCategory}
/>
{selectedCategory ? (
<Box sx={{ mt: 3 }}>
<AllProducts
products={productList.filter(
(p) => p.category.toLowerCase() === selectedCategoryName?.toLowerCase()
)}
showAll={true}
onToggleShowAll={() => setSelectedCategory(null)}
onProductClick={setSelectedProduct}
categoryName={selectedCategoryName} // ✅ بدل القيمة الثابتة
/>
</Box>
) : !showAllPopular && !showAllTopItems ? (
<Box
sx={{
mt: 3,
display: 'flex',
flexDirection: { xs: 'column', md: 'row' },
alignItems: 'stretch',
gap: 2,
}}
>
<Box
sx={{
flexBasis: { xs: '100%', md: '100%' },
display: 'flex',
flexDirection: 'column',
pb: { xs: 4, md: 4 },
pr: { xs: 1, md: 0 },
mb: 10,
}}
>
<PopularProductSection
products={productList}
showAll={showAllPopular}
onToggleShowAll={toggleShowAllPopular}
onProductClick={setSelectedProduct}
/>
{/* <Box sx={{ mt: 3 }}>
<TopItems
item={item}
showAll={showAllTopItems}
onToggleShowAll={toggleShowAllTopItems}
onProductClick={setSelectedProduct}
/>
</Box> */}
</Box>
{/* <Box
sx={{
flexBasis: { xs: '100%', md: '40%' },
display: 'flex',
flexDirection: 'column',
pb: 10,
pr: { xs: 3, md: 0 },
}}
>
<Box sx={{ height: '100%' }}>
<OrderList />
</Box>
</Box> */}
</Box>
) : showAllPopular ? (
<Box sx={{ mt: 3, width: '100%' }}>
<PopularProductSection
products={productList}
showAll={showAllPopular}
onToggleShowAll={toggleShowAllPopular}
onProductClick={setSelectedProduct}
/>
</Box>
) : showAllTopItems ? (
<Box sx={{ mt: 3, width: '100%' }}>
{/* <TopItems
item={item}
showAll={showAllTopItems}
onToggleShowAll={toggleShowAllTopItems}
onProductClick={setSelectedProduct}
/> */}
</Box>
) : null}
</>
)}
</Box>
</Box>
</Box>
);
};
export default Supplier;
@@ -0,0 +1,307 @@
import React, { useState, useRef, useEffect } from 'react';
import {
Menu,
MenuItem,
Modal,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
useMediaQuery,
Box,
Typography,
Button,
useTheme,
IconButton,
} from '@mui/material';
import AddCategory from './AddCategory'; // مكون لإضافة/تعديل الفئة (يفترض موجود)
import AddIcon from '@mui/icons-material/Add';
import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew';
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
import TuneIcon from '@mui/icons-material/Tune';
import CategoryScrollList from './CategoryScrollList'; // مكون لعرض الفئات بشكل أفقي مع تمرير
//import auth servise
import api from '../../../../services/authService';
const IMAGE_URL = 'http://127.0.0.1:8000/storage/' ;
const categoriesInit0 = await api.getAllCategory();
const categoriesInit = categoriesInit0.data.map(category => (
{
id: category.id,
name: category.name,
icon: IMAGE_URL + category.icon || "/images/default-product.png",
}));
const AccountSettings = ({ selectedCategory, setSelectedCategory }) => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
const [categories, setCategories] = useState(categoriesInit);
const [selectedCategoryForEdit, setSelectedCategoryForEdit] = useState(null);
const [contextMenu, setContextMenu] = useState(null);
const [openAddModal, setOpenAddModal] = useState(false);
const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false);
const scrollRef = useRef();
const scroll = (offset) => {
if (scrollRef.current) {
scrollRef.current.scrollLeft += offset;
}
};
const handleAddCategory = (newCategory) => {
setCategories((prev) => [...prev, { ...newCategory, id: Date.now() }]);
setOpenAddModal(false);
};
const handleUpdateCategory = (updatedCategory) => {
setCategories((prev) =>
prev.map((cat) =>
cat.id === selectedCategoryForEdit.id ? { ...cat, ...updatedCategory } : cat
)
);
setOpenAddModal(false);
};
const handleDeleteCategory = () => {
setCategories((prev) => prev.filter((cat) => cat.id !== selectedCategoryForEdit.id));
setConfirmDeleteOpen(false);
setSelectedCategoryForEdit(null);
if (selectedCategory === selectedCategoryForEdit?.id) {
setSelectedCategory(null); // أبطل اختيار الفئة
}
};
useEffect(() => {
if (!openAddModal) {
setSelectedCategoryForEdit(null);
}
}, [openAddModal]);
return (
<Box
sx={{
pl: { xs: 2, sm: 3 },
pr: { xs: 2, sm: 3 },
pb: { xs: 2, sm: 3 },
pt: { xs: 2, sm: 1.5 },
backgroundColor: '#FFFFFF',
maxWidth: { xs: '90%', md: '100%' },
borderRadius: 2,
maxHeight: { xs: '293px', sm: '30%', md: 160 },
}}
>
{/* رأس القسم */}
<Box
sx={{
width: '100%',
display: 'flex',
justifyContent: 'space-between',
alignItems: { xs: 'flex-start', sm: 'center' },
mb: 3,
pl: 1,
pr: { xs: 1, sm: 0 },
flexDirection: { xs: 'column', sm: 'row' },
}}
>
<Typography
variant="h6"
sx={{
fontWeight: '600',
fontSize: { xs: '20px', sm: '22px', md: '24px' },
color: '#121212',
}}
>
Categories
</Typography>
<Box
sx={{
display: 'flex',
gap: { xs: 1, sm: 2 },
flexWrap: { xs: 'wrap', sm: 'nowrap' },
width: { xs: '100%', sm: 'auto' },
justifyContent: { xs: 'space-between', sm: 'flex-end' },
}}
>
{/* <Button
variant="contained"
sx={{
textTransform: 'none',
color: '#fff',
backgroundColor: theme.palette.primary.main,
boxShadow: 'none',
borderRadius: '8px',
height: '40px',
width: { xs: '100%', sm: '130px', md: '150px' },
fontSize: { xs: '12px', sm: '14px', md: '16px' },
fontWeight: 700,
minWidth: 'unset',
}}
onClick={() => {
setSelectedCategoryForEdit(null);
setOpenAddModal(true);
}}
>
Add Category
</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>
<IconButton
onClick={() => scroll(-200)}
sx={{
backgroundColor: theme.palette.primary.main,
color: '#fff',
width: 40,
height: 40,
'&:hover': { backgroundColor: '#e96b00' },
}}
>
<ArrowBackIosNewIcon fontSize="small" />
</IconButton>
<IconButton
onClick={() => scroll(200)}
sx={{
backgroundColor: theme.palette.primary.main,
color: '#fff',
width: 40,
height: 40,
'&:hover': { backgroundColor: '#e96b00' },
}}
>
<ArrowForwardIosIcon fontSize="small" />
</IconButton>
</Box>
</Box>
{/* قائمة التمرير للفئات */}
<Box
sx={{
width: { xs: '90%', sm: '95%', md: '100%' },
display: 'flex',
justifyContent: 'space-between',
alignItems: { xs: 'flex-start', sm: 'center' },
mb: 3,
pl: 1,
pr: { xs: 1, sm: 4 },
flexDirection: { xs: 'column', sm: 'row' },
overflowX: 'hidden',
}}
>
<CategoryScrollList
categories={categories}
selectedCategory={selectedCategory}
setSelectedCategory={setSelectedCategory} // مرر الدالة مباشرة
setSelectedCategoryForEdit={setSelectedCategoryForEdit}
setContextMenu={setContextMenu}
scrollRef={scrollRef}
/>
</Box>
{/* مودال الإضافة / التعديل */}
<Modal
open={openAddModal}
onClose={() => setOpenAddModal(false)}
aria-labelledby="add-category-modal"
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
p: 2,
}}
>
<Box
sx={{
backgroundColor: '#fff',
borderRadius: 2,
boxShadow: 24,
p: 2,
maxWidth: '90vw',
width: 400,
}}
>
<AddCategory
onAdd={selectedCategoryForEdit ? handleUpdateCategory : handleAddCategory}
editingCategory={selectedCategoryForEdit}
/>
</Box>
</Modal>
{/* قائمة السياق (الزر الأيمن) */}
<Menu
open={contextMenu !== null}
onClose={() => setContextMenu(null)}
anchorReference="anchorPosition"
anchorPosition={
contextMenu !== null
? { top: contextMenu.mouseY, left: contextMenu.mouseX }
: undefined
}
sx={{ zIndex: 2000 }}
>
<MenuItem
onClick={() => {
setOpenAddModal(true);
setContextMenu(null);
}}
>
Edit Category
</MenuItem>
<MenuItem
onClick={() => {
setConfirmDeleteOpen(true);
setContextMenu(null);
}}
sx={{ color: 'red' }}
>
Delete Category
</MenuItem>
</Menu>
{/* تأكيد الحذف */}
<Dialog open={confirmDeleteOpen} onClose={() => setConfirmDeleteOpen(false)}>
<DialogTitle>Confirm Delete</DialogTitle>
<DialogContent>
<Typography>Are you sure you want to delete this category?</Typography>
</DialogContent>
<DialogActions>
<Button onClick={() => setConfirmDeleteOpen(false)}>Cancel</Button>
<Button onClick={handleDeleteCategory} color="error" variant="contained">
Delete
</Button>
</DialogActions>
</Dialog>
</Box>
);
};
export default AccountSettings;
@@ -0,0 +1,186 @@
import React, { useState, useEffect } from 'react';
import { Box, Button, TextField, Typography, IconButton } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import AddAPhotoOutlinedIcon from '@mui/icons-material/AddAPhotoOutlined';
const AddCategory = ({ onAdd, editingCategory }) => {
const [productName, setProductName] = useState('');
const [description, setDescription] = useState('');
const [price, setPrice] = useState('');
const [discount, setDiscount] = useState('');
const [productImage, setProductImage] = useState(null);
useEffect(() => {
if (editingCategory) {
setProductName(editingCategory.name || '');
setDescription(editingCategory.description || '');
setPrice(editingCategory.price || '');
setDiscount(editingCategory.discount || '');
setProductImage(editingCategory.icon || null);
}
}, [editingCategory]);
const handleImageUpload = (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onloadend = () => {
setProductImage(reader.result);
};
reader.readAsDataURL(file);
}
};
const handleRemoveImage = () => {
setProductImage(null);
};
const handleSubmit = () => {
const newCategory = {
name: productName,
description,
price,
discount,
icon: productImage,
};
if (onAdd) {
onAdd(newCategory);
}
// إعادة تعيين الحقول فقط عند الإضافة الجديدة
if (!editingCategory) {
setProductName('');
setDescription('');
setPrice('');
setDiscount('');
setProductImage(null);
}
};
return (
<Box p={2} maxWidth={400} mx="auto">
{/* ✅ صورة التصنيف */}
{productImage ? (
<Box
sx={{
position: 'relative',
width: '100%',
height: 150,
borderRadius: 2,
overflow: 'hidden',
mb: 2,
}}
>
<img
src={productImage}
alt="Preview"
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
/>
<IconButton
onClick={handleRemoveImage}
sx={{
position: 'absolute',
top: 4,
right: 4,
backgroundColor: 'rgba(0,0,0,0.5)',
color: 'white',
'&:hover': {
backgroundColor: 'rgba(0,0,0,0.7)',
},
}}
size="small"
>
<CloseIcon fontSize="small" />
</IconButton>
</Box>
) : (
<Box
component="label"
htmlFor="upload-image"
sx={{
border: '2px dashed #ccc',
borderRadius: '10px',
height: '150px',
width: '100%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
cursor: 'pointer',
color: '#969BA7',
backgroundColor: '#FAFAFA',
fontWeight: 500,
fontSize: '18px',
textAlign: 'center',
mb: 2,
transition: 'border-color 0.3s',
'&:hover': {
borderColor: '#FF914D',
backgroundColor: '#fffaf5',
}
}}
>
Upload Menu Picture
<AddAPhotoOutlinedIcon sx={{ fontSize: 32, color: '#9e9e9e', mt: 1 }} />
<input
id="upload-image"
type="file"
accept="image/*"
hidden
onChange={handleImageUpload}
/>
</Box>
)}
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.1, mt: 4 }}>
<Typography variant="body2" color="black" sx={{ fontWeight: '500', fontSize: '16px' }}>
Categories name
</Typography>
<TextField
fullWidth
placeholder="Enter category name"
value={productName}
onChange={(e) => setProductName(e.target.value)}
margin="normal"
sx={{
'& input': { fontWeight: 500, fontSize: '15px' },
'& input::placeholder': { color: '#969BA7' },
'& .MuiOutlinedInput-root': {
borderRadius: '10px',
transition: '0.3s',
'&.Mui-focused fieldset': {
borderColor: '#FF914D',
boxShadow: '0 0 0 2px rgba(255, 145, 77, 0.2)'
}
},
'& .MuiOutlinedInput-root.Mui-focused': {
borderColor: '#FF914D',
boxShadow: '0 0 0 2px rgba(255,145,77,0.15)'
}
}}
/>
</Box>
<Button
variant="contained"
fullWidth
sx={{
mt: 2,
backgroundColor: '#FF914D',
'&:hover': { backgroundColor: '#e57f3c' },
borderRadius: 2,
color: '#fff',
textTransform: 'none',
fontWeight: 600,
fontSize: '16px'
}}
onClick={handleSubmit}
>
{editingCategory ? 'Update Category' : 'Add Category'}
</Button>
</Box>
);
};
export default AddCategory;
@@ -0,0 +1,93 @@
import React from "react";
import { Box, Typography } from "@mui/material";
import ProductCard from "./ProductCard";
const AllProducts = ({ products, showAll, onToggleShowAll, onProductClick, categoryName }) => {
const visibleProducts = showAll ? products : products.slice(0, 6);
return (
<Box
sx={{
mb: showAll ? { xs: 10, sm: 10 } : 0,
mr: showAll ? { xs: 2, sm: 3, md: 0 } : { xs: 2, sm: 2, md: 0 },
pl: { xs: 2, sm: 3 },
pr: { xs: 2, sm: 3 },
pb: { xs: 2, sm: 3 },
pt: { xs: 2, sm: 1.5 },
backgroundColor: "#FFFFFF",
maxWidth: showAll ? { xs: "90%", sm: "100%" } : undefined,
borderRadius: "10px",
padding: "20px",
display: "flex",
flexDirection: "column",
gap: "20px",
overflowY: showAll ? "auto" : "hidden",
}}
>
{/* العنوان */}
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<Typography variant="h6" fontWeight={600}>
{categoryName ? categoryName : "All Products"}
</Typography>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
</Box>
<Typography
variant="body2"
color="primary"
sx={{ cursor: "pointer" }}
onClick={onToggleShowAll}
>
{showAll ? "Show Less" : "See All"}
</Typography>
</Box>
{/* عرض المنتجات أو رسالة لا يوجد منتجات */}
{visibleProducts.length === 0 ? (
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
minHeight: "150px",
}}
>
<Typography variant="body1" color="text.secondary">
{categoryName
? `There is no product in ${categoryName}`
: "There is no product"}
</Typography>
</Box>
) : (
<Box
sx={{
display: "flex",
flexWrap: "wrap",
gap: 2,
}}
>
{visibleProducts.map((product, index) => (
<Box
key={index}
sx={{
// flex: "1 1 calc(33.333% - 16px)", // 3 منتجات بالصف في الديسكتوب
// minWidth: "250px", // أصغر حجم للمنتج
}}
onClick={() => onProductClick(product)}
>
<ProductCard
name={product.name}
price={product.price}
unit={product.unit}
image={product.image}
/>
</Box>
))}
</Box>
)}
</Box>
);
};
export default AllProducts;
@@ -0,0 +1,88 @@
import React from 'react';
import { Box, Avatar, Typography } from '@mui/material';
import { useTheme } from '@mui/material/styles';
const CategoryScrollList = ({
categories,
selectedCategory,
setSelectedCategory,
setSelectedCategoryForEdit,
setContextMenu,
scrollRef,
}) => {
const theme = useTheme();
return (
<Box
ref={scrollRef}
sx={{
width: '100%',
maxWidth: '100%',
overflowX: 'auto',
scrollBehavior: 'smooth',
pl: 1,
pr: { xs: 1, sm: 0 },
mb: 3,
mx: 'auto',
display: 'flex',
flexDirection: 'row',
gap: 2,
alignItems: 'center',
'&::-webkit-scrollbar': { display: 'none' },
scrollbarWidth: 'none',
}}
>
{categories.map((cat) => (
<Box
key={cat.id}
sx={{
p: 1,
borderRadius: '12px',
backgroundColor: selectedCategory === cat.id
? `${theme.palette.primary.main}`
: '#F9F9FC',
border: selectedCategory === cat.id
? `0px solid ${theme.palette.primary.main}`
: '0px solid #ddd',
textAlign: 'center',
minWidth: '70px',
cursor: 'pointer',
flexShrink: 0,
}}
onClick={() => setSelectedCategory(cat.id)}
onContextMenu={(e) => {
e.preventDefault();
setSelectedCategoryForEdit(cat);
setContextMenu({ mouseX: e.clientX + 2, mouseY: e.clientY - 6 });
}}
>
<Box
component="img"
src={cat.icon}
sx={{
width: 60,
height: 50,
mx: 'auto',
mb: 1,
}}
/>
<Typography
variant="body2"
fontWeight={500}
sx={{ color: selectedCategory === cat.id ? '#fff' : '#121212' }}
>
{cat.name}
</Typography>
</Box>
))}
</Box>
);
};
export default CategoryScrollList;
@@ -0,0 +1,359 @@
import React, { useState } from 'react';
import {
Box,
Typography,
Paper,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Chip,
IconButton,
TableContainer,
Button,
Menu,
MenuItem,
} from '@mui/material';
import TuneIcon from '@mui/icons-material/Tune';
import ReplayIcon from '@mui/icons-material/Replay';
import VisibilityIcon from '@mui/icons-material/Visibility';
import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew';
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
import { useTheme } from '@mui/material/styles';
import { useMediaQuery } from '@mui/material';
const SimplePagination = ({ currentPage, pageCount, onChange }) => {
const theme = useTheme();
const handlePrev = () => {
if (currentPage > 1) onChange(currentPage - 1);
};
const handleNext = () => {
if (currentPage < pageCount) onChange(currentPage + 1);
};
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 OrderList = () => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
const initialOrders = [
{ restaurant: 'المطعم الاول المطعم الاول ', time: '5:00 Pm', status: 'Pending' },
{ restaurant: 'Al-Baik', time: '5:00 Pm', status: 'Delivered' },
{ restaurant: 'Al-Baik', time: '5:00 Pm', status: 'Completed' },
{ restaurant: 'Al-Baik', time: '5:00 Pm', status: 'In-Progress' },
{ restaurant: 'Al-Baik', time: '5:00 Pm', status: 'Delivered' },
{ restaurant: 'Al-Baik', time: '5:00 Pm', status: 'Pending' },
{ restaurant: 'Al-Baik', time: '5:00 Pm', status: 'Completed' },
{ restaurant: 'Al-Baik', time: '5:00 Pm', status: 'Delivered' },
{ restaurant: 'Al-Baik', time: '5:00 Pm', status: 'Pending' },
{ restaurant: 'Al-Baik', time: '5:00 Pm', status: 'Delivered' },
{ restaurant: 'Al-Baik', time: '5:00 Pm', status: 'Completed' },
];
const [currentPage, setCurrentPage] = useState(1);
const [filterAnchorEl, setFilterAnchorEl] = useState(null);
const [statusFilter, setStatusFilter] = useState('all'); // 'all' يعني بدون فلتر
const itemsPerPage = 5;
// افتح قائمة الفلتر
const handleFilterClick = (event) => {
setFilterAnchorEl(event.currentTarget);
};
// اغلق قائمة الفلتر
const handleFilterClose = () => {
setFilterAnchorEl(null);
};
// عند اختيار فلتر جديد
const handleFilterSelect = (filter) => {
setStatusFilter(filter);
setCurrentPage(1); // العودة للصفحة الأولى بعد تغيير الفلتر
handleFilterClose();
};
// تصفية البيانات حسب الفلتر
const filteredOrders =
statusFilter === 'all'
? initialOrders
: initialOrders.filter(
(order) => order.status.toLowerCase() === statusFilter.toLowerCase()
);
const pageCount = Math.ceil(filteredOrders.length / itemsPerPage);
const paginatedData = filteredOrders.slice(
(currentPage - 1) * itemsPerPage,
currentPage * itemsPerPage
);
const getStatusColor = (status) => {
switch (status) {
case 'Pending':
return { bg: '#FFF3E0', color: '#E65100' };
case 'Delivered':
return { bg: '#E8F5E9', color: '#2E7D32' };
case 'Completed':
return { bg: '#F3E5F5', color: '#6A1B9A' };
case 'In-Progress':
return { bg: '#F1F8E9', color: '#9E9D24' };
default:
return { bg: '#E0E0E0', color: '#424242' };
}
};
const rowHeight = 73; // ارتفاع الصف الواحد تقريبي
const emptyRowsCount = itemsPerPage - paginatedData.length;
return (
<Paper
sx={{
backgroundColor: '#FFFFFF',
width: { xs: '100%', md: 400 },
borderRadius: '10px',
display: 'flex',
flexDirection: 'column',
overflow: 'hidden',
}}
>
{/* Header */}
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
px: 2,
py: 2,
}}
>
<Typography variant="h6" sx={{ fontSize: { xs: '1rem', sm: '1.25rem' } }}>
Order List
</Typography>
<Button
variant="outlined"
sx={{
textTransform: 'none',
color: '#667085',
borderColor: '#e0e0e0',
backgroundColor: '#fff',
borderRadius: '8px',
height: '40px',
fontSize: { xs: '12px', sm: '14px' },
fontWeight: 600,
}}
startIcon={<TuneIcon fontSize={isMobile ? 'small' : 'medium'} />}
onClick={handleFilterClick}
>
{isMobile ? '' : 'Filters'}
</Button>
{/* قائمة الفلاتر */}
<Menu
anchorEl={filterAnchorEl}
open={Boolean(filterAnchorEl)}
onClose={handleFilterClose}
PaperProps={{
sx: {
backgroundColor: 'white',
px: 1,
position: 'relative',
width: '140px',
},
}}
>
{['all', 'Pending', 'Delivered', 'Completed', 'In-Progress'].map((status) => (
<MenuItem
key={status}
selected={statusFilter.toLowerCase() === status.toLowerCase()}
onClick={() => handleFilterSelect(status)}
sx={{
color: statusFilter.toLowerCase() === status.toLowerCase() ? '#FF914D' : '#4F5867',
backgroundColor:
statusFilter.toLowerCase() === status.toLowerCase() ? '#fffcf9d5' : 'transparent',
'&:hover': {
backgroundColor: '#f0f0f0ff',
transition: 'background-color 150ms ease-in-out',
},
}}
>
{status.charAt(0).toUpperCase() + status.slice(1)}
</MenuItem>
))}
</Menu>
</Box>
{/* Table */}
<TableContainer
sx={{
maxHeight: 430,
overflowY: 'auto',
overflowX: 'hidden',
'&::-webkit-scrollbar': { height: 4 },
}}
>
<Table size={isMobile ? 'small' : 'medium'}>
<TableHead sx={{ backgroundColor: '#F9FAFB' }}>
<TableRow>
<TableCell sx={{ pl: 2 }}>Restaurant</TableCell>
<TableCell sx={{ pl: 2 }}>Time</TableCell>
<TableCell sx={{ pl: 4 }}>Status</TableCell>
<TableCell sx={{ pl: 2 }}>Action</TableCell>
</TableRow>
</TableHead>
<TableBody>
{paginatedData.length > 0 ? (
paginatedData.map((row, i) => {
const colors = getStatusColor(row.status);
return (
<TableRow key={i}>
<TableCell
sx={{
pl: 2,
whiteSpace: 'normal',
wordBreak: 'break-word',
lineHeight: 1, // للتحكم في المسافة بين الأسطر
}}
>
{row.restaurant}
</TableCell>
<TableCell>{row.time}</TableCell>
<TableCell>
<Chip
label={row.status}
sx={{
backgroundColor: colors.bg,
color: colors.color,
fontWeight: 600,
}}
/>
</TableCell>
<TableCell>
<Box sx={{ display: 'flex', gap: 0.4 }}>
<IconButton>
<ReplayIcon />
</IconButton>
<IconButton>
<VisibilityIcon />
</IconButton>
</Box>
</TableCell>
</TableRow>
);
})
) : (
<TableRow>
<TableCell colSpan={4} align="center" sx={{ py: 5 }}>
No orders found.
</TableCell>
</TableRow>
)}
{/* صفوف تعويضية فارغة لتثبيت ارتفاع الجدول */}
{emptyRowsCount > 0 &&
[...Array(emptyRowsCount)].map((_, idx) => (
<TableRow key={`empty-${idx}`} sx={{ height: rowHeight }}>
<TableCell colSpan={4} />
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
{/* Pagination */}
<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} -{' '}
{Math.min(currentPage * itemsPerPage, filteredOrders.length)} of {filteredOrders.length}
</Typography>
<SimplePagination currentPage={currentPage} pageCount={pageCount} onChange={setCurrentPage} />
</Box>
</Paper>
);
};
export default OrderList;
@@ -0,0 +1,154 @@
import React, { useState } from 'react';
import { Box, Typography, Grid, useTheme, IconButton } from '@mui/material';
import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew';
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
import ProductCard from './ProductCard';
const ITEMS_PER_PAGE = 10;
/* مكوّن الباجينيشن */
const SimplePagination = ({ currentPage, pageCount, onChange }) => {
const theme = useTheme();
const handlePrev = () => {
if (currentPage > 1) onChange(currentPage - 1);
};
const handleNext = () => {
if (currentPage < pageCount) onChange(currentPage + 1);
};
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',
}}
>
{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 PopularProductSection = ({ products = [], onProductClick }) => {
const theme = useTheme();
const [currentPage, setCurrentPage] = useState(1);
const totalPages = Math.ceil(products.length / ITEMS_PER_PAGE);
const startIdx = (currentPage - 1) * ITEMS_PER_PAGE;
const visibleProducts = products.slice(startIdx, startIdx + ITEMS_PER_PAGE);
return (
<Box
sx={{
pl: { xs: 2, sm: 3 },
pr: { xs: 2, sm: 3 },
pt: { xs: 2, sm: 1.5 },
pb: { xs: 2, sm: 3 },
backgroundColor: '#fff',
borderRadius: '10px',
display: 'flex',
flexDirection: 'column',
gap: '20px',
overflow: 'hidden',
}}
>
{/* الهيدر */}
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography variant="h6" fontWeight={600}>All Products</Typography>
</Box>
{/* المنتجات */}
<Grid container spacing={2}>
{visibleProducts.length > 0 ? (
visibleProducts.map((product, index) => (
<Grid item xs={12} sm={6} md={4} key={index}>
<ProductCard
{...product}
unit={product.unit || '/pk'}
onClick={() => onProductClick && onProductClick(product)}
/>
</Grid>
))
) : (
<Typography>There is no Product</Typography>
)}
</Grid>
{/* الباجينيشن */}
{totalPages > 1 && (
<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 {startIdx + 1} - {Math.min(startIdx + ITEMS_PER_PAGE, products.length)} of {products.length}
</Typography>
<SimplePagination
currentPage={currentPage}
pageCount={totalPages}
onChange={setCurrentPage}
/>
</Box>
)}
</Box>
);
};
export default PopularProductSection;
@@ -0,0 +1,117 @@
import React from 'react';
import { Box, Typography, Card, CardContent, CardMedia, IconButton } from '@mui/material';
import AddIcon from '@mui/icons-material/Add';
const ProductCard = ({ name, price, discountedPrice, unit, image, onClick, onAdd }) => {
return (
<Card
onClick={onClick}
sx={{
width: { xs: '160px', md: '152px' },
height: 245,
borderRadius: '20px',
p: '20px',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'space-between',
backgroundColor: '#FCFCFC',
boxShadow: 'none',
border: 'none',
cursor: 'pointer',
transition: 'transform 0.2s ease-in-out',
'&:hover': { transform: 'scale(1.02)' },
}}
>
{/* صورة المنتج */}
<CardMedia
component="img"
image={image || '/placeholder.png'}
alt={name}
sx={{
width: '140px',
height: '140px',
objectFit: 'contain',
mb: 2,
pointerEvents: 'none', // ما يمنع الكليك على الكارد
}}
/>
{/* تفاصيل المنتج */}
<CardContent sx={{ p: 0, width: '100%' }}>
<Typography variant="body1" fontWeight={600} fontSize={20} noWrap>
{name}
</Typography>
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
pt: 2,
gap: 1,
}}
>
{discountedPrice ? (
<>
<Typography
variant="body2"
fontWeight={600}
sx={{
textDecoration: 'line-through',
color: 'text.secondary',
fontSize: '14px',
}}
>
${price}
</Typography>
<Typography
variant="body2"
fontWeight={700}
sx={{ color: '#FF914D', fontSize: '16px' }}
>
${discountedPrice}{' '}
<Typography component="span" variant="caption" color="text.secondary">
{unit}
</Typography>
</Typography>
</>
) : (
<Typography
variant="body2"
fontWeight={600}
sx={{ color: '#FF914D', fontSize: '16px' }}
>
${price}{' '}
<Typography component="span" variant="caption" color="text.secondary">
{unit}
</Typography>
</Typography>
)}
{/* زر الإضافة */}
<IconButton
color="primary"
size="small"
onClick={(e) => {
e.stopPropagation(); // يمنع فتح التفاصيل عند الضغط على الزر
if (onAdd) onAdd();
}}
sx={{
backgroundColor: '#FF8551',
color: '#fff',
ml: 3,
width: 28,
height: 28,
'&:hover': { backgroundColor: '#ff7043' },
}}
>
<AddIcon fontSize="small" />
</IconButton>
</Box>
</CardContent>
</Card>
);
};
export default ProductCard;
@@ -0,0 +1,94 @@
import React, { useState, useContext, useEffect } from "react";
import { Box, Typography, Button, IconButton, LinearProgress, useTheme } from "@mui/material";
import RemoveIcon from "@mui/icons-material/Remove";
import AddIcon from "@mui/icons-material/Add";
import ShoppingCartOutlinedIcon from "@mui/icons-material/ShoppingCartOutlined";
import { CartContext } from "../../../../contexts/CartContextR";
// import { useRestaurant } from '../../../contexts/RestaurantContext';
import api from '../../../../services/authService';
const ProductDetail = ({ product, onBack }) => {
const theme = useTheme();
const { addToCart , createNewCart } = useContext(CartContext);
const [quantity, setQuantity] = useState(1);
const [totalPrice, setTotalPrice] = useState(Number(product?.price) || 0);
useEffect(() => {
if (product) {
setTotalPrice(Number(product.price) * quantity);
}
}, [quantity, product]);
if (!product) return null;
const handleIncrease = () => quantity < 9 && setQuantity(q => q + 1);
const handleDecrease = () => quantity > 1 && setQuantity(q => q - 1);
const handleAddToCart = () => {
addToCart(product, quantity);
createNewCart();
alert(`${product.name} added to cart! Quantity: ${quantity}, Total: $${totalPrice.toFixed(2)}`);
};
return (
<Box sx={{ display: "flex", backgroundColor: "#fff", borderRadius: "8px", p: 3, mx: "auto" }}>
<Box component="img" src={product.image} alt={product.name} sx={{ width: "50%", objectFit: "contain" }} />
<Box sx={{ width: "50%", pl: 4, display: "flex", flexDirection: "column" }}>
<Typography variant="h5" fontWeight={700} mb={0.5}>{product.name}</Typography>
<Typography variant="body2" sx={{ color: "#777", mb: 1 }}>{product.category}</Typography>
<Typography variant="h6" sx={{ color: theme.palette.primary.main, fontWeight: 700, mb: 1 }}>
${Number(product.price).toFixed(2)} {product.unit}
</Typography>
{/* <Typography variant="caption" sx={{ fontWeight: 600 }}>20 Item left</Typography> */}
<LinearProgress
variant="determinate"
value={100}
sx={{
mt: 3,
height: 4,
borderRadius: 2,
backgroundColor: "#f0f0f0",
"& .MuiLinearProgress-bar": { backgroundColor: theme.palette.primary.main },
mb: 2
}}
/>
<Typography variant="subtitle2" fontWeight={600}>Quantity</Typography>
<Box sx={{ display: "flex", alignItems: "center", border: "1px solid #ddd", borderRadius: "4px", width: "150px", mt: 1, mb: 0.5 }}>
<IconButton size="small" onClick={handleDecrease}><RemoveIcon /></IconButton>
<Typography sx={{ width: "40px", textAlign: "center" }}>{quantity}</Typography>
<IconButton size="small" onClick={handleIncrease}><AddIcon /></IconButton>
</Box>
<Typography variant="caption" sx={{ color: "#999", mb: 2 }}>Maximum purchase 9</Typography>
<Typography variant="h6" sx={{ color: theme.palette.primary.main, fontWeight: 700, mb: 2 }}>
Total: ${totalPrice.toFixed(2)}
</Typography>
<Box sx={{ display: "flex", gap: 1, mt: 10 }}>
<Button
fullWidth
variant="outlined"
startIcon={<ShoppingCartOutlinedIcon />}
sx={{ borderColor: theme.palette.primary.main, color: theme.palette.primary.main, textTransform: "none" }}
onClick={handleAddToCart}
>
Add To Cart
</Button>
<Button
fullWidth
variant="outlined"
onClick={onBack}
sx={{ borderColor: "#999", color: "#999" }}
>
Back
</Button>
</Box>
</Box>
</Box>
);
};
export default ProductDetail;
@@ -0,0 +1,140 @@
import React from 'react';
import {
Box,
Typography,
Card,
CardContent,
CardMedia,
IconButton,
Grid,
useTheme,
useMediaQuery,
} from '@mui/material';
import AddIcon from '@mui/icons-material/Add';
const ProductCard = ({ name, price, unit, image }) => {
return (
<Card
sx={{
width: { xs: '160px', md: '150px' },
height: 253,
borderRadius: '20px',
p: '20px',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'space-between',
backgroundColor: '#FCFCFC',
boxShadow: 'none',
border: 'none',
}}
>
<CardMedia
component="img"
image={image}
alt={name}
sx={{
width: '120px',
height: '400px',
objectFit: 'contain',
mb: 2,
}}
/>
<CardContent sx={{ p: 0, width: '100%' }}>
<Typography variant="body1" fontWeight={600} noWrap>
{name}
</Typography>
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
pt: 2,
gap: 1,
}}
>
<Typography
variant="body2"
fontWeight={600}
sx={{ color: '#FF914D', fontSize: '16px' }}
>
${price}{' '}
<Typography component="span" variant="caption" color="text.secondary">
{unit}
</Typography>
</Typography>
<IconButton
color="primary"
size="small"
sx={{ backgroundColor: '#FF8551', color: '#fff', ml: 5, width: 28, height: 28 }}
>
<AddIcon fontSize="small" />
</IconButton>
</Box>
</CardContent>
</Card>
);
};
const TopItems = ({ item, showAll, onToggleShowAll }) => {
const theme = useTheme();
const isXs = useMediaQuery(theme.breakpoints.down('sm'));
const visibleProducts = showAll ? item : item.slice(0, 6);
const maxHeight = showAll ? 'auto' : isXs ? '340px' : '335px';
const maxWidth = showAll ? { xs: '90%', sm: '100%' } : undefined;
return (
<Box
sx={{
mb: showAll ? { xs: 10, sm: 10 } : 0,
mr: showAll ? { xs: 2, sm: 3, md: 0 } : { xs: 2, sm: 2, md: 0 },
pl: { xs: 2, sm: 3 },
pr: { xs: 2, sm: 3 },
pb: { xs: 2, sm: 3 },
pt: { xs: 2, sm: 1.5 },
backgroundColor: '#FFFFFF',
maxWidth: maxWidth,
maxHeight: maxHeight,
borderRadius: '10px',
padding: '20px',
backgroundColor: '#fff',
display: 'flex',
flexDirection: 'column',
gap: '20px',
transition: 'max-height 0.3s ease-in-out',
overflowY: showAll ? 'auto' : 'hidden',
}}
>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography variant="h6" fontWeight={600}>
Top Items
</Typography>
<Typography
variant="body2"
color="primary"
sx={{ cursor: 'pointer' }}
onClick={onToggleShowAll}
>
{showAll ? 'Show Less' : 'See All'}
</Typography>
</Box>
<Grid container spacing={2} sx={{ width: '100%' }}>
{visibleProducts.map((product, index) => (
<Grid item xs={12} sm={6} md={4} key={index}>
<ProductCard
name={product.name}
price={product.price}
unit={product.unit}
image={product.image}
/>
</Grid>
))}
</Grid>
</Box>
);
};
export default TopItems;