diff --git a/wannameal/package-lock.json b/wannameal/package-lock.json index a2c3491..e9c9bf7 100644 --- a/wannameal/package-lock.json +++ b/wannameal/package-lock.json @@ -22,6 +22,7 @@ "jwt-decode": "^4.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-helmet": "^6.1.0", "react-i18next": "^14.1.2", "react-icons": "^5.1.0", "react-input-emoji": "^5.9.0", @@ -15582,6 +15583,25 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" + }, + "node_modules/react-helmet": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", + "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", + "dependencies": { + "object-assign": "^4.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.1.1", + "react-side-effect": "^2.1.0" + }, + "peerDependencies": { + "react": ">=16.3.0" + } + }, "node_modules/react-i18next": { "version": "14.1.2", "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-14.1.2.tgz", @@ -15771,6 +15791,14 @@ } } }, + "node_modules/react-side-effect": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz", + "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==", + "peerDependencies": { + "react": "^16.3.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-smooth": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz", diff --git a/wannameal/package.json b/wannameal/package.json index 477c39f..fbbcc9e 100644 --- a/wannameal/package.json +++ b/wannameal/package.json @@ -17,6 +17,7 @@ "jwt-decode": "^4.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-helmet": "^6.1.0", "react-i18next": "^14.1.2", "react-icons": "^5.1.0", "react-input-emoji": "^5.9.0", diff --git a/wannameal/public/index.html b/wannameal/public/index.html index fa9ffd9..d860711 100644 --- a/wannameal/public/index.html +++ b/wannameal/public/index.html @@ -24,7 +24,7 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - React App + WannaMeal diff --git a/wannameal/public/locales/ar/translation.json b/wannameal/public/locales/ar/translation.json index 60f9928..8b56d5f 100644 --- a/wannameal/public/locales/ar/translation.json +++ b/wannameal/public/locales/ar/translation.json @@ -1,8 +1,167 @@ { - "Where!?!?!?!": "صفحة الاولية", - "heading": "الصحة تتطلب اكل صحى", - "mini": "تحكم فى وجباتك باسهل طريقة", - "intro_p1": "شارك وجباتك مع اصدقائك واستكشف الجديد", - "intro_p2": "تعرف على أجزاء الوجبة ومما تتكون والمكونات التي يحتوي عليها كل جزيء.", - "mainBtn": "اصنع وجبة" + "Where!?!?!?!": "الصفحة الرئيسية", + "heading": "الصحة تتطلب طعامًا صحيًا", + "mini": "إدارة وصفاتك بالطريقة السهلة", + "intro_p1": "شارك الوصفات مع أصدقائك واكتشف وصفات جديدة", + "intro_p2": "اعرف أجزاء الوجبة ومكوناتها وما تحتويه كل جزيئة.", + "mainBtn": "قم بإعداد وجبة", + "profile": { + "name": "{{fullname}}", + "Nickname": "{{nickname}}", + "edit": "تعديل الملف الشخصي", + "share": "مشاركة الملف الشخصي", + "followers": "المتابعون", + "following": "اتابع", + "savedRE": "الوصفات المحفوظة", + "MyRE": "وصفاتي", + "WannaMeaL": "WannaMeaL", + "premium": "مدفوع", + "Country": "الدولة", + "language": "اللغة", + "Invite": "ادعو صديقك", + "contactus": "تواصل معنا", + "log": "تسجيل خروج", + "Cancel": "الغاء" + }, + "editprofile": { + "setting": "الإعدادات الشخصية", + "Profile": "الملف الشخصي", + "info": "سيتم عرض هذه المعلومات بشكل عام، لذا كن حذرًا فيما تشاركه.", + "fname": "الاسم الأول", + "lname": "الاسم الأخير", + "Email": "البريد الإلكتروني", + "image": "صورة الملف الشخصي", + "change": "تغيير الصورة", + "delte": "حذف", + "changePass": "تغيير كلمة المرور", + "info2": "سيتم استخدام هذا لتسجيل الدخول إلى حسابك وإكمال الإجراءات ذات الحساسية العالية.", + "CurrentPass": "كلمة المرور الحالية", + "NewPass": "كلمة المرور الجديدة", + "ConfirmPass": "تأكيد كلمة المرور", + "savedCH": "تم حفظ التغييرات", + "cancel": "إلغاء التغييرات", + "deltePers": "حذف الحساب الشخصي", + "info3": "لم تعد بحاجة إلى WeCraft؟ نحن نفهم ذلك ونقدر استخدامك لخدمتنا!", + "delteAcc": "حذف الحساب؟" + }, + "add": { + "Rname": "اسم الوصفة", + "Special": "ما يميز هذه الوصفة", + "share": "شارك معنا صورة الوصفة", + "breaks": "فطور", + "lunch": "غداء", + "dinner": "عشاء", + "enough": "يكفي لـ", + "time": "وقت التحضير", + "Ingredients": "المكونات", + "Ingredient": "مكون", + "Directions": "التوجيهات", + "Direction": "توجيه", + "addingredient": "أضف مكون", + "addStep": "أضف خطوة", + "saveRecipe": "احفظ الوصفة", + "cancelRecipe": "ألغِ الوصفة", + "caloriess": "السعرات" + }, + "contact": { + "head": "تواصل معنا", + "fname": "اسمك", + "Email": "بريدك الإلكتروني", + "msg": "أدخل رسالتك", + "send": "إرسال", + "location": "جامعة قناة السويس", + "phone": "010-11-0101010-14-52", + "mail": "Teem-ala-alaah@gmail.com" + }, + "View": { + "apply": "تكفى حتى ", + "minutes": "دقيقة", + "components": "مكونات", + "people": "أشخاص", + "Ingredients": "المكونات", + "Directions": "التوجيهات", + "Latest": "أحدث الوصفات" + }, + "make": { + "make": "اصنع", + "meal": "وجبتك الخاصة", + "Choose": "اختر المكونات", + "searchh": "ابحث عن المكونات" + }, + "community": { + "Fname": "{{fullname}}", + "email": "{{email}}", + "edit": "تعديل الملف الشخصي", + "mine": "ملفي الشخصي", + "recommend": "موصى به لك", + "Norecommend": "لا يوجد مستخدمون موصى بهم لك!", + "more": "عرض المزيد", + "followers": "اتابعهم", + "following": "المتابعون", + "follow": "متابعة", + "photo": "صورة", + "video": "فيديو", + "share": "شارك ما تريد", + "who": "من تتابع", + "msg": "محادثة", + "comment": "تعليق", + "sharee": "مشاركة", + "like": "اعجاب" + }, + "about": { + "about": "تعرف", + "us": "علينا", + "Healthy": "خيارات صحية ومتوازنة", + "HealthyP": "تُعد وجباتنا من قبل أخصائيي التغذية والطهاة لضمان توازن مثالي بين الطعم والتغذية، مما يساعدك على الحفاظ على نمط حياة صحي بسهولة.", + "Convenience": "خيارات صحية ومتوازنة", + "ConvenienceP": "استمتع براحة الحصول على وجبات فاخرة مُعدة من قبل الطهاة", + "Save": "وفر الوقت وتناول الطعام الجيد", + "SaveP": "وفر وقتك في التفكير فيما ستعده للطعام. أدخل ما تمتلكه وسنقدم لك أفضل الخيارات الصحية", + "Customizable": "خطط وجبات قابلة للتخصيص", + "CustomizableP": "خطط وجبات قابلة للتخصيص. مع مجموعة متنوعة من خطط الوجبات المصممة لتلبية الاحتياجات الغذائية المختلفة والتفضيلات، يمكنك بسهولة العثور على الخطة المثالية التي تناسب نمط حياتك.", + "Expertly": "وصفات معدة بخبرة", + "ExpertlyP": "يستخدم طهاتنا المحترفون خبراتهم في الطهي لإنشاء قائمة متنوعة من الوجبات ذات النكهات التي ستحافظ على حماسك لتذوقها", + "Community": "مركزة على المجتمع", + "CommunityP": "لدينا مجتمع للمستخدمين لمشاركة الوجبات التي يعدونها بأنفسهم ويمكنهم الإعجاب بمنشورات الآخرين والدردشة مع بعضهم البعض" + }, + "auth": { + "Login": "تسجيل الدخول", + "email": "أدخل بريدك الإلكتروني", + "Password": "أدخل كلمة المرور", + "Remember": "تذكرني", + "Forgot": "نسيت كلمة المرور؟", + "Google": "جوجل", + "have": "ليس لديك حساب؟", + "haves": "هل لديك حساب؟", + "Sign": "سجل الآن", + "With": "أو بواسطة", + "Name": "الاسم", + "characters": "على الاقل 8 حروف", + "number": "على الاقل رقم", + "letters": "يتضمن كل من حروف الكبيرة و الصغيرة ", + "Create": "إنشاء حساب", + "What": "ماذا ستحصل؟", + "Manage": "إدارة وصفاتك بسهولة", + "Share": "شارك الوصفات مع أصدقائك واكتشف وصفات جديدة.", + "Organize": "نظم الوصفات بواسطة العلامات وشاركها مع أصدقائك", + "Know": "اعرف أجزاء الوجبة ومكوناتها والمكونات التي يحتوي عليها كل جزء.", + "ForgotP": "ادخل بريدك الاكتونى وسنرسل اليك كود لاعادة انشاء كلمة السر ", + "Send": "ارسل الكود", + "CreatePas": "انشاء كلمة سر جديدة", + "shuold": "كلمة السر يجب ان تكون مختلفة عن السابقة", + "Reset": "اعادة انشاء كلمة السر", + "check": "رجاء راجع بريدك الالكترونى", + "Verification": "تاكيد", + "sended": "لقد ارسلنا الكود الى بريدك" + }, + "canvas": { + "Home": "الرئيسية", + "Community": "المجتمع", + "Make": "اصنع وجبة", + "Profile": "الملف الشخصى", + "Contact": "تواصل معنا", + "logoutt": "تسجيل الخروج", + "dashboard": "لوحة التحكم", + "makeMeal": " اضافة وجبه" + } } diff --git a/wannameal/public/locales/en/translation.json b/wannameal/public/locales/en/translation.json index 9827dea..f6f5a23 100644 --- a/wannameal/public/locales/en/translation.json +++ b/wannameal/public/locales/en/translation.json @@ -4,5 +4,164 @@ "mini": "Manage your recipes the easy way", "intro_p1": "Share Recipes With Your Friends And Discover New Ones", "intro_p2": "Know The Parts Of The Meal And What It Consists Of And The Components That Each Molecule Contains.", - "mainBtn": "Make a Meal" + "mainBtn": "Make a Meal", + "profile": { + "name": "{{fullname}}", + "Nickname": "{{NickName}}", + "edit": "edit profile", + "share": "share profile", + "followers": "followers", + "following": "following", + "savedRE": " saved recipes", + "MyRE": " My recipes", + "WannaMeaL": "WannaMeaL", + "premium": "premium", + "Country": "Country", + "language": "language", + "Invite": "Invite your friends to WannaMeaL", + "contactus": "contact us", + "log": "log out", + "Cancel": "Cancel" + }, + "editprofile": { + "setting": "Personal Setting", + "Profile": "Profile", + "info": "This information will be displayed publicly so be careful what you share.", + "fname": "First Name", + "lname": "last Name", + "Email": "Email", + "image": "Profile Picture", + "change": "change Picture", + "delte": "delte", + "changePass": "Change Password", + "info2": "This will be used to log into your account and complete high severity actions.", + "CurrentPass": "Current Password", + "NewPass": "New Password", + "ConfirmPass": "Current Password", + "savedCH": "saved Changes", + "cancel": "cancel Changes", + "deltePers": "Delete Personal Account", + "info3": "Don't need wecraft any longer? We understand, and appreciate you using our service!", + "delteAcc": "Delete Account?" + }, + "add": { + "Rname": "Recipe Name", + "Special": "What distinguishes this recipe", + "share": "Share with us the picture of your dish", + "breaks": "Breakfast", + "lunch": "Lunch", + "dinner": "Dinner", + "enough": "enough for", + "time": "Preparation Time", + "Ingredients": "Ingredients", + "Ingredient": "Ingredient", + "Directions": "Directions", + "Direction": "Direction", + "addingredient": "add ingredient", + "addStep": "add Step", + "saveRecipe": "save Recipe", + "cancelRecipe": "cancel Recipe", + "caloriess": "calories" + }, + "contact": { + "head": "get in touch", + "fname": "Your Name", + "Email": "Your Email", + "msg": "enter your message", + "send": "send", + "location": "Suez Canal University", + "phone": " 010-11-0101010-14-52", + "mail": "Teem-ala-alaah@gmail.com" + }, + "View": { + "apply": "apply for", + "minutes": "minutes", + "components": "components", + "people": "people", + "Ingredients": "Ingredients", + "Directions": "Directions", + "Latest": "Latest Recipes" + }, + "make": { + "make": "Make", + "meal": "Own Meal", + "Choose": "Choose Your Ingredients", + "searchh": "search for Your Ingredients" + }, + "community": { + "Fname": "{{fullname}}", + "email": "{{email}}", + "edit": "edit profile", + "mine": "my profile", + "recommend": "recommended for you", + "Norecommend": "No recommended users for you!", + "more": "Show more", + "followers": "followers", + "following": "following", + "follow": "follow", + "photo": "photo", + "video": "video", + "share": "Share what you want", + "who": "who are you following", + "msg": "message", + "comment": "comment", + "sharee": "share", + "like": "like" + }, + "about": { + "about": "about", + "us": "us", + "Healthy": "Healthy and Balanced Options", + "HealthyP": "Our meals are crafted by nutritionists and chefs to ensure a perfect balance of taste and nutrition, helping you maintain a healthy lifestyle effortlessly.", + "Convenience": "Healthy and Balanced Options", + "ConvenienceP": "Enjoy the convenience of having gourmet, chef-prepared meals", + "Save": "Save time and eat well", + "SaveP": "Save your time thinking about what to make for food Enter for uswhat you own and we give the best healthy choices", + "Customizable": "CustomizableMeal Plans", + "CustomizableP": "Customizable Meal PlansWith a variety of meal plans tailored todifferent dietary needs and preferences, you can easily find theperfect plan that fits your lifestyle.", + "Expertly": "ExpertlyMeal Crafted Recipes", + "ExpertlyP": " Our professional chefs use their culinary expertise to create adiverse menu of flavorful meals that will keep your taste budsexcited", + "Community": "Community Focused", + "CommunityP": " We have a community for users to share meals they madethemselves and can like their posts and chat with each other" + }, + "auth": { + "Login": "Login", + "email": "Enter your Email", + "Password": "Enter your Password", + "Remember": "Remember Me", + "Forgot": "Forgot password?", + "Google": "Google", + "have": " Don't have an account?", + "haves": " have an account?", + "Sign": " Sign UP", + "With": "Or With", + "Name": "Name", + "characters": "At least 8 characters", + "number": "At least 1 number", + "letters": "Both upper and lower case letters", + "Create": "Create Account", + "What": "What you will get?", + "Manage": "Manage your recipes the easy way", + "Share": "Share recipes with your friends and discover new ones.", + "Organize": "Organize recipes by tag, share it with your friends", + "Know": "Know the parts of the meal and what it consists of and the components that each molecule contains.", + "ForgotP": "Enter the email address with your account and we’ll send an email with confirmation to reset your password", + "Send": "Send Code", + "CreatePas": "Create New password", + "shuold": "The password shuold be different from the previous password", + "Reset": "Reset Password", + "check": "Please check your Email", + "Verification": "Verification", + "sended": "We have sent code to Your email" + }, + "canvas": { + "Home": "Home", + "Community": "Community", + "Make": "Make Meal", + "Profile": "My Profile", + "Contact": "Contact Us", + "logoutt": "logout", + "dashboard": "dashboard", + "makeMeal": "Add Recipe" + } } diff --git a/wannameal/src/components/FoodIngreientSlider/FoodIngreientSlider.jsx b/wannameal/src/components/FoodIngreientSlider/FoodIngreientSlider.jsx index 89ff81b..90553ca 100644 --- a/wannameal/src/components/FoodIngreientSlider/FoodIngreientSlider.jsx +++ b/wannameal/src/components/FoodIngreientSlider/FoodIngreientSlider.jsx @@ -30,10 +30,13 @@ import { } from "../../redux/slices/ingredients"; import { getuser } from "../../redux/slices/authSlice"; import { getLanguage } from "../../redux/slices/language"; +import axios from "axios"; +import { useTranslation } from "react-i18next"; function FoodIngreientSlider() { // const ingredients = useSelector(getIngredients); const ingredients = useSelector(getIngredients); + console.log("🚀 ~ FoodIngreientSlider ~ ingredients:", ingredients); const [checkedIngredients, setCheckedIngredients] = useState([]); const [searchQuery, setSearchQuery] = useState(""); const [filteredIngredients, setFilteredIngredients] = useState(ingredients); @@ -43,7 +46,6 @@ function FoodIngreientSlider() { const ingredientsStatus = useSelector(getIngredientsStatus); const ingredientError = useSelector(getIngredientsError); const language = useSelector(getLanguage); - console.log("🚀 ~ FoodIngreientSlider ~ language:", language); const handleCheckboxChange = (ingredient) => { setCheckedIngredients((prevIngredients) => { @@ -63,7 +65,7 @@ function FoodIngreientSlider() { ); }; - useEffect(() => { + useEffect(() => { const fetch = async () => { await dispatch( fetchIngredients({ token: availableUser.token, lang: language }) @@ -87,22 +89,31 @@ function FoodIngreientSlider() { useEffect(() => { const fetchMeals = async () => { try { - let ingredientNames = 'موز'; // Default value if no checked ingredients - console.log("🚀 ~ fetchMeals ~ ingredientNames:", ingredientNames) + let ingredientNames = ""; // Default value if no checked ingredients + console.log("🚀 ~ fetchMeals ~ ingredientNames:", ingredientNames); if (checkedIngredients.length > 0) { ingredientNames = checkedIngredients - .map(ingredient => ingredient.name) - .join(",") + .map((ingredient) => ingredient.name) + .join(","); } - - console.log("🚀 ~ fetchMeals ~ checkedIngredients:", checkedIngredients) - console.log("🚀🚀🚀🚀🚀 ~ fetchMeals ~ ingredientNames:", ingredientNames) - - const response = await dispatch( - recommendMeals({ ingredients: ingredientNames, lang: language,token:availableUser?.token }) + + console.log( + "🚀 ~ fetchMeals ~ checkedIngredients:", + checkedIngredients ); + console.log(ingredientNames); + console.log(typeof ingredientNames); - console.log("Recommendation response:", response); // Log the response or handle it as needed + if (ingredientNames !== "") { + const response = await dispatch( + recommendMeals({ + ingredients: ingredientNames, + lang: language, + token: availableUser?.token, + }) + ); + console.log("Recommendation response:", response); // Log the response or handle it as needed + } } catch (error) { console.error("Error fetching meals:", error); } @@ -110,6 +121,8 @@ function FoodIngreientSlider() { fetchMeals(); }, [checkedIngredients, dispatch, language]); + const { t } = useTranslation(); + const { Choose, searchh } = t("make"); return ( <>
@@ -119,14 +132,14 @@ function FoodIngreientSlider() { className={`col-4 ${styles.label}`} htmlFor="ingredientSearch" > - Choose your ingredients + {Choose} setSearchQuery(e.target.value)} // autocomplete="off" @@ -201,6 +214,7 @@ function FoodIngreientSlider() { slidesPerView: 8.5, }, }} + style={{ direction: "ltr" }} > {filteredIngredients.map((ingredient, index) => ( diff --git a/wannameal/src/components/MyRecips/MyRecips.jsx b/wannameal/src/components/MyRecips/MyRecips.jsx index b085764..5da5c83 100644 --- a/wannameal/src/components/MyRecips/MyRecips.jsx +++ b/wannameal/src/components/MyRecips/MyRecips.jsx @@ -1,79 +1,86 @@ -import React from "react"; +import React, { useEffect } from "react"; import style from "./MyRecips.module.css"; import MealCard from "../mealCard/mealCard"; +import { MdSmsFailed } from "react-icons/md"; + import noMeals from "../../assets/nomeals.gif"; -export default function MyRecips() { - const meals = [ - // { - // index: 488, - // name: "النجريسكو", - // time: "غداء", - // ingredients: - // "400 صدور دجاج، مخلية ومقطعة إلى قطع صغيرة الحجم. , الملح والفلفل حسب الذوق. , 2 فص الثوم مفروم. , 80 جم زيت زيتون. , 70 جم دقيق. , 400 مل لبن كامل الدسم. , 400 جم مكرونة بينا. , 100 جم جبنة شيدر مبشورة. , 50 جم جبنة بارميزان مبشورة. ", - // prep: " اطهى المكرونة وفقا للتعليمات على الكيس، ثم صفيها وضعيها جانباً. , فى هذه الأثناء فى حلة صلصة كبيرة، اضيفى زيت الزيتون، والدجاج، الثوم، والملح والفلفل، ثم حمريها على نار متوسطة إلى منخفضة، واطهى الدجاج مع التحريك المستمر، حتى لم يعد لونه وردى اضيفى الدقيق وقلبيه حتى يمتص كل السائل ويصبح كالمعجون الذى يغطى الدجاج. , ببطء اضيفى اللبن مع التحريك المستمر، حتى يكون لديكِ صلصة كريمية خفيفة من البشاميل. , ارفعى الحلة من على النار، اضيفى الجبن الشيدر وقلبيه حتى يتم توزيعه بشكل جيد. , اضيفي المكرونة المطبوخة وقلبيها حتى يتم توزيعها جيداً، صبى الخليط كله فى صينية فرن عميقة، ثم رشى على السطح الجبن البارميزان. , اخبزيها فى الفرن على 210درجة مئوية، حتى يصبح الوجه لونه بنياً ذهبياً، ثم تقدم ساخنة. ", - // img_link: { - // url: "https://res.cloudinary.com/dz5dpvxg7/image/upload/v1718750274/WannaMeal/recommend/ocrxycya2j0ieo9suqg7.jpg", - // id: "WannaMeal/recommend/ocrxycya2j0ieo9suqg7", - // }, - // calories: 1061, - // prep_time: 30, - // no_of_ppl: 4, - // }, - // { - // index: 152, - // name: "الأرز بالزعفران", - // time: "غداء", - // ingredients: - // " 400 جم أرز بسمتي مغسول. , 810 مل مرق دجاج أو مرق خضار. , 150 جم بصل أصفر مفروم ناعمًا. , 1 ملعقة صغيرة زعفران. , 60 مل ماء مغلي. , 2 ملعقة كبيرة زيت زيتون. , 1 ملعقة صغيرة ملح. ", - // prep: " ضعي الزعفران في وعاء صغير، ثم أضيفي الماء المغلي، وقلبي جيدًا، ثم اتركيه جانبًا لمدة خمس دقائق. , سخني الزيت في قدر كبير على نار متوسطة، ثم أضيفي البصل وقلبيه لمدة ثماني إلى عشر دقائق حتى يتكرمل. , أضيفي الأرز وقلبي لمدة دقيقة، ثم أضيفي خليط الزعفران والمرق والملح، وقلبي جيدًا، واتركيه حتى يغلي. , غطي القدر، واتركيه على نار هادئة مدة 30 دقيقة حتى ينضج. , ضعي الأرز في أطباق التقديم، وقدميه ساخنًا مع الدجاج والسلطات. ", - // img_link: { - // url: "https://res.cloudinary.com/dz5dpvxg7/image/upload/v1718750275/WannaMeal/recommend/rqyhl3iqkk5fndudnjmu.jpg", - // id: "WannaMeal/recommend/rqyhl3iqkk5fndudnjmu", - // }, - // calories: 454.99, - // prep_time: 50, - // no_of_ppl: 4, - // }, - // { - // index: 154, - // name: "الكوارع", - // time: "غداء", - // ingredients: - // " 1500 مل من الماء المغلي , 1000 جم كوارع مقطعة , بصلة كبيرة مفرومة ناعمًا , بصلة صغيرة مقطعة مكعبات صغيرة , 200 جم فلفل أخضر حار مفروم , 4 ثمرات طماطم مقشرة، ومفرومة ناعمًا , 6 فصوص ثوم مهروس , 2 ملعقة كبيرة خل أبيض , 4 معلقة كبيرة زيت , 1 ملعقة كبيرة زبدة في درجة حرارة الغرفة , 1 ملعقة كبيرة صلصة طماطم , 3 ورقات لورا , عود قرفة , 5 حبات حبهان , 4 حبات قرنفل , 1 ملعقة صغيرة فلفل أسود حصى , ملح وفلفل أسود ", - // prep: " انقعي الكوارع في ماء وخل وملح وعصير ليمون لمدة ساعة. , صفي الكوارع من الماء، واغسليها جيدًا تحت الماء الجاري مرتين. , على نار متوسطة، ضعي الكوارع والملح والفلفل الأسود، ثم غطيها بالماء، واتركيها حتى تغلي. , صفي الكوارع من الماء، واتركيها جانبًا. , على نار متوسطة، ضعي الزبدة ونصف كمية الزيت، واتركيهما حتى تذوب الزبدة. , أضيفي الكوارع، وقلبيها لمدة 5 دقائق، ثم أضيفي ورق اللورا والقرفة والحبهان والقرنفل والفلفل الحصى، وقلبي لمدة دقيقة. , أضيفي البصل المفروم، وقلبي لمدة دقيقتين، ثم أضيفي الماء، واتركيه حتى تغلي. , غطي القدر، واتركي الكوارع على نار هادئة جدًّا لمدة ساعة حتى تنضج. , على نار متوسطة، سخني الزيت، ثم أضيفي مكعبات البصل الصغيرة، وقلبيها حتى تذبل ويصفر لونها. , أضيفي الثوم والفلفل الحار، وقلبي لمدة دقيقة، ثم أضيفي الطماطم المفرومة وصلصة الطماطم والملح والفلفل الأسود، وقلبي لمدة دقيقة. , اتركي الصلصة على النار حتى تغلي، ثم غطي القرد واتركيها على نار هادئة جدًّا لمدة 10 دقائق. , أضيفي الخل واتركيها لمدة دقيقة، ثم أضيفي الكوارع، وكوبًا من شوربة الكوارع، واتركيها على نار هادئة لمدة 15 دقيقة، ثم قدميها ساخنة. ", - // img_link: { - // url: "https://res.cloudinary.com/dz5dpvxg7/image/upload/v1718750275/WannaMeal/recommend/urvgyfbjd3aak0njsl6u.jpg", - // id: "WannaMeal/recommend/urvgyfbjd3aak0njsl6u", - // }, - // calories: 150, - // prep_time: 105, - // no_of_ppl: 6, - // }, - ]; +import { useDispatch, useSelector } from "react-redux"; +import { + fetchMyRecipes, + fetchSavedMeals, + getMealsError, + getMealsStatus, + getMyRecipes, + getSavedMeals, +} from "../../redux/slices/recomendedMealsSlice"; +import { getDecodedToken } from "../../redux/slices/authSlice"; +import Loading from "../loading/loading"; +import { getLanguage } from "../../redux/slices/language"; +export default function SavedMeals() { + const decodedToken = useSelector(getDecodedToken); + console.log("🚀 ~ SavedMeals ~ decodedToken:", decodedToken); + const mealsError = useSelector(getMealsError); + const mealsStatus = useSelector(getMealsStatus); + const dispatch = useDispatch(); + const language = useSelector(getLanguage); + const myRecips = useSelector(getMyRecipes); + console.log("🚀 ~ SavedMeals ~ myRecips:", myRecips); + + useEffect(() => { + if (decodedToken?.id) { + const fetchMeals = async () => { + await dispatch( + fetchMyRecipes({ userId: decodedToken?.id, lang: language }) + ); + }; + fetchMeals(); + } + }, []); + + if (mealsStatus === "loading") + return ( +
+ +
+ ); + + if (mealsError) + return ( +
+

+ {mealsError} + +

+
+ ); return ( - <> -
- {meals && meals.length > 0 ? ( - meals.map((meal) => { - return ; - }) - ) : ( -
-
- +
+
+
+ {myRecips && myRecips.length > 0 ? ( + myRecips.map((meal) => ( +
+ +
+ )) + ) : ( +
+
+ +
+

+ You haven't published any recipe yet. +

+

+ Post your first recipe and you'll find it here. +

-

- You haven't published any recipe yet. -

-

- Post your first recipe and you'll find it here. -

-
- )} + )} +
- +
); } diff --git a/wannameal/src/components/aboutUs/aboutus.jsx b/wannameal/src/components/aboutUs/aboutus.jsx index 0738183..cd8f684 100644 --- a/wannameal/src/components/aboutUs/aboutus.jsx +++ b/wannameal/src/components/aboutUs/aboutus.jsx @@ -6,8 +6,28 @@ import { GiMeal } from "react-icons/gi"; import { MdDashboardCustomize } from "react-icons/md"; import { MdMoreTime } from "react-icons/md"; import { TbHealthRecognition } from "react-icons/tb"; - +import { useTranslation } from "react-i18next"; export default function AboutUs() { + const { t, i18n } = useTranslation(); + const isAr = i18n.dir(); + console.log(isAr); + const { + about, + us, + Healthy, + HealthyP, + Convenience, + ConvenienceP, + Save, + SaveP, + Customizable, + CustomizableP, + Expertly, + ExpertlyP, + Community, + CommunityP, + } = t("about"); + return (

- About Us + {about} {us}

-
Healthy and Balanced Options
+
{Healthy}
- Our meals are crafted by nutritionists and chefs to ensure a - perfect balance of taste and nutrition, helping you maintain a - healthy lifestyle effortlessly. + {HealthyP}
@@ -43,9 +61,9 @@ export default function AboutUs() {
-
Convenience
+
{Convenience}
- Enjoy the convenience of having gourmet, chef-prepared meals + {ConvenienceP}
@@ -56,10 +74,9 @@ export default function AboutUs() {
-
Save time and eat well
+
{Save}
- Save your time thinking about what to make for food Enter for us - what you own and we give the best healthy choices{" "} + {SaveP}
@@ -70,11 +87,9 @@ export default function AboutUs() {
-
Customizable Meal Plans
+
{Customizable}
- Customizable Meal PlansWith a variety of meal plans tailored to - different dietary needs and preferences, you can easily find the - perfect plan that fits your lifestyle. + {CommunityP}
@@ -85,11 +100,9 @@ export default function AboutUs() {
-
Expertly Crafted Recipes
+
{Expertly}
- Our professional chefs use their culinary expertise to create a - diverse menu of flavorful meals that will keep your taste buds - excited + {ExpertlyP}
@@ -100,10 +113,9 @@ export default function AboutUs() {
-
Community Focused
+
{Community}
- We have a community for users to share meals they made - themselves and can like their posts and chat with each other + {CommunityP}
diff --git a/wannameal/src/components/commonMeals/commonMeals.jsx b/wannameal/src/components/commonMeals/commonMeals.jsx index 7a5cd1a..bb83257 100644 --- a/wannameal/src/components/commonMeals/commonMeals.jsx +++ b/wannameal/src/components/commonMeals/commonMeals.jsx @@ -11,6 +11,7 @@ import { } from "../../redux/slices/recomendedMealsSlice"; import { MdSmsFailed } from "react-icons/md"; import { getuser } from "../../redux/slices/authSlice"; +import { getLanguage } from "../../redux/slices/language"; export default function CommonMeals() { const commonMeals = useSelector(getCommonMeals); @@ -18,17 +19,20 @@ export default function CommonMeals() { const mealsError = useSelector(getMealsError); const mealsStatus = useSelector(getMealsStatus); const availableUser = useSelector(getuser); + const language = useSelector(getLanguage); const dispatch = useDispatch(); useEffect(() => { if (availableUser?.token) { const fetchMeals = async () => { - await dispatch(fetchCommonMeals({ token: availableUser.token })); + await dispatch( + fetchCommonMeals({ token: availableUser.token, lang: language }) + ); }; fetchMeals(); } - }, [dispatch, availableUser?.token]); + }, [dispatch, language]); if (mealsStatus === "loading") return ( diff --git a/wannameal/src/components/dashNav/page.module.css b/wannameal/src/components/dashNav/page.module.css index 9af7db3..60cd13f 100644 --- a/wannameal/src/components/dashNav/page.module.css +++ b/wannameal/src/components/dashNav/page.module.css @@ -4,7 +4,7 @@ align-items: center; position: relative; border-radius: 12px; - background: #fff; + background: var(--text_white); box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.25); } @@ -17,8 +17,8 @@ border: 2px solid transparent; border-radius: 8px; outline: none; - background-color: #fff; - color: #0d0c22; + background-color: var(--text_white); + color: var(--text_black); transition: 0.3s ease; } @@ -34,7 +34,7 @@ } .backIcon { border-radius: 8px; - background: #fff; + background: var(--text_white); box-shadow: 0px 0px 7px 2px rgba(0, 0, 0, 0.25); padding: 0.5rem; } diff --git a/wannameal/src/components/landingPage/LandingPage.jsx b/wannameal/src/components/landingPage/LandingPage.jsx index c362e2f..7a8fca7 100644 --- a/wannameal/src/components/landingPage/LandingPage.jsx +++ b/wannameal/src/components/landingPage/LandingPage.jsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { } from "react"; import rec2 from "../../assets/Rectangle 2.png"; import rec3 from "../../assets/Rectangle 3.png"; import rec4 from "../../assets/Rectangle 4.png"; @@ -11,11 +11,15 @@ import rec10 from "../../assets/Rectangle 10.png"; import { motion } from "framer-motion"; import styles from "./landing.module.css"; import { Link } from "react-router-dom"; -export default function landingPage() { +import { useTranslation } from "react-i18next"; +export default function LandingPage() { + const { t, i18n } = useTranslation() + const isAr = i18n.dir(); + console.log(isAr); return (
-
+
-

Health Requires Healthy food

-

Manage your recipes the easy way

+

{t("heading")}

+

{t("mini")}

-

share recipes with your friends and discover new ones

-

- Know the parts of the meal and what it consists of and the - components that each molecule contains. -

+

{t('intro_p1')}

+

{t('intro_p2')}

- Make a meal + {t('mainBtn')}
-
+
{ + i18n.changeLanguage(lng); + if (lng === 'en') { + dispatch(switchToEnglish()); + } else if (lng === 'ar') { + dispatch(switchToArabic()); + } + }; + useEffect(() => { + document.body.dir = i18n.dir() + }, [i18n, i18n.language]) + + return ( + <> + + + + ) +} diff --git a/wannameal/src/components/languageSelector/style.module.css b/wannameal/src/components/languageSelector/style.module.css new file mode 100644 index 0000000..e1e9326 --- /dev/null +++ b/wannameal/src/components/languageSelector/style.module.css @@ -0,0 +1,187 @@ +.popup { + --burger-line-width: 1.125em; + --burger-line-height: 0.125em; + --burger-offset: 0.625em; + --burger-bg: #00bf63; + --burger-color: #333; + --burger-line-border-radius: 0.1875em; + --burger-diameter: 3.125em; + --burger-btn-border-radius: calc(var(--burger-diameter) / 2); + --burger-line-transition: 0.3s; + --burger-transition: all 0.1s ease-in-out; + --burger-hover-scale: 1.1; + --burger-active-scale: 0.95; + --burger-enable-outline-color: var(--burger-bg); + --burger-enable-outline-width: 0.125em; + --burger-enable-outline-offset: var(--burger-enable-outline-width); + /* nav */ + --nav-padding-x: 0.25em; + --nav-padding-y: 0.625em; + --nav-border-radius: 0.375em; + --nav-border-color: #ccc; + --nav-border-width: 0.0625em; + --nav-shadow-color: rgba(0, 0, 0, 0.2); + --nav-shadow-width: 0 1px 5px; + --nav-bg: #eee; + --nav-font-family: "Poppins", sans-serif; + --nav-default-scale: 0.8; + --nav-active-scale: 1; + --nav-position-left: 0; + --nav-position-right: unset; + /* if you want to change sides just switch one property */ + /* from properties to "unset" and the other to 0 */ + /* title */ + --nav-title-size: 0.625em; + --nav-title-color: #777; + --nav-title-padding-x: 1rem; + --nav-title-padding-y: 0.25em; + /* nav button */ + --nav-button-padding-x: 1rem; + --nav-button-padding-y: 0.375em; + --nav-button-border-radius: 0.375em; + --nav-button-font-size: 17px; + --nav-button-hover-bg: #00bf63; + --nav-button-hover-text-color: #fff; + --nav-button-distance: 0.875em; + /* underline */ + --underline-border-width: 0.0625em; + --underline-border-color: #ccc; + --underline-margin-y: 0.3125em; +} + +/* popup settings 👆 */ + +.popup { + display: inline-block; + text-rendering: optimizeLegibility; + position: relative; +} + +.popup input { + display: none; +} + +.burger { + display: flex; + position: relative; + align-items: center; + justify-content: center; + background: var(--green); + width: var(--burger-diameter); + height: var(--burger-diameter); + border-radius: var(--burger-btn-border-radius); + border: none; + cursor: pointer; + overflow: hidden; + transition: var(--burger-transition); + outline: var(--burger-enable-outline-width) solid transparent; + outline-offset: 0; +} + +.popup_window { + transform: scale(var(--nav-default-scale)); + visibility: hidden; + opacity: 0; + position: absolute; + padding: var(--nav-padding-y) var(--nav-padding-x); + background: var(--light_burble); + font-family: var(--nav-font-family); + color: var(--nav-text-color); + border-radius: var(--nav-border-radius); + box-shadow: var(--nav-shadow-width) var(--nav-shadow-color); + border: var(--nav-border-width) solid var(--nav-border-color); + top: calc( + var(--burger-diameter) + var(--burger-enable-outline-width) + + var(--burger-enable-outline-offset) + ); + left: var(--nav-position-left); + right: var(--nav-position-right); + transition: var(--burger-transition); + margin-top: 10px; +} + +@media (max-width: 1056px) { + .popup_window { + left: -38px; + } +} +@media (max-width: 991px) { + .popup_window { + top: 0px; + left: 110%; + margin: 0; + padding: 0; + } + .popup_window ul { + display: flex; + } + .popup_window ul button { + padding: 5px !important; + } +} +.popup_window ul { + margin: 0; + padding: 0; + list-style-type: none; +} + +.popup_window ul button { + outline: none; + width: 100%; + border: none; + background: none; + display: flex; + align-items: center; + color: var(--green); + font-size: var(--nav-button-font-size); + padding: var(--nav-button-padding-y) var(--nav-button-padding-x); + white-space: nowrap; + border-radius: var(--nav-button-border-radius); + cursor: pointer; + column-gap: var(--nav-button-distance); +} + +/* actions */ + +.popup_window ul button:hover, +.popup_window ul button:focus-visible, +.popup_window ul button:hover svg, +.popup_window ul button:focus-visible svg { + color: var(--text_white); + background: var(--green); +} + +.burger:hover { + transform: scale(var(--burger-hover-scale)); +} + +.burger:active { + transform: scale(var(--burger-active-scale)); +} + +.burger:focus:not(:hover) { + outline-color: var(--burger-enable-outline-color); + outline-offset: var(--burger-enable-outline-offset); +} + +.popup input:checked + .burger span:nth-child(1) { + top: 50%; + transform: translateY(-50%) rotate(45deg); +} + +.popup input:checked + .burger span:nth-child(2) { + bottom: 50%; + transform: translateY(50%) rotate(-45deg); +} + +.popup input:checked + .burger span:nth-child(3) { + transform: translateX( + calc(var(--burger-diameter) * -1 - var(--burger-line-width)) + ); +} + +.popup input:checked ~ nav { + transform: scale(var(--nav-active-scale)); + visibility: visible; + opacity: 1; +} diff --git a/wannameal/src/components/mealCard/dashCard.jsx b/wannameal/src/components/mealCard/dashCard.jsx new file mode 100644 index 0000000..c9585fc --- /dev/null +++ b/wannameal/src/components/mealCard/dashCard.jsx @@ -0,0 +1,48 @@ +import React from "react"; +import styles from "./dashcard.module.css"; +import { IoTime } from "react-icons/io5"; +import { BsFire } from "react-icons/bs"; +import { GiCookingPot } from "react-icons/gi"; +import { CiBookmark } from "react-icons/ci"; +import { Link } from "react-router-dom"; +function DashCard({ meal }) { + return ( + <> + +
+
+ {`meal +
+
+ +
+
+
+
+ + 60Minutes +
+
+ + 600 +
+
+ 9{" "} + Components +
+
+

{meal?.name}

+
+
+ + + ); +} + +export default DashCard; diff --git a/wannameal/src/components/mealCard/dashcard.module.css b/wannameal/src/components/mealCard/dashcard.module.css new file mode 100644 index 0000000..0ff1e7e --- /dev/null +++ b/wannameal/src/components/mealCard/dashcard.module.css @@ -0,0 +1,93 @@ +.mealCard { + /* margin-top: 50px; */ + width: 370px; + height: 300px; + border-radius: 15px; + background-image: url("../../assets/Rectangle\ 9.png"); + background-repeat: no-repeat; + background-size: cover; + position: relative; + cursor: pointer; + transition: 0.2s; + transform: scale(1.001); +} +@media (min-width: 770px) { + .mealCard { + width: 300px; + } +} +@media (min-width: 1550px) { + .mealCard { + width: 300px !important; + } +} +@media (min-width: 1200px) { + .mealCard { + width: 250px; + } +} +.mealCard .mealInfo { + color: var(--text_white); + background-color: #4c4c4cba; + display: flex; + width: 100%; + height: 120px; + border-radius: 15px; + position: absolute; + left: 0; + bottom: 0; + flex-direction: column; + justify-content: space-between; + align-items: center; + align-items: end; + padding: 10px; +} +.mealCard .mealInfo .mealRates { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; +} +.mealCard .mealInfo .mealRates span { + font-size: 18px; + font-weight: 600; +} + +.mealCard .mealInfo .description { + font-family: poppins; + font-size: 28px; + font-weight: 600; + line-height: 36px; + letter-spacing: 0em; + text-align: right; + width: 100%; + white-space: nowrap; /* Prevent the text from wrapping */ + overflow: hidden; /* Hide the overflowed text */ + text-overflow: ellipsis; +} + +.mealCard .bookmark { + position: absolute; + top: 15px; + left: 15px; + background-color: #4c4c4cba; + padding: 3px 12px; + border-radius: 10px; +} + +.mealCard .imageContainer { + width: 100%; + height: 100%; + border-radius: 15px; + position: absolute; + left: 0; + top: 0; + overflow: hidden; +} +.mealCard .imageContainer img { + width: 100%; + height: 100%; + border-radius: 15px; + position: relative; + overflow: hidden; +} diff --git a/wannameal/src/components/mealCard/mealCard.jsx b/wannameal/src/components/mealCard/mealCard.jsx index 77fe51b..dc3eb59 100644 --- a/wannameal/src/components/mealCard/mealCard.jsx +++ b/wannameal/src/components/mealCard/mealCard.jsx @@ -1,11 +1,103 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import styles from "./mealcard.module.css"; import { IoTime } from "react-icons/io5"; import { BsFire } from "react-icons/bs"; import { GiCookingPot } from "react-icons/gi"; import { CiBookmark } from "react-icons/ci"; import { Link } from "react-router-dom"; +import { + getMealsError, + getMealsStatus, + getsSavedMeal, + saveMeal, +} from "../../redux/slices/recomendedMealsSlice"; +import { useDispatch, useSelector } from "react-redux"; +import { getDecodedToken, getuser } from "../../redux/slices/authSlice"; +import Swal from "sweetalert2"; +import { getLanguage } from "../../redux/slices/language"; + function MealCard({ meal }) { + console.log("🚀 ~ MealCard ~ meal:", meal); + const dispatch = useDispatch(); + const [isSaved, setIsSaved] = useState(meal?.isSaved || false); + const availableUser = useSelector(getuser); + const mealError = useSelector(getMealsError); + const mealstatus = useSelector(getMealsStatus); + console.log("🚀 ~ MealCard ~ mealstatus:", mealstatus); + console.log("🚀 ~ MealCard ~ mealError:", mealError); + const savedmeal = useSelector(getsSavedMeal); + console.log("🚀 ~ MealCard ~ savedmeal:", savedmeal); + + useEffect(() => { + if (savedmeal && savedmeal?._id === meal?._id) { + setIsSaved(savedmeal.isSaved); + } + }, [savedmeal, meal?._id]); + + const row = { + _id: 182, + recipeName: "soucees-pgklh", + image: + "https://res.cloudinary.com/dz5dpvxg7/image/upload/v1719800011/WannaMeal/meals/0TFBf-7vKLP389w4n9EGY/lypi8rese0p43vw4lmyn.png", + typeMeals: "Lunch", + ingredients: "vnbgxxxxxxxx,gfxh", + steps: "hshtsjrjj,jjy", + calories: 125, + times: 145, + EnoughFor: 5, + }; + + const handleBookmarkClick = async (e) => { + e.preventDefault(); + const mealData = { + _id: meal?._id, + recipeName: meal.recipeName, + image: meal?.image?.url, + typeMeals: "Lunch", + ingredients: meal?.ingredients, + steps: meal?.steps, + calories: meal?.calories, + times: meal?.times, + EnoughFor: meal?.EnoughFor, + }; + console.log("🚀 ~ handleBookmarkClick ~ mealData:", mealData); + + try { + await dispatch( + saveMeal({ + mealData, + token: availableUser?.token, + mealId: meal._id, + }) + ); + setIsSaved((prevIsSaved) => !prevIsSaved); + + Swal.fire({ + icon: "success", + title: isSaved + ? "Meal unsaved successfully" + : "Meal saved successfully", + toast: true, + position: "top-end", + showConfirmButton: false, + timer: 3000, + timerProgressBar: true, + }); + } catch (error) { + console.error("Failed to save meal: ", error); + + Swal.fire({ + icon: "error", + title: `${mealError || "Failed to save meal"}`, + toast: true, + position: "top-end", + showConfirmButton: false, + timer: 2000, + timerProgressBar: true, + }); + } + }; + return ( <> @@ -13,20 +105,24 @@ function MealCard({ meal }) {
{`meal
-
+
- {meal?.prep_time}Minutes + {meal?.times} Minutes
diff --git a/wannameal/src/components/mealCard/mealcard.module.css b/wannameal/src/components/mealCard/mealcard.module.css index c1d6804..a74a7c6 100644 --- a/wannameal/src/components/mealCard/mealcard.module.css +++ b/wannameal/src/components/mealCard/mealcard.module.css @@ -3,7 +3,7 @@ width: 340px; height: 300px; border-radius: 15px; - background-image: url("../../assets/Rectangle\ 9.png"); + /* background-image: url("../../assets/Rectangle\ 9.png"); */ background-repeat: no-repeat; background-size: cover; position: relative; diff --git a/wannameal/src/components/mealsSlider/mealsSlider.jsx b/wannameal/src/components/mealsSlider/mealsSlider.jsx index eeeec96..79653db 100644 --- a/wannameal/src/components/mealsSlider/mealsSlider.jsx +++ b/wannameal/src/components/mealsSlider/mealsSlider.jsx @@ -24,6 +24,7 @@ import { getRecommendMeals, } from "../../redux/slices/recomendedMealsSlice"; import Loading from "../loading/loading"; +import CommonMeals from "../commonMeals/commonMeals"; function MealsSlider() { const recomendedMeals = useSelector(getRecommendMeals); @@ -33,6 +34,9 @@ function MealsSlider() { const error = useSelector(getMealsError); console.log("🚀 ~ MealsSlider ~ error:", error); + if (recomendedMeals && recomendedMeals.length == 0) { + return ; + } if (mealsStatus == "loading") return (
@@ -74,12 +78,13 @@ function MealsSlider() { spaceBetween: 40, }, }} + style={{ direction: "ltr" }} > - {/* {recomendedMeals?.map((meal, index) => ( + {recomendedMeals?.map((meal, index) => ( - ))} */} + ))}
); diff --git a/wannameal/src/components/navbar/navbar.jsx b/wannameal/src/components/navbar/navbar.jsx index 9c51f53..69d4aa6 100644 --- a/wannameal/src/components/navbar/navbar.jsx +++ b/wannameal/src/components/navbar/navbar.jsx @@ -1,23 +1,23 @@ // import { jwtDecode } from "jwt-decode"; -import { curve, menuSlide, slide } from "./Animate"; -import { AnimatePresence, motion } from "framer-motion"; -import userImage from "../../assets/man-user.svg"; import Swal from "sweetalert2"; -import React, { useMemo, useState } from "react"; +import React, { useMemo } from "react"; import style from "./page.module.css"; import { Link, useLocation } from "react-router-dom"; import { IoIosSearch } from "react-icons/io"; import { useDispatch, useSelector } from "react-redux"; import { getuser, logout } from "../../redux/slices/authSlice"; import { IoNotificationsOutline } from "react-icons/io5"; +import LanguageSelector from "../../components/languageSelector/LanguageSelector"; import { getTheme, toggleTheme } from "../../redux/slices/systemModeSlice"; import { IoMoonOutline } from "react-icons/io5"; import { IoSunnyOutline } from "react-icons/io5"; import { HiBars3 } from "react-icons/hi2"; - +import { useTranslation } from "react-i18next"; +import { getLoggoedUser } from "../../redux/slices/userSLice"; function Navbar() { const availableUser = useSelector(getuser); - + const loggedUser = useSelector(getLoggoedUser); + console.log("🚀 ~ Navbar ~ loggedUser:", loggedUser); console.log("🚀 ~ Navbar ~ availableUser:", availableUser); const dispatch = useDispatch(); const theme = useSelector(getTheme); @@ -70,8 +70,7 @@ function Navbar() { path === "/accounting" || path === "/verification" || path === "/forgotPassword" || - path === "/reset" || - path.startsWith("/dashboard") + path === "/reset" ) { return true; } else { @@ -79,37 +78,30 @@ function Navbar() { } }, [path]); - const [clicked, setClicked] = useState(false); - - const navItems = [ - { - title: "Home", - href: "/", - }, - { - title: "Community", - href: "/", - }, - { - title: "Demo", - href: "/", - }, - { - title: "Contact", - href: "/", - }, - ]; - const toggletheme = () => { dispatch(toggleTheme()); }; + const { t, i18n } = useTranslation(); + const isAr = i18n.dir(); + console.log(isAr); + const { + Home, + Community, + Make, + Profile, + Contact, + logoutt, + dashboard, + makeMeal, + } = t("canvas"); + return ( <> {!hide && (
@@ -226,7 +219,9 @@ function Navbar() { {/*left aside offcanvas */}
  • - Home + {Home}
  • - Community + {Community}
  • - Make Meal + {Make}
  • - My Profile + {Profile}
  • - Contact Us + {Contact}
  • + {loggedUser && loggedUser?.role === "admin" && ( + <> +
  • + {dashboard} +
  • +
  • + {makeMeal} +
  • + + )}
- logout + {logoutt}
diff --git a/wannameal/src/components/navbar/page.module.css b/wannameal/src/components/navbar/page.module.css index d81062e..59206d7 100644 --- a/wannameal/src/components/navbar/page.module.css +++ b/wannameal/src/components/navbar/page.module.css @@ -163,6 +163,21 @@ transform: translateY(-50%); left: 20px; } +.rightBars { + padding: 7px; + border-radius: 50%; + border: 0; + display: flex; + justify-content: center; + align-items: center; + transition: 0.3s; + background-color: var(--light-green); + color: #fff; + position: absolute; + top: 50%; + transform: translateY(-50%); + right: 50px; +} .Aside { display: flex; @@ -186,6 +201,7 @@ transition: 0.3s; color: var(--text_black); border-radius: 6px; + text-align: center; } .Aside ul li a { width: 100% !important; diff --git a/wannameal/src/components/pagination/pagination.jsx b/wannameal/src/components/pagination/pagination.jsx index ef4c1a2..3f96e7f 100644 --- a/wannameal/src/components/pagination/pagination.jsx +++ b/wannameal/src/components/pagination/pagination.jsx @@ -1,33 +1,33 @@ import React from "react"; import { FaArrowLeft, FaArrowRight } from "react-icons/fa"; import style from "./pagination.module.css"; -export default function Pagination() { + +export default function Pagination({ who, currentPage, totalPages, setCurrentPage, totalUsers }) { + const handlePageChange = (page) => { + if (page >= 1 && page <= totalPages) { + setCurrentPage(page); + } + }; return ( -
-

- Total users :98 -

+
+

Total {who} :{totalUsers}

-

At least 8 characters

+

{characters}

-

At least 1 number

+

{number}

-

Bath upper and lower case letters

+

{letters}

- -

Or With

+ +

{With}

- + - Google + {Google}

- have an account? setlogappear(!logappear)} className={style.span}>log in + {haves} setlogappear(!logappear)} className={style.span}>{Login}

-

Create Account

-

What you will get?

+

{Create}

+

{What}

-

Manage your recipes the easy way

+

{Manage}

-

Share recipes with your friends and discover new ones.

+

{Share}

-

Organize recipes by tag, share it with your friends

+

{Organize}

- Know the parts of the meal and what it consists of and the - components that each molecule contains. + {Know}

diff --git a/wannameal/src/pages/Auth/Auth.jsx b/wannameal/src/pages/Auth/Auth.jsx index ac28ae9..7c10d63 100644 --- a/wannameal/src/pages/Auth/Auth.jsx +++ b/wannameal/src/pages/Auth/Auth.jsx @@ -5,20 +5,20 @@ import Account from "./Account/Account"; import Forgot from "./Forgot/Forgot"; import Verification from "./Verification/Verification"; import Reset from "./Reset/Reset"; +import plate_1 from '../../assets/plate_1.png'; +import plate_2 from '../../assets/plate_2.png'; +import plate_3 from '../../assets/plate_3.png'; export default function Auth() { return (
- - Wanna Meal -
- plate + plate
- plate + plate
- plate + plate
} /> diff --git a/wannameal/src/pages/Auth/Forgot/Forgot.jsx b/wannameal/src/pages/Auth/Forgot/Forgot.jsx index 044f532..c586784 100644 --- a/wannameal/src/pages/Auth/Forgot/Forgot.jsx +++ b/wannameal/src/pages/Auth/Forgot/Forgot.jsx @@ -4,6 +4,7 @@ import { MdOutlineMailOutline } from "react-icons/md"; import { useNavigate } from "react-router-dom"; import Swal from "sweetalert2"; import style from "./page.module.css"; +import { useTranslation } from "react-i18next"; export default function Forgot() { const [email, setemail] = useState(""); @@ -15,7 +16,7 @@ export default function Forgot() { const confirmEmali = (e) => { e.preventDefault(); axios - .patch("https://fast-plat1.vercel.app/auth/forgetCode", { email }) + .patch("https://fast-plat1.vercel.app/auth/forgetCode?lang=en", { email }) .then((respo) => { console.log(respo.data); if (respo.data.success) { @@ -44,13 +45,18 @@ export default function Forgot() { console.log(err.response.data); }); }; + const { t } = useTranslation(); + const { + Forgot, + Forgotp, + Send, + } = t('auth'); return (
-

Forgot Password

+

{Forgot}

- Enter the email address with your account and we’ll send an email with - confirmation to reset your password + {Forgotp}

@@ -65,7 +71,7 @@ export default function Forgot() { placeholder="Enter your Email" />
- +
); diff --git a/wannameal/src/pages/Auth/Forgot/page.module.css b/wannameal/src/pages/Auth/Forgot/page.module.css index b5c5348..0fd5c59 100644 --- a/wannameal/src/pages/Auth/Forgot/page.module.css +++ b/wannameal/src/pages/Auth/Forgot/page.module.css @@ -59,7 +59,7 @@ } .button_submit { margin: 25px 0 20px 0; - background-color: var(--blue); + background-color: var(--green); border: none; color: white; font-size: 1.5rem; diff --git a/wannameal/src/pages/Auth/Reset/Reset.jsx b/wannameal/src/pages/Auth/Reset/Reset.jsx index 34e4f19..3d76ff4 100644 --- a/wannameal/src/pages/Auth/Reset/Reset.jsx +++ b/wannameal/src/pages/Auth/Reset/Reset.jsx @@ -5,6 +5,7 @@ import { MdPassword } from 'react-icons/md'; import { useNavigate } from 'react-router-dom'; import Swal from 'sweetalert2'; import style from './page.module.css'; +import { useTranslation } from 'react-i18next'; export default function Reset() { const [user, setUser] = useState({ @@ -41,7 +42,7 @@ export default function Reset() { }; const submit = (e) => { e.preventDefault(); - axios.patch('https://fast-plat1.vercel.app/auth/resetPassword', user, config) + axios.patch('https://fast-plat1.vercel.app/auth/resetPassword?lang=en', user, config) .then((respo) => { console.log(respo.data); if (respo.data.success) { @@ -67,10 +68,20 @@ export default function Reset() { console.log(err.response.data); }) } + const { t } = useTranslation(); + const { + CreatePas, + shuold, + Reset, + Password, + characters, + number, + letters, + } = t('auth'); return (
-

Create New password

-
The password shuold be different from the previous password
+

{CreatePas}

+
{shuold}
@@ -80,7 +91,7 @@ export default function Reset() { value={user.password} onChange={handleChange} className={style.input} - placeholder="Enter your Password" + placeholder={`${Password}`} />
@@ -91,25 +102,25 @@ export default function Reset() { value={user.confirmPassword} onChange={handleChange} className={style.input} - placeholder="confirm your Password" + placeholder={`${Password}`} />
-

At least 8 characters

+

{characters}

-

At least 1 number

+

{number}

-

Bath upper and lower case letters

+

{letters}

- +
) } diff --git a/wannameal/src/pages/Auth/Reset/page.module.css b/wannameal/src/pages/Auth/Reset/page.module.css index 01a7421..628e02b 100644 --- a/wannameal/src/pages/Auth/Reset/page.module.css +++ b/wannameal/src/pages/Auth/Reset/page.module.css @@ -8,14 +8,14 @@ align-items: center; gap: 10px; - background-color: #ffffff; + background-color: var(--text_white); padding: 3rem 4rem; width: 650px; border-radius: 10px; box-shadow: 0px 0px 25px 10px rgba(0, 0, 0, 0.25); } .head { - color: var(text_black); + color: var(--text_black); font-size: 42px; font-style: normal; font-weight: 500; @@ -63,9 +63,9 @@ } .button_submit { margin: 25px 0 20px 0; - background-color: var(--blue); + background-color: var(--green); border: none; - color: white; + color: var(--text_white); font-size: 1.2rem; font-weight: 600; border-radius: 20px; diff --git a/wannameal/src/pages/Auth/Verification/Verification.jsx b/wannameal/src/pages/Auth/Verification/Verification.jsx index 69c3ec8..dfab08c 100644 --- a/wannameal/src/pages/Auth/Verification/Verification.jsx +++ b/wannameal/src/pages/Auth/Verification/Verification.jsx @@ -3,6 +3,7 @@ import React, { useRef, useState } from 'react' import { useNavigate } from 'react-router-dom'; import Swal from 'sweetalert2'; import style from "./page.module.css"; +import { useTranslation } from 'react-i18next'; export default function Verification() { const [code, setCode] = useState(['', '', '', '', '']); @@ -31,7 +32,7 @@ export default function Verification() { const forgetCode = code.join('') const submitCode = (e) => { e.preventDefault(); - axios.patch('https://fast-plat1.vercel.app/auth/VerifyCode', { forgetCode }, config) + axios.patch('https://fast-plat1.vercel.app/auth/VerifyCode?lang=en', { forgetCode }, config) .then((respo) => { console.log(respo.data); if (respo.data.success) { @@ -57,12 +58,19 @@ export default function Verification() { console.log(err.response.data); }) } + const { t } = useTranslation(); + const { + check, + Verification, + Send, + Sended, + } = t('auth'); return (
-

Please check your Email

-

We have sent code to Badr23@gmail.com

+

{check}

+

{Sended}

{code.map((digit, index) => ( @@ -77,8 +85,8 @@ export default function Verification() { /> ))}
-

send code again 00:45

- +

{Send} again 00:45

+
) diff --git a/wannameal/src/pages/Auth/Verification/page.module.css b/wannameal/src/pages/Auth/Verification/page.module.css index a73fa52..1cf0c07 100644 --- a/wannameal/src/pages/Auth/Verification/page.module.css +++ b/wannameal/src/pages/Auth/Verification/page.module.css @@ -47,14 +47,18 @@ align-items: center; gap: 1rem; border-radius: 10px; - background: #fff; + background: var(--text_white); box-shadow: 0px 0px 25px 10px rgba(0, 0, 0, 0.25); padding: 3rem 5rem; width: 730px; } +.contain h2 { + color: var(--text_black); +} + .button_submit { - background-color: var(--blue); + background-color: var(--green); border: none; color: white; font-size: 1.5rem; diff --git a/wannameal/src/pages/Auth/auth.module.css b/wannameal/src/pages/Auth/auth.module.css index 9b99628..ff20e4c 100644 --- a/wannameal/src/pages/Auth/auth.module.css +++ b/wannameal/src/pages/Auth/auth.module.css @@ -1,7 +1,7 @@ .thebody { background-image: linear-gradient(to top, var(--body) 90%, var(--text_white)); color: var(--text_Black); - height: 100vh; + height: 93vh; width: 100vw; position: relative; } diff --git a/wannameal/src/pages/Home/Home.jsx b/wannameal/src/pages/Home/Home.jsx index ac06e4f..62dfe0e 100644 --- a/wannameal/src/pages/Home/Home.jsx +++ b/wannameal/src/pages/Home/Home.jsx @@ -1,10 +1,15 @@ import AboutUs from "../../components/aboutUs/aboutus"; import CommonMeals from "../../components/commonMeals/commonMeals"; import LandingPage from "../../components/landingPage/LandingPage"; +import { Helmet } from "react-helmet"; export default function Home() { return ( <> + + WannaMeal + + diff --git a/wannameal/src/pages/RecipeDetails/RecipeDetails.jsx b/wannameal/src/pages/RecipeDetails/RecipeDetails.jsx index 7807375..99db405 100644 --- a/wannameal/src/pages/RecipeDetails/RecipeDetails.jsx +++ b/wannameal/src/pages/RecipeDetails/RecipeDetails.jsx @@ -1,40 +1,55 @@ import React from "react"; import style from "./page.module.css"; import Food_Icon from "../../assets/fluent_food-24-filled.svg"; -import dish from "../../assets/dish.png"; import { IoTime } from "react-icons/io5"; -import { BsFire } from "react-icons/bs"; -import { GiCookingPot } from "react-icons/gi"; +import { BsDiagram3, BsFire } from "react-icons/bs"; import { useLocation } from "react-router-dom"; import MealCard from "../../components/mealCard/mealCard"; +import CommonMeals from "../../components/commonMeals/commonMeals"; +import { useTranslation } from "react-i18next"; +import { Helmet } from "react-helmet"; const RecipeDetails = () => { - const location = useLocation(); const { meal } = location.state || {}; console.log("🚀 ~ RecipeDetails ~ meal:", meal); - let x = [1, 23, 45, 68, 564, 6, 456, 44]; + const stepsArray = meal?.steps ? meal.steps.split(",") : []; + const ingredientsArray = meal?.steps ? meal.ingredients.split(",") : []; + console.log(stepsArray); + let x = [1, 2, 35, 459, 88, 113, 87, 2]; + const { t } = useTranslation(); + const { + apply, + minutes, + components, + people, + Ingredients, + Directions, + Latest, + } = t("View"); return (
+ + Recipe Detailes + +
-

- The modus operandi of meat with potato and onion pieces -

+

{meal.recipeName}

icon
-

apply for

+

{apply}

- 2 people + {meal.EnoughFor} {people}
- + */}
{ {" "} - 60 minutes + {meal.times} {minutes}
{ {" "} - 60 minutes + {meal.times} {minutes}
- + {" "} - 6 components + {meal.calories} {components}
- dish + dish
-

ingredients

+

{Ingredients}

-
-

meat

-

1/2 K

-
-
-

meat

-

1/2 K

-
-
-

meat

-

1/2 K

-
+ {ingredientsArray?.map((x, ind) => { + return ( +
+ {ind + 1} +

{x}

+
+ ); + })}
-

Directions

+

{Directions}

-
- 1 -

قطع اللحم الي مكعبات متوسطه الحجم

-
-
- 2 -

- ضع قطع اللحم في وعاء به ماء علي نار متوسطه وضع فيه البهارات (ملح - - توابل لحم - فلفل اسود -ورق لوري ) -

-
-
- 3 -

- عندما تنتهي البطاطس من الغليان اخرجها من الوعاء وقم بتقشيرها - وقطعها اللي مكعبات متوسطه وقم برش بعض الملح والفلفل الاسود عليها{" "} -

-
+ {stepsArray?.map((x, ind) => { + return ( +
+ {ind + 1} +

{x}

+
+ ); + })}
-

Latest Recipes

+

{Latest}

- {x.map((x) => { - return ( -
- -
- ); - })} +
); diff --git a/wannameal/src/pages/addProduct/addProduct.jsx b/wannameal/src/pages/addProduct/addProduct.jsx index b2ec396..53f30ce 100644 --- a/wannameal/src/pages/addProduct/addProduct.jsx +++ b/wannameal/src/pages/addProduct/addProduct.jsx @@ -1,359 +1,417 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from "react"; import style from "./page.module.css"; import { BiSolidTrash } from "react-icons/bi"; import { IoMdAddCircleOutline } from "react-icons/io"; import { MdCheckCircleOutline } from "react-icons/md"; import { CiCamera } from "react-icons/ci"; import axios from "axios"; +import { useTranslation } from "react-i18next"; +import { useSelector } from "react-redux"; +import { getuser } from "../../redux/slices/authSlice"; +import { Helmet } from "react-helmet"; -const AddProduct = () => { // Changed from addProduct to AddProduct - const [ingredientsValue, setIngredientsValue] = useState([{ id: 1, value: "" }]); - const [directionsValue, setDirectionsValue] = useState([{ id: 1, value: "" }]); - const [isActive, setisActive] = useState(""); - const [image, setImage] = useState(null); - const [recipe, setRecipe] = useState({ - recipeName: "", - information: "", - typeMeals: "", - times: "", - EnoughFor: "", - calories: 500, - image: null, - ingredients: [], - steps: [], - }); - - const handleInputChange = (e) => { - const { name, value } = e.target; - setRecipe((prevRecipe) => ({ - ...prevRecipe, - [name]: value, - })); - console.log(recipe); - }; +const AddProduct = () => { + // Changed from addProduct to AddProduct + const [ingredientsValue, setIngredientsValue] = useState([ + { id: 1, value: "" }, + ]); + const [directionsValue, setDirectionsValue] = useState([ + { id: 1, value: "" }, + ]); + const [isActive, setisActive] = useState(""); + const [image, setImage] = useState(null); + const [randomNumber, setRandomNumber] = useState(null); + const { t } = useTranslation(); + const { + Rname, + Special, + share, + breaks, + lunch, + dinner, + enough, + time, + Ingredients, + Directions, + addingredient, + addStep, + saveRecipe, + cancelRecipe, + caloriess, + } = t("add"); + useEffect(() => { + const randomNum = Math.floor(Math.random() * 100) + 1; // Generate random number between 1 and 100 + setRandomNumber(randomNum); + }, []) - const handleFileChange = (e) => { - const file = e.target.files[0]; - setRecipe((prevRecipe) => ({ - ...prevRecipe, - image: file, - })); - setImage(URL.createObjectURL(file)); - }; + const user = useSelector(getuser); + const [recipe, setRecipe] = useState({ + recipeName: "", + information: "", + typeMeals: "", + times: "", + EnoughFor: "", + calories: null, + _id: 24, + image: null, + ingredients: [], + steps: [], + }); - const handleAddIngredient = () => { - const newIngredient = { id: ingredientsValue.length + 1, value: "" }; - setIngredientsValue([...ingredientsValue, newIngredient]); - setRecipe((prevRecipe) => ({ - ...prevRecipe, - ingredients: [...ingredientsValue, newIngredient], - })); - }; + const handleInputChange = (e) => { + const { name, value } = e.target; + setRecipe((prevRecipe) => ({ + ...prevRecipe, + [name]: value, + })); + console.log(recipe); + }; - const handleAddDirection = () => { - const newDirection = { id: directionsValue.length + 1, value: "" }; - setDirectionsValue([...directionsValue, newDirection]); - setRecipe((prevRecipe) => ({ - ...prevRecipe, - steps: [...directionsValue, newDirection], - })); - }; + const handleFileChange = (e) => { + const file = e.target.files[0]; + setRecipe((prevRecipe) => ({ + ...prevRecipe, + image: file, + })); + setImage(URL.createObjectURL(file)); + }; - const handleIngredientChange = (index, value) => { - const updatedIngredients = [...ingredientsValue]; - updatedIngredients[index].value = value; - setIngredientsValue(updatedIngredients); - setRecipe((prevRecipe) => ({ - ...prevRecipe, - ingredients: updatedIngredients, - })); - }; + const handleAddIngredient = () => { + const newIngredient = { id: ingredientsValue.length + 1, value: "" }; + setIngredientsValue([...ingredientsValue, newIngredient]); + setRecipe((prevRecipe) => ({ + ...prevRecipe, + ingredients: [...ingredientsValue, newIngredient], + })); + }; - const handleDirectionChange = (index, value) => { - const updatedDirections = [...directionsValue]; - updatedDirections[index].value = value; - setDirectionsValue(updatedDirections); - setRecipe((prevRecipe) => ({ - ...prevRecipe, - steps: updatedDirections, - })); - }; + const handleAddDirection = () => { + const newDirection = { id: directionsValue.length + 1, value: "" }; + setDirectionsValue([...directionsValue, newDirection]); + setRecipe((prevRecipe) => ({ + ...prevRecipe, + steps: [...directionsValue, newDirection], + })); + }; - const handleRemoveIngredient = (index) => { - const updatedIngredients = [...ingredientsValue]; - updatedIngredients.splice(index, 1); - setIngredientsValue(updatedIngredients); - setRecipe((prevRecipe) => ({ - ...prevRecipe, - ingredients: updatedIngredients, - })); - }; + const handleIngredientChange = (index, value) => { + const updatedIngredients = [...ingredientsValue]; + updatedIngredients[index].value = value; + setIngredientsValue(updatedIngredients); + setRecipe((prevRecipe) => ({ + ...prevRecipe, + ingredients: updatedIngredients, + })); + }; - const handleRemoveDirection = (index) => { - const updatedDirections = [...directionsValue]; - updatedDirections.splice(index, 1); - setDirectionsValue(updatedDirections); - setRecipe((prevRecipe) => ({ - ...prevRecipe, - steps: updatedDirections, - })); - }; + const handleDirectionChange = (index, value) => { + const updatedDirections = [...directionsValue]; + updatedDirections[index].value = value; + setDirectionsValue(updatedDirections); + setRecipe((prevRecipe) => ({ + ...prevRecipe, + steps: updatedDirections, + })); + console.log(recipe); + }; - const clearInputs = () => { - setRecipe({ - recipeName: "", - information: "", - typeMeals: "", - times: "", - EnoughFor: "", - calories: 500, - image: null, - ingredients: [], - steps: [], - }); - setImage(null); - setIngredientsValue([{ id: 1, value: "" }]); - setDirectionsValue([{ id: 1, value: "" }]); - setisActive('') - }; + const handleRemoveIngredient = (index) => { + const updatedIngredients = [...ingredientsValue]; + updatedIngredients.splice(index, 1); + setIngredientsValue(updatedIngredients); + setRecipe((prevRecipe) => ({ + ...prevRecipe, + ingredients: updatedIngredients, + })); + }; + const handleRemoveDirection = (index) => { + const updatedDirections = [...directionsValue]; + updatedDirections.splice(index, 1); + setDirectionsValue(updatedDirections); + setRecipe((prevRecipe) => ({ + ...prevRecipe, + steps: updatedDirections, + })); + }; - const handleAddRecipe = async (e) => { - e.preventDefault(); - // take the value only - const ingredients = ingredientsValue.map((ingredient) => ingredient.value); - const directions = directionsValue.map((direction) => direction.value); - // add data to form - const formData = new FormData(); - formData.append("recipeName", recipe.recipeName); - formData.append("information", recipe.information); - formData.append("typeMeals", recipe.typeMeals); - formData.append("times", recipe.times); - formData.append("EnoughFor", recipe.EnoughFor); - formData.append("calories", recipe.calories); - formData.append("image", recipe.image); - formData.append("ingredients", JSON.stringify(ingredients)); - formData.append("steps", JSON.stringify(directions)); - - try { - const response = await axios.post( - "https://fast-plat1.vercel.app/meals/addAnewRecipe", - formData, - { - headers: { - "Content-Type": "multipart/form-data", - token: - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY1ZTBlZWI1NDQ0NjExZTI2NzczNmMxNSIsImVtYWlsIjoia2FzYWJ5MTQxNUBnbWFpbC5jb20iLCJpYXQiOjE3MDk2NTQ0NjV9.l9-2_aFHYFrvSn_C0KpnbYTTpImtGMHCOWV0QXjYKZo", - }, - } - ); + const clearInputs = () => { + setRecipe({ + recipeName: "", + information: "", + typeMeals: "", + times: "", + EnoughFor: "", + calories: 500, + image: null, + ingredients: [], + steps: [], + }); + setImage(null); + setIngredientsValue([{ id: 1, value: "" }]); + setDirectionsValue([{ id: 1, value: "" }]); + setisActive(""); + }; - console.log(response.data); - // Handle success - clearInputs(); - } catch (error) { - console.error(error.response.data); - // Handle error + const handleAddRecipe = async (e) => { + e.preventDefault(); + // take the value only + const ingredients = ingredientsValue.map((ingredient) => ingredient.value); + const directions = directionsValue.map((direction) => direction.value); + // add data to form + const formData = new FormData(); + formData.append("recipeName", recipe.recipeName); + formData.append("information", recipe.information); + formData.append("typeMeals", recipe.typeMeals); + formData.append("times", recipe.times); + formData.append("EnoughFor", recipe.EnoughFor); + formData.append("calories", recipe.calories); + formData.append("_id", recipe._id); + formData.append("image", recipe.image); + formData.append("ingredients", JSON.stringify(ingredients.join(','))); + formData.append("steps", JSON.stringify(directions.join(','))); + try { + const response = await axios.post( + "https://fast-plat1.vercel.app/meals/addAnewRecipe", + formData, + { + headers: { + "Content-Type": "multipart/form-data", + token: user?.token, + }, } - }; - return ( -
-
-
- + + Add Meal + + +
+
+ + +
+ + + +
+
+

{enough}

+ +
+
+

{time}

+ +
+
+

{caloriess}

+ +
+
+
+
+

{Ingredients}

+
+
+ {ingredientsValue.map((ingredient, index) => ( +
+
{index + 1}
+ handleIngredientChange(index, e.target.value)} + /> + handleRemoveIngredient(index)} + />
-
- - + ))} + +
+
+

{Directions}

+
+
+ {directionsValue.map((direction, index) => ( +
+
{index + 1}
+ handleDirectionChange(index, e.target.value)} + /> + handleRemoveDirection(index)} + />
+ ))} +
- ); +
+
+ + +
+
+ ); }; export default AddProduct; // Changed from addProduct to AddProduct diff --git a/wannameal/src/pages/addProduct/page.module.css b/wannameal/src/pages/addProduct/page.module.css index ccb7f6e..24b8f35 100644 --- a/wannameal/src/pages/addProduct/page.module.css +++ b/wannameal/src/pages/addProduct/page.module.css @@ -92,7 +92,7 @@ border: none; border-radius: 10px; padding: 0.9rem; - color: var(--text_black); + color: #000; } .add_input:focus { diff --git a/wannameal/src/pages/chatting/chatting.jsx b/wannameal/src/pages/chatting/chatting.jsx index ecd523a..e91b5aa 100644 --- a/wannameal/src/pages/chatting/chatting.jsx +++ b/wannameal/src/pages/chatting/chatting.jsx @@ -7,6 +7,8 @@ import { VscSend } from "react-icons/vsc"; import InputEmoji from "react-input-emoji"; import { useSelector, useDispatch } from "react-redux"; import { getTheme } from "../../redux/slices/systemModeSlice"; +import { Helmet } from "react-helmet"; + export default function Chatting() { let chats = [1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8]; const [activeChat, setActiveChat] = useState(true); @@ -44,6 +46,13 @@ export default function Chatting() { }, []); return (
+ + WannaMeal messenger + +
diff --git a/wannameal/src/pages/community/comunity.jsx b/wannameal/src/pages/community/comunity.jsx index ad2a779..d034d49 100644 --- a/wannameal/src/pages/community/comunity.jsx +++ b/wannameal/src/pages/community/comunity.jsx @@ -11,12 +11,16 @@ import Post from "../../components/post/post"; import { VscSend } from "react-icons/vsc"; import { useSelector, useDispatch } from "react-redux"; import { getuser, getDecodedToken } from "../../redux/slices/authSlice"; +import { Helmet } from "react-helmet"; + import { createPost, fetchFeedPosts, fetchRandomPosts, getCreatedPosts, getFeedPosts, + getPostError, + getPostStatus, getRandomPosts, } from "../../redux/slices/postsSLlce"; import { @@ -28,6 +32,7 @@ import { fetchFollowingUsers, } from "../../redux/slices/communityUserSlice"; import Swal from "sweetalert2"; +import { useTranslation } from "react-i18next"; import { getLanguage } from "../../redux/slices/language"; export default function Community() { @@ -36,10 +41,36 @@ export default function Community() { const [videos, setVideos] = useState([]); const [previewPhotos, setPreviewPhotos] = useState([]); const [previewVideos, setPreviewVideos] = useState([]); + const { t } = useTranslation(); + const { + Fname, + email, + edit, + comment, + sharee, + like, + mine, + recommend, + Norecommend, + more, + follow, + msg, + share, + followers, + following, + photo, + video, + who, + } = t("community", { + fullname: "mahmoud khairy402", + email: "mahmoudkhairy402@gmail.com", + }); const [followingUsersPage, setFollowingUsersPage] = useState(1); console.log("🚀 ~ Community ~ followingUsersPage:", followingUsersPage); const [suggestedUsersPage, setSuggestedUsersPage] = useState(1); console.log("🚀 ~ Community ~ suggestedUsersPage:", suggestedUsersPage); + const [postsLimit, setPostsLimit] = useState(5); + console.log("🚀🚀🚀🚀🚀🚀 ~ Community ~ postsLimit:", postsLimit); // Use selectors to get state values const { suggestedUsers } = useSelector(getSuggestedUsers); @@ -55,8 +86,9 @@ export default function Community() { const randomposts = useSelector(getRandomPosts); console.log("🚀 ~ Community ~ randomposts:", randomposts); console.log("🚀 ~ Community ~ posts:", createdPosts); - const postError = useSelector((state) => state.posts.error); - const postStatus = useSelector((state) => state.posts.status); + const postError = useSelector(getPostError); + const postStatus = useSelector(getPostStatus); + console.log("🚀 ~ Community ~ postStatus:", postStatus); const profileError = useSelector((state) => state.communityUser.error); const profileStatus = useSelector((state) => state.communityUser.status); const decodedToken = useSelector(getDecodedToken); @@ -192,8 +224,8 @@ export default function Community() { try { dispatch( getProfileById({ - userId: decodedToken.id, - token: availableUser.token, + userId: decodedToken?.id, + token: availableUser?.token, lang: language, }) ); @@ -220,8 +252,38 @@ export default function Community() { } }, [availableUser.token, decodedToken.id, followingUsersPage]); + const handleScroll = () => { + const bottom = + Math.ceil(window.innerHeight + window.scrollY) >= + document.documentElement.scrollHeight; + + if (bottom) { + // Load more random posts + // Adjust the number of posts to load here + // dispatch( + // fetchRandomPosts({ + // token: availableUser.token, + // lang: language, + // limit: randomposts.length + 5, // Increase the limit by 5 when scrolling to bottom + // }) + // ); + setPostsLimit((prev) => prev + 5); + } + }; + useEffect(() => { + window.addEventListener("scroll", handleScroll); + return () => window.removeEventListener("scroll", handleScroll); + }, []); + return (
+ + WannaMeal community + +
@@ -250,17 +312,17 @@ export default function Community() {
{profile.user.following?.length || 0}{" "}
-
following
+
{following}
{profile.user.followers?.length || 0}{" "}
-
followers
+
{followers}
- my profile + {mine}
) : ( @@ -269,24 +331,24 @@ export default function Community() { {suggestedUsers ? (
-
recommended for you
+
{recommend}
{suggestedUsers && suggestedUsers?.length > 0 ? ( suggestedUsers.map((user) => ( // Ensure unique key for each UserCard )) ) : ( -

No recommended users for you!

+

{Norecommend}

)}
{suggestedUsers && suggestedUsers?.length > 3 && (
-
Show more
+
{more}
)}
@@ -311,7 +373,7 @@ export default function Community() {
) ) : randomposts && randomposts.length > 0 ? ( randomposts - .slice(0, 3) + .slice(0, postsLimit) .map((post) => ) ) : ( @@ -386,19 +448,19 @@ export default function Community() {
{followingUsers ? (
-
who are you following
+
{who}
{followingUsers && followingUsers?.length > 0 ? ( followingUsers.map((user) => ( )) ) : ( -

Start to follow people to share your activity..!

+

{msg}

)}
{followingUsers && followingUsers?.length > 3 && (
-
Show more
+
{more}
)}
diff --git a/wannameal/src/pages/contact/contact.jsx b/wannameal/src/pages/contact/contact.jsx index 9427d38..9bd0252 100644 --- a/wannameal/src/pages/contact/contact.jsx +++ b/wannameal/src/pages/contact/contact.jsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; import styles from "./contactUS.module.css"; import { MdOutlineLocationOn } from "react-icons/md"; import { FiPhone } from "react-icons/fi"; @@ -7,20 +7,82 @@ import { TiSocialFacebook } from "react-icons/ti"; import { FaXTwitter } from "react-icons/fa6"; import { TiSocialLinkedin } from "react-icons/ti"; import "animate.css"; +import { useTranslation } from "react-i18next"; +import axios from "axios"; +import Swal from "sweetalert2"; +import { Helmet } from "react-helmet"; + function ContactUs() { + const { t } = useTranslation(); + const { head, fname, Email, msg, send, location, phone, mail } = t("contact"); + + const [formData, setFormData] = useState({ + name: "", + email: "", + message: "", + }); + + console.log("🚀 ~ ContactUs ~ formData:", formData); + const handleChange = (e) => { + const { name, value } = e.target; + setFormData({ + ...formData, + [name]: value, + }); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + try { + const response = await axios.post( + "https://fast-plat1.vercel.app/contactus", + formData + ); + console.log("🚀 ~ handleSubmit ~ response:", response); + Swal.fire({ + icon: "success", + title: response?.data?.message, + toast: true, + position: "top-end", + showConfirmButton: false, + timer: 3000, + timerProgressBar: true, + }); + + setFormData({ + name: "", + email: "", + message: "", + }); + } catch (error) { + console.error("Failed to send contact: ", error); + Swal.fire({ + icon: "error", + title: "Failed to send", + toast: true, + position: "top-end", + showConfirmButton: false, + timer: 2000, + timerProgressBar: true, + }); + } + }; + return (
+ + Contact Us + +
-
- get in touch -
+
{head}
-
+
@@ -37,58 +102,42 @@ function ContactUs() { name="email" className={`form-control ${styles.input}`} id="exampleFormControlInput1" - placeholder="Your Email " + placeholder={Email} + value={formData.email} + onChange={handleChange} + required />