250 lines
9.5 KiB
TypeScript
250 lines
9.5 KiB
TypeScript
import { use, useEffect, useState } from "react";
|
|
import SalmonVideo from "../assets/videos/salmon_video.mp4";
|
|
import Banner from "../components/Banner";
|
|
import ROUTE_CONSTANTS from "../types/routes";
|
|
|
|
import RecipeLarge from "../components/cards/RecipeCardLarge";
|
|
import type { Recipe } from "../types/recipe";
|
|
import RecipeCardSmall from "../components/cards/RecipeCardSmall";
|
|
import ContentCardSmall from "../components/cards/ContentCardSmall";
|
|
import RecipeSearchBar from "../components/inputs/RecipeSearchBar";
|
|
import { GetRecipeOfTheWeek } from "../services/RecipeService";
|
|
import { isApiError, type ApiError } from "../types/api/error";
|
|
import { AuthContext } from "../context/AuthContext";
|
|
|
|
export default function Home() {
|
|
// Context
|
|
const { isLoggedIn } = use(AuthContext);
|
|
|
|
// Page state
|
|
const [recipeOfTheWeek, setRecipeOfTheWeek] = useState<Recipe | null>(null);
|
|
|
|
const [madeRecipes, setMadeRecipes] = useState<Recipe[]>([]);
|
|
const [viewedRecipes, setViewedRecipes] = useState<Recipe[]>([]);
|
|
|
|
const [error, setError] = useState<string>("");
|
|
|
|
|
|
// BUG: Remove these
|
|
useEffect(() => {
|
|
const recipe: Recipe = {
|
|
Id: 1,
|
|
Title: "Classic Pancakes",
|
|
Description: "Fluffy and delicious pancakes perfect for breakfast.",
|
|
Instructions: [
|
|
"In a bowl, mix all the dry ingredients.",
|
|
"In another bowl, whisk the wet ingredients.",
|
|
"Combine both mixes until smooth.",
|
|
"Heat a non-stick skillet and pour batter.",
|
|
"Cook until bubbles form, flip and cook the other side.",
|
|
"Serve warm with syrup."
|
|
],
|
|
Serves: 4,
|
|
Difficulty: 2, // scale 1-5 (example)
|
|
Duration: {
|
|
Total: 20,
|
|
Prep: 5,
|
|
Cook: 15
|
|
},
|
|
Category: "breakfast",
|
|
Ingredients: [
|
|
{ Name: "Flour", Quantity: "2 cups" },
|
|
{ Name: "Milk", Quantity: "1.5 cups" },
|
|
{ Name: "Egg", Quantity: "1 large" },
|
|
{ Name: "Baking Powder", Quantity: "2 teaspoons" },
|
|
{ Name: "Salt", Quantity: "0.5 teaspoon" },
|
|
{ Name: "Sugar", Quantity: "1 tablespoon" }
|
|
],
|
|
UserId: 101,
|
|
Modified: new Date("2025-10-30T09:00:00"),
|
|
Created: new Date("2025-10-01T08:30:00"),
|
|
Tags: [
|
|
{ Id: 1, Name: "easy", Created: new Date("2025-01-01T12:00:00") },
|
|
{ Id: 2, Name: "quick", Created: new Date("2025-01-02T12:00:00") },
|
|
{ Id: 3, Name: "breakfast", Created: new Date("2025-01-03T12:00:00") }
|
|
],
|
|
Favorite: true
|
|
};
|
|
|
|
const recipe2: Recipe = {
|
|
Id: 2,
|
|
Title: "Classic Pancakes",
|
|
Description: "Fluffy and delicious pancakes perfect for breakfast.",
|
|
Instructions: [
|
|
"In a bowl, mix all the dry ingredients.",
|
|
"In another bowl, whisk the wet ingredients.",
|
|
"Combine both mixes until smooth.",
|
|
"Heat a non-stick skillet and pour batter.",
|
|
"Cook until bubbles form, flip and cook the other side.",
|
|
"Serve warm with syrup."
|
|
],
|
|
Serves: 4,
|
|
Difficulty: 2, // scale 1-5 (example)
|
|
Duration: {
|
|
Total: 20,
|
|
Prep: 5,
|
|
Cook: 15
|
|
},
|
|
Category: "breakfast",
|
|
Ingredients: [
|
|
{ Name: "Flour", Quantity: "2 cups" },
|
|
{ Name: "Milk", Quantity: "1.5 cups" },
|
|
{ Name: "Egg", Quantity: "1 large" },
|
|
{ Name: "Baking Powder", Quantity: "2 teaspoons" },
|
|
{ Name: "Salt", Quantity: "0.5 teaspoon" },
|
|
{ Name: "Sugar", Quantity: "1 tablespoon" }
|
|
],
|
|
UserId: 101,
|
|
Modified: new Date("2025-10-30T09:00:00"),
|
|
Created: new Date("2025-10-01T08:30:00"),
|
|
Tags: [
|
|
{ Id: 1, Name: "easy", Created: new Date("2025-01-01T12:00:00") },
|
|
{ Id: 2, Name: "quick", Created: new Date("2025-01-02T12:00:00") },
|
|
{ Id: 3, Name: "breakfast", Created: new Date("2025-01-03T12:00:00") }
|
|
],
|
|
Favorite: true
|
|
};
|
|
|
|
setRecipeOfTheWeek(recipe);
|
|
|
|
const recipes: Recipe[] = [recipe, recipe2];
|
|
setMadeRecipes(recipes);
|
|
setViewedRecipes(recipes);
|
|
}, []);
|
|
|
|
// TODO: Fetch other items when needed
|
|
// Fetch the recipe of the week
|
|
useEffect(() => {
|
|
async function fetch() {
|
|
const result: Recipe | ApiError = await GetRecipeOfTheWeek();
|
|
if (isApiError(result)) {
|
|
setError(result.message);
|
|
return;
|
|
}
|
|
setRecipeOfTheWeek(result);
|
|
}
|
|
void fetch();
|
|
}, []);
|
|
|
|
// BUG: Prob remove
|
|
useEffect(() => {
|
|
if (error)
|
|
console.error(error);
|
|
}, [error]);
|
|
|
|
return (
|
|
<>
|
|
{/* Intro Section */}
|
|
<section className="w-full h-fit mb-16">
|
|
<div className="relative">
|
|
<video autoPlay loop muted playsInline>
|
|
<source src={SalmonVideo} type="video/mp4" />
|
|
</video>
|
|
<h1 className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-center
|
|
text-white text-3xl w-4/5 font-bold z-10">
|
|
Discover Your Next Favorite Meal
|
|
</h1>
|
|
</div>
|
|
<p className="leading-relaxed p-4 my-8">
|
|
Welcome to your ultimate recipe hub! Whether you're a seasoned chef or just starting your culinary adventure,
|
|
we're here to inspire. Explore thousands of delicious recipes, from quick weeknight dinners to gourmet delights,
|
|
all at your fingertips. Find exactly what you're craving with our powerful search and intuitive filters, or
|
|
browse our trending dishes for fresh ideas.
|
|
</p>
|
|
</section>
|
|
|
|
{/* Search Section */}
|
|
<section className="w-full flex flex-col items-center justify-center my-8 py-4">
|
|
<Banner content="Craving Something Specific?" />
|
|
<div className="w-full md:w-3/4">
|
|
<RecipeSearchBar filters={null} redirect={true} favorites={false} searchOnLoad={false} />
|
|
</div>
|
|
<div className="hidden" id="result-list"></div>
|
|
</section>
|
|
|
|
{/* Highlight Section */}
|
|
<section className="w-full flex flex-col items-center justify-center my-8 py-4">
|
|
<Banner content="Recipe of the Week!" />
|
|
<p className="leading-relaxed p-4 my-8">
|
|
Our 'Recipe of the Week' is the cream of the crop! We handpick it by looking at what recipes
|
|
our community loves most. This isn't just about how many people view a recipe; it's also about
|
|
how many times it's been made, liked, reviewed, and its average rating, all combined to find
|
|
the true fan favorite of the week. It's our way of highlighting the best recipes that truly
|
|
resonate with our users!
|
|
</p>
|
|
<div className="flex items-center justify-center w-full">
|
|
<RecipeLarge recipe={recipeOfTheWeek} />
|
|
</div>
|
|
</section>
|
|
|
|
{/* Lists Section */}
|
|
<section className="w-full flex flex-col items-center justify-center my-8 py-4">
|
|
<Banner content="Take Another Look." />
|
|
<div className="w-full">
|
|
<h3 className="text-lg mt-8 mx-4">Recently viewed</h3>
|
|
{isLoggedIn ?
|
|
<div className="flex overflow-x-auto gap-x-4 mx-4 my-4">
|
|
{viewedRecipes && viewedRecipes.length > 0 ? (
|
|
<>
|
|
{viewedRecipes.map((recipe: Recipe) => (
|
|
<RecipeCardSmall key={recipe.Id} recipe={recipe} />
|
|
))}
|
|
<ContentCardSmall content="View full history..." target={ROUTE_CONSTANTS.History} />
|
|
</>
|
|
) : (
|
|
<p className="text-sm">No recently viewed recipes</p>
|
|
)}
|
|
</div>
|
|
:
|
|
<div className="my-2 mx-4 text-gray-800">
|
|
<a className="underline" href={ROUTE_CONSTANTS.Login}>
|
|
<p className="text-sm">Log in to view metrics.</p>
|
|
</a>
|
|
</div>
|
|
}
|
|
<h3 className="text-lg mt-8 mx-4">Make again</h3>
|
|
{isLoggedIn ?
|
|
<div className="flex overflow-x-auto gap-x-4 mx-4 my-4">
|
|
{madeRecipes && madeRecipes.length > 0 ? (
|
|
<>
|
|
{madeRecipes.map((recipe: Recipe) => (
|
|
<RecipeCardSmall key={recipe.Id} recipe={recipe} />
|
|
))}
|
|
<ContentCardSmall content="View full history..." target={ROUTE_CONSTANTS.History} />
|
|
</>
|
|
) : (
|
|
<p className="text-sm">No recently made recipes</p>
|
|
)}
|
|
</div>
|
|
:
|
|
<div className="my-2 mx-4 text-gray-800">
|
|
<a className="underline" href={ROUTE_CONSTANTS.Login}>
|
|
<p className="text-sm">Log in to view metrics.</p>
|
|
</a>
|
|
</div>
|
|
}
|
|
</div>
|
|
</section >
|
|
|
|
{/* Call-to-Action Section */}
|
|
< section
|
|
className="w-full flex flex-col items-center justify-center mt-16 py-8 md:py-12 bg-gradient-to-br from-blue-100 to-purple-100 text-center" >
|
|
<h2 className="text-2xl md:text-3xl font-extrabold text-gray-800 mb-6 px-4">
|
|
Unleash Your Inner Chef!
|
|
</h2>
|
|
<p className="text-md md:text-lg text-gray-700 max-w-2xl mb-10 px-4 leading-relaxed">
|
|
Have a unique recipe idea? Want to share your culinary masterpiece with the world?
|
|
It's time to bring your creations to life!
|
|
</p>
|
|
<a href={ROUTE_CONSTANTS.Create} className="flex items-center justify-center
|
|
bg-gradient-to-r from-blue-400 to-blue-600 text-white
|
|
px-12 py-5 rounded-full shadow-sm hover:shadow-md
|
|
transition-all duration-300 ease-in-out shadow-blue-700
|
|
text-lg md:text-2xl font-bold uppercase tracking-wide">
|
|
Create Your Recipe!
|
|
</a>
|
|
</section >
|
|
</>
|
|
);
|
|
}
|