diff --git a/client/src/pages/recipes.tsx b/client/src/pages/recipes.tsx index f177c87..036e7df 100644 --- a/client/src/pages/recipes.tsx +++ b/client/src/pages/recipes.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { useQuery } from "@tanstack/react-query"; import { Link } from "wouter"; import { ChefHat, Clock, Users, X, ChevronLeft } from "lucide-react"; import { usePageMeta } from "@/hooks/use-page-meta"; @@ -295,6 +296,50 @@ function RecipeModal({ recipe, onClose }: { recipe: Recipe; onClose: () => void ); } +interface DailyRecipe { + name: string; + intro?: string; + recipe?: string; + seasonNote?: string | null; + imageUrl?: string | null; +} + +function RecipeOfTheDay() { + const { data } = useQuery({ + queryKey: ["/api/recipes/today"], + staleTime: 1000 * 60 * 30, + }); + if (!data || !data.name) return null; + return ( +
+
+
+

Rezept des Tages

+
+
+
+ {data.imageUrl && ( + {data.name} + )} +
+

{data.name}

+ {data.intro &&

{data.intro}

} + {data.recipe && ( +
{data.recipe}
+ )} + {data.seasonNote &&

{data.seasonNote}

} +
+
+
+ ); +} + export default function RecipesPage() { usePageMeta("Rezepte - Alpenküche & Volksmusik", "Traditionelle Rezepte aus der Alpenküche und österreichische Hausmannskost. Kochen wie die Volksmusik-Stars bei FOLX TV."); const [selectedRecipe, setSelectedRecipe] = useState(null); @@ -320,6 +365,8 @@ export default function RecipesPage() { Traditionelle Rezepte aus den germanischen Regionen -- von der Alm bis zur Küste, von der Oma überliefert.

+ + {RECIPE_REGIONS.map((region, ri) => (
diff --git a/server/daily-recipe.ts b/server/daily-recipe.ts new file mode 100644 index 0000000..4b87e7d --- /dev/null +++ b/server/daily-recipe.ts @@ -0,0 +1,39 @@ +import { db } from "./db"; +import { dailyRecipes } from "@shared/schema"; +import { eq } from "drizzle-orm"; + +function todayStr(): string { + const d = new Date(); + return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`; +} + +export async function getRecipeForToday(): Promise { + const rows = await db.select().from(dailyRecipes).where(eq(dailyRecipes.dateStr, todayStr())); + return rows.length > 0 ? rows[0] : null; +} + +export async function upsertRecipe(data: { + dateStr?: string; + name: string; + intro?: string; + recipe?: string; + seasonNote?: string | null; + imageUrl?: string | null; +}): Promise { + const dateStr = data.dateStr || todayStr(); + const values = { + dateStr, + name: data.name, + intro: data.intro || "", + recipe: data.recipe || "", + seasonNote: data.seasonNote || null, + imageUrl: data.imageUrl || null, + }; + const existing = await db.select().from(dailyRecipes).where(eq(dailyRecipes.dateStr, dateStr)); + if (existing.length > 0) { + const [row] = await db.update(dailyRecipes).set(values).where(eq(dailyRecipes.id, existing[0].id)).returning(); + return row; + } + const [row] = await db.insert(dailyRecipes).values(values).returning(); + return row; +} diff --git a/server/routes.ts b/server/routes.ts index 9cb7e54..0578556 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -4,6 +4,7 @@ import { storage } from "./storage"; import { insertArticleSchema } from "@shared/schema"; import { seedDatabase } from "./seed"; import { generateDailyHoroscopes, getHoroscopesForToday, getOrGenerateHoroscope, setHoroscopeImages } from "./horoscope-generator"; +import { getRecipeForToday, upsertRecipe } from "./daily-recipe"; import { startDailyScheduler } from "./scheduler"; import { analyzeAllArticleImages, getCachedFocalPoints } from "./focal-point"; import { optimizeImage } from "./image-optimizer"; @@ -754,6 +755,26 @@ export async function registerRoutes( } }); + app.get("/api/recipes/today", async (_req, res) => { + try { + const recipe = await getRecipeForToday(); + res.json(recipe); + } catch (err: any) { + res.status(500).json({ message: err.message }); + } + }); + + app.post("/api/recipes", requireApiKey, async (req, res) => { + try { + const { name } = req.body || {}; + if (!name) return res.status(400).json({ message: "name ist erforderlich" }); + const row = await upsertRecipe(req.body); + res.json(row); + } catch (err: any) { + res.status(500).json({ message: err.message }); + } + }); + app.get("/api/horoscopes/today", async (_req, res) => { try { const horoscopes = await getHoroscopesForToday(); diff --git a/shared/schema.ts b/shared/schema.ts index acdc822..b9918ab 100644 --- a/shared/schema.ts +++ b/shared/schema.ts @@ -34,6 +34,17 @@ export const articleViews = pgTable("article_views", { viewedAt: timestamp("viewed_at").notNull().defaultNow(), }); +export const dailyRecipes = pgTable("daily_recipes", { + id: serial("id").primaryKey(), + dateStr: varchar("date_str", { length: 10 }).notNull(), + name: text("name").notNull(), + intro: text("intro").notNull().default(""), + recipe: text("recipe").notNull().default(""), + seasonNote: text("season_note"), + imageUrl: text("image_url"), + createdAt: timestamp("created_at").notNull().defaultNow(), +}); + export const dailyHoroscopes = pgTable("daily_horoscopes", { id: serial("id").primaryKey(), signIndex: integer("sign_index").notNull(),