import type { Metadata } from "next"; import { getLocale, getTranslations } from "next-intl/server"; import { Container } from "@/components/layout/container"; import { HomeHero } from "@/components/layout/home-hero"; import { MotionFade } from "@/components/motion-fade"; import { MagicBentoSection } from "@/components/home/magic-bento-section"; import { CapabilitiesSection } from "@/components/home/capabilities-section"; import { ContactCtaSection } from "@/components/home/contact-cta-section"; import { MarqueeSection } from "@/components/home/marquee-section"; import { ProcessSection } from "@/components/home/process-section"; import { ProjectsSection } from "@/components/home/projects-section"; import type { CapabilitiesSectionCopy, ContactCtaSectionCopy, MarqueeSectionCopy, ProcessSectionCopy, ProjectCardCopy, ProjectsSectionCopy, } from "@/components/home/types"; import { buildLocalizedMetadata } from "@/lib/metadata"; import { getLocalizedPath, resolveLocale } from "@/lib/locale"; import { getMarqueeSettings, getSiteSettings, splitMarqueeRowItems, } from "@/lib/app-config"; import { getLocalizedValue, getPublishedPortfolioProjects, } from "@/lib/portfolio"; import { type MarqueeRow } from "@/components/layout/stacked-marquee-section"; type HomePageProps = { params: Promise<{ locale: string; }>; }; export const dynamic = "force-dynamic"; export async function generateMetadata({ params }: HomePageProps): Promise { await params; const siteSettings = await getSiteSettings(); const localeKey = resolveLocale( await getLocale().catch(() => siteSettings.defaultLocale), siteSettings.defaultLocale, ); const t = await getTranslations({ locale: localeKey, namespace: "homepage" }); return await buildLocalizedMetadata({ locale: localeKey, pathname: "/", title: t("meta.title"), description: t("meta.description"), applyTitleTemplate: false, }); } export default async function HomePage({ params }: HomePageProps) { await params; const siteSettings = await getSiteSettings(); const localeKey = resolveLocale( await getLocale().catch(() => siteSettings.defaultLocale), siteSettings.defaultLocale, ); const [t, marqueeSettings, publishedProjects] = await Promise.all([ getTranslations({ locale: localeKey, namespace: "homepage" }), getMarqueeSettings(), getPublishedPortfolioProjects(), ]); const bentoCards = [ { label: t("bento.about.eyebrow"), title: t("bento.about.title"), description: t("bento.about.description"), }, { label: t("bento.coreStack.eyebrow"), title: t("bento.coreStack.title"), description: (t.raw("bento.coreStack.items") as string[]).join(" · "), }, { label: t("bento.availability.eyebrow"), title: t("bento.availability.title"), description: t("bento.availability.description"), }, { label: t("bento.currentFocus.eyebrow"), title: t("bento.currentFocus.title"), description: (t.raw("bento.currentFocus.items") as string[]).join(" · "), }, { label: t("bento.contact.eyebrow"), title: t("bento.contact.title"), description: t("bento.contact.description"), }, { label: t("bento.highlights.eyebrow"), title: t("bento.highlights.title"), description: (t.raw("bento.highlights.items") as Array<{ value: string; label: string }>) .map((i) => `${i.value} ${i.label}`) .join(" · "), }, ]; const marqueeCopy: MarqueeSectionCopy = { eyebrow: t("marquee.eyebrow"), title: t("marquee.title"), description: t("marquee.description"), }; const projectsCopy: ProjectsSectionCopy = { eyebrow: t("projects.eyebrow"), title: t("projects.title"), description: t("projects.description"), stackLabel: t("projects.stackLabel"), fallbackItems: t.raw("projects.fallbackItems") as ProjectsSectionCopy["fallbackItems"], }; const capabilitiesCopy: CapabilitiesSectionCopy = { eyebrow: t("capabilities.eyebrow"), title: t("capabilities.title"), description: t("capabilities.description"), items: t.raw("capabilities.items") as CapabilitiesSectionCopy["items"], }; const processCopy: ProcessSectionCopy = { eyebrow: t("process.eyebrow"), title: t("process.title"), description: t("process.description"), steps: t.raw("process.steps") as ProcessSectionCopy["steps"], }; const contactCtaCopy: ContactCtaSectionCopy = { eyebrow: t("contactCta.eyebrow"), title: t("contactCta.title"), description: t("contactCta.description"), contactCta: t("contactCta.contactCta"), githubCta: t("contactCta.githubCta"), emailLabel: t("contactCta.emailLabel"), emailValue: t("contactCta.emailValue"), availabilityLabel: t("contactCta.availabilityLabel"), availabilityValue: t("contactCta.availabilityValue"), githubHref: t("contactCta.githubHref"), }; const marqueeLocale = marqueeSettings.locales[localeKey]; const marqueeRows = [ { direction: "left", duration: 126, items: splitMarqueeRowItems(marqueeLocale.row1) }, { direction: "right", duration: 220, items: splitMarqueeRowItems(marqueeLocale.row2) }, { direction: "left", duration: 168, items: splitMarqueeRowItems(marqueeLocale.row3) }, { direction: "right", duration: 144, items: splitMarqueeRowItems(marqueeLocale.row4) }, ] satisfies MarqueeRow[]; const portfolioHref = getLocalizedPath(localeKey, "/portfolio", siteSettings.defaultLocale); const contactHref = getLocalizedPath(localeKey, "/contact", siteSettings.defaultLocale); const stackSets = t.raw("projects.stackSets") as string[][]; const selectedProjects = [ ...publishedProjects.filter((project) => project.isFeatured), ...publishedProjects.filter((project) => !project.isFeatured), ].slice(0, 3); const projectCards: ProjectCardCopy[] = selectedProjects.map((project, index) => ({ title: getLocalizedValue(project.title, localeKey), summary: getLocalizedValue(project.summary, localeKey), stack: stackSets[index] ?? stackSets[stackSets.length - 1] ?? [], href: getLocalizedPath( localeKey, `/portfolio/${project.slug}`, siteSettings.defaultLocale, ), cta: t("projects.cta"), meta: [ getLocalizedValue(project.category.name, localeKey), getLocalizedValue(project.serviceLabel, localeKey), String(project.projectYear), ], featured: index === 0, coverImagePath: project.coverImagePath, })); const fallbackProjectCards: ProjectCardCopy[] = projectsCopy.fallbackItems.map((item) => ({ title: item.title, summary: item.summary, stack: item.stack, href: item.href === "/portfolio" ? portfolioHref : item.href === "/contact" ? contactHref : item.href, cta: t("projects.cta"), meta: [t("projects.fallbackMeta")], })); const mergedProjectCards = [ ...projectCards, ...fallbackProjectCards.slice(0, Math.max(0, 3 - projectCards.length)), ].slice(0, 3); return ( <> ); }