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:
sebastjanartic
2026-02-28 19:58:23 +00:00
parent d0ee926b35
commit 4963edd636
34 changed files with 970 additions and 534 deletions
+42 -129
View File
@@ -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>
);
}