first commit

This commit is contained in:
AyrisAI
2026-08-20 01:51:59 +03:00
commit 97b83c7fd4
109 changed files with 21215 additions and 0 deletions
+159
View File
@@ -0,0 +1,159 @@
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 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;
}
interface MenuCategoryRow {
id: string;
name: string;
description: string | null;
is_active: boolean;
menu_items: MenuItemRow[];
}
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
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 {
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] : [],
},
};
}
export default async function PublicMenuPage({ params, searchParams }: PageProps) {
const { slug } = await params;
const resolvedSearchParams = searchParams ? await searchParams : {};
const themeParam = resolvedSearchParams.theme;
// 1. Try finding restaurant by slug
let restaurant: any = null;
const { data: bySlug } = await supabase
.from("restaurants")
.select("id, name, slug, logo_url, phone, address")
.eq("slug", slug)
.maybeSingle();
if (bySlug) {
restaurant = bySlug;
} else {
// 2. Try finding restaurant by custom domain hostname
const { data: domainRow } = await supabase
.from("domains")
.select("restaurant_id, restaurants(id, name, slug, logo_url, phone, address)")
.eq("hostname", slug)
.maybeSingle();
if (domainRow?.restaurants) {
restaurant = domainRow.restaurants;
}
}
if (!restaurant) notFound();
const { 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) notFound();
const { data: rawCategories } = await supabase
.from("menu_categories")
.select("id, name, description, is_active, menu_items(id, name, description, price, image_url, is_active)")
.eq("menu_id", menu.id)
.eq("is_active", true)
.order("sort_order", { ascending: true })
.returns<MenuCategoryRow[]>();
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<ThemeConfig> } | null;
const themeKey = themeParam || themeRelation?.key || "elegant";
const customConfig = (themeRow?.overrides as Partial<ThemeConfig>) || themeRelation?.config;
const categories: MenuCategory[] = (rawCategories ?? []).map((cat) => ({
id: cat.id,
name: cat.name,
description: cat.description,
is_active: cat.is_active,
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,
})),
}));
const restaurantData: RestaurantData = {
id: restaurant.id,
name: restaurant.name,
slug: restaurant.slug,
logo_url: restaurant.logo_url,
phone: restaurant.phone,
address: restaurant.address,
};
return (
<PublicMenuClient
restaurant={restaurantData}
categories={categories}
initialThemeKey={themeKey}
customThemeConfig={customConfig}
/>
);
}