77 lines
3.6 KiB
TypeScript
77 lines
3.6 KiB
TypeScript
import { useNavigate } from "react-router-dom";
|
|
import { EngagementViewRecipe } from "../../services/EngagementService";
|
|
import { isApiError } from "../../types/api/error";
|
|
import type { Recipe } from "../../types/recipe";
|
|
import ServingSizeIconSmall from "../icons/ServingSizeIconSmall";
|
|
import StarIcon from "../icons/StarIcon";
|
|
import TimeIconSmall from "../icons/TimeIconSmall";
|
|
import RecipePlaceholder from "../../assets/images/recipe_placeholder.png";
|
|
|
|
interface RecipeSearchResultProps {
|
|
recipe: Recipe;
|
|
};
|
|
|
|
export default function RecipeSearchResult({ recipe }: RecipeSearchResultProps) {
|
|
const navigate = useNavigate();
|
|
|
|
// HANDLERS
|
|
const clickHandler = async () => {
|
|
// Navigate first, so it feels faster
|
|
await navigate(`/v2/web/recipe/${recipe.Id}`);
|
|
|
|
const result = await EngagementViewRecipe(recipe.Id);
|
|
if (isApiError(result)) {
|
|
console.error(result.message);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div onClick={() => void clickHandler()} className="w-full p-2 border-b border-gray-200 hover:bg-gray-100 duration-200 flex items-center flex-col md:flex-row even:bg-[#f8f8f8] cursor-pointer">
|
|
<img className="bg-gray-50 size-56 md:size-40 rounded-md border border-gray-200 shadow-sm shadow-gray-100" src={RecipePlaceholder} alt="Recipe placeholder image" />
|
|
<div className="text-gray-700 p-4 flex flex-col items-center md:items-start w-full">
|
|
<div className="flex flex-col md:flex-row items-center md:items-start justify-between w-full">
|
|
<div className="flex flex-col items-center md:items-start">
|
|
<h3 className="text-xl font-semibold text-black pb-1 text-center">
|
|
{recipe.Title} <span className="text-sm font-normal hidden md:inline">{recipe.Category}</span>
|
|
</h3>
|
|
<div className="text-sm flex gap-x-3 gap-y-1 items-center flex-wrap">
|
|
<span className="flex gap-x-1 align-center">
|
|
<TimeIconSmall />
|
|
{recipe.Duration.Total} min
|
|
</span>
|
|
<span className="flex gap-x-1 align-center">
|
|
{Array.from({ length: recipe.Difficulty }).map((_, i) => (
|
|
<StarIcon key={`${recipe.Id}-filled-${i}`} size={4} filled={true} />
|
|
))}
|
|
{Array.from({ length: 5 - (recipe.Difficulty) }).map((_, i) => (
|
|
<StarIcon key={`${recipe.Id}-unfilled-${i}`} size={4} filled={false} />
|
|
))}
|
|
</span>
|
|
<span className="flex gap-x-1 align-center">
|
|
<ServingSizeIconSmall />
|
|
Serves {recipe.Serves}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div className="mb-2 mt-4 md:my-0 hidden md:block">
|
|
{recipe.Favorite && (
|
|
<svg className="h-6 text-red-500" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<path
|
|
d="M2 9.1371C2 14 6.01943 16.5914 8.96173 18.9109C10 19.7294 11 20.5 12 20.5C13 20.5 14 19.7294 15.0383 18.9109C17.9806 16.5914 22 14 22 9.1371C22 4.27416 16.4998 0.825464 12 5.50063C7.50016 0.825464 2 4.27416 2 9.1371Z"
|
|
fill="currentColor"></path>
|
|
</svg>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<div className="my-1">
|
|
<p className="text-xs text-gray-500 italic">{recipe.Tags.map(x => x.Name).join(", ")}</p>
|
|
</div>
|
|
<p className="text-sm text-center md:text-left overflow-hidden text-ellipsis break-all"
|
|
style={{ display: "-webkit-box", WebkitLineClamp: 3, WebkitBoxOrient: "vertical" }}>
|
|
{recipe.Description}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|