diff --git a/app/api/home/route.ts b/app/api/home/route.ts new file mode 100644 index 0000000..d8c550b --- /dev/null +++ b/app/api/home/route.ts @@ -0,0 +1,97 @@ +// app/api/home/route.ts +import { NextResponse } from "next/server"; + +export const runtime = "edge"; + +export interface HomeAction { + key: string; + label: string; + url: string; + isVisible: boolean; +} + +export interface GalleryItem { + imageUrl: string; + caption: string; + altText: string; +} + +export interface HomeResponse { + success: boolean; + data: { + actions: HomeAction[]; + hero: { + imageUrl: string; + caption: string; + altText: string; + }; + gallery: GalleryItem[]; + }; +} + +export async function GET() { + const apiUrl = process.env.API_BASE_URL; + + if (!apiUrl) { + return NextResponse.json( + { success: false, message: "API base URL is not defined", data: { actions: [] } }, + { status: 500 } + ); + } + + try { + const res = await fetch(`${apiUrl}/api/v1/site-content`, { + cache: "no-store", + signal: AbortSignal.timeout(30000), // 30 second timeout + }); + + if (!res.ok) { + console.error(`Upstream API returned status ${res.status}`); + return NextResponse.json( + { success: false, message: `Upstream API error: ${res.status}`, data: { actions: [] } }, + { status: 502 } + ); + } + + const text = await res.text(); + let data; + try { + data = JSON.parse(text); + } catch (err: unknown) { + if (err instanceof Error) { + console.error("Invalid JSON from upstream API:", text, err.message); + } else { + console.error("Invalid JSON from upstream API:", text); + } + + return NextResponse.json( + { success: false, message: "Upstream API returned invalid JSON", data: { actions: [] } }, + { status: 500 } + ); + } + + if (!data || !data.success || !data.data || !Array.isArray(data.data.actions)) { + console.error("Unexpected API response structure", data); + return NextResponse.json( + { success: false, message: "Invalid response from upstream API", data: { actions: [] } }, + { status: 500 } + ); + } + + return NextResponse.json({ + success: true, + data: data.data, + }); + } catch (error: unknown) { + if (error instanceof Error) { + console.error("Error in /api/home route:", error.message); + } else { + console.error("Error in /api/home route:", error); + } + + return NextResponse.json( + { success: false, message: "Internal error", data: { actions: [] } }, + { status: 500 } + ); + } +} diff --git a/components/Home.tsx b/components/Home.tsx index b36c38e..8f9c972 100644 --- a/components/Home.tsx +++ b/components/Home.tsx @@ -8,6 +8,32 @@ import Ferris from "./ui/ferris-eyes"; import { Button } from 'pixel-retroui'; import localFont from "next/font/local"; +interface HomeAction { + key: string; + label: string; + url: string; + isVisible: boolean; +} + +interface GalleryItem { + imageUrl: string; + caption: string; + altText: string; +} + +interface HomeResponse { + success: boolean; + data: { + actions: HomeAction[]; + hero: { + imageUrl: string; + caption: string; + altText: string; + }; + gallery: GalleryItem[]; + }; +} + const pressStart2P = localFont({ src: "../app/fonts/PressStart2P-Regular.ttf", display: "swap", @@ -18,7 +44,24 @@ const pressStart2P = localFont({ export default function HeroSection() { const [color, setColor] = useState("#000000"); const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }); - const [showButton ] = useState(false); + const [actions, setActions] = useState([]); + + useEffect(() => { + async function fetchHomeData() { + try { + const res = await fetch("/api/home"); + if (res.ok) { + const json: HomeResponse = await res.json(); + if (json.success && json.data && Array.isArray(json.data.actions)) { + setActions(json.data.actions); + } + } + } catch (err) { + console.error("Failed to fetch home page data:", err); + } + } + fetchHomeData(); + }, []); useEffect(() => { const updateColor = () => { @@ -68,7 +111,7 @@ export default function HeroSection() { shadow-[6px_6px_0px_rgba(0,0,0,1)] dark:shadow-[6px_6px_0px_rgba(255,255,255,1)] hover:shadow-[8px_8px_0px_rgba(0,0,0,1)] dark:hover:shadow-[8px_8px_0px_rgba(255,255,255,1)] active:translate-x-0.5 active:translate-y-0.5 - transition-all duration-200 mb-32 + transition-all duration-200 `.replace(/\s+/g, ' ').trim(); @@ -183,17 +226,24 @@ export default function HeroSection() { - - + {actions.filter(action => action.isVisible).length > 0 && ( +
+ {actions.filter(action => action.isVisible).map((action) => ( + + + + ))} +
+ )} +

<> CALL OF CODE </>