Replace entire repo content with correct code from /root/shahikitchen-website/
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
'use client';
|
||||
|
||||
import type { MenuItem } from '@/domain/menu/entities';
|
||||
import WishlistButton from '@/components/WishlistButton';
|
||||
import {
|
||||
applyNextImageFallback,
|
||||
getMenuPosterCandidates,
|
||||
getMenuPosterSrc,
|
||||
} from '@/lib/assets';
|
||||
|
||||
export type SignatureDish = MenuItem & {
|
||||
desc: string;
|
||||
};
|
||||
|
||||
interface SignatureMenuMarqueeProps {
|
||||
dishes: SignatureDish[];
|
||||
addToCartLabel: string;
|
||||
perHalfKgLabel: string;
|
||||
perKgLabel: string;
|
||||
onAdd: (dish: SignatureDish) => void;
|
||||
}
|
||||
|
||||
function renderMarqueePrice(
|
||||
item: MenuItem,
|
||||
perHalfKgLabel: string,
|
||||
perKgLabel: string
|
||||
) {
|
||||
if (item.pricing === 'weight') {
|
||||
return (
|
||||
<div className="shrink-0 text-right">
|
||||
<div className="text-[#B38B4D] text-lg font-medium tracking-[-0.5px] tabular-nums leading-tight">
|
||||
{item.pricePerHalfKg ?? item.price}
|
||||
</div>
|
||||
<div className="text-[10px] text-[#8A8478] tracking-wide">{perHalfKgLabel}</div>
|
||||
<div className="text-[#B38B4D] text-sm font-medium tabular-nums mt-0.5">
|
||||
{item.pricePerKg} {perKgLabel}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="shrink-0 text-right">
|
||||
<div className="text-[#B38B4D] text-xl font-medium tracking-[-0.5px] tabular-nums">
|
||||
{item.price}
|
||||
</div>
|
||||
<div className="text-[10px] text-[#8A8478] tracking-widest -mt-1">KR</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const CARD_WIDTH = 240;
|
||||
const CARD_GAP = 20;
|
||||
|
||||
export default function SignatureMenuMarquee({
|
||||
dishes,
|
||||
addToCartLabel,
|
||||
perHalfKgLabel,
|
||||
perKgLabel,
|
||||
onAdd,
|
||||
}: SignatureMenuMarqueeProps) {
|
||||
if (dishes.length === 0) return null;
|
||||
|
||||
const reelItems = [...dishes, ...dishes];
|
||||
const trackWidth = reelItems.length * (CARD_WIDTH + CARD_GAP);
|
||||
const durationSeconds = Math.max(50, dishes.length * 2.8);
|
||||
|
||||
return (
|
||||
<div className="signature-menu-reel relative -mx-6 overflow-hidden lg:-mx-8">
|
||||
<div
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute inset-y-0 left-0 z-10 w-16 bg-gradient-to-r from-[#fffdf8] to-transparent sm:w-24"
|
||||
/>
|
||||
<div
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute inset-y-0 right-0 z-10 w-16 bg-gradient-to-l from-[#fffdf8] to-transparent sm:w-24"
|
||||
/>
|
||||
|
||||
<div
|
||||
className="signature-menu-reel-track flex w-max gap-5 py-2 pl-6 lg:pl-8"
|
||||
style={{
|
||||
width: `${trackWidth}px`,
|
||||
animationDuration: `${durationSeconds}s`,
|
||||
}}
|
||||
>
|
||||
{reelItems.map((dish, index) => (
|
||||
<article
|
||||
key={`${dish.id}-${index}`}
|
||||
className="signature-card group flex w-[240px] shrink-0 flex-col overflow-hidden rounded-[1.75rem] border border-[#EDE6D9] bg-white shadow-sm transition-shadow hover:border-[#c99a2e]/45 hover:shadow-md"
|
||||
>
|
||||
<div className="relative h-40 overflow-hidden bg-[#F2EDE4]">
|
||||
<img
|
||||
src={getMenuPosterSrc(dish)}
|
||||
alt={dish.name}
|
||||
className="signature-image h-full w-full object-cover transition-transform duration-500 group-hover:scale-105"
|
||||
loading="lazy"
|
||||
onError={(e) => {
|
||||
applyNextImageFallback(
|
||||
e.target as HTMLImageElement,
|
||||
getMenuPosterCandidates(dish),
|
||||
(e.target as HTMLImageElement).src
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-black/5 via-transparent to-black/20" />
|
||||
<div className="absolute top-3 left-3 z-10">
|
||||
<WishlistButton
|
||||
item={{ id: dish.id, name: dish.name, price: dish.price, image: dish.image }}
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-1 flex-col p-4">
|
||||
<div className="mb-2 flex items-start justify-between gap-3">
|
||||
<h4 className="min-w-0 line-clamp-2 text-[17px] leading-tight tracking-[-0.3px] text-[#101724] group-hover:text-[#B38B4D]">
|
||||
{dish.name}
|
||||
</h4>
|
||||
{renderMarqueePrice(dish, perHalfKgLabel, perKgLabel)}
|
||||
</div>
|
||||
<p className="mb-3 line-clamp-2 flex-1 text-[12px] leading-snug text-[#6B665F]">
|
||||
{dish.desc}
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onAdd(dish)}
|
||||
className="w-full rounded-full border border-[#B38B4D] py-2.5 text-xs font-medium tracking-wide text-[#B38B4D] transition-all hover:bg-[#B38B4D] hover:text-white active:scale-[0.98]"
|
||||
>
|
||||
{addToCartLabel}
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user