141 lines
4.5 KiB
TypeScript
141 lines
4.5 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useEffect } from 'react';
|
|
import Link from 'next/link';
|
|
import { catalogService, BrandResponse } from '../services/api/catalogService';
|
|
import { getImageUrl } from '../lib/utils';
|
|
import { BrandTileSkeleton } from '../components/ui/Skeleton';
|
|
import { getSectionTitle } from '../lib/homepageDefaults';
|
|
|
|
interface BrandItem {
|
|
id: string;
|
|
name: string;
|
|
slug: string;
|
|
logo?: string;
|
|
fontClass?: string;
|
|
}
|
|
|
|
/** Real brands from catalog / CMS only — no fake filler brands */
|
|
const FALLBACK_FONT = 'font-bold tracking-wide text-[15px]';
|
|
|
|
interface BrandsSectionProps {
|
|
config?: any;
|
|
initialBrands?: BrandResponse[];
|
|
}
|
|
|
|
function toBrandItem(b: any, idx: number): BrandItem {
|
|
return {
|
|
id: String(b.brand_id || b.id || b.slug || idx),
|
|
name: b.name || 'Brand',
|
|
slug: b.slug || String(b.brand_id || b.id || idx),
|
|
logo: getImageUrl(b.logo_url || b.logo || b.image) || undefined,
|
|
fontClass: FALLBACK_FONT,
|
|
};
|
|
}
|
|
|
|
export const BrandsSection: React.FC<BrandsSectionProps> = ({
|
|
config,
|
|
initialBrands,
|
|
}) => {
|
|
const [brands, setBrands] = useState<BrandItem[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
const rawConfig =
|
|
config?.metadata_json?.brands || config?.metadata?.brands;
|
|
|
|
const applyList = (list: any[]) => {
|
|
if (list && list.length > 0) {
|
|
setBrands(list.slice(0, 10).map(toBrandItem));
|
|
} else {
|
|
setBrands([]);
|
|
}
|
|
setLoading(false);
|
|
};
|
|
|
|
if (rawConfig && Array.isArray(rawConfig) && rawConfig.length > 0) {
|
|
applyList(rawConfig);
|
|
return;
|
|
}
|
|
|
|
if (initialBrands && initialBrands.length > 0) {
|
|
applyList(initialBrands);
|
|
return;
|
|
}
|
|
|
|
catalogService
|
|
.getBrands()
|
|
.then((res) => applyList(res || []))
|
|
.catch(() => applyList([]));
|
|
}, [config, initialBrands]);
|
|
|
|
const title = getSectionTitle(config, 'official_brands', 'Official Tech Brands');
|
|
|
|
|
|
if (!loading && brands.length === 0) {
|
|
return (
|
|
<section className="py-8 bg-white font-sans">
|
|
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-[1400px]">
|
|
<div className="flex items-center justify-between gap-4 pb-3.5 mb-6 border-b border-[#e5e5e5]">
|
|
<h2 className="text-[18px] md:text-[20px] font-bold text-[#222] tracking-tight">
|
|
{title}
|
|
</h2>
|
|
</div>
|
|
<div className="border border-[#e5e5e5] rounded-lg p-10 text-center text-[13px] text-[#777]">
|
|
Brand partners will appear here once logos are configured.
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<section className="py-8 bg-white font-sans">
|
|
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-[1400px]">
|
|
{/* Header + full-width divider */}
|
|
<div className="pb-3.5 border-b border-[#e5e5e5] mb-5">
|
|
<h2 className="text-[18px] md:text-[20px] font-bold text-[#333] tracking-tight text-left">
|
|
{title}
|
|
</h2>
|
|
</div>
|
|
|
|
{loading ? (
|
|
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-3 md:gap-4">
|
|
{Array.from({ length: 10 }).map((_, i) => (
|
|
<BrandTileSkeleton key={i} />
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-3 md:gap-4">
|
|
{brands.map((brand) => (
|
|
<Link
|
|
key={brand.id}
|
|
href={`/shop?brand=${encodeURIComponent(brand.slug)}`}
|
|
className="group flex items-center justify-center h-[72px] md:h-[80px] px-4 rounded-lg bg-[#F3F3F3] hover:bg-[#ebebeb] transition-colors"
|
|
>
|
|
{brand.logo ? (
|
|
<img
|
|
src={brand.logo}
|
|
alt={brand.name}
|
|
className="max-h-[36px] md:max-h-[40px] max-w-[70%] object-contain opacity-70 group-hover:opacity-100 transition-opacity"
|
|
style={{
|
|
filter: 'grayscale(1) contrast(0.85) brightness(0.55)',
|
|
}}
|
|
/>
|
|
) : (
|
|
<span
|
|
className={`select-none text-[#555] group-hover:text-[#222] transition-colors ${
|
|
brand.fontClass || 'font-bold text-[15px]'
|
|
}`}
|
|
>
|
|
{brand.name}
|
|
</span>
|
|
)}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|