qudoco-fe/components/landing-page/product.tsx

173 lines
6.4 KiB
TypeScript
Raw Normal View History

2026-02-17 09:05:22 +00:00
import Image from "next/image";
import { Check } from "lucide-react";
2026-04-10 07:21:29 +00:00
import type { CmsProductContent } from "@/types/cms-landing";
2026-02-17 09:05:22 +00:00
2026-04-10 07:21:29 +00:00
const DEFAULT_TITLE =
"The product we offer is designed to meet your business needs.";
const DEFAULT_BODY =
"Social media marketing services are provided by companies or individuals who specialize in marketing strategies through social media platforms.";
2026-02-17 09:05:22 +00:00
2026-04-10 07:21:29 +00:00
function cardImageUrl(p: CmsProductContent) {
return p.images?.[0]?.image_url?.trim() || "/image/p1.png";
}
2026-02-17 09:05:22 +00:00
2026-04-10 07:21:29 +00:00
export default function ProductSection({
products,
}: {
products?: CmsProductContent[] | null;
}) {
const list = products?.filter((p) => p.id) ?? [];
if (list.length === 0) {
return (
<section className="bg-white py-32">
<div className="container mx-auto px-6">
<div className="mb-20 text-center">
<p className="mb-4 text-sm font-semibold uppercase tracking-widest text-gray-400">
Our Product
2026-02-17 09:05:22 +00:00
</p>
2026-04-10 07:21:29 +00:00
<h2 className="mx-auto max-w-3xl text-4xl font-extrabold leading-tight md:text-5xl whitespace-pre-line">
{DEFAULT_TITLE}
</h2>
2026-02-17 09:05:22 +00:00
</div>
2026-04-10 07:21:29 +00:00
<div className="grid grid-cols-1 items-center gap-16 md:grid-cols-2">
<div className="flex justify-center">
2026-02-17 09:05:22 +00:00
<Image
2026-04-10 07:21:29 +00:00
src="/image/p1.png"
alt="Product illustration"
width={520}
height={420}
className="object-contain"
2026-02-17 09:05:22 +00:00
/>
</div>
2026-04-10 07:21:29 +00:00
<div className="max-w-xl">
<div className="mb-6 flex h-12 w-12 items-center justify-center rounded-xl bg-[#fdecc8]">
<Image
src="/image/product-icon.png"
alt=""
width={22}
height={22}
/>
</div>
<h3 className="mb-4 text-2xl font-bold text-gray-900">
MediaHUB Content Aggregator
</h3>
<p className="mb-8 text-sm leading-relaxed text-gray-600 whitespace-pre-line">
{DEFAULT_BODY}
</p>
<button
type="button"
className="text-sm font-semibold text-[#966314] hover:underline"
>
Learn More
</button>
</div>
2026-02-17 09:05:22 +00:00
</div>
</div>
2026-04-10 07:21:29 +00:00
</section>
);
}
2026-02-17 09:05:22 +00:00
2026-04-10 07:21:29 +00:00
const first = list[0];
const sectionTitle =
first?.primary_title?.trim() || "Products tailored to your business";
const subtitle = first?.secondary_title?.trim();
2026-02-17 09:05:22 +00:00
2026-04-10 07:21:29 +00:00
return (
<section className="bg-white py-32">
<div className="container mx-auto px-6">
<div className="mb-16 text-center">
<p className="mb-4 text-sm font-semibold uppercase tracking-widest text-gray-400">
Our Product
</p>
<h2 className="mx-auto max-w-3xl text-4xl font-extrabold leading-tight md:text-5xl whitespace-pre-line">
{list.length > 1 ? "Our Products" : sectionTitle}
</h2>
{subtitle && list.length > 1 ? (
<p className="mx-auto mt-4 max-w-2xl text-lg text-gray-600">{subtitle}</p>
) : null}
</div>
2026-02-17 09:05:22 +00:00
2026-04-10 07:21:29 +00:00
<div className="grid gap-10 md:grid-cols-2 lg:grid-cols-3">
{list.map((p) => {
const imgSrc = cardImageUrl(p);
const external = /^https?:\/\//i.test(imgSrc);
const rawDesc = p.description?.trim();
const lines = rawDesc
? rawDesc.split(/\r?\n/).map((l) => l.trim()).filter(Boolean)
: [];
const useBullets = lines.length > 1;
const bodyText = lines.length === 0 ? DEFAULT_BODY : rawDesc ?? "";
return (
<div
key={p.id}
className="flex flex-col overflow-hidden rounded-2xl border border-gray-100 bg-white shadow-sm"
>
<div className="relative flex aspect-[4/3] items-center justify-center bg-gray-50">
{external ? (
// eslint-disable-next-line @next/next/no-img-element
<img
src={imgSrc}
alt=""
className="h-full w-full object-cover"
/>
) : (
<Image
src={imgSrc}
alt=""
width={480}
height={360}
className="object-contain"
/>
)}
</div>
<div className="flex flex-1 flex-col p-6">
<div className="mb-4 flex h-12 w-12 items-center justify-center rounded-xl bg-[#fdecc8]">
<Image
src="/image/product-icon.png"
alt=""
width={22}
height={22}
/>
</div>
<h3 className="mb-3 text-xl font-bold text-gray-900 whitespace-pre-line">
{p.primary_title?.trim() || "Product"}
</h3>
{p.secondary_title?.trim() ? (
<p className="mb-3 text-sm font-medium text-gray-500">
{p.secondary_title.trim()}
</p>
) : null}
{useBullets ? (
<ul className="mb-4 space-y-3">
{lines.map((item) => (
<li key={item} className="flex gap-3 text-sm text-gray-600">
<span className="mt-1 flex h-8 w-16 shrink-0 items-center justify-center rounded-full bg-[#fdecc8]">
<Check size={12} className="text-[#966314]" />
</span>
{item}
</li>
))}
</ul>
) : (
<p className="mb-4 flex-1 text-sm leading-relaxed text-gray-600 whitespace-pre-line line-clamp-6">
{bodyText}
</p>
)}
<button
type="button"
className="mt-auto text-left text-sm font-semibold text-[#966314] hover:underline"
>
Learn More
</button>
</div>
</div>
);
})}
2026-02-17 09:05:22 +00:00
</div>
</div>
</section>
);
}