ifixkart-storefront/app/page.tsx

194 lines
6.2 KiB
TypeScript

import React from 'react';
import { AnnouncementBar } from '../layout/AnnouncementBar';
import { Header } from '../layout/Header';
import { Navbar } from '../layout/Navbar';
import { Footer } from '../layout/Footer';
import { FloatingButtons } from '../components/FloatingButtons';
import { StickyHeaderSpacer } from '../components/StickyHeaderSpacer';
import { HomeHeroRow } from '../sections/HomeHeroRow';
import { PromoCardsSection } from '../sections/PromoCardsSection';
import { CategoriesSection } from '../sections/CategoriesSection';
import { NewArrivalsSection } from '../sections/NewArrivalsSection';
import { PromoBannersSection } from '../sections/PromoBannersSection';
import { HotDealsSection } from '../sections/HotDealsSection';
import { RecentlyLaunchedSection } from '../sections/RecentlyLaunchedSection';
import { BrandsSection } from '../sections/BrandsSection';
import { UltimateTechSection } from '../sections/UltimateTechSection';
import { ArticlesSection } from '../sections/ArticlesSection';
import { FeaturesSection } from '../sections/FeaturesSection';
import { catalogServerService } from '../services/api/catalogServerService';
import { mapCatalogProductToProduct } from '../utils/productMapper';
import { Product } from '../types';
import {
buildHomepageSections,
isSectionVisible,
normalizeLayoutPayload,
} from '../lib/layoutConfig';
import { sortHomeSectionsByOrder } from '../lib/techshopHome';
/**
* Always fetch fresh layout so admin Visible/Hidden + display_order apply.
* Homepage stack matches TechShop demo:
* https://demo.templatemonster.com/demo/501755.html
*/
export const dynamic = 'force-dynamic';
export const revalidate = 0;
export default async function Home() {
let layoutData: any[] = [];
let dbProducts: any[] = [];
let categoriesData: any[] = [];
let brandsData: any[] = [];
let productsError = false;
const [productsResult, layoutResult, categoriesResult, brandsResult] =
await Promise.allSettled([
catalogServerService.getProducts(),
catalogServerService.fetchLayoutConfigOnServer(),
catalogServerService.getCategories(),
catalogServerService.getBrands(),
]);
if (productsResult.status === 'fulfilled') {
dbProducts = productsResult.value || [];
} else {
console.error('Homepage products fetch failed:', productsResult.reason);
productsError = true;
}
if (layoutResult.status === 'fulfilled') {
layoutData = normalizeLayoutPayload(layoutResult.value);
} else {
console.error('Homepage layout fetch failed:', layoutResult.reason);
}
if (categoriesResult.status === 'fulfilled') {
categoriesData = categoriesResult.value || [];
}
if (brandsResult.status === 'fulfilled') {
brandsData = brandsResult.value || [];
}
const catalogProducts: Product[] =
dbProducts.length > 0 ? dbProducts.map(mapCatalogProductToProduct) : [];
const { activeSections, sectionsData, layoutLoaded } =
buildHomepageSections(layoutData);
/** Honor dashboard Visible/Hidden (`metadata_json.is_active`) for every section. */
const show = (key: string) =>
isSectionVisible(activeSections, key, layoutLoaded);
const orderedKeys = sortHomeSectionsByOrder(sectionsData).map((s) => s.key);
const productsLoading = false;
const sectionNodes: Record<string, React.ReactNode> = {
hero_slider: show('hero_slider') ? (
<HomeHeroRow
key="hero_slider"
heroConfig={sectionsData.hero_slider}
showHero
initialCategories={categoriesData}
/>
) : null,
promo_cards: show('promo_cards') ? (
<PromoCardsSection
key="promo_cards"
config={sectionsData.promo_cards}
/>
) : null,
featured_categories: show('featured_categories') ? (
<CategoriesSection
key="featured_categories"
config={sectionsData.featured_categories}
initialCategories={categoriesData}
/>
) : null,
editorial_story: show('editorial_story') ? (
<NewArrivalsSection
key="editorial_story"
config={sectionsData.editorial_story}
products={catalogProducts}
loading={productsLoading}
error={productsError}
/>
) : null,
promo_banners: show('promo_banners') ? (
<PromoBannersSection
key="promo_banners"
config={sectionsData.promo_banners}
/>
) : null,
deal_of_the_day: show('deal_of_the_day') ? (
<HotDealsSection
key="deal_of_the_day"
config={sectionsData.deal_of_the_day}
products={catalogProducts}
loading={productsLoading}
error={productsError}
/>
) : null,
recently_launched: show('recently_launched') ? (
<RecentlyLaunchedSection
key="recently_launched"
config={sectionsData.recently_launched}
products={catalogProducts}
loading={productsLoading}
error={productsError}
/>
) : null,
official_brands: show('official_brands') ? (
<BrandsSection
key="official_brands"
config={sectionsData.official_brands}
initialBrands={brandsData}
/>
) : null,
ultimate_tech: show('ultimate_tech') ? (
<UltimateTechSection
key="ultimate_tech"
config={sectionsData.ultimate_tech}
products={catalogProducts}
loading={productsLoading}
error={productsError}
/>
) : null,
tech_blog: show('tech_blog') ? (
<ArticlesSection key="tech_blog" config={sectionsData.tech_blog} />
) : null,
trust_badges: show('trust_badges') ? (
<FeaturesSection
key="trust_badges"
config={sectionsData.trust_badges}
/>
) : null,
};
return (
<div className="min-h-screen bg-[#F8F9FB] text-[#1E293B] flex flex-col font-sans">
<AnnouncementBar
config={sectionsData.announcement_bar}
isActive={show('announcement_bar')}
/>
<Header />
<Navbar
config={sectionsData.nav_links}
isActive={show('nav_links')}
/>
{/* After Header + Navbar so sticky chrome reserves full height */}
<StickyHeaderSpacer />
<main className="flex-grow">
{orderedKeys.map((key) => sectionNodes[key] ?? null)}
</main>
<Footer />
<FloatingButtons />
</div>
);
}