feat: implement separate subpages for Home, About, Menu, Blog, Contact, and Reservation with shared SubpageLayout
This commit is contained in:
parent
b0a414a65e
commit
b798bac2de
33
src/app/about/page.tsx
Normal file
33
src/app/about/page.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import SubpageLayout from "@/components/SubpageLayout";
|
||||||
|
import RestaurantStory from "@/components/RestaurantStory";
|
||||||
|
import ChefShowcase from "@/components/ChefShowcase";
|
||||||
|
|
||||||
|
export default function AboutPage() {
|
||||||
|
return (
|
||||||
|
<SubpageLayout>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
|
||||||
|
{/* Subpage Header Banner */}
|
||||||
|
<div className="text-center pt-12 pb-6 max-w-2xl mx-auto px-6 relative z-10 select-text">
|
||||||
|
<div className="flex items-center gap-3 justify-center mb-2">
|
||||||
|
<span className="text-[#bc9e65] text-xs">❧</span>
|
||||||
|
<span className="font-sans text-[10px] tracking-[0.3em] text-[#bc9e65] font-extrabold uppercase">
|
||||||
|
ABOUT OUR HOUSE
|
||||||
|
</span>
|
||||||
|
<span className="text-[#bc9e65] text-xs">❧</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="font-serif text-3xl sm:text-4xl lg:text-[42px] font-bold text-white tracking-wider leading-tight uppercase">
|
||||||
|
Our Legacy & <span className="text-[#bc9e65]">Passion</span>
|
||||||
|
</h1>
|
||||||
|
<div className="w-24 h-[1px] bg-gradient-to-r from-transparent via-[#bc9e65] to-transparent mx-auto mt-4" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Core sections */}
|
||||||
|
<RestaurantStory />
|
||||||
|
<ChefShowcase />
|
||||||
|
</div>
|
||||||
|
</SubpageLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
135
src/app/blog/page.tsx
Normal file
135
src/app/blog/page.tsx
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import Image from "next/image";
|
||||||
|
import SubpageLayout from "@/components/SubpageLayout";
|
||||||
|
|
||||||
|
const CornerOrnaments = () => (
|
||||||
|
<>
|
||||||
|
<svg className="absolute w-3 h-3 text-[#bc9e65]/40 top-2 left-2 pointer-events-none select-none transition-all duration-500 group-hover:text-[#bc9e65]/80" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M2 2h6v1.5H3.5V8H2V2zm5 5c0-1.1.9-2 2-2h4v1.5H9V11H7V7z" />
|
||||||
|
</svg>
|
||||||
|
<svg className="absolute w-3 h-3 text-[#bc9e65]/40 top-2 right-2 rotate-90 pointer-events-none select-none transition-all duration-500 group-hover:text-[#bc9e65]/80" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M2 2h6v1.5H3.5V8H2V2zm5 5c0-1.1.9-2 2-2h4v1.5H9V11H7V7z" />
|
||||||
|
</svg>
|
||||||
|
<svg className="absolute w-3 h-3 text-[#bc9e65]/40 bottom-2 left-2 -rotate-90 pointer-events-none select-none transition-all duration-500 group-hover:text-[#bc9e65]/80" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M2 2h6v1.5H3.5V8H2V2zm5 5c0-1.1.9-2 2-2h4v1.5H9V11H7V7z" />
|
||||||
|
</svg>
|
||||||
|
<svg className="absolute w-3 h-3 text-[#bc9e65]/40 bottom-2 right-2 rotate-180 pointer-events-none select-none transition-all duration-500 group-hover:text-[#bc9e65]/80" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M2 2h6v1.5H3.5V8H2V2zm5 5c0-1.1.9-2 2-2h4v1.5H9V11H7V7z" />
|
||||||
|
</svg>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const BLOG_POSTS = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: "The Art of Fermenting Dosa Batter: A 24-Hour Ritual",
|
||||||
|
desc: "Discover the traditional science and family secrets behind cultivating the perfect sourness and crispiness for our signature stone-ground dosas.",
|
||||||
|
date: "JULY 15, 2026",
|
||||||
|
image: "/images/masala_dosa_luxury.png",
|
||||||
|
author: "Chef Sundaram Iyer",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: "Secret Spices of Chettinad Cookery",
|
||||||
|
desc: "Unveiling the delicate proportions of kalpasi (stone flower), star anise, and freshly crushed peppercorns that define our royal spice blends.",
|
||||||
|
date: "JUNE 28, 2026",
|
||||||
|
image: "/images/category_curries.jpg",
|
||||||
|
author: "My Dosa Place Culinary Team",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: "Why Brass Cookware Remains King in Heritage Dining",
|
||||||
|
desc: "How slow-heating copper and brass vessels preserve the vital nutrients and aroma of our house-cooked lentil sambar and vegetable poriyals.",
|
||||||
|
date: "MAY 12, 2026",
|
||||||
|
image: "/images/south_indian_thali_luxury.png",
|
||||||
|
author: "Heritage Preservation Society",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function BlogPage() {
|
||||||
|
return (
|
||||||
|
<SubpageLayout>
|
||||||
|
{/* Background elements */}
|
||||||
|
<div className="absolute inset-0 z-0 pointer-events-none select-none opacity-40">
|
||||||
|
<Image
|
||||||
|
src="/images/category_bg.jpg"
|
||||||
|
alt="Backdrop"
|
||||||
|
fill
|
||||||
|
priority
|
||||||
|
className="object-cover object-center"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="max-w-5xl mx-auto px-6 py-12 relative z-10">
|
||||||
|
|
||||||
|
{/* Subpage Header Banner */}
|
||||||
|
<div className="text-center pb-14 max-w-2xl mx-auto select-text">
|
||||||
|
<div className="flex items-center gap-3 justify-center mb-2">
|
||||||
|
<span className="text-[#bc9e65] text-xs">❧</span>
|
||||||
|
<span className="font-sans text-[10px] tracking-[0.3em] text-[#bc9e65] font-extrabold uppercase">
|
||||||
|
CULINARY CHRONICLES
|
||||||
|
</span>
|
||||||
|
<span className="text-[#bc9e65] text-xs">❧</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="font-serif text-3xl sm:text-4xl lg:text-[42px] font-bold text-white tracking-wider leading-tight uppercase">
|
||||||
|
Spices & <span className="text-[#bc9e65]">Stories</span>
|
||||||
|
</h1>
|
||||||
|
<div className="w-24 h-[1px] bg-gradient-to-r from-transparent via-[#bc9e65] to-transparent mx-auto mt-4" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Blog Posts Grid */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 w-full">
|
||||||
|
{BLOG_POSTS.map((post) => (
|
||||||
|
<div
|
||||||
|
key={post.id}
|
||||||
|
className="relative bg-gradient-to-br from-[#0c2419]/90 via-[#06120c]/95 to-[#040b07]/98 border border-[#bc9e65]/40 p-5 rounded-none shadow-2xl flex flex-col group overflow-visible transition-all duration-300 hover:-translate-y-1"
|
||||||
|
>
|
||||||
|
{/* Corner leaves and border outline */}
|
||||||
|
<div className="absolute inset-1.5 border border-[#bc9e65]/15 pointer-events-none" />
|
||||||
|
<CornerOrnaments />
|
||||||
|
|
||||||
|
{/* Image Frame */}
|
||||||
|
<div className="relative w-full aspect-[4/3] border border-[#bc9e65]/35 overflow-hidden mb-4 bg-[#06120c] shadow-md z-10"
|
||||||
|
style={{
|
||||||
|
clipPath: "polygon(50% 0%, 100% 20%, 100% 100%, 0% 100%, 0% 20%)"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={post.image}
|
||||||
|
alt={post.title}
|
||||||
|
fill
|
||||||
|
sizes="(max-width: 768px) 100vw, 30vw"
|
||||||
|
className="object-cover group-hover:scale-105 transition-transform duration-[1.5s]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Meta */}
|
||||||
|
<div className="flex items-center justify-between font-sans text-[9px] tracking-wider text-[#bc9e65] uppercase font-bold mb-2 z-10">
|
||||||
|
<span>{post.date}</span>
|
||||||
|
<span>BY {post.author}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Details */}
|
||||||
|
<h3 className="font-serif text-sm lg:text-[15px] font-bold text-white tracking-wider uppercase mb-2 group-hover:text-[#bc9e65] transition-colors duration-300 z-10 min-h-[44px]">
|
||||||
|
{post.title}
|
||||||
|
</h3>
|
||||||
|
<p className="font-sans text-stone-300 text-[11px] leading-relaxed tracking-wider mb-6 flex-1 z-10">
|
||||||
|
{post.desc}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Read button */}
|
||||||
|
<button
|
||||||
|
className="clickable self-start font-sans font-bold text-[9px] tracking-[0.2em] text-[#bc9e65] uppercase hover:text-white transition-colors duration-300 z-10 flex items-center gap-1.5"
|
||||||
|
onClick={() => alert(`Enjoying "${post.title}"? Full recipe chronicles coming soon to Waterloo!`)}
|
||||||
|
>
|
||||||
|
READ MORE <span className="text-[10px] group-hover:translate-x-1 transition-transform duration-300">→</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</SubpageLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
207
src/app/contact/page.tsx
Normal file
207
src/app/contact/page.tsx
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import Image from "next/image";
|
||||||
|
import SubpageLayout from "@/components/SubpageLayout";
|
||||||
|
|
||||||
|
const CornerOrnaments = () => (
|
||||||
|
<>
|
||||||
|
<svg className="absolute w-3.5 h-3.5 text-[#bc9e65]/40 top-2.5 left-2.5 pointer-events-none select-none transition-all duration-500 group-hover:text-[#bc9e65]/80" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M2 2h6v1.5H3.5V8H2V2zm5 5c0-1.1.9-2 2-2h4v1.5H9V11H7V7z" />
|
||||||
|
</svg>
|
||||||
|
<svg className="absolute w-3.5 h-3.5 text-[#bc9e65]/40 top-2.5 right-2.5 rotate-90 pointer-events-none select-none transition-all duration-500 group-hover:text-[#bc9e65]/80" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M2 2h6v1.5H3.5V8H2V2zm5 5c0-1.1.9-2 2-2h4v1.5H9V11H7V7z" />
|
||||||
|
</svg>
|
||||||
|
<svg className="absolute w-3.5 h-3.5 text-[#bc9e65]/40 bottom-2.5 left-2.5 -rotate-90 pointer-events-none select-none transition-all duration-500 group-hover:text-[#bc9e65]/80" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M2 2h6v1.5H3.5V8H2V2zm5 5c0-1.1.9-2 2-2h4v1.5H9V11H7V7z" />
|
||||||
|
</svg>
|
||||||
|
<svg className="absolute w-3.5 h-3.5 text-[#bc9e65]/40 bottom-2.5 right-2.5 rotate-180 pointer-events-none select-none transition-all duration-500 group-hover:text-[#bc9e65]/80" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M2 2h6v1.5H3.5V8H2V2zm5 5c0-1.1.9-2 2-2h4v1.5H9V11H7V7z" />
|
||||||
|
</svg>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default function ContactPage() {
|
||||||
|
const [formData, setFormData] = useState({ name: "", email: "", msg: "" });
|
||||||
|
const [submitted, setSubmitted] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (formData.name && formData.email && formData.msg) {
|
||||||
|
setSubmitted(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setSubmitted(false);
|
||||||
|
setFormData({ name: "", email: "", msg: "" });
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SubpageLayout>
|
||||||
|
<div className="absolute inset-0 z-0 pointer-events-none select-none opacity-40">
|
||||||
|
<Image
|
||||||
|
src="/images/category_bg.jpg"
|
||||||
|
alt="Backdrop"
|
||||||
|
fill
|
||||||
|
priority
|
||||||
|
className="object-cover object-center"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="max-w-5xl mx-auto px-6 py-12 relative z-10">
|
||||||
|
|
||||||
|
{/* Header */}
|
||||||
|
<div className="text-center pb-12 max-w-2xl mx-auto select-text">
|
||||||
|
<div className="flex items-center gap-3 justify-center mb-2">
|
||||||
|
<span className="text-[#bc9e65] text-xs">❧</span>
|
||||||
|
<span className="font-sans text-[10px] tracking-[0.3em] text-[#bc9e65] font-extrabold uppercase">
|
||||||
|
GET IN TOUCH
|
||||||
|
</span>
|
||||||
|
<span className="text-[#bc9e65] text-xs">❧</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="font-serif text-3xl sm:text-4xl lg:text-[42px] font-bold text-white tracking-wider leading-tight uppercase">
|
||||||
|
Connect With <span className="text-[#bc9e65]">Us</span>
|
||||||
|
</h1>
|
||||||
|
<div className="w-24 h-[1px] bg-gradient-to-r from-transparent via-[#bc9e65] to-transparent mx-auto mt-4" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 2-Column Info & Form */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 items-stretch mt-4">
|
||||||
|
|
||||||
|
{/* Column 1: Info */}
|
||||||
|
<div className="relative bg-gradient-to-br from-[#0c2419]/90 via-[#06120c]/95 to-[#040b07]/98 border border-[#bc9e65]/40 p-8 flex flex-col justify-between group overflow-visible shadow-2xl">
|
||||||
|
<div className="absolute inset-1.5 border border-[#bc9e65]/15 pointer-events-none" />
|
||||||
|
<CornerOrnaments />
|
||||||
|
|
||||||
|
<div className="select-text relative z-10">
|
||||||
|
<h3 className="font-serif text-lg font-bold text-white tracking-wider uppercase mb-4">
|
||||||
|
THE DINING COURT
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<h4 className="font-sans text-[10px] sm:text-[11px] tracking-widest text-[#bc9e65] uppercase font-bold mb-1">
|
||||||
|
OUR HAVEN
|
||||||
|
</h4>
|
||||||
|
<p className="font-sans text-stone-300 text-xs leading-relaxed tracking-wider">
|
||||||
|
123 Erb Street West,<br />
|
||||||
|
Waterloo, ON N2L 1T4
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h4 className="font-sans text-[10px] sm:text-[11px] tracking-widest text-[#bc9e65] uppercase font-bold mb-1">
|
||||||
|
HEAR OUR VOICE
|
||||||
|
</h4>
|
||||||
|
<p className="font-sans text-stone-300 text-xs leading-relaxed tracking-wider">
|
||||||
|
Direct Line: (519) 555-0199<br />
|
||||||
|
Royal Inquiries: concierge@mydosaplace.ca
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h4 className="font-sans text-[10px] sm:text-[11px] tracking-widest text-[#bc9e65] uppercase font-bold mb-1">
|
||||||
|
TIMINGS OF THE COURT
|
||||||
|
</h4>
|
||||||
|
<p className="font-sans text-stone-300 text-xs leading-relaxed tracking-wider">
|
||||||
|
Lunch Service: 11:30 AM – 3:00 PM<br />
|
||||||
|
Dinner Service: 5:30 PM – 10:30 PM<br />
|
||||||
|
<span className="text-[#bc9e65] font-semibold mt-1 block">Closed on Tuesdays for spice curing.</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Banner bottom */}
|
||||||
|
<div className="border-t border-[#bc9e65]/20 pt-6 mt-8 relative z-10">
|
||||||
|
<span className="font-serif text-[10px] tracking-[0.2em] text-[#bc9e65]/80 uppercase block text-center">
|
||||||
|
❧ HOSPITALITY SERVED WITH SOUL ☙
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Column 2: Form */}
|
||||||
|
<div className="relative bg-gradient-to-br from-[#0c2419]/90 via-[#06120c]/95 to-[#040b07]/98 border border-[#bc9e65]/40 p-8 group overflow-visible shadow-2xl">
|
||||||
|
<div className="absolute inset-1.5 border border-[#bc9e65]/15 pointer-events-none" />
|
||||||
|
<CornerOrnaments />
|
||||||
|
|
||||||
|
<div className="relative z-10 h-full flex flex-col justify-between">
|
||||||
|
<div>
|
||||||
|
<h3 className="font-serif text-lg font-bold text-white tracking-wider uppercase mb-5">
|
||||||
|
WRITE A NOTE
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
{submitted ? (
|
||||||
|
<div className="flex flex-col items-center justify-center py-10 text-center animate-pulse">
|
||||||
|
<svg className="w-10 h-10 text-[#bc9e65] mb-4" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z" />
|
||||||
|
</svg>
|
||||||
|
<h4 className="font-serif text-white tracking-wider uppercase text-sm mb-2">
|
||||||
|
INQUIRY SENT
|
||||||
|
</h4>
|
||||||
|
<p className="font-sans text-stone-300 text-[11px] leading-relaxed max-w-[260px] mx-auto tracking-wider">
|
||||||
|
Thank you for contacting the court. Our hospitality concierge will respond within a single sun cycle.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label className="block font-sans text-[9px] tracking-widest text-[#bc9e65] uppercase font-bold mb-1">
|
||||||
|
YOUR NAME
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
className="w-full bg-[#06120c]/60 border border-[#bc9e65]/35 p-2 text-xs font-sans text-white focus:outline-none focus:border-[#bc9e65] transition-colors rounded-none placeholder-stone-600"
|
||||||
|
placeholder="Enter your name"
|
||||||
|
value={formData.name}
|
||||||
|
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block font-sans text-[9px] tracking-widest text-[#bc9e65] uppercase font-bold mb-1">
|
||||||
|
EMAIL ADRESS
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
required
|
||||||
|
className="w-full bg-[#06120c]/60 border border-[#bc9e65]/35 p-2 text-xs font-sans text-white focus:outline-none focus:border-[#bc9e65] transition-colors rounded-none placeholder-stone-600"
|
||||||
|
placeholder="concierge@example.com"
|
||||||
|
value={formData.email}
|
||||||
|
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block font-sans text-[9px] tracking-widest text-[#bc9e65] uppercase font-bold mb-1">
|
||||||
|
MESSAGE TO THE COURT
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
rows={4}
|
||||||
|
required
|
||||||
|
className="w-full bg-[#06120c]/60 border border-[#bc9e65]/35 p-2 text-xs font-sans text-white focus:outline-none focus:border-[#bc9e65] transition-colors rounded-none resize-none placeholder-stone-600"
|
||||||
|
placeholder="Tell us what's on your mind..."
|
||||||
|
value={formData.msg}
|
||||||
|
onChange={(e) => setFormData({ ...formData, msg: e.target.value })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="clickable w-full py-2.5 border border-[#bc9e65]/50 hover:border-[#bc9e65] bg-transparent text-[#bc9e65] hover:bg-[#bc9e65] hover:text-[#06120c] font-sans font-bold text-[9px] tracking-widest uppercase transition-all duration-300 shadow-md"
|
||||||
|
>
|
||||||
|
SEND INQUIRY
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</SubpageLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
33
src/app/menu/page.tsx
Normal file
33
src/app/menu/page.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import SubpageLayout from "@/components/SubpageLayout";
|
||||||
|
import CulinaryCategories from "@/components/CulinaryCategories";
|
||||||
|
import MenuPreview from "@/components/MenuPreview";
|
||||||
|
|
||||||
|
export default function MenuPage() {
|
||||||
|
return (
|
||||||
|
<SubpageLayout>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
|
||||||
|
{/* Subpage Header Banner */}
|
||||||
|
<div className="text-center pt-12 pb-6 max-w-2xl mx-auto px-6 relative z-10 select-text">
|
||||||
|
<div className="flex items-center gap-3 justify-center mb-2">
|
||||||
|
<span className="text-[#bc9e65] text-xs">❧</span>
|
||||||
|
<span className="font-sans text-[10px] tracking-[0.3em] text-[#bc9e65] font-extrabold uppercase">
|
||||||
|
THE ROYAL BANQUET
|
||||||
|
</span>
|
||||||
|
<span className="text-[#bc9e65] text-xs">❧</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="font-serif text-3xl sm:text-4xl lg:text-[42px] font-bold text-white tracking-wider leading-tight uppercase">
|
||||||
|
Feast <span className="text-[#bc9e65]">Menu</span>
|
||||||
|
</h1>
|
||||||
|
<div className="w-24 h-[1px] bg-gradient-to-r from-transparent via-[#bc9e65] to-transparent mx-auto mt-4" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Core sections */}
|
||||||
|
<CulinaryCategories />
|
||||||
|
<MenuPreview />
|
||||||
|
</div>
|
||||||
|
</SubpageLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
185
src/app/reservation/page.tsx
Normal file
185
src/app/reservation/page.tsx
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import Image from "next/image";
|
||||||
|
import SubpageLayout from "@/components/SubpageLayout";
|
||||||
|
|
||||||
|
const CornerOrnaments = () => (
|
||||||
|
<>
|
||||||
|
<svg className="absolute w-3.5 h-3.5 text-[#bc9e65]/40 top-2.5 left-2.5 pointer-events-none select-none transition-all duration-500 group-hover:text-[#bc9e65]/80" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M2 2h6v1.5H3.5V8H2V2zm5 5c0-1.1.9-2 2-2h4v1.5H9V11H7V7z" />
|
||||||
|
</svg>
|
||||||
|
<svg className="absolute w-3.5 h-3.5 text-[#bc9e65]/40 top-2.5 right-2.5 rotate-90 pointer-events-none select-none transition-all duration-500 group-hover:text-[#bc9e65]/80" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M2 2h6v1.5H3.5V8H2V2zm5 5c0-1.1.9-2 2-2h4v1.5H9V11H7V7z" />
|
||||||
|
</svg>
|
||||||
|
<svg className="absolute w-3.5 h-3.5 text-[#bc9e65]/40 bottom-2.5 left-2.5 -rotate-90 pointer-events-none select-none transition-all duration-500 group-hover:text-[#bc9e65]/80" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M2 2h6v1.5H3.5V8H2V2zm5 5c0-1.1.9-2 2-2h4v1.5H9V11H7V7z" />
|
||||||
|
</svg>
|
||||||
|
<svg className="absolute w-3.5 h-3.5 text-[#bc9e65]/40 bottom-2.5 right-2.5 rotate-180 pointer-events-none select-none transition-all duration-500 group-hover:text-[#bc9e65]/80" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M2 2h6v1.5H3.5V8H2V2zm5 5c0-1.1.9-2 2-2h4v1.5H9V11H7V7z" />
|
||||||
|
</svg>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default function ReservationPage() {
|
||||||
|
const [guests, setGuests] = useState("2 Guests");
|
||||||
|
const [date, setDate] = useState("2026-07-16");
|
||||||
|
const [time, setTime] = useState("06:00 PM");
|
||||||
|
const [area, setArea] = useState("Heritage Dining Court");
|
||||||
|
const [reserved, setReserved] = useState(false);
|
||||||
|
|
||||||
|
const handleReserve = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setReserved(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setReserved(false);
|
||||||
|
}, 6000);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SubpageLayout>
|
||||||
|
<div className="absolute inset-0 z-0 pointer-events-none select-none opacity-40">
|
||||||
|
<Image
|
||||||
|
src="/images/category_bg.jpg"
|
||||||
|
alt="Backdrop"
|
||||||
|
fill
|
||||||
|
priority
|
||||||
|
className="object-cover object-center"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="max-w-[500px] mx-auto px-6 py-12 relative z-10">
|
||||||
|
|
||||||
|
{/* Header */}
|
||||||
|
<div className="text-center pb-10 select-text">
|
||||||
|
<div className="flex items-center gap-3 justify-center mb-2">
|
||||||
|
<span className="text-[#bc9e65] text-xs">❧</span>
|
||||||
|
<span className="font-sans text-[10px] tracking-[0.3em] text-[#bc9e65] font-extrabold uppercase">
|
||||||
|
TABLE BOOKING
|
||||||
|
</span>
|
||||||
|
<span className="text-[#bc9e65] text-xs">❧</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="font-serif text-3xl sm:text-4xl lg:text-[42px] font-bold text-white tracking-wider leading-tight uppercase">
|
||||||
|
Reserve <span className="text-[#bc9e65]">Table</span>
|
||||||
|
</h1>
|
||||||
|
<div className="w-24 h-[1px] bg-gradient-to-r from-transparent via-[#bc9e65] to-transparent mx-auto mt-4" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Card Frame containing booking inputs */}
|
||||||
|
<div className="relative bg-gradient-to-br from-[#0c2419]/90 via-[#06120c]/95 to-[#040b07]/98 border border-[#bc9e65]/40 p-8 group overflow-visible shadow-2xl">
|
||||||
|
<div className="absolute inset-1.5 border border-[#bc9e65]/15 pointer-events-none" />
|
||||||
|
<CornerOrnaments />
|
||||||
|
|
||||||
|
<div className="relative z-10 h-full flex flex-col justify-between">
|
||||||
|
{reserved ? (
|
||||||
|
<div className="flex flex-col items-center justify-center py-12 text-center animate-pulse">
|
||||||
|
<svg className="w-12 h-12 text-[#bc9e65] mb-5" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" />
|
||||||
|
</svg>
|
||||||
|
<h4 className="font-serif text-white tracking-wider uppercase text-[15px] mb-2.5">
|
||||||
|
RESERVATION CONFIRMED
|
||||||
|
</h4>
|
||||||
|
<div className="h-[1px] w-20 bg-[#bc9e65]/35 mb-4" />
|
||||||
|
<p className="font-sans text-stone-300 text-[11px] leading-relaxed max-w-[280px] mx-auto tracking-wider mb-2">
|
||||||
|
Your table has been reserved for <strong className="text-white">{guests}</strong> on <strong className="text-white">{date}</strong> at <strong className="text-white">{time}</strong> in our <strong className="text-white">{area}</strong>.
|
||||||
|
</p>
|
||||||
|
<p className="font-sans text-[#bc9e65] text-[10px] tracking-widest uppercase mt-4">
|
||||||
|
A verification parchment has been sent.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<form onSubmit={handleReserve} className="space-y-5">
|
||||||
|
|
||||||
|
{/* Guests selection */}
|
||||||
|
<div>
|
||||||
|
<label className="block font-sans text-[9px] tracking-widest text-[#bc9e65] uppercase font-bold mb-1.5">
|
||||||
|
NUMBER OF GUESTS
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
className="w-full bg-[#06120c]/60 border border-[#bc9e65]/35 p-2 text-xs font-sans text-white focus:outline-none focus:border-[#bc9e65] transition-colors rounded-none cursor-pointer"
|
||||||
|
value={guests}
|
||||||
|
onChange={(e) => setGuests(e.target.value)}
|
||||||
|
>
|
||||||
|
<option className="bg-[#06120c]" value="1 Guest">1 Guest</option>
|
||||||
|
<option className="bg-[#06120c]" value="2 Guests">2 Guests</option>
|
||||||
|
<option className="bg-[#06120c]" value="3 Guests">3 Guests</option>
|
||||||
|
<option className="bg-[#06120c]" value="4 Guests">4 Guests</option>
|
||||||
|
<option className="bg-[#06120c]" value="5 Guests">5 Guests</option>
|
||||||
|
<option className="bg-[#06120c]" value="6+ Guests">6+ Guests (Banquet inquiry)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Date Selection */}
|
||||||
|
<div>
|
||||||
|
<label className="block font-sans text-[9px] tracking-widest text-[#bc9e65] uppercase font-bold mb-1.5">
|
||||||
|
RESERVATION DATE
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
required
|
||||||
|
className="w-full bg-[#06120c]/60 border border-[#bc9e65]/35 p-2 text-xs font-sans text-white focus:outline-none focus:border-[#bc9e65] transition-colors rounded-none"
|
||||||
|
value={date}
|
||||||
|
onChange={(e) => setDate(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Time Selection */}
|
||||||
|
<div>
|
||||||
|
<label className="block font-sans text-[9px] tracking-widest text-[#bc9e65] uppercase font-bold mb-1.5">
|
||||||
|
PREFERRED TIME
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
className="w-full bg-[#06120c]/60 border border-[#bc9e65]/35 p-2 text-xs font-sans text-white focus:outline-none focus:border-[#bc9e65] transition-colors rounded-none cursor-pointer"
|
||||||
|
value={time}
|
||||||
|
onChange={(e) => setTime(e.target.value)}
|
||||||
|
>
|
||||||
|
<optgroup className="bg-[#06120c]" label="Lunch Service">
|
||||||
|
<option value="11:30 AM">11:30 AM</option>
|
||||||
|
<option value="12:00 PM">12:00 PM</option>
|
||||||
|
<option value="01:00 PM">01:00 PM</option>
|
||||||
|
<option value="02:00 PM">02:00 PM</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup className="bg-[#06120c]" label="Dinner Service">
|
||||||
|
<option value="05:30 PM">05:30 PM</option>
|
||||||
|
<option value="06:00 PM">06:00 PM</option>
|
||||||
|
<option value="07:00 PM">07:00 PM</option>
|
||||||
|
<option value="08:00 PM">08:00 PM</option>
|
||||||
|
<option value="09:00 PM">09:00 PM</option>
|
||||||
|
<option value="10:00 PM">10:00 PM</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Seating Area Selection */}
|
||||||
|
<div>
|
||||||
|
<label className="block font-sans text-[9px] tracking-widest text-[#bc9e65] uppercase font-bold mb-1.5">
|
||||||
|
SEATING COURT
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
className="w-full bg-[#06120c]/60 border border-[#bc9e65]/35 p-2 text-xs font-sans text-white focus:outline-none focus:border-[#bc9e65] transition-colors rounded-none cursor-pointer"
|
||||||
|
value={area}
|
||||||
|
onChange={(e) => setArea(e.target.value)}
|
||||||
|
>
|
||||||
|
<option className="bg-[#06120c]" value="Heritage Dining Court">Heritage Dining Court (Main Hall)</option>
|
||||||
|
<option className="bg-[#06120c]" value="Banana Leaf Courtyard">Banana Leaf Courtyard (Alfresco)</option>
|
||||||
|
<option className="bg-[#06120c]" value="Royal Maharaja Chambers">Royal Maharaja Chambers (Private Dining)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Submit button */}
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="clickable w-full py-3 border border-[#bc9e65]/50 hover:border-[#bc9e65] bg-transparent text-[#bc9e65] hover:bg-[#bc9e65] hover:text-[#06120c] font-sans font-bold text-[9px] tracking-widest uppercase transition-all duration-300 shadow-md mt-6"
|
||||||
|
>
|
||||||
|
SECURE ROYAL SEAT
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</SubpageLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -10,7 +10,7 @@ export default function Header({ visible }: { visible: boolean }) {
|
|||||||
<div className="max-w-7xl mx-auto h-full px-6 flex items-center justify-between">
|
<div className="max-w-7xl mx-auto h-full px-6 flex items-center justify-between">
|
||||||
|
|
||||||
{/* Left Side: Brand Logo */}
|
{/* Left Side: Brand Logo */}
|
||||||
<a href="#" className="clickable flex items-center gap-2.5 select-none">
|
<a href="/" className="clickable flex items-center gap-2.5 select-none">
|
||||||
<svg className="w-5 h-5 text-[#bc9e65]" viewBox="0 0 24 24" fill="currentColor">
|
<svg className="w-5 h-5 text-[#bc9e65]" viewBox="0 0 24 24" fill="currentColor">
|
||||||
<path d="M12 21c4.5-3.5 6-7.5 6-10.5C18 7.5 15.5 6 12 10 8.5 6 6 7.5 6 10.5c0 3 1.5 7 6 10.5z" />
|
<path d="M12 21c4.5-3.5 6-7.5 6-10.5C18 7.5 15.5 6 12 10 8.5 6 6 7.5 6 10.5c0 3 1.5 7 6 10.5z" />
|
||||||
</svg>
|
</svg>
|
||||||
@ -22,41 +22,41 @@ export default function Header({ visible }: { visible: boolean }) {
|
|||||||
{/* Center: Navigation Links */}
|
{/* Center: Navigation Links */}
|
||||||
<nav className="hidden md:flex items-center gap-8">
|
<nav className="hidden md:flex items-center gap-8">
|
||||||
<a
|
<a
|
||||||
href="#story"
|
href="/"
|
||||||
className="clickable font-sans text-[10px] sm:text-xs tracking-widest uppercase text-stone-300 hover:text-[#bc9e65] transition-colors duration-300 font-bold"
|
className="clickable font-sans text-[10px] sm:text-xs tracking-widest uppercase text-stone-300 hover:text-[#bc9e65] transition-colors duration-300 font-bold"
|
||||||
>
|
>
|
||||||
Our Story
|
Home
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="#categories"
|
href="/about"
|
||||||
className="clickable font-sans text-[10px] sm:text-xs tracking-widest uppercase text-stone-300 hover:text-[#bc9e65] transition-colors duration-300 font-bold"
|
className="clickable font-sans text-[10px] sm:text-xs tracking-widest uppercase text-stone-300 hover:text-[#bc9e65] transition-colors duration-300 font-bold"
|
||||||
>
|
>
|
||||||
Categories
|
About
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="#menu"
|
href="/menu"
|
||||||
className="clickable font-sans text-[10px] sm:text-xs tracking-widest uppercase text-stone-300 hover:text-[#bc9e65] transition-colors duration-300 font-bold"
|
className="clickable font-sans text-[10px] sm:text-xs tracking-widest uppercase text-stone-300 hover:text-[#bc9e65] transition-colors duration-300 font-bold"
|
||||||
>
|
>
|
||||||
Feast Menu
|
Menu
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="#chef"
|
href="/blog"
|
||||||
className="clickable font-sans text-[10px] sm:text-xs tracking-widest uppercase text-stone-300 hover:text-[#bc9e65] transition-colors duration-300 font-bold"
|
className="clickable font-sans text-[10px] sm:text-xs tracking-widest uppercase text-stone-300 hover:text-[#bc9e65] transition-colors duration-300 font-bold"
|
||||||
>
|
>
|
||||||
Our Chef
|
Blog
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="#events"
|
href="/contact"
|
||||||
className="clickable font-sans text-[10px] sm:text-xs tracking-widest uppercase text-stone-300 hover:text-[#bc9e65] transition-colors duration-300 font-bold"
|
className="clickable font-sans text-[10px] sm:text-xs tracking-widest uppercase text-stone-300 hover:text-[#bc9e65] transition-colors duration-300 font-bold"
|
||||||
>
|
>
|
||||||
Banquets
|
Contact
|
||||||
</a>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Right Side: Reservation Button */}
|
{/* Right Side: Reservation Button */}
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<a
|
<a
|
||||||
href="#footer"
|
href="/reservation"
|
||||||
className="clickable px-4 sm:px-5 py-2 border border-[#bc9e65]/50 hover:border-[#bc9e65] rounded-full text-[9px] tracking-widest font-sans font-bold text-[#bc9e65] hover:bg-[#bc9e65] hover:text-[#06120c] transition-all duration-300 shadow-md"
|
className="clickable px-4 sm:px-5 py-2 border border-[#bc9e65]/50 hover:border-[#bc9e65] rounded-full text-[9px] tracking-widest font-sans font-bold text-[#bc9e65] hover:bg-[#bc9e65] hover:text-[#06120c] transition-all duration-300 shadow-md"
|
||||||
>
|
>
|
||||||
RESERVE TABLE
|
RESERVE TABLE
|
||||||
|
|||||||
37
src/components/SubpageLayout.tsx
Normal file
37
src/components/SubpageLayout.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import SmoothScroll from "@/components/SmoothScroll";
|
||||||
|
import CustomCursor from "@/components/CustomCursor";
|
||||||
|
import SoundManager from "@/components/SoundManager";
|
||||||
|
import Header from "@/components/Header";
|
||||||
|
import Footer from "@/components/Footer";
|
||||||
|
|
||||||
|
export default function SubpageLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
const [mounted, setMounted] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="w-full relative bg-darkbg text-foreground overflow-hidden select-none">
|
||||||
|
{/* Isolated wrapper for client-only widgets to prevent hydration insertion conflicts */}
|
||||||
|
<div id="client-only-widgets">
|
||||||
|
{mounted && <SoundManager />}
|
||||||
|
{mounted && <CustomCursor />}
|
||||||
|
{mounted && <Header visible={true} />}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Lenis Scroll syncing with GSAP triggers */}
|
||||||
|
<SmoothScroll>
|
||||||
|
<div className="pt-24 min-h-screen flex flex-col justify-between">
|
||||||
|
<div className="flex-1 w-full relative z-10">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
</SmoothScroll>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user