ifixkart-storefront/components/CmsMegaMenuPanel.tsx

211 lines
6.3 KiB
TypeScript

'use client';
import React from 'react';
import Link from 'next/link';
import { getImageUrl } from '@/lib/utils';
import { requireLoginForCheckout } from '@/lib/requireLoginForCheckout';
import type { MegaMenuBadge, MegaMenuData } from '@/services/api/storefrontService';
type BadgeKind = MegaMenuBadge;
type MegaLink = {
label: string;
href: string;
badge?: BadgeKind;
accent?: boolean;
};
type MegaGroup = {
title: string;
links: MegaLink[];
};
const BADGE_STYLES: Record<BadgeKind, string> = {
new: 'bg-[#e8f5e9] text-[#43a047]',
hot: 'bg-[#ffebee] text-[#e57373]',
trending: 'bg-[#e3f2fd] text-[#1976d2]',
featured: 'bg-[#e0f7fa] text-[#00acc1]',
};
const BADGE_LABEL: Record<BadgeKind, string> = {
new: 'NEW',
hot: 'HOT',
trending: 'TRENDING',
featured: 'FEATURED',
};
function parseBadge(value: unknown): BadgeKind | undefined {
const v = String(value || '')
.toLowerCase()
.trim();
if (v === 'hot' || v === 'new' || v === 'trending' || v === 'featured') {
return v;
}
return undefined;
}
export function hasCmsMegaGroups(menu?: MegaMenuData | null): boolean {
return Boolean(
menu?.groups?.some(
(group) =>
Array.isArray(group?.links) &&
group.links.some((link) => link?.label && link?.href)
)
);
}
export function normalizeCmsMegaMenu(menu: MegaMenuData): {
groups: MegaGroup[];
promo: NonNullable<MegaMenuData['promo']> | null;
} {
const groups: MegaGroup[] = (menu.groups || [])
.map((group) => ({
title: String(group.title || '').trim(),
links: (group.links || [])
.filter((link) => link?.label && link?.href)
.map((link) => {
const href = String(link.href).trim();
const label = String(link.label).trim();
return {
label,
href,
badge: parseBadge(link.badge),
accent: Boolean(link.accent),
};
}),
}))
.filter((group) => group.links.length > 0);
const promo = menu.promo;
const hasPromo = Boolean(
promo && (promo.image_url || promo.title || promo.badge || promo.href)
);
return { groups, promo: (hasPromo && promo) ? promo : null };
}
function MegaBadge({ kind }: { kind: BadgeKind }) {
return (
<span
className={`inline-flex items-center px-1.5 py-0.5 rounded text-[9px] font-bold uppercase tracking-wide leading-none ${BADGE_STYLES[kind]}`}
>
{BADGE_LABEL[kind]}
</span>
);
}
function MegaColumn({ title, links }: MegaGroup) {
return (
<div className="min-w-0">
{title ? (
<h4 className="text-[15px] font-bold text-[#2d2d2d] mb-4">{title}</h4>
) : null}
<ul className="space-y-2.5">
{links.map((item) => (
<li key={`${title}-${item.label}-${item.href}`}>
{item.href === '/checkout' ? (
<button
type="button"
onClick={() => {
if (!requireLoginForCheckout('/checkout')) return;
window.location.assign('/checkout');
}}
className={`inline-flex items-center gap-2 text-[13px] leading-snug transition-colors hover:text-primary cursor-pointer bg-transparent border-0 p-0 ${
item.accent
? 'text-primary font-medium'
: 'text-[#555] font-normal'
}`}
>
<span>{item.label}</span>
{item.badge ? <MegaBadge kind={item.badge} /> : null}
</button>
) : (
<Link
href={item.href}
className={`inline-flex items-center gap-2 text-[13px] leading-snug transition-colors hover:text-primary ${
item.accent
? 'text-primary font-medium'
: 'text-[#555] font-normal'
}`}
>
<span>{item.label}</span>
{item.badge ? <MegaBadge kind={item.badge} /> : null}
</Link>
)}
</li>
))}
</ul>
</div>
);
}
function PromoCard({
promo,
}: {
promo: NonNullable<MegaMenuData['promo']>;
}) {
const href = promo.href || '/shop';
const image = getImageUrl(promo.image_url);
return (
<Link
href={href}
className="relative block min-h-[220px] lg:min-h-[260px] rounded-xl overflow-hidden group"
>
<div
className="absolute inset-0 bg-cover bg-center transition-transform duration-500 group-hover:scale-105 bg-[#ecf0f5]"
style={
image
? {
backgroundImage: `linear-gradient(105deg, rgba(236,240,245,0.92) 0%, rgba(236,240,245,0.55) 42%, rgba(236,240,245,0.15) 100%), url(${image})`,
}
: undefined
}
/>
<div className="relative z-10 p-5 md:p-6 flex flex-col justify-center h-full min-h-[220px] lg:min-h-[260px]">
{promo.badge ? (
<span className="inline-flex self-start bg-white text-[#222] text-[11px] font-bold tracking-wide px-2.5 py-1 rounded-sm shadow-sm mb-2">
{promo.badge}
</span>
) : null}
{promo.title ? (
<p className="text-[22px] md:text-[24px] font-extrabold text-[#1a1a1a] leading-tight max-w-[140px]">
{promo.title}
</p>
) : null}
</div>
</Link>
);
}
export function CmsMegaMenuPanel({ menu }: { menu: MegaMenuData }) {
const { groups, promo } = normalizeCmsMegaMenu(menu);
if (groups.length === 0 && !promo) return null;
const colCount = groups.length + (promo ? 1 : 0);
const gridClass =
colCount >= 4
? 'lg:grid-cols-[repeat(3,minmax(0,1fr))_minmax(200px,240px)]'
: colCount === 3
? 'lg:grid-cols-3'
: colCount === 2
? 'lg:grid-cols-2'
: 'lg:grid-cols-1';
return (
<div className="w-full max-w-full bg-white border border-t-0 border-gray-200 shadow-[0_16px_40px_rgba(0,0,0,0.12)] rounded-b-md overflow-hidden">
<div
className={`grid grid-cols-1 sm:grid-cols-2 ${gridClass} gap-6 lg:gap-8 px-5 py-6 md:px-7 md:py-7`}
>
{groups.map((group) => (
<MegaColumn
key={group.title || group.links[0]?.href}
title={group.title}
links={group.links}
/>
))}
{promo ? <PromoCard promo={promo} /> : null}
</div>
</div>
);
}