Files
shahikitchen-prod/app/page.tsx
T

192 lines
8.9 KiB
TypeScript

"use client";
/**
* SHAHI KITCHEN HOMEPAGE
* Version with advanced Signature Menu + elegant hero (pre full Sultan redesign)
*/
import React, { useEffect, useMemo, useState } from "react";
import Lenis from "lenis";
import { ArrowRight, UtensilsCrossed } from "lucide-react";
import Navbar from "@/components/Navbar";
import Footer from "@/components/Footer";
import { useCart } from "@/components/CartContext";
import { useLanguage } from "@/lib/language-context";
import { heroBannerSources } from "@/lib/assets";
import { menuCategories } from "@/lib/menu-data";
import { buildCartLineFromMenuItem } from "@/application/cart/cart-line-builder";
import type { MenuItem } from "@/domain/menu/entities";
import { getTranslation } from "@/lib/translations";
import SignatureMenuMarquee, { type SignatureDish } from "@/components/SignatureMenuMarquee";
type SignatureFilter = "All" | "Rice" | "Curry" | "Meat" | "Street" | "Roll" | "Sweets";
type HomepageDish = SignatureDish & { filterTag: SignatureFilter | null };
export default function ShahiKitchenHomepage() {
const { addToCart } = useCart();
const { language } = useLanguage();
const t = getTranslation(language);
const [menuFilter, setMenuFilter] = useState<SignatureFilter>("All");
// Lenis smooth scroll
useEffect(() => {
const lenis = new Lenis({
duration: 1.1,
easing: (t: number) => Math.min(1, 1.001 * (-Math.pow(2, -10 * t) + 1)),
smoothWheel: true,
});
const raf = (time: number) => { lenis.raf(time); requestAnimationFrame(raf); };
requestAnimationFrame(raf);
return () => lenis.destroy();
}, []);
const getDishFilterTag = (item: MenuItem, categoryId: string): SignatureFilter | null => {
if (categoryId === "sweets") return "Sweets";
if (categoryId === "street-food") return "Street";
if (categoryId === "naan-roll") return "Roll";
if (categoryId === "meat") return "Meat";
if (item.id.includes("biryani")) return "Rice";
if (categoryId === "vegetarian" || categoryId === "chicken") return "Curry";
return null;
};
const allDishes = useMemo<HomepageDish[]>(
() =>
menuCategories.flatMap((category) =>
category.items.map((item) => ({
...item,
desc: item.description ?? "",
filterTag: getDishFilterTag(item, category.id),
}))
),
[]
);
const filtered = useMemo(() => {
if (menuFilter === "All") return allDishes;
return allDishes.filter((dish) => dish.filterTag === menuFilter);
}, [allDishes, menuFilter]);
const banner = heroBannerSources();
const addDish = (d: SignatureDish) => {
addToCart(buildCartLineFromMenuItem(d));
};
return (
<div className="min-h-screen bg-[#F8F5F0] text-[#2C2A26]">
<Navbar />
{/* HERO - Full Banner Video (no text overlay) */}
<section
className="relative min-h-[100dvh] overflow-hidden bg-[#fbf7ef]"
aria-label="Hero"
>
{/* Banner Video - Responsive sources for proper mobile/iPad visualization (portrait asset) + desktop (landscape).
Mobile/iPad (<=1024px) uses the compressed banner_mobile-optimized versions (WebM primary for size/speed, MP4 fallback).
These were generated from the provided banner_mobile.mp4 (original kept as source asset). */}
<video
autoPlay
muted
playsInline
preload="auto"
className="absolute inset-0 z-0 w-full h-full object-cover
object-[50%_10%] sm:object-[50%_12%] md:object-[50%_16%]
lg:object-[50%_20%] xl:object-[50%_24%]"
onError={(e) => console.error('Hero banner video failed to load', e)}
>
{/* Mobile + iPad (portrait-friendly framing for better visualization on small screens) */}
<source
src={banner.mobileWebm}
type="video/webm"
media="(max-width: 1024px)"
/>
<source
src={banner.mobileMp4}
type="video/mp4"
media="(max-width: 1024px)"
/>
{/* Desktop / large screens */}
<source src={banner.desktopMp4} type="video/mp4" />
</video>
{/* Cinematic overlays */}
<div className="absolute inset-0 z-[1] bg-gradient-to-b from-[#0f5a4a]/30 via-transparent to-[#101724]/50" />
<div className="absolute inset-0 z-[2] bg-[radial-gradient(ellipse_at_center,transparent_0%,rgba(16,23,36,0.25)_100%)]" />
</section>
{/* ADVANCED SIGNATURE MENU */}
<section id="menu" className="scroll-mt-header section-ornament bg-[#fffdf8] px-6 py-20 lg:px-8">
<div className="mx-auto max-w-7xl">
<div className="flex flex-col md:flex-row md:items-end justify-between gap-6 mb-10">
<div>
<div className="mb-3 inline-flex items-center gap-2 rounded-full bg-[#fff6dc] px-4 py-1.5 text-sm font-semibold text-[#60420d]">
<UtensilsCrossed className="h-4 w-4" /> {t.signatureMenu.title}
</div>
<h2 className="text-2xl sm:text-[1.75rem] md:text-3xl leading-[1.15] tracking-[-0.8px] break-words text-[#101724]">{t.signatureMenu.title}</h2>
</div>
<p className="max-w-md text-sm md:text-[15px] font-medium text-[#4b5563]">{t.signatureMenu.subtitle}</p>
</div>
{/* Filters - modern horizontal scroller on mobile for easy thumb use */}
<div className="mb-8 -mx-1 px-1 flex gap-2 overflow-x-auto snap-x snap-mandatory pb-2 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
{(["All", "Rice", "Curry", "Meat", "Street", "Roll", "Sweets"] as const).map((cat) => (
<button
key={cat}
onClick={() => setMenuFilter(cat)}
className={`flex-shrink-0 snap-start rounded-full px-5 py-2.5 text-sm font-bold transition-all active:scale-[0.97] touch-manipulation ${menuFilter === cat ? "bg-[#101724] text-white" : "border border-[#e5e1d7] bg-white text-[#182235] hover:border-[#c99a2e] hover:bg-[#fff6dc] active:bg-[#fff6dc]"}`}
>
{(t.signatureMenu as any)[`filter${cat}`] || cat}
</button>
))}
</div>
<SignatureMenuMarquee
dishes={filtered}
addToCartLabel={t.wishlistDrawer.addToCart}
perHalfKgLabel={t.menu.perHalfKg}
perKgLabel={t.menu.perKg}
onAdd={addDish}
/>
<div className="mt-10 text-center">
<a href="/menu" className="inline-flex items-center gap-2 text-sm font-bold tracking-wider text-[#B38B4D] hover:text-[#8C6B3A]">
{t.signatureMenu.viewFullMenu} <ArrowRight className="h-4 w-4" />
</a>
</div>
</div>
</section>
{/* THE SHAHI EXPERIENCE */}
<section id="experience" className="scroll-mt-header section bg-white border-y border-[#EDE6D9]">
<div className="max-w-6xl mx-auto px-6">
<div className="text-center mb-14">
<div className="text-[#B38B4D] text-xs tracking-[3px] mb-3">{language === 'sv' ? 'SHAHI-SÄTTET' : 'THE SHAHI WAY'}</div>
<h3 className="text-2xl sm:text-[1.75rem] md:text-3xl tracking-[-0.8px] leading-[1.15] whitespace-pre-line">{language === 'sv' ? 'Mer än en måltid.\nEtt ögonblick av Shahi.' : 'More than a meal.\nA moment of Shahi.'}</h3>
</div>
<div className="grid md:grid-cols-3 gap-6">
{(language === 'sv' ? [
{ title: "Den Legendariska Buffén", desc: "Vår berömda lunchbuffé har över 20 roterande rätter — curry, biryani, färsk naan och sötsaker." },
{ title: "Shahi Sötsaker", desc: "Hemgjord mithai dagligen. Från färsk jalebi till rasmalai — det perfekta söta avslutet." },
{ title: "Varm Gästfrihet", desc: "Oavsett om du är här för en snabb lunch eller familjefest, behandlas du alltid som shahi." },
] : [
{ title: "The Legendary Buffet", desc: "Our famous lunch buffet features over 20 rotating dishes — curries, biryanis, fresh naan, and sweets." },
{ title: "Shahi Sweets", desc: "Homemade mithai made daily. From fresh Jalebi to Rasmalai — the perfect sweet ending." },
{ title: "Warm Hospitality", desc: "Whether you're here for a quick lunch or a family celebration, you will always be treated like Shahi." },
]).map((item, index) => (
<div key={index} className="experience-card group relative border border-[#EDE6D9] p-9 rounded-2xl bg-[#F8F5F0] overflow-hidden">
<div className="text-[#B38B4D] text-6xl font-light mb-9 tracking-[-2px]">0{index + 1}</div>
<h4 className="text-[29px] tracking-[-0.8px] mb-5 text-[#2C2A26] leading-tight">{item.title}</h4>
<p className="text-[#6B665F] text-[15.5px] leading-relaxed">{item.desc}</p>
</div>
))}
</div>
</div>
</section>
<Footer />
</div>
);
}