Fazekit

Code

import * as React from "react";
import { PartyPopper, Truck } from "lucide-react";
import { cn } from "@/lib/utils";

export interface FreeShippingBarProps {
  subtotal: number;
  threshold: number;
  currency?: string;
  locale?: string;
  className?: string;
}

export function FreeShippingBar({ subtotal, threshold, currency = "EUR", locale = "en-IE", className }: FreeShippingBarProps) {
  const pct = Math.min(100, (subtotal / threshold) * 100);
  const left = Math.max(0, threshold - subtotal);
  const fmt = new Intl.NumberFormat(locale, { style: "currency", currency });
  const done = left === 0;

  return (
    <div className={cn("w-full rounded-xl border bg-card p-4", className)}>
      <p className="flex items-center gap-2 text-sm">
        {done ? <PartyPopper className="size-4 text-emerald-500" /> : <Truck className="size-4 text-primary" />}
        {done ? (
          <span className="font-medium">You unlocked free shipping!</span>
        ) : (
          <span>
            Add <span className="font-semibold">{fmt.format(left)}</span> more for <span className="font-semibold">free shipping</span>
          </span>
        )}
      </p>
      <div
        className="relative mt-3 h-2 overflow-hidden rounded-full bg-muted"
        role="progressbar"
        aria-valuenow={Math.round(pct)}
        aria-valuemin={0}
        aria-valuemax={100}
      >
        <div
          className={cn(
            "h-full rounded-full transition-[width] duration-700 ease-out",
            done ? "bg-emerald-500" : "bg-gradient-to-r from-primary/70 to-primary",
          )}
          style={{ width: `${pct}%` }}
        />
      </div>
      <div className="mt-1.5 flex justify-between text-xs text-muted-foreground tabular-nums">
        <span>{fmt.format(0)}</span>
        <span>{fmt.format(threshold)}</span>
      </div>
    </div>
  );
}

More in E-commerce

View all →