1
0

Linking of login, logout and registration interfaces.

هذا الالتزام موجود في:
raghad
2025-06-24 21:33:32 +03:00
الأصل 4a72e96c76
التزام 9f05ca5f9c
19 ملفات معدلة مع 452 إضافات و170 حذوفات

ملف ثنائي غير معروض.

قبل

العرض:  |  الارتفاع:  |  الحجم: 44 KiB

ملف ثنائي غير معروض.

قبل

العرض:  |  الارتفاع:  |  الحجم: 45 KiB

ملف ثنائي غير معروض.

قبل

العرض:  |  الارتفاع:  |  الحجم: 1.2 MiB

ثنائية
public/images/waitress1.png Normal file

ملف ثنائي غير معروض.

بعد

العرض:  |  الارتفاع:  |  الحجم: 124 KiB

ثنائية
public/images/waitress2.png Normal file

ملف ثنائي غير معروض.

بعد

العرض:  |  الارتفاع:  |  الحجم: 164 KiB

ملف ثنائي غير معروض.

قبل

العرض:  |  الارتفاع:  |  الحجم: 47 KiB

بعد

العرض:  |  الارتفاع:  |  الحجم: 135 KiB

ثنائية
public/images/waitress4.png Normal file

ملف ثنائي غير معروض.

بعد

العرض:  |  الارتفاع:  |  الحجم: 122 KiB

عرض الملف

@@ -21,32 +21,109 @@ import Training from './components/Home/Training/Training';
import RestaurantProfile from './components/Home/RestaurantProfile/RestaurantProfile'; import RestaurantProfile from './components/Home/RestaurantProfile/RestaurantProfile';
import HostKitchen from './components/Home/HostKitchen/HostKitchen'; import HostKitchen from './components/Home/HostKitchen/HostKitchen';
import Settings from './components/Home/Settings/Setting'; import Settings from './components/Home/Settings/Setting';
import PrivateRoute from './components/Routes/PrivateRoute';
import PublicRoute from './components/Routes/PublicRoute';
import { Navigate } from 'react-router-dom';
function App() { function App() {
return ( return (
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<Router> <Router>
<Routes> <Routes>
<Route path="/" element={<LoginForm />} /> {/* الصفحات العامة */}
<Route path="/login" element={<LoginForm />} /> <Route path="/" element={<Navigate to="/login" replace />} />
<Route path="/register" element={<RegisterForm />} />
<Route path="/dashboard" element={<Dashboard />} /> <Route path="/" element={<PublicRoute><LoginForm /></PublicRoute>} />
<Route path="/inventory" element={<Inventory />} /> <Route path="/login" element={<PublicRoute><LoginForm /></PublicRoute>} />
<Route path="/settings" element={<Settings />} /> <Route path="/register" element={<PublicRoute><RegisterForm /></PublicRoute>} />
<Route path="/profile" element={<RestaurantProfile />} /> <Route path="/forget" element={<PublicRoute><ForgetPassword /></PublicRoute>} />
<Route path="/create-kitchen" element={<CreateYourRestaurant />} />
<Route path="/host-kitchen" element={<HostKitchen />} /> {/* الصفحات المحمية */}
<Route path="/cashier" element={<Cashier />} /> <Route
<Route path="/supplier" element={<Supplier />} /> path="/dashboard"
<Route path="/analytics" element={<Analytics />} /> element={
<Route path="/forget" element={<ForgetPassword />} /> <PrivateRoute>
<Route path="/training" element={<Training />} /> <Dashboard />
</PrivateRoute>
}
/>
<Route
path="/inventory"
element={
<PrivateRoute>
<Inventory />
</PrivateRoute>
}
/>
<Route
path="/settings"
element={
<PrivateRoute>
<Settings />
</PrivateRoute>
}
/>
<Route
path="/profile"
element={
<PrivateRoute>
<RestaurantProfile />
</PrivateRoute>
}
/>
<Route
path="/create-kitchen"
element={
<PrivateRoute>
<CreateYourRestaurant />
</PrivateRoute>
}
/>
<Route
path="/host-kitchen"
element={
<PrivateRoute>
<HostKitchen />
</PrivateRoute>
}
/>
<Route
path="/cashier"
element={
<PrivateRoute>
<Cashier />
</PrivateRoute>
}
/>
<Route
path="/supplier"
element={
<PrivateRoute>
<Supplier />
</PrivateRoute>
}
/>
<Route
path="/analytics"
element={
<PrivateRoute>
<Analytics />
</PrivateRoute>
}
/>
<Route
path="/training"
element={
<PrivateRoute>
<Training />
</PrivateRoute>
}
/>
</Routes> </Routes>
</Router> </Router>
</ThemeProvider> </ThemeProvider>
); );
} }
export default App; export default App;

عرض الملف

@@ -3,27 +3,47 @@ import { TextField, Button, Typography, Stack, Box, IconButton, InputAdornment }
import { useTheme } from '@mui/material/styles'; import { useTheme } from '@mui/material/styles';
import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined'; import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined';
import { VisibilityOutlined } from '@mui/icons-material'; import { VisibilityOutlined } from '@mui/icons-material';
import SidePanel from './SidePanel'; // استدعاء SidePanel من نفس المجلد أو حسب المسار المناسب import SidePanel from './SidePanel';
import { useNavigate } from 'react-router-dom'; import { useNavigate, Link } from 'react-router-dom';
import { Link } from 'react-router-dom'; import authService from '../../../services/authService';
const LoginForm = () => { const LoginForm = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const theme = useTheme(); const theme = useTheme();
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const handleTogglePassword = () => { const handleTogglePassword = () => {
setShowPassword((prev) => !prev); setShowPassword(prev => !prev);
}; };
const handleLogin = () => {
// بعد تنفيذ عملية تسجيل الدخول بنجاح، يتم التوجيه const handleLogin = async () => {
navigate('/dashboard'); setLoading(true);
setError('');
const result = await authService.login(email, password);
if (!result.success) {
setError(result.message || 'Login failed');
setLoading(false);
return;
}
localStorage.setItem('token', result.data.token);
localStorage.setItem('refresh_token', result.data.refresh_token);
setLoading(false);
// الانتقال للداشبورد مع منع الرجوع
navigate('/dashboard', { replace: true });
}; };
return ( return (
<Box <Box
bgcolor={"background.default"} bgcolor={"background.default"}
color={"text.primary"} color={"text.primary"}
@@ -38,14 +58,11 @@ const LoginForm = () => {
}, },
}} }}
> >
{/* SidePanel ثابت */}
<SidePanel /> <SidePanel />
{/* Login Content */}
<Box <Box
sx={{ sx={{
marginLeft: { xs: 0, sm: '50%' }, marginLeft: { xs: 0, sm: '50%' },
width: { xs: '100%', sm: '50%' ,md:'70%'}, width: { xs: '100%', sm: '50%', md: '70%' },
}} }}
> >
<Box <Box
@@ -76,7 +93,7 @@ const LoginForm = () => {
> >
<Stack spacing={2}> <Stack spacing={2}>
{/* Logo */} {/* الشعار */}
<Box> <Box>
<img <img
src="/image.png" src="/image.png"
@@ -90,30 +107,16 @@ const LoginForm = () => {
/> />
</Box> </Box>
<Typography {/* عنوان تسجيل الدخول */}
variant="h4" <Typography variant="h4" fontWeight={700} sx={{ fontSize: { xs: '1.8rem', sm: '2rem', md: '2.2rem' } }}>
fontWeight={700}
sx={{
fontSize: {
xs: '1.8rem',
sm: '2rem',
md: '2.2rem'
}
}}
>
Login Login
</Typography> </Typography>
<Typography <Typography variant="body2" color="text.secondary" fontWeight={500} sx={{ pb: 1 }}>
variant="body2"
color="text.secondary"
fontWeight={500}
sx={{ pb: 1 }}
>
Enter your username and password to access your account securely. Welcome back to our service! Enter your username and password to access your account securely. Welcome back to our service!
</Typography> </Typography>
{/* Email Input */} {/* إدخال البريد */}
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}> <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
<Typography variant="body2" color="black" sx={{ fontWeight: '500', fontSize: '16px' }}> <Typography variant="body2" color="black" sx={{ fontWeight: '500', fontSize: '16px' }}>
Email Email
@@ -123,15 +126,17 @@ const LoginForm = () => {
type="email" type="email"
variant="outlined" variant="outlined"
fullWidth fullWidth
value={email}
onChange={e => setEmail(e.target.value)}
sx={{ sx={{
'& input': { fontWeight: 500, fontSize: '15px' }, '& input': { fontWeight: 500, fontSize: '15px' },
'& input::placeholder': { color: '#969BA7' }, '& input::placeholder': { color: '#969BA7' },
'& .MuiOutlinedInput-root': { '& .MuiOutlinedInput-root': {
borderRadius: '10px', borderRadius: '10px',
transition: '0.3s', transition: '0.3s',
'&.Mui-focused fieldset': { // أضف هذا الجزء لتغيير لون الحدود عند التركيز '&.Mui-focused fieldset': {
borderColor: theme.palette.primary.main, borderColor: theme.palette.primary.main,
boxShadow: '0 0 0 2px rgba(255, 145, 77, 0.2)' // ظل برتقالي خفيف boxShadow: '0 0 0 2px rgba(255, 145, 77, 0.2)'
} }
}, },
'& .MuiOutlinedInput-root.Mui-focused': { '& .MuiOutlinedInput-root.Mui-focused': {
@@ -142,7 +147,7 @@ const LoginForm = () => {
/> />
</Box> </Box>
{/* Password Input */} {/* إدخال كلمة السر */}
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}> <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
<Typography variant="body2" color="black" sx={{ fontWeight: '500', fontSize: '16px' }}> <Typography variant="body2" color="black" sx={{ fontWeight: '500', fontSize: '16px' }}>
Password Password
@@ -153,15 +158,17 @@ const LoginForm = () => {
fullWidth fullWidth
variant="outlined" variant="outlined"
autoComplete="new-password" autoComplete="new-password"
value={password}
onChange={e => setPassword(e.target.value)}
sx={{ sx={{
'& input': { fontWeight: 500, fontSize: '15px' }, '& input': { fontWeight: 500, fontSize: '15px' },
'& input::placeholder': { color: '#969BA7' }, '& input::placeholder': { color: '#969BA7' },
'& .MuiOutlinedInput-root': { '& .MuiOutlinedInput-root': {
borderRadius: '10px', borderRadius: '10px',
transition: '0.3s', transition: '0.3s',
'&.Mui-focused fieldset': { // أضف هذا الجزء لتغيير لون الحدود عند التركيز '&.Mui-focused fieldset': {
borderColor: theme.palette.primary.main, borderColor: theme.palette.primary.main,
boxShadow: '0 0 0 2px rgba(255, 145, 77, 0.2)' // ظل برتقالي خفيف boxShadow: '0 0 0 2px rgba(255, 145, 77, 0.2)'
} }
}, },
'& .MuiOutlinedInput-root.Mui-focused': { '& .MuiOutlinedInput-root.Mui-focused': {
@@ -184,14 +191,22 @@ const LoginForm = () => {
/> />
</Box> </Box>
{/* Login Button */} {/* عرض الخطأ */}
{error && (
<Typography color="error" textAlign="center">
{error}
</Typography>
)}
{/* زر تسجيل الدخول */}
<Box sx={{ pt: 2 }}> <Box sx={{ pt: 2 }}>
<Button <Button
variant="contained" variant="contained"
fullWidth fullWidth
onClick={handleLogin} onClick={handleLogin}
disabled={loading}
sx={{ sx={{
fontFamily: 'PlusJakartaSans' , fontFamily: 'PlusJakartaSans',
fontWeight: 600, fontWeight: 600,
fontSize: { xs: '14px', sm: '16px' }, fontSize: { xs: '14px', sm: '16px' },
height: { xs: '45px', sm: '52px' }, height: { xs: '45px', sm: '52px' },
@@ -204,7 +219,7 @@ const LoginForm = () => {
} }
}} }}
> >
Log in {loading ? 'Loading...' : 'Log in'}
</Button> </Button>
</Box> </Box>

عرض الملف

@@ -4,12 +4,22 @@ import { useTheme } from '@mui/material/styles';
import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined'; import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined';
import { VisibilityOutlined } from '@mui/icons-material'; import { VisibilityOutlined } from '@mui/icons-material';
import SidePanel from './SidePanel'; import SidePanel from './SidePanel';
import { Link } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import authService from '../../../services/authService';
const Register = () => { const Register = () => {
const theme = useTheme(); const theme = useTheme();
const navigate = useNavigate();
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const [successMessage, setSuccessMessage] = useState('');
const handleTogglePassword = () => { const handleTogglePassword = () => {
setShowPassword((prev) => !prev); setShowPassword((prev) => !prev);
}; };
@@ -18,6 +28,36 @@ const Register = () => {
setShowConfirmPassword((prev) => !prev); setShowConfirmPassword((prev) => !prev);
}; };
const handleRegister = async () => {
setLoading(true);
setError('');
setSuccessMessage('');
const result = await authService.register(email, password, confirmPassword);
if (!result.success) {
if (result.errors) {
const firstError = Object.values(result.errors)[0][0];
setError(firstError);
} else if (result.message && result.message.includes('Duplicate entry')) {
setError('The email has already been taken.');
} else {
setError(result.message || 'Registration failed');
}
setLoading(false);
return;
}
// تسجيل ناجح
localStorage.setItem('token', result.data.token);
localStorage.setItem('refresh_token', result.data.refresh_token);
setTimeout(() => {
navigate('/dashboard', { replace: true }); // استبدال الرابط وعدم السماح بالعودة
setLoading(false);
}, 500);
};
return ( return (
<Box bgcolor={"background.default"} color={"text.primary"} display="flex" sx={{ <Box bgcolor={"background.default"} color={"text.primary"} display="flex" sx={{
width: '100vw', width: '100vw',
@@ -28,17 +68,12 @@ const Register = () => {
display: 'none', display: 'none',
}, },
}}> }}>
{/* SidePanel ثابت */}
<SidePanel /> <SidePanel />
{/* Login Content */} <Box sx={{
<Box marginLeft: { xs: 0, sm: '50%' },
sx={{ width: { xs: '100%', sm: '50%' },
marginLeft: { xs: 0, sm: '50%' }, }}>
width: { xs: '100%', sm: '50%' },
}}
>
<Box <Box
flex={1} flex={1}
paddingTop={4} paddingTop={4}
@@ -66,8 +101,6 @@ const Register = () => {
}} }}
> >
<Stack spacing={2}> <Stack spacing={2}>
{/* Logo */}
<Box> <Box>
<img <img
src="/image.png" src="/image.png"
@@ -105,7 +138,6 @@ const Register = () => {
create your account successfully. create your account successfully.
</Typography> </Typography>
{/* Email Input */}
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}> <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
<Typography <Typography
variant="body2" variant="body2"
@@ -122,6 +154,8 @@ const Register = () => {
type="email" type="email"
variant="outlined" variant="outlined"
fullWidth fullWidth
value={email}
onChange={(e) => setEmail(e.target.value)}
sx={{ sx={{
'& input': { '& input': {
fontWeight: 500, fontWeight: 500,
@@ -133,9 +167,9 @@ const Register = () => {
'& .MuiOutlinedInput-root': { '& .MuiOutlinedInput-root': {
borderRadius: '10px', borderRadius: '10px',
transition: '0.3s', transition: '0.3s',
'&.Mui-focused fieldset': { // أضف هذا الجزء لتغيير لون الحدود عند التركيز '&.Mui-focused fieldset': {
borderColor: theme.palette.primary.main, borderColor: theme.palette.primary.main,
boxShadow: '0 0 0 2px rgba(255, 145, 77, 0.2)' // ظل برتقالي خفيف boxShadow: '0 0 0 2px rgba(255, 145, 77, 0.2)'
} }
}, },
'& .MuiOutlinedInput-root.Mui-focused': { '& .MuiOutlinedInput-root.Mui-focused': {
@@ -146,7 +180,6 @@ const Register = () => {
/> />
</Box> </Box>
{/* Password Input */}
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}> <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
<Typography <Typography
variant="body2" variant="body2"
@@ -164,6 +197,8 @@ const Register = () => {
fullWidth fullWidth
variant="outlined" variant="outlined"
autoComplete="new-password" autoComplete="new-password"
value={password}
onChange={(e) => setPassword(e.target.value)}
sx={{ sx={{
'& input': { '& input': {
fontWeight: 500, fontWeight: 500,
@@ -175,9 +210,9 @@ const Register = () => {
'& .MuiOutlinedInput-root': { '& .MuiOutlinedInput-root': {
borderRadius: '10px', borderRadius: '10px',
transition: '0.3s', transition: '0.3s',
'&.Mui-focused fieldset': { // أضف هذا الجزء لتغيير لون الحدود عند التركيز '&.Mui-focused fieldset': {
borderColor: theme.palette.primary.main, borderColor: theme.palette.primary.main,
boxShadow: '0 0 0 2px rgba(255, 145, 77, 0.2)' // ظل برتقالي خفيف boxShadow: '0 0 0 2px rgba(255, 145, 77, 0.2)'
} }
}, },
'& .MuiOutlinedInput-root.Mui-focused': { '& .MuiOutlinedInput-root.Mui-focused': {
@@ -200,7 +235,6 @@ const Register = () => {
/> />
</Box> </Box>
{/* Confirm Password Input */}
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}> <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
<Typography <Typography
variant="body2" variant="body2"
@@ -218,6 +252,8 @@ const Register = () => {
fullWidth fullWidth
variant="outlined" variant="outlined"
autoComplete="new-password" autoComplete="new-password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
sx={{ sx={{
'& input': { '& input': {
fontWeight: 500, fontWeight: 500,
@@ -229,9 +265,9 @@ const Register = () => {
'& .MuiOutlinedInput-root': { '& .MuiOutlinedInput-root': {
borderRadius: '10px', borderRadius: '10px',
transition: '0.3s', transition: '0.3s',
'&.Mui-focused fieldset': { // أضف هذا الجزء لتغيير لون الحدود عند التركيز '&.Mui-focused fieldset': {
borderColor: '#FF914D', borderColor: '#FF914D',
boxShadow: '0 0 0 2px rgba(255, 145, 77, 0.2)' // ظل برتقالي خفيف boxShadow: '0 0 0 2px rgba(255, 145, 77, 0.2)'
} }
}, },
'& .MuiOutlinedInput-root.Mui-focused': { '& .MuiOutlinedInput-root.Mui-focused': {
@@ -254,21 +290,28 @@ const Register = () => {
/> />
</Box> </Box>
{/* Login Button */} {/* Error & Success Message */}
{error && (
<Typography color="error" textAlign="center">
{error}
</Typography>
)}
{successMessage && (
<Typography color="green" textAlign="center">
{successMessage}
</Typography>
)}
<Box sx={{ pt: 2 }}> <Box sx={{ pt: 2 }}>
<Button <Button
variant="contained" variant="contained"
fullWidth fullWidth
onClick={handleRegister}
disabled={loading}
sx={{ sx={{
fontWeight: 600, fontWeight: 600,
fontSize: { fontSize: { xs: '14px', sm: '16px' },
xs: '14px', height: { xs: '45px', sm: '52px' },
sm: '16px'
},
height: {
xs: '45px',
sm: '52px'
},
borderRadius: '50px', borderRadius: '50px',
textTransform: 'none', textTransform: 'none',
color: 'white', color: 'white',
@@ -278,7 +321,7 @@ const Register = () => {
} }
}} }}
> >
Continue {loading ? 'Loading...' : 'Continue'}
</Button> </Button>
</Box> </Box>
@@ -298,7 +341,7 @@ const Register = () => {
<Box sx={{ flex: 1, height: '1px', backgroundColor: '#E0E0E0' }} /> <Box sx={{ flex: 1, height: '1px', backgroundColor: '#E0E0E0' }} />
</Box> </Box>
{/* Google Button */} {/* Google */}
<Button <Button
variant="outlined" variant="outlined"
fullWidth fullWidth
@@ -327,7 +370,7 @@ const Register = () => {
Login with Google Login with Google
</Button> </Button>
{/* Facebook Button */} {/* Facebook */}
<Button <Button
variant="outlined" variant="outlined"
fullWidth fullWidth
@@ -356,8 +399,6 @@ const Register = () => {
Login with Facebook Login with Facebook
</Button> </Button>
{/* login Link */}
<Typography <Typography
variant="body2" variant="body2"
sx={{ sx={{
@@ -379,7 +420,6 @@ const Register = () => {
</Link> </Link>
</Typography> </Typography>
{/* Terms */}
<Typography <Typography
variant="caption" variant="caption"
sx={{ sx={{
@@ -389,7 +429,7 @@ const Register = () => {
pt: 5 pt: 5
}} }}
> >
By log in, I agree to the and {' '} By log in, I agree to the and{' '}
<a href="#" style={{ color: '#2261FF', textDecoration: 'none' }}> <a href="#" style={{ color: '#2261FF', textDecoration: 'none' }}>
Terms of Service Terms of Service
</a>{' '} </a>{' '}
@@ -406,4 +446,4 @@ const Register = () => {
); );
}; };
export default Register; export default Register;

عرض الملف

@@ -15,12 +15,12 @@ const SidePanel = ({ setMode, mode }) => {
const slides = [ const slides = [
{ {
image: '/images/waitress3.png', image: '/images/waitress1.png',
title: "Welcome to our cutting-edge postal application,", title: "Welcome to our cutting-edge postal application,",
description: "Welcome to our cutting-edge postal application, where sending and receiving mail has never been more convenient and efficient." description: "Welcome to our cutting-edge postal application, where sending and receiving mail has never been more convenient and efficient."
}, },
{ {
image: '/images/waitress3.png', image: '/images/waitress2.png',
title: "Second Slide Title", title: "Second Slide Title",
description: "Welcome to our cutting-edge postal application, where sending and receiving mail has never been more convenient and efficient." description: "Welcome to our cutting-edge postal application, where sending and receiving mail has never been more convenient and efficient."
}, },
@@ -30,7 +30,7 @@ const SidePanel = ({ setMode, mode }) => {
description: "Welcome to our cutting-edge postal application, where sending and receiving mail has never been more convenient and efficient." description: "Welcome to our cutting-edge postal application, where sending and receiving mail has never been more convenient and efficient."
}, },
{ {
image: '/images/waitress3.png', image: '/images/waitress4.png',
title: "Fourth Slide Title", title: "Fourth Slide Title",
description: "Welcome to our cutting-edge postal application, where sending and receiving mail has never been more convenient and efficient." description: "Welcome to our cutting-edge postal application, where sending and receiving mail has never been more convenient and efficient."
} }

عرض الملف

@@ -22,25 +22,25 @@ const AnalyticsPage = () => {
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [sidebarOpen, setSidebarOpen] = useState(!isMobile); const [sidebarOpen, setSidebarOpen] = useState(!isMobile);
const dailyData = [ const dailyData = [
{ date: '10:00', visitors: 1500, conversions: 300 }, { date: '10:00', visitors: 1500, conversions: 300 },
{ date: '11:00', visitors: 1800, conversions: 320 }, { date: '11:00', visitors: 1800, conversions: 320 },
]; ];
const weeklyData = [ const weeklyData = [
{ label: 'Week 1', visitors: 1500, conversions: 200 }, { label: 'Week 1', visitors: 1500, conversions: 200 },
{ label: 'Week 2', visitors: 2300, conversions: 400 }, { label: 'Week 2', visitors: 2300, conversions: 400 },
]; ];
const monthlyData = [ const monthlyData = [
{ label: 'Jan', visitors: 15000, conversions: 3000 }, { label: 'Jan', visitors: 15000, conversions: 3000 },
{ label: 'Feb', visitors: 18000, conversions: 4000 }, { label: 'Feb', visitors: 18000, conversions: 4000 },
]; ];
const yearlyData = [ const yearlyData = [
{ label: '2024', visitors: 25000, conversions: 7000 }, { label: '2024', visitors: 25000, conversions: 7000 },
{ label: '2025', visitors: 38000, conversions: 12000 }, { label: '2025', visitors: 38000, conversions: 12000 },
]; ];
const getData = () => { const getData = () => {
@@ -94,11 +94,11 @@ const yearlyData = [
}, [theme.breakpoints.values.md]); }, [theme.breakpoints.values.md]);
return ( return (
<Box <Box
p={{ xs: 1, sm: 2 }} p={{ xs: 1, sm: 2 }}
maxWidth="100%" maxWidth="100%"
overflowX="hidden" overflowX="hidden"
> >
{/* Header Buttons */} {/* Header Buttons */}
<Box <Box
display="flex" display="flex"

عرض الملف

@@ -3,7 +3,7 @@ import { Box, Typography, Stack, Button, useTheme, TextField } from '@mui/materi
import AddIcon from '@mui/icons-material/Add'; import AddIcon from '@mui/icons-material/Add';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
const Budget= ({ currentStepIndex = 0, onNext, onBack }) => { const Budget = ({ currentStepIndex = 0, onNext, onBack }) => {
const theme = useTheme(); const theme = useTheme();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -49,7 +49,7 @@ const Budget= ({ currentStepIndex = 0, onNext, onBack }) => {
{/* Expansion Plans Through Cloud Kitchen (No of Branches) Input */} {/* Expansion Plans Through Cloud Kitchen (No of Branches) Input */}
<InputField label="Expansion Plans Through Cloud Kitchen (No of Branches)" placeholder="200" theme={theme} /> <InputField label="Expansion Plans Through Cloud Kitchen (No of Branches)" placeholder="200" theme={theme} />
{/* Next Button */} {/* Next Button */}

عرض الملف

@@ -57,7 +57,7 @@ const TableView = ({ data = [], onAddNewProduct }) => {
setCurrentPage(1); // العودة للصفحة الأولى عند تغيير البحث setCurrentPage(1); // العودة للصفحة الأولى عند تغيير البحث
}; };
const handleExportPDF = () => { const handleExportPDF = () => {
const doc = new jsPDF(); const doc = new jsPDF();
doc.setFontSize(14); doc.setFontSize(14);
doc.text('Top Selling Products', 14, 20); doc.text('Top Selling Products', 14, 20);
@@ -68,7 +68,7 @@ const TableView = ({ data = [], onAddNewProduct }) => {
row.product, row.product,
row.sales, row.sales,
`$${row.amount}`, `$${row.amount}`,
`$${row.price}`, `$${row.price}`,
row.expiration, row.expiration,
row.status row.status
])); ]));
@@ -92,7 +92,7 @@ const TableView = ({ data = [], onAddNewProduct }) => {
row.product, row.product,
row.sales, row.sales,
`$${row.amount}`, `$${row.amount}`,
`$${row.price}`, `$${row.price}`,
row.expiration, row.expiration,
row.status row.status
]) ])
@@ -120,8 +120,8 @@ const TableView = ({ data = [], onAddNewProduct }) => {
overflow: 'hidden', overflow: 'hidden',
boxShadow: theme.shadows[1], boxShadow: theme.shadows[1],
}} }}
> >
{/* Header */} {/* Header */}
<Box <Box
sx={{ sx={{
@@ -130,7 +130,7 @@ const TableView = ({ data = [], onAddNewProduct }) => {
alignItems: { xs: 'flex-start', sm: 'center', md: 'center' }, alignItems: { xs: 'flex-start', sm: 'center', md: 'center' },
mb: 3, mb: 3,
pl: { xs: 1, sm: 3 }, pl: { xs: 1, sm: 3 },
pt:2, pt: 2,
pr: { xs: 1, sm: 3 }, pr: { xs: 1, sm: 3 },
flexDirection: { xs: 'column', sm: 'row', md: 'row' }, flexDirection: { xs: 'column', sm: 'row', md: 'row' },
gap: { xs: 2, sm: 0 } gap: { xs: 2, sm: 0 }
@@ -147,11 +147,11 @@ const TableView = ({ data = [], onAddNewProduct }) => {
width={{ xs: '100%', sm: 'auto' }} width={{ xs: '100%', sm: 'auto' }}
justifyContent={{ xs: 'space-between', sm: 'flex-end' }} justifyContent={{ xs: 'space-between', sm: 'flex-end' }}
> >
{/* زر إضافة منتج جديد */} {/* زر إضافة منتج جديد */}
<Button <Button
variant="contained" variant="contained"
color="primary" color="primary"
onClick={onAddNewProduct} onClick={onAddNewProduct}
sx={{ sx={{
textTransform: 'none', textTransform: 'none',
borderRadius: '8px', borderRadius: '8px',
@@ -162,8 +162,8 @@ const TableView = ({ data = [], onAddNewProduct }) => {
}} }}
> >
Add New Product Add New Product
</Button> </Button>
{/* زر تصدير جدول (Spreadsheet) */} {/* زر تصدير جدول (Spreadsheet) */}
<Button <Button
@@ -234,8 +234,8 @@ const TableView = ({ data = [], onAddNewProduct }) => {
{isMobile ? '' : 'Filters'} {isMobile ? '' : 'Filters'}
</Button> </Button>
</Box> </Box>
</Box> </Box>
{/* جدول البيانات */} {/* جدول البيانات */}
<TableContainer <TableContainer

عرض الملف

@@ -78,7 +78,7 @@ const Setting = () => {
<Box sx={{ <Box sx={{
flexGrow: 1, flexGrow: 1,
width: sidebarOpen ? 'calc(100% - 40px)' : '100%', width: sidebarOpen ? 'calc(100% - 40px)' : '100%',
pt: { xs:2, sm: 3 }, pt: { xs: 2, sm: 3 },
overflowY: 'auto', overflowY: 'auto',
pl: { xs: 2, sm: 3 }, pl: { xs: 2, sm: 3 },
pb: { xs: 2, sm: 4 }, pb: { xs: 2, sm: 4 },

عرض الملف

@@ -24,6 +24,8 @@ import {
Logout as LogoutIcon Logout as LogoutIcon
} from '@mui/icons-material'; } from '@mui/icons-material';
import authService from '../../services/authService'; // تأكد من المسار الصحيح
const menuItems = [ const menuItems = [
{ text: 'Dashboard', icon: <DashboardIcon />, path: '/dashboard' }, { text: 'Dashboard', icon: <DashboardIcon />, path: '/dashboard' },
{ text: 'Cashier', icon: <CashierIcon />, path: '/cashier' }, { text: 'Cashier', icon: <CashierIcon />, path: '/cashier' },
@@ -32,9 +34,8 @@ const menuItems = [
{ text: 'Training', icon: <TrainingIcon />, path: '/training' }, { text: 'Training', icon: <TrainingIcon />, path: '/training' },
{ text: 'Analytics & Reporting', icon: <AnalyticsIcon />, path: '/analytics' }, { text: 'Analytics & Reporting', icon: <AnalyticsIcon />, path: '/analytics' },
{ text: 'Restaurant Profile', icon: <RestaurantIcon />, path: '/profile' }, { text: 'Restaurant Profile', icon: <RestaurantIcon />, path: '/profile' },
{ text: 'Host Kitchen', icon: <HostKitchenIcon />, path: '/host-kitchen' }, // { text: 'Host Kitchen', icon: <HostKitchenIcon />, path: '/host-kitchen' },
{ text: 'Create Kitchen', icon: <CreateKitchenIcon />, path: '/create-kitchen' }, { text: 'Create Kitchen', icon: <CreateKitchenIcon />, path: '/create-kitchen' },
]; ];
const bottomItems = [ const bottomItems = [
@@ -47,6 +48,17 @@ const Sidebar = ({ open, onClose, isMobile, drawerWidth }) => {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const handleLogout = async () => {
const result = await authService.logout();
localStorage.removeItem('token');
localStorage.removeItem('refresh_token');
navigate('/login', { replace: true });
if (isMobile) onClose();
};
const renderListItems = (items) => const renderListItems = (items) =>
items.map((item, index) => { items.map((item, index) => {
const isActive = location.pathname === item.path; const isActive = location.pathname === item.path;
@@ -56,10 +68,11 @@ const Sidebar = ({ open, onClose, isMobile, drawerWidth }) => {
<Box <Box
onClick={() => { onClick={() => {
if (item.text === 'Log Out') { if (item.text === 'Log Out') {
localStorage.removeItem('token'); handleLogout();
} else {
navigate(item.path);
if (isMobile) onClose();
} }
navigate(item.path);
if (isMobile) onClose();
}} }}
sx={{ sx={{
display: 'flex', display: 'flex',
@@ -76,10 +89,12 @@ const Sidebar = ({ open, onClose, isMobile, drawerWidth }) => {
}, },
}} }}
> >
<ListItemIcon sx={{ <ListItemIcon
color: isActive ? theme.palette.primary.main : '#A6ACB8', sx={{
minWidth: 36 color: isActive ? theme.palette.primary.main : '#A6ACB8',
}}> minWidth: 36,
}}
>
{item.icon} {item.icon}
</ListItemIcon> </ListItemIcon>
<ListItemText <ListItemText
@@ -87,7 +102,7 @@ const Sidebar = ({ open, onClose, isMobile, drawerWidth }) => {
primaryTypographyProps={{ primaryTypographyProps={{
sx: { sx: {
color: isActive ? theme.palette.primary.main : '#61677F', color: isActive ? theme.palette.primary.main : '#61677F',
fontSize: '0.875rem' fontSize: '0.875rem',
}, },
}} }}
/> />
@@ -97,12 +112,14 @@ const Sidebar = ({ open, onClose, isMobile, drawerWidth }) => {
}); });
const drawer = ( const drawer = (
<Box sx={{ <Box
height: '100%', sx={{
display: 'flex', height: '100%',
flexDirection: 'column', display: 'flex',
bgcolor: 'background.paper' flexDirection: 'column',
}}> bgcolor: 'background.paper',
}}
>
<Box <Box
sx={{ sx={{
px: 2, px: 2,
@@ -111,7 +128,7 @@ const Sidebar = ({ open, onClose, isMobile, drawerWidth }) => {
position: 'sticky', position: 'sticky',
top: 0, top: 0,
zIndex: 1, zIndex: 1,
bgcolor: 'background.paper' bgcolor: 'background.paper',
}} }}
> >
<Box display="flex" alignItems="center"> <Box display="flex" alignItems="center">
@@ -152,22 +169,26 @@ const Sidebar = ({ open, onClose, isMobile, drawerWidth }) => {
</Box> </Box>
</Box> </Box>
<Box sx={{ <Box
flex: 1, sx={{
overflowY: 'auto', flex: 1,
scrollbarWidth: 'none', overflowY: 'auto',
'&::-webkit-scrollbar': { display: 'none' }, scrollbarWidth: 'none',
py: 1 '&::-webkit-scrollbar': { display: 'none' },
}}> py: 1,
}}
>
<List>{renderListItems(menuItems)}</List> <List>{renderListItems(menuItems)}</List>
</Box> </Box>
<Box sx={{ <Box
position: 'sticky', sx={{
bottom: 0, position: 'sticky',
bgcolor: 'background.paper', bottom: 0,
borderColor: 'divider' bgcolor: 'background.paper',
}}> borderColor: 'divider',
}}
>
<List>{renderListItems(bottomItems)}</List> <List>{renderListItems(bottomItems)}</List>
</Box> </Box>
</Box> </Box>
@@ -192,16 +213,14 @@ const Sidebar = ({ open, onClose, isMobile, drawerWidth }) => {
width: drawerWidth, width: drawerWidth,
boxSizing: 'border-box', boxSizing: 'border-box',
borderRight: 'none', borderRight: 'none',
boxShadow: 'none' boxShadow: 'none',
}, },
}} }}
> >
{drawer} {drawer}
</Drawer> </Drawer>
</Box> </Box>
); );
}; };
export default Sidebar; export default Sidebar;

عرض الملف

@@ -0,0 +1,15 @@
import React from 'react';
import { Navigate } from 'react-router-dom';
const PrivateRoute = ({ children }) => {
const token = localStorage.getItem('token');
if (!token) {
// لو مافي توكن، نعيد التوجيه للصفحة تسجيل الدخول
return <Navigate to="/login" replace />;
}
return children;
};
export default PrivateRoute;

عرض الملف

@@ -0,0 +1,15 @@
import React from 'react';
import { Navigate } from 'react-router-dom';
const PublicRoute = ({ children }) => {
const token = localStorage.getItem('token');
if (token) {
// لو فيه توكن، نعيد التوجيه للداشبورد
return <Navigate to="/dashboard" replace />;
}
return children;
};
export default PublicRoute;

101
src/services/authService.js Normal file
عرض الملف

@@ -0,0 +1,101 @@
import axios from 'axios';
const API_BASE_URL = 'http://127.0.0.1:8000/api';
const authService = {
register: async (email, password, confirmPassword) => {
try {
const response = await axios.post(`${API_BASE_URL}/Admin/register`, {
email,
password,
password_confirmation: confirmPassword
}, {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
return response.data;
} catch (error) {
// في حال كان الرد يحتوي على رسائل خطأ من الباك
if (error.response) {
return {
success: false,
status: error.response.status,
message: error.response.data.message || 'Registration failed',
errors: error.response.data.errors || null
};
}
// في حال كانت المشكلة من الشبكة أو من axios نفسه
return {
success: false,
message: 'Network error. Please try again.'
};
}
},
login: async (email, password) => {
try {
const response = await axios.post(`${API_BASE_URL}/Admin/login`, {
email,
password
}, {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
return response.data;
} catch (error) {
if (error.response) {
return {
success: false,
status: error.response.status,
message: error.response.data.message || 'Login failed',
errors: error.response.data.errors || null
};
}
return {
success: false,
message: 'Network error. Please try again.'
};
}
},
logout: async () => {
try {
const token = localStorage.getItem('token');
if (!token) {
return { success: false, message: 'No token found' };
}
const response = await axios.post(`${API_BASE_URL}/Admin/logout`, {}, {
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'application/json'
}
});
return response.data;
} catch (error) {
if (error.response) {
return {
success: false,
status: error.response.status,
message: error.response.data.message || 'Logout failed',
};
}
return {
success: false,
message: 'Network error. Please try again.'
};
}
},
};
export default authService;