Next.js 14 website with standalone output configured for Docker deployment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
76 lines
2.1 KiB
TypeScript
76 lines
2.1 KiB
TypeScript
"use client";
|
|
|
|
import { motion, useInView } from "framer-motion";
|
|
import { useRef } from "react";
|
|
import { ItineraryStop } from "@/lib/constants";
|
|
|
|
interface ItineraryProps {
|
|
stops: ItineraryStop[];
|
|
}
|
|
|
|
function ItineraryStopItem({
|
|
stop,
|
|
index,
|
|
isLast,
|
|
}: {
|
|
stop: ItineraryStop;
|
|
index: number;
|
|
isLast: boolean;
|
|
}) {
|
|
const ref = useRef(null);
|
|
const isInView = useInView(ref, { once: true, margin: "-50px" });
|
|
|
|
return (
|
|
<motion.div
|
|
ref={ref}
|
|
className="relative flex gap-6 md:gap-8"
|
|
initial={{ opacity: 0, x: -20 }}
|
|
animate={isInView ? { opacity: 1, x: 0 } : {}}
|
|
transition={{ delay: index * 0.1, duration: 0.5 }}
|
|
>
|
|
{/* Timeline */}
|
|
<div className="flex flex-col items-center">
|
|
<div className="w-12 h-12 rounded-full bg-bosphorus/10 border-2 border-bosphorus flex items-center justify-center text-bosphorus font-display font-bold text-xs flex-shrink-0">
|
|
{stop.time.replace(" PM", "").replace(" AM", "").replace("~", "")}
|
|
</div>
|
|
{!isLast && (
|
|
<div className="w-0.5 flex-1 bg-bosphorus/20 mt-2" />
|
|
)}
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div className="pb-10">
|
|
<span className="text-bosphorus font-semibold text-sm">{stop.time}</span>
|
|
<h3 className="font-display text-xl font-bold text-deep-nazar mt-1 mb-3">
|
|
{stop.title}
|
|
</h3>
|
|
<p className="text-deep-nazar/70 leading-relaxed">
|
|
{stop.description}
|
|
</p>
|
|
</div>
|
|
</motion.div>
|
|
);
|
|
}
|
|
|
|
export function Itinerary({ stops }: ItineraryProps) {
|
|
return (
|
|
<section className="py-16 md:py-24 section-padding">
|
|
<div className="max-w-3xl mx-auto">
|
|
<h2 className="font-display text-3xl md:text-4xl font-bold text-deep-nazar mb-12 text-center">
|
|
Your Itinerary
|
|
</h2>
|
|
|
|
<div>
|
|
{stops.map((stop, i) => (
|
|
<ItineraryStopItem
|
|
key={i}
|
|
stop={stop}
|
|
index={i}
|
|
isLast={i === stops.length - 1}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|