profit-planet-frontend/src/app/coffee-abonnements/components/SelectionSummaryCard.tsx
DeathKaioken 4074ea4eee Bibelbumser
Co-authored-by: Copilot <copilot@github.com>
2026-05-04 23:48:09 +02:00

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>
);
}