110 lines
4.1 KiB
TypeScript
110 lines
4.1 KiB
TypeScript
import type { CoffeeItem } from '../hooks/getActiveCoffees';
|
|
|
|
type SelectedEntry = {
|
|
coffee: CoffeeItem;
|
|
quantity: number;
|
|
};
|
|
|
|
type Props = {
|
|
selectedEntries: SelectedEntry[];
|
|
shippingLoading: boolean;
|
|
isFreeShippingSelected: boolean;
|
|
selectedShippingFee: number;
|
|
totalNetWithShipping: number;
|
|
totalCapsules: number;
|
|
packsSelected: number;
|
|
selectedPlanCapsules: number;
|
|
requiredPacks: number;
|
|
canProceed: boolean;
|
|
onProceed: () => void;
|
|
title: string;
|
|
emptyText: string;
|
|
continueText: string;
|
|
};
|
|
|
|
export default function SelectionSummaryCard({
|
|
selectedEntries,
|
|
shippingLoading,
|
|
isFreeShippingSelected,
|
|
selectedShippingFee,
|
|
totalNetWithShipping,
|
|
totalCapsules,
|
|
packsSelected,
|
|
selectedPlanCapsules,
|
|
requiredPacks,
|
|
canProceed,
|
|
onProceed,
|
|
title,
|
|
emptyText,
|
|
continueText,
|
|
}: Props) {
|
|
return (
|
|
<section className="rounded-[28px] border border-white/80 bg-white/90 p-6 shadow-[0_24px_70px_-40px_rgba(15,23,42,0.3)] backdrop-blur space-y-4">
|
|
<h2 className="text-lg font-semibold text-slate-900">{title}</h2>
|
|
|
|
{selectedEntries.length === 0 && <p className="text-sm text-slate-600">{emptyText}</p>}
|
|
|
|
{selectedEntries.map((entry) => (
|
|
<div key={entry.coffee.id} className="flex justify-between text-sm border-b border-slate-100 last:border-b-0 pb-2 last:pb-0">
|
|
<div className="flex flex-col">
|
|
<span className="font-medium text-slate-800">{entry.coffee.name}</span>
|
|
<span className="text-xs text-slate-500">
|
|
{entry.quantity} pcs • <span className="inline-flex items-center font-semibold text-slate-900">EUR {entry.coffee.pricePer10}/10</span>
|
|
</span>
|
|
</div>
|
|
<div className="text-right font-semibold text-slate-800">EUR {((entry.quantity / 10) * entry.coffee.pricePer10).toFixed(2)}</div>
|
|
</div>
|
|
))}
|
|
|
|
<div className="flex justify-between text-sm border-b border-slate-100 pb-2">
|
|
<span className="text-sm font-medium text-slate-700">Shipping</span>
|
|
<span className="text-sm font-semibold text-slate-900">
|
|
{shippingLoading ? 'Loading...' : isFreeShippingSelected ? 'FREE SHIPPING' : `EUR ${selectedShippingFee.toFixed(2)}`}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="flex justify-between pt-2 border-t border-slate-200">
|
|
<span className="text-sm font-semibold text-slate-700">Total (net)</span>
|
|
<span className="text-lg font-extrabold tracking-tight text-slate-900">EUR {totalNetWithShipping.toFixed(2)}</span>
|
|
</div>
|
|
|
|
<div className="text-xs text-slate-700">
|
|
Selected: {totalCapsules} capsules ({packsSelected} packs of 10). Target: {selectedPlanCapsules} capsules ({requiredPacks} packs).
|
|
{packsSelected !== requiredPacks && (
|
|
<span className="ml-2 inline-flex items-center rounded-md bg-rose-50 text-rose-700 px-2 py-1 border border-rose-200">
|
|
{packsSelected < requiredPacks ? `${requiredPacks - packsSelected} packs missing.` : `${packsSelected - requiredPacks} packs too many.`}
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
<button
|
|
onClick={onProceed}
|
|
disabled={!canProceed}
|
|
className={`group w-full mt-2 rounded-xl px-4 py-3 font-semibold transition inline-flex items-center justify-center ${
|
|
canProceed ? 'bg-slate-900 text-white hover:bg-slate-800 shadow-md hover:shadow-lg' : 'bg-slate-200 text-slate-500 cursor-not-allowed'
|
|
}`}
|
|
>
|
|
{continueText}
|
|
<svg
|
|
className={`ml-2 h-5 w-5 transition-transform ${canProceed ? 'group-hover:translate-x-0.5' : ''}`}
|
|
viewBox="0 0 20 20"
|
|
fill="currentColor"
|
|
aria-hidden="true"
|
|
>
|
|
<path
|
|
fillRule="evenodd"
|
|
d="M10.293 3.293a1 1 0 011.414 0l5.999 6a1 1 0 010 1.414l-6 6a1 1 0 11-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z"
|
|
clipRule="evenodd"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
|
|
{!canProceed && (
|
|
<p className="text-xs text-slate-600">
|
|
You can continue once exactly {selectedPlanCapsules} capsules ({requiredPacks} packs) are selected.
|
|
</p>
|
|
)}
|
|
</section>
|
|
);
|
|
}
|