initial commit
This commit is contained in:
@@ -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;
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user