Fazekit
Catalog

Code

import * as React from "react";
import { Check, Home, Package, ShoppingCart, Truck } from "lucide-react";
import { cn } from "@/lib/utils";

export type OrderStep = { label: string; time?: string; icon?: React.ComponentType<{ className?: string }> };

const DEFAULT_STEPS: OrderStep[] = [
  { label: "Order placed", icon: ShoppingCart },
  { label: "Packed", icon: Package },
  { label: "Shipped", icon: Truck },
  { label: "Delivered", icon: Home },
];

export interface OrderTrackerProps {
  /** Index of the current step (0-based). */
  current: number;
  steps?: OrderStep[];
  orientation?: "horizontal" | "vertical";
  className?: string;
}

export function OrderTracker({ current, steps = DEFAULT_STEPS, orientation = "horizontal", className }: OrderTrackerProps) {
  const vertical = orientation === "vertical";
  return (
    <ol className={cn("flex w-full", vertical ? "flex-col" : "items-start", className)} aria-label="Order progress">
      {steps.map((s, i) => {
        const done = i < current;
        const active = i === current;
        const Icon = done ? Check : s.icon ?? DEFAULT_STEPS[i]?.icon ?? Package;
        const last = i === steps.length - 1;
        return (
          <li key={s.label} className={cn("relative flex", vertical ? "gap-4 pb-8 last:pb-0" : "flex-1 flex-col items-center text-center")} aria-current={active ? "step" : undefined}>
            {!last && (
              <span
                aria-hidden
                className={cn("absolute bg-border", vertical ? "left-5 top-10 h-[calc(100%-2.5rem)] w-0.5" : "left-1/2 top-5 h-0.5 w-full")}
              >
                <span
                  className="block bg-primary transition-all duration-700"
                  style={vertical ? { height: done ? "100%" : "0%", width: "100%" } : { width: done ? "100%" : "0%", height: "100%" }}
                />
              </span>
            )}
            <span
              className={cn(
                "relative z-10 grid size-10 shrink-0 place-items-center rounded-full border-2 bg-background transition",
                done && "border-primary bg-primary text-primary-foreground",
                active && "border-primary text-primary shadow-[0_0_0_6px] shadow-primary/15",
                !done && !active && "text-muted-foreground",
              )}
            >
              <Icon className="size-4" />
            </span>
            <div className={cn(vertical ? "pt-2" : "mt-3 px-1")}>
              <p className={cn("text-sm font-medium", !done && !active && "text-muted-foreground")}>{s.label}</p>
              {s.time && <p className="text-xs text-muted-foreground">{s.time}</p>}
            </div>
          </li>
        );
      })}
    </ol>
  );
}

More in E-commerce