358 lines
17 KiB
TypeScript
358 lines
17 KiB
TypeScript
"use client"
|
|
|
|
import Image from "next/image"
|
|
import Link from "next/link"
|
|
import { useEffect, useState } from "react"
|
|
import { ensureHttpsImageUrl } from "@/lib/utils"
|
|
|
|
interface QuickLink {
|
|
label: string
|
|
href: string
|
|
}
|
|
|
|
interface ContactItem {
|
|
svg: string
|
|
text: string
|
|
link: string | null
|
|
}
|
|
|
|
interface SocialIcon {
|
|
name: string
|
|
svg: string
|
|
link?: string | null
|
|
}
|
|
|
|
export default function Footer() {
|
|
const [logoUrl, setLogoUrl] = useState("")
|
|
const [quickLinks, setQuickLinks] = useState<QuickLink[]>([])
|
|
const [quickLinksHeading, setQuickLinksHeading] = useState("Quick Links")
|
|
const [resourcesLinks, setResourcesLinks] = useState<QuickLink[]>([])
|
|
const [resourcesHeading, setResourcesHeading] = useState("Resources")
|
|
const [contactHeading, setContactHeading] = useState("Contact")
|
|
const [contactItems, setContactItems] = useState<ContactItem[]>([])
|
|
const [tagline, setTagline] = useState("Transforming education through innovative technology solutions.")
|
|
const [copyright, setCopyright] = useState("© 2025 School For Schools. All rights reserved.")
|
|
const [privacyPolicy, setPrivacyPolicy] = useState("")
|
|
const [socialIcons, setSocialIcons] = useState<SocialIcon[]>([])
|
|
|
|
useEffect(() => {
|
|
const loadFooterData = async () => {
|
|
try {
|
|
// Use Next.js API route instead of direct Strapi calls
|
|
const res = await fetch("/api/footer", {
|
|
headers: { Accept: "application/json" },
|
|
})
|
|
if (!res.ok) {
|
|
throw new Error(`Footer API status ${res.status}`)
|
|
}
|
|
const data = await res.json()
|
|
|
|
// Load Quick Links
|
|
if (data.quickLinks) {
|
|
const json = data.quickLinks
|
|
const items = Array.isArray(json?.data) ? json.data : []
|
|
if (items.length > 0) {
|
|
const item = items[0]
|
|
const attrs = item?.attributes || item || {}
|
|
if (attrs.heading) setQuickLinksHeading(attrs.heading)
|
|
const links: QuickLink[] = []
|
|
let index = 1
|
|
while (true) {
|
|
const labelKey = `list${index}` as keyof typeof attrs
|
|
const linkKey = `list${index}_link` as keyof typeof attrs
|
|
const label = attrs[labelKey]
|
|
const href = attrs[linkKey]
|
|
if (!label || !href) break
|
|
links.push({ label: String(label), href: String(href) })
|
|
index++
|
|
}
|
|
if (links.length > 0) setQuickLinks(links)
|
|
}
|
|
}
|
|
|
|
// Load Resources Links
|
|
if (data.resources) {
|
|
const json = data.resources
|
|
const items = Array.isArray(json?.data) ? json.data : []
|
|
if (items.length > 0) {
|
|
const item = items[0]
|
|
const attrs = item?.attributes || item || {}
|
|
if (attrs.heading) setResourcesHeading(attrs.heading)
|
|
const links: QuickLink[] = []
|
|
let index = 1
|
|
while (true) {
|
|
const labelKey = `resource${index}` as keyof typeof attrs
|
|
const linkKey = `resource${index}_link` as keyof typeof attrs
|
|
const label = attrs[labelKey]
|
|
const href = attrs[linkKey]
|
|
if (!label || !href) break
|
|
links.push({ label: String(label), href: String(href) })
|
|
index++
|
|
}
|
|
if (links.length > 0) setResourcesLinks(links)
|
|
}
|
|
}
|
|
|
|
// Load Contact Links
|
|
if (data.contact) {
|
|
const json = data.contact
|
|
const items = Array.isArray(json?.data) ? json.data : []
|
|
if (items.length > 0) {
|
|
const item = items[0]
|
|
const attrs = item?.attributes || item || {}
|
|
if (attrs.heading) setContactHeading(attrs.heading)
|
|
const contacts: ContactItem[] = []
|
|
if (attrs.email && attrs.email_svg) {
|
|
contacts.push({
|
|
svg: attrs.email_svg,
|
|
text: attrs.email,
|
|
link: attrs.email_link || `mailto:${attrs.email}`,
|
|
})
|
|
}
|
|
if (attrs.phone && attrs.phone_svg) {
|
|
contacts.push({
|
|
svg: attrs.phone_svg,
|
|
text: attrs.phone,
|
|
link: attrs.phone_link || `tel:${attrs.phone.replace(/\D/g, '')}`,
|
|
})
|
|
}
|
|
if (attrs.address && attrs.address_svg) {
|
|
contacts.push({
|
|
svg: attrs.address_svg,
|
|
text: attrs.address,
|
|
link: attrs.address_link || null,
|
|
})
|
|
}
|
|
if (contacts.length > 0) setContactItems(contacts)
|
|
}
|
|
}
|
|
|
|
// Load Tagline
|
|
if (data.logoTag) {
|
|
const json = data.logoTag
|
|
const item = json?.data
|
|
if (item) {
|
|
const attrs = item?.attributes || item || {}
|
|
const taglineText = attrs?.tag_line || attrs?.tagline || ""
|
|
if (taglineText) setTagline(taglineText)
|
|
}
|
|
}
|
|
|
|
// Load Privacy Policy
|
|
if (data.privacy) {
|
|
const json = data.privacy
|
|
const item = json?.data
|
|
if (item) {
|
|
const attrs = item?.attributes || item || {}
|
|
const privacyText = attrs?.privacy_policy || ""
|
|
if (privacyText) setPrivacyPolicy(privacyText)
|
|
}
|
|
}
|
|
|
|
// Load Social Icons
|
|
if (data.socialIcons) {
|
|
const json = data.socialIcons
|
|
const items = Array.isArray(json?.data) ? json.data : []
|
|
const icons: SocialIcon[] = items.map((item: any) => {
|
|
const attrs = item?.attributes || item || {}
|
|
return {
|
|
name: attrs?.company_name || "",
|
|
svg: attrs?.company_svg || "",
|
|
link: attrs?.company_link || null,
|
|
}
|
|
}).filter((icon: SocialIcon) => icon.name && icon.svg)
|
|
if (icons.length > 0) setSocialIcons(icons)
|
|
}
|
|
|
|
// Load Logo
|
|
if (data.logo) {
|
|
const json = data.logo
|
|
const logoData = json?.data?.logo || json?.data?.attributes?.logo?.data || json?.data?.attributes?.logo
|
|
if (logoData) {
|
|
const logo = Array.isArray(logoData) ? logoData[0] : logoData
|
|
const logoObj = logo?.attributes || logo
|
|
const url =
|
|
logoObj?.url ||
|
|
logoObj?.formats?.small?.url ||
|
|
logoObj?.formats?.thumbnail?.url ||
|
|
logoObj?.formats?.medium?.url ||
|
|
logoObj?.formats?.large?.url ||
|
|
""
|
|
if (url) {
|
|
// Ensure HTTPS to prevent Mixed Content errors
|
|
const absolute = ensureHttpsImageUrl(url)
|
|
setLogoUrl(absolute)
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error("Failed to load footer data:", err)
|
|
}
|
|
}
|
|
loadFooterData()
|
|
}, [])
|
|
|
|
return (
|
|
<footer className="w-full bg-[#525252] text-white py-6 md:py-8 lg:py-12 px-4 md:px-8">
|
|
<div className="max-w-[1400px] mx-auto grid grid-cols-1 md:grid-cols-4 gap-4 md:gap-6 lg:gap-8">
|
|
|
|
{/* Column 1: Logo & Description */}
|
|
<div className="flex flex-col items-start gap-2 md:gap-3 lg:gap-4">
|
|
{logoUrl && (
|
|
<div className="relative w-auto" style={{ minHeight: "32px" }}>
|
|
<Image
|
|
src={logoUrl}
|
|
alt="School For Schools"
|
|
width={200}
|
|
height={48}
|
|
className="h-8 md:h-10 lg:h-12 object-contain"
|
|
style={{ width: "auto", maxWidth: "200px" }}
|
|
/>
|
|
</div>
|
|
)}
|
|
<p className="text-gray-300 text-xs leading-relaxed max-w-xs">
|
|
{tagline}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Column 2: Quick Links */}
|
|
{quickLinks.length > 0 && (
|
|
<div className="flex flex-col gap-2 md:gap-3 lg:gap-4">
|
|
<h3 className="text-sm font-semibold text-white uppercase tracking-wider">
|
|
{quickLinksHeading}
|
|
</h3>
|
|
<ul className="flex flex-col gap-1 md:gap-1.5 lg:gap-2">
|
|
{quickLinks.map((link, index) => (
|
|
<li key={`${link.label}-${index}`}>
|
|
<Link
|
|
href={link.href}
|
|
className="text-gray-300 text-xs md:text-sm hover:text-[#F48120] transition-colors"
|
|
>
|
|
{link.label}
|
|
</Link>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
)}
|
|
|
|
{/* Column 3: Resources */}
|
|
{resourcesLinks.length > 0 && (
|
|
<div className="flex flex-col gap-2 md:gap-3 lg:gap-4">
|
|
<h3 className="text-sm font-semibold text-white uppercase tracking-wider">
|
|
{resourcesHeading}
|
|
</h3>
|
|
<ul className="flex flex-col gap-1 md:gap-1.5 lg:gap-2">
|
|
{resourcesLinks.map((link, index) => (
|
|
<li key={`${link.label}-${index}`}>
|
|
<Link
|
|
href={link.href}
|
|
className="text-gray-300 text-xs md:text-sm hover:text-[#F48120] transition-colors"
|
|
>
|
|
{link.label}
|
|
</Link>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
)}
|
|
|
|
{/* Column 4: Contact */}
|
|
{contactItems.length > 0 && (
|
|
<div className="flex flex-col gap-2 md:gap-3 lg:gap-4">
|
|
<h3 className="text-sm font-semibold text-white uppercase tracking-wider">
|
|
{contactHeading}
|
|
</h3>
|
|
<ul className="flex flex-col gap-2 md:gap-2.5 lg:gap-3">
|
|
{contactItems.map((contact, index) => (
|
|
<li key={`contact-${index}`} className="flex items-start gap-3">
|
|
<span
|
|
dangerouslySetInnerHTML={{ __html: contact.svg }}
|
|
className="shrink-0"
|
|
/>
|
|
{contact.link ? (
|
|
<a
|
|
href={contact.link}
|
|
className="text-gray-300 text-xs md:text-sm hover:text-[#F48120] transition-colors"
|
|
>
|
|
{contact.text}
|
|
</a>
|
|
) : (
|
|
<span className="text-gray-300 text-xs md:text-sm max-w-[200px]">
|
|
{contact.text}
|
|
</span>
|
|
)}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Footer Bottom Line */}
|
|
<div className="max-w-[1400px] mx-auto mt-6 md:mt-8 lg:mt-12 pt-4 md:pt-5 lg:pt-6 border-t border-gray-300">
|
|
<div className="flex flex-col md:flex-row items-center justify-between gap-4">
|
|
{/* Left: Copyright */}
|
|
<div className="flex-1 text-left">
|
|
<p className="text-gray-400 text-xs">
|
|
{copyright}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Center: Social Media Icons */}
|
|
{socialIcons.length > 0 && (
|
|
<div className="flex items-center justify-center gap-4 flex-1">
|
|
{socialIcons.map((icon, index) => (
|
|
<a
|
|
key={`social-${index}`}
|
|
href={icon.link || "#"}
|
|
className="flex items-center justify-center hover:opacity-80 transition-opacity"
|
|
aria-label={icon.name}
|
|
>
|
|
<span
|
|
dangerouslySetInnerHTML={{ __html: icon.svg }}
|
|
className="w-6 h-6"
|
|
/>
|
|
</a>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* Right: Privacy Policy Links */}
|
|
{privacyPolicy && (
|
|
<div className="flex-1 text-right">
|
|
<div className="text-gray-400 text-xs flex flex-wrap gap-x-1 justify-end">
|
|
{privacyPolicy.match(/([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)/g)?.map((policy, index, array) => {
|
|
const trimmed = policy.trim()
|
|
if (!trimmed) return null
|
|
return (
|
|
<span key={index}>
|
|
<Link
|
|
href="#"
|
|
className="hover:text-[#F48120] transition-colors"
|
|
>
|
|
{trimmed}
|
|
</Link>
|
|
{index < array.length - 1 && " "}
|
|
</span>
|
|
)
|
|
}) || privacyPolicy.split(" ").map((word, index, array) => (
|
|
<span key={index}>
|
|
<Link
|
|
href="#"
|
|
className="hover:text-[#F48120] transition-colors"
|
|
>
|
|
{word}
|
|
</Link>
|
|
{index < array.length - 1 && " "}
|
|
</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
)
|
|
}
|