197 lines
9.2 KiB
TypeScript
197 lines
9.2 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 {
|
|
getMenuItemDescription,
|
|
getMenuItemName,
|
|
} from "@/application/i18n/menu-localization";
|
|
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,
|
|
name: getMenuItemName(language, item),
|
|
desc: getMenuItemDescription(language, t, item),
|
|
filterTag: getDishFilterTag(item, category.id),
|
|
}))
|
|
),
|
|
[language, t]
|
|
);
|
|
|
|
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-6 sm:p-9 rounded-2xl bg-[#F8F5F0] overflow-hidden">
|
|
<div className="text-[#B38B4D] text-4xl sm:text-6xl font-light mb-6 sm:mb-9 tracking-[-2px]">0{index + 1}</div>
|
|
<h4 className="text-xl sm:text-2xl md:text-[29px] tracking-[-0.8px] mb-4 sm:mb-5 text-[#2C2A26] leading-tight break-words">{item.title}</h4>
|
|
<p className="text-[#6B665F] text-[15.5px] leading-relaxed">{item.desc}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<Footer />
|
|
</div>
|
|
);
|
|
}
|