diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 8dbe145..0345064 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,33 +1,32 @@ Please include a summary of the changes and the related issue. Please also include relevant motivation and context. List any dependencies that are required for this change. - Fixes # (issue) - ## Type of change -Please give a X on it which is applicable +Please mark with an X the type that applies: - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) -- [ ] Refactor Code -- [ ] A documentation update -- [ ] Others(mentioned in the issue number) +- [ ] Refactor code +- [ ] Documentation update +- [ ] Other (mentioned in the issue number) # How Has This Been Tested? -Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration +Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce them. Please also list any relevant details for your test configuration. + +**_Test A: Describe here_** -**_Test A Describe here_** +**_Test B: Describe here (if required)_** -**_Test B Describe here (if Requred)_** +# Screenshots and Videos -# Screenshorts and Vedios: +Please provide screenshots and videos of the changes you made. -give screenshorts and vedio of the changes you made +# Checklist -# Checklist: -give a X on it which is applicable +Please mark with an X the items that apply: - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code diff --git a/envexample b/envexample new file mode 100644 index 0000000..e30449b --- /dev/null +++ b/envexample @@ -0,0 +1 @@ +REACT_APP_BASE_URL=localhost:8000/api/v1 8000 can be replaced by the server you are running backend on \ No newline at end of file diff --git a/package.json b/package.json index 33d7efb..39b940c 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "aos": "^2.3.4", "axios": "^1.5.1", "framer-motion": "^11.1.9", + "jwt-decode": "^4.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hot-toast": "^2.4.1", diff --git a/server/controllers/Auth.js b/server/controllers/Auth.js index 57982fa..065fa16 100644 --- a/server/controllers/Auth.js +++ b/server/controllers/Auth.js @@ -11,6 +11,7 @@ const { findUserById, } = require("../utils/PasswordTokenAndUser"); const nodemailer = require("nodemailer"); + require("dotenv").config(); exports.studentSignup = async (req, res) => { @@ -264,7 +265,7 @@ exports.canteenSignup = async (req, res) => { // Create a token const token = jwt.sign( - { id: canteen._id, email: canteen.email }, + { id: canteen._id, email: canteen.email, accountType: canteen.accountType, }, process.env.JWT_SECRET, { expiresIn: "1h", // Set token expiration time as needed @@ -441,6 +442,7 @@ exports.changeCanteenPassword = async (req, res) => { }); }; + //contactUs exports.saveContactMessage = async (req, res) => { @@ -457,6 +459,7 @@ exports.saveContactMessage = async (req, res) => { res.status(500).send('Error saving message'); } }; + // verify user for reset password exports.forgotPassword = async (req, res) => { try { @@ -594,4 +597,3 @@ exports.resetPassword = async (req, res) => { res.status(500).json("Some error occurred!"); } }; - diff --git a/server/envexample b/server/envexample new file mode 100644 index 0000000..f4f26dd --- /dev/null +++ b/server/envexample @@ -0,0 +1,6 @@ +PORT=8000 +DATABASE_URL= +JWT_SECRET= +CLOUDINARY_CLOUD_NAME= +CLOUDINARY_API_KEY= +CLOUDINARY_API_SECRET= diff --git a/src/authContext.js b/src/authContext.js index 4b1fb0b..bdb1d70 100644 --- a/src/authContext.js +++ b/src/authContext.js @@ -1,4 +1,4 @@ -import { createContext, useContext, useState } from "react"; +import { createContext, useContext, useState, useEffect } from "react"; const authContext = createContext({ isAuthenticated: false @@ -8,13 +8,34 @@ export const useAuth = () => useContext(authContext); const AuthProvider = ({ children }) => { const [isAuthenticated, setAuthenticated] = useState(false); + + useEffect(() => { + const token = localStorage.getItem('authToken'); + if (token) { + setAuthenticated(true); + } + }, []); - const checkAuthentication = (token) => { - setAuthenticated(!!token); + const login = (token) => { + // saving the token to local storage when canteen user logIn + localStorage.setItem('authToken', token); + setAuthenticated(true); + }; + + const signUp = (token) => { + // saving the token to local storage when canteen user logIn + localStorage.setItem('authToken', token); + setAuthenticated(true); + }; + + const logout = () => { + + localStorage.removeItem('authToken'); + setAuthenticated(false); }; return ( - + {children} ); diff --git a/src/components/CanteenCard.jsx b/src/components/CanteenCard.jsx index b78a9b7..af60d08 100644 --- a/src/components/CanteenCard.jsx +++ b/src/components/CanteenCard.jsx @@ -41,7 +41,7 @@ const CanteenCard = ({ canteen }) => { {canteen.name} diff --git a/src/components/CanteenList.jsx b/src/components/CanteenList.jsx index 5db5757..0a35e8d 100644 --- a/src/components/CanteenList.jsx +++ b/src/components/CanteenList.jsx @@ -1,19 +1,18 @@ - -import React, { useEffect, useState } from 'react'; +import React from 'react'; import CanteenCard from './CanteenCard'; - - -const CanteenList = ({canteenData}) => { +const CanteenList = ({ canteenData }) => { + if (!canteenData || !canteenData.data) { + return

No canteen data available.

; + } return (
- {canteenData?.data.map(canteen => ( - + {canteenData.data.map((canteen) => ( + ))}
); - }; export default CanteenList; diff --git a/src/components/ModalForm.jsx b/src/components/ModalForm.jsx index 09ae7d4..d8f9339 100644 --- a/src/components/ModalForm.jsx +++ b/src/components/ModalForm.jsx @@ -60,7 +60,7 @@ const ModalForm = ({ onSubmit , sectionName , canteenData , id}) => { if(sectionName === "Breakfast"){ - const apiUrl = `http://localhost:8000/api/v1/${id}/breakfast/add`; + const apiUrl = `${process.env.REACT_APP_BASE_URL}/${id}/breakfast/add`; axios.post(apiUrl , foodDetails) .then((response)=>{ @@ -75,7 +75,7 @@ const ModalForm = ({ onSubmit , sectionName , canteenData , id}) => { } else if(sectionName === "Lunch"){ - const apiUrl = `http://localhost:8000/api/v1/${id}/lunch/add`; + const apiUrl = `${process.env.REACT_APP_BASE_URL}/${id}/lunch/add`; axios.post(apiUrl , foodDetails) .then((response)=>{ @@ -92,7 +92,7 @@ const ModalForm = ({ onSubmit , sectionName , canteenData , id}) => { } else{ - const apiUrl = `http://localhost:8000/api/v1/${id}/dinner/add`; + const apiUrl = `${process.env.REACT_APP_BASE_URL}/${id}/dinner/add`; axios.post(apiUrl , foodDetails) .then((response)=>{ diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx index a6a34ba..dd97a3a 100644 --- a/src/components/Navbar.jsx +++ b/src/components/Navbar.jsx @@ -1,14 +1,34 @@ -import React, { useState, useContext } from "react"; +import React, { useState, useContext, useEffect } from "react"; import { Link } from "react-router-dom"; import logo from "../assets/logo2.png"; import { motion, AnimatePresence, useScroll } from "framer-motion"; import { IoClose } from "react-icons/io5"; import { GiHamburgerMenu } from "react-icons/gi"; import { ThemeContext } from '../themeContext'; +import { jwtDecode } from "jwt-decode"; +import { useAuth } from "../authContext"; + const Navbar = () => { + + const { logout} = useAuth(); + const [canteenId, setCanteenId] = useState(null); const { theme, toggleTheme } = useContext(ThemeContext); const [isOpen, setIsOpen] = useState(false); const { scrollYProgress } = useScroll(); + + useEffect(() => { + const token = localStorage.getItem("authToken"); + if (token) { + try { + const decodedToken = jwtDecode(token); + if (decodedToken.accountType === "Canteen") { + setCanteenId(decodedToken.id); + } + } catch (error) { + console.error("Invalid token", error); + } + } + }, []); const toggleMenu = () => { setIsOpen(!isOpen); @@ -16,60 +36,51 @@ const Navbar = () => { return ( <> - + ); }; const NavItem = ({ icon, to, children }) => { return ( - + + + {icon && {icon}} {children} @@ -136,23 +146,18 @@ const NavItem = ({ icon, to, children }) => { }; const MobileNavItem = ({ to, children }) => { - let classname = "z-[2] text-gray-300 text-center hover:text-white block px-3 py-2 rounded-md text-xl font-medium "; + const classname = "z-[2] text-gray-300 text-center hover:text-white block px-3 py-2 rounded-md text-xl font-medium"; return ( - + {children} ); }; -// Define your icon components here const IconHome = () => 🏠; const IconAbout = () => ℹī¸; const IconNews = () => 📰; const IconRateUs = () => ⭐; +const IconCanteen = () => đŸĨ—; - -export default Navbar; - +export default Navbar; \ No newline at end of file diff --git a/src/index.js b/src/index.js index bda112c..9582310 100644 --- a/src/index.js +++ b/src/index.js @@ -12,6 +12,7 @@ root.render( + ); diff --git a/src/pages/AddFoodItem.jsx b/src/pages/AddFoodItem.jsx index 7e25280..a897bd2 100644 --- a/src/pages/AddFoodItem.jsx +++ b/src/pages/AddFoodItem.jsx @@ -35,13 +35,13 @@ function AddFoodItem() { switch (mealType) { case "Breakfast": - apiUrl = `http://localhost:8000/api/v1/${canteenId}/breakfast/add`; + apiUrl = `${process.env.REACT_APP_BASE_URL}/${canteenId}/breakfast/add`; break; case "Lunch": - apiUrl = `http://localhost:8000/api/v1/${canteenId}/lunch/add`; + apiUrl = `${process.env.REACT_APP_BASE_URL}/${canteenId}/lunch/add`; break; case "Dinner": - apiUrl = `http://localhost:8000/api/v1/${canteenId}/dinner/add`; + apiUrl = `${process.env.REACT_APP_BASE_URL}/${canteenId}/dinner/add`; break; default: toast.error("Please select a meal type."); diff --git a/src/pages/EditProfile.jsx b/src/pages/EditProfile.jsx index aa5192d..fcf7445 100644 --- a/src/pages/EditProfile.jsx +++ b/src/pages/EditProfile.jsx @@ -17,7 +17,7 @@ const EditProfile = () => { const fetchCanteenData = async () => { try { - const getCanteen = await fetch(`http://localhost:8000/api/v1/getcanteen`, { + const getCanteen = await fetch(`${process.env.REACT_APP_BASE_URL}/getcanteen`, { method: 'GET', headers: { 'Content-Type': 'application/json', @@ -69,7 +69,7 @@ const EditProfile = () => { const handleSubmit = async (e) => { e.preventDefault(); try { - const response = await fetch(`http://localhost:8000/api/v1/${_id}/update`, { + const response = await fetch(`${process.env.REACT_APP_BASE_URL}/${_id}/update`, { method: 'PUT', headers: { 'Content-Type': 'application/json', diff --git a/src/pages/Foodlist.jsx b/src/pages/Foodlist.jsx index cda6993..0ae3d4e 100644 --- a/src/pages/Foodlist.jsx +++ b/src/pages/Foodlist.jsx @@ -22,7 +22,7 @@ const Foodlist = () => { try { setLoading(true); const response = await fetch( - `http://localhost:8000/api/v1/${_id}/${mealType}`, + `${process.env.REACT_APP_BASE_URL}/${_id}/${mealType}`, { method: "GET", headers: { @@ -48,7 +48,7 @@ const Foodlist = () => { } await axios.delete( - `http://localhost:8000/api/v1/${_id}/${mealType}/remove`, + `${process.env.REACT_APP_BASE_URL}/${_id}/${mealType}/remove`, { headers: { "Content-Type": "application/json", @@ -84,7 +84,7 @@ const Foodlist = () => { } await axios.put( - `http://localhost:8000/api/v1/${_id}/${currentDish.mealType}/updateitem`, + `${process.env.REACT_APP_BASE_URL}/${_id}/${currentDish.mealType}/updateitem`, { dishId: currentDish._id, dish: updatedDish, diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx index 33ae514..95f0de0 100644 --- a/src/pages/Home.jsx +++ b/src/pages/Home.jsx @@ -1,6 +1,5 @@ - - import React, { useState, useEffect } from "react"; +import { AiOutlineSearch } from "react-icons/ai"; // Importing the search icon import axios from "axios"; import { Link } from "react-router-dom"; import { toast } from "react-hot-toast"; @@ -8,7 +7,6 @@ import Navbar from "../components/Navbar"; import CanteenList from "../components/CanteenList"; import Loader from "../components/Loader/Loader"; import Footer from "../components/Footer"; - import FloatBtn from "../components/FloatBtn/FloatBtn"; import { useAuth } from "../authContext"; import { useNavigate } from "react-router-dom"; @@ -16,92 +14,81 @@ import { useNavigate } from "react-router-dom"; function Home() { const navigate = useNavigate(); const { isAuthenticated } = useAuth(); - const [canteenData , setCanteenData] = useState(); + const [canteenData, setCanteenData] = useState([]); const [loading, setLoading] = useState(false); + const [filteredCanteenData, setFilteredCanteenData] = useState([]); + const [searchTerm, setSearchTerm] = useState(""); - useEffect(() => { - if(!isAuthenticated){ - navigate('/') - } - }, []) - - const getCanteenData = async () =>{ - try{ + const getCanteenData = async () => { + try { setLoading(true); const getCanteen = await fetch( - `${process.env.REACT_APP_BASE_URL}/api/v1/getcanteen`, + `${process.env.REACT_APP_BASE_URL}/getcanteen`, { - method : "GET", - headers :{ - "Content-Type" : "application/json", + method: "GET", + headers: { + "Content-Type": "application/json", }, } ); const res = await getCanteen.json(); setCanteenData(res); - } - catch(error){ - console.error(error); - } - finally { + setFilteredCanteenData(res); // Initialize filtered data with all canteens + } catch (error) { + console.error(error); + } finally { setLoading(false); } - }; - useEffect(()=>{ + useEffect(() => { getCanteenData(); - },[]) + }, []); + useEffect(() => { + if (!canteenData?.data) return; // Ensure canteenData.data exists + // Filter canteenData based on searchTerm + if (searchTerm.trim() === "") { + setFilteredCanteenData(canteenData); // If search term is empty, show all canteens + } else { + const filteredData = canteenData.data.filter((canteen) => + canteen.name.toLowerCase().includes(searchTerm.toLowerCase()) + ); + setFilteredCanteenData({ data: filteredData }); + } + }, [searchTerm, canteenData]); + const handleSearch = (event) => { + setSearchTerm(event.target.value); + }; return ( <> - { - loading ? ( - - ):( -
- -
- + {loading ? ( + + ) : ( +
+ +
+
+ + +
+
+
+ +
+
-
-
- ) - } + )} - - ); } export default Home; - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx index bc4c187..c0a2f70 100644 --- a/src/pages/Login.jsx +++ b/src/pages/Login.jsx @@ -4,22 +4,29 @@ import { Link, useNavigate } from "react-router-dom"; import { toast } from "react-hot-toast"; import axios from "axios"; import logo from "../assets/logo2.png"; -import Loader from "../components/Loader/Loader"; +import Loader from "../components/Loader/Loader"; // Ensure this path is correct import { useAuth } from "../authContext"; function Login() { + const [formData, setFormData] = useState({ email: "", accountType: "", password: "", }); - const { checkAuthentication } = useAuth(); + const { isAuthenticated, login} = useAuth(); const [showPassword, setShowPassword] = useState(false); const [loading, setLoading] = useState(false); const [rememberMe, setRememberMe] = useState(false); const navigate = useNavigate(); + + useEffect(() => { + if(isAuthenticated){ + navigate('/home'); + } + }) useEffect(() => { const storedEmail = localStorage.getItem("rememberedEmail"); @@ -41,33 +48,46 @@ function Login() { function rememberMeHandler(event) { setRememberMe(event.target.checked); + if (!event.target.checked) { + localStorage.removeItem("rememberedEmail"); + } } async function submitHandler(event) { event.preventDefault(); - setLoading(true); - + + const apiUrl = + formData.accountType === "User" + ? `${process.env.REACT_APP_BASE_URL}/studentLogin` + : `${process.env.REACT_APP_BASE_URL}/canteenLogin`; + try { - const apiUrl = - formData.accountType === "User" - ? `${process.env.REACT_APP_BASE_URL}/studentLogin` - : `${process.env.REACT_APP_BASE_URL}/canteenLogin`; - const response = await axios.post(apiUrl, formData); - toast.success("User Logged in"); + + if (rememberMe) { + localStorage.setItem("rememberedEmail", formData.email); + } else { + localStorage.removeItem("rememberedEmail"); + } + if (formData.accountType === "User") { + navigate("/home"); + localStorage.setItem("token", response.data.token); } else { + localStorage.setItem("canteenId", response.data.cantId); + localStorage.setItem("token", response.data.token); + navigate(`/section/${response.data.cantId}`); + } + navigate("/home"); } catch (error) { + console.error(error); toast.error("Failed to login"); - } finally { - setLoading(false); } } - return ( <> {loading ? ( @@ -94,20 +114,13 @@ function Login() {
-
-

- Hello Again! -

-

- Welcome Back -

+ +

Hello Again!

+

Welcome Back

+ className="mt-1 p-2 w-full border rounded-2xl border-b-3 border-customBlue"> @@ -133,7 +145,7 @@ function Login() {
setShowPassword((prev) => !prev)} > - {showPassword ? ( - - ) : ( - - )} + {showPassword ? : }
@@ -185,4 +193,4 @@ function Login() { ); } -export default Login; +export default Login; \ No newline at end of file diff --git a/src/pages/MenuPage.jsx b/src/pages/MenuPage.jsx index 4a20eb9..dc267d3 100644 --- a/src/pages/MenuPage.jsx +++ b/src/pages/MenuPage.jsx @@ -20,7 +20,7 @@ function MenuPage() { try { setLoading(true); const getBreakfast = await fetch( - `http://localhost:8000/api/v1/${_id}/breakfast`, + `${process.env.REACT_APP_BASE_URL}/${_id}/breakfast`, { method: "GET", headers: { @@ -42,7 +42,7 @@ function MenuPage() { try { setLoading(true); const getLunch = await fetch( - `http://localhost:8000/api/v1/${_id}/lunch`, + `${process.env.REACT_APP_BASE_URL}/${_id}/lunch`, { method: "GET", headers: { @@ -64,7 +64,7 @@ function MenuPage() { try { setLoading(true); const getDinner = await fetch( - `http://localhost:8000/api/v1/${_id}/dinner`, + `${process.env.REACT_APP_BASE_URL}/${_id}/dinner`, { method: "GET", headers: { diff --git a/src/pages/Rateus.jsx b/src/pages/Rateus.jsx index de470d5..14ac6d8 100644 --- a/src/pages/Rateus.jsx +++ b/src/pages/Rateus.jsx @@ -1,18 +1,15 @@ -import React, { useState, useContext } from "react"; +import React, { useState } from 'react'; +import { ToastContainer, toast } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; import Footer from "../components/Footer"; import Navbar from "../components/Navbar"; -import { ToastContainer, toast } from "react-toastify"; -import "react-toastify/dist/ReactToastify.css"; -import { ThemeContext } from "../themeContext"; const RateUs = () => { - const [rating, setRating] = useState(0); - const [feedback, setFeedback] = useState(""); - const { theme } = useContext(ThemeContext); + const [rating, setRating] = useState(''); + const [feedback, setFeedback] = useState(''); - const handleRatingChange = (value) => { - setRating(value); - updateStars(value); + const handleRatingClick = (emoji) => { + setRating(emoji); }; const handleFeedbackChange = (event) => { @@ -20,84 +17,46 @@ const RateUs = () => { }; const handleSubmit = () => { - if (rating > 0 && feedback.trim() !== "") { - toast.success("Thanks for Your Feedback :)"); - console.log("Rating:", rating); - console.log("Feedback:", feedback); - setRating(0); - setFeedback(""); + if (rating && feedback.trim() !== '') { + toast.success('Thank you for your feedback :)'); } else { - toast.error("Please Fill All The Details :("); + toast.error('Please fill all the details :('); } }; - const updateStars = (rating) => { - const stars = document.querySelectorAll('.stars input[type="radio"]'); - stars.forEach((star, index) => { - if (index < rating) { - star.checked = true; - star.nextElementSibling.style.color = "#ffcf00"; - } else { - star.checked = false; - star.nextElementSibling.style.color = "#ccc"; - } - }); - }; - return ( -
-

- Rate Our Website -

-
- {[1, 2, 3, 4, 5].map((value) => ( - - handleRatingChange(value)} - className="hidden" - /> - - +

Rate Our Website

+
+ {['đŸ˜ĸ', '😡', '😐', '😊', '🤩'].map((emoji) => ( + handleRatingClick(emoji)} + > + {emoji} + ))}
-
diff --git a/src/pages/SectionPage.jsx b/src/pages/SectionPage.jsx index a9ed459..97bd0bc 100644 --- a/src/pages/SectionPage.jsx +++ b/src/pages/SectionPage.jsx @@ -25,7 +25,7 @@ const SectionPage = () => { try { setLoading(true); const getCanteen = await fetch( - `http://localhost:8000/api/v1/getcanteen`, + `${process.env.REACT_APP_BASE_URL}/getcanteen`, { method: "GET", headers: { diff --git a/tailwind.config.js b/tailwind.config.js index 35de8a8..2ef1f4e 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -10,13 +10,19 @@ module.exports = { extend: { colors:{ customColor: '#484b6a', + customBlue: '#151586', aliceblue: '#f0f8ff', cadetblue: '#5f9ea0', - lightcadetblue: '#84bbbd' + lightcadetblue: '#84bbbd', + }, boxShadow: { green: '0 0 4px rgba(0, 255, 0, 0.5)', }, + borderWidth: { + '3': '4px', + }, + }, }, plugins: [],