"use client";
import * as React from "react";
import { MotionConfig, useReducedMotion } from "motion/react";
import { cn } from "@/lib/utils";
import { defaultHotelData, type HotelData } from "./data";
import type { StaySearch } from "./sections/booking";
import { Faq, Footer, Gallery, Location, Reviews } from "./sections/closing";
import { Amenities, Offers, Wellness } from "./sections/features";
import { Hero, Intro } from "./sections/hero";
import { Navbar } from "./sections/navbar";
import { Rooms, type ReservationRequest } from "./sections/rooms";
import { scrollToId, useMoney } from "./sections/ui";
export type { HotelData, StaySearch, ReservationRequest };
export { defaultHotelData };
export interface HotelTemplateProps {
/** Override any top-level content group (brand, rooms, offers…). Missing groups fall back to the demo content. */
data?: Partial<HotelData>;
/** Called when a guest searches dates from the booking bar. */
onSearch?: (search: StaySearch) => void;
/** Called when a guest requests a room. Return a promise to keep the loading state until your API answers. */
onReserve?: (request: ReservationRequest) => void | Promise<void>;
onSubscribe?: (email: string) => void | Promise<void>;
className?: string;
}
/**
* Maris House — a boutique seaside hotel landing page.
* Animated sea hero with a date-range booking bar, rooms with photo carousels and a reservation dialog,
* amenities, spa & dining tabs, offers with live countdowns, reviews, an illustrated map, a lightbox gallery, FAQ and footer.
*/
export function HotelTemplate({ data, onSearch, onReserve, onSubscribe, className }: HotelTemplateProps) {
const d: HotelData = React.useMemo(() => ({ ...defaultHotelData, ...data }), [data]);
const reduce = useReducedMotion();
const money = useMoney(d.brand.currency);
const [search, setSearch] = React.useState<StaySearch | null>(null);
const go = React.useCallback((id: string) => scrollToId(id, reduce), [reduce]);
const bookTop = React.useCallback(() => {
go("top");
window.setTimeout(
() => document.querySelector<HTMLButtonElement>('form[aria-label="Check availability"] button')?.focus({ preventScroll: true }),
reduce ? 0 : 600,
);
}, [go, reduce]);
return (
<MotionConfig reducedMotion="user">
<div className={cn("relative w-full bg-background text-foreground antialiased selection:bg-[#c9895c]/25", className)}>
<Navbar data={d} onBook={bookTop} />
<main>
<Hero
data={d}
onSearch={(s) => {
setSearch(s);
onSearch?.(s);
window.setTimeout(() => go("rooms"), 50);
}}
/>
<Intro data={d} />
<Rooms data={d} money={money} search={search} onReserve={onReserve} />
<Amenities items={d.amenities} />
<Wellness data={d} money={money} />
<Offers offers={d.offers} onBook={bookTop} />
<Reviews reviews={d.reviews} />
<Location data={d} />
<Gallery items={d.gallery} />
<Faq items={d.faq} />
</main>
<Footer data={d} onBook={bookTop} onNav={go} onSubscribe={onSubscribe} />
</div>
</MotionConfig>
);
}