From e61c003e07178a1d1d367ef23a1c6d17218ad106 Mon Sep 17 00:00:00 2001 From: maulanasdqn Date: Fri, 10 Apr 2026 15:34:37 +0700 Subject: [PATCH] fix: convert landing events/testimonials to client components Server-side fetch fails during Nix build due to SSL cert issues in the sandbox. Converted to client components with useEffect+fetch so data loads at runtime instead of build time. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/landing/src/app/(public)/events/page.tsx | 40 ++++++++++++------- .../src/app/(public)/testimonials/page.tsx | 28 ++++++------- 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/apps/landing/src/app/(public)/events/page.tsx b/apps/landing/src/app/(public)/events/page.tsx index 55f7438..156f66a 100644 --- a/apps/landing/src/app/(public)/events/page.tsx +++ b/apps/landing/src/app/(public)/events/page.tsx @@ -1,5 +1,8 @@ +'use client'; + import { buttonVariants } from '@components'; import { cn } from '@utils'; +import { useEffect, useState } from 'react'; import { HiCalendar, HiClock, HiLocationMarker } from 'react-icons/hi'; interface ApiEvent { @@ -47,28 +50,35 @@ const getEventStatus = (endDate: string) => { return end > now ? 'upcoming' : 'past'; }; -async function fetchEvents(): Promise { - const res = await fetch( - 'https://api.imphnen.dev/v1/landing/cms/events', - { next: { revalidate: 60 } } - ); +export default function EventsPage() { + const [events, setEvents] = useState([]); + const [loading, setLoading] = useState(true); - if (!res.ok) { - return []; - } - - const json: ApiResponse = await res.json(); - return json.data.filter((e) => !e.is_deleted); -} - -export default async function EventsPage() { - const events = await fetchEvents(); + useEffect(() => { + fetch('https://api.imphnen.dev/v1/landing/cms/events') + .then((res) => res.json()) + .then((json: ApiResponse) => { + setEvents(json.data?.filter((e) => !e.is_deleted) || []); + }) + .catch(() => setEvents([])) + .finally(() => setLoading(false)); + }, []); const sortedEvents = [...events].sort( (a, b) => new Date(b.start_date).getTime() - new Date(a.start_date).getTime() ); + if (loading) { + return ( +
+
+
+
+
+ ); + } + if (sortedEvents.length === 0) { return (
diff --git a/apps/landing/src/app/(public)/testimonials/page.tsx b/apps/landing/src/app/(public)/testimonials/page.tsx index 7eb935d..7b9ab74 100644 --- a/apps/landing/src/app/(public)/testimonials/page.tsx +++ b/apps/landing/src/app/(public)/testimonials/page.tsx @@ -1,5 +1,8 @@ +'use client'; + import { Button } from '@components'; import Link from 'next/link'; +import { useEffect, useState } from 'react'; import { BsChatLeftQuote } from 'react-icons/bs'; import { FaQuoteLeft } from 'react-icons/fa'; @@ -36,22 +39,17 @@ function getInitial(name: string) { return name.charAt(0).toUpperCase(); } -async function fetchTestimonials() { - const res = await fetch( - 'https://api.imphnen.dev/v1/landing/cms/testimonials', - { next: { revalidate: 60 } } - ); +export default function Page() { + const [testimonials, setTestimonials] = useState([]); - if (!res.ok) { - return []; - } - - const json: ApiResponse = await res.json(); - return json.data.filter((t) => !t.is_deleted); -} - -export default async function Page() { - const testimonials = await fetchTestimonials(); + useEffect(() => { + fetch('https://api.imphnen.dev/v1/landing/cms/testimonials') + .then((res) => res.json()) + .then((json: ApiResponse) => { + setTestimonials(json.data?.filter((t: ApiTestimonial) => !t.is_deleted) || []); + }) + .catch(() => setTestimonials([])); + }, []); return (