SFS/components/our-offerings.tsx
2025-12-16 10:03:26 +05:30

138 lines
6.1 KiB
TypeScript

import { FlaskConical, Smartphone } from "lucide-react"
import Link from "next/link"
type Offering = {
id: number | string
title: string
description: string
info_footer: string
svg?: string | null
// Fallback icon key (not used when svg exists)
icon?: typeof FlaskConical
}
async function fetchOfferings(): Promise<Offering[]> {
try {
// For server components, we can fetch directly from Strapi since there's no CORS issue
// But for consistency, we'll use the API route
const baseUrl = process.env.NEXT_PUBLIC_STRAPI_BASE_URL || "http://160.187.167.213"
const apiUrl = `${baseUrl.replace(/\/+$/, "")}/api/offerings`
const res = await fetch(apiUrl, {
headers: { Accept: "application/json" },
next: { revalidate: 300 },
})
if (!res.ok) throw new Error(`Offerings API status ${res.status}`)
const json = await res.json()
const items = Array.isArray(json?.data) ? [...json.data].reverse() : []
return items.map((item: any, index: number) => {
// Strapi v4: attributes, but sample payload has fields at root; handle both
const attrs = item?.attributes ?? item ?? {}
// Optional fallback icon rotation if SVG missing
const fallbackIcons = [FlaskConical, Smartphone]
return {
id: item?.id ?? attrs?.id ?? index,
title: attrs?.title ?? "",
description: attrs?.description ?? "",
info_footer: attrs?.info_footer ?? "",
svg: attrs?.svg_icon ?? null,
icon: fallbackIcons[index % fallbackIcons.length],
} satisfies Offering
})
} catch (error) {
console.error("Failed to load offerings:", error)
// Fallback to empty array if API fails
return []
}
}
async function fetchOfferingHeading() {
try {
// For server components, we can fetch directly from Strapi since there's no CORS issue
const baseUrl = process.env.NEXT_PUBLIC_STRAPI_BASE_URL || "http://160.187.167.213"
const apiUrl = `${baseUrl.replace(/\/+$/, "")}/api/offering-text`
const res = await fetch(apiUrl, {
headers: { Accept: "application/json" },
next: { revalidate: 300 },
})
if (!res.ok) throw new Error(`Offering heading API status ${res.status}`)
const json = await res.json()
// Handle both Strapi shapes (with or without attributes wrapper)
const attrs = json?.data?.attributes ?? json?.data ?? {}
return attrs?.offering || "Our Offerings"
} catch (error) {
console.error("Failed to load offering heading:", error)
return "Our Offerings"
}
}
export default async function OurOfferings() {
const heading = await fetchOfferingHeading()
const offerings = await fetchOfferings()
return (
<section className="w-full bg-white py-16 md:py-24 px-4">
<div className="max-w-6xl mx-auto">
<h2 className="text-4xl md:text-5xl font-bold text-[#353535] text-center mb-16">
{heading}
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 max-w-4xl mx-auto">
{offerings.map((offering) => (
<div
key={offering.id}
className="group relative bg-[#EAEAEA] hover:bg-[#353535] transition-colors duration-300 overflow-hidden md:aspect-square flex flex-col justify-center p-6 md:p-12"
>
{/* Icon */}
<div className="mb-6 w-12 h-12 md:w-16 md:h-16 flex items-center justify-start">
{offering.svg ? (
<span
aria-hidden
dangerouslySetInnerHTML={{
__html: offering.svg
.replace(/width="[^"]*"/, '')
.replace(/height="[^"]*"/, '')
.replace('<svg', '<svg preserveAspectRatio="xMidYMid meet" style="width: 100%; height: auto;"')
}}
className="block w-10 h-10 md:w-14 md:h-14 text-[#353535] group-hover:text-[#F48120] transition-colors duration-300 [&>svg]:w-full [&>svg]:h-auto [&>svg]:fill-none [&>svg]:stroke-current [&>svg]:shrink-0 [&>svg]:[stroke-width:1]"
/>
) : (
offering.icon && (
<offering.icon
strokeWidth={1}
className="w-10 h-10 md:w-14 md:h-14 text-[#353535] group-hover:text-[#F48120] transition-colors duration-300 shrink-0"
/>
)
)}
</div>
{/* Title */}
<h3 className="text-3xl font-bold text-[#353535] group-hover:text-white mb-4 transition-colors duration-300">
{offering.title}
</h3>
{/* Description */}
<p className="text-base text-[#353535] group-hover:text-white/90 mb-8 leading-relaxed transition-colors duration-300 max-w-sm whitespace-pre-line">
{offering.description}
</p>
{/* Info footer / link text */}
<span className="inline-block text-xs font-bold uppercase tracking-wider text-[#353535] group-hover:text-[#F48120] transition-colors duration-300">
{offering.info_footer}
</span>
</div>
))}
</div>
</div>
</section>
)
}