import type { Metadata } from "next"; import { notFound } from "next/navigation"; import { supabase } from "@/lib/supabase"; import { PublicMenuClient, type MenuCategory, type MenuItem, type RestaurantData } from "@/components/PublicMenuClient"; import { DEMO_RESTAURANT, DEMO_CATEGORIES } from "@/lib/demo-data"; import type { ThemeConfig } from "@menulio/shared"; type PageProps = { params: Promise<{ slug: string }>; searchParams?: Promise<{ theme?: string }>; }; interface MenuItemRow { id: string; name: string; description: string | null; price: number; image_url: string | null; is_active: boolean; translations: Record | null; } interface MenuCategoryRow { id: string; name: string; description: string | null; is_active: boolean; menu_items: MenuItemRow[]; translations: Record | null; } export async function generateMetadata({ params }: PageProps): Promise { const { slug } = await params; let restaurant: any = null; const { data: bySlug } = await supabase .from("restaurants") .select("name, logo_url") .eq("slug", slug) .maybeSingle(); if (bySlug) { restaurant = bySlug; } else if (slug === "demo" || slug === "gusto-brasserie") { restaurant = DEMO_RESTAURANT; } else { const { data: domainRow } = await supabase .from("domains") .select("restaurants(name, logo_url)") .eq("hostname", slug) .maybeSingle(); if (domainRow?.restaurants) { restaurant = domainRow.restaurants; } } if (!restaurant) { return { title: "Menü Bulunamadı | Menulio", }; } return { title: `${restaurant.name} | QR Menü`, description: `${restaurant.name} restoranının güncel dijital QR menüsü ve fiyatları.`, openGraph: { title: `${restaurant.name} QR Menü`, description: `${restaurant.name} dijital menüsünü inceleyin.`, images: restaurant.logo_url ? [restaurant.logo_url] : [], }, }; } async function fetchRestaurantBySlug(slug: string) { let { data: bySlug, error } = await supabase .from("restaurants") .select("id, name, slug, logo_url, cover_url, phone, address, enabled_languages") .eq("slug", slug) .maybeSingle(); if (error && error.code === "42703") { const { data: fallback } = await supabase .from("restaurants") .select("id, name, slug, logo_url, phone, address, enabled_languages") .eq("slug", slug) .maybeSingle(); if (fallback) { return { ...fallback, cover_url: null }; } } if (bySlug) return bySlug; let { data: domainRow, error: domErr } = await supabase .from("domains") .select("restaurant_id, restaurants(id, name, slug, logo_url, cover_url, phone, address, enabled_languages)") .eq("hostname", slug) .maybeSingle(); if (domErr && domErr.code === "42703") { const { data: fallbackDom } = await supabase .from("domains") .select("restaurant_id, restaurants(id, name, slug, logo_url, phone, address, enabled_languages)") .eq("hostname", slug) .maybeSingle(); if (fallbackDom?.restaurants) { return { ...(fallbackDom.restaurants as any), cover_url: null }; } } return (domainRow?.restaurants as any) ?? null; } export default async function PublicMenuPage({ params, searchParams }: PageProps) { const { slug } = await params; const resolvedSearchParams = searchParams ? await searchParams : {}; const themeParam = resolvedSearchParams.theme; const restaurant: any = await fetchRestaurantBySlug(slug); if (!restaurant) { if (slug === "demo" || slug === "gusto-brasserie") { return ( ); } notFound(); } // Find menu (published or any available menu) let { data: menu } = await supabase .from("menus") .select("id, name, locations!inner(restaurant_id)") .eq("locations.restaurant_id", restaurant.id) .eq("is_published", true) .maybeSingle(); if (!menu) { const { data: anyMenu } = await supabase .from("menus") .select("id, name, locations!inner(restaurant_id)") .eq("locations.restaurant_id", restaurant.id) .maybeSingle(); menu = anyMenu; } let categories: MenuCategory[] = []; if (menu) { const { data: rawCategories } = await supabase .from("menu_categories") .select( "id, name, description, is_active, translations, menu_items(id, name, description, price, image_url, is_active, translations)", ) .eq("menu_id", menu.id) .eq("is_active", true) .order("sort_order", { ascending: true }) .returns(); categories = (rawCategories ?? []).map((cat) => ({ id: cat.id, name: cat.name, description: cat.description, is_active: cat.is_active, translations: cat.translations ?? {}, menu_items: (cat.menu_items ?? []) .filter((item) => item.is_active) .map((item): MenuItem => ({ id: item.id, name: item.name, description: item.description, price: item.price, image_url: item.image_url, is_active: item.is_active, translations: item.translations ?? {}, })), })); } else if (slug === "demo" || slug === "gusto-brasserie") { categories = DEMO_CATEGORIES; } const { data: themeRow } = await supabase .from("restaurant_themes") .select("overrides, themes(key, config)") .eq("restaurant_id", restaurant.id) .maybeSingle(); const themeRelation = themeRow?.themes as unknown as { key?: string; config?: Partial } | null; const themeKey = themeParam || themeRelation?.key || "elegant"; const customConfig = (themeRow?.overrides as Partial) || themeRelation?.config; const restaurantData: RestaurantData = { id: restaurant.id, name: restaurant.name, slug: restaurant.slug, logo_url: restaurant.logo_url, cover_url: restaurant.cover_url, phone: restaurant.phone, address: restaurant.address, }; return ( ); }