Add new regional recipes and enhance the horoscope page functionality
Introduce a new /rezepte page with 21 regional recipes featuring AI-generated images. Enhance the /horoskop page with element colors, star ratings, and navigation. Update navigation, routing, and widgets to support these new features. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 413891e8-d784-4bea-b9f5-91a5a68316b4 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: ca8c12e6-1e00-476d-86cc-c529591779ac Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/413891e8-d784-4bea-b9f5-91a5a68316b4/oYq1Msd Replit-Helium-Checkpoint-Created: true
This commit is contained in:
@@ -30,6 +30,16 @@ export default function Footer() {
|
||||
<span className="text-muted-foreground cursor-pointer hover:text-primary transition-colors" data-testid="link-footer-gallery">Fotogalerie</span>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/horoskop">
|
||||
<span className="text-muted-foreground cursor-pointer hover:text-primary transition-colors" data-testid="link-footer-horoskop">Horoskop</span>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/rezepte">
|
||||
<span className="text-muted-foreground cursor-pointer hover:text-primary transition-colors" data-testid="link-footer-rezepte">Rezepte</span>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -10,6 +10,7 @@ const navItems = [
|
||||
{ label: "Video", href: "/videos" },
|
||||
{ label: "Galerie", href: "/gallery" },
|
||||
{ label: "Horoskop", href: "/horoskop" },
|
||||
{ label: "Rezepte", href: "/rezepte" },
|
||||
];
|
||||
|
||||
export default function Header() {
|
||||
|
||||
@@ -1,90 +1,14 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { useLocation } from "wouter";
|
||||
import { Star } from "lucide-react";
|
||||
|
||||
const SIGNS = [
|
||||
{ name: "Widder", symbol: "♈", date: "21.03 – 19.04", element: "Feuer" },
|
||||
{ name: "Stier", symbol: "♉", date: "20.04 – 20.05", element: "Erde" },
|
||||
{ name: "Zwillinge", symbol: "♊", date: "21.05 – 20.06", element: "Luft" },
|
||||
{ name: "Krebs", symbol: "♋", date: "21.06 – 22.07", element: "Wasser" },
|
||||
{ name: "Löwe", symbol: "♌", date: "23.07 – 22.08", element: "Feuer" },
|
||||
{ name: "Jungfrau", symbol: "♍", date: "23.08 – 22.09", element: "Erde" },
|
||||
{ name: "Waage", symbol: "♎", date: "23.09 – 22.10", element: "Luft" },
|
||||
{ name: "Skorpion", symbol: "♏", date: "23.10 – 21.11", element: "Wasser" },
|
||||
{ name: "Schütze", symbol: "♐", date: "22.11 – 21.12", element: "Feuer" },
|
||||
{ name: "Steinbock", symbol: "♑", date: "22.12 – 19.01", element: "Erde" },
|
||||
{ name: "Wassermann", symbol: "♒", date: "20.01 – 18.02", element: "Luft" },
|
||||
{ name: "Fische", symbol: "♓", date: "19.02 – 20.03", element: "Wasser" },
|
||||
];
|
||||
|
||||
const DAILY_TEXTS = [
|
||||
"Ein wunderbarer Tag für neue Begegnungen. Die Sterne stehen günstig für spontane Entscheidungen.",
|
||||
"Heute ist Geduld gefragt. Lassen Sie sich Zeit und genießen Sie die kleinen Freuden des Alltags.",
|
||||
"Kommunikation steht im Vordergrund. Ein gutes Gespräch kann heute Wunder wirken.",
|
||||
"Vertrauen Sie auf Ihre Intuition. Ihr Bauchgefühl weist Ihnen den richtigen Weg.",
|
||||
"Kreativität und Mut werden heute belohnt. Zeigen Sie, was in Ihnen steckt!",
|
||||
"Ordnung und Struktur bringen heute den Erfolg. Nutzen Sie die Energie für wichtige Aufgaben.",
|
||||
"Harmonie und Ausgeglichenheit bestimmen den Tag. Genießen Sie die Zeit mit Ihren Liebsten.",
|
||||
"Tiefgründige Erkenntnisse warten auf Sie. Nehmen Sie sich Zeit für Reflexion.",
|
||||
"Abenteuerlust liegt in der Luft! Planen Sie etwas Besonderes für heute.",
|
||||
"Disziplin und Ausdauer zahlen sich aus. Bleiben Sie an Ihren Zielen dran.",
|
||||
"Originelle Ideen kommen heute von ganz allein. Lassen Sie Ihrer Fantasie freien Lauf.",
|
||||
"Einfühlungsvermögen und Mitgefühl stärken heute Ihre Beziehungen.",
|
||||
];
|
||||
|
||||
function getDailyText(signIndex: number): string {
|
||||
const dayOfYear = Math.floor((Date.now() - new Date(new Date().getFullYear(), 0, 0).getTime()) / 86400000);
|
||||
return DAILY_TEXTS[(signIndex + dayOfYear) % DAILY_TEXTS.length];
|
||||
}
|
||||
|
||||
function HoroscopeModal({ onClose, initialSign }: { onClose: () => void; initialSign: number }) {
|
||||
const [selected, setSelected] = useState(initialSign);
|
||||
import { Star, Heart, Briefcase, TrendingUp, ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { SIGNS, ELEMENT_COLORS, getHoroscope, getRating } from "@/lib/horoscope-data";
|
||||
|
||||
function MiniStars({ count, max = 5 }: { count: number; max?: number }) {
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 bg-black/80 flex items-center justify-center p-4" onClick={onClose} data-testid="modal-horoscope">
|
||||
<div
|
||||
className="bg-card rounded-xl border border-card-border max-w-lg w-full max-h-[80vh] overflow-y-auto p-6"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h2 className="text-lg font-bold text-foreground flex items-center gap-2">
|
||||
<Star className="w-5 h-5 text-primary" />
|
||||
Tageshoroskop
|
||||
</h2>
|
||||
<button onClick={onClose} className="text-muted-foreground hover:text-foreground" data-testid="button-horoscope-close">
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-4 sm:grid-cols-6 gap-2 mb-6">
|
||||
{SIGNS.map((sign, i) => (
|
||||
<button
|
||||
key={sign.name}
|
||||
onClick={() => setSelected(i)}
|
||||
className={`flex flex-col items-center p-2 rounded-lg transition-colors ${
|
||||
i === selected ? "bg-primary/20 border border-primary" : "hover:bg-muted border border-transparent"
|
||||
}`}
|
||||
data-testid={`button-sign-${sign.name}`}
|
||||
>
|
||||
<span className="text-2xl">{sign.symbol}</span>
|
||||
<span className="text-[10px] text-muted-foreground mt-1">{sign.name}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="bg-muted/50 rounded-lg p-4">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<span className="text-3xl">{SIGNS[selected].symbol}</span>
|
||||
<div>
|
||||
<h3 className="font-bold text-foreground">{SIGNS[selected].name}</h3>
|
||||
<p className="text-xs text-muted-foreground">{SIGNS[selected].date} · {SIGNS[selected].element}</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-foreground/80 leading-relaxed mt-3">
|
||||
{getDailyText(selected)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-px">
|
||||
{Array.from({ length: max }).map((_, i) => (
|
||||
<div key={i} className={`w-1.5 h-1.5 rounded-full ${i < count ? "bg-amber-400" : "bg-white/10"}`} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -95,6 +19,7 @@ export function HoroscopeWidget() {
|
||||
const [paused, setPaused] = useState(false);
|
||||
|
||||
const next = useCallback(() => setIndex((i) => (i + 1) % SIGNS.length), []);
|
||||
const prev = useCallback(() => setIndex((i) => (i - 1 + SIGNS.length) % SIGNS.length), []);
|
||||
|
||||
useEffect(() => {
|
||||
if (paused) return;
|
||||
@@ -103,30 +28,76 @@ export function HoroscopeWidget() {
|
||||
}, [paused, next]);
|
||||
|
||||
const sign = SIGNS[index];
|
||||
const horoscope = getHoroscope(index);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="bg-card rounded-lg border border-card-border overflow-hidden h-full w-full cursor-pointer group hover:border-primary/50 transition-colors flex flex-col"
|
||||
className="rounded-lg border border-card-border overflow-hidden h-full w-full cursor-pointer group hover:border-primary/50 transition-colors flex flex-col"
|
||||
style={{ background: "linear-gradient(135deg, hsl(250 30% 14%), hsl(270 25% 10%))" }}
|
||||
onMouseEnter={() => setPaused(true)}
|
||||
onMouseLeave={() => setPaused(false)}
|
||||
onClick={() => navigate("/horoskop")}
|
||||
data-testid="widget-horoscope"
|
||||
>
|
||||
<div className="p-3 flex items-center gap-2 border-b border-card-border flex-shrink-0">
|
||||
<Star className="w-4 h-4 text-primary" />
|
||||
<h3 className="font-bold text-card-foreground text-sm">Horoskop</h3>
|
||||
<div className="p-3 flex items-center justify-between border-b border-white/10 flex-shrink-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<Star className="w-4 h-4 text-amber-400" />
|
||||
<h3 className="font-bold text-white text-sm">Horoskop</h3>
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); prev(); }}
|
||||
className="w-5 h-5 rounded-full bg-white/10 hover:bg-white/20 flex items-center justify-center transition-colors"
|
||||
data-testid="button-horoscope-prev"
|
||||
>
|
||||
<ChevronLeft className="w-3 h-3 text-white/70" />
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); next(); }}
|
||||
className="w-5 h-5 rounded-full bg-white/10 hover:bg-white/20 flex items-center justify-center transition-colors"
|
||||
data-testid="button-horoscope-next"
|
||||
>
|
||||
<ChevronRight className="w-3 h-3 text-white/70" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-5 flex-1 flex flex-col justify-center items-center text-center">
|
||||
<span className="text-7xl mb-4 transition-all duration-500 block">{sign.symbol}</span>
|
||||
<p className="font-bold text-card-foreground text-lg">{sign.name}</p>
|
||||
<p className="text-xs text-muted-foreground mt-1">{sign.date}</p>
|
||||
<p className="text-sm text-muted-foreground mt-4 leading-relaxed">{getDailyText(index)}</p>
|
||||
<div className="flex justify-center gap-1 mt-5">
|
||||
<div className="flex-1 flex flex-col">
|
||||
<div className="p-4 flex items-center gap-3">
|
||||
<div className="w-14 h-14 rounded-xl bg-white/5 border border-white/10 flex items-center justify-center flex-shrink-0">
|
||||
<span className="text-3xl">{sign.symbol}</span>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="font-bold text-lg text-white">{sign.name}</p>
|
||||
<p className="text-[10px] text-white/50">{sign.date}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-1 px-3 py-1.5 border-t border-b border-white/5">
|
||||
<div className="flex flex-col items-center gap-0.5">
|
||||
<Heart className="w-3 h-3 text-rose-400" />
|
||||
<MiniStars count={getRating(index, "love")} />
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-0.5">
|
||||
<Briefcase className="w-3 h-3 text-amber-400" />
|
||||
<MiniStars count={getRating(index, "career")} />
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-0.5">
|
||||
<TrendingUp className="w-3 h-3 text-emerald-400" />
|
||||
<MiniStars count={getRating(index, "health")} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-3 pb-3 pt-2 flex-1 flex flex-col justify-between">
|
||||
<p className="text-xs text-white/60 leading-relaxed line-clamp-5">{horoscope.general}</p>
|
||||
<p className="text-[10px] text-amber-400 mt-2 group-hover:underline">Mehr lesen</p>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center gap-1 pb-3">
|
||||
{SIGNS.map((_, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={(e) => { e.stopPropagation(); setIndex(i); }}
|
||||
className={`w-1.5 h-1.5 rounded-full transition-all ${i === index ? "bg-primary w-3" : "bg-muted-foreground/30 hover:bg-muted-foreground/50"}`}
|
||||
className={`w-1.5 h-1.5 rounded-full transition-all ${i === index ? "bg-amber-400 w-3" : "bg-white/15 hover:bg-white/30"}`}
|
||||
data-testid={`button-horoscope-dot-${i}`}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -1,144 +1,57 @@
|
||||
import { useState } from "react";
|
||||
import { ChefHat, X, Clock, Users } from "lucide-react";
|
||||
import { useLocation } from "wouter";
|
||||
import { ChefHat, Clock, Users } from "lucide-react";
|
||||
|
||||
interface Recipe {
|
||||
interface WidgetRecipe {
|
||||
title: string;
|
||||
image: string;
|
||||
time: string;
|
||||
servings: string;
|
||||
ingredients: string[];
|
||||
steps: string[];
|
||||
}
|
||||
|
||||
const RECIPES: Recipe[] = [
|
||||
{
|
||||
title: "Kaiserschmarrn",
|
||||
image: "https://images.unsplash.com/photo-1621939514649-280e2ee25f60?w=400&h=300&fit=crop",
|
||||
time: "25 Min.",
|
||||
servings: "2 Portionen",
|
||||
ingredients: ["3 Eier", "200 ml Milch", "120 g Mehl", "30 g Zucker", "1 Prise Salz", "50 g Butter", "Rosinen nach Belieben", "Puderzucker"],
|
||||
steps: ["Eigelb, Milch, Mehl und Salz verrühren.", "Eiweiß mit Zucker steif schlagen und unterheben.", "Butter in der Pfanne erhitzen, Teig eingießen.", "Rosinen darüber streuen, von unten goldbraun backen.", "Mit zwei Gabeln in Stücke reißen.", "Mit Puderzucker bestreut servieren."],
|
||||
},
|
||||
{
|
||||
title: "Wiener Schnitzel",
|
||||
image: "https://images.unsplash.com/photo-1599921841143-819065a55cc6?w=400&h=300&fit=crop",
|
||||
time: "30 Min.",
|
||||
servings: "4 Portionen",
|
||||
ingredients: ["4 Kalbsschnitzel", "2 Eier", "Mehl", "Semmelbrösel", "Butterschmalz", "Salz", "Zitrone"],
|
||||
steps: ["Schnitzel dünn klopfen, salzen.", "In Mehl, verquirltem Ei und Semmelbröseln panieren.", "In reichlich heißem Butterschmalz goldbraun backen.", "Auf Küchenpapier abtropfen lassen.", "Mit Zitrone und Petersilienkartoffeln servieren."],
|
||||
},
|
||||
{
|
||||
title: "Apfelstrudel",
|
||||
image: "https://images.unsplash.com/photo-1535920527002-b35e96722eb9?w=400&h=300&fit=crop",
|
||||
time: "60 Min.",
|
||||
servings: "6 Portionen",
|
||||
ingredients: ["250 g Mehl", "1 Ei", "2 EL Öl", "125 ml Wasser", "1 kg Äpfel", "100 g Zucker", "Zimt", "80 g Semmelbrösel", "80 g Butter"],
|
||||
steps: ["Strudelteig kneten, 30 Min. ruhen lassen.", "Äpfel schälen, in dünne Scheiben schneiden.", "Mit Zucker, Zimt und Rosinen mischen.", "Teig dünn ausziehen, Brösel verteilen.", "Füllung auflegen, einrollen.", "Bei 180°C 40 Min. goldbraun backen."],
|
||||
},
|
||||
{
|
||||
title: "Tiroler Knödel",
|
||||
image: "https://images.unsplash.com/photo-1548940740-204726a19be3?w=400&h=300&fit=crop",
|
||||
time: "40 Min.",
|
||||
servings: "4 Portionen",
|
||||
ingredients: ["300 g Knödelbrot", "200 ml Milch", "3 Eier", "150 g Speck", "1 Zwiebel", "Petersilie", "Salz, Pfeffer", "Mehl"],
|
||||
steps: ["Knödelbrot in eine Schüssel geben, warme Milch darüber.", "Speck und Zwiebel anbraten.", "Eier, Speck, Petersilie zum Brot geben, mischen.", "30 Min. rasten lassen.", "Knödel formen, in Salzwasser 15 Min. kochen.", "Mit Butter und Schnittlauch servieren."],
|
||||
},
|
||||
{
|
||||
title: "Sachertorte",
|
||||
image: "https://images.unsplash.com/photo-1578985545062-69928b1d9587?w=400&h=300&fit=crop",
|
||||
time: "90 Min.",
|
||||
servings: "8 Portionen",
|
||||
ingredients: ["150 g Butter", "110 g Zucker", "6 Eier", "130 g Zartbitterschokolade", "130 g Mehl", "Marillenmarmelade", "200 g Kuvertüre"],
|
||||
steps: ["Butter und Zucker schaumig rühren.", "Geschmolzene Schokolade und Eigelb unterrühren.", "Eiweiß steif schlagen, mit Mehl unterheben.", "Bei 170°C 50 Min. backen.", "Auskühlen lassen, mit Marmelade bestreichen.", "Mit Schokoladen-Glasur überziehen."],
|
||||
},
|
||||
{
|
||||
title: "Kärntner Kasnudeln",
|
||||
image: "https://images.unsplash.com/photo-1551183053-bf91a1d81141?w=400&h=300&fit=crop",
|
||||
time: "50 Min.",
|
||||
servings: "4 Portionen",
|
||||
ingredients: ["400 g Mehl", "2 Eier", "Wasser", "500 g Topfen", "200 g Kartoffeln", "Minze", "Kerbel", "Butter", "Salz"],
|
||||
steps: ["Nudelteig aus Mehl, Eiern und Wasser herstellen.", "Kartoffeln kochen und stampfen.", "Mit Topfen, Minze und Kerbel mischen.", "Teig ausrollen, Kreise ausstechen.", "Füllung aufsetzen, Ränder krendeln.", "In Salzwasser kochen, mit Butter servieren."],
|
||||
},
|
||||
const WIDGET_RECIPES: WidgetRecipe[] = [
|
||||
{ title: "Kaiserschmarrn", image: "/uploads/recipe-kaiserschmarrn.png", time: "25 Min.", servings: "2" },
|
||||
{ title: "Wiener Schnitzel", image: "/uploads/recipe-wiener-schnitzel.png", time: "30 Min.", servings: "4" },
|
||||
{ title: "Apfelstrudel", image: "/uploads/recipe-apfelstrudel.png", time: "60 Min.", servings: "6" },
|
||||
{ title: "Schweinshaxe", image: "/uploads/recipe-schweinshaxe.png", time: "180 Min.", servings: "4" },
|
||||
{ title: "Käsespätzle", image: "/uploads/recipe-kaesespaetzle.png", time: "45 Min.", servings: "4" },
|
||||
{ title: "Sachertorte", image: "/uploads/recipe-sachertorte.png", time: "90 Min.", servings: "8" },
|
||||
{ title: "Maultaschen", image: "/uploads/recipe-maultaschen.png", time: "90 Min.", servings: "4" },
|
||||
{ title: "Schlutzkrapfen", image: "/uploads/recipe-schlutzkrapfen.png", time: "60 Min.", servings: "4" },
|
||||
];
|
||||
|
||||
function RecipeModal({ recipe, onClose }: { recipe: Recipe; onClose: () => void }) {
|
||||
export function RecipeWidget() {
|
||||
const [, navigate] = useLocation();
|
||||
const dayIndex = Math.floor(Date.now() / 86400000) % WIDGET_RECIPES.length;
|
||||
const recipe = WIDGET_RECIPES[dayIndex];
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 bg-black/80 flex items-center justify-center p-4" onClick={onClose} data-testid="modal-recipe">
|
||||
<div
|
||||
className="bg-card rounded-xl border border-card-border max-w-lg w-full max-h-[80vh] overflow-y-auto"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="relative">
|
||||
<img src={recipe.image} alt={recipe.title} className="w-full h-48 object-cover rounded-t-xl" />
|
||||
<button onClick={onClose} className="absolute top-3 right-3 bg-black/60 rounded-full p-1.5 text-white hover:bg-black/80" data-testid="button-recipe-close">
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-5">
|
||||
<h2 className="text-xl font-bold text-foreground mb-2">{recipe.title}</h2>
|
||||
<div className="flex items-center gap-4 text-sm text-muted-foreground mb-4">
|
||||
<span className="flex items-center gap-1"><Clock className="w-4 h-4" />{recipe.time}</span>
|
||||
<span className="flex items-center gap-1"><Users className="w-4 h-4" />{recipe.servings}</span>
|
||||
</div>
|
||||
|
||||
<h3 className="font-semibold text-foreground text-sm mb-2">Zutaten</h3>
|
||||
<ul className="text-sm text-foreground/80 mb-4 space-y-1">
|
||||
{recipe.ingredients.map((ing, i) => (
|
||||
<li key={i} className="flex items-start gap-2">
|
||||
<span className="text-primary mt-1">•</span>{ing}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<h3 className="font-semibold text-foreground text-sm mb-2">Zubereitung</h3>
|
||||
<ol className="text-sm text-foreground/80 space-y-2">
|
||||
{recipe.steps.map((step, i) => (
|
||||
<li key={i} className="flex gap-2">
|
||||
<span className="font-bold text-primary flex-shrink-0">{i + 1}.</span>{step}
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
<button
|
||||
onClick={() => navigate("/rezepte")}
|
||||
className="bg-card rounded-lg border border-card-border overflow-hidden h-full w-full text-left cursor-pointer group hover:border-primary/50 transition-colors flex flex-col"
|
||||
data-testid="widget-recipe"
|
||||
>
|
||||
<div className="p-3 flex items-center gap-2 border-b border-card-border flex-shrink-0">
|
||||
<ChefHat className="w-4 h-4 text-primary" />
|
||||
<h3 className="font-bold text-card-foreground text-sm">Rezept des Tages</h3>
|
||||
</div>
|
||||
<div className="relative flex-1">
|
||||
<img
|
||||
src={recipe.image}
|
||||
alt={recipe.title}
|
||||
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
|
||||
loading="lazy"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/20 to-transparent" />
|
||||
<div className="absolute bottom-0 left-0 right-0 p-3">
|
||||
<h4 className="text-white font-bold text-base">{recipe.title}</h4>
|
||||
<p className="text-white/60 text-xs mt-0.5 flex items-center gap-2">
|
||||
<span className="flex items-center gap-1"><Clock className="w-3 h-3" />{recipe.time}</span>
|
||||
<span className="flex items-center gap-1"><Users className="w-3 h-3" />{recipe.servings}</span>
|
||||
</p>
|
||||
<p className="text-[10px] text-primary mt-1 group-hover:underline">Alle Rezepte ansehen</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function RecipeWidget() {
|
||||
const [showRecipe, setShowRecipe] = useState<Recipe | null>(null);
|
||||
const dayIndex = Math.floor(Date.now() / 86400000) % RECIPES.length;
|
||||
const recipe = RECIPES[dayIndex];
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={() => setShowRecipe(recipe)}
|
||||
className="bg-card rounded-lg border border-card-border overflow-hidden h-full w-full text-left cursor-pointer group hover:border-primary/50 transition-colors flex flex-col"
|
||||
data-testid="widget-recipe"
|
||||
>
|
||||
<div className="p-3 flex items-center gap-2 border-b border-card-border flex-shrink-0">
|
||||
<ChefHat className="w-4 h-4 text-primary" />
|
||||
<h3 className="font-bold text-card-foreground text-sm">Rezept des Tages</h3>
|
||||
</div>
|
||||
<div className="relative flex-1">
|
||||
<img
|
||||
src={recipe.image}
|
||||
alt={recipe.title}
|
||||
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
|
||||
loading="lazy"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/20 to-transparent" />
|
||||
<div className="absolute bottom-0 left-0 right-0 p-3">
|
||||
<h4 className="text-white font-bold text-base">{recipe.title}</h4>
|
||||
<p className="text-white/60 text-xs mt-0.5 flex items-center gap-2">
|
||||
<span className="flex items-center gap-1"><Clock className="w-3 h-3" />{recipe.time}</span>
|
||||
<span className="flex items-center gap-1"><Users className="w-3 h-3" />{recipe.servings}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{showRecipe && <RecipeModal recipe={showRecipe} onClose={() => setShowRecipe(null)} />}
|
||||
</>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user