Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Michael #108

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions exploring-national-parks/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

# misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
Expand Down
37 changes: 36 additions & 1 deletion exploring-national-parks/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions exploring-national-parks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/react-router-dom": "^5.3.3",
"axios": "^1.7.7",
"moment": "^2.29.4",
"rc-footer": "^0.6.8",
"react": "^18.2.0",
Expand Down
3 changes: 3 additions & 0 deletions exploring-national-parks/src/GlobalComponents/Navbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ const Navbar = () => {
<li>
<NavLink to="/ParkPlan">Park Planner</NavLink>
</li>
<li>
<NavLink to="/ParkAI">Park AI</NavLink>
</li>
</ul>
</nav>
)
Expand Down
1 change: 0 additions & 1 deletion exploring-national-parks/src/HomePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ const HomePage = () => {
<div className = "home-page main-component">
{/* <h1>Test Hello</h1> */}
<Welcome/>
<HighlightGallery/>
<Buttons/>
</div>
)
Expand Down
41 changes: 16 additions & 25 deletions exploring-national-parks/src/HomePage/Components/Buttons.jsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,23 @@
/**
* Renders the Buttons component.
* @module Buttons
* @memberof HomePage
*
* @returns {JSX.Element} The rendered Buttons component.
*/
import React from 'react'
import { Link } from 'react-router-dom'
import React from 'react';
import { Link } from 'react-router-dom';

const Buttons = () => {
function click () {
console.log("clicked")
// invoke a react link to the park search page
window.location.href = "ParkSearch"
}

return (
<div className = "homepage-button-wrapper">
<div className = "button-container">
<p>Learn More About Parks</p>
<Link className="homepage-button" to='/ParkSearch'><button className="homepage-button">Park Search</button></Link>
<div className="homepage-button-wrapper">
<div className="button-container">
<p>Discover New Recipes</p>
<Link to="/ParkAI">
<button className="homepage-button">Get Recipes</button>
</Link>
</div>
<div className = "button-container">
<p>Plan A Trip To A National Park</p>
<Link className="homepage-button" to='/ParkPlan'><button className="homepage-button">Plan a Trip</button></Link>
<div className="button-container">
<p>Plan Your Weekly Meals</p>
<Link to="/WeeklyMealPlan">
<button className="homepage-button">Weekly Meal Plan</button>
</Link>
</div>
</div>
)
}
);
};

export default Buttons
export default Buttons;
14 changes: 7 additions & 7 deletions exploring-national-parks/src/HomePage/Components/Welcome.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ import React from 'react'
*/
const Welcome = () => {
return (
<div className = "welcome">
<h1 className = "welcome-title">Explore National Parks</h1>
<p className = "welcome-text">
Welcome to Exploring National Parks! This web app is designed to help you find the perfect national park for your next trip.
Click on the "Park Search" button to search for a park by activity, or click on the "Plan A Trip" button to plan a trip to a park you've already selected.
<div className="welcome">
<h1 className="welcome-title">Meal Prep Made Easy</h1>
<p className="welcome-text">
Welcome to the Meal Prep App! This web app is designed to help you plan delicious and healthy meals for the week.
Click on "Get Recipes" to discover new recipes tailored to your preferences, or click on "Weekly Meal Plan" to organize your weekly meals and stay on track with your goals.
</p>
<div>

{/* Additional content (if needed) */}
</div>
</div>
)
}

export default Welcome
export default Welcome
19 changes: 19 additions & 0 deletions exploring-national-parks/src/ParkAI.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Renders the ParkSearch component page.
* @component
* @module ParkSearch
* @returns {JSX.Element} The rendered ParkSearch component.
*/
import React from 'react';
import ParkAIHome from './ParkAI/Components/ParkAIHome.jsx';
import './Style/parkSearch.css';
function ParkSearch(){
return(
<div className='park-ai' >
<ParkAIHome />

</div>
);
}

export default ParkSearch;
80 changes: 80 additions & 0 deletions exploring-national-parks/src/ParkAI/Components/ParkAIHome.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React, { useState, useEffect } from 'react';
import '../../Style/parkAI.css';
import { fetchChatGPTResponse } from '../Functionality/FetchChatGPT';

const ParkAIHome = () => {
const [recipeName, setRecipeName] = useState('');
const [description, setDescription] = useState('');
const [ingredients, setIngredients] = useState('');
const [recipeImage, setRecipeImage] = useState('');
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
const fetchRecipe = async () => {
setIsLoading(true);
try {
const response = await fetchChatGPTResponse("Give me a recipe with a name, description, and ingredients.");
setRecipeName(response.name);
setDescription(response.description);
setIngredients(response.ingredients);
setRecipeImage(response.image);
} catch (error) {
console.error('Error fetching recipe:', error);
} finally {
setIsLoading(false);
}
};

fetchRecipe();
}, []);

const handleSave = async () => {
try {
const response = await fetch('http://localhost:5000/addRecipe', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: recipeName,
description,
ingredients,
image: recipeImage,
}),
});
const data = await response.json();
if (response.ok) {
alert("Recipe saved successfully!");
} else {
throw new Error(data.error || 'Failed to save recipe.');
}
} catch (error) {
console.error('Error saving recipe:', error);
alert("Failed to save recipe.");
}
};

return (
<div className='park-ai'>
<center>
<h1 id="ai-title">Recipe Recommendation</h1>
{isLoading ? (
<p>Loading recipe...</p>
) : (
<div className="recipe-container">
{recipeImage && <img src={recipeImage} alt={recipeName} className="recipe-image" />}
{recipeName && <h2>{recipeName}</h2>}
{description && <p>{description}</p>}
{ingredients && (
<div>
<h3>Ingredients:</h3>
<p>{ingredients}</p>
</div>
)}
<button onClick={handleSave} className="save-button">Save</button>
</div>
)}
</center>
</div>
);
};

export default ParkAIHome;
96 changes: 96 additions & 0 deletions exploring-national-parks/src/ParkAI/Functionality/FetchChatGPT.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import axios from 'axios';

/**
* Sends a request to the ChatGPT API to get a recipe based on the selected meal option.
* Then, it sends the recipe name to the DALL·E API to generate a custom image for the recipe.
*
* @function fetchChatGPTResponse
* @param {string} mealOption - The meal option selected by the user (e.g., Chicken, Fish, Vegan, etc.)
* @returns {Object} - The AI-generated recipe data including name, description, and image URL
* @throws {Error} - If the API request fails
*/
export const fetchChatGPTResponse = async (mealOption) => {
const apiKey = process.env.REACT_APP_OPENAI_API_KEY; // Use environment variable for API key

try {
// Step 1: Fetch recipe details from ChatGPT API
const chatGPTResponse = await axios.post(
'https://api.openai.com/v1/chat/completions',
{
model: 'gpt-3.5-turbo',
messages: [
{ role: 'system', content: 'You are an expert on recipes and cooking.' },
{ role: 'user', content: `Please provide a recipe for ${mealOption} with the following structure: "Recipe Name: [Name] | Description: [Description] | Ingredients: [Ingredients List]"` }
],
max_tokens: 300,
temperature: 0.7,
},
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
}
);

const aiResponse = chatGPTResponse.data.choices[0]?.message?.content;

if (!aiResponse) {
throw new Error('No response received from ChatGPT');
}

// Parse the response with validation
const parts = aiResponse.split('|').map(part => part.trim());
if (parts.length < 3) throw new Error('Unexpected response format from ChatGPT');

const [namePart, descriptionPart, ingredientsPart] = parts;
const name = namePart.replace("Recipe Name:", "").trim();
const description = descriptionPart.replace("Description:", "").trim();
const ingredients = ingredientsPart.replace("Ingredients:", "").trim();

// Step 2: Generate image using DALL·E API
const dalleImageUrl = await fetchDalleImage(name);

return { name, description, ingredients, image: dalleImageUrl };

} catch (error) {
console.error('Error fetching response from ChatGPT:', error.message);
throw new Error('Failed to fetch response from ChatGPT or generate image.');
}
};

/**
* Sends a request to the DALL·E API to generate an image for the recipe.
*
* @function fetchDalleImage
* @param {string} recipeName - The name of the recipe to generate an image for.
* @returns {string} - The URL of the generated image
* @throws {Error} - If the API request fails
*/
const fetchDalleImage = async (recipeName) => {
try {
const apiKey = process.env.REACT_APP_OPENAI_API_KEY;

const response = await axios.post(
'https://api.openai.com/v1/images/generations',
{
prompt: `A delicious and appetizing image of ${recipeName}`,
n: 1,
size: '512x512',
},
{
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
}
);

return response.data.data[0].url;
} catch (error) {
console.error('Error fetching image from DALL·E:', error.message);
throw new Error('Image generation failed.');
}
};

export default fetchChatGPTResponse;
Loading