sample coding
app.py
from flask import Flask, request, jsonify, session
from flask_cors import CORS
import pymysql # MySQL library
import requests
from werkzeug.security import generate_password_hash, check_password_hash
from retrying import retry
import requests
app = Flask(__name__)
app.secret_key = '2576fa35bdb55ddfd7476f752939fa7e'
CORS(app)
# Database Configuration (Using pymysql)
db = pymysql.connect(
host="localhost",
user="root", # Replace with your MySQL username
password="", # Replace with your MySQL password
database="recipe_finder" # Ensure the database exists
)
SPOONACULAR_API_KEY = "4cf45fa4ab174fb5bcd586e17b02a653" # Replace with your real API key
@app.route('/', methods=['GET'])
def home():
return jsonify({"message": "Welcome to the Recipe Finder API. Use the /find-recipes endpoint to search for recipes."})
@app.route('/signup', methods=['POST'])
def signup():
data = request.get_json() # Corrected to safely fetch JSON data
if not data:
return jsonify({'message': 'No input data provided'}), 400
user_name = data.get('user_name') # Ensure consistent key
password = data.get('password') # Use the correct key for password
if not user_name or not password:
return jsonify({'message': 'Username and password are required'}), 400
hashed_password = generate_password_hash(password)
cur = db.cursor()
cur.execute("SELECT * FROM users WHERE user_name=%s", (user_name,))
user = cur.fetchone()
if user:
cur.close()
return jsonify({'message': 'Username already registered'}), 400
cur.execute("INSERT INTO users (user_name, password) VALUES (%s, %s)", (user_name, hashed_password))
db.commit()
cur.close()
return jsonify({'message': 'Signup successful!'})
@app.route('/login', methods=['POST'])
def login():
data = request.get_json() # Corrected to safely fetch JSON data
if not data:
return jsonify({'message': 'No input data provided'}), 400
user_name = data.get('user_name') # Ensure consistent key
password = data.get('password') # Use the correct key for password
if not user_name or not password:
return jsonify({'message': 'Username and password are required'}), 400
cur = db.cursor()
cur.execute("SELECT * FROM users WHERE user_name=%s", (user_name,))
user = cur.fetchone()
if user and check_password_hash(user[2], password): # Ensure the password comparison is correct
session['user_name'] = user_name
cur.close()
return jsonify({'message': 'Login successful'})
cur.close()
return jsonify({'message': 'Invalid username or password'}), 401
@app.route('/feedback', methods=['POST'])
def feedback():
data = request.json
name = data.get('name')
email = data.get('email')
message = data.get('message')
try:
with db.cursor() as cursor:
sql = "INSERT INTO feedback (name, email, message) VALUES (%s, %s, %s)"
cursor.execute(sql, (name, email, message))
db.commit()
return jsonify({"message": "Feedback received!"}), 200
except Exception as e:
print(f"Error: {e}")
return jsonify({"error": "Error storing feedback"}), 500
@app.route('/find-recipes', methods=['POST'])
def find_recipes():
try:
if not request.is_json:
return jsonify({"error": "Content-Type must be application/json."}), 415
data = request.json
ingredients = data.get("ingredients", [])
diet = data.get("diet", "")
if not ingredients:
return jsonify({"error": "Ingredients list cannot be empty."}), 400
ingredients_query = ",".join(ingredients)
url = "https://api.spoonacular.com/recipes/complexSearch"
params = {
"apiKey": SPOONACULAR_API_KEY,
"includeIngredients": ingredients_query,
"diet": diet,
"number": 10,
}
response = requests.get(url, params=params)
if response.status_code != 200:
return jsonify({"error": f"Spoonacular API error: {response.status_code} {response.text}"}), 500
recipes_data = response.json().get("results", [])
recipes = [{"id": recipe.get("id"), "title": recipe.get("title")} for recipe in recipes_data]
return jsonify({"recipes": recipes})
except Exception as e:
print(f"Error occurred: {str(e)}")
return jsonify({"error": f"An error occurred: {str(e)}"}), 500
@app.route('/recipe/<int:recipe_id>', methods=['GET'])
def get_recipe_details(recipe_id):
url = f"https://api.spoonacular.com/recipes/{recipe_id}/information"
params = {"apiKey": SPOONACULAR_API_KEY}
response = requests.get(url, params=params)
if response.status_code == 200:
return jsonify(response.json())
else:
return jsonify({"error": f"Failed to fetch recipe details: {response.status_code}"}), 500
@app.route('/like-recipe', methods=['POST'])
def like_recipe():
# Check if user is logged in
if 'user_name' not in session:
return jsonify({"message": "Please login to like recipes"}), 401
data = request.get_json()
recipe_id = data.get("recipe_id")
recipe_title = data.get("recipe_title")
if not recipe_id or not recipe_title:
return jsonify({"message": "Recipe ID and title are required"}), 400
user_name = session['user_name']
# Fetch the user ID based on the logged-in username
cur = db.cursor()
cur.execute("SELECT id FROM users WHERE user_name = %s", (user_name,))
user = cur.fetchone()
if not user:
cur.close()
return jsonify({"message": "User not found"}), 404
user_id = user[0]
# Check if the recipe is already liked
cur.execute("SELECT * FROM liked_recipes WHERE user_id = %s AND recipe_id = %s",
(user_id, recipe_id))
existing = cur.fetchone()
try:
if existing:
# If already liked, remove it (unlike)
cur.execute("DELETE FROM liked_recipes WHERE user_id = %s AND recipe_id = %s",
(user_id, recipe_id))
db.commit()
message = "Recipe unliked successfully!"
else:
# Otherwise, add it to liked_recipes
cur.execute("INSERT INTO liked_recipes (user_id, recipe_id, recipe_title) VALUES (%s, %s, %s)",
(user_id, recipe_id, recipe_title))
db.commit()
message = "Recipe liked successfully!"
cur.close()
return jsonify({"message": message}), 201
except Exception as e:
print(f"Error: {e}")
return jsonify({"error": "Error storing liked recipe"}), 500
@app.route('/get-liked-recipes/<user_name>', methods=['GET'])
def get_liked_recipes(user_name):
if user_name in liked_recipes_db:
return jsonify({"recipes": liked_recipes_db[user_name]}), 200
return jsonify({"message": "No liked recipes found."}), 404
@retry(stop_max_attempt_number=3, wait_fixed=2000)
def fetch_recipes():
response = requests.get(
'https://api.spoonacular.com/recipes/complexSearch',
params={
'apiKey': 'YOUR_API_KEY',
'includeIngredients': 'tomato',
'diet': '',
'number': 10
},
timeout=60
)
return response.json()
try:
data = fetch_recipes()
print(data)
except Exception as e:
print(f"Failed to fetch recipes: {e}")
if __name__ == "__main__":
app.run(debug=True)
app.css
body {
margin: 0;
font-family: Arial, sans-serif;
/*background-image: url('/public/assets/smart-back.png'); /* Set your background image
background-size: cover; /* Ensure the image covers the entire screen
background-position: center; /* Center the background image
background-repeat: no-repeat; /* Don't repeat the image
height: 100vh; /* Full height
display: flex;
flex-direction: column;
justify-content: space-between; /* To ensure the content is spaced properly */
color: #333; /* Set text color for contrast */
/* Remove background image */
background: none;
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
overflow-x: hidden; /* Prevent horizontal scroll */
}
/* Basic CSS for app layo*/
/* Remove background image */
body {
background: none;
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}
/* Remove background image */
/* Remove background image */
body {
background: none;
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}
.App {
position: relative;
min-height: 100vh;
overflow: hidden;
}
.dancing-icon {
position: absolute;
width: 40px;
height: 40px;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
animation: dance 3s infinite ease-in-out;
}
/* Use Corrected Paths for Images */
.vegetable-1 {
background-image: url('./assets/veg1.png'); /* Ensure the path is correct */
}
.vegetable-2 {
background-image: url('./assets/veg2.png');
}
.vegetable-3 {
background-image: url('./assets/veg3.png');
}
.vegetable-4 {
background-image: url('./assets/veg4.png');
}
.vegetable-5 {
background-image: url('./assets/veg5.png');
}
.vegetable-6 {
background-image: url('./assets/veg1.png');
}
.vegetable-7 {
background-image: url('./assets/veg2.png');
}
.vegetable-8 {
background-image: url('./assets/veg3.png');
}
.vegetable-9 {
background-image: url('./assets/veg4.png');
}
.vegetable-10 {
background-image: url('./assets/veg5.png');
}
/* Random Positions for Each Vegetable */
.dancing-icon:nth-child(1) { top: 10%; left: 10%; }
.dancing-icon:nth-child(2) { top: 15%; left: 30%; }
.dancing-icon:nth-child(3) { top: 25%; left: 70%; }
.dancing-icon:nth-child(4) { top: 35%; left: 50%; }
.dancing-icon:nth-child(5) { top: 45%; left: 20%; }
.dancing-icon:nth-child(6) { top: 55%; left: 80%; }
.dancing-icon:nth-child(7) { top: 65%; left: 10%; }
.dancing-icon:nth-child(8) { top: 75%; left: 60%; }
.dancing-icon:nth-child(9) { top: 85%; left: 40%; }
.dancing-icon:nth-child(10) { top: 95%; left: 30%; }
.dancing-icon:nth-child(11) { top: 10%; left: 80%; }
.dancing-icon:nth-child(12) { top: 20%; left: 40%; }
.dancing-icon:nth-child(13) { top: 30%; left: 60%; }
.dancing-icon:nth-child(14) { top: 40%; left: 10%; }
.dancing-icon:nth-child(15) { top: 50%; left: 70%; }
.dancing-icon:nth-child(16) { top: 60%; left: 50%; }
.dancing-icon:nth-child(17) { top: 70%; left: 20%; }
.dancing-icon:nth-child(18) { top: 80%; left: 80%; }
.dancing-icon:nth-child(19) { top: 90%; left: 30%; }
.dancing-icon:nth-child(20) { top: 100%; left: 50%; }
/* Animation for Dancing Effect */
@keyframes dance {
0%, 100% {
transform: translateY(0) rotate(0deg);
}
25% {
transform: translateY(-20px) rotate(10deg);
}
50% {
transform: translateY(0) rotate(-10deg);
}
75% {
transform: translateY(20px) rotate(5deg);
}
}
.App {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.nav-container {
display: flex;
justify-content: flex-end;
align-items: center;
padding: 20px;
background-color: rgba(0, 0, 0, 0.5); /* Slight transparency for background */
}
.sign-up-container {
flex-grow: 1;
}
.sign-up-button {
background-color: #e60000;
color: white;
font-size: 16px;
padding: 10px 20px;
border: none;
cursor: pointer;
}
.menu {
list-style: none;
display: flex;
gap: 20px;
}
.menu li {
display: flex;
}
.menu a {
color: white;
text-decoration: none;
font-size: 18px;
}
.App-header {
padding: 50px;
color:black;
}
footer {
background-color: rgba(0, 0, 0, 0.8);
color: white;
padding: 30px;
margin-top: auto;
}
.footer-content {
max-width: 1200px;
margin: 0 auto;
text-align: center;
}
.footer-links a {
color: white;
margin-right: 20px;
text-decoration: none;
}
.footer-store img {
width: 100px;
margin: 10px;
}
.footer-social a {
color: white;
margin-right: 15px;
text-decoration: none;
}
.donate-button {
padding: 10px 20px;
background-color: #e60000;
color: white;
border: none;
font-size: 16px;
cursor: pointer;
margin-top: 20px;
}
.App-header {
text-align: center;
padding: 20px;
color: black;
}
h1 {
font-size: 3rem;
margin-bottom: 10px;
}
main {
padding: 20px;
}
.recipe-form-container {
background: rgba(177, 186, 176, 0.7);
color: #333;
border-radius: 10px;
padding: 20px;
width: 80%;
margin: 20px auto;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.form-group {
margin-bottom: 15px;
}
input, select {
width: 100%;
padding: 10px;
margin-top: 5px;
border: 1px solid #ddd;
border-radius: 5px;
}
button.fetch-button {
background: #ff5e62;
color: #fff;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 1rem;
}
button.fetch-button:hover {
background: #ff9966;
}
.recipe-list {
display: flex;
flex-wrap: wrap;
gap: 20px;
justify-content: center;
}
.recipe-card {
background: #fff;
color: #333;
border-radius: 10px;
width: 200px;
padding: 15px;
text-align: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
position: relative;
}
.recipe-card img {
width: 100%;
border-radius: 10px;
}
footer {
background-color: #f1f1f1;
color: #333;
padding: 20px;
text-align: center;
font-family: Arial, sans-serif;
}
.footer-container {
max-width: 1200px;
margin: auto;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
gap: 20px;
}
.footer-content {
flex: 1 1 300px;
text-align: center;
}
.footer-links {
display: flex;
justify-content: center;
gap: 20px;
flex-wrap: wrap;
}
.footer-links a {
color: #ff5e62;
text-decoration: none;
font-size: 14px;
}
.footer-links a:hover {
text-decoration: underline;
}
.footer-social {
display: flex;
justify-content: center;
gap: 15px;
}
.footer-social a {
color: #555;
font-size: 1.2em;
transition: color 0.3s;
}
.footer-social a:hover {
color: #ff5e62;
}
.footer-store {
display: flex;
justify-content: center;
gap: 30px;
}
.footer-store img {
width: 100px;
height: auto;
}
.donate-button {
background-color: #4caf50;
color: white;
border: none;
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
border-radius: 5px;
}
.donate-button:hover {
background-color: #45a049;
}
@media (max-width: 768px) {
.footer-content {
flex-direction: column;
}
}
.login-form {
max-width: 400px;
margin: auto;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(186, 178, 178, 0.5);
}
label {
display: block;
margin-bottom: 5px;
}
input {
width: 100%;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
}
.error-message {
color: red;
font-size: 0.9em;
}
.login-button {
background-color: #28a745;
color: white;
padding: 10px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.login-button:hover {
background-color: #218838;
}
nav ul {
list-style-type: none;
padding: 0;
margin: 0;
display: flex;
justify-content: flex-end;
background-color: #333;
}
/*
nav ul li {
margin: 0 15px;
}
nav ul li a {
color: white;
text-decoration: none;
padding: 10px 15px;
font-size: 16px;
}
nav ul li a:hover {
background-color: #555;
border-radius: 5px;
}
*/
.recipe-details {
padding: 20px;
border: 1px solid #ddd;
background-color: #fff;
position: fixed;
top: 10px;
left: 30%;
width: 40%;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
z-index: 10;
}
.recipe-details h2 {
margin-bottom: 10px;
}
.recipe-details img {
width: 100%;
height: auto;
margin-bottom: 50px;
}
.recipe-details button {
padding: 10px;
background: #ff5a5f;
color: #fff;
border: none;
cursor: pointer;
}
nav {
background-color: #333;
}
.menu {
list-style-type: none;
margin: 0;
padding: 0;
display: inline;
justify-content: flex-end;
}
.menu li {
margin: 0 20px;
display:flex;
}
.menu li a {
color: white;
text-decoration: none;
font-size: 1.1rem;
padding: 15px 20px;
display: block;
transition: background-color 0.3s, color 0.3s;
border-radius: 5px;
}
.menu li a:hover {
background-color: #ff5e62;
color: #fff;
}
.menu li a.active {
background-color: #ff9966;
}
@keyframes bounce {
0%, 20%, 50%, 80%, 100% {
transform: translateY(0);
}
40% {
transform: translateY(-10px);
}
60% {
transform: translateY(-5px);
}
}
.like-icon {
position: absolute;
top: 10px;
left: 10px;
font-size: 24px;
cursor: pointer;
}
.nav-container {
display: flex;
margin-left: 0;;
align-items: center;
}
.nav-container .menu {
list-style-type: none;
display: flex; /* Align menu items in a row*/
padding: 0;
margin: 0;
width:100%;
justify-content: flex-end;
}
.nav-container .menu li {
margin-right: 15px; /*Adjust spacing between menu items */
}
.nav-container .menu li:last-child {
margin-left: 0; /* Remove margin from the last item*/
}
.sign-up-button {
margin-right: auto; /* Moves the button to the leftmost position */
padding: 10px 20px;
background-color: #ff5733;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.user-greeting {
margin-right: auto;
font-size: 1rem;
font-weight: bold;
color: #333;
padding-right: 15px;
}
/* Style for the sign-up and donate buttons */
/* Button styles for primary and secondary actions */
.button.primary {
background-color: #4caf50;
color: white;
}
.button.secondary {
background-color: #2196f3;
color: white;
}
.button.primary:hover,
.button.secondary:hover {
opacity: 0.8;
}
/* Welcome message styling */
.welcome-message {
font-size: 2.2rem;
font-weight: 500;
color: #444;
top: 20px;
}
.login-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100%;
position: relative;
}
.login-card {
background: rgba(179, 165, 173, 0.1);
border-radius: 8px;
padding: 60px 40px 40px;
width: 400px;
box-shadow: 0 2px 10px rgba(169, 161, 161, 0.1);
text-align: center;
position: relative;
}
.login-title {
font-size: 28px;
margin-bottom: 10px;
font-weight: bold;
}
.login-subtitle {
font-size: 16px;
color: #6b6b6b;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 20px;
text-align: left;
}
.form-group label {
font-size: 14px;
margin-bottom: 5px;
display: block;
}
.input-field {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
.options {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
}
.button {
width: 100%;
padding: 12px;
font-size: 16px;
border: none;
border-radius: 4px;
cursor: pointer;
margin-bottom: 10px;
}
.button.primary {
background-color: #6c63ff;
color: #ffffff;
}
.button.google {
background-color: #ffffff;
color: #333;
border: 1px solid #ddd;
}
.signup-link {
font-size: 14px;
}
.signup-link a {
color: #6c63ff;
text-decoration: none;
}
.signup-link a:hover {
text-decoration: underline;
}
/* Custom styling for the sign-up button */
.sign-up-button {
background-color: #ff7043;
color: #ffffff;
font-size: 16px;
padding: 12px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.sign-up-button:hover {
background-color: #ff5722;
}
@keyframes jumpy {
0%, 100% {
transform: translateY(0);
}
80% {
transform: translateY(-10px);
}
}
.user-greeting {
font-size: 1.2rem;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
animation: jumpy 2.5s infinite;
font-weight: bold;
color: orangered;
}
.logo-top-right {
display: flex;
align-items: center;
position: absolute;
top: 30px;
left: 50px;
}
.logo {
height: 40px;
margin-right: 8px;
}
.logo-name {
font-size: 1.5rem;
font-weight: bold;
color: #4CAF50;
}
login page.js
import React, { useState } from 'react';
import axios from 'axios';
import './App.css';
const LoginPage = ({ setUserName }) => {
const [userName, setUserNameInput] = useState('');
const [password, setPassword] = useState('');
const [showPassword, setShowPassword] = useState(false);
const validateInput = () => {
if (!userName || !password) {
alert('Please enter a valid username and password.');
setUserNameInput('');
setPassword('');
return false;
}
return true;
};
const handleSignup = async () => {
if (!validateInput()) return;
try {
const response = await axios.post('http://localhost:5000/signup', {
user_name: userName,
password: password,
});
alert(response.data.message);
if (response.data.message === 'Signup successful!') {
setUserName(userName);
setUserNameInput('');
setPassword('');
window.location.href = '/';
}
} catch (error) {
handleError(error);
}
};
const handleLogin = async () => {
if (!validateInput()) return;
try {
const response = await axios.post('http://localhost:5000/login', {
user_name: userName,
password: password,
});
alert(response.data.message);
if (response.data.message === 'Login successful') {
setUserName(userName);
setUserNameInput('');
setPassword('');
window.location.href = '/';
}
} catch (error) {
handleError(error);
}
};
const handleError = (error) => {
if (error.response && error.response.data) {
alert(error.response.data.message);
} else {
alert('Network error: Could not connect to the server. Please check if the server is running.');
}
setUserNameInput('');
setPassword('');
};
return (
<div className="login-container">
<div className="login-card">
<h1 className="login-title">Welcome to Recipe Finder</h1>
<p className="login-subtitle">Please sign up or log in to continue</p>
<div className="form-group">
<label>Username</label>
<input
type="text"
placeholder="Username"
value={userName}
onChange={(e) => setUserNameInput(e.target.value)}
className="input-field"
/>
</div>
<div className="form-group">
<label>Password</label>
<input
type={showPassword ? 'text' : 'password'}
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="input-field"
/>
</div>
<div className="options">
<label>
<input
type="checkbox"
checked={showPassword}
onChange={() => setShowPassword(!showPassword)}
/>
Show Password
</label>
</div>
<div className="button-group">
<button onClick={handleSignup} className="button primary">Sign up</button>
<button onClick={handleLogin} className="button secondary">Sign in</button>
</div>
</div>
</div>
);
};
export default LoginPage;
app.js
import React, { useState, useEffect } from "react";
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import "./App.css";
import RecipeForm from "./RecipeForm";
import FeedbackForm from "./FeedbackForm";
import GalleryPage from "./GalleryPage";
import AboutPage from "./AboutPage";
import ContactPage from "./ContactPage";
import LoginPage from "./LoginPage";
function HomePage({ likedRecipes, setLikedRecipes, userName }) {
return (
<div>
{userName && <p className="welcome-message">Hi, {userName}!</p>}
<RecipeForm likedRecipes={likedRecipes} setLikedRecipes={setLikedRecipes} />
</div>
);
}
function FamousRecipesPage() {
return <h2>Famous Recipes</h2>;
}
function App() {
const [likedRecipes, setLikedRecipes] = useState([]);
const [userName, setUserName] = useState(localStorage.getItem("userName") || null);
useEffect(() => {
if (userName) {
localStorage.setItem("userName", userName);
}
}, [userName]);
const handleLogout = () => {
localStorage.removeItem("userName");
setUserName(null);
window.location.href = "/";
};
return (
<Router>
<div className="App">
<div className="dancing-icons-container">
{Array.from({ length: 20 }, (_, i) => (
<div key={i} className={`dancing-icon vegetable-${i % 5 + 1}`}></div>
))}
</div>
<nav className="nav-container">
<ul className="menu">
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About Us</Link></li>
<li><Link to="/famous-recipes">Famous Recipes</Link></li>
<li><Link to="/gallery">Gallery</Link></li>
<li><Link to="/feedback">Feedback</Link></li>
<li><Link to="/contact">Contact Us</Link></li>
{userName ? (
<>
<li>
<button onClick={handleLogout} className="logout-button">Logout</button>
</li>
</>
) : (
<li>
<Link to="/login">
<button className="sign-up-button">Sign Up/Sign in</button>
</Link>
</li>
)}
</ul>
</nav>
<header className="App-header">
<h1 className="animate__animated animate__bounce">Recipe Finder</h1>
<div className="logo-top-right">
<img src="assets/logo.png" alt="TasteBuddy Logo" className="logo" />
<h1 className="logo-name">TasteBuddy</h1>
</div>
<p>Enter ingredients and preferences to find amazing recipes!</p>
</header>
<main>
<Routes>
<Route path="/" element={<HomePage likedRecipes={likedRecipes} setLikedRecipes={setLikedRecipes} userName={userName} />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/famous-recipes" element={<FamousRecipesPage />} />
<Route path="/gallery" element={<GalleryPage likedRecipes={likedRecipes} />} />
<Route path="/feedback" element={<FeedbackForm />} />
<Route path="/contact" element={<ContactPage />} />
<Route path="/login" element={<LoginPage setUserName={setUserName} />} />
</Routes>
</main>
<footer>
<div className="footer-content">
<div className="footer-links">
<Link to="/about">About</Link>
<Link to="/">Press</Link>
<Link to="/">Terms & Privacy</Link>
<Link to="/">DMCA</Link>
<Link to="/">Contact & Imprint</Link>
</div>
<div className="footer-store">
<a href="https://play.google.com" target="_blank" rel="noopener noreferrer">
<img src="assets/but-play.png" alt="Google Play" />
</a>
<a href="https://www.apple.com/app-store/" target="_blank" rel="noopener noreferrer">
<img src="assets/but-app.png" alt="App Store" />
</a>
</div>
<div className="footer-social">
<a href="https://www.facebook.com" target="_blank" rel="noopener noreferrer">Facebook</a>
<a href="https://www.twitter.com" target="_blank" rel="noopener noreferrer">Twitter</a>
<a href="https://www.pinterest.com" target="_blank" rel="noopener noreferrer">Pinterest</a>
</div>
<p>© 2024 Recipe Finder. All rights reserved.</p>
<button className="donate-button">Donate</button>
</div>
</footer>
</div>
</Router>
);
}
export default App
recipedetailse.js
import React from 'react';
import './RecipeDetails.css'; // Ensure the updated styles are used
const RecipeDetails = ({ recipe, onClose }) => {
if (!recipe) return null;
return (
<div className="overlay">
<div className="recipe-details">
<h2>{recipe.title}</h2>
<div className="fixed-image">
<img src={recipe.image} alt={recipe.title} />
</div>
<div className="details-scrollable">
<p><strong>Instructions:</strong> {recipe.instructions || 'No instructions available.'}</p>
<p><strong>Ready in:</strong> {recipe.readyInMinutes} minutes</p>
<p><strong>Servings:</strong> {recipe.servings}</p>
<p><strong>Dietary Information:</strong> {recipe.diets?.join(', ') || 'Not specified'}</p>
<p><strong>Cuisines:</strong> {recipe.cuisines?.join(', ') || 'Not specified'}</p>
<p><strong>Health Score:</strong> {recipe.healthScore || 'Not available'}</p>
<p><strong>Intolerances:</strong> {recipe.intolerances?.join(', ') || 'Not specified'}</p>
<p><strong>Ingredients:</strong></p>
<ul>
{recipe.ingredients?.map((ingredient, index) => (
<li key={index}>{ingredient.name || ingredient}</li>
)) || <li>No ingredients available.</li>}
</ul>
</div>
<button onClick={onClose}>Close</button>
</div>
</div>
);
};
export default RecipeDetails;
recipedetailse.css
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.6); /* Darken background */
z-index: 1000;
display: flex;
justify-content: center;
align-items: center;
}
.recipe-details {
background-color: #fff;
padding: 20px;
border-radius: 10px;
width: 80%;
max-width: 600px;
max-height: 80%;
overflow-y: auto;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.fixed-image img {
width: 100%;
max-height: 300px;
object-fit: cover;
}
button {
margin-top: 10px;
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
App.js
import React, { useState, useEffect } from "react";
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import "./App.css";
import RecipeForm from "./RecipeForm";
import FeedbackForm from "./FeedbackForm";
import GalleryPage from "./GalleryPage";
import AboutPage from "./AboutPage";
import ContactPage from "./ContactPage";
import LoginPage from "./LoginPage";
import TicTacToe from "./Game"; // Import the Tic Tac Toe game component
function HomePage({ likedRecipes, setLikedRecipes, userName }) {
return (
<div>
{userName && <p className="welcome-message">Hi, {userName}!</p>}
<RecipeForm likedRecipes={likedRecipes} setLikedRecipes={setLikedRecipes} />
</div>
);
}
/*function FamousRecipesPage() {
return <h2>Famous Recipes</h2>;
}*/
function App() {
const [likedRecipes, setLikedRecipes] = useState([]);
const [userName, setUserName] = useState(localStorage.getItem("userName") || null);
useEffect(() => {
if (userName) {
localStorage.setItem("userName", userName);
}
}, [userName]);
const handleLogout = () => {
localStorage.removeItem("userName");
setUserName(null);
window.location.href = "/"; // Redirect to home page after logout
};
return (
<Router>
<div className="App">
<div className="dancing-icons-container">
{Array.from({ length: 20 }, (_, i) => (
<div key={i} className={`dancing-icon vegetable-${i % 5 + 1}`}></div>
))}
</div>
<nav className="nav-container">
<ul className="menu">
<ul className="menu_1">
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About Us</Link></li>
<li><Link to="/contact">Contact Us</Link></li>
{/* Show these menu items ONLY after login */}
{userName && (
<>
<li><Link to="/gallery">Gallery</Link></li>
<li><Link to="/FeedbackForm">FeedbackForm</Link></li>
<li><Link to="/game">Play Game</Link></li>
</>
)}
{/* Show Login / Sign Up only if the user is NOT logged in */}
{!userName ?
<li>
<Link to="/login">
<button className="sign-up-button">Sign Up/Sign in</button>
</Link>
</li>
: (
<li>
<button onClick={handleLogout} className="logout-button">Logout</button>
</li>
)}
</ul>
</ul>
</nav>
<header className="App-header">
<div className="logo-top-right">
<img src="assets/logo.png" alt="TasteBuddy Logo" className="logo" />
<h1 className="logo-name">TasteBuddy</h1>
</div>
</header>
<main>
<Routes>
<Route path="/" element={<HomePage likedRecipes={likedRecipes} setLikedRecipes={setLikedRecipes} userName={userName} />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/contact" element={<ContactPage />} />
<Route path="/login" element={<LoginPage setUserName={setUserName} />} />
{/* Protect Gallery & Game routes - Only show if logged in */}
{userName && <Route path="/gallery" element={<GalleryPage likedRecipes={likedRecipes} />} />}
{userName && <Route path="/FeedbackForm"element={<FeedbackForm />}/>}
{userName && <Route path="/game" element={<TicTacToe />} />}
</Routes>
</main>
<footer>
<div className="footer-content">
<div className="footer-links">
<Link to="/about">About</Link>
<Link to="/">Press</Link>
<Link to="/">Terms & Privacy</Link>
<Link to="/">DMCA</Link>
<Link to="/">Contact & Imprint</Link>
</div>
<div className="footer-store">
<a href="https://play.google.com" target="_blank" rel="noopener noreferrer">
<img src="assets/but-play.png" alt="Google Play" />
</a>
<a href="https://www.apple.com/app-store/" target="_blank" rel="noopener noreferrer">
<img src="assets/but-app.png" alt="App Store" />
</a>
</div>
<div className="footer-social">
<a href="https://www.facebook.com" target="_blank" rel="noopener noreferrer">Facebook</a>
<a href="https://www.twitter.com" target="_blank" rel="noopener noreferrer">Twitter</a>
<a href="https://www.pinterest.com" target="_blank" rel="noopener noreferrer">Pinterest</a>
</div>
<p>© 2024 Recipe Finder. All rights reserved.</p>
<button className="donate-button">Donate</button>
</div>
</footer>
</div>
</Router>
);
}
export default App;
Comments
Post a Comment