Linking of login, logout and registration interfaces.
ثنائية
public/images/waiterss2.png
|
قبل العرض: | الارتفاع: | الحجم: 44 KiB |
ثنائية
public/images/waiterss4.png
|
قبل العرض: | الارتفاع: | الحجم: 45 KiB |
ثنائية
public/images/waitress.png
|
قبل العرض: | الارتفاع: | الحجم: 1.2 MiB |
ثنائية
public/images/waitress1.png
Normal file
|
بعد العرض: | الارتفاع: | الحجم: 124 KiB |
ثنائية
public/images/waitress2.png
Normal file
|
بعد العرض: | الارتفاع: | الحجم: 164 KiB |
ثنائية
public/images/waitress3.png
|
قبل العرض: | الارتفاع: | الحجم: 47 KiB بعد العرض: | الارتفاع: | الحجم: 135 KiB |
ثنائية
public/images/waitress4.png
Normal file
|
بعد العرض: | الارتفاع: | الحجم: 122 KiB |
111
src/App.js
@@ -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;
|
||||||
|
|||||||
15
src/components/Routes/PrivateRoute.js
Normal file
@@ -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;
|
||||||
15
src/components/Routes/PublicRoute.js
Normal file
@@ -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;
|
||||||