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

175 lines
6.9 KiB
TypeScript

"use client"
import Image from "next/image"
import { useEffect, useState } from "react"
import { motion } from "framer-motion"
import { ensureHttpsImageUrl } from "@/lib/utils"
interface Logo {
id: string
src: string
alt: string
}
export default function TrustedBy() {
const [heading, setHeading] = useState("Trusted by leading schools worldwide")
const [subtext, setSubtext] = useState(
"Join the growing community of schools that trust our platform to transform education through technology."
)
const [logos, setLogos] = useState<Logo[]>([])
useEffect(() => {
const loadTexts = async () => {
try {
// Use Next.js API route instead of direct Strapi calls
const res = await fetch("/api/trusted-by", {
headers: { Accept: "application/json" },
})
if (!res.ok) {
throw new Error(`Trusted-by API status ${res.status}`)
}
const data = await res.json()
// Extract heading
if (data.heading) {
const headingJson = data.heading
const text =
headingJson?.data?.attributes?.text ??
headingJson?.data?.text ??
headingJson?.data?.TEXT ??
headingJson?.data?.attributes?.TEXT
if (text) setHeading(text)
}
// Extract subtext
if (data.subtext) {
const subtextJson = data.subtext
const text =
subtextJson?.data?.attributes?.text ??
subtextJson?.data?.text ??
subtextJson?.data?.TEXT ??
subtextJson?.data?.attributes?.TEXT
if (text) setSubtext(text)
}
} catch (err) {
console.error("Failed to load trusted-by texts:", err)
}
}
loadTexts()
}, [])
useEffect(() => {
const loadLogos = async () => {
try {
// Use Next.js API route instead of direct Strapi call
const res = await fetch("/api/trusted-by", {
headers: { Accept: "application/json" },
})
if (!res.ok) throw new Error(`Trusted-by API status ${res.status}`)
const data = await res.json()
if (!data.logos) return
const json = data.logos
const entries = Array.isArray(json?.data) ? json.data : []
// Collect all media URLs from trusted_schools relation
const collected: Logo[] = []
entries.forEach((item: any, idx: number) => {
const attrs = item?.attributes ?? item ?? {}
const media =
attrs?.trusted_schools?.data ||
attrs?.trusted_schools || // handle array directly (as seen in response)
attrs?.trusted_school?.data ||
attrs?.trustedSchools?.data
const mediaArray = Array.isArray(media) ? media : Array.isArray(media?.data) ? media.data : []
mediaArray.forEach((m: any, mIdx: number) => {
const a = m?.attributes ?? m ?? {}
const url =
a?.url ||
a?.formats?.medium?.url ||
a?.formats?.small?.url ||
a?.formats?.thumbnail?.url ||
""
if (!url) return
// Ensure HTTPS to prevent Mixed Content errors
const absolute = ensureHttpsImageUrl(url)
collected.push({
id: `${item?.id ?? idx}-${m?.id ?? mIdx}`,
src: absolute,
alt: a?.alternativeText || "Trusted school",
})
})
})
if (collected.length > 0) {
setLogos(collected)
}
// Don't set fallback - user wants only Strapi images
} catch (err) {
console.error("Failed to load trusted schools logos:", err)
// Don't set fallback - user wants only Strapi images
}
}
loadLogos()
}, [])
const renderedLogos = logos
return (
<section className="w-full bg-white py-16">
<div className="max-w-[1400px] mx-auto px-4 mb-12 text-center">
<h2 className="text-3xl md:text-4xl font-bold text-[#353535] mb-4">
{heading}
</h2>
<p className="text-gray-600 text-sm md:text-base max-w-2xl mx-auto">
{subtext}
</p>
</div>
{renderedLogos.length > 0 && (
<div className="relative max-w-6xl mx-auto px-4">
<div className="relative rounded-3xl border border-gray-300 px-4 md:px-8 py-6 md:py-8 shadow-sm bg-white overflow-hidden">
<motion.div
className="flex items-center gap-8 md:gap-10"
animate={{ x: ["0%", "-50%"] }}
transition={{
x: {
repeat: Infinity,
repeatType: "loop",
duration: 18,
ease: "linear",
},
}}
style={{ width: "max-content" }}
>
{renderedLogos.concat(renderedLogos).map((item, idx) => (
<div
key={`${item.id}-${idx}`}
className="min-w-[220px] md:min-w-[240px] flex items-center justify-center"
style={{ minHeight: "120px" }}
>
<Image
src={item.src}
alt={item.alt}
width={180}
height={140}
className="object-contain h-[120px] md:h-[140px]"
style={{ width: "auto", maxWidth: "180px" }}
/>
</div>
))}
</motion.div>
</div>
</div>
)}
</section>
)
}