diff --git a/backend/backendServer.js b/backend/backendServer.js index 919025b..74f3ea6 100644 --- a/backend/backendServer.js +++ b/backend/backendServer.js @@ -15,7 +15,7 @@ app.get("/backend/test", async (req, res) => { // ### GET ALL RECIPES ### app.get("/backend/recipes", async (req, res) => { try { - const recipes = await db("recipes").select("id", "name", "cuisine"); + const recipes = await db("recipes").select("id", "name", "cuisine", "stars", "prep_minutes", "cook_minutes"); res.json(recipes); } catch (err) { console.log(err); diff --git a/frontend/src/components/CookbookRecipeTile.tsx b/frontend/src/components/CookbookRecipeTile.tsx index fd2ad97..c090f26 100644 --- a/frontend/src/components/CookbookRecipeTile.tsx +++ b/frontend/src/components/CookbookRecipeTile.tsx @@ -1,55 +1,21 @@ -import { useState } from 'react'; import { type RecipeSmall } from "../types/Recipe" -import Modal from '../components/Modal.tsx' -import DemoModal from '../components/DemoModal.tsx' import { Link } from 'react-router-dom'; +import StarRating from '../components/StarRating.tsx' -interface CookbookRecipeTileProps { - recipe: RecipeSmall; - handleDelete: (id: number | undefined) => void; -} - -function CookbookRecipeTile({ recipe, handleDelete }: CookbookRecipeTileProps) { - const [showConfirmModal, setShowConfirmModal] = useState(false); - const [showDemoModal, setShowDemoModal] = useState(false); - - const openModal = () => { setShowConfirmModal(true) }; - const closeModal = () => { setShowConfirmModal(false) }; - const confirmDelete = () => { - if (process.env.NODE_ENV === 'demo') { - closeModal(); - setShowDemoModal(true); - } else { - handleDelete(recipe.id); - closeModal(); - } - }; - +function CookbookRecipeTile({ recipe }: { recipe: RecipeSmall }) { return ( -
+
-

{recipe.name}

- +

{recipe.name}

+
+ {recipe.cuisine} +
+
+
+ ⏰ {recipe.prep_minutes + recipe.cook_minutes} min + { }} />
- - {showDemoModal && ( - setShowDemoModal(false)} - closeModal={() => setShowDemoModal(false)} - /> - )}
); }; diff --git a/frontend/src/components/RecipeBookTabs.tsx b/frontend/src/components/RecipeBookTabs.tsx index 7f678b1..890e6fb 100644 --- a/frontend/src/components/RecipeBookTabs.tsx +++ b/frontend/src/components/RecipeBookTabs.tsx @@ -30,9 +30,6 @@ const RecipeBookTabs = () => { useEffect(() => { if (!lastViewedRecipeId) { loadRandomRecipeId(); - } else { - console.log('id found', lastViewedRecipeId) - } }, []); diff --git a/frontend/src/components/StarRating.tsx b/frontend/src/components/StarRating.tsx index 91af1e0..cd5d5bc 100644 --- a/frontend/src/components/StarRating.tsx +++ b/frontend/src/components/StarRating.tsx @@ -13,7 +13,7 @@ const StarRating = ({ rating, onRatingChange }: StarRatingProps) => { onRatingChange(index)} - style={{ color: index <= rating ? 'gold' : 'gray', fontSize: '2rem', cursor: 'pointer' }} + style={{ color: index <= rating ? '#FFB800' : 'gray', fontSize: '1.5rem', cursor: 'pointer' }} > ★ diff --git a/frontend/src/pages/AddRecipe.tsx b/frontend/src/pages/AddRecipe.tsx index 9ce744c..54f61c4 100644 --- a/frontend/src/pages/AddRecipe.tsx +++ b/frontend/src/pages/AddRecipe.tsx @@ -5,7 +5,6 @@ import AddBulkIngredients from "../components/AddBulkIngredients.tsx" import AddBulkSteps from "../components/AddBulkSteps.tsx" import StarRating from "../components/StarRating.tsx" import DemoModal from '../components/DemoModal.tsx' -// import { type Step } from "../types/Recipe"; interface Step { step_number: number; @@ -14,7 +13,6 @@ interface Step { function AddRecipe() { const [newRecipeId, setNewRecipeId] = useState(null); - const navigate = useNavigate(); const [ingredients, setIngredients] = useState([]); const [steps, setSteps] = useState([]); const [showBulkForm, setShowBulkForm] = useState(true); @@ -25,6 +23,7 @@ function AddRecipe() { const [prepMinutes, setPrepMinutes] = useState(5); const [cookMinutes, setCookMinutes] = useState(5); const [showDemoModal, setShowDemoModal] = useState(false); + const navigate = useNavigate(); const addRecipeForm = async (event: React.FormEvent) => { event.preventDefault(); diff --git a/frontend/src/pages/Index.tsx b/frontend/src/pages/Index.tsx index 3f1c8bb..894492e 100644 --- a/frontend/src/pages/Index.tsx +++ b/frontend/src/pages/Index.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from "react"; import CookbookRecipeTile from "../components/CookbookRecipeTile.tsx" -import { getRecipes, deleteRecipe } from "../services/frontendApi.js"; +import { getRecipes } from "../services/frontendApi.js"; import { type RecipeSmall } from "../types/Recipe.ts" @@ -18,12 +18,13 @@ function AllRecipes() { try { const recipes = await getRecipes(); setRecipes(recipes); - console.log(recipes) + if (process.env.NODE_ENV === 'dev') { + console.log(recipes) + } const uniqueCuisines: string[] = recipes.length > 0 ? Array.from(new Set(recipes.map((recipe: RecipeSmall) => recipe.cuisine))) : []; setCuisines(uniqueCuisines) - console.log(cuisines) } catch (error) { console.log(error); setError("Failed to load recipes..."); @@ -36,14 +37,6 @@ function AllRecipes() { } }, [shouldFetchRecipes]); - const handleDelete = async (id: number | void) => { - try { - await deleteRecipe(id); - setShouldFetchRecipes(true); - } catch (error) { - console.error("Error deleting recipe:", error); - } - }; const filteredRecipes = selectedCuisine ? recipes.filter(recipe => recipe.cuisine === selectedCuisine) : recipes; return ( @@ -74,7 +67,7 @@ function AllRecipes() {
{filteredRecipes.map((recipe) => ( recipe.name.toLowerCase().includes(searchQuery.toLowerCase()) && - + ))}
diff --git a/frontend/src/pages/RecipePage.tsx b/frontend/src/pages/RecipePage.tsx index 83ff1ad..dbc92de 100644 --- a/frontend/src/pages/RecipePage.tsx +++ b/frontend/src/pages/RecipePage.tsx @@ -1,9 +1,11 @@ -import { useParams } from "react-router-dom"; +import { useParams, useNavigate, Link } from "react-router-dom"; import { useState, useEffect } from "react"; -import { getRecipeById } from "../services/frontendApi.js"; +import { getRecipeById, deleteRecipe } from "../services/frontendApi.js"; import { type Recipe, type Ingredient } from "../types/Recipe" import StarRating from "../components/StarRating.tsx" import { setDBStars } from "../services/frontendApi.js"; +import Modal from '../components/Modal.tsx' +import DemoModal from '../components/DemoModal.tsx' function RecipePage() { const [recipe, setRecipe] = useState({ @@ -13,21 +15,43 @@ function RecipePage() { }); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); + const [stars, setStars] = useState(0); + const [initialStars, setInitialStars] = useState(null); + const [showConfirmModal, setShowConfirmModal] = useState(false); + const [showDemoModal, setShowDemoModal] = useState(false); const { id } = useParams(); const isWebSource = recipe && recipe.details && recipe.details.author ? /http|com/.test(recipe.details.author) //etc : false; - const [stars, setStars] = useState(0); - const [initialStars, setInitialStars] = useState(null); + const navigate = useNavigate(); + + const openModal = () => { setShowConfirmModal(true) }; + const closeModal = () => { setShowConfirmModal(false) }; + + const confirmDelete = () => { + if (process.env.NODE_ENV === 'demo') { + closeModal(); + setShowDemoModal(true); + } else { + handleDelete(recipe.details.id); + closeModal(); + } + }; useEffect(() => { const loadRecipe = async () => { try { const recipe = await getRecipeById(id); - setRecipe(recipe); - setStars(recipe.details?.stars ?? 0) - setInitialStars(recipe.details?.stars ?? 0); - console.log(recipe) + if (!recipe.details) { + setError("Sorry, this recipe no longer exists") + } else { + setRecipe(recipe); + setStars(recipe.details?.stars ?? 0) + setInitialStars(recipe.details?.stars ?? 0); + if (process.env.NODE_ENV === 'dev') { + console.log(recipe) + } + } } catch (error) { console.log(error); setError("Failed to load recipes..."); @@ -49,16 +73,36 @@ function RecipePage() { updateStarsInDB(); }, [stars]); + + const handleDelete = async (id: number | void) => { + try { + await deleteRecipe(id); + navigate('/') + } catch (error) { + console.error("Error deleting recipe:", error); + } + }; return (
- {error &&
{error}
} {loading ? (
Loading...
+ ) : error ? ( +
+
{error}
+
+ + Return to Cookbook + +
+
) : ( -
+
+

{recipe.details.name}

{recipe.details.cuisine}

@@ -110,8 +154,23 @@ function RecipePage() {
+ ) + } + + {showDemoModal && ( + setShowDemoModal(false)} + closeModal={() => setShowDemoModal(false)} + /> )} -
+ ); } diff --git a/frontend/src/services/frontendApi.js b/frontend/src/services/frontendApi.js index 4158c4a..69117c9 100644 --- a/frontend/src/services/frontendApi.js +++ b/frontend/src/services/frontendApi.js @@ -23,6 +23,9 @@ export const getRecipeIngredients = async () => { export const getRecipeById = async (id) => { const response = await fetch(`${baseUrl}backend/recipe/${id}`); const data = await response.json(); + if (!data || !data.details) { + return { details: null }; + } return data; }; diff --git a/frontend/src/types/Recipe.ts b/frontend/src/types/Recipe.ts index c988ea7..97298aa 100644 --- a/frontend/src/types/Recipe.ts +++ b/frontend/src/types/Recipe.ts @@ -29,6 +29,9 @@ interface RecipeSmall { id: number; name: string; cuisine: string; + stars: number; + cook_minutes: number; + prep_minutes: number; }