So much progress! Yay!! Whats missing is the global storage of the filters. That is the final touch for searching.
181 lines
7.3 KiB
TypeScript
181 lines
7.3 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";
|
|
import { GetAuthenticatedUserMadeRecipes, GetAuthenticateUserViewedRecipes } from "../services/UserService";
|
|
import { type SearchFilters } from "../types/search";
|
|
|
|
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>("");
|
|
|
|
// TODO: Fetch other items when needed
|
|
// Fetch the recipe of the week
|
|
useEffect(() => {
|
|
async function fetch() {
|
|
const result_rotw: Recipe | ApiError = await GetRecipeOfTheWeek();
|
|
if (isApiError(result_rotw)) {
|
|
setError(result_rotw.message);
|
|
} else {
|
|
setRecipeOfTheWeek(result_rotw);
|
|
}
|
|
|
|
if (isLoggedIn) {
|
|
const result_made: Recipe[] | ApiError = await GetAuthenticatedUserMadeRecipes();
|
|
if (isApiError(result_made)) {
|
|
setError(result_made.message);
|
|
} else {
|
|
setMadeRecipes(result_made);
|
|
}
|
|
|
|
const result_viewed: Recipe[] | ApiError = await GetAuthenticateUserViewedRecipes();
|
|
if (isApiError(result_viewed)) {
|
|
setError(result_viewed.message);
|
|
} else {
|
|
setViewedRecipes(result_viewed);
|
|
}
|
|
}
|
|
|
|
}
|
|
void fetch();
|
|
}, [isLoggedIn]);
|
|
|
|
// 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 redirect={true} favorites={false} searchOnLoad={false} setRecipes={null} />
|
|
</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 >
|
|
</>
|
|
);
|
|
}
|