mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 15:48:11 +00:00
91 lines
3.4 KiB
TypeScript
91 lines
3.4 KiB
TypeScript
'use client';
|
|
|
|
import { Check } from 'lucide-react';
|
|
import { useEffect, useRef } from 'react';
|
|
|
|
// Shared source of truth for the booking flow's step order/labels — also
|
|
// consumed by AppSidebar's vertical step list on desktop.
|
|
export const BOOKING_STEPS = [
|
|
{ id: 'search', name: 'Search' },
|
|
{ id: 'results', name: 'Results' },
|
|
{ id: 'passengers', name: 'Passengers' },
|
|
{ id: 'seats', name: 'Seats' },
|
|
{ id: 'review', name: 'Review' },
|
|
{ id: 'payment', name: 'Payment' },
|
|
{ id: 'confirmation', name: 'Done' },
|
|
];
|
|
const steps = BOOKING_STEPS;
|
|
|
|
export function ProgressIndicator({ currentStep }: { currentStep: string }) {
|
|
const currentIndex = steps.findIndex((s) => s.id === currentStep);
|
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
const activeRef = useRef<HTMLLIElement>(null);
|
|
|
|
// Auto-scroll active step to centre on mobile
|
|
useEffect(() => {
|
|
const container = scrollRef.current;
|
|
const active = activeRef.current;
|
|
if (!container || !active) return;
|
|
const offset = active.offsetLeft + active.offsetWidth / 2 - container.clientWidth / 2;
|
|
container.scrollTo({ left: offset, behavior: 'smooth' });
|
|
}, [currentIndex]);
|
|
|
|
const stepItem = (step: typeof steps[0], index: number) => {
|
|
const isComplete = index < currentIndex;
|
|
const isCurrent = index === currentIndex;
|
|
|
|
return (
|
|
<li
|
|
key={step.id}
|
|
ref={isCurrent ? activeRef : undefined}
|
|
className="flex flex-col items-center flex-shrink-0 w-[4.5rem] md:flex-1"
|
|
>
|
|
{/* Line + Circle row */}
|
|
<div className="flex items-center w-full">
|
|
{/* Left connector */}
|
|
<div className={`flex-1 h-0.5 ${index === 0 ? 'invisible' : isComplete ? 'bg-primary' : 'bg-gray-200 dark:bg-gray-700'}`} />
|
|
|
|
{/* Circle */}
|
|
<div className={`flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full transition-all duration-300 ${
|
|
isComplete ? 'bg-primary shadow-sm' :
|
|
isCurrent ? 'border-[3px] border-primary bg-white dark:bg-gray-800 shadow-sm' :
|
|
'border-2 border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800'
|
|
}`}>
|
|
{isComplete
|
|
? <Check className="h-4 w-4 text-white" />
|
|
: <span className={`text-xs font-bold ${isCurrent ? 'text-primary' : 'text-gray-400 dark:text-gray-500'}`}>{index + 1}</span>
|
|
}
|
|
</div>
|
|
|
|
{/* Right connector */}
|
|
<div className={`flex-1 h-0.5 ${index === steps.length - 1 ? 'invisible' : isComplete ? 'bg-primary' : 'bg-gray-200 dark:bg-gray-700'}`} />
|
|
</div>
|
|
|
|
{/* Label */}
|
|
<p className={`mt-1.5 text-[10px] md:text-xs font-medium text-center leading-tight whitespace-nowrap ${
|
|
isCurrent ? 'text-primary font-bold' :
|
|
isComplete ? 'text-gray-600 dark:text-gray-300' :
|
|
'text-gray-400 dark:text-gray-500'
|
|
}`}>
|
|
{step.name}
|
|
</p>
|
|
</li>
|
|
);
|
|
};
|
|
|
|
// Desktop shows the vertical step list embedded in AppSidebar instead —
|
|
// this component is mobile-only now (the sidebar doesn't exist below lg).
|
|
return (
|
|
<nav aria-label="Progress" className="lg:hidden">
|
|
<div
|
|
ref={scrollRef}
|
|
className="overflow-x-auto scrollbar-hide px-4 py-3"
|
|
>
|
|
<ol className="flex items-start min-w-full">
|
|
{steps.map((s, i) => stepItem(s, i))}
|
|
</ol>
|
|
</div>
|
|
</nav>
|
|
);
|
|
}
|