Initial commit: Shahi Kitchen premium website

- Royal cream + gold theme
- Playful animated hero with chef mascot
- Advanced menu with sidebar + video hover
- Multilingual support (EN, SV, HI, UR)
- Cart system with WhatsApp ordering
- Real restaurant photos integration
- Responsive design with proper navbar
This commit is contained in:
Zeeshan Khan
2026-06-01 15:14:19 +02:00
parent edd906d893
commit 56fe68eb48
314 changed files with 4129 additions and 111 deletions
+304 -58
View File
@@ -1,65 +1,311 @@
import Image from "next/image";
"use client";
/**
* SHAHI KITCHEN HOMEPAGE
* Version with advanced Signature Menu + elegant hero (pre full Sultan redesign)
*/
import React, { useEffect, useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import Lenis from "lenis";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { Crown, Clock, 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 { getTranslation } from "@/lib/translations";
gsap.registerPlugin(ScrollTrigger);
export default function ShahiKitchenHomepage() {
const { addToCart } = useCart();
const { language } = useLanguage();
const t = getTranslation(language);
const [menuFilter, setMenuFilter] = useState<"All" | "Rice" | "Curry" | "Meat" | "Street" | "Roll" | "Sweet">("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 signatureDishes = [
{
id: "chicken-biryani",
name: "Chicken Biryani",
category: "Rice",
price: 149,
time: "32 min",
desc: "Fragrant aged basmati layered with spiced chicken, saffron & caramelized onions",
image: "chicken-biryani-poster.jpg"
},
{
id: "bong-nihari",
name: "Bong Nihari",
category: "Curry",
price: 199,
time: "45 min",
desc: "Slow-cooked beef shank in rich aromatic gravy with ginger, lemon & fresh naan",
image: "bong-nihari-poster.jpg"
},
{
id: "panipuri",
name: "Golgappy / Panipuri",
category: "Street",
price: 69,
time: "15 min",
desc: "Crispy hollow puris filled with spiced chickpeas, potatoes & tangy tamarind water",
image: "panipuri-poster.jpg"
},
{
id: "lamm-palak",
name: "Lamm Palak",
category: "Meat",
price: 179,
time: "30 min",
desc: "Tender lamb cooked with fresh spinach in a flavorful mild gravy",
image: "lamm-palak-poster.jpg"
},
{
id: "chicken-karahi",
name: "Chicken Karahi",
category: "Curry",
price: 149,
time: "28 min",
desc: "Wok-tossed chicken in a robust tomato, chili & ginger gravy",
image: "chicken-karahi-poster.jpg"
},
{
id: "chicken-haleem",
name: "Chicken Haleem",
category: "Curry",
price: 149,
time: "35 min",
desc: "Slow-cooked shredded chicken with lentils, wheat & aromatic spices",
image: "chicken-haleem-poster.jpg"
},
{
id: "tikka-boti-roll",
name: "Tikka Boti Roll",
category: "Roll",
price: 99,
time: "20 min",
desc: "Juicy chicken tikka wrapped in soft naan with mint chutney & onions",
image: "tikka-boti-roll-poster.jpg"
},
{
id: "jalebi",
name: "Jalebi",
category: "Sweet",
price: 119,
time: "12 min",
desc: "Crispy golden saffron spirals soaked in fragrant sugar syrup",
image: "jalebi-poster.jpg"
},
{
id: "kulfi",
name: "Kulfi",
category: "Sweet",
price: 39,
time: "10 min",
desc: "Traditional frozen milk dessert with cardamom, pistachio & saffron",
image: "kulfi-poster.jpg"
},
];
const filtered = menuFilter === "All" ? signatureDishes : signatureDishes.filter(d => d.category === menuFilter);
const addDish = (d: any) => {
addToCart({ id: d.id, name: d.name, price: d.price, image: d.image });
};
// Advanced GSAP effects for signature cards
useEffect(() => {
const cards = document.querySelectorAll<HTMLElement>(".signature-card");
gsap.fromTo(cards,
{ opacity: 0, y: 45 },
{
opacity: 1, y: 0, duration: 0.9, ease: "power3.out", stagger: 0.06,
scrollTrigger: { trigger: ".signature-menu-grid", start: "top 82%", once: true }
}
);
cards.forEach((card) => {
const image = card.querySelector(".signature-image") as HTMLElement | null;
let bounds: DOMRect;
const onMouseMove = (e: MouseEvent) => {
if (!bounds) bounds = card.getBoundingClientRect();
const x = ((e.clientX - bounds.left) / bounds.width - 0.5) * 2;
const y = ((e.clientY - bounds.top) / bounds.height - 0.5) * 2;
gsap.to(card, {
rotationY: x * 11, rotationX: -y * 8, transformPerspective: 1400, duration: 0.35, ease: "power2.out"
});
if (image) gsap.to(image, { x: x * 8, y: y * 6, duration: 0.45, ease: "power2.out" });
};
const onMouseLeave = () => {
gsap.to(card, { rotationY: 0, rotationX: 0, duration: 1.1, ease: "elastic.out(1, 0.45)" });
if (image) gsap.to(image, { x: 0, y: 0, scale: 1, duration: 0.9, ease: "power2.out" });
};
const onMouseEnter = () => {
if (image) gsap.to(image, { scale: 1.12, duration: 1.1, ease: "power2.out" });
};
card.addEventListener("mousemove", onMouseMove);
card.addEventListener("mouseleave", onMouseLeave);
card.addEventListener("mouseenter", onMouseEnter);
(card as any)._cleanup = () => {
card.removeEventListener("mousemove", onMouseMove);
card.removeEventListener("mouseleave", onMouseLeave);
card.removeEventListener("mouseenter", onMouseEnter);
};
});
return () => {
cards.forEach(card => (card as any)._cleanup?.());
};
}, [filtered]);
export default function Home() {
return (
<div className="flex flex-col flex-1 items-center justify-center bg-zinc-50 font-sans dark:bg-black">
<main className="flex flex-1 w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={100}
height={20}
priority
/>
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
To get started, edit the page.tsx file.
</h1>
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
Looking for a starting point or more instructions? Head over to{" "}
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Templates
</a>{" "}
or the{" "}
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Learning
</a>{" "}
center.
</p>
<div className="min-h-screen bg-[#F8F5F0] text-[#2C2A26]">
<Navbar />
{/* HERO - Full Banner Video with responsive framing */}
<section className="relative min-h-[70dvh] sm:min-h-[78dvh] md:min-h-[85dvh] lg:min-h-[92dvh] xl:min-h-[100dvh]
flex items-center justify-center pt-20 lg:pt-[88px] overflow-hidden bg-[#fbf7ef]">
{/* Banner Video - Optimized positioning per device */}
<video
autoPlay
muted
loop
playsInline
className="absolute inset-0 z-10 w-full h-full object-cover
object-[50%_22%] sm:object-[50%_26%] md:object-[50%_30%]
lg:object-[50%_35%] xl:object-[50%_38%]"
>
<source src="/videos/banner.webm" type="video/webm" />
<source src="/videos/banner.mp4" type="video/mp4" />
</video>
{/* Subtle gradient to improve visibility of baked-in text */}
<div className="absolute inset-0 z-20 bg-gradient-to-b from-black/5 via-transparent to-black/30" />
</section>
{/* ADVANCED SIGNATURE MENU */}
<section id="menu" className="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="font-serif text-6xl leading-none tracking-[-0.055em] md:text-7xl">{t.signatureMenu.title}</h2>
</div>
<p className="max-w-md text-lg font-medium text-[#4b5563]">{t.signatureMenu.subtitle}</p>
</div>
{/* Filters */}
<div className="mb-8 flex flex-wrap gap-2">
{["All", "Rice", "Curry", "Meat", "Street", "Roll", "Sweet"].map((cat) => (
<button
key={cat}
onClick={() => setMenuFilter(cat as any)}
className={`rounded-full px-6 py-3 text-sm font-bold transition-all ${menuFilter === cat ? "bg-[#101724] text-white" : "border border-[#e5e1d7] bg-white text-[#182235] hover:border-[#c99a2e] hover:bg-[#fff6dc]"}`}
>
{cat}
</button>
))}
</div>
{/* Menu Grid with Advanced Effects */}
<div className="signature-menu-grid grid gap-5 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
<AnimatePresence>
{filtered.map((dish, index) => (
<motion.div
key={dish.id}
layout
onClick={() => addDish(dish)}
className="signature-card group flex cursor-pointer flex-col overflow-hidden rounded-[2rem] border border-[#EDE6D9] bg-white"
>
<div className="relative h-48 overflow-hidden bg-[#F2EDE4]">
<img
src={`/images/dishes/${dish.image}`}
alt={dish.name}
className="signature-image absolute inset-0 w-full h-full object-cover transition-transform duration-700"
/>
<div className="absolute inset-0 bg-gradient-to-b from-black/5 via-transparent to-black/25" />
<div className="absolute top-4 right-4 px-4 py-1 rounded-full bg-white/95 text-[#B38B4D] text-sm font-medium tracking-tight shadow-sm border border-[#EDE6D9]">
{dish.price} kr
</div>
</div>
<div className="p-5 flex-1 flex flex-col">
<h4 className="text-[21px] leading-tight tracking-[-0.5px] mb-2 group-hover:text-[#B38B4D] transition-colors">
{dish.name}
</h4>
<p className="text-[#6B665F] text-[13.5px] leading-snug flex-1">{dish.desc}</p>
<button
onClick={(e) => { e.stopPropagation(); addDish(dish); }}
className="mt-5 w-full py-3 text-sm tracking-[0.6px] border border-[#B38B4D] text-[#B38B4D] rounded-full hover:bg-[#B38B4D] hover:text-white font-medium transition-all"
>
{t.signatureMenu.addToTable}
</button>
</div>
</motion.div>
))}
</AnimatePresence>
</div>
<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>
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
<a
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={16}
height={16}
/>
Deploy Now
</a>
<a
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Documentation
</a>
</section>
{/* THE SHAHI EXPERIENCE */}
<section id="experience" className="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">THE SHAHI WAY</div>
<h3 className="text-5xl md:text-6xl tracking-[-2px]">More than a meal.<br />A moment of royalty.</h3>
</div>
<div className="grid md:grid-cols-3 gap-6">
{[
{ 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 royalty." },
].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>
</main>
</section>
<Footer />
</div>
);
}