diff --git a/public/images/waiterss2.png b/public/images/waiterss2.png deleted file mode 100644 index 6273f68..0000000 Binary files a/public/images/waiterss2.png and /dev/null differ diff --git a/public/images/waiterss4.png b/public/images/waiterss4.png deleted file mode 100644 index 50848f0..0000000 Binary files a/public/images/waiterss4.png and /dev/null differ diff --git a/public/images/waitress.png b/public/images/waitress.png deleted file mode 100644 index c04a1d5..0000000 Binary files a/public/images/waitress.png and /dev/null differ diff --git a/public/images/waitress1.png b/public/images/waitress1.png new file mode 100644 index 0000000..1ecbbea Binary files /dev/null and b/public/images/waitress1.png differ diff --git a/public/images/waitress2.png b/public/images/waitress2.png new file mode 100644 index 0000000..e1a9fca Binary files /dev/null and b/public/images/waitress2.png differ diff --git a/public/images/waitress3.png b/public/images/waitress3.png index 44d630f..884e963 100644 Binary files a/public/images/waitress3.png and b/public/images/waitress3.png differ diff --git a/public/images/waitress4.png b/public/images/waitress4.png new file mode 100644 index 0000000..5e6faf9 Binary files /dev/null and b/public/images/waitress4.png differ diff --git a/src/App.js b/src/App.js index e17ce8d..486d96e 100644 --- a/src/App.js +++ b/src/App.js @@ -21,32 +21,109 @@ import Training from './components/Home/Training/Training'; import RestaurantProfile from './components/Home/RestaurantProfile/RestaurantProfile'; import HostKitchen from './components/Home/HostKitchen/HostKitchen'; 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() { return ( - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - + {/* الصفحات العامة */} + } /> + + } /> + } /> + } /> + } /> + + {/* الصفحات المحمية */} + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> - ); } + export default App; diff --git a/src/components/Authentication/SignUp_In/LoginForm.js b/src/components/Authentication/SignUp_In/LoginForm.js index a372f76..a1be1c3 100644 --- a/src/components/Authentication/SignUp_In/LoginForm.js +++ b/src/components/Authentication/SignUp_In/LoginForm.js @@ -3,27 +3,47 @@ import { TextField, Button, Typography, Stack, Box, IconButton, InputAdornment } import { useTheme } from '@mui/material/styles'; import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined'; import { VisibilityOutlined } from '@mui/icons-material'; -import SidePanel from './SidePanel'; // استدعاء SidePanel من نفس المجلد أو حسب المسار المناسب -import { useNavigate } from 'react-router-dom'; -import { Link } from 'react-router-dom'; - +import SidePanel from './SidePanel'; +import { useNavigate, Link } from 'react-router-dom'; +import authService from '../../../services/authService'; const LoginForm = () => { - const navigate = useNavigate(); const theme = useTheme(); + const [showPassword, setShowPassword] = useState(false); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); const handleTogglePassword = () => { - setShowPassword((prev) => !prev); + setShowPassword(prev => !prev); }; - const handleLogin = () => { - // بعد تنفيذ عملية تسجيل الدخول بنجاح، يتم التوجيه - navigate('/dashboard'); + + const handleLogin = async () => { + 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 ( - - { }, }} > - {/* SidePanel ثابت */} - - {/* Login Content */} { > - {/* Logo */} + {/* الشعار */} { /> - + {/* عنوان تسجيل الدخول */} + Login - + Enter your username and password to access your account securely. Welcome back to our service! - {/* Email Input */} + {/* إدخال البريد */} Email @@ -123,15 +126,17 @@ const LoginForm = () => { type="email" variant="outlined" fullWidth + value={email} + onChange={e => setEmail(e.target.value)} sx={{ '& input': { fontWeight: 500, fontSize: '15px' }, '& input::placeholder': { color: '#969BA7' }, '& .MuiOutlinedInput-root': { borderRadius: '10px', transition: '0.3s', - '&.Mui-focused fieldset': { // أضف هذا الجزء لتغيير لون الحدود عند التركيز + '&.Mui-focused fieldset': { 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': { @@ -142,7 +147,7 @@ const LoginForm = () => { /> - {/* Password Input */} + {/* إدخال كلمة السر */} Password @@ -153,15 +158,17 @@ const LoginForm = () => { fullWidth variant="outlined" autoComplete="new-password" + value={password} + onChange={e => setPassword(e.target.value)} sx={{ '& input': { fontWeight: 500, fontSize: '15px' }, '& input::placeholder': { color: '#969BA7' }, '& .MuiOutlinedInput-root': { borderRadius: '10px', transition: '0.3s', - '&.Mui-focused fieldset': { // أضف هذا الجزء لتغيير لون الحدود عند التركيز + '&.Mui-focused fieldset': { 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': { @@ -184,14 +191,22 @@ const LoginForm = () => { /> - {/* Login Button */} + {/* عرض الخطأ */} + {error && ( + + {error} + + )} + + {/* زر تسجيل الدخول */} diff --git a/src/components/Authentication/SignUp_In/RegisterForm.js b/src/components/Authentication/SignUp_In/RegisterForm.js index 5b9bade..ed391cf 100644 --- a/src/components/Authentication/SignUp_In/RegisterForm.js +++ b/src/components/Authentication/SignUp_In/RegisterForm.js @@ -4,12 +4,22 @@ import { useTheme } from '@mui/material/styles'; import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined'; import { VisibilityOutlined } from '@mui/icons-material'; 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 theme = useTheme(); + const navigate = useNavigate(); const [showPassword, setShowPassword] = 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 = () => { setShowPassword((prev) => !prev); }; @@ -18,6 +28,36 @@ const Register = () => { 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 ( { display: 'none', }, }}> - {/* SidePanel ثابت */} - - {/* Login Content */} - + { }} > - - {/* Logo */} { create your account successfully. - {/* Email Input */} { type="email" variant="outlined" fullWidth + value={email} + onChange={(e) => setEmail(e.target.value)} sx={{ '& input': { fontWeight: 500, @@ -133,9 +167,9 @@ const Register = () => { '& .MuiOutlinedInput-root': { borderRadius: '10px', transition: '0.3s', - '&.Mui-focused fieldset': { // أضف هذا الجزء لتغيير لون الحدود عند التركيز + '&.Mui-focused fieldset': { 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': { @@ -146,7 +180,6 @@ const Register = () => { /> - {/* Password Input */} { fullWidth variant="outlined" autoComplete="new-password" + value={password} + onChange={(e) => setPassword(e.target.value)} sx={{ '& input': { fontWeight: 500, @@ -175,9 +210,9 @@ const Register = () => { '& .MuiOutlinedInput-root': { borderRadius: '10px', transition: '0.3s', - '&.Mui-focused fieldset': { // أضف هذا الجزء لتغيير لون الحدود عند التركيز + '&.Mui-focused fieldset': { 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': { @@ -200,7 +235,6 @@ const Register = () => { /> - {/* Confirm Password Input */} { fullWidth variant="outlined" autoComplete="new-password" + value={confirmPassword} + onChange={(e) => setConfirmPassword(e.target.value)} sx={{ '& input': { fontWeight: 500, @@ -229,9 +265,9 @@ const Register = () => { '& .MuiOutlinedInput-root': { borderRadius: '10px', transition: '0.3s', - '&.Mui-focused fieldset': { // أضف هذا الجزء لتغيير لون الحدود عند التركيز + '&.Mui-focused fieldset': { 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': { @@ -254,21 +290,28 @@ const Register = () => { /> - {/* Login Button */} + {/* Error & Success Message */} + {error && ( + + {error} + + )} + {successMessage && ( + + {successMessage} + + )} + @@ -298,7 +341,7 @@ const Register = () => { - {/* Google Button */} + {/* Google */} - {/* Facebook Button */} + {/* Facebook */} - {/* login Link */} - { - {/* Terms */} { pt: 5 }} > - By log in, I agree to the and {' '} + By log in, I agree to the and{' '} Terms of Service {' '} @@ -406,4 +446,4 @@ const Register = () => { ); }; -export default Register; \ No newline at end of file +export default Register; diff --git a/src/components/Authentication/SignUp_In/SidePanel.js b/src/components/Authentication/SignUp_In/SidePanel.js index 2e78203..f847db0 100644 --- a/src/components/Authentication/SignUp_In/SidePanel.js +++ b/src/components/Authentication/SignUp_In/SidePanel.js @@ -15,12 +15,12 @@ const SidePanel = ({ setMode, mode }) => { const slides = [ { - image: '/images/waitress3.png', + image: '/images/waitress1.png', 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." }, { - image: '/images/waitress3.png', + image: '/images/waitress2.png', title: "Second Slide Title", 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." }, { - image: '/images/waitress3.png', + image: '/images/waitress4.png', title: "Fourth Slide Title", description: "Welcome to our cutting-edge postal application, where sending and receiving mail has never been more convenient and efficient." } diff --git a/src/components/Home/Analytics&Reporting/AnalyticsContect.js b/src/components/Home/Analytics&Reporting/AnalyticsContect.js index 8415cee..8065230 100644 --- a/src/components/Home/Analytics&Reporting/AnalyticsContect.js +++ b/src/components/Home/Analytics&Reporting/AnalyticsContect.js @@ -22,25 +22,25 @@ const AnalyticsPage = () => { const [isLoading, setIsLoading] = useState(true); const [sidebarOpen, setSidebarOpen] = useState(!isMobile); -const dailyData = [ - { date: '10:00', visitors: 1500, conversions: 300 }, - { date: '11:00', visitors: 1800, conversions: 320 }, -]; + const dailyData = [ + { date: '10:00', visitors: 1500, conversions: 300 }, + { date: '11:00', visitors: 1800, conversions: 320 }, + ]; -const weeklyData = [ - { label: 'Week 1', visitors: 1500, conversions: 200 }, - { label: 'Week 2', visitors: 2300, conversions: 400 }, -]; + const weeklyData = [ + { label: 'Week 1', visitors: 1500, conversions: 200 }, + { label: 'Week 2', visitors: 2300, conversions: 400 }, + ]; -const monthlyData = [ - { label: 'Jan', visitors: 15000, conversions: 3000 }, - { label: 'Feb', visitors: 18000, conversions: 4000 }, -]; + const monthlyData = [ + { label: 'Jan', visitors: 15000, conversions: 3000 }, + { label: 'Feb', visitors: 18000, conversions: 4000 }, + ]; -const yearlyData = [ - { label: '2024', visitors: 25000, conversions: 7000 }, - { label: '2025', visitors: 38000, conversions: 12000 }, -]; + const yearlyData = [ + { label: '2024', visitors: 25000, conversions: 7000 }, + { label: '2025', visitors: 38000, conversions: 12000 }, + ]; const getData = () => { @@ -94,11 +94,11 @@ const yearlyData = [ }, [theme.breakpoints.values.md]); return ( - + {/* Header Buttons */} { +const Budget = ({ currentStepIndex = 0, onNext, onBack }) => { const theme = useTheme(); const navigate = useNavigate(); @@ -49,7 +49,7 @@ const Budget= ({ currentStepIndex = 0, onNext, onBack }) => { {/* Expansion Plans Through Cloud Kitchen (No of Branches) Input */} - + {/* Next Button */} diff --git a/src/components/Home/Inventory/TableView.js b/src/components/Home/Inventory/TableView.js index 7d73164..1c85468 100644 --- a/src/components/Home/Inventory/TableView.js +++ b/src/components/Home/Inventory/TableView.js @@ -57,7 +57,7 @@ const TableView = ({ data = [], onAddNewProduct }) => { setCurrentPage(1); // العودة للصفحة الأولى عند تغيير البحث }; - const handleExportPDF = () => { + const handleExportPDF = () => { const doc = new jsPDF(); doc.setFontSize(14); doc.text('Top Selling Products', 14, 20); @@ -68,7 +68,7 @@ const TableView = ({ data = [], onAddNewProduct }) => { row.product, row.sales, `$${row.amount}`, - `$${row.price}`, + `$${row.price}`, row.expiration, row.status ])); @@ -92,7 +92,7 @@ const TableView = ({ data = [], onAddNewProduct }) => { row.product, row.sales, `$${row.amount}`, - `$${row.price}`, + `$${row.price}`, row.expiration, row.status ]) @@ -120,8 +120,8 @@ const TableView = ({ data = [], onAddNewProduct }) => { overflow: 'hidden', boxShadow: theme.shadows[1], }} - > - {/* Header */} + > + {/* Header */} { alignItems: { xs: 'flex-start', sm: 'center', md: 'center' }, mb: 3, pl: { xs: 1, sm: 3 }, - pt:2, + pt: 2, pr: { xs: 1, sm: 3 }, flexDirection: { xs: 'column', sm: 'row', md: 'row' }, gap: { xs: 2, sm: 0 } @@ -147,11 +147,11 @@ const TableView = ({ data = [], onAddNewProduct }) => { width={{ xs: '100%', sm: 'auto' }} justifyContent={{ xs: 'space-between', sm: 'flex-end' }} > - {/* زر إضافة منتج جديد */} + {/* زر إضافة منتج جديد */} {/* زر تصدير جدول (Spreadsheet) */} - - + + {/* جدول البيانات */} { , path: '/dashboard' }, { text: 'Cashier', icon: , path: '/cashier' }, @@ -32,9 +34,8 @@ const menuItems = [ { text: 'Training', icon: , path: '/training' }, { text: 'Analytics & Reporting', icon: , path: '/analytics' }, { text: 'Restaurant Profile', icon: , path: '/profile' }, - { text: 'Host Kitchen', icon: , path: '/host-kitchen' }, + // { text: 'Host Kitchen', icon: , path: '/host-kitchen' }, { text: 'Create Kitchen', icon: , path: '/create-kitchen' }, - ]; const bottomItems = [ @@ -47,6 +48,17 @@ const Sidebar = ({ open, onClose, isMobile, drawerWidth }) => { const navigate = useNavigate(); 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) => items.map((item, index) => { const isActive = location.pathname === item.path; @@ -56,10 +68,11 @@ const Sidebar = ({ open, onClose, isMobile, drawerWidth }) => { { if (item.text === 'Log Out') { - localStorage.removeItem('token'); + handleLogout(); + } else { + navigate(item.path); + if (isMobile) onClose(); } - navigate(item.path); - if (isMobile) onClose(); }} sx={{ display: 'flex', @@ -76,10 +89,12 @@ const Sidebar = ({ open, onClose, isMobile, drawerWidth }) => { }, }} > - + {item.icon} { primaryTypographyProps={{ sx: { 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 = ( - + { position: 'sticky', top: 0, zIndex: 1, - bgcolor: 'background.paper' + bgcolor: 'background.paper', }} > @@ -152,22 +169,26 @@ const Sidebar = ({ open, onClose, isMobile, drawerWidth }) => { - + {renderListItems(menuItems)} - + {renderListItems(bottomItems)} @@ -192,16 +213,14 @@ const Sidebar = ({ open, onClose, isMobile, drawerWidth }) => { width: drawerWidth, boxSizing: 'border-box', borderRight: 'none', - boxShadow: 'none' + boxShadow: 'none', }, }} > {drawer} - ); - }; export default Sidebar; diff --git a/src/components/Routes/PrivateRoute.js b/src/components/Routes/PrivateRoute.js new file mode 100644 index 0000000..b26d9b6 --- /dev/null +++ b/src/components/Routes/PrivateRoute.js @@ -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 ; + } + + return children; +}; + +export default PrivateRoute; diff --git a/src/components/Routes/PublicRoute.js b/src/components/Routes/PublicRoute.js new file mode 100644 index 0000000..1030091 --- /dev/null +++ b/src/components/Routes/PublicRoute.js @@ -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 ; + } + + return children; +}; + +export default PublicRoute; diff --git a/src/services/authService.js b/src/services/authService.js new file mode 100644 index 0000000..a8d40a5 --- /dev/null +++ b/src/services/authService.js @@ -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;