256 lines
7.1 KiB
TypeScript
256 lines
7.1 KiB
TypeScript
'use client';
|
|
|
|
import React from 'react';
|
|
import { getSectionTitle } from '@/lib/homepageDefaults';
|
|
|
|
interface FeatureItem {
|
|
id: string;
|
|
title: string;
|
|
desc: string;
|
|
icon: 'shipping' | 'return' | 'support' | 'payment' | 'wrench' | 'clock' | string;
|
|
}
|
|
|
|
interface FeaturesSectionProps {
|
|
config?: any;
|
|
}
|
|
|
|
/** TechShop reference strip — matches admin Homepage Layout defaults */
|
|
const REFERENCE_FEATURES: FeatureItem[] = [
|
|
{
|
|
id: 'shipping',
|
|
icon: 'shipping',
|
|
title: 'Free Shipping',
|
|
desc: 'On order over $100',
|
|
},
|
|
{
|
|
id: 'return',
|
|
icon: 'return',
|
|
title: 'Flexible & Easy Return',
|
|
desc: 'Return within 14 days',
|
|
},
|
|
{
|
|
id: 'support',
|
|
icon: 'support',
|
|
title: '24/7 Support Services',
|
|
desc: 'Any Time Customer Support',
|
|
},
|
|
{
|
|
id: 'payment',
|
|
icon: 'payment',
|
|
title: 'Secure payment',
|
|
desc: '100% Fast & Secure Payment',
|
|
},
|
|
];
|
|
|
|
const ICON_ALIAS: Record<string, FeatureItem['icon']> = {
|
|
Truck: 'shipping',
|
|
shipping: 'shipping',
|
|
RefreshCcw: 'return',
|
|
RefreshCw: 'return',
|
|
RotateCcw: 'return',
|
|
return: 'return',
|
|
Headphones: 'support',
|
|
Headset: 'support',
|
|
support: 'support',
|
|
CreditCard: 'payment',
|
|
ShieldCheck: 'payment',
|
|
payment: 'payment',
|
|
Wrench: 'wrench',
|
|
wrench: 'wrench',
|
|
Clock: 'clock',
|
|
clock: 'clock',
|
|
ThumbsUp: 'payment',
|
|
};
|
|
|
|
/** Custom line icons matching TechShop reference artwork */
|
|
function FeatureIcon({
|
|
type,
|
|
className = '',
|
|
}: {
|
|
type: string;
|
|
className?: string;
|
|
}) {
|
|
const kind = ICON_ALIAS[type] || type;
|
|
const common = {
|
|
fill: 'none',
|
|
stroke: 'currentColor',
|
|
strokeWidth: 1.7,
|
|
strokeLinecap: 'round' as const,
|
|
strokeLinejoin: 'round' as const,
|
|
};
|
|
|
|
if (kind === 'shipping') {
|
|
return (
|
|
<svg viewBox="0 0 40 32" className={className} aria-hidden>
|
|
{/* motion lines */}
|
|
<path d="M2 10h6M2 16h5M2 22h6" {...common} />
|
|
{/* truck body */}
|
|
<path d="M10 20V9.5a1.5 1.5 0 0 1 1.5-1.5H24v12" {...common} />
|
|
<path d="M24 12h5.2l3.8 5.2V20H24V12z" {...common} />
|
|
<circle cx="15" cy="22.5" r="2.4" {...common} />
|
|
<circle cx="29" cy="22.5" r="2.4" {...common} />
|
|
<path d="M17.4 22.5h9.2" {...common} />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
if (kind === 'return') {
|
|
return (
|
|
<svg viewBox="0 0 32 32" className={className} aria-hidden>
|
|
<path d="M25.5 12.5A9.5 9.5 0 1 0 22 23.2" {...common} />
|
|
<path d="M25.5 7.5v5h-5" {...common} />
|
|
<circle cx="16" cy="16" r="5.2" {...common} />
|
|
<text
|
|
x="16"
|
|
y="19.2"
|
|
textAnchor="middle"
|
|
fontSize="9"
|
|
fontWeight="700"
|
|
fill="currentColor"
|
|
stroke="none"
|
|
fontFamily="system-ui,sans-serif"
|
|
>
|
|
$
|
|
</text>
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
if (kind === 'support') {
|
|
return (
|
|
<svg viewBox="0 0 32 32" className={className} aria-hidden>
|
|
<path
|
|
d="M8 18v-3.5a8 8 0 0 1 16 0V18"
|
|
{...common}
|
|
/>
|
|
<path
|
|
d="M8 18.5a2.2 2.2 0 0 0-2.2 2.2V23a2.2 2.2 0 0 0 2.2 2.2h1.2V18.5H8z"
|
|
{...common}
|
|
/>
|
|
<path
|
|
d="M24 18.5h1.2a2.2 2.2 0 0 1 2.2 2.2V23a2.2 2.2 0 0 1-2.2 2.2H24V18.5z"
|
|
{...common}
|
|
/>
|
|
<path d="M12 25.2h5.5a2 2 0 0 0 2-2v-.6" {...common} />
|
|
{/* speech bubble */}
|
|
<rect x="13.2" y="12.2" width="7.2" height="5.2" rx="1.2" {...common} />
|
|
<path d="M15 14.8h.1M16.8 14.8h.1M18.6 14.8h.1" {...common} />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
if (kind === 'wrench') {
|
|
return (
|
|
<svg viewBox="0 0 32 32" className={className} aria-hidden>
|
|
<path
|
|
d="M20.5 6.5a6 6 0 0 0-7.8 7.2L6.2 20.2a2.2 2.2 0 0 0 3.1 3.1l6.5-6.5a6 6 0 0 0 7.2-7.8l-3.2 3.2-2.6-2.6 3.3-3.1z"
|
|
{...common}
|
|
/>
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
if (kind === 'clock') {
|
|
return (
|
|
<svg viewBox="0 0 32 32" className={className} aria-hidden>
|
|
<circle cx="16" cy="16" r="10" {...common} />
|
|
<path d="M16 10v6.5l4 2.5" {...common} />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
// payment — card + shield
|
|
return (
|
|
<svg viewBox="0 0 36 28" className={className} aria-hidden>
|
|
<rect x="2" y="5" width="26" height="18" rx="2.2" {...common} />
|
|
<path d="M2 11h26" {...common} />
|
|
<path d="M7 17h7" {...common} />
|
|
<path
|
|
d="M27 4.5l4.5 1.6v4.2c0 3.1-2 5.2-4.5 6.2-2.5-1-4.5-3.1-4.5-6.2V6.1L27 4.5z"
|
|
{...common}
|
|
/>
|
|
<path d="M25.2 10.2l1.5 1.5 3-3" {...common} />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
function resolveIconKey(raw: string | undefined, idx: number): string {
|
|
if (raw && ICON_ALIAS[raw]) return ICON_ALIAS[raw] as string;
|
|
if (
|
|
raw === 'shipping' ||
|
|
raw === 'return' ||
|
|
raw === 'support' ||
|
|
raw === 'payment' ||
|
|
raw === 'wrench' ||
|
|
raw === 'clock'
|
|
) {
|
|
return raw;
|
|
}
|
|
return REFERENCE_FEATURES[idx]?.icon || 'shipping';
|
|
}
|
|
|
|
/**
|
|
* Prefer CMS features from admin Homepage Layout.
|
|
* Fall back to TechShop reference badges when CMS has none.
|
|
*/
|
|
function buildTiles(config?: any): FeatureItem[] {
|
|
const fromConfig =
|
|
config?.metadata_json?.features || config?.metadata?.features;
|
|
|
|
if (!Array.isArray(fromConfig) || fromConfig.length === 0) {
|
|
return REFERENCE_FEATURES;
|
|
}
|
|
|
|
return fromConfig.slice(0, 8).map((item: any, idx: number) => {
|
|
const ref = REFERENCE_FEATURES[idx] || REFERENCE_FEATURES[0];
|
|
return {
|
|
id: String(item.id || `feature-${idx + 1}`),
|
|
title: String(item.title || ref.title),
|
|
desc: String(item.desc || item.description || item.subtitle || ref.desc),
|
|
icon: resolveIconKey(item.icon, idx),
|
|
};
|
|
});
|
|
}
|
|
|
|
export const FeaturesSection: React.FC<FeaturesSectionProps> = ({ config }) => {
|
|
const tiles = buildTiles(config);
|
|
const title = getSectionTitle(config, 'trust_badges', '');
|
|
|
|
return (
|
|
<section className="py-8 md:py-10 bg-white font-sans">
|
|
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-[1400px]">
|
|
{title ? (
|
|
<div className="text-left mb-6 pb-3.5 border-b border-[#e5e5e5]">
|
|
<h2 className="text-[18px] md:text-[20px] font-bold text-[#222] tracking-tight">
|
|
{title}
|
|
</h2>
|
|
</div>
|
|
) : null}
|
|
<div
|
|
className={`grid grid-cols-1 sm:grid-cols-2 gap-4 ${
|
|
tiles.length >= 4 ? 'xl:grid-cols-4' : 'xl:grid-cols-3'
|
|
}`}
|
|
>
|
|
{tiles.map((item) => (
|
|
<div
|
|
key={item.id}
|
|
className="flex items-center gap-[18px] bg-[#F8F8F8] rounded-[8px] px-6 py-[22px] min-h-[100px]"
|
|
>
|
|
<div className="shrink-0 w-10 h-10 flex items-center justify-center text-primary">
|
|
<FeatureIcon type={item.icon} className="w-9 h-8 text-primary" />
|
|
</div>
|
|
<div className="text-left min-w-0 flex flex-col gap-0.5">
|
|
<h4 className="text-[15px] font-bold text-[#222] leading-[1.25]">
|
|
{item.title}
|
|
</h4>
|
|
<p className="text-[13px] font-normal text-[#777] leading-[1.35]">
|
|
{item.desc}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|