193 lines
7.6 KiB
TypeScript
193 lines
7.6 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import {
|
||
|
|
ArrowPathIcon,
|
||
|
|
ClockIcon,
|
||
|
|
FireIcon,
|
||
|
|
PlusIcon,
|
||
|
|
ScaleIcon,
|
||
|
|
} from "@heroicons/react/24/outline";
|
||
|
|
import Link from "next/link";
|
||
|
|
import { useEffect, useMemo, useState } from "react";
|
||
|
|
import { useSession } from "@/lib/auth-client";
|
||
|
|
import {
|
||
|
|
RecipeRating,
|
||
|
|
loadHellthData,
|
||
|
|
recipeRatingSummary,
|
||
|
|
saveHellthData,
|
||
|
|
uid,
|
||
|
|
} from "@/lib/hellth-data";
|
||
|
|
|
||
|
|
const recipeId = "recipe-eggs";
|
||
|
|
|
||
|
|
function MacroBar() {
|
||
|
|
return (
|
||
|
|
<div className="flex h-4 overflow-hidden rounded-full bg-gray-100">
|
||
|
|
<div className="w-[22%] bg-amber-400" />
|
||
|
|
<div className="w-[33%] bg-blue-500" />
|
||
|
|
<div className="flex-1 bg-green-500" />
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
export default function RecipePage() {
|
||
|
|
const { data: session } = useSession();
|
||
|
|
const userKey = session?.user?.id ?? session?.user?.email ?? null;
|
||
|
|
const displayName = session?.user?.name || session?.user?.email || "Du";
|
||
|
|
const [ratings, setRatings] = useState<RecipeRating[]>([]);
|
||
|
|
const [rating, setRating] = useState(5);
|
||
|
|
const [comment, setComment] = useState("");
|
||
|
|
const summary = useMemo(() => recipeRatingSummary(recipeId, ratings), [ratings]);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (!userKey) return;
|
||
|
|
const data = loadHellthData(userKey);
|
||
|
|
setRatings(data.recipeRatings);
|
||
|
|
const own = data.recipeRatings.find((item) => item.recipeId === recipeId && item.userKey === userKey);
|
||
|
|
if (own) {
|
||
|
|
setRating(own.rating);
|
||
|
|
setComment(own.comment ?? "");
|
||
|
|
}
|
||
|
|
}, [userKey]);
|
||
|
|
|
||
|
|
function saveRating() {
|
||
|
|
if (!userKey) return;
|
||
|
|
const data = loadHellthData(userKey);
|
||
|
|
const nextRating: RecipeRating = {
|
||
|
|
id: data.recipeRatings.find((item) => item.recipeId === recipeId && item.userKey === userKey)?.id ?? uid("rating"),
|
||
|
|
recipeId,
|
||
|
|
userKey,
|
||
|
|
displayName,
|
||
|
|
rating,
|
||
|
|
comment: comment.trim() || undefined,
|
||
|
|
createdAt: new Date().toISOString(),
|
||
|
|
};
|
||
|
|
const nextRatings = [
|
||
|
|
nextRating,
|
||
|
|
...data.recipeRatings.filter((item) => !(item.recipeId === recipeId && item.userKey === userKey)),
|
||
|
|
];
|
||
|
|
saveHellthData({ ...data, recipeRatings: nextRatings }, userKey);
|
||
|
|
setRatings(nextRatings);
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="mx-auto max-w-3xl pb-24">
|
||
|
|
<section className="rounded-[2.5rem] bg-white px-6 pb-8 pt-5 shadow-sm ring-1 ring-gray-100 sm:px-10">
|
||
|
|
<div className="mx-auto mb-10 h-2 w-14 rounded-full bg-gray-200" />
|
||
|
|
<div className="mx-auto flex h-56 w-56 items-center justify-center rounded-full bg-gradient-to-br from-amber-100 to-orange-200 text-6xl font-black text-orange-700 shadow-inner">
|
||
|
|
EM
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<h1 className="mt-10 text-center text-5xl font-black leading-none tracking-normal text-gray-950">
|
||
|
|
Eier-Muffins mit Paprika
|
||
|
|
</h1>
|
||
|
|
|
||
|
|
<div className="mt-10 grid grid-cols-3 gap-4 text-center">
|
||
|
|
<div>
|
||
|
|
<FireIcon className="mx-auto h-9 w-9 text-green-500" />
|
||
|
|
<p className="mt-3 text-3xl font-black text-gray-950">523 <span className="text-xl">kcal</span></p>
|
||
|
|
<p className="text-sm font-black text-gray-400">Pro Portion</p>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<ClockIcon className="mx-auto h-9 w-9 text-orange-500" />
|
||
|
|
<p className="mt-3 text-3xl font-black text-gray-950">20<span className="text-xl"> Min</span></p>
|
||
|
|
<p className="text-sm font-black text-gray-400">Kochzeit</p>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<ScaleIcon className="mx-auto h-9 w-9 text-blue-400" />
|
||
|
|
<p className="mt-3 text-3xl font-black text-gray-950">88<span className="text-xl">/100</span></p>
|
||
|
|
<p className="text-sm font-black text-gray-400">Score</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="mt-8 rounded-[2rem] bg-amber-50 p-5">
|
||
|
|
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||
|
|
<div>
|
||
|
|
<h2 className="text-xl font-black text-gray-950">User-Bewertung</h2>
|
||
|
|
<p className="mt-1 text-sm font-bold text-amber-800">
|
||
|
|
{summary.count ? `${summary.average}/5 aus ${summary.count} Bewertung(en)` : "Noch keine Bewertungen."}
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
<div className="flex gap-1">
|
||
|
|
{[1, 2, 3, 4, 5].map((value) => (
|
||
|
|
<button
|
||
|
|
key={value}
|
||
|
|
type="button"
|
||
|
|
onClick={() => setRating(value)}
|
||
|
|
className={`text-3xl ${value <= rating ? "text-amber-500" : "text-amber-200"}`}
|
||
|
|
aria-label={`${value} Sterne`}
|
||
|
|
title={`${value} Sterne`}
|
||
|
|
>
|
||
|
|
*
|
||
|
|
</button>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<textarea
|
||
|
|
value={comment}
|
||
|
|
onChange={(event) => setComment(event.target.value)}
|
||
|
|
placeholder="Kurzer Kommentar: schmeckt gut, sättigt, Meal-Prep geeignet..."
|
||
|
|
className="mt-4 min-h-24 w-full rounded-3xl border border-amber-100 bg-white px-4 py-3 text-sm font-semibold"
|
||
|
|
/>
|
||
|
|
<button type="button" onClick={saveRating} className="mt-3 rounded-full bg-gray-950 px-5 py-3 text-sm font-black text-white">
|
||
|
|
Bewertung speichern
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="my-10 h-px bg-gray-100" />
|
||
|
|
|
||
|
|
<h2 className="text-xl font-black text-gray-400">Makros</h2>
|
||
|
|
<div className="mt-6">
|
||
|
|
<MacroBar />
|
||
|
|
<div className="mt-6 grid grid-cols-3 gap-4">
|
||
|
|
<div>
|
||
|
|
<p className="text-sm font-black text-gray-400">Kohlenhydr.</p>
|
||
|
|
<p className="mt-2 text-4xl font-black text-gray-950">20 <span className="text-gray-300">g</span></p>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<p className="text-sm font-black text-gray-400">Fett</p>
|
||
|
|
<p className="mt-2 text-4xl font-black text-gray-950">30 <span className="text-gray-300">g</span></p>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<p className="text-sm font-black text-gray-400">Proteine</p>
|
||
|
|
<p className="mt-2 text-4xl font-black text-gray-950">41 <span className="text-gray-300">g</span></p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="my-10 h-px bg-gray-100" />
|
||
|
|
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<h2 className="text-xl font-black text-gray-400">Zutaten</h2>
|
||
|
|
<button type="button" className="text-lg font-black text-gray-400">1 Portionen</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="mt-6 rounded-[2rem] bg-gray-50 p-5">
|
||
|
|
{[
|
||
|
|
["Paprika", "150 g"],
|
||
|
|
["Eier", "3 Stück"],
|
||
|
|
["Hirtenkäse", "60 g"],
|
||
|
|
["Zwiebel", "1/2 Stück"],
|
||
|
|
].map(([name, amount]) => (
|
||
|
|
<div key={name} className="flex items-center justify-between border-b border-gray-200 py-4 last:border-0">
|
||
|
|
<p className="text-lg font-black text-gray-950">{name}</p>
|
||
|
|
<p className="text-lg font-black text-gray-400">{amount}</p>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<div className="fixed inset-x-0 bottom-20 z-40 mx-auto grid max-w-md grid-cols-2 gap-3 px-5 lg:bottom-6">
|
||
|
|
<button type="button" className="inline-flex items-center justify-center gap-2 rounded-full border border-gray-200 bg-white px-5 py-4 text-base font-black text-gray-950 shadow-sm">
|
||
|
|
<ArrowPathIcon className="h-5 w-5" />
|
||
|
|
Tauschen
|
||
|
|
</button>
|
||
|
|
<Link href="/" className="inline-flex items-center justify-center gap-2 rounded-full bg-gray-950 px-5 py-4 text-base font-black text-white shadow-sm">
|
||
|
|
<PlusIcon className="h-5 w-5" />
|
||
|
|
Protokollieren
|
||
|
|
</Link>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|