نسخ من RaghadAlkhous/RestaurantDash
252 أسطر
8.5 KiB
JavaScript
252 أسطر
8.5 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
|
import {
|
|
Dialog,
|
|
DialogTitle,
|
|
DialogContent,
|
|
DialogActions,
|
|
TextField,
|
|
Typography,
|
|
Box,
|
|
Button,
|
|
IconButton,
|
|
InputAdornment,
|
|
} from '@mui/material';
|
|
import AddAPhotoOutlinedIcon from '@mui/icons-material/AddAPhotoOutlined';
|
|
import DeleteIcon from '@mui/icons-material/Delete';
|
|
import authService from '../../../../services/authService';
|
|
import AddIcon from '@mui/icons-material/Add';
|
|
|
|
const MAX_FILE_SIZE_MB = 2;
|
|
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/jpg'];
|
|
|
|
const AddMeal = ({ open, onClose, onAdd, selectedCategory, restaurantId }) => {
|
|
const [mealName, setMealName] = useState('');
|
|
const [description, setDescription] = useState('');
|
|
const [additions, setAdditions] = useState(['']);
|
|
const [price, setPrice] = useState('');
|
|
const [mealImage, setMealImage] = useState(null);
|
|
const [errors, setErrors] = useState({});
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (!open) {
|
|
setMealName('');
|
|
setDescription('');
|
|
setAdditions(['']);
|
|
setPrice('');
|
|
setMealImage(null);
|
|
setErrors({});
|
|
}
|
|
}, [open]);
|
|
|
|
const handleImageUpload = (e) => {
|
|
const file = e.target.files[0];
|
|
if (!file) return;
|
|
|
|
if (!ALLOWED_TYPES.includes(file.type)) {
|
|
setErrors(prev => ({ ...prev, image: 'Allowed types: jpg, jpeg, png' }));
|
|
return;
|
|
}
|
|
if (file.size > MAX_FILE_SIZE_MB * 1024 * 1024) {
|
|
setErrors(prev => ({ ...prev, image: `Image must be smaller than ${MAX_FILE_SIZE_MB} MB` }));
|
|
return;
|
|
}
|
|
|
|
setMealImage(file);
|
|
setErrors(prev => ({ ...prev, image: '' }));
|
|
};
|
|
|
|
const handleAddAddition = () => setAdditions([...additions, '']);
|
|
const handleRemoveAddition = (index) => setAdditions(additions.filter((_, i) => i !== index));
|
|
const handleChangeAddition = (index, value) => {
|
|
const newAdditions = [...additions];
|
|
newAdditions[index] = value;
|
|
setAdditions(newAdditions);
|
|
};
|
|
|
|
const validate = () => {
|
|
const newErrors = {};
|
|
if (!mealName.trim()) newErrors.name = 'Meal name is required';
|
|
if (!description.trim()) newErrors.description = 'Description is required';
|
|
if (!price || isNaN(price) || Number(price) <= 0) newErrors.price = 'Valid price is required';
|
|
if (!mealImage) newErrors.image = 'Meal image is required';
|
|
if (!selectedCategory) newErrors.category = 'Category must be selected';
|
|
additions.forEach((add, i) => {
|
|
if (!add.trim()) newErrors[`addition_${i}`] = 'Cannot be empty';
|
|
});
|
|
setErrors(newErrors);
|
|
return Object.keys(newErrors).length === 0;
|
|
};
|
|
|
|
const handleSubmit = async () => {
|
|
if (!validate()) return;
|
|
setIsSubmitting(true);
|
|
|
|
const mealData = {
|
|
name: mealName.trim(),
|
|
category_id: selectedCategory,
|
|
price,
|
|
restaurant_id: restaurantId,
|
|
photo: mealImage,
|
|
description: [description.trim()],
|
|
additions: additions.filter(a => a.trim() !== ''),
|
|
};
|
|
|
|
try {
|
|
const result = await authService.addMeal(mealData);
|
|
|
|
// عرض رسالة Snackbar أو alert
|
|
alert(result.message);
|
|
|
|
// تمرير الوجبة الجديدة للمكون الأب لتحديث القائمة
|
|
if (result.data && onAdd) {
|
|
const newMeal = {
|
|
id: result.data.id,
|
|
name: result.data.name,
|
|
price: result.data.price,
|
|
description: result.data.description || [],
|
|
additions: result.data.additions || [],
|
|
unit: result.data.unit || "/pcs",
|
|
photo: result.data.photo || "/images/default-product.png",
|
|
category_id: result.data.category_id || selectedCategory,
|
|
};
|
|
onAdd(newMeal);
|
|
}
|
|
|
|
// إغلاق المودال بعد الإضافة
|
|
onClose();
|
|
|
|
} catch (error) {
|
|
console.error('Add meal error:', error);
|
|
alert(error.message || "Something went wrong");
|
|
} finally {
|
|
setIsSubmitting(false);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
<Dialog open={open} onClose={onClose} maxWidth="sm" fullWidth>
|
|
<DialogTitle sx={{ fontWeight: 600, fontSize: '20px' }}>Add Meal</DialogTitle>
|
|
<DialogContent
|
|
dividers
|
|
sx={{
|
|
overflow: 'auto',
|
|
'&::-webkit-scrollbar': {
|
|
display: 'none',
|
|
},
|
|
scrollbarWidth: 'none',
|
|
msOverflowStyle: 'none',
|
|
}}
|
|
>
|
|
|
|
|
|
{/* Meal Image */}
|
|
<Typography variant="body2" fontWeight={500} sx={{ mb: 1 }}>Meal Image</Typography>
|
|
<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,
|
|
'&:hover': { borderColor: '#FF914D', backgroundColor: '#fffaf5' }
|
|
}}
|
|
>
|
|
{mealImage ? (
|
|
<Box sx={{ position: 'relative', width: '100%', height: '100%', borderRadius: 2, overflow: 'hidden' }}>
|
|
<img src={mealImage instanceof File ? URL.createObjectURL(mealImage) : mealImage} alt="Preview" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
|
|
<Button onClick={() => setMealImage(null)} sx={{ position: 'absolute', top: 4, right: 4, minWidth: 0, padding: '2px 6px', fontSize: 12 }}>Remove</Button>
|
|
</Box>
|
|
) : (
|
|
<>
|
|
Upload Meal Picture
|
|
<AddAPhotoOutlinedIcon sx={{ fontSize: 32, color: '#9e9e9e', mt: 1 }} />
|
|
</>
|
|
)}
|
|
<input id="upload-image" type="file" accept="image/*" hidden onChange={handleImageUpload} />
|
|
</Box>
|
|
{errors.image && <Typography color="error" sx={{ mb: 1 }}>{errors.image}</Typography>}
|
|
|
|
{/* Meal Name */}
|
|
<Typography variant="body2" fontWeight={500} sx={{ mb: 1 }}>Meal Name</Typography>
|
|
<TextField fullWidth placeholder="Meal Name" value={mealName} onChange={e => setMealName(e.target.value)} error={!!errors.name} helperText={errors.name} sx={{ mb: 2 }} />
|
|
|
|
{/* Description */}
|
|
<Typography variant="body2" fontWeight={500} sx={{ mb: 1 }}>Description</Typography>
|
|
<TextField fullWidth placeholder="Description" value={description} onChange={e => setDescription(e.target.value)} error={!!errors.description} helperText={errors.description} sx={{ mb: 2 }} />
|
|
|
|
{/* Additions */}
|
|
<Typography variant="body2" fontWeight={500} sx={{ mb: 1 }}>Additions</Typography>
|
|
{additions.map((add, i) => (
|
|
<Box key={i} sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
|
|
<TextField
|
|
fullWidth
|
|
placeholder={`Addition ${i + 1}`}
|
|
value={add}
|
|
onChange={e => handleChangeAddition(i, e.target.value)}
|
|
error={!!errors[`addition_${i}`]}
|
|
helperText={errors[`addition_${i}`]}
|
|
/>
|
|
<IconButton onClick={() => handleRemoveAddition(i)} color="error"><DeleteIcon /></IconButton>
|
|
</Box>
|
|
))}
|
|
<Button variant="text" startIcon={<AddIcon />} onClick={handleAddAddition} sx={{ mb: 2 ,textTransform:'none'}}>Add Another</Button>
|
|
|
|
{/* Price */}
|
|
<Typography variant="body2" fontWeight={500} sx={{ mb: 1 }}>Price</Typography>
|
|
<TextField fullWidth type="number" placeholder="Price" value={price} onChange={e => setPrice(e.target.value)} error={!!errors.price} helperText={errors.price} sx={{ mb: 2 }} />
|
|
</DialogContent>
|
|
|
|
<DialogActions sx={{ pr: 3, pb: 2 }}>
|
|
<Button
|
|
onClick={onClose}
|
|
sx={{
|
|
borderRadius: '8px',
|
|
fontWeight: 600,
|
|
fontSize: '14px',
|
|
height: '40px',
|
|
width: '135px',
|
|
textTransform: 'none',
|
|
}}
|
|
variant="outlined"
|
|
size="small"
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
onClick={handleSubmit}
|
|
sx={{
|
|
color: 'white',
|
|
borderRadius: '8px',
|
|
fontWeight: 600,
|
|
fontSize: '14px',
|
|
height: '40px',
|
|
width: '135px',
|
|
textTransform: 'none',
|
|
}}
|
|
variant="contained"
|
|
size="small"
|
|
>
|
|
Add Meal
|
|
</Button>
|
|
</DialogActions>
|
|
</Dialog>
|
|
);
|
|
};
|
|
|
|
export default AddMeal;
|