"use client" import { useState, useEffect } from "react" import { motion, AnimatePresence } from "framer-motion" // --- Types & Data --- interface ShapeData { path: string viewBox: string // Optional scaling factor to visually normalize optical size if needed scale?: number // Custom styles if specific shapes need tweaks (e.g. fill) fill?: boolean strokeWidth?: number } const SHAPES: Record = { ladder: { path: "M56.48 0V77.99H19.5601V0H0V354.08H19.5601V276.57H56.48V354.08H75.79V0H56.48ZM56.48 93.64V169.1H19.5601V93.64H56.48ZM56.48 260.93H19.5601V184.98H56.48V260.93Z", viewBox: "0 0 76 355", // Ladder is very tall/thin. }, dna: { path: "M147.696 201.09C151.706 184.52 146.746 166.92 132.936 148.8C121.456 133.73 105.836 123.73 89.9058 114.76C104.846 106.03 118.956 96.04 128.366 81.41C147.586 51.51 142.246 2.1 142.006 0.0100021L127.106 1.7C127.126 1.92 128.256 12.29 127.696 25.85H21.3659C20.7959 12.29 21.9159 1.90999 21.9359 1.68999L14.4858 0.849998L7.03583 0C6.79583 2.09 1.45585 51.5 20.6758 81.4C30.0858 96.03 44.1958 106.02 59.1358 114.75C43.2058 123.72 27.5858 133.73 16.1058 148.79C2.29584 166.91 -2.66417 184.5 1.34583 201.08C6.31583 221.58 24.9558 240.34 58.1558 258.19C1.86583 290.79 1.99581 336.03 2.07581 361.09C2.07581 364.17 2.09586 367.09 1.99586 369.52L16.9858 370.14C17.1058 367.37 17.0958 364.29 17.0858 361.04C17.0758 356.93 17.0558 352.5 17.2658 347.83H131.496V342.78C132.016 349.31 132.006 355.47 131.986 361.04C131.976 364.3 131.966 367.37 132.086 370.14L147.076 369.52C146.976 367.09 146.986 364.17 146.996 361.09C147.076 336.03 147.206 290.79 90.9158 258.19C124.126 240.34 142.766 221.58 147.726 201.08L147.696 201.09ZM126.636 38.85C125.726 46.2 124.196 53.81 121.706 60.85H27.3358C24.8558 53.82 23.3258 46.2 22.4158 38.85H126.636ZM33.6658 73.85H115.366C106.126 87.85 91.0058 97.11 74.5158 106.25C58.0258 97.1 42.9058 87.84 33.6658 73.85ZM74.5158 123.38C90.1958 131.98 106.196 140.95 117.646 153.85H31.3759C42.8259 140.95 58.8359 131.98 74.5059 123.38H74.5158ZM21.9959 166.85H127.026C133.436 177.85 135.436 187.97 133.116 197.56C132.606 199.67 131.876 201.76 130.956 203.85H18.0758C17.1458 201.76 16.4258 199.67 15.9158 197.56C13.5958 187.97 15.5959 177.85 22.0059 166.85H21.9959ZM18.4758 334.85C19.5758 327.73 21.5058 320.31 24.9158 312.85H124.106C127.516 320.31 129.456 327.73 130.546 334.85H18.4658H18.4758ZM116.546 299.85H32.4858C40.9758 288.05 54.1358 276.55 74.5158 266.43C94.8958 276.55 108.056 288.05 116.546 299.85ZM74.5158 249.76C52.2258 238.81 36.3658 227.92 26.6358 216.85H122.386C112.666 227.92 96.7959 238.81 74.5059 249.76H74.5158Z", viewBox: "0 0 160 380", fill: true, }, bridge: { path: "M280.54 25.28C294.52 17.73 301.18 11.42 301.85 10.76L291.4 0C291 0.38 250.66 37.89 149.61 37.89C58.66 37.89 9.68996 0.840001 9.20996 0.470001L0 12.31C0.87 12.99 9.96998 19.93 27.04 27.89V85.12H10.79V100.12H27.04V148.89H42.04V100.12H265.54V148.89H280.54V100.12H296.79V85.12H280.54V25.28ZM146.29 85.13H97.29V48.85C112 51.2 128.34 52.72 146.29 52.88V85.13ZM161.29 52.73C179.61 52.22 195.9 50.52 210.29 48.09V85.13H161.29V52.74V52.73ZM42.04 34.24C53.26 38.54 66.68 42.73 82.29 46.05V85.12H42.04V34.24ZM225.29 85.13V45.16C241.46 41.54 254.79 37.03 265.54 32.47V85.13H225.29Z", viewBox: "0 0 302 149", }, wheel: { path: "M305.5 0C264.34 0 213.51 58.76 197.5 78.62C181.49 58.76 130.66 0 89.5 0C40.15 0 0 40.15 0 89.5C0 138.85 40.15 179 89.5 179C130.66 179 181.49 120.24 197.5 100.38C213.51 120.24 264.34 179 305.5 179C354.85 179 395 138.85 395 89.5C395 40.15 354.85 0 305.5 0ZM15 89.5C15 57.56 35.2 30.26 63.5 19.69V159.31C35.2 148.74 15 121.44 15 89.5ZM78.5 163.18V15.82C82.09 15.29 85.76 15 89.5 15C100.38 15 112.92 20.82 125.5 29.46V149.54C112.92 158.18 100.38 164 89.5 164C85.76 164 82.09 163.72 78.5 163.18ZM140.5 138.02V40.99C159.44 56.97 176.92 76.76 187.02 89.51C176.93 102.26 159.45 122.05 140.5 138.03V138.02ZM207.98 89.5C218.07 76.75 235.55 56.96 254.5 40.98V138.01C235.56 122.03 218.08 102.24 207.98 89.49V89.5ZM269.5 149.54V29.46C282.08 20.82 294.62 15 305.5 15C309.24 15 312.91 15.28 316.5 15.82V163.18C312.91 163.71 309.24 164 305.5 164C294.62 164 282.08 158.18 269.5 149.54ZM331.5 159.31V19.69C359.8 30.26 380 57.56 380 89.5C380 121.44 359.8 148.74 331.5 159.31Z", viewBox: "0 0 395 179", }, gear: { path: "M143.5 0C64.37 0 0 64.37 0 143.5C0 222.63 64.37 287 143.5 287C222.63 287 287 222.63 287 143.5C287 64.37 222.63 0 143.5 0ZM143.5 231C95.25 231 56 191.75 56 143.5C56 95.25 95.25 56 143.5 56C191.75 56 231 95.25 231 143.5C231 191.75 191.75 231 143.5 231ZM220.47 75.89L238.88 57.48C246.53 65.96 253.07 75.46 258.27 85.75L234.24 95.87C230.44 88.66 225.81 81.95 220.47 75.88V75.89ZM209.77 65.37C203.35 59.91 196.25 55.24 188.62 51.48L198.46 27.36C209.29 32.51 219.29 39.12 228.2 46.95L209.78 65.37H209.77ZM174.71 45.86C167.15 43.44 159.21 41.87 151 41.27V15.22C162.67 15.89 173.92 18.13 184.55 21.72L174.71 45.86ZM136 41.28C127.44 41.9 119.17 43.58 111.32 46.19L101.24 22.16C112.23 18.32 123.9 15.93 136.01 15.23V41.28H136ZM97.46 51.94C90.18 55.62 83.39 60.14 77.22 65.38L58.8 46.96C67.39 39.41 77 32.99 87.38 27.93L97.46 51.95V51.94ZM66.53 75.89C61.28 81.86 56.71 88.45 52.94 95.53L28.95 85.31C34.11 75.19 40.58 65.84 48.12 57.48L66.53 75.89ZM46.89 109.26C44.02 117.34 42.13 125.89 41.38 134.75H15.31C16.15 122.28 18.77 110.3 22.93 99.05L46.89 109.25V109.26ZM41.2 149.75C41.73 158.57 43.39 167.1 46.02 175.19L21.99 185.31C18.11 174.07 15.75 162.14 15.15 149.75H41.19H41.2ZM51.7 189.07C55.42 196.53 60.02 203.48 65.37 209.77L46.95 228.19C39.3 219.48 32.8 209.73 27.7 199.18L51.7 189.07ZM75.89 220.47C82.42 226.22 89.69 231.15 97.54 235.1L87.7 259.24C76.66 253.9 66.5 247.01 57.49 238.88L75.9 220.47H75.89ZM111.39 240.84C119.22 243.43 127.47 245.1 136 245.72V271.77C124.01 271.08 112.45 268.73 101.55 264.96L111.39 240.84ZM151 245.72C159.89 245.07 168.48 243.29 176.6 240.51L186.68 264.52C175.42 268.55 163.45 271.05 151 271.77V245.72ZM190.4 234.62C197.89 230.75 204.84 225.98 211.11 220.46L229.52 238.87C220.83 246.71 211.07 253.39 200.48 258.64L190.4 234.61V234.62ZM221.63 209.77C226.89 203.57 231.43 196.75 235.12 189.42L259.08 199.62C254.02 210 247.6 219.6 240.05 228.19L221.63 209.77ZM240.86 175.56C243.57 167.36 245.26 158.71 245.81 149.75H271.85C271.25 162.31 268.82 174.41 264.85 185.78L240.86 175.56ZM245.62 134.75C244.88 126.02 243.04 117.6 240.24 109.63L264.24 99.52C268.3 110.63 270.86 122.46 271.69 134.76H245.62V134.75Z", viewBox: "0 0 287 287", } } interface ContentItem { id: number subtitle: string description: string shape: ShapeData } export default function MainHero() { const [content, setContent] = useState([]) const [currentIndex, setCurrentIndex] = useState(0) // Fetch content from API useEffect(() => { let isMounted = true let controller: AbortController | null = null let timeoutId: NodeJS.Timeout | null = null const fetchContent = async () => { try { controller = new AbortController() timeoutId = setTimeout(() => { if (controller) { controller.abort() } }, 10000) // 10 second timeout let response try { // Use Next.js API route instead of direct Strapi call response = await fetch("/api/main-hero", { method: 'GET', headers: { 'Content-Type': 'application/json', }, signal: controller.signal, }) // Clear timeout on successful fetch if (timeoutId) { clearTimeout(timeoutId) timeoutId = null } } catch (fetchError: any) { // Clear timeout in case of error if (timeoutId) { clearTimeout(timeoutId) timeoutId = null } if (fetchError.name === 'AbortError' || fetchError.name === 'AbortController') { throw new Error("Request timeout: The server took too long to respond") } throw fetchError } if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) } const contentType = response.headers.get("content-type") if (!contentType || !contentType.includes("application/json")) { throw new Error("Response is not JSON") } const json = await response.json().catch((parseError) => { throw new Error(`Failed to parse JSON: ${parseError.message}`) }) if (!isMounted) return if (json && json.data && Array.isArray(json.data)) { // Map API data to component structure // Assign shapes cyclically based on index const shapeKeys = Object.keys(SHAPES) if (shapeKeys.length === 0) { console.warn("No shapes available") setContent([]) return } const mappedContent = json.data .filter((item: any) => item && item.id && item.sfssubtitle && item.sfsdescription) .map((item: any, index: number) => { try { const shapeKey = shapeKeys[index % shapeKeys.length] const shape = SHAPES[shapeKey] if (!shape || !shape.path || !shape.viewBox) { console.warn(`Invalid shape for key: ${shapeKey}`) return null } return { id: item.id, subtitle: String(item.sfssubtitle || ""), description: String(item.sfsdescription || ""), shape: shape } } catch (mapError) { console.error("Error mapping content item:", mapError) return null } }) .filter((item: any) => item !== null) as ContentItem[] if (mappedContent.length > 0 && isMounted) { setContent(mappedContent) } else if (isMounted) { console.warn("No valid content items found after mapping") setContent([]) } } else if (isMounted) { console.warn("Invalid API response structure", json) setContent([]) } } catch (error) { if (isMounted) { console.error("Failed to fetch hero content:", error) setContent([]) } } } // Execute fetch and ensure all errors are caught const promise = fetchContent() // Always attach a catch handler to prevent unhandled rejections promise.catch((error) => { // Only update state if component is still mounted if (isMounted) { console.error("Error in fetchContent:", error) // Use setTimeout to ensure state update happens even if component is unmounting setTimeout(() => { if (isMounted) { try { setContent([]) } catch (e) { // Ignore setState errors during unmount } } }, 0) } }) return () => { isMounted = false // Cleanup: abort any pending requests and clear timeout if (controller) { controller.abort() controller = null } if (timeoutId) { clearTimeout(timeoutId) timeoutId = null } } }, []) // Cycle content useEffect(() => { if (content.length === 0) return const timer = setInterval(() => { setCurrentIndex((prev) => (prev + 1) % content.length) }, 4500) return () => clearInterval(timer) }, [content]) if (content.length === 0) { return (
{/* Loading state or initial empty state */}
) } const currentContent = content[currentIndex] if (!currentContent) { return (
{/* Loading state or initial empty state */}
) } return (
{/* --- Left Column: Text --- */}

Learning

{/* Fixed Height Text Container to prevent jumping */}

{currentContent.subtitle}

{currentContent.description}

{/* --- Right Column: Animated Shape --- */}
{/* Background Glow - Reduced horizontal space by 40% on large screens */}
{/* Square Container: enforced aspect-square with max dimensions. Reduced by 40% for mobile/tablet, original size for desktop. */}
{currentContent.shape && currentContent.shape.path && currentContent.shape.viewBox ? ( {/* SVG Container */} ) : (

Loading...

)}
) }