diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..7683ef55 Binary files /dev/null and b/.DS_Store differ diff --git a/cal_predict/cal_api.py b/cal_predict/cal_api.py new file mode 100755 index 00000000..35251877 --- /dev/null +++ b/cal_predict/cal_api.py @@ -0,0 +1,37 @@ +import pickle +import pandas as pd +from fastapi import FastAPI +from pydantic import BaseModel + +# Initialize FastAPI app +app = FastAPI() + +# Load model once at startup +def load_model(): + path = 'calorie_model.pkl' + with open(path, 'rb') as f: + return pickle.load(f) + +model = load_model() + +# Define request model +class InputData(BaseModel): + Dish_Weight: float + Protein: float + Fat: float + Carbohydrates: float + Fiber: float + +@app.get("/") +def home(): + return {"message": "Calorie Prediction API is running!"} + +@app.post("/predict") +def predict_calories(data: InputData): + df = pd.DataFrame([data.model_dump()]) # Use model_dump() instead of .dict() + pred = model.predict(df) + return {'Predicted Calories': pred[0]} + +if __name__ == '__main__': + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) diff --git a/cal_predict/calorie_model.pkl b/cal_predict/calorie_model.pkl new file mode 100755 index 00000000..52808ead Binary files /dev/null and b/cal_predict/calorie_model.pkl differ diff --git a/cal_predict/dockerfile b/cal_predict/dockerfile new file mode 100755 index 00000000..235c24d8 --- /dev/null +++ b/cal_predict/dockerfile @@ -0,0 +1,17 @@ +# Use an official Python runtime as a parent image +FROM python:3.10.0 + +# Set the working directory in the container +WORKDIR /app + +# Copy the current directory contents into the container at /app +COPY . /app + +# Install dependencies +RUN pip install --no-cache-dir -r requirements.txt + +# Expose the FastAPI default port +EXPOSE 8000 + +# Run the FastAPI application +CMD ["python", "cal_api.py"] diff --git a/cal_predict/requirements.txt b/cal_predict/requirements.txt new file mode 100755 index 00000000..c7fd83c4 --- /dev/null +++ b/cal_predict/requirements.txt @@ -0,0 +1,6 @@ +fastapi +uvicorn +numpy +pandas +pydantic +scikit-learn \ No newline at end of file diff --git a/fit-bite-web/.eslintrc.js b/fit-bite-web/.eslintrc.js new file mode 100644 index 00000000..620d5211 --- /dev/null +++ b/fit-bite-web/.eslintrc.js @@ -0,0 +1,41 @@ +module.exports = { + env: { + browser:true, + node: true, + es2021: true, + }, + extends: ["eslint:recommended", "plugin:react/recommended", "next/core-web-vitals"], + overrides: [ + { + env: { + node: true, + }, + files: [".eslintrc.{js,cjs}"], + parserOptions: { + sourceType: "script", + }, + }, + { + files: ["*.mjs"], + parserOptions: { + sourceType: "module", + }, + }, + ], + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + }, + plugins: [ + "react"], + rules: { + "react/prop-types": "off", + "react/react-in-jsx-scope": "off", + // allow jsx syntax in js files (for next.js project) + "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],}, + settings: { + react: { + version: "detect", + }, + }, +}; diff --git a/fit-bite-web/.eslintrc.json b/fit-bite-web/.eslintrc.json new file mode 100644 index 00000000..76a186db --- /dev/null +++ b/fit-bite-web/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": ["next/core-web-vitals", "plugin:react-hooks/recommended"] +} diff --git a/fit-bite-web/.gitignore b/fit-bite-web/.gitignore new file mode 100644 index 00000000..45c1abce --- /dev/null +++ b/fit-bite-web/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local +.env + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/fit-bite-web/README.md b/fit-bite-web/README.md new file mode 100644 index 00000000..e5f733ef --- /dev/null +++ b/fit-bite-web/README.md @@ -0,0 +1,34 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/fit-bite-web/app/Checkout/page.jsx b/fit-bite-web/app/Checkout/page.jsx new file mode 100644 index 00000000..8c24cf46 --- /dev/null +++ b/fit-bite-web/app/Checkout/page.jsx @@ -0,0 +1,285 @@ +"use client"; +import React from "react"; + +import "../globals.css"; +import { useState, useEffect } from "react"; +import { addtoCart, removeFromCart } from "../functions/cart"; +import { SubTotal } from "../functions/subtotal"; +import Head from "next/head"; + +import { ToastContainer, toast } from "react-toastify"; +import { useRouter } from "next/navigation"; +import getStripe from "@/lib/getStripe"; +import { UserContext } from "../Context/UserProvider"; +import Image from "next/image"; + +function Page() { + const router = useRouter(); + const [cartData, setcartData] = useState({}); + + const [count, setCount] = useState(0); + const { loggedIn, userData, contextLoading, setCountAgain, login } = + React.useContext(UserContext); + + let userId = userData._id; + + const initiatePayment = async () => { + + if (contextLoading == false) { + login() + if (!loggedIn) { + login() + router.replace("/login"); + } else { + toast.loading("Redirecting..."); + const stripe = await getStripe(); + try { + const response = await fetch("api/stripe", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ cartData, userId }), + }); + + let res = await response.json(); + // console.log(res); + stripe.redirectToCheckout({ + sessionId: res.id, + }); + } catch (e) { + console.log(e); + } + } + } + }; + + useEffect(() => { + if (contextLoading == false) { + login() + } + try { + if (localStorage.getItem("cart")) { + setcartData(JSON.parse(localStorage.getItem("cart"))); + saveCart(JSON.parse(localStorage.getItem("cart"))); + } + } catch (error) { + console.error(error); + localStorage.removeItem("cart"); + } + }, [contextLoading, loggedIn, userData, count, login]); + + const handleClick = () => { + setCount((prev) => prev + 1); + setCountAgain((prev) => prev + 1); + }; + + const saveCart = (myCart) => { + localStorage.setItem("cart", JSON.stringify(myCart)); + }; + // let Subtotal; + // { + // typeof window !== "undefined" && (Subtotal = SubTotal()); + // } + + function CartAmountToggle({ k, cartData }) { + return ( +
+ + + + + + {cartData[k].qty} + +
+ ); + } + + return ( +
+ + {/* {!loggedIn ? ( +
+ Loading... +
+ ) : ( + <> */} +
+ + + + + + + + + + + {Object.keys(cartData).map((k) => { + return ( + + + + + + + ); + })} + +
+ Product + + Qty + + Price +
+ + {cartData[k].productName}{" "} +

+ {" "} + {cartData[k].productName} +

+
+
+ + + ${cartData[k].price?.toFixed(2)} +
+
+
+
+

Subtotal

+

+ {" "} + + {" "} + {" "} + {" "} + {" "} + {" "} + +

+ {typeof window !== "undefined" && ( +

${SubTotal()}

+ )} +
+ +
+

Order Now

+

+ {" "} + + {" "} + {" "} + {" "} + +

+
+
+ {/* + )} */} +
+ ); +} + +export default Page; diff --git a/fit-bite-web/app/Context/ChatContext.js b/fit-bite-web/app/Context/ChatContext.js new file mode 100644 index 00000000..bcad73f3 --- /dev/null +++ b/fit-bite-web/app/Context/ChatContext.js @@ -0,0 +1,22 @@ +"use client"; +import { createContext, useContext, useState } from "react"; + +const ChatContext = createContext(); + +export const ChatProvider = ({ children }) => { + const [messages, setMessages] = useState([]); + + const addMessage = (text, sender) => { + setMessages((prev) => [...prev, { id: Date.now(), text, sender }]); + }; + + return ( + + {children} + + ); +}; + +export const useChat = () => { + return useContext(ChatContext); +}; diff --git a/fit-bite-web/app/Context/SidebarContext.js b/fit-bite-web/app/Context/SidebarContext.js new file mode 100644 index 00000000..1f81fc34 --- /dev/null +++ b/fit-bite-web/app/Context/SidebarContext.js @@ -0,0 +1,84 @@ +"use client"; +import React, { createContext, useContext, useState, useEffect } from "react"; + +// type SidebarContextType = { +// isExpanded: boolean; +// isMobileOpen: boolean; +// isHovered: boolean; +// activeItem: string | null; +// openSubmenu: string | null; +// toggleSidebar: () => void; +// toggleMobileSidebar: () => void; +// setIsHovered: (isHovered: boolean) => void; +// setActiveItem: (item: string | null) => void; +// toggleSubmenu: (item: string) => void; +// }; + +const SidebarContext = createContext(undefined); + +export const useSidebar = () => { + const context = useContext(SidebarContext); + if (!context) { + throw new Error("useSidebar must be used within a SidebarProvider"); + } + return context; +}; + +export const SidebarProvider= ({ + children, +}) => { + const [isExpanded, setIsExpanded] = useState(true); + const [isMobileOpen, setIsMobileOpen] = useState(false); + const [isMobile, setIsMobile] = useState(false); + const [isHovered, setIsHovered] = useState(false); + const [activeItem, setActiveItem] = useState(null); + const [openSubmenu, setOpenSubmenu] = useState(null); + + useEffect(() => { + const handleResize = () => { + const mobile = window.innerWidth < 768; + setIsMobile(mobile); + if (!mobile) { + setIsMobileOpen(false); + } + }; + + handleResize(); + window.addEventListener("resize", handleResize); + + return () => { + window.removeEventListener("resize", handleResize); + }; + }, []); + + const toggleSidebar = () => { + setIsExpanded((prev) => !prev); + }; + + const toggleMobileSidebar = () => { + setIsMobileOpen((prev) => !prev); + }; + + const toggleSubmenu = (item) => { + setOpenSubmenu((prev) => (prev === item ? null : item)); + }; + + return ( + + {children} + + ); +}; diff --git a/fit-bite-web/app/Context/ThemeContext.js b/fit-bite-web/app/Context/ThemeContext.js new file mode 100644 index 00000000..42fd8ba6 --- /dev/null +++ b/fit-bite-web/app/Context/ThemeContext.js @@ -0,0 +1,58 @@ +"use client"; + + +import { createContext, useState, useContext, useEffect } from "react"; + +// type Theme = "light" | "dark"; + +// type ThemeContextType = { +// theme: Theme; +// toggleTheme: () => void; +// }; + +const ThemeContext = createContext(undefined); + +export const ThemeProvider = ({ + children, +}) => { + const [theme, setTheme] = useState("light"); + const [isInitialized, setIsInitialized] = useState(false); + + useEffect(() => { + // This code will only run on the client side + const savedTheme = localStorage.getItem("theme"); + const initialTheme = savedTheme || "light"; // Default to light theme + + setTheme(initialTheme); + setIsInitialized(true); + }, []); + + useEffect(() => { + if (isInitialized) { + localStorage.setItem("theme", theme); + if (theme === "dark") { + document.documentElement.classList.add("dark"); + } else { + document.documentElement.classList.remove("dark"); + } + } + }, [theme, isInitialized]); + + const toggleTheme = () => { + setTheme((prevTheme) => (prevTheme === "light" ? "dark" : "light")); + }; + + return ( + + {children} + + ); +}; + +export const useTheme = () => { + const context = useContext(ThemeContext); + if (context === undefined) { + throw new Error("useTheme must be used within a ThemeProvider"); + } + return context; +}; diff --git a/fit-bite-web/app/Context/UserProvider.js b/fit-bite-web/app/Context/UserProvider.js new file mode 100644 index 00000000..edac7b3d --- /dev/null +++ b/fit-bite-web/app/Context/UserProvider.js @@ -0,0 +1,200 @@ +"use client"; +import React, { useCallback, useEffect, useState } from "react"; +var jwt = require("jsonwebtoken"); +const UserContext = React.createContext({ + loggedIn: true, + setLoggedIn: ()=>{}, + adminloggedIn: true, + setAdminLoggedIn: ()=>{}, + admin: null, + user: null, + contextLoading: true, + userData: "", + setCountAgain: () => {}, + cartData: 0, + // cart: null, + login: () => {}, + logout: () => {}, + adminlogin: () => {}, + adminlogout: () => {}, +}); +const UserProvider = ({ children }) => { + const [loggedIn, setLoggedIn] = useState(true); + const [adminloggedIn, setAdminLoggedIn] = useState(false); + const [contextLoading, setContextLoading] = useState(true); + const [user, setUser] = useState({ value: null }); + const [admin, setAdmin] = useState({ value: null }); + const [userData, setUserData] = useState({}); + const [countAgain, setCountAgain] = useState(0); + const [cartData, setcartData] = useState(0); + + + + + const getUser = useCallback(async(token)=> { + // if (loggedIn) { + if (token) { + setUser({ value: token }); + try { + let decodedToken = jwt.decode(token); + // console.log("decoded token", decodedToken); + return decodedToken; + } catch (e) { + console.log("token verification failed"); + setContextLoading(false); + } + } else { + + console.log("token not found"); + logout() + setContextLoading(false); + } + }, []) + const loginUser = useCallback(async(decodedToken) => { + try { + if (decodedToken) { + // console.log("token found", decodedToken.Email); + const response = await fetch( + `/api/login/?userEmail=${decodedToken.Email}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + } + ); + let responseData = await response.json(); + // console.log("response data", responseData.result[0].Email); + if (response.status === 200) { + setUserData(responseData.result[0]); + setLoggedIn(true); + setContextLoading(false); + // if (decodedToken.Email === responseData.result[0].Email) { + // console.log("User found with that token. Verified"); + // setLoggedIn(true); + // // console.log("token found"); + // setContextLoading(false); + // } + } else { + console.log("User not found | Login Again"); + logout(); + + setContextLoading(false); + } + } + } catch (e) { + console.log("Error", e.message); + } + },[]) + // async function verifyToken(decodedToken) { + // if (decodedToken) { + // if (decodedToken.Email) { + // console.log("Token seems to be correct"); + // setLoggedIn(true); + // // console.log("token found"); + // setContextLoading(false); + // } else { + // logout(); + // setContextLoading(false); + // } + // if (decodedToken.Email === userEmail) { + // console.log("token verified successfully"); + // setLoggedIn(true); + // // console.log("token found"); + // setContextLoading(false); + // } else { + // // If no token in local storage, set loggedIn to false + // console.log("Will implement verification feature in the future"); + // setLoggedIn(true); + // setContextLoading(false); + // } + // } + // } +// const stableLoginUser = useCallback(loginUser, []); +// const stableGetUser = useCallback(getUser, []); + useEffect(() => { + if (window !== undefined) { + const token = localStorage.getItem("token"); + + const adminToken = localStorage.getItem("adminToken") || null; + + // console.log({ adminToken }); + if (adminToken) { + setAdmin({ value: adminToken }); + // console.log("admin logged in"); + setAdminLoggedIn(true); + setContextLoading(false); + } else { + // console.log("No admin Token found"); + setAdminLoggedIn(false); + setContextLoading(false); + } + getUser(token, adminToken).then((user) => { + loginUser(user); + }); + } + }, [loggedIn, adminloggedIn, loginUser, getUser]); + useEffect(() => { + // console.log("cart") + let keys = Object.keys(JSON.parse(localStorage.getItem("cart")) || {}); + if (keys.length > 0) { + setcartData( + Object.values(JSON.parse(localStorage.getItem("cart"))).reduce( + (acc, item) => acc + item.qty, + 0 + ) + ); + } + // let cartDataLength = keys.length; + // console.log({cartDataLength}); + // setcartData(cartDataLength); + }, [countAgain]); + + const login = useCallback(() => { + if (user.value) { + setLoggedIn(true); + // console.log("Token exists, user logged in"); + } + },[user.value]); + const adminlogin = () => { + if (admin.value != null) { + setAdminLoggedIn(true); + // console.log("Admin Token exists, Admin logged in"); + } else { + adminlogout(); + } + }; + + const adminlogout = () => { + setAdminLoggedIn(false); + localStorage.removeItem("adminToken"); + }; + const logout = () => { + localStorage.removeItem("token"); + setLoggedIn(false); + }; + + return ( + + {children} + + ); +}; +export { UserContext, UserProvider }; diff --git a/fit-bite-web/app/account/[id]/page.jsx b/fit-bite-web/app/account/[id]/page.jsx new file mode 100644 index 00000000..85870c0c --- /dev/null +++ b/fit-bite-web/app/account/[id]/page.jsx @@ -0,0 +1,371 @@ +"use client"; +import React, { useEffect, useState } from "react"; +import { UserContext } from "@/app/Context/UserProvider"; +import { menuData } from "../../dishdata"; +import Link from "next/link"; +import { addtoCart } from "@/app/functions/cart"; +import { useRouter } from "next/navigation"; +import { ToastContainer, toast } from "react-toastify"; +import Image from "next/image"; +import "react-toastify/dist/ReactToastify.css"; +//when you place a order the dishes of different restaurant should be in order dashboard for those restaurants only +function page({ params }) { + const [slug, setSlug] = useState(params.id); + const [dish, setDish] = useState({}); + const { setCountAgain } = React?.useContext(UserContext); + const [itemData, setitemData] = useState([]); + const [submitted, setSubmitted] = useState(false); + const [errors, setErrors] = useState({ }); + + const [isChecked, setIsChecked] = useState({ checked: false }); + const [formData, setFormData] = useState({ + name: "", + Description: "", + Price: 0, + Carbohydrates:0, + Protein:0, + Fat: 0, + Calories:0, + restaurantId:slug, + type:"" + }); + let router = useRouter(); + // async function render() { + + // try { + // const response = await fetch(`/api/item?id=${slug}&dish=desserts`, { + // method: "GET", + // }); + // if (response) { + // setitemData(res.result); + // // console.log(res.result, "response orders"); + // } + // }catch (err) { + // console.log(err) + // } + // } + const handleInputChange = (e) => { + let name = e.target.name; + let value = e.target.value; + + setFormData((prevData) => ({ + ...prevData, + [name]: value, + restaurantId: slug, + image: "https://res.cloudinary.com/dkv7cimyy/image/upload/v1740123654/hack2/k7y0mnlaufgojdj3g47t.jpg", + + })); +} + + +// Handle form submission + const handleSubmit = async (e) => { + e.preventDefault(); + + // setActiveStep(2) + // Convert form data to JSON + // const jsonData = JSON.stringify(formData, null, 2); + setErrors({}) + const validationErrors = {}; + + if (!formData.name) { + validationErrors.name = "Please enter product name."; + } + + if (!formData.Description) { + validationErrors.Description = "Description is required."; + } + + + if (Object.keys(validationErrors).length > 0) { + setErrors(validationErrors); + } + + // { "username": "Abhi", + // "ContactNumber": 679234567890, + // "Email": "abhi2y@email.com", + // "Password": "abhi", + // "Address": "PNT Colony" + // } + // Log the JSON data (you can send it to a server or save it as needed) + + // Convert form data to JSON + const jsonDataFinal = JSON.stringify(formData, null, 2); + console.log("String form Data Final", jsonDataFinal); + // console.log("String form Data Final", jsonDataFinal); + if (Object.keys(validationErrors).length == 0) { + try { + const response = await fetch(`/api/add-product`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: jsonDataFinal, + }); + + if (response.status === 200) { + setSubmitted(true); + // console.log("Form data submitted successfully", formData); + toast.success("😎 Product created successfully!", { + position: "bottom-center", + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + setTimeout(() => { + router.replace("/dishes"); + }, 3000); + setFormData({ + username: "", + ContactNumber: 0, + Address: "", + Email: "", + Password: "", + }); + } else { + console.error("Form submission failed"); + } + } catch (error) { + console.error("Error:", error); + }} + + + // console.log(formData); + }; + + return ( +
+

Add Product

+
+ +
+ Logo + {/*

+ Zomato +

*/} +
+
+
+
+ + + {errors.name && ( +

{errors.name}

+ )} + {/*

+ Please enter your desired Username +

*/} +
+ +
+ + + {errors.Description && ( +

{errors.Description}

+ )} + {/*

+ Please choose a password. +

*/} +
+ + + +
+ + + {errors.Price && ( +

{errors.Price}

+ )} + {/*

+ Please choose a password. +

*/} +
+
+ + + {errors.Protein && ( +

{errors.Protein}

+ )} + {/*

+ Please choose a password. +

*/} + + + + {errors.Calories && ( +

{errors.Calories}

+ )} + {/*

+ Please choose a password. +

*/} +
+
+ + + + {errors.Carbohydrates && ( +

{errors.Carbohydrates}

+ )} + {/*

+ Please choose a password. +

*/} + + + + {errors.Fat && ( +

{errors.Fat}

+ )} + {/*

+ Please choose a password. +

*/} +
+ + +
+ +

+ ©2023 Fit-Bite. All rights reserved. +

+
+
+
+ ); +} + +export default page; diff --git a/fit-bite-web/app/account/page.jsx b/fit-bite-web/app/account/page.jsx new file mode 100644 index 00000000..6ec8d6bb --- /dev/null +++ b/fit-bite-web/app/account/page.jsx @@ -0,0 +1,576 @@ +"use client"; +import React from "react"; +import { UserContext } from "../Context/UserProvider"; +import Image from "next/image"; +import { useState, useEffect, useCallback } from "react"; + +import "../globals.css"; +import { ToastContainer, toast } from "react-toastify"; +import "react-toastify/dist/ReactToastify.css"; +import { useRouter } from "next/navigation"; + +function Page() { + // console.log(restaurantTypes.items[0]) + const { loggedIn, userData, contextLoading } = React.useContext(UserContext); + + let [toggleEdit, SetToggleEdit] = useState(false); + let [toggleRestaurants, SetToggleRestaurants] = useState(false); + const [errors, setErrors] = useState({ default: "default is required" }); + const [submitted, setSubmitted] = useState(false); + const [restaurantData, setRestaurantData] = useState({}); + + const router = useRouter(); + + // console.log(userData?.username, "above form"); + const [formData, setFormData] = useState({ + username: "", + ContactNumber: 0, + Address: "", + Email: "", + Password: "", + }); + + // Handle input changes and update state + const handleInputChange = (e) => { + let name = e.target.name; + let value = e.target.value; + + setFormData((prevData) => ({ + ...prevData, + [name]: value, + })); + //Validation Logic Here + const validationErrors = {}; + + if (formData.username.length > 0 && formData.username.length < 4) { + validationErrors.username = + "Username should have more than 4 characters."; + } + if (formData.Password.length > 0 && formData.Password.length < 5) { + validationErrors.Password = "Password should be atleast of 5 characters."; + } + console.log( + formData.Password.includes(formData.username), + formData.Password.length > 0 + ); + if ( + formData.Password.length > 0 && + formData.Password.includes(formData.username) + ) { + validationErrors.Password = "Username should not be used as a password."; + } + if (formData.Email.length > 1 && formData.Email.length < 10) { + validationErrors.Email = "Please enter correct E-mail id"; + } + if ( + formData.ContactNumber.length > 0 && + formData.ContactNumber.length < 10 && + formData.ContactNumber.length > 10 + ) { + validationErrors.ContactNumber = "Please enter correct Contact Number"; + } + + if (Object.keys(validationErrors).length > 0) { + setErrors(validationErrors); + } else { + setErrors(validationErrors); + } + }; + const isFormValid = () => { + // Check if there are any errors in the errors state + return Object.values(errors).every((error) => error === ""); + }; + // Handle form submission + const handleSubmit = async (id, e) => { + e.preventDefault(); + + const validationErrors = {}; + + if (formData.username.length > 0 && formData.username.length < 4) { + validationErrors.username = + "Username should have more than 4 characters."; + } + + if (formData.Password.length > 0 && formData.Password.length < 5) { + validationErrors.Password = "Password should be atleast of 5 characters."; + } + + if (formData.Password.includes(formData.username)) { + validationErrors.Password = "Username should not be used as a password."; + } + if (formData.Email.length > 1 && formData.Email.length < 10) { + validationErrors.Email = "Please enter correct E-mail id"; + } + if ( + formData.ContactNumber.length > 0 && + formData.ContactNumber.length < 10 && + formData.ContactNumber.length > 10 + ) { + validationErrors.ContactNumber = "Please enter correct Contact Number"; + } + + if (Object.keys(validationErrors).length > 0) { + setErrors(validationErrors); + } + + // { "username": "Abhi", + // "ContactNumber": 679234567890, + // "Email": "abhi2y@email.com", + // "Password": "abhi", + // "Address": "California" + // } + // Log the JSON data (you can send it to a server or save it as needed) + + // Convert form data to JSON + const jsonDataFinal = JSON.stringify(formData, null, 2); + // console.log("String form Data Final", jsonDataFinal); + try { + const response = await fetch(`/api/account/${id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: jsonDataFinal, + }); + let res = await response.json(); + if (response.status === 200) { + toast.success("😎 AccountInfo updated successfully!", { + position: "bottom-center", + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + // console.log(res.token) + if (res.token) { + localStorage.setItem("token", res.token); + } + setSubmitted(true); + console.log("Account Information Successfully Updated", formData); + setTimeout(() => { + router.push("/"); + }, 3000); + setFormData({ + username: "", + ContactNumber: 0, + Address: "", + Email: "", + Password: "", + }); + } else { + toast.error("👹 Invalid Data! Try Again.", { + position: "bottom-center", + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + console.error("Form submission failed"); + } + } catch (error) { + console.error("Error:", error); + } + // console.log(formData); + }; + // mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true }); + // setTimeout(() => { + // const decoded = jwt.decode(user.value); + + // if (loggedIn) { + // // let token = localStorage.getItem('token') + // console.log(decoded); + // } + // if (decoded.Email) { + + // const user = User.find({ + // Email: decoded.Email, + // }); + // console.log("user", user); + // } + // }, 5000); + const getRestaurant = useCallback(async() =>{ + try { + if (userData._id) { + // console.log("user ID found", userData._id); + const response = await fetch( + `/api/restaurants/?userId=${userData._id}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + } + ); + let responseData = await response.json(); + // console.log("response data", responseData.result[0].Email); + if (response.status === 200) { + // console.log("response status", responseData.result[0].restaurantType.items) + // console.log(responseData.result, "response data result"); + // setRestaurantType(responseData.result[0].restaurantType); + setRestaurantData(responseData.result); + + // if (decodedToken.Email === responseData.result[0].Email) { + // console.log("User found with that token. Verified"); + // setLoggedIn(true); + // // console.log("token found"); + // setContextLoading(false); + // } + } else { + console.log("No restaurants found | Login Again"); + } + } + } catch (e) { + console.log("Error", e.message); + } + },[userData._id]) + useEffect(() => { + if (contextLoading == false) { + if (loggedIn) { + // console.log(userData?.username); + + getRestaurant(); + } + if (!loggedIn) { + console.log("else from orders page is getting triggered"); + + router.push("/login"); + } + } + }, [loggedIn, userData, submitted, contextLoading, getRestaurant, router]); + function editInfo() { + SetToggleEdit(!toggleEdit); + } + function restaurantInfo() { + SetToggleRestaurants(!toggleRestaurants); + } + + return ( +
+ +
+ Loading... +
+ +

Account Information

+
+ + {" "} + {" "} + {" "} + + + 🙅‍♂️ + +
+

+ {userData?.username}, How are you? +

+

+ Email: {userData?.Email} +

+

+ Contact Number:{" "} + {userData?.ContactNumber} +

+

+ Address:{" "} + {userData?.Address} +

+
+
+ {toggleEdit ? ( +
+
{ + handleSubmit(userData._id, e); + }} + action="/api/account" + method="PUT" + className="border + border-purple-400 rounded-xl px-8 pt-6 pb-8 mb-4" + > +
+
+ + + {errors.username && ( +

+ {errors.username} +

+ )} +
+
+ + + {errors.Email && ( +

+ {errors.Email} +

+ )} +
+
+ + + {errors.Password && ( +

+ {errors.Password} +

+ )} +
+
+ + + {errors.ContactNumber && ( +

+ {errors.ContactNumber} +

+ )} +
+ Logo +
+
+ + + {errors.Address && ( +

+ {errors.Address} +

+ )} + {/*

+ Please enter your Delivery Address. +

*/} +
+ +
+
+ ) : ( + "" + )} + + {/* RESTAURANT DATA */} +
restaurantInfo()} + className={`text-gray-600 body-font cursor-pointer p-6 m-8 ml-0 w-fit ${ + restaurantData && restaurantData.length > 0 ? "block" : "hidden" + }`} + > + {!toggleRestaurants ? "View" : "Hide"} Restaurants +
+ {toggleRestaurants ? ( +
+ {restaurantData && + restaurantData.map((item, index) => ( +
router.push(`/account/${item._id}`)} + > +

+ Restaurant Details +

+ {/*

+ Get ready with your menu card 🎉 +

*/} + +

+ Restaurant Name:{" "} + + {item.restaurantName} + {/* {console.log(item.restaurantName, "restaurantName")} */} + +

+

+ Contact Number:{" "} + + {item.restaurantContactNumber} + +

+

+ E-mail:{" "} + + {item.restaurantEmail} + +

+

+ Restaurant Address:{" "} + + {item.restaurantAddress} + +

+

+ Restaurant Latitude:{" "} + {item.restaurantLat} +

+

+ Restaurant Longitude:{" "} + {item.restaurantLng} +

+ {/*
    + {jsonDataObj.map((item) => ( +
  • +

    Name: {item.name}

    +

    Description: {item.description}

    +
  • + ))} +
*/} +

+ RestaurantType +

+ {/* //
*/} + + {/* {restaurantType && restaurantType.items && restaurantType.items.map((item, index) => ( */} + {/*
    */} + {item.restaurantType.items.map((item, index) => ( +
  • + + Name: {item.name} + +

    + Description:{" "} + + {item.description} + +

    +
  • + ))} + {/*
*/} + {/* ))} */} + + {/*

+ Our team will soon reach out to you! +

*/} +
+ ))} +
+ ) : ( + "" + )} +
+
+ ); +} + +export default Page; diff --git a/fit-bite-web/app/add-restaurant/page.jsx b/fit-bite-web/app/add-restaurant/page.jsx new file mode 100644 index 00000000..6150620a --- /dev/null +++ b/fit-bite-web/app/add-restaurant/page.jsx @@ -0,0 +1,348 @@ +"use client"; +import React, { useState, useEffect } from "react"; +import Tabs from "../components/Tabs"; +import RestaurantType from "../components/RestaurantType"; +import { UserContext } from "../Context/UserProvider"; +import useGeoLocation from "./useGeoLocationHook"; +import { useRouter } from "next/navigation"; +import { CldUploadWidget } from 'next-cloudinary'; +function CreateRestaurant() { + const location = useGeoLocation(); + const { loggedIn, contextLoading, userData, login } = + React.useContext(UserContext); + + let router = useRouter(); + + const [formData, setFormData] = useState({ + restaurantName: "", + restaurantContactNumber: 0, + restaurantEmail: "", + restaurantAddress: "", + restaurantLat: location.coordinates?.lat || "Not Available", + restaurantLng: location.coordinates?.lng || "Not Available", + restaurantType: { + items: [ + { + name: "", + description: "", + }, + ], + }, + }); + useEffect(() => { + if (contextLoading == false) { + login(); + setFormData((prevFormData) => ({ + ...prevFormData, + restaurantLat: location.coordinates?.lat || "", + restaurantLng: location.coordinates?.lng || "", + })); + if (!loggedIn) { + login(); + setTimeout(() => { + router.push("/login"); + }, 2000); + } + } + }, [loggedIn, contextLoading, userData, location, login, router]); + + const overlayStyles = { + // position: "absolute", + // top: 0, + // left: 0, + // right:0, + // bottom:0, + // marginLeft:"14px", + // width: "100%", + // height: "100%", + display: "none", + // justifyContent: "center", + // alignItems: "center", + // backgroundColor: "rgba(0, 0, 0, 0.5)", + // zIndex: 9999, + }; + + const [activeStep, setActiveStep] = useState(1); // Declare activeStep state + const [submitted, setSubmitted] = useState(false); + const [errors, setErrors] = useState({}); + //Handling form data + + // let lat = location.coordinates?.lat; + // let lng = location.coordinates?.lng; + + // setFormData((prevData) => ({ + // ...prevData, + // restaurantLat:lat, + + // restaurantLng:lng + // })); + + // Handle input changes and update state + const handleInputChange = (e) => { + let name = e.target.name; + let value = e.target.value; + + setFormData((prevData) => ({ + ...prevData, + [name]: value, + userId: userData._id, + })); + }; + + // Handle form submission + const handleSubmit = async (e) => { + e.preventDefault(); + + // Validation logic here + const validationErrors = {}; + + if (!formData.restaurantName) { + validationErrors.restaurantName = "Name is required."; + } + if (!formData.restaurantContactNumber) { + validationErrors.restaurantContactNumber = "Contact Number is required."; + } + if (!formData.restaurantEmail) { + validationErrors.restaurantEmail = "Restaurant Email is required."; + } + if (!formData.restaurantAddress) { + validationErrors.restaurantAddress = "Restaurant Address is required."; + } + + if ( + formData.restaurantEmail.length > 1 && + formData.restaurantEmail.length < 10 + ) { + validationErrors.restaurantEmail = "Please enter correct E-mail id"; + } + if (formData.restaurantEmail.length < 1) { + validationErrors.restaurantEmail = "Please enter E-mail id"; + } + if (formData.restaurantContactNumber.length < 9) { + validationErrors.restaurantContactNumber = + "Please enter correct Contact Number"; + } + + if (Object.keys(validationErrors).length > 0) { + setErrors(validationErrors); + } else { + setSubmitted(true); + setActiveStep(2); + } + // console.log(formData); + }; + // Conditionally set the overlay to be visible + if (location.coordinates && location.coordinates.lat) { + overlayStyles.display = "none"; + } else { + overlayStyles.display = "block"; + } + return ( +
+ + + + + {!loggedIn ? ( +
+ Loading... +
+ ) : ( + <> +
+

+ Turn on your GPS +

+
+
+
+
+ +
+
+

+ ADDRESS +

+

+ Please place the pin accurately at your outlet’s location + on the map +

+
+
+

+ EMAIL +

+

+ example@email.com +

+

+ PHONE +

+

123-456-7890

+
+
+
+
+

+ Restaurant Details +

+

+ Name, address and location +

+
+
+ + + {errors.restaurantName && ( +

{errors.restaurantName}

+ )} +
+
+ + + {errors.restaurantEmail && ( +

{errors.restaurantEmail}

+ )} +
+
+ + + {errors.restaurantAddress && ( +

{errors.restaurantAddress}

+ )} +
+ +
+
+
+ +
+
+
+
+ +
+
+
+ + +

+ Enter accurate address or your restaurant will not get sortlisted +

+
+ {submitted && ( +
+

Form submitted successfully!

+ +
+ )} +
+
+
+ + + + )} +
+ ); +} +export default CreateRestaurant; diff --git a/fit-bite-web/app/add-restaurant/restaurantTypeData.js b/fit-bite-web/app/add-restaurant/restaurantTypeData.js new file mode 100644 index 00000000..9741d3ed --- /dev/null +++ b/fit-bite-web/app/add-restaurant/restaurantTypeData.js @@ -0,0 +1,117 @@ +const restaurantTypeData = [ + { + index:0, + name: "Fine Dining Restaurant", + description: + "These restaurants offer high-quality cuisine and a formal dining experience. They often have an elegant ambiance, trained staff, and a wide selection of food..", + icon: ( + + + + ), + }, + { + index:1, + name: "Casual Dining Restaurant", + description: + "Customers can enjoy a variety of dishes at affordable prices, and they typically don't require reservations.", + icon: ( + + + + + + ), + }, + { + name: "Fast Food Restaurant", + description: + "Fast food restaurants serve quick, convenient, and affordable meals.", + icon: ( + + + + + ), + }, + { + name: "Café or Coffee Shop", + description: "Cafés are known for serving coffee, tea, and light meals.", + icon: ( + + + + ), + }, + + { + name: "Bistro", + description: + "A bistro is a small, cozy eatery that offers a mix of classic and contemporary dishes. It often has a European-inspired menu and a relaxed ambiance.", + icon: ( + + + + ), + }, + { + name: "Ethnic or Specialty Restaurant", + description: + "These restaurants focus on specific cuisines or dishes from around the world. Examples include Italian, Mexican, Thai, sushi bars, and seafood restaurants.", + icon: ( + + + + ), + }, +]; + +export default restaurantTypeData; diff --git a/fit-bite-web/app/add-restaurant/useGeoLocationHook.jsx b/fit-bite-web/app/add-restaurant/useGeoLocationHook.jsx new file mode 100644 index 00000000..6b9bd221 --- /dev/null +++ b/fit-bite-web/app/add-restaurant/useGeoLocationHook.jsx @@ -0,0 +1,43 @@ +import { useState, useEffect } from "react"; + +const useGeoLocation = () => { + const [location, setLocation] = useState({ + loaded: false, + coordinates: { lat: "", lng: "" }, + }); + + const onSuccess = (location) => { + setLocation({ + loaded: true, + coordinates: { + lat: location.coords.latitude, + lng: location.coords.longitude, + }, + }); + }; + + const onError = (error) => { + setLocation({ + loaded: true, + error: { + code: error.code, + message: error.message, + }, + }); + }; + + useEffect(() => { + if (!("geolocation" in navigator)) { + onError({ + code: 0, + message: "Geolocation not supported", + }); + } + + navigator.geolocation.getCurrentPosition(onSuccess, onError); + }, []); + + return location; +}; + +export default useGeoLocation; \ No newline at end of file diff --git a/fit-bite-web/app/admin/page.jsx b/fit-bite-web/app/admin/page.jsx new file mode 100644 index 00000000..9d029ad3 --- /dev/null +++ b/fit-bite-web/app/admin/page.jsx @@ -0,0 +1,250 @@ +"use client"; +import React from "react"; +import { useState, useEffect } from "react"; +import { useRouter } from "next/navigation"; +import { UserContext } from "../Context/UserProvider"; +import Link from "next/link"; +// var jwt = require("jsonwebtoken"); +function Page() { + const [orders, setOrders] = useState([]); + const [counter, setCounter] = useState(0); + const [selectedStatus, setSelectedStatus] = useState({}); + const { contextLoading, adminloggedIn, adminlogout} = + React.useContext(UserContext); + const router = useRouter(); + const handleChange = (event, order) => { + // console.log(event, order.chargeId); + setSelectedStatus({ chargeId: order.chargeId, status: event }); + }; + + const handleUpdateStatus = async (order) => { + // console.log(order.chargeId); + // console.log(selectedStatus); + // console.log(order._id); + let jsonData = { + deliveryStatus: selectedStatus.status, + }; + const jsonDataFinal = JSON.stringify(jsonData, null, 2); + const response = await fetch(`/api/orders/${order._id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: jsonDataFinal, + }); + + // const data = await response.json(); + + if (response.status === 200) { + // Update UI to reflect successful update + console.log("Delivery status updated successfully!"); + setCounter((prev) => prev + 1); + } else { + // Handle error + console.error(response.message); + } + }; + async function render() { + try { + const response = await fetch("/api/orders", { + method: "GET", + }); + let res = await response.json(); + if (response.status === 200) { + setOrders(res.orders); + } + } catch (err) { + console.log(err); + } + } + + useEffect(() => { + if (typeof window !== "undefined") { + if (contextLoading == false) { + // console.log({adminloggedIn}, "adminlogged in"); + if (adminloggedIn) { + // adminlogin(); + render(); + // adminlogin(); + } + if (!adminloggedIn) { + + console.log("else from admin orders page is getting triggered"); + // Schedule the redirect after 3 seconds + setTimeout(() => { + router.push("/adminSignin"); + }, 1000); + } + } + } + // else { + // // Show the div first + + // // Schedule the redirect after 3 seconds + // setTimeout(() => { + // router.push("/login"); + // }, 3000); + // } + + // getUser().then(()=>{ + // console.log({decoded}); + // }) + + // cs_test_a1sBkOnJyyCv9FfZt5rogyFokDMk60s6SSVyrogePJGRyVE8u8I1c1vA2Y + }, [adminloggedIn, contextLoading, selectedStatus, counter, router]); + + return ( +
+

+ {" "} + {contextLoading || !adminloggedIn ? ( +
+ Loading... +
+ ) : orders.length > 0 ? ( + "Recent Orders" + ) : ( + "No Orders Found" + )} + {/* {orders.length > 0 ? "Recent Orders" : "Orders Found: NAN"} */} +

+ + {adminloggedIn ? ( +
+
+
+ {orders?.map((order) => ( + //

{order.chargeId, + // order.userId, + // order.orderStatus, + // order.amount, + // order.paymentMethod, + // order.billingEmail, + // order.cardBrand, + // order.last4Digits, + // order.receipt_url}

+ +
+ Order ID: {order.chargeId} +
+
+ {/* + + */} + + {" "} + {" "} + {" "} + + {/* */} +
+
+

+ Amount:{" "} + + {order.amount / 100} INR. + +

+

+ {order.orderStatus.toUpperCase()} +

+
+

+ Method: {order.paymentMethod} +

+

+ Billing Email: {order.billingEmail} +

+

+ Card Brand: {order.cardBrand} +

+

+ Last 4 Digits: {order.last4Digits} +

+
+
+

+ {order?.deliveryStatus + ? order.deliveryStatus === "Delivered" + ? "" + : "Delivery Status: " + : "Awaiting approval"} + + {order?.deliveryStatus} + +

+ + +
+ + {" "} + Invoice + + + + +
+
+
+ ))} +
+
+
+ ) : ( +
+ )} +
+ ); +} + +export default Page; diff --git a/fit-bite-web/app/adminSignin/page.jsx b/fit-bite-web/app/adminSignin/page.jsx new file mode 100644 index 00000000..e1993182 --- /dev/null +++ b/fit-bite-web/app/adminSignin/page.jsx @@ -0,0 +1,315 @@ +"use client"; +import React from "react"; +import { useState } from "react"; +import Image from "next/image"; +import Link from "next/link"; +import "../globals.css"; +import { ToastContainer, toast } from "react-toastify"; +// import { injectStyle } from "react-toastify/dist/inject-style"; +import "react-toastify/dist/ReactToastify.css"; +import { useRouter } from "next/navigation"; +import { UserContext } from "../Context/UserProvider"; +function LoginPage() { + const { setAdminLoggedIn } = React.useContext(UserContext); + const [submitted, setSubmitted] = useState(false); + const [errors, setErrors] = useState({ }); + + const [isChecked, setIsChecked] = useState({ checked: false }); + const [formData, setFormData] = useState({ + Email: "", + Password: "", + }); + let router = useRouter(); + const handleCheckboxChange = () => { + // Toggle the value of isChecked when the checkbox is clicked + setIsChecked({ checked: !isChecked.checked }); + }; + // IF REMEMBER ME IS CHECKED DO THIS + // if(isChecked.checked === true) { + + // console.log("Don't ask for password for this user again") + // } + + // Handle input changes and update state + const handleInputChange = (e) => { + let name = e.target.name; + let value = e.target.value; + + setFormData((prevData) => ({ + ...prevData, + [name]: value, + })); + //Validation Logic Here + // const validationErrors = {}; + + // if (!formData.Email) { + // validationErrors.Email = "Please enter your Email for future updates."; + // } + + // if (!formData.Password) { + // validationErrors.Password = "Password is required."; + // } + + // if (formData.Password.includes(formData.username)) { + // validationErrors.Password = "Username should not be used as a password."; + // } + // if (formData.Email.length > 1 && formData.Email.length < 10) { + // validationErrors.Email = "Please enter correct E-mail id"; + // } + // if (formData.Email.length < 1) { + // validationErrors.Email = "Please enter your Email for future updates."; + // } + + // if (Object.keys(validationErrors).length > 0) { + // setErrors(validationErrors); + + // // console.log(validationErrors) + // // setShouldSignup(false); + // } else { + // setErrors(validationErrors); + // } + }; + // const isFormValid = () => { + // // Check if there are any errors in the errors state + // return Object.values(errors).every((error) => error === ""); + // }; + // Handle form submission + const handleSubmit = async (e) => { + e.preventDefault(); + + // setActiveStep(2) + // Convert form data to JSON + // const jsonData = JSON.stringify(formData, null, 2); + setErrors({}) + const validationErrors = {}; + + if (!formData.Email) { + validationErrors.Email = "Please enter your Email for future updates."; + } + + if (!formData.Password) { + validationErrors.Password = "Password is required."; + } + + if (formData.Password.includes(formData.username)) { + validationErrors.Password = "Username should not be used as a password."; + } + if (formData.Email.length > 1 && formData.Email.length < 10) { + validationErrors.Email = "Please enter correct E-mail id"; + } + if (formData.Email.length < 1) { + validationErrors.Email = "Please enter your Email for future updates."; + } + + if (Object.keys(validationErrors).length > 0) { + setErrors(validationErrors); + } + + // { "username": "Abhi", + // "ContactNumber": 679234567890, + // "Email": "abhi2y@email.com", + // "Password": "abhi", + // "Address": "PNT Colony" + // } + // Log the JSON data (you can send it to a server or save it as needed) + + // Convert form data to JSON + const jsonDataFinal = JSON.stringify(formData, null, 2); + // console.log("String form Data Final", jsonDataFinal); + if (Object.keys(validationErrors).length == 0) { + try { + const response = await fetch("/api/admin", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: jsonDataFinal, + }); + // console.log(response.body); + if (response.status === 201) { + // Parse the JSON response and extract the token + const responseData = await response.json(); + const token = responseData.token; + + // Use the extracted token for further processing + // console.log("Token:", token); + // const token = response.cookies.get("token") + localStorage.setItem("adminToken", token); + setSubmitted(true); + // console.log("Form data submitted successfully", formData); + setAdminLoggedIn(true); + // adminlogin() + // setAdminEmail(formData.Email) + + toast.success("🎉 Signed In successfully! Redirecting 🤗", { + position: "bottom-center", + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + setTimeout(() => { + router.push("/admin"); + }, 3000); + + setFormData({ + Email: "", + Password: "", + }); + } else { + console.error("Form submission failed"); + toast.error("👹 Admin Not Found!", { + position: "bottom-center", + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + setFormData({ + Email: "", + Password: "", + }); + setIsChecked({ checked: false }); + } + } catch (error) { + console.error("Error:", error); + } + } + // console.log(formData); + }; + + return ( +
+ +
+ Logo + {/*

+ Zomato +

*/} +
+
+
+
+ + + {errors.Email && ( +

{errors.Email}

+ )} + {/*

+ Please enter your desired Username +

*/} +
+ +
+ + + {errors.Password && ( +

{errors.Password}

+ )} + {/*

+ Please choose a password. +

*/} +
+
+
+ + +
+ + Forgot Password? + +
+ + +
+
+

+ {`Don't`} have an account? + Contact Developer +

+
+

+ ©2023 Zomato. All rights reserved. +

+
+
+ ); +} + +export default LoginPage; diff --git a/fit-bite-web/app/aiTrainer/page.jsx b/fit-bite-web/app/aiTrainer/page.jsx new file mode 100644 index 00000000..d5a10f94 --- /dev/null +++ b/fit-bite-web/app/aiTrainer/page.jsx @@ -0,0 +1,233 @@ +"use client"; +import { useState, useEffect, useRef } from "react"; +import { useChat } from "../Context/ChatContext"; +import ReactMarkdown from 'react-markdown'; +const VideoRecorder = () => { + // const { messages, addMessage } = useChat(); + const [messages, setMessages] = useState([]); + const [input, setInput] = useState(""); + const chatEndRef = useRef(null); + const videoRef = useRef(null); + const mediaRecorderRef = useRef(null); + const [recording, setRecording] = useState(false); + const chunks = useRef([]); + const [selectedFile, setSelectedFile] = useState(null); + const [uploading, setUploading] = useState(false); + const [response, setResponse] = useState(null); + const [exercise, setExercise] = useState(""); + const handleFileChange = (e) => { + console.log(e.target.files[0]); + setSelectedFile(e.target.files[0]); + }; + const handleChange = (event) => { + // console.log(event, order.chargeId); + setExercise(event); + }; + const addMessage = (text, sender) => { + setMessages((prevMessages) => [ + ...prevMessages, + { id: prevMessages.length, text, sender }, + ]); + }; + useEffect(() => { + chatEndRef.current?.scrollIntoView({ behavior: "smooth" }); + }, [messages]); + + const handleSend = () => { + if (!input.trim()) return; + + addMessage(input, "user"); + + // Simulate AI response + setTimeout(() => { + addMessage("Hello! How can I help you?", "ai"); + }, 1000); + + setInput(""); + }; + const handleUpload = async () => { + if (!selectedFile) { + alert("Please select a video file first."); + return; + } + + setUploading(true); + const formData = new FormData(); + formData.append("file", selectedFile, selectedFile.name); + + try { + const res = await fetch("/api/uploadVideoML", { + method: "POST", + body: formData, + }); + + const result = await res.json(); + console.log({ result }); + setResponse(result); + let prompt = ` I am doing this ${exercise}, and my current joint angle is ${result.angles} degrees. Give me feedback on whether I am doing it correctly. Also suggest improveents if needed`; + try { + const resFinal = await fetch("/api/ai", { + method: "POST", + body: JSON.stringify({prompt}), + }); + console.log({ resFinal }); + let responseText= await resFinal.text(); + console.log({ responseText }); + // const = await resFinal2.text(); + // console.log({ responseText }); + + const aiMessages = responseText.match(/[^.!?]+[.!?]+/g) || []; + const formattedText = aiMessages.replace(/\n/g, '
'); + for (const msg of formattedText) { + if (msg.trim()) { + addMessage(msg.trim(), "ai"); + await new Promise((resolve) => setTimeout(resolve, 500)); // Delay between messages + } + } + } catch (err) { + console.log(err, "gemini error"); + } + } catch (error) { + console.error("Upload failed:", error); + } + + setUploading(false); + }; + + useEffect(() => { + async function getCameraStream() { + try { + const stream = await navigator.mediaDevices.getUserMedia({ + video: true, + audio: true, + }); + if (videoRef.current) { + videoRef.current.srcObject = stream; + } + } catch (error) { + console.error("Error accessing webcam:", error); + } + } + getCameraStream(); + }, []); + + const startRecording = () => { + if (!videoRef.current || !videoRef.current.srcObject) return; + + chunks.current = []; + const stream = videoRef.current.srcObject; + const mediaRecorder = new MediaRecorder(stream); + + mediaRecorder.ondataavailable = (event) => { + if (event.data.size > 0) { + chunks.current.push(event.data); + } + }; + + mediaRecorder.onstop = async () => { + const blob = new Blob(chunks.current, { type: "video/webm" }); + + // Convert Blob to File + const file = new File([blob], "recorded-video.webm", { + type: "video/webm", + }); + + // Upload video to API for saving + const formData = new FormData(); + formData.append("video", file); + + try { + const response = await fetch("/api/uploadVideo", { + method: "POST", + body: blob, // Send raw video data + }); + + const data = await response.json(); + // console.log(data.message, "Saved at:", data.filePath); + } catch (error) { + console.error("Error uploading video:", error); + } + }; + + mediaRecorderRef.current = mediaRecorder; + mediaRecorder.start(); + setRecording(true); + }; + + const stopRecording = () => { + if (mediaRecorderRef.current) { + mediaRecorderRef.current.stop(); + } + setRecording(false); + }; + + return ( +
+