Merge version_2 into main #5

Merged
bender merged 20 commits from version_2 into main 2026-03-12 03:55:27 +00:00
16 changed files with 799 additions and 3052 deletions

View File

@@ -1,520 +1,37 @@
"use client";
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
import NavbarStyleFullscreen from "@/components/navbar/NavbarStyleFullscreen/NavbarStyleFullscreen";
import HeroBillboardTestimonial from "@/components/sections/hero/HeroBillboardTestimonial";
import ProductCardTwo from "@/components/sections/product/ProductCardTwo";
import FeatureCardTen from "@/components/sections/feature/FeatureCardTen";
import MetricSplitMediaAbout from "@/components/sections/about/MetricSplitMediaAbout";
import MetricCardSeven from "@/components/sections/metrics/MetricCardSeven";
import SocialProofOne from "@/components/sections/socialProof/SocialProofOne";
import TestimonialCardFifteen from "@/components/sections/testimonial/TestimonialCardFifteen";
import FaqSplitMedia from "@/components/sections/faq/FaqSplitMedia";
import ContactCTA from "@/components/sections/contact/ContactCTA";
import FooterBase from "@/components/sections/footer/FooterBase";
import Link from "next/link";
import {
Sparkles,
Star,
Grid,
Award,
TrendingUp,
Briefcase,
Mail,
HelpCircle,
Shirt,
Home,
Sofa,
Layout,
Dumbbell,
Activity,
Zap,
Smartphone,
Cpu,
} from "lucide-react";
import { ThemeProvider } from '@/providers/themeProvider/ThemeProvider';
import NavbarStyleFullscreen from '@/components/navbar/NavbarStyleFullscreen/NavbarStyleFullscreen';
export default function ElectronicsPage() {
return (
<ThemeProvider
defaultButtonVariant="hover-magnetic"
defaultTextAnimation="reveal-blur"
defaultButtonVariant="text-stagger"
defaultTextAnimation="entrance-slide"
borderRadius="rounded"
contentWidth="smallMedium"
sizing="mediumLargeSizeLargeTitles"
background="floatingGradient"
cardStyle="glass-depth"
primaryButtonStyle="double-inset"
secondaryButtonStyle="radial-glow"
headingFontWeight="extrabold"
contentWidth="medium"
sizing="medium"
background="circleGradient"
cardStyle="glass-elevated"
primaryButtonStyle="gradient"
secondaryButtonStyle="glass"
headingFontWeight="normal"
>
<div id="nav" data-section="nav">
<NavbarStyleFullscreen
brandName="ZSMX Store"
navItems={[
{ name: "Home", id: "/" },
{ name: "Fashion", id: "fashion" },
{ name: "Home", id: "home-category" },
{ name: "Home Decor", id: "home-category" },
{ name: "Gym", id: "gym" },
{ name: "Electronics", id: "electronics" },
{ name: "Contact", id: "contact" },
{ name: "Electronics", id: "electronics" }
]}
brandName="ZSMX Store"
bottomLeftText="Premium Multi-Category Store"
bottomRightText="hello@zsmxstore.com"
/>
</div>
<div id="hero" data-section="hero">
<HeroBillboardTestimonial
title="Discover Your Perfect Style"
description="Explore our curated collection of fashion, home decor, fitness equipment, and premium electronics. Where quality meets elegance."
tag="Welcome to ZSMX Store"
tagIcon={Sparkles}
tagAnimation="slide-up"
background={{ variant: "floatingGradient" }}
imageSrc="http://img.b2bpic.net/free-photo/internationals-people-standing-cafe_1157-32402.jpg?_wi=3"
imageAlt="Premium multi-category product showcase"
mediaAnimation="slide-up"
testimonials={[
{
name: "Sarah Mitchell",
handle: "Fashion Enthusiast",
testimonial:
"Exceptional quality and stunning designs. ZSMX Store has become my go-to for everything.",
rating: 5,
imageSrc:
"http://img.b2bpic.net/free-photo/portrait-confident-young-businessman-with-his-arms-crossed_23-2148176206.jpg?_wi=3",
imageAlt: "Sarah Mitchell",
},
{
name: "James Chen",
handle: "Interior Designer",
testimonial:
"The home collection is absolutely exquisite. Premium pieces that transform any space.",
rating: 5,
imageSrc:
"http://img.b2bpic.net/free-photo/positive-confident-businessman-posing-outside_74855-1183.jpg?_wi=3",
imageAlt: "James Chen",
},
{
name: "Emma Rodriguez",
handle: "Fitness Coach",
testimonial:
"Top-tier gym equipment. My clients and I love the durability and design.",
rating: 5,
imageSrc:
"http://img.b2bpic.net/free-photo/modern-businesswoman_23-2148012909.jpg?_wi=3",
imageAlt: "Emma Rodriguez",
},
]}
testimonialRotationInterval={5000}
buttons={[
{ text: "Shop Now", href: "https://example.com" },
{ text: "Explore Categories", href: "#" },
]}
buttonAnimation="slide-up"
useInvertedBackground={false}
/>
</div>
<div id="products" data-section="products">
<ProductCardTwo
title="Featured Collection"
description="Hand-picked premium products across all categories. New arrivals updated daily."
tag="Best Sellers"
tagIcon={Star}
tagAnimation="slide-up"
textboxLayout="default"
animationType="slide-up"
gridVariant="bento-grid"
useInvertedBackground={false}
products={[
{
id: "fashion-1",
brand: "LuxeStyle",
name: "Premium Wool Overcoat",
price: "$450.00",
rating: 5,
reviewCount: "342",
imageSrc:
"http://img.b2bpic.net/free-photo/bag-hanging-from-furniture-item-indoors_23-2151073505.jpg?_wi=9",
imageAlt: "Premium Wool Overcoat",
},
{
id: "fashion-2",
brand: "ElegantWear",
name: "Designer Evening Gown",
price: "$680.00",
rating: 5,
reviewCount: "289",
imageSrc:
"http://img.b2bpic.net/free-photo/store-customer-holding-shirt-body_482257-85803.jpg?_wi=7",
imageAlt: "Designer Evening Gown",
},
{
id: "fashion-3",
brand: "ClassicThreads",
name: "Italian Leather Shoes",
price: "$395.00",
rating: 4,
reviewCount: "156",
imageSrc:
"http://img.b2bpic.net/free-photo/still-life-with-classic-shirts_23-2150828626.jpg?_wi=7",
imageAlt: "Italian Leather Shoes",
},
{
id: "home-1",
brand: "Luxehome",
name: "Modern Sectional Sofa",
price: "$1,299.00",
rating: 5,
reviewCount: "201",
imageSrc:
"http://img.b2bpic.net/free-photo/beautiful-dried-flowers-table_23-2149591635.jpg?_wi=5",
imageAlt: "Modern Sectional Sofa",
},
{
id: "home-2",
brand: "DecorPremium",
name: "Crystal Chandelier",
price: "$850.00",
rating: 5,
reviewCount: "178",
imageSrc:
"http://img.b2bpic.net/free-photo/couch-with-cushions-glass-table_1203-764.jpg?_wi=3",
imageAlt: "Crystal Chandelier",
},
{
id: "home-3",
brand: "InteriorLux",
name: "Turkish Area Rug",
price: "$625.00",
rating: 4,
reviewCount: "124",
imageSrc:
"http://img.b2bpic.net/free-photo/cafe-with-coffee-tables-cosy-sofas-plants-shelves_140725-7785.jpg?_wi=3",
imageAlt: "Turkish Area Rug",
},
]}
buttons={[{ text: "View All Products", href: "#" }]}
/>
</div>
<div id="categories" data-section="categories">
<FeatureCardTen
title="Our Category Showcase"
description="Explore our diverse range of premium products across four expertly curated categories. Each collection represents the finest in quality and design."
tag="Categories"
tagIcon={Grid}
tagAnimation="slide-up"
textboxLayout="default"
animationType="slide-up"
useInvertedBackground={false}
features={[
{
id: "1",
title: "Fashion Excellence",
description:
"Premium apparel and accessories designed for those who appreciate style. From casual elegance to formal sophistication.",
media: {
imageSrc:
"http://img.b2bpic.net/free-photo/bag-hanging-from-furniture-item-indoors_23-2151073505.jpg?_wi=10",
},
items: [
{ icon: Shirt, text: "Designer Collections" },
{ icon: Sparkles, text: "Premium Fabrics" },
{ icon: Star, text: "Timeless Styles" },
],
reverse: false,
},
{
id: "2",
title: "Home Furnishings",
description:
"Transform your living space with luxury home decor. Curated pieces that combine functionality with aesthetic elegance.",
media: {
imageSrc:
"http://img.b2bpic.net/free-photo/beautiful-dried-flowers-table_23-2149591635.jpg?_wi=6",
},
items: [
{ icon: Home, text: "Modern Design" },
{ icon: Sofa, text: "Premium Materials" },
{ icon: Layout, text: "Expert Curation" },
],
reverse: true,
},
{
id: "3",
title: "Fitness & Gym",
description:
"Professional-grade fitness equipment and apparel. Engineered for performance and durability in every workout.",
media: {
imageSrc:
"http://img.b2bpic.net/free-photo/still-life-perfectly-ordered-fitness-gym-accessories_52683-100705.jpg?_wi=6",
},
items: [
{ icon: Dumbbell, text: "Professional Equipment" },
{ icon: Activity, text: "Performance Gear" },
{ icon: Zap, text: "High Durability" },
],
reverse: false,
},
{
id: "4",
title: "Premium Electronics",
description:
"Latest technology and innovative gadgets. Cutting-edge devices that enhance your digital lifestyle.",
media: {
imageSrc:
"http://img.b2bpic.net/free-photo/view-robotic-vacuum-cleaner-flat-surface_23-2151736769.jpg?_wi=3",
},
items: [
{ icon: Smartphone, text: "Latest Technology" },
{ icon: Cpu, text: "Advanced Features" },
{ icon: Zap, text: "Top Performance" },
],
reverse: true,
},
]}
/>
</div>
<div id="about" data-section="about">
<MetricSplitMediaAbout
title="Your Trusted Multi-Category Destination"
description="ZSMX Store is your premier destination for premium products across fashion, home, fitness, and electronics. We believe in delivering excellence through carefully curated collections, exceptional quality, and outstanding customer service. Our mission is to make luxury and quality accessible to everyone."
tag="About ZSMX"
tagIcon={Award}
tagAnimation="slide-up"
metrics={[
{ value: "50k+", title: "Satisfied Customers" },
{ value: "10k+", title: "Premium Products" },
]}
imageSrc="http://img.b2bpic.net/free-photo/modern-sauna-with-panoramic-windows-wooden-design_169016-70021.jpg?_wi=3"
imageAlt="ZSMX Store - Premium retail environment"
useInvertedBackground={true}
mediaAnimation="slide-up"
/>
</div>
<div id="metrics" data-section="metrics">
<MetricCardSeven
title="By The Numbers"
description="Trusted by thousands of customers worldwide. Our commitment to quality and service speaks for itself."
tag="Our Growth"
tagIcon={TrendingUp}
tagAnimation="slide-up"
textboxLayout="default"
animationType="slide-up"
useInvertedBackground={false}
metrics={[
{
id: "1",
value: "98%",
title: "Customer Satisfaction Rate",
items: [
"Premium quality guaranteed",
"Expert curation",
"Dedicated support team",
],
},
{
id: "2",
value: "24/7",
title: "Customer Support Available",
items: [
"Real-time assistance",
"Expert consultations",
"Fast responses",
],
},
{
id: "3",
value: "100%",
title: "Authentic Products",
items: [
"Verified sources",
"Quality assurance",
"Brand authenticity",
],
},
{
id: "4",
value: "Free",
title: "Shipping On Orders Over $100",
items: ["Fast delivery", "Tracking included", "Safe packaging"],
},
]}
/>
</div>
<div id="social-proof" data-section="social-proof">
<SocialProofOne
title="Trusted by Leading Brands & Retailers"
description="Partnered with premium brands worldwide to bring you authentic luxury products."
tag="Our Partners"
tagIcon={Briefcase}
tagAnimation="slide-up"
textboxLayout="default"
useInvertedBackground={false}
names={[
"LuxeStyle",
"ElegantWear",
"ClassicThreads",
"Luxehome",
"DecorPremium",
"InteriorLux",
"FitnessPro",
"SportsTech",
]}
speed={40}
showCard={true}
/>
</div>
<div id="testimonials" data-section="testimonials">
<TestimonialCardFifteen
testimonial="ZSMX Store has completely revolutionized how I shop online. The selection is incredible, the quality is unmatched, and the customer service is exceptional. I've purchased from all four categories and been amazed every single time."
rating={5}
author="Victoria Thompson, Premium Lifestyle Enthusiast"
avatars={[
{
src: "http://img.b2bpic.net/free-photo/portrait-confident-young-businessman-with-his-arms-crossed_23-2148176206.jpg",
alt: "Customer 1",
},
{
src: "http://img.b2bpic.net/free-photo/positive-confident-businessman-posing-outside_74855-1183.jpg",
alt: "Customer 2",
},
{
src: "http://img.b2bpic.net/free-photo/modern-businesswoman_23-2148012909.jpg",
alt: "Customer 3",
},
{
src: "http://img.b2bpic.net/free-photo/businessman-formal-wear-professional-corporate-concept_53876-71166.jpg",
alt: "Customer 4",
},
{
src: "http://img.b2bpic.net/free-photo/beautiful-business-woman-portrait_23-2149280717.jpg",
alt: "Customer 5",
},
{
src: "http://img.b2bpic.net/free-photo/portrait-outdoors-business-man-smiles_23-2148763856.jpg",
alt: "Customer 6",
},
]}
ratingAnimation="slide-up"
avatarsAnimation="slide-up"
useInvertedBackground={false}
/>
</div>
<div id="faq" data-section="faq">
<FaqSplitMedia
title="Frequently Asked Questions"
description="Find answers to common questions about our products, ordering, shipping, and customer service."
tag="Help Center"
tagIcon={HelpCircle}
tagAnimation="slide-up"
textboxLayout="default"
useInvertedBackground={false}
faqs={[
{
id: "1",
title: "Are all products authentic and guaranteed?",
content:
"Yes, absolutely. We source directly from authorized distributors and verify authenticity of all products. Every item comes with our quality guarantee and certification of authenticity.",
},
{
id: "2",
title: "What is your return and exchange policy?",
content:
"We offer hassle-free returns and exchanges within 30 days of purchase. Items must be unused and in original packaging. Simply contact our customer service team to initiate the process.",
},
{
id: "3",
title: "How long does shipping typically take?",
content:
"Standard shipping takes 5-7 business days. Express shipping options (2-3 days) are available for most orders. Orders over $100 qualify for free standard shipping.",
},
{
id: "4",
title: "Do you offer international shipping?",
content:
"Yes, we ship to most countries worldwide. International shipping costs vary by location and are calculated at checkout. Customs duties may apply depending on your country.",
},
{
id: "5",
title: "Is my personal information secure?",
content:
"We use industry-standard SSL encryption to protect all personal and payment information. Your data is never shared with third parties. We comply with all privacy regulations.",
},
{
id: "6",
title: "What payment methods do you accept?",
content:
"We accept all major credit cards, debit cards, PayPal, Apple Pay, and Google Pay. All transactions are secure and encrypted.",
},
]}
imageSrc="http://img.b2bpic.net/free-photo/woman-sitting-wheelchair-modern-concept_23-2148497283.jpg?_wi=3"
imageAlt="Customer service support team"
mediaAnimation="slide-up"
faqsAnimation="slide-up"
mediaPosition="left"
animationType="smooth"
/>
</div>
<div id="contact" data-section="contact">
<ContactCTA
tag="Get In Touch"
tagIcon={Mail}
tagAnimation="slide-up"
title="Ready to Discover Premium Products?"
description="Have questions about our products or services? Our expert team is here to help. Contact us today and experience the ZSMX Store difference."
buttons={[
{ text: "Contact Our Team", href: "#" },
{ text: "Shop Now", href: "#" },
]}
buttonAnimation="slide-up"
background={{ variant: "plain" }}
useInvertedBackground={false}
/>
</div>
<div id="footer" data-section="footer">
<FooterBase
logoText="ZSMX Store"
copyrightText="© 2025 ZSMX Store. All rights reserved."
columns={[
{
title: "Shop",
items: [
{ label: "Fashion", href: "#" },
{ label: "Home", href: "#" },
{ label: "Gym", href: "#" },
{ label: "Electronics", href: "#" },
],
},
{
title: "Support",
items: [
{ label: "Contact Us", href: "#" },
{ label: "FAQ", href: "#faq" },
{ label: "Shipping Info", href: "#" },
{ label: "Returns", href: "#" },
],
},
{
title: "Company",
items: [
{ label: "About Us", href: "#about" },
{ label: "Blog", href: "#" },
{ label: "Careers", href: "#" },
{ label: "Privacy Policy", href: "#" },
],
},
]}
/>
</div>
<div>Electronics Page</div>
</ThemeProvider>
);
}
}

View File

@@ -1,248 +1,37 @@
"use client";
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
import NavbarStyleFullscreen from "@/components/navbar/NavbarStyleFullscreen/NavbarStyleFullscreen";
import ProductCardTwo from "@/components/sections/product/ProductCardTwo";
import FooterBase from "@/components/sections/footer/FooterBase";
import Link from "next/link";
import { Star } from "lucide-react";
import { ThemeProvider } from '@/providers/themeProvider/ThemeProvider';
import NavbarStyleFullscreen from '@/components/navbar/NavbarStyleFullscreen/NavbarStyleFullscreen';
export default function FashionPage() {
return (
<ThemeProvider
defaultButtonVariant="hover-magnetic"
defaultTextAnimation="reveal-blur"
defaultButtonVariant="text-stagger"
defaultTextAnimation="entrance-slide"
borderRadius="rounded"
contentWidth="smallMedium"
sizing="mediumLargeSizeLargeTitles"
background="floatingGradient"
cardStyle="glass-depth"
primaryButtonStyle="double-inset"
secondaryButtonStyle="radial-glow"
headingFontWeight="extrabold"
contentWidth="medium"
sizing="medium"
background="circleGradient"
cardStyle="glass-elevated"
primaryButtonStyle="gradient"
secondaryButtonStyle="glass"
headingFontWeight="normal"
>
<div id="nav" data-section="nav">
<NavbarStyleFullscreen
brandName="ZSMX Store"
navItems={[
{ name: "Home", id: "/" },
{ name: "Fashion", id: "fashion" },
{ name: "Home", id: "home-category" },
{ name: "Home Decor", id: "home-category" },
{ name: "Gym", id: "gym" },
{ name: "Electronics", id: "electronics" },
{ name: "Contact", id: "contact" },
{ name: "Electronics", id: "electronics" }
]}
brandName="ZSMX Store"
bottomLeftText="Premium Multi-Category Store"
bottomRightText="hello@zsmxstore.com"
/>
</div>
<div id="fashion-collection" data-section="fashion-collection">
<ProductCardTwo
title="Fashion Collection"
description="Premium apparel and accessories carefully selected for style, quality, and timeless elegance. Discover designer pieces and contemporary fashion."
tag="New Collection"
tagIcon={Star}
tagAnimation="slide-up"
textboxLayout="default"
animationType="slide-up"
gridVariant="bento-grid"
useInvertedBackground={false}
products={[
{
id: "fashion-1",
brand: "LuxeStyle",
name: "Premium Wool Overcoat",
price: "$450.00",
rating: 5,
reviewCount: "342",
imageSrc: "http://img.b2bpic.net/free-photo/bag-hanging-from-furniture-item-indoors_23-2151073505.jpg?_wi=3",
imageAlt: "Premium Wool Overcoat",
},
{
id: "fashion-2",
brand: "ElegantWear",
name: "Designer Evening Gown",
price: "$680.00",
rating: 5,
reviewCount: "289",
imageSrc: "http://img.b2bpic.net/free-photo/store-customer-holding-shirt-body_482257-85803.jpg?_wi=2",
imageAlt: "Designer Evening Gown",
},
{
id: "fashion-3",
brand: "ClassicThreads",
name: "Italian Leather Shoes",
price: "$395.00",
rating: 4,
reviewCount: "156",
imageSrc: "http://img.b2bpic.net/free-photo/still-life-with-classic-shirts_23-2150828626.jpg?_wi=2",
imageAlt: "Italian Leather Shoes",
},
{
id: "fashion-4",
brand: "LuxeStyle",
name: "Cashmere Sweater Collection",
price: "$320.00",
rating: 5,
reviewCount: "267",
imageSrc: "http://img.b2bpic.net/free-photo/bag-hanging-from-furniture-item-indoors_23-2151073505.jpg?_wi=4",
imageAlt: "Cashmere Sweater",
},
{
id: "fashion-5",
brand: "ElegantWear",
name: "Silk Blouse Set",
price: "$275.00",
rating: 5,
reviewCount: "198",
imageSrc: "http://img.b2bpic.net/free-photo/store-customer-holding-shirt-body_482257-85803.jpg?_wi=3",
imageAlt: "Silk Blouse",
},
{
id: "fashion-6",
brand: "ClassicThreads",
name: "Designer Jean Collection",
price: "$199.00",
rating: 4,
reviewCount: "445",
imageSrc: "http://img.b2bpic.net/free-photo/still-life-with-classic-shirts_23-2150828626.jpg?_wi=3",
imageAlt: "Designer Jeans",
},
]}
buttons={[
{ text: "View All", href: "#" },
{ text: "Back to Home", href: "/" },
]}
/>
</div>
<div id="fashion-details" data-section="fashion-details">
<ProductCardTwo
title="Trending Now"
description="Explore the latest fashion trends and seasonal favorites. Premium quality pieces for every occasion and style preference."
tag="Trending"
tagIcon={Star}
tagAnimation="slide-up"
textboxLayout="default"
animationType="slide-up"
gridVariant="three-columns-all-equal-width"
useInvertedBackground={true}
products={[
{
id: "fashion-trend-1",
brand: "LuxeStyle",
name: "Minimalist White Shirt",
price: "$155.00",
rating: 5,
reviewCount: "523",
imageSrc: "http://img.b2bpic.net/free-photo/store-customer-holding-shirt-body_482257-85803.jpg?_wi=4",
imageAlt: "Minimalist White Shirt",
},
{
id: "fashion-trend-2",
brand: "ElegantWear",
name: "Black Leather Jacket",
price: "$495.00",
rating: 5,
reviewCount: "389",
imageSrc: "http://img.b2bpic.net/free-photo/bag-hanging-from-furniture-item-indoors_23-2151073505.jpg?_wi=5",
imageAlt: "Black Leather Jacket",
},
{
id: "fashion-trend-3",
brand: "ClassicThreads",
name: "Neutral Tone Blazer",
price: "$425.00",
rating: 4,
reviewCount: "267",
imageSrc: "http://img.b2bpic.net/free-photo/still-life-with-classic-shirts_23-2150828626.jpg?_wi=4",
imageAlt: "Neutral Blazer",
},
]}
/>
</div>
<div id="fashion-cta" data-section="fashion-cta">
<ProductCardTwo
title="Featured Designers"
description="Exclusive collections from world-renowned fashion designers. Limited edition pieces that showcase luxury and artistry."
tag="Exclusive"
tagIcon={Star}
tagAnimation="slide-up"
textboxLayout="default"
animationType="slide-up"
gridVariant="uniform-all-items-equal"
useInvertedBackground={false}
products={[
{
id: "designer-1",
brand: "ElegantWear",
name: "Haute Couture Gown",
price: "$1,200.00",
rating: 5,
reviewCount: "89",
imageSrc: "http://img.b2bpic.net/free-photo/store-customer-holding-shirt-body_482257-85803.jpg?_wi=5",
imageAlt: "Haute Couture Gown",
},
{
id: "designer-2",
brand: "LuxeStyle",
name: "Signature Collection Dress",
price: "$895.00",
rating: 5,
reviewCount: "134",
imageSrc: "http://img.b2bpic.net/free-photo/bag-hanging-from-furniture-item-indoors_23-2151073505.jpg?_wi=6",
imageAlt: "Signature Dress",
},
{
id: "designer-3",
brand: "ClassicThreads",
name: "Artisan Wool Coat",
price: "$750.00",
rating: 5,
reviewCount: "167",
imageSrc: "http://img.b2bpic.net/free-photo/still-life-with-classic-shirts_23-2150828626.jpg?_wi=5",
imageAlt: "Artisan Wool Coat",
},
]}
/>
</div>
<div id="footer" data-section="footer">
<FooterBase
logoText="ZSMX Store"
copyrightText="© 2025 ZSMX Store. All rights reserved."
columns={[
{
title: "Shop",
items: [
{ label: "Fashion", href: "/fashion" },
{ label: "Home", href: "#home-category" },
{ label: "Gym", href: "#gym" },
{ label: "Electronics", href: "#electronics" },
],
},
{
title: "Support",
items: [
{ label: "Contact Us", href: "#contact" },
{ label: "FAQ", href: "#faq" },
{ label: "Shipping Info", href: "#" },
{ label: "Returns", href: "#" },
],
},
{
title: "Company",
items: [
{ label: "About Us", href: "#about" },
{ label: "Blog", href: "#" },
{ label: "Careers", href: "#" },
{ label: "Privacy Policy", href: "#" },
],
},
]}
/>
</div>
<div>Fashion Page</div>
</ThemeProvider>
);
}
}

View File

@@ -26,11 +26,11 @@ export default function GymPage() {
<div id="nav" data-section="nav">
<NavbarStyleFullscreen
navItems={[
{ name: "Home", id: "/" },
{ name: "Fashion", id: "fashion" },
{ name: "Home", id: "home-category" },
{ name: "Home Decor", id: "home-category" },
{ name: "Gym", id: "gym" },
{ name: "Electronics", id: "electronics" },
{ name: "Contact", id: "contact" },
{ name: "Electronics", id: "electronics" }
]}
brandName="ZSMX Store"
bottomLeftText="Premium Multi-Category Store"
@@ -52,65 +52,23 @@ export default function GymPage() {
useInvertedBackground={false}
products={[
{
id: "gym-1",
brand: "FitnessPro",
name: "Professional Dumbbell Set",
price: "$599.00",
rating: 5,
reviewCount: "287",
imageSrc: "http://img.b2bpic.net/free-photo/still-life-perfectly-ordered-fitness-gym-accessories_52683-100705.jpg?_wi=3",
imageAlt: "Professional Dumbbell Set",
},
id: "gym-1", brand: "FitnessPro", name: "Professional Dumbbell Set", price: "$599.00", rating: 5,
reviewCount: "287", imageSrc: "http://img.b2bpic.net/free-photo/still-life-perfectly-ordered-fitness-gym-accessories_52683-100705.jpg?_wi=3", imageAlt: "Professional Dumbbell Set"},
{
id: "gym-2",
brand: "SportsTech",
name: "High-Performance Treadmill",
price: "$1,199.00",
rating: 5,
reviewCount: "342",
imageSrc: "http://img.b2bpic.net/free-vector/sport-landing-page-template-with-photo_23-2148217108.jpg?_wi=1",
imageAlt: "High-Performance Treadmill",
},
id: "gym-2", brand: "SportsTech", name: "High-Performance Treadmill", price: "$1,199.00", rating: 5,
reviewCount: "342", imageSrc: "http://img.b2bpic.net/free-vector/sport-landing-page-template-with-photo_23-2148217108.jpg?_wi=1", imageAlt: "High-Performance Treadmill"},
{
id: "gym-3",
brand: "EliteGym",
name: "Commercial Weight Bench",
price: "$450.00",
rating: 4,
reviewCount: "198",
imageSrc: "http://img.b2bpic.net/free-photo/perfectly-ordered-compositions-view_23-2149872090.jpg?_wi=1",
imageAlt: "Commercial Weight Bench",
},
id: "gym-3", brand: "EliteGym", name: "Commercial Weight Bench", price: "$450.00", rating: 4,
reviewCount: "198", imageSrc: "http://img.b2bpic.net/free-photo/perfectly-ordered-compositions-view_23-2149872090.jpg?_wi=1", imageAlt: "Commercial Weight Bench"},
{
id: "gym-4",
brand: "PowerTech",
name: "Adjustable Kettlebell Set",
price: "$349.00",
rating: 5,
reviewCount: "265",
imageSrc: "http://img.b2bpic.net/free-photo/still-life-perfectly-ordered-fitness-gym-accessories_52683-100705.jpg?_wi=4",
imageAlt: "Adjustable Kettlebell Set",
},
id: "gym-4", brand: "PowerTech", name: "Adjustable Kettlebell Set", price: "$349.00", rating: 5,
reviewCount: "265", imageSrc: "http://img.b2bpic.net/free-photo/still-life-perfectly-ordered-fitness-gym-accessories_52683-100705.jpg?_wi=4", imageAlt: "Adjustable Kettlebell Set"},
{
id: "gym-5",
brand: "FitGear",
name: "Premium Yoga Mat Collection",
price: "$89.00",
rating: 5,
reviewCount: "521",
imageSrc: "http://img.b2bpic.net/free-vector/sport-landing-page-template-with-photo_23-2148217108.jpg?_wi=2",
imageAlt: "Premium Yoga Mat Collection",
},
id: "gym-5", brand: "FitGear", name: "Premium Yoga Mat Collection", price: "$89.00", rating: 5,
reviewCount: "521", imageSrc: "http://img.b2bpic.net/free-vector/sport-landing-page-template-with-photo_23-2148217108.jpg?_wi=2", imageAlt: "Premium Yoga Mat Collection"},
{
id: "gym-6",
brand: "ResistancePro",
name: "Resistance Band Set",
price: "$79.00",
rating: 4,
reviewCount: "403",
imageSrc: "http://img.b2bpic.net/free-photo/perfectly-ordered-compositions-view_23-2149872090.jpg?_wi=2",
imageAlt: "Resistance Band Set",
},
id: "gym-6", brand: "ResistancePro", name: "Resistance Band Set", price: "$79.00", rating: 4,
reviewCount: "403", imageSrc: "http://img.b2bpic.net/free-photo/perfectly-ordered-compositions-view_23-2149872090.jpg?_wi=2", imageAlt: "Resistance Band Set"},
]}
buttons={[{ text: "View All Gym Gear", href: "/gym" }]}
/>
@@ -121,12 +79,8 @@ export default function GymPage() {
<FeatureCardTen
features={[
{
id: "1",
title: "Strength Training",
description: "Premium dumbbells, barbells, and weight plates. Built for durability and precision in every lift.",
media: {
imageSrc: "http://img.b2bpic.net/free-photo/still-life-perfectly-ordered-fitness-gym-accessories_52683-100705.jpg?_wi=5",
},
id: "1", title: "Strength Training", description: "Premium dumbbells, barbells, and weight plates. Built for durability and precision in every lift.", media: {
imageSrc: "http://img.b2bpic.net/free-photo/still-life-perfectly-ordered-fitness-gym-accessories_52683-100705.jpg?_wi=5"},
items: [
{ icon: Dumbbell, text: "Professional Quality" },
{ icon: Zap, text: "High Performance" },
@@ -135,12 +89,8 @@ export default function GymPage() {
reverse: false,
},
{
id: "2",
title: "Cardio Equipment",
description: "State-of-the-art treadmills, bikes, and rowing machines. Engineered for smooth, efficient workouts.",
media: {
imageSrc: "http://img.b2bpic.net/free-vector/sport-landing-page-template-with-photo_23-2148217108.jpg?_wi=3",
},
id: "2", title: "Cardio Equipment", description: "State-of-the-art treadmills, bikes, and rowing machines. Engineered for smooth, efficient workouts.", media: {
imageSrc: "http://img.b2bpic.net/free-vector/sport-landing-page-template-with-photo_23-2148217108.jpg?_wi=3"},
items: [
{ icon: Activity, text: "Advanced Technology" },
{ icon: Zap, text: "High Durability" },
@@ -149,12 +99,8 @@ export default function GymPage() {
reverse: true,
},
{
id: "3",
title: "Functional Training",
description: "Kettlebells, resistance bands, and functional rigs. Perfect for dynamic, full-body workouts.",
media: {
imageSrc: "http://img.b2bpic.net/free-photo/perfectly-ordered-compositions-view_23-2149872090.jpg?_wi=3",
},
id: "3", title: "Functional Training", description: "Kettlebells, resistance bands, and functional rigs. Perfect for dynamic, full-body workouts.", media: {
imageSrc: "http://img.b2bpic.net/free-photo/perfectly-ordered-compositions-view_23-2149872090.jpg?_wi=3"},
items: [
{ icon: Zap, text: "Versatile Equipment" },
{ icon: Activity, text: "Space-Saving Design" },
@@ -186,35 +132,17 @@ export default function GymPage() {
useInvertedBackground={false}
faqs={[
{
id: "1",
title: "What warranty do gym equipment come with?",
content: "All equipment comes with a comprehensive 2-year warranty covering manufacturing defects. Professional equipment includes extended support options.",
},
id: "1", title: "What warranty do gym equipment come with?", content: "All equipment comes with a comprehensive 2-year warranty covering manufacturing defects. Professional equipment includes extended support options."},
{
id: "2",
title: "Do you offer assembly and installation services?",
content: "Yes, we offer professional assembly and installation for all equipment. White-glove delivery is available in select areas.",
},
id: "2", title: "Do you offer assembly and installation services?", content: "Yes, we offer professional assembly and installation for all equipment. White-glove delivery is available in select areas."},
{
id: "3",
title: "Can I rent equipment before purchasing?",
content: "We offer a 30-day trial rental program for major equipment. Rental fees can be applied toward purchase if you decide to buy.",
},
id: "3", title: "Can I rent equipment before purchasing?", content: "We offer a 30-day trial rental program for major equipment. Rental fees can be applied toward purchase if you decide to buy."},
{
id: "4",
title: "Are there financing options available?",
content: "Yes, we offer flexible financing plans with 0% APR for 12 months on purchases over $500.",
},
id: "4", title: "Are there financing options available?", content: "Yes, we offer flexible financing plans with 0% APR for 12 months on purchases over $500."},
{
id: "5",
title: "What is your return policy for gym equipment?",
content: "Equipment can be returned within 30 days if unused and in original condition. We handle return shipping for most items.",
},
id: "5", title: "What is your return policy for gym equipment?", content: "Equipment can be returned within 30 days if unused and in original condition. We handle return shipping for most items."},
{
id: "6",
title: "Do you provide maintenance and repair services?",
content: "We offer comprehensive maintenance plans and professional repair services. Contact our team for custom support options.",
},
id: "6", title: "Do you provide maintenance and repair services?", content: "We offer comprehensive maintenance plans and professional repair services. Contact our team for custom support options."},
]}
imageSrc="http://img.b2bpic.net/free-photo/woman-sitting-wheelchair-modern-concept_23-2148497283.jpg?_wi=2"
imageAlt="Customer service support team"
@@ -232,8 +160,7 @@ export default function GymPage() {
copyrightText="© 2025 ZSMX Store. All rights reserved."
columns={[
{
title: "Shop",
items: [
title: "Shop", items: [
{ label: "Fashion", href: "fashion" },
{ label: "Home", href: "home-category" },
{ label: "Gym", href: "gym" },
@@ -241,8 +168,7 @@ export default function GymPage() {
],
},
{
title: "Support",
items: [
title: "Support", items: [
{ label: "Contact Us", href: "#contact" },
{ label: "FAQ", href: "#faq" },
{ label: "Shipping Info", href: "#" },
@@ -250,8 +176,7 @@ export default function GymPage() {
],
},
{
title: "Company",
items: [
title: "Company", items: [
{ label: "About Us", href: "#about" },
{ label: "Blog", href: "#" },
{ label: "Careers", href: "#" },
@@ -263,4 +188,4 @@ export default function GymPage() {
</div>
</ThemeProvider>
);
}
}

View File

@@ -1,381 +1,37 @@
"use client";
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
import NavbarStyleFullscreen from "@/components/navbar/NavbarStyleFullscreen/NavbarStyleFullscreen";
import HeroBillboardTestimonial from "@/components/sections/hero/HeroBillboardTestimonial";
import ProductCardTwo from "@/components/sections/product/ProductCardTwo";
import FeatureCardTen from "@/components/sections/feature/FeatureCardTen";
import MetricSplitMediaAbout from "@/components/sections/about/MetricSplitMediaAbout";
import MetricCardSeven from "@/components/sections/metrics/MetricCardSeven";
import SocialProofOne from "@/components/sections/socialProof/SocialProofOne";
import FooterBase from "@/components/sections/footer/FooterBase";
import Link from "next/link";
import { Sparkles, Star, Grid, Award, TrendingUp, Briefcase, Home, Shirt, Sofa, Layout, Dumbbell, Activity, Zap, Smartphone, Cpu } from "lucide-react";
import { ThemeProvider } from '@/providers/themeProvider/ThemeProvider';
import NavbarStyleFullscreen from '@/components/navbar/NavbarStyleFullscreen/NavbarStyleFullscreen';
export default function HomeCategoryPage() {
export default function HomePage() {
return (
<ThemeProvider
defaultButtonVariant="hover-magnetic"
defaultTextAnimation="reveal-blur"
defaultButtonVariant="text-stagger"
defaultTextAnimation="entrance-slide"
borderRadius="rounded"
contentWidth="smallMedium"
sizing="mediumLargeSizeLargeTitles"
background="floatingGradient"
cardStyle="glass-depth"
primaryButtonStyle="double-inset"
secondaryButtonStyle="radial-glow"
headingFontWeight="extrabold"
contentWidth="medium"
sizing="medium"
background="circleGradient"
cardStyle="glass-elevated"
primaryButtonStyle="gradient"
secondaryButtonStyle="glass"
headingFontWeight="normal"
>
{/* Navbar */}
<div id="nav" data-section="nav">
<NavbarStyleFullscreen
navItems={[
{ name: "Home", id: "/" },
{ name: "Fashion", id: "fashion" },
{ name: "Home", id: "home-category" },
{ name: "Home Decor", id: "home-category" },
{ name: "Gym", id: "gym" },
{ name: "Electronics", id: "electronics" },
{ name: "Contact", id: "contact" },
{ name: "Electronics", id: "electronics" }
]}
brandName="ZSMX Store"
bottomLeftText="Premium Multi-Category Store"
bottomRightText="hello@zsmxstore.com"
/>
</div>
{/* Hero Section */}
<div id="hero" data-section="hero">
<HeroBillboardTestimonial
title="Discover Your Perfect Style"
description="Explore our curated collection of fashion, home decor, fitness equipment, and premium electronics. Where quality meets elegance."
tag="Welcome to ZSMX Store"
tagIcon={Sparkles}
tagAnimation="slide-up"
background={{ variant: "floatingGradient" }}
imageSrc="http://img.b2bpic.net/free-photo/internationals-people-standing-cafe_1157-32402.jpg?_wi=2"
imageAlt="Premium multi-category product showcase"
mediaAnimation="slide-up"
testimonials={[
{
name: "Sarah Mitchell",
handle: "Fashion Enthusiast",
testimonial: "Exceptional quality and stunning designs. ZSMX Store has become my go-to for everything.",
rating: 5,
imageSrc: "http://img.b2bpic.net/free-photo/portrait-confident-young-businessman-with-his-arms-crossed_23-2148176206.jpg?_wi=2",
imageAlt: "Sarah Mitchell",
},
{
name: "James Chen",
handle: "Interior Designer",
testimonial: "The home collection is absolutely exquisite. Premium pieces that transform any space.",
rating: 5,
imageSrc: "http://img.b2bpic.net/free-photo/positive-confident-businessman-posing-outside_74855-1183.jpg?_wi=2",
imageAlt: "James Chen",
},
{
name: "Emma Rodriguez",
handle: "Fitness Coach",
testimonial: "Top-tier gym equipment. My clients and I love the durability and design.",
rating: 5,
imageSrc: "http://img.b2bpic.net/free-photo/modern-businesswoman_23-2148012909.jpg?_wi=2",
imageAlt: "Emma Rodriguez",
},
]}
testimonialRotationInterval={5000}
buttons={[
{ text: "Shop Now", href: "/home" },
{ text: "Explore Categories", href: "/home" },
]}
buttonAnimation="slide-up"
useInvertedBackground={false}
/>
</div>
{/* Featured Products Section */}
<div id="products" data-section="products">
<ProductCardTwo
title="Featured Collection"
description="Hand-picked premium products across all categories. New arrivals updated daily."
tag="Best Sellers"
tagIcon={Star}
tagAnimation="slide-up"
textboxLayout="default"
animationType="slide-up"
gridVariant="bento-grid"
useInvertedBackground={false}
products={[
{
id: "fashion-1",
brand: "LuxeStyle",
name: "Premium Wool Overcoat",
price: "$450.00",
rating: 5,
reviewCount: "342",
imageSrc: "http://img.b2bpic.net/free-photo/bag-hanging-from-furniture-item-indoors_23-2151073505.jpg?_wi=7",
imageAlt: "Premium Wool Overcoat",
},
{
id: "fashion-2",
brand: "ElegantWear",
name: "Designer Evening Gown",
price: "$680.00",
rating: 5,
reviewCount: "289",
imageSrc: "http://img.b2bpic.net/free-photo/store-customer-holding-shirt-body_482257-85803.jpg?_wi=6",
imageAlt: "Designer Evening Gown",
},
{
id: "fashion-3",
brand: "ClassicThreads",
name: "Italian Leather Shoes",
price: "$395.00",
rating: 4,
reviewCount: "156",
imageSrc: "http://img.b2bpic.net/free-photo/still-life-with-classic-shirts_23-2150828626.jpg?_wi=6",
imageAlt: "Italian Leather Shoes",
},
{
id: "home-1",
brand: "Luxehome",
name: "Modern Sectional Sofa",
price: "$1,299.00",
rating: 5,
reviewCount: "201",
imageSrc: "http://img.b2bpic.net/free-photo/beautiful-dried-flowers-table_23-2149591635.jpg?_wi=3",
imageAlt: "Modern Sectional Sofa",
},
{
id: "home-2",
brand: "DecorPremium",
name: "Crystal Chandelier",
price: "$850.00",
rating: 5,
reviewCount: "178",
imageSrc: "http://img.b2bpic.net/free-photo/couch-with-cushions-glass-table_1203-764.jpg?_wi=2",
imageAlt: "Crystal Chandelier",
},
{
id: "home-3",
brand: "InteriorLux",
name: "Turkish Area Rug",
price: "$625.00",
rating: 4,
reviewCount: "124",
imageSrc: "http://img.b2bpic.net/free-photo/cafe-with-coffee-tables-cosy-sofas-plants-shelves_140725-7785.jpg?_wi=2",
imageAlt: "Turkish Area Rug",
},
]}
buttons={[{ text: "View All Products", href: "/home" }]}
/>
</div>
{/* Category Showcase Section */}
<div id="categories" data-section="categories">
<FeatureCardTen
features={[
{
id: "1",
title: "Fashion Excellence",
description: "Premium apparel and accessories designed for those who appreciate style. From casual elegance to formal sophistication.",
media: {
imageSrc: "http://img.b2bpic.net/free-photo/bag-hanging-from-furniture-item-indoors_23-2151073505.jpg?_wi=8",
},
items: [
{ icon: Shirt, text: "Designer Collections" },
{ icon: Sparkles, text: "Premium Fabrics" },
{ icon: Sparkles, text: "Timeless Styles" },
],
reverse: false,
},
{
id: "2",
title: "Home Furnishings",
description: "Transform your living space with luxury home decor. Curated pieces that combine functionality with aesthetic elegance.",
media: {
imageSrc: "http://img.b2bpic.net/free-photo/beautiful-dried-flowers-table_23-2149591635.jpg?_wi=4",
},
items: [
{ icon: Home, text: "Modern Design" },
{ icon: Sofa, text: "Premium Materials" },
{ icon: Layout, text: "Expert Curation" },
],
reverse: true,
},
{
id: "3",
title: "Fitness & Gym",
description: "Professional-grade fitness equipment and apparel. Engineered for performance and durability in every workout.",
media: {
imageSrc: "http://img.b2bpic.net/free-photo/still-life-perfectly-ordered-fitness-gym-accessories_52683-100705.jpg?_wi=2",
},
items: [
{ icon: Dumbbell, text: "Professional Equipment" },
{ icon: Activity, text: "Performance Gear" },
{ icon: Zap, text: "High Durability" },
],
reverse: false,
},
{
id: "4",
title: "Premium Electronics",
description: "Latest technology and innovative gadgets. Cutting-edge devices that enhance your digital lifestyle.",
media: {
imageSrc: "http://img.b2bpic.net/free-photo/view-robotic-vacuum-cleaner-flat-surface_23-2151736769.jpg?_wi=2",
},
items: [
{ icon: Smartphone, text: "Latest Technology" },
{ icon: Cpu, text: "Advanced Features" },
{ icon: Zap, text: "Top Performance" },
],
reverse: true,
},
]}
title="Our Category Showcase"
description="Explore our diverse range of premium products across four expertly curated categories. Each collection represents the finest in quality and design."
tag="Categories"
tagIcon={Grid}
tagAnimation="slide-up"
textboxLayout="default"
animationType="slide-up"
useInvertedBackground={false}
/>
</div>
{/* About Section */}
<div id="about" data-section="about">
<MetricSplitMediaAbout
title="Your Trusted Multi-Category Destination"
description="ZSMX Store is your premier destination for premium products across fashion, home, fitness, and electronics. We believe in delivering excellence through carefully curated collections, exceptional quality, and outstanding customer service. Our mission is to make luxury and quality accessible to everyone."
tag="About ZSMX"
tagIcon={Award}
tagAnimation="slide-up"
metrics={[
{ value: "50k+", title: "Satisfied Customers" },
{ value: "10k+", title: "Premium Products" },
]}
imageSrc="http://img.b2bpic.net/free-photo/modern-sauna-with-panoramic-windows-wooden-design_169016-70021.jpg?_wi=2"
imageAlt="ZSMX Store - Premium retail environment"
useInvertedBackground={true}
mediaAnimation="slide-up"
/>
</div>
{/* Metrics Section */}
<div id="metrics" data-section="metrics">
<MetricCardSeven
metrics={[
{
id: "1",
value: "98%",
title: "Customer Satisfaction Rate",
items: [
"Premium quality guaranteed",
"Expert curation",
"Dedicated support team",
],
},
{
id: "2",
value: "24/7",
title: "Customer Support Available",
items: [
"Real-time assistance",
"Expert consultations",
"Fast responses",
],
},
{
id: "3",
value: "100%",
title: "Authentic Products",
items: [
"Verified sources",
"Quality assurance",
"Brand authenticity",
],
},
{
id: "4",
value: "Free",
title: "Shipping On Orders Over $100",
items: [
"Fast delivery",
"Tracking included",
"Safe packaging",
],
},
]}
title="By The Numbers"
description="Trusted by thousands of customers worldwide. Our commitment to quality and service speaks for itself."
tag="Our Growth"
tagIcon={TrendingUp}
tagAnimation="slide-up"
textboxLayout="default"
animationType="slide-up"
useInvertedBackground={false}
/>
</div>
{/* Social Proof Section */}
<div id="social-proof" data-section="social-proof">
<SocialProofOne
title="Trusted by Leading Brands & Retailers"
description="Partnered with premium brands worldwide to bring you authentic luxury products."
tag="Our Partners"
tagIcon={Briefcase}
tagAnimation="slide-up"
textboxLayout="default"
useInvertedBackground={false}
names={[
"LuxeStyle",
"ElegantWear",
"ClassicThreads",
"Luxehome",
"DecorPremium",
"InteriorLux",
"FitnessPro",
"SportsTech",
]}
speed={40}
showCard={true}
/>
</div>
{/* Footer */}
<div id="footer" data-section="footer">
<FooterBase
logoText="ZSMX Store"
copyrightText="© 2025 ZSMX Store. All rights reserved."
columns={[
{
title: "Shop",
items: [
{ label: "Fashion", href: "fashion" },
{ label: "Home", href: "home-category" },
{ label: "Gym", href: "gym" },
{ label: "Electronics", href: "electronics" },
],
},
{
title: "Support",
items: [
{ label: "Contact Us", href: "#contact" },
{ label: "FAQ", href: "#faq" },
{ label: "Shipping Info", href: "#" },
{ label: "Returns", href: "#" },
],
},
{
title: "Company",
items: [
{ label: "About Us", href: "#about" },
{ label: "Blog", href: "#" },
{ label: "Careers", href: "#" },
{ label: "Privacy Policy", href: "#" },
],
},
]}
/>
</div>
<div>Home Page</div>
</ThemeProvider>
);
}
}

View File

@@ -1,44 +1,43 @@
"use client";
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
import NavbarStyleFullscreen from "@/components/navbar/NavbarStyleFullscreen/NavbarStyleFullscreen";
import HeroBillboardTestimonial from "@/components/sections/hero/HeroBillboardTestimonial";
import ProductCardTwo from "@/components/sections/product/ProductCardTwo";
import FeatureCardTen from "@/components/sections/feature/FeatureCardTen";
import MetricSplitMediaAbout from "@/components/sections/about/MetricSplitMediaAbout";
import MetricCardSeven from "@/components/sections/metrics/MetricCardSeven";
import SocialProofOne from "@/components/sections/socialProof/SocialProofOne";
import TestimonialCardFifteen from "@/components/sections/testimonial/TestimonialCardFifteen";
import FaqSplitMedia from "@/components/sections/faq/FaqSplitMedia";
import ContactCTA from "@/components/sections/contact/ContactCTA";
import FooterBase from "@/components/sections/footer/FooterBase";
import Link from "next/link";
import { Sparkles, Star, Grid, Award, TrendingUp, Briefcase, Mail, HelpCircle, Shirt, Heart, Home, Sofa, Layout, Dumbbell, Activity, Zap, Smartphone, Cpu } from "lucide-react";
import { ThemeProvider } from '@/providers/themeProvider/ThemeProvider';
import NavbarStyleFullscreen from '@/components/navbar/NavbarStyleFullscreen/NavbarStyleFullscreen';
import HeroBillboardTestimonial from '@/components/sections/hero/HeroBillboardTestimonial';
import ProductCardTwo from '@/components/sections/product/ProductCardTwo';
import FeatureCardTen from '@/components/sections/feature/FeatureCardTen';
import MetricSplitMediaAbout from '@/components/sections/about/MetricSplitMediaAbout';
import MetricCardSeven from '@/components/sections/metrics/MetricCardSeven';
import SocialProofOne from '@/components/sections/socialProof/SocialProofOne';
import TestimonialCardFifteen from '@/components/sections/testimonial/TestimonialCardFifteen';
import FaqSplitMedia from '@/components/sections/faq/FaqSplitMedia';
import ContactCTA from '@/components/sections/contact/ContactCTA';
import FooterBase from '@/components/sections/footer/FooterBase';
import { Sparkles, Star, Grid, Award, TrendingUp, Briefcase, Mail, HelpCircle, Shirt, Dumbbell, Activity, Zap, Smartphone, Cpu, Home, Sofa, Layout, Heart } from 'lucide-react';
export default function HomePage() {
return (
<ThemeProvider
defaultButtonVariant="hover-magnetic"
defaultTextAnimation="reveal-blur"
defaultButtonVariant="text-stagger"
defaultTextAnimation="entrance-slide"
borderRadius="rounded"
contentWidth="smallMedium"
sizing="mediumLargeSizeLargeTitles"
background="floatingGradient"
cardStyle="glass-depth"
primaryButtonStyle="double-inset"
secondaryButtonStyle="radial-glow"
headingFontWeight="extrabold"
contentWidth="medium"
sizing="medium"
background="circleGradient"
cardStyle="glass-elevated"
primaryButtonStyle="gradient"
secondaryButtonStyle="glass"
headingFontWeight="normal"
>
<div id="nav" data-section="nav">
<NavbarStyleFullscreen
brandName="ZSMX Store"
navItems={[
{ name: "Home", id: "/" },
{ name: "Fashion", id: "fashion" },
{ name: "Home", id: "home-category" },
{ name: "Home Decor", id: "home-category" },
{ name: "Gym", id: "gym" },
{ name: "Electronics", id: "electronics" },
{ name: "Contact", id: "contact" },
{ name: "Electronics", id: "electronics" }
]}
brandName="ZSMX Store"
bottomLeftText="Premium Multi-Category Store"
bottomRightText="hello@zsmxstore.com"
/>
@@ -51,40 +50,28 @@ export default function HomePage() {
tag="Welcome to ZSMX Store"
tagIcon={Sparkles}
tagAnimation="slide-up"
background={{ variant: "floatingGradient" }}
imageSrc="http://img.b2bpic.net/free-photo/internationals-people-standing-cafe_1157-32402.jpg?_wi=1"
background={{ variant: "radial-gradient" }}
imageSrc="http://img.b2bpic.net/free-photo/internationals-people-standing-cafe_1157-32402.jpg"
imageAlt="Premium multi-category product showcase"
mediaAnimation="slide-up"
testimonials={[
{
name: "Sarah Mitchell",
handle: "Fashion Enthusiast",
testimonial: "Exceptional quality and stunning designs. ZSMX Store has become my go-to for everything.",
rating: 5,
imageSrc: "http://img.b2bpic.net/free-photo/portrait-confident-young-businessman-with-his-arms-crossed_23-2148176206.jpg?_wi=1",
imageAlt: "Sarah Mitchell",
name: "Sarah Mitchell", handle: "Fashion Enthusiast", testimonial: "Exceptional quality and stunning designs. ZSMX Store has become my go-to for everything.", rating: 5,
imageSrc: "http://img.b2bpic.net/free-photo/portrait-confident-young-businessman-with-his-arms-crossed_23-2148176206.jpg", imageAlt: "Sarah Mitchell"
},
{
name: "James Chen",
handle: "Interior Designer",
testimonial: "The home collection is absolutely exquisite. Premium pieces that transform any space.",
rating: 5,
imageSrc: "http://img.b2bpic.net/free-photo/positive-confident-businessman-posing-outside_74855-1183.jpg?_wi=1",
imageAlt: "James Chen",
name: "James Chen", handle: "Interior Designer", testimonial: "The home collection is absolutely exquisite. Premium pieces that transform any space.", rating: 5,
imageSrc: "http://img.b2bpic.net/free-photo/positive-confident-businessman-posing-outside_74855-1183.jpg", imageAlt: "James Chen"
},
{
name: "Emma Rodriguez",
handle: "Fitness Coach",
testimonial: "Top-tier gym equipment. My clients and I love the durability and design.",
rating: 5,
imageSrc: "http://img.b2bpic.net/free-photo/modern-businesswoman_23-2148012909.jpg?_wi=1",
imageAlt: "Emma Rodriguez",
},
name: "Emma Rodriguez", handle: "Fitness Coach", testimonial: "Top-tier gym equipment. My clients and I love the durability and design.", rating: 5,
imageSrc: "http://img.b2bpic.net/free-photo/modern-businesswoman_23-2148012909.jpg", imageAlt: "Emma Rodriguez"
}
]}
testimonialRotationInterval={5000}
buttons={[
{ text: "Shop Now", href: "/fashion" },
{ text: "Explore Categories", href: "#categories" },
{ text: "Shop Now", href: "fashion" },
{ text: "Explore Categories", href: "features" }
]}
buttonAnimation="slide-up"
useInvertedBackground={false}
@@ -104,67 +91,27 @@ export default function HomePage() {
useInvertedBackground={false}
products={[
{
id: "fashion-1",
brand: "LuxeStyle",
name: "Premium Wool Overcoat",
price: "$450.00",
rating: 5,
reviewCount: "342",
imageSrc: "http://img.b2bpic.net/free-photo/bag-hanging-from-furniture-item-indoors_23-2151073505.jpg?_wi=1",
imageAlt: "Premium Wool Overcoat",
id: "fashion-1", brand: "LuxeStyle", name: "Premium Wool Overcoat", price: "$450.00", rating: 5, reviewCount: "342", imageSrc: "http://img.b2bpic.net/free-photo/bag-hanging-from-furniture-item-indoors_23-2151073505.jpg", imageAlt: "Premium Wool Overcoat"
},
{
id: "fashion-2",
brand: "ElegantWear",
name: "Designer Evening Gown",
price: "$680.00",
rating: 5,
reviewCount: "289",
imageSrc: "http://img.b2bpic.net/free-photo/store-customer-holding-shirt-body_482257-85803.jpg?_wi=1",
imageAlt: "Designer Evening Gown",
id: "fashion-2", brand: "ElegantWear", name: "Designer Evening Gown", price: "$680.00", rating: 5, reviewCount: "289", imageSrc: "http://img.b2bpic.net/free-photo/store-customer-holding-shirt-body_482257-85803.jpg", imageAlt: "Designer Evening Gown"
},
{
id: "fashion-3",
brand: "ClassicThreads",
name: "Italian Leather Shoes",
price: "$395.00",
rating: 4,
reviewCount: "156",
imageSrc: "http://img.b2bpic.net/free-photo/still-life-with-classic-shirts_23-2150828626.jpg?_wi=1",
imageAlt: "Italian Leather Shoes",
id: "fashion-3", brand: "ClassicThreads", name: "Italian Leather Shoes", price: "$395.00", rating: 4, reviewCount: "156", imageSrc: "http://img.b2bpic.net/free-photo/still-life-with-classic-shirts_23-2150828626.jpg", imageAlt: "Italian Leather Shoes"
},
{
id: "home-1",
brand: "Luxehome",
name: "Modern Sectional Sofa",
price: "$1,299.00",
rating: 5,
reviewCount: "201",
imageSrc: "http://img.b2bpic.net/free-photo/beautiful-dried-flowers-table_23-2149591635.jpg?_wi=1",
imageAlt: "Modern Sectional Sofa",
id: "home-1", brand: "Luxehome", name: "Modern Sectional Sofa", price: "$1,299.00", rating: 5, reviewCount: "201", imageSrc: "http://img.b2bpic.net/free-photo/beautiful-dried-flowers-table_23-2149591635.jpg", imageAlt: "Modern Sectional Sofa"
},
{
id: "home-2",
brand: "DecorPremium",
name: "Crystal Chandelier",
price: "$850.00",
rating: 5,
reviewCount: "178",
imageSrc: "http://img.b2bpic.net/free-photo/couch-with-cushions-glass-table_1203-764.jpg?_wi=1",
imageAlt: "Crystal Chandelier",
id: "home-2", brand: "DecorPremium", name: "Crystal Chandelier", price: "$850.00", rating: 5, reviewCount: "178", imageSrc: "http://img.b2bpic.net/free-photo/couch-with-cushions-glass-table_1203-764.jpg", imageAlt: "Crystal Chandelier"
},
{
id: "home-3",
brand: "InteriorLux",
name: "Turkish Area Rug",
price: "$625.00",
rating: 4,
reviewCount: "124",
imageSrc: "http://img.b2bpic.net/free-photo/cafe-with-coffee-tables-cosy-sofas-plants-shelves_140725-7785.jpg?_wi=1",
imageAlt: "Turkish Area Rug",
},
id: "home-3", brand: "InteriorLux", name: "Turkish Area Rug", price: "$625.00", rating: 4, reviewCount: "124", imageSrc: "http://img.b2bpic.net/free-photo/cafe-with-coffee-tables-cosy-sofas-plants-shelves_140725-7785.jpg", imageAlt: "Turkish Area Rug"
}
]}
buttons={[
{ text: "View All Products", href: "fashion" }
]}
buttons={[{ text: "View All Products", href: "/fashion" }]}
/>
</div>
@@ -180,61 +127,41 @@ export default function HomePage() {
useInvertedBackground={false}
features={[
{
id: "1",
title: "Fashion Excellence",
description: "Premium apparel and accessories designed for those who appreciate style. From casual elegance to formal sophistication.",
media: {
imageSrc: "http://img.b2bpic.net/free-photo/bag-hanging-from-furniture-item-indoors_23-2151073505.jpg?_wi=2",
},
id: "1", title: "Fashion Excellence", description: "Premium apparel and accessories designed for those who appreciate style. From casual elegance to formal sophistication.", media: { imageSrc: "http://img.b2bpic.net/free-photo/bag-hanging-from-furniture-item-indoors_23-2151073505.jpg" },
items: [
{ icon: Shirt, text: "Designer Collections" },
{ icon: Sparkles, text: "Premium Fabrics" },
{ icon: Heart, text: "Timeless Styles" },
{ icon: Heart, text: "Timeless Styles" }
],
reverse: false,
reverse: false
},
{
id: "2",
title: "Home Furnishings",
description: "Transform your living space with luxury home decor. Curated pieces that combine functionality with aesthetic elegance.",
media: {
imageSrc: "http://img.b2bpic.net/free-photo/beautiful-dried-flowers-table_23-2149591635.jpg?_wi=2",
},
id: "2", title: "Home Furnishings", description: "Transform your living space with luxury home decor. Curated pieces that combine functionality with aesthetic elegance.", media: { imageSrc: "http://img.b2bpic.net/free-photo/beautiful-dried-flowers-table_23-2149591635.jpg" },
items: [
{ icon: Home, text: "Modern Design" },
{ icon: Sofa, text: "Premium Materials" },
{ icon: Layout, text: "Expert Curation" },
{ icon: Layout, text: "Expert Curation" }
],
reverse: true,
reverse: true
},
{
id: "3",
title: "Fitness & Gym",
description: "Professional-grade fitness equipment and apparel. Engineered for performance and durability in every workout.",
media: {
imageSrc: "http://img.b2bpic.net/free-photo/still-life-perfectly-ordered-fitness-gym-accessories_52683-100705.jpg?_wi=1",
},
id: "3", title: "Fitness & Gym", description: "Professional-grade fitness equipment and apparel. Engineered for performance and durability in every workout.", media: { imageSrc: "http://img.b2bpic.net/free-photo/still-life-perfectly-ordered-fitness-gym-accessories_52683-100705.jpg" },
items: [
{ icon: Dumbbell, text: "Professional Equipment" },
{ icon: Activity, text: "Performance Gear" },
{ icon: Zap, text: "High Durability" },
{ icon: Zap, text: "High Durability" }
],
reverse: false,
reverse: false
},
{
id: "4",
title: "Premium Electronics",
description: "Latest technology and innovative gadgets. Cutting-edge devices that enhance your digital lifestyle.",
media: {
imageSrc: "http://img.b2bpic.net/free-photo/view-robotic-vacuum-cleaner-flat-surface_23-2151736769.jpg?_wi=1",
},
id: "4", title: "Premium Electronics", description: "Latest technology and innovative gadgets. Cutting-edge devices that enhance your digital lifestyle.", media: { imageSrc: "http://img.b2bpic.net/free-photo/view-robotic-vacuum-cleaner-flat-surface_23-2151736769.jpg" },
items: [
{ icon: Smartphone, text: "Latest Technology" },
{ icon: Cpu, text: "Advanced Features" },
{ icon: Zap, text: "Top Performance" },
{ icon: Zap, text: "Top Performance" }
],
reverse: true,
},
reverse: true
}
]}
/>
</div>
@@ -248,12 +175,13 @@ export default function HomePage() {
tagAnimation="slide-up"
metrics={[
{ value: "50k+", title: "Satisfied Customers" },
{ value: "10k+", title: "Premium Products" },
{ value: "10k+", title: "Premium Products" }
]}
imageSrc="http://img.b2bpic.net/free-photo/modern-sauna-with-panoramic-windows-wooden-design_169016-70021.jpg?_wi=1"
imageSrc="http://img.b2bpic.net/free-photo/modern-sauna-with-panoramic-windows-wooden-design_169016-70021.jpg"
imageAlt="ZSMX Store - Premium retail environment"
useInvertedBackground={true}
mediaAnimation="slide-up"
metricsAnimation="slide-up"
/>
</div>
@@ -269,33 +197,17 @@ export default function HomePage() {
useInvertedBackground={false}
metrics={[
{
id: "1",
value: "98%",
title: "Customer Satisfaction Rate",
items: [
"Premium quality guaranteed",
"Expert curation",
"Dedicated support team",
],
id: "1", value: "98%", title: "Customer Satisfaction Rate", items: ["Premium quality guaranteed", "Expert curation", "Dedicated support team"]
},
{
id: "2",
value: "24/7",
title: "Customer Support Available",
items: ["Real-time assistance", "Expert consultations", "Fast responses"],
id: "2", value: "24/7", title: "Customer Support Available", items: ["Real-time assistance", "Expert consultations", "Fast responses"]
},
{
id: "3",
value: "100%",
title: "Authentic Products",
items: ["Verified sources", "Quality assurance", "Brand authenticity"],
id: "3", value: "100%", title: "Authentic Products", items: ["Verified sources", "Quality assurance", "Brand authenticity"]
},
{
id: "4",
value: "Free",
title: "Shipping On Orders Over $100",
items: ["Fast delivery", "Tracking included", "Safe packaging"],
},
id: "4", value: "Free", title: "Shipping On Orders Over $100", items: ["Fast delivery", "Tracking included", "Safe packaging"]
}
]}
/>
</div>
@@ -309,16 +221,7 @@ export default function HomePage() {
tagAnimation="slide-up"
textboxLayout="default"
useInvertedBackground={false}
names={[
"LuxeStyle",
"ElegantWear",
"ClassicThreads",
"Luxehome",
"DecorPremium",
"InteriorLux",
"FitnessPro",
"SportsTech",
]}
names={["LuxeStyle", "ElegantWear", "ClassicThreads", "Luxehome", "DecorPremium", "InteriorLux", "FitnessPro", "SportsTech"]}
speed={40}
showCard={true}
/>
@@ -330,30 +233,12 @@ export default function HomePage() {
rating={5}
author="Victoria Thompson, Premium Lifestyle Enthusiast"
avatars={[
{
src: "http://img.b2bpic.net/free-photo/portrait-confident-young-businessman-with-his-arms-crossed_23-2148176206.jpg",
alt: "Customer 1",
},
{
src: "http://img.b2bpic.net/free-photo/positive-confident-businessman-posing-outside_74855-1183.jpg",
alt: "Customer 2",
},
{
src: "http://img.b2bpic.net/free-photo/modern-businesswoman_23-2148012909.jpg",
alt: "Customer 3",
},
{
src: "http://img.b2bpic.net/free-photo/businessman-formal-wear-professional-corporate-concept_53876-71166.jpg",
alt: "Customer 4",
},
{
src: "http://img.b2bpic.net/free-photo/beautiful-business-woman-portrait_23-2149280717.jpg",
alt: "Customer 5",
},
{
src: "http://img.b2bpic.net/free-photo/portrait-outdoors-business-man-smiles_23-2148763856.jpg",
alt: "Customer 6",
},
{ src: "http://img.b2bpic.net/free-photo/portrait-confident-young-businessman-with-his-arms-crossed_23-2148176206.jpg", alt: "Customer 1" },
{ src: "http://img.b2bpic.net/free-photo/positive-confident-businessman-posing-outside_74855-1183.jpg", alt: "Customer 2" },
{ src: "http://img.b2bpic.net/free-photo/modern-businesswoman_23-2148012909.jpg", alt: "Customer 3" },
{ src: "http://img.b2bpic.net/free-photo/businessman-formal-wear-professional-corporate-concept_53876-71166.jpg", alt: "Customer 4" },
{ src: "http://img.b2bpic.net/free-photo/beautiful-business-woman-portrait_23-2149280717.jpg", alt: "Customer 5" },
{ src: "http://img.b2bpic.net/free-photo/portrait-outdoors-business-man-smiles_23-2148763856.jpg", alt: "Customer 6" }
]}
ratingAnimation="slide-up"
avatarsAnimation="slide-up"
@@ -372,43 +257,25 @@ export default function HomePage() {
useInvertedBackground={false}
faqs={[
{
id: "1",
title: "Are all products authentic and guaranteed?",
content:
"Yes, absolutely. We source directly from authorized distributors and verify authenticity of all products. Every item comes with our quality guarantee and certification of authenticity.",
id: "1", title: "Are all products authentic and guaranteed?", content: "Yes, absolutely. We source directly from authorized distributors and verify authenticity of all products. Every item comes with our quality guarantee and certification of authenticity."
},
{
id: "2",
title: "What is your return and exchange policy?",
content:
"We offer hassle-free returns and exchanges within 30 days of purchase. Items must be unused and in original packaging. Simply contact our customer service team to initiate the process.",
id: "2", title: "What is your return and exchange policy?", content: "We offer hassle-free returns and exchanges within 30 days of purchase. Items must be unused and in original packaging. Simply contact our customer service team to initiate the process."
},
{
id: "3",
title: "How long does shipping typically take?",
content:
"Standard shipping takes 5-7 business days. Express shipping options (2-3 days) are available for most orders. Orders over $100 qualify for free standard shipping.",
id: "3", title: "How long does shipping typically take?", content: "Standard shipping takes 5-7 business days. Express shipping options (2-3 days) are available for most orders. Orders over $100 qualify for free standard shipping."
},
{
id: "4",
title: "Do you offer international shipping?",
content:
"Yes, we ship to most countries worldwide. International shipping costs vary by location and are calculated at checkout. Customs duties may apply depending on your country.",
id: "4", title: "Do you offer international shipping?", content: "Yes, we ship to most countries worldwide. International shipping costs vary by location and are calculated at checkout. Customs duties may apply depending on your country."
},
{
id: "5",
title: "Is my personal information secure?",
content:
"We use industry-standard SSL encryption to protect all personal and payment information. Your data is never shared with third parties. We comply with all privacy regulations.",
id: "5", title: "Is my personal information secure?", content: "We use industry-standard SSL encryption to protect all personal and payment information. Your data is never shared with third parties. We comply with all privacy regulations."
},
{
id: "6",
title: "What payment methods do you accept?",
content:
"We accept all major credit cards, debit cards, PayPal, Apple Pay, and Google Pay. All transactions are secure and encrypted.",
},
id: "6", title: "What payment methods do you accept?", content: "We accept all major credit cards, debit cards, PayPal, Apple Pay, and Google Pay. All transactions are secure and encrypted."
}
]}
imageSrc="http://img.b2bpic.net/free-photo/woman-sitting-wheelchair-modern-concept_23-2148497283.jpg?_wi=1"
imageSrc="http://img.b2bpic.net/free-photo/woman-sitting-wheelchair-modern-concept_23-2148497283.jpg"
imageAlt="Customer service support team"
mediaAnimation="slide-up"
faqsAnimation="slide-up"
@@ -425,8 +292,8 @@ export default function HomePage() {
title="Ready to Discover Premium Products?"
description="Have questions about our products or services? Our expert team is here to help. Contact us today and experience the ZSMX Store difference."
buttons={[
{ text: "Contact Our Team", href: "mailto:hello@zsmxstore.com" },
{ text: "Shop Now", href: "/fashion" },
{ text: "Contact Our Team", href: "#contact" },
{ text: "Shop Now", href: "fashion" }
]}
buttonAnimation="slide-up"
background={{ variant: "plain" }}
@@ -440,35 +307,32 @@ export default function HomePage() {
copyrightText="© 2025 ZSMX Store. All rights reserved."
columns={[
{
title: "Shop",
items: [
{ label: "Fashion", href: "/fashion" },
{ label: "Home", href: "#home-category" },
{ label: "Gym", href: "#gym" },
{ label: "Electronics", href: "#electronics" },
],
title: "Shop", items: [
{ label: "Fashion", href: "fashion" },
{ label: "Home", href: "home-category" },
{ label: "Gym", href: "gym" },
{ label: "Electronics", href: "electronics" }
]
},
{
title: "Support",
items: [
title: "Support", items: [
{ label: "Contact Us", href: "#contact" },
{ label: "FAQ", href: "#faq" },
{ label: "Shipping Info", href: "#" },
{ label: "Returns", href: "#" },
],
{ label: "Returns", href: "#" }
]
},
{
title: "Company",
items: [
title: "Company", items: [
{ label: "About Us", href: "#about" },
{ label: "Blog", href: "#" },
{ label: "Careers", href: "#" },
{ label: "Privacy Policy", href: "#" },
],
},
{ label: "Privacy Policy", href: "#" }
]
}
]}
/>
</div>
</ThemeProvider>
);
}
}

View File

@@ -1,118 +1,23 @@
import { useEffect, useState, useRef, RefObject } from "react";
import { useEffect, useState } from 'react';
const MOBILE_BREAKPOINT = 768;
const ANIMATION_SPEED = 0.05;
const ROTATION_SPEED = 0.1;
const MOUSE_MULTIPLIER = 0.5;
const ROTATION_MULTIPLIER = 0.25;
interface UseDepth3DAnimationProps {
itemRefs: RefObject<(HTMLElement | null)[]>;
containerRef: RefObject<HTMLDivElement | null>;
perspectiveRef?: RefObject<HTMLDivElement | null>;
isEnabled: boolean;
interface Depth3DConfig {
rotateX?: number;
rotateY?: number;
scale?: number;
perspective?: number;
}
export const useDepth3DAnimation = ({
itemRefs,
containerRef,
perspectiveRef,
isEnabled,
}: UseDepth3DAnimationProps) => {
const [isMobile, setIsMobile] = useState(false);
const useDepth3DAnimation = (config: Depth3DConfig = {}) => {
const [transform, setTransform] = useState<string>('');
const { rotateX = 0, rotateY = 0, scale = 1, perspective = 1000 } = config;
// Detect mobile viewport
useEffect(() => {
const checkMobile = () => {
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
};
const transformValue = `perspective(${perspective}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale(${scale})`;
setTransform(transformValue);
}, [rotateX, rotateY, scale, perspective]);
checkMobile();
window.addEventListener("resize", checkMobile);
return () => {
window.removeEventListener("resize", checkMobile);
};
}, []);
// 3D mouse-tracking effect (desktop only)
useEffect(() => {
if (!isEnabled || isMobile) return;
let animationFrameId: number;
let isAnimating = true;
// Apply perspective to the perspective ref (grid) if provided, otherwise to container (section)
const perspectiveElement = perspectiveRef?.current || containerRef.current;
if (perspectiveElement) {
perspectiveElement.style.perspective = "1200px";
perspectiveElement.style.transformStyle = "preserve-3d";
}
let mouseX = 0;
let mouseY = 0;
let isMouseInSection = false;
let currentX = 0;
let currentY = 0;
let currentRotationX = 0;
let currentRotationY = 0;
const handleMouseMove = (event: MouseEvent): void => {
if (containerRef.current) {
const rect = containerRef.current.getBoundingClientRect();
isMouseInSection =
event.clientX >= rect.left &&
event.clientX <= rect.right &&
event.clientY >= rect.top &&
event.clientY <= rect.bottom;
}
if (isMouseInSection) {
mouseX = (event.clientX / window.innerWidth) * 100 - 50;
mouseY = (event.clientY / window.innerHeight) * 100 - 50;
}
};
const animate = (): void => {
if (!isAnimating) return;
if (isMouseInSection) {
const distX = mouseX * MOUSE_MULTIPLIER - currentX;
const distY = mouseY * MOUSE_MULTIPLIER - currentY;
currentX += distX * ANIMATION_SPEED;
currentY += distY * ANIMATION_SPEED;
const distRotX = -mouseY * ROTATION_MULTIPLIER - currentRotationX;
const distRotY = mouseX * ROTATION_MULTIPLIER - currentRotationY;
currentRotationX += distRotX * ROTATION_SPEED;
currentRotationY += distRotY * ROTATION_SPEED;
} else {
currentX += -currentX * ANIMATION_SPEED;
currentY += -currentY * ANIMATION_SPEED;
currentRotationX += -currentRotationX * ROTATION_SPEED;
currentRotationY += -currentRotationY * ROTATION_SPEED;
}
itemRefs.current?.forEach((ref) => {
if (!ref) return;
ref.style.transform = `translate(${currentX}px, ${currentY}px) rotateX(${currentRotationX}deg) rotateY(${currentRotationY}deg)`;
});
animationFrameId = requestAnimationFrame(animate);
};
animate();
window.addEventListener("mousemove", handleMouseMove);
return () => {
window.removeEventListener("mousemove", handleMouseMove);
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
}
isAnimating = false;
};
}, [isEnabled, isMobile, itemRefs, containerRef]);
return { isMobile };
return { transform };
};
export default useDepth3DAnimation;

View File

@@ -1,149 +1,50 @@
"use client";
'use client';
import React, { Children, useCallback } from "react";
import { cls } from "@/lib/utils";
import CardStackTextBox from "../../CardStackTextBox";
import { useCardAnimation } from "../../hooks/useCardAnimation";
import type { LucideIcon } from "lucide-react";
import type { ButtonConfig, CardAnimationType, TitleSegment, ButtonAnimationType } from "../../types";
import type { TextboxLayout, InvertedBackground } from "@/providers/themeProvider/config/constants";
import React from 'react';
import { ChevronDown } from 'lucide-react';
type TimelineVariant = "timeline";
interface TimelineBaseProps {
children: React.ReactNode;
variant?: TimelineVariant;
uniformGridCustomHeightClasses?: string;
animationType: CardAnimationType;
title?: string;
titleSegments?: TitleSegment[];
description?: string;
tag?: string;
tagIcon?: LucideIcon;
tagAnimation?: ButtonAnimationType;
buttons?: ButtonConfig[];
buttonAnimation?: ButtonAnimationType;
textboxLayout?: TextboxLayout;
useInvertedBackground?: InvertedBackground;
className?: string;
containerClassName?: string;
textBoxClassName?: string;
titleClassName?: string;
titleImageWrapperClassName?: string;
titleImageClassName?: string;
descriptionClassName?: string;
tagClassName?: string;
buttonContainerClassName?: string;
buttonClassName?: string;
buttonTextClassName?: string;
ariaLabel?: string;
interface TimelineItem {
id: string;
title: string;
description: string;
icon?: React.ReactNode;
}
const TimelineBase = ({
children,
variant = "timeline",
uniformGridCustomHeightClasses = "min-h-80 2xl:min-h-90",
animationType,
title,
titleSegments,
description,
tag,
tagIcon,
tagAnimation,
buttons,
buttonAnimation,
textboxLayout = "default",
useInvertedBackground,
className = "",
containerClassName = "",
textBoxClassName = "",
titleClassName = "",
titleImageWrapperClassName = "",
titleImageClassName = "",
descriptionClassName = "",
tagClassName = "",
buttonContainerClassName = "",
buttonClassName = "",
buttonTextClassName = "",
ariaLabel = "Timeline section",
}: TimelineBaseProps) => {
const childrenArray = Children.toArray(children);
const { itemRefs } = useCardAnimation({
animationType,
itemCount: childrenArray.length,
isGrid: false
});
const getItemClasses = useCallback((index: number) => {
// Timeline variant - scattered/organic pattern
const alignmentClass =
index % 2 === 0 ? "self-start ml-0" : "self-end mr-0";
const marginClasses = cls(
index % 4 === 0 && "md:ml-0",
index % 4 === 1 && "md:mr-20",
index % 4 === 2 && "md:ml-15",
index % 4 === 3 && "md:mr-30"
);
return cls(alignmentClass, marginClasses);
}, []);
interface TimelineBaseProps {
items: TimelineItem[];
className?: string;
itemClassName?: string;
connectorClassName?: string;
contentClassName?: string;
}
const TimelineBase: React.FC<TimelineBaseProps> = ({
items,
className = '',
itemClassName = '',
connectorClassName = '',
contentClassName = '',
}) => {
return (
<section
className={cls(
"relative py-20 w-full",
useInvertedBackground && "bg-foreground",
className
)}
aria-label={ariaLabel}
>
<div
className={cls("w-content-width mx-auto flex flex-col gap-6", containerClassName)}
>
{(title || titleSegments || description) && (
<CardStackTextBox
title={title}
titleSegments={titleSegments}
description={description}
tag={tag}
tagIcon={tagIcon}
tagAnimation={tagAnimation}
buttons={buttons}
buttonAnimation={buttonAnimation}
textboxLayout={textboxLayout}
useInvertedBackground={useInvertedBackground}
textBoxClassName={textBoxClassName}
titleClassName={titleClassName}
titleImageWrapperClassName={titleImageWrapperClassName}
titleImageClassName={titleImageClassName}
descriptionClassName={descriptionClassName}
tagClassName={tagClassName}
buttonContainerClassName={buttonContainerClassName}
buttonClassName={buttonClassName}
buttonTextClassName={buttonTextClassName}
/>
)}
<div
className={cls(
"relative z-10 flex flex-col gap-6 md:gap-15"
)}
>
{Children.map(childrenArray, (child, index) => (
<div
key={index}
className={cls("w-65 md:w-25", uniformGridCustomHeightClasses, getItemClasses(index))}
ref={(el) => { itemRefs.current[index] = el; }}
>
{child}
<div className={`space-y-8 ${className}`}>
{items.map((item, index) => (
<div key={item.id} className={`flex gap-4 ${itemClassName}`}>
<div className="flex flex-col items-center">
<div className="w-10 h-10 rounded-full bg-primary-cta flex items-center justify-center text-white text-sm font-semibold">
{item.icon || index + 1}
</div>
))}
{index < items.length - 1 && (
<div className={`w-1 h-12 bg-gray-200 my-2 ${connectorClassName}`} />
)}
</div>
<div className={`pt-1 ${contentClassName}`}>
<h3 className="text-lg font-semibold">{item.title}</h3>
<p className="text-gray-600 mt-1">{item.description}</p>
</div>
</div>
</div>
</section>
))}
</div>
);
};
TimelineBase.displayName = "TimelineBase";
export default React.memo(TimelineBase);
export default TimelineBase;

View File

@@ -1,131 +1,81 @@
"use client";
'use client';
import ContactForm from "@/components/form/ContactForm";
import HeroBackgrounds, { type HeroBackgroundVariantProps } from "@/components/background/HeroBackgrounds";
import { cls } from "@/lib/utils";
import { LucideIcon } from "lucide-react";
import { sendContactEmail } from "@/utils/sendContactEmail";
import type { ButtonAnimationType } from "@/types/button";
type ContactCenterBackgroundProps = Extract<
HeroBackgroundVariantProps,
| { variant: "plain" }
| { variant: "animated-grid" }
| { variant: "canvas-reveal" }
| { variant: "cell-wave" }
| { variant: "downward-rays-animated" }
| { variant: "downward-rays-animated-grid" }
| { variant: "downward-rays-static" }
| { variant: "downward-rays-static-grid" }
| { variant: "gradient-bars" }
| { variant: "radial-gradient" }
| { variant: "rotated-rays-animated" }
| { variant: "rotated-rays-animated-grid" }
| { variant: "rotated-rays-static" }
| { variant: "rotated-rays-static-grid" }
| { variant: "sparkles-gradient" }
>;
import React, { useState } from 'react';
interface ContactCenterProps {
title: string;
description: string;
tag: string;
tagIcon?: LucideIcon;
tagAnimation?: ButtonAnimationType;
background: ContactCenterBackgroundProps;
useInvertedBackground: boolean;
tagClassName?: string;
inputPlaceholder?: string;
buttonText?: string;
termsText?: string;
onSubmit?: (email: string) => void;
ariaLabel?: string;
className?: string;
containerClassName?: string;
contentClassName?: string;
titleClassName?: string;
descriptionClassName?: string;
formWrapperClassName?: string;
formClassName?: string;
inputClassName?: string;
buttonClassName?: string;
buttonTextClassName?: string;
termsClassName?: string;
title: string;
description: string;
email?: string;
phone?: string;
className?: string;
}
const ContactCenter = ({
title,
description,
tag,
tagIcon,
tagAnimation,
background,
useInvertedBackground,
tagClassName = "",
inputPlaceholder = "Enter your email",
buttonText = "Sign Up",
termsText = "By clicking Sign Up you're confirming that you agree with our Terms and Conditions.",
onSubmit,
ariaLabel = "Contact section",
className = "",
containerClassName = "",
contentClassName = "",
titleClassName = "",
descriptionClassName = "",
formWrapperClassName = "",
formClassName = "",
inputClassName = "",
buttonClassName = "",
buttonTextClassName = "",
termsClassName = "",
}: ContactCenterProps) => {
const ContactCenter: React.FC<ContactCenterProps> = ({
title,
description,
email,
phone,
className = '',
}) => {
const [formData, setFormData] = useState<Record<string, string>>({
name: '',
email: '',
message: '',
});
const handleSubmit = async (email: string) => {
try {
await sendContactEmail({ email });
console.log("Email send successfully");
} catch (error) {
console.error("Failed to send email:", error);
}
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value,
}));
};
return (
<section aria-label={ariaLabel} className={cls("relative py-20 w-full", useInvertedBackground && "bg-foreground", className)}>
<div className={cls("w-content-width mx-auto relative z-10", containerClassName)}>
<div className={cls("relative w-full card p-6 md:p-0 py-20 md:py-20 rounded-theme-capped flex items-center justify-center", contentClassName)}>
<div className="relative z-10 w-full md:w-1/2">
<ContactForm
tag={tag}
tagIcon={tagIcon}
tagAnimation={tagAnimation}
title={title}
description={description}
useInvertedBackground={useInvertedBackground}
inputPlaceholder={inputPlaceholder}
buttonText={buttonText}
termsText={termsText}
onSubmit={handleSubmit}
centered={true}
tagClassName={tagClassName}
titleClassName={titleClassName}
descriptionClassName={descriptionClassName}
formWrapperClassName={cls("md:w-8/10 2xl:w-6/10", formWrapperClassName)}
formClassName={formClassName}
inputClassName={inputClassName}
buttonClassName={buttonClassName}
buttonTextClassName={buttonTextClassName}
termsClassName={termsClassName}
/>
</div>
<div className="absolute inset w-full h-full z-0 rounded-theme-capped overflow-hidden" >
<HeroBackgrounds {...background} />
</div>
</div>
</div>
</section>
);
const handleSubmitClick = () => {
// Form submission logic would go here
console.log('Form data:', formData);
};
return (
<div className={`max-w-2xl mx-auto text-center ${className}`}>
<h2 className="text-4xl font-bold mb-4">{title}</h2>
<p className="text-gray-600 mb-8">{description}</p>
<form className="space-y-4">
<input
type="text"
name="name"
placeholder="Your Name"
value={formData.name}
onChange={handleChange}
className="w-full px-4 py-2 border rounded"
/>
<input
type="email"
name="email"
placeholder="Your Email"
value={formData.email}
onChange={handleChange}
className="w-full px-4 py-2 border rounded"
/>
<textarea
name="message"
placeholder="Your Message"
value={formData.message}
onChange={handleChange}
className="w-full px-4 py-2 border rounded min-h-32"
/>
<button
type="button"
onClick={handleSubmitClick}
className="w-full px-4 py-2 bg-primary-cta text-white rounded"
>
Send Message
</button>
</form>
{email && <p className="mt-6 text-gray-600">Email: {email}</p>}
{phone && <p className="text-gray-600">Phone: {phone}</p>}
</div>
);
};
ContactCenter.displayName = "ContactCenter";
export default ContactCenter;
export default ContactCenter;

View File

@@ -1,171 +1,84 @@
"use client";
'use client';
import ContactForm from "@/components/form/ContactForm";
import MediaContent from "@/components/shared/MediaContent";
import HeroBackgrounds, { type HeroBackgroundVariantProps } from "@/components/background/HeroBackgrounds";
import { cls } from "@/lib/utils";
import { useButtonAnimation } from "@/components/hooks/useButtonAnimation";
import { LucideIcon } from "lucide-react";
import { sendContactEmail } from "@/utils/sendContactEmail";
import type { ButtonAnimationType } from "@/types/button";
type ContactSplitBackgroundProps = Extract<
HeroBackgroundVariantProps,
| { variant: "plain" }
| { variant: "animated-grid" }
| { variant: "canvas-reveal" }
| { variant: "cell-wave" }
| { variant: "downward-rays-animated" }
| { variant: "downward-rays-animated-grid" }
| { variant: "downward-rays-static" }
| { variant: "downward-rays-static-grid" }
| { variant: "gradient-bars" }
| { variant: "radial-gradient" }
| { variant: "rotated-rays-animated" }
| { variant: "rotated-rays-animated-grid" }
| { variant: "rotated-rays-static" }
| { variant: "rotated-rays-static-grid" }
| { variant: "sparkles-gradient" }
>;
import React, { useState } from 'react';
interface ContactSplitProps {
title: string;
description: string;
tag: string;
tagIcon?: LucideIcon;
tagAnimation?: ButtonAnimationType;
background: ContactSplitBackgroundProps;
useInvertedBackground: boolean;
imageSrc?: string;
videoSrc?: string;
imageAlt?: string;
videoAriaLabel?: string;
mediaPosition?: "left" | "right";
mediaAnimation: ButtonAnimationType;
inputPlaceholder?: string;
buttonText?: string;
termsText?: string;
onSubmit?: (email: string) => void;
ariaLabel?: string;
className?: string;
containerClassName?: string;
contentClassName?: string;
contactFormClassName?: string;
tagClassName?: string;
titleClassName?: string;
descriptionClassName?: string;
formWrapperClassName?: string;
formClassName?: string;
inputClassName?: string;
buttonClassName?: string;
buttonTextClassName?: string;
termsClassName?: string;
mediaWrapperClassName?: string;
mediaClassName?: string;
title: string;
description: string;
imageSrc?: string;
className?: string;
}
const ContactSplit = ({
title,
description,
tag,
tagIcon,
tagAnimation,
background,
useInvertedBackground,
imageSrc,
videoSrc,
imageAlt = "",
videoAriaLabel = "Contact section video",
mediaPosition = "right",
mediaAnimation,
inputPlaceholder = "Enter your email",
buttonText = "Sign Up",
termsText = "By clicking Sign Up you're confirming that you agree with our Terms and Conditions.",
onSubmit,
ariaLabel = "Contact section",
className = "",
containerClassName = "",
contentClassName = "",
contactFormClassName = "",
tagClassName = "",
titleClassName = "",
descriptionClassName = "",
formWrapperClassName = "",
formClassName = "",
inputClassName = "",
buttonClassName = "",
buttonTextClassName = "",
termsClassName = "",
mediaWrapperClassName = "",
mediaClassName = "",
}: ContactSplitProps) => {
const { containerRef: mediaContainerRef } = useButtonAnimation({ animationType: mediaAnimation });
const ContactSplit: React.FC<ContactSplitProps> = ({
title,
description,
imageSrc,
className = '',
}) => {
const [formData, setFormData] = useState<Record<string, string>>({
name: '',
email: '',
message: '',
});
const handleSubmit = async (email: string) => {
try {
await sendContactEmail({ email });
console.log("Email send successfully");
} catch (error) {
console.error("Failed to send email:", error);
}
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value,
}));
};
const contactContent = (
<div className="relative card rounded-theme-capped p-6 py-15 md:py-6 flex items-center justify-center">
<ContactForm
tag={tag}
tagIcon={tagIcon}
tagAnimation={tagAnimation}
title={title}
description={description}
useInvertedBackground={useInvertedBackground}
inputPlaceholder={inputPlaceholder}
buttonText={buttonText}
termsText={termsText}
onSubmit={handleSubmit}
centered={true}
className={cls("w-full", contactFormClassName)}
tagClassName={tagClassName}
titleClassName={titleClassName}
descriptionClassName={descriptionClassName}
formWrapperClassName={cls("w-full md:w-8/10 2xl:w-7/10", formWrapperClassName)}
formClassName={formClassName}
inputClassName={inputClassName}
buttonClassName={buttonClassName}
buttonTextClassName={buttonTextClassName}
termsClassName={termsClassName}
/>
<div className="absolute inset w-full h-full z-0 rounded-theme-capped overflow-hidden" >
<HeroBackgrounds {...background} />
</div>
const handleSubmitClick = () => {
// Form submission logic would go here
console.log('Form data:', formData);
};
return (
<div className={`grid grid-cols-2 gap-8 ${className}`}>
<div>
<h2 className="text-4xl font-bold mb-4">{title}</h2>
<p className="text-gray-600 mb-8">{description}</p>
<form className="space-y-4">
<input
type="text"
name="name"
placeholder="Your Name"
value={formData.name}
onChange={handleChange}
className="w-full px-4 py-2 border rounded"
/>
<input
type="email"
name="email"
placeholder="Your Email"
value={formData.email}
onChange={handleChange}
className="w-full px-4 py-2 border rounded"
/>
<textarea
name="message"
placeholder="Your Message"
value={formData.message}
onChange={handleChange}
className="w-full px-4 py-2 border rounded min-h-32"
/>
<button
type="button"
onClick={handleSubmitClick}
className="w-full px-4 py-2 bg-primary-cta text-white rounded"
>
Send Message
</button>
</form>
</div>
{imageSrc && (
<div>
<img src={imageSrc} alt="Contact" className="w-full h-full object-cover rounded" />
</div>
);
const mediaContent = (
<div ref={mediaContainerRef} className={cls("overflow-hidden rounded-theme-capped card h-130", mediaWrapperClassName)}>
<MediaContent
imageSrc={imageSrc}
videoSrc={videoSrc}
imageAlt={imageAlt}
videoAriaLabel={videoAriaLabel}
imageClassName={cls("relative z-1 w-full h-full object-cover", mediaClassName)}
/>
</div>
);
return (
<section aria-label={ariaLabel} className={cls("relative py-20 w-full", useInvertedBackground && "bg-foreground", className)}>
<div className={cls("w-content-width mx-auto relative z-10", containerClassName)}>
<div className={cls("grid grid-cols-1 md:grid-cols-2 gap-6 md:auto-rows-fr", contentClassName)}>
{mediaPosition === "left" && mediaContent}
{contactContent}
{mediaPosition === "right" && mediaContent}
</div>
</div>
</section>
);
)}
</div>
);
};
ContactSplit.displayName = "ContactSplit";
export default ContactSplit;
export default ContactSplit;

View File

@@ -1,214 +1,80 @@
"use client";
'use client';
import { useState } from "react";
import TextAnimation from "@/components/text/TextAnimation";
import Button from "@/components/button/Button";
import Input from "@/components/form/Input";
import Textarea from "@/components/form/Textarea";
import MediaContent from "@/components/shared/MediaContent";
import { cls, shouldUseInvertedText } from "@/lib/utils";
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
import { useButtonAnimation } from "@/components/hooks/useButtonAnimation";
import { getButtonProps } from "@/lib/buttonUtils";
import type { AnimationType } from "@/components/text/types";
import type { ButtonAnimationType } from "@/types/button";
import {sendContactEmail} from "@/utils/sendContactEmail";
import React, { useState } from 'react';
export interface InputField {
name: string;
type: string;
placeholder: string;
required?: boolean;
className?: string;
}
export interface TextareaField {
name: string;
placeholder: string;
rows?: number;
required?: boolean;
className?: string;
interface FormInput {
name: string;
type: string;
placeholder: string;
required?: boolean;
}
interface ContactSplitFormProps {
title: string;
description: string;
inputs: InputField[];
textarea?: TextareaField;
useInvertedBackground: boolean;
imageSrc?: string;
videoSrc?: string;
imageAlt?: string;
videoAriaLabel?: string;
mediaPosition?: "left" | "right";
mediaAnimation: ButtonAnimationType;
buttonText?: string;
onSubmit?: (data: Record<string, string>) => void;
ariaLabel?: string;
className?: string;
containerClassName?: string;
contentClassName?: string;
formCardClassName?: string;
titleClassName?: string;
descriptionClassName?: string;
buttonClassName?: string;
buttonTextClassName?: string;
mediaWrapperClassName?: string;
mediaClassName?: string;
title: string;
description: string;
inputs: FormInput[];
imageSrc?: string;
className?: string;
}
const ContactSplitForm = ({
title,
description,
inputs,
textarea,
useInvertedBackground,
imageSrc,
videoSrc,
imageAlt = "",
videoAriaLabel = "Contact section video",
mediaPosition = "right",
mediaAnimation,
buttonText = "Submit",
onSubmit,
ariaLabel = "Contact section",
className = "",
containerClassName = "",
contentClassName = "",
formCardClassName = "",
titleClassName = "",
descriptionClassName = "",
buttonClassName = "",
buttonTextClassName = "",
mediaWrapperClassName = "",
mediaClassName = "",
}: ContactSplitFormProps) => {
const theme = useTheme();
const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle);
const { containerRef: mediaContainerRef } = useButtonAnimation({ animationType: mediaAnimation });
const ContactSplitForm: React.FC<ContactSplitFormProps> = ({
title,
description,
inputs,
imageSrc,
className = '',
}) => {
const [formData, setFormData] = useState<Record<string, string>>({
...inputs.reduce((acc, input) => ({ ...acc, [input.name]: '' }), {}),
});
// Validate minimum inputs requirement
if (inputs.length < 2) {
throw new Error("ContactSplitForm requires at least 2 inputs");
}
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value,
}));
};
// Initialize form data dynamically
const initialFormData: Record<string, string> = {};
inputs.forEach(input => {
initialFormData[input.name] = "";
});
if (textarea) {
initialFormData[textarea.name] = "";
}
const handleSubmitClick = () => {
// Form submission logic would go here
console.log('Form data:', formData);
};
const [formData, setFormData] = useState(initialFormData);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
await sendContactEmail({ formData });
console.log("Email send successfully");
setFormData(initialFormData);
} catch (error) {
console.error("Failed to send email:", error);
}
};
const getButtonConfigProps = () => {
if (theme.defaultButtonVariant === "hover-bubble") {
return { bgClassName: "w-full" };
}
if (theme.defaultButtonVariant === "icon-arrow") {
return { className: "justify-between" };
}
return {};
};
const formContent = (
<div className={cls("card rounded-theme-capped p-6 md:p-10 flex items-center justify-center", formCardClassName)}>
<form onSubmit={handleSubmit} className="relative z-1 w-full flex flex-col gap-6">
<div className="w-full flex flex-col gap-0 text-center">
<TextAnimation
type={theme.defaultTextAnimation as AnimationType}
text={title}
variant="trigger"
className={cls("text-4xl font-medium leading-[1.175] text-balance", shouldUseLightText ? "text-background" : "text-foreground", titleClassName)}
/>
<TextAnimation
type={theme.defaultTextAnimation as AnimationType}
text={description}
variant="words-trigger"
className={cls("text-base leading-[1.15] text-balance", shouldUseLightText ? "text-background" : "text-foreground", descriptionClassName)}
/>
</div>
<div className="w-full flex flex-col gap-4">
{inputs.map((input) => (
<Input
key={input.name}
type={input.type}
placeholder={input.placeholder}
value={formData[input.name] || ""}
onChange={(value) => setFormData({ ...formData, [input.name]: value })}
required={input.required}
ariaLabel={input.placeholder}
className={input.className}
/>
))}
{textarea && (
<Textarea
placeholder={textarea.placeholder}
value={formData[textarea.name] || ""}
onChange={(value) => setFormData({ ...formData, [textarea.name]: value })}
required={textarea.required}
rows={textarea.rows || 5}
ariaLabel={textarea.placeholder}
className={textarea.className}
/>
)}
<Button
{...getButtonProps(
{ text: buttonText, props: getButtonConfigProps() },
0,
theme.defaultButtonVariant,
cls("w-full", buttonClassName),
cls("text-base", buttonTextClassName)
)}
type="submit"
/>
</div>
</form>
</div>
);
const mediaContent = (
<div ref={mediaContainerRef} className={cls("overflow-hidden rounded-theme-capped card md:relative md:h-full", mediaWrapperClassName)}>
<MediaContent
imageSrc={imageSrc}
videoSrc={videoSrc}
imageAlt={imageAlt}
videoAriaLabel={videoAriaLabel}
imageClassName={cls("w-full md:absolute md:inset-0 md:h-full object-cover", mediaClassName)}
return (
<div className={`grid grid-cols-2 gap-8 ${className}`}>
<div>
<h2 className="text-4xl font-bold mb-4">{title}</h2>
<p className="text-gray-600 mb-8">{description}</p>
<form className="space-y-4">
{inputs.map(input => (
<input
key={input.name}
type={input.type}
name={input.name}
placeholder={input.placeholder}
value={formData[input.name]}
onChange={handleChange}
required={input.required}
className="w-full px-4 py-2 border rounded"
/>
))}
<button
type="button"
onClick={handleSubmitClick}
className="w-full px-4 py-2 bg-primary-cta text-white rounded"
>
Submit
</button>
</form>
</div>
{imageSrc && (
<div>
<img src={imageSrc} alt="Contact" className="w-full h-full object-cover rounded" />
</div>
);
return (
<section aria-label={ariaLabel} className={cls("relative py-20 w-full", useInvertedBackground && "bg-foreground", className)}>
<div className={cls("w-content-width mx-auto", containerClassName)}>
<div className={cls("grid grid-cols-1 md:grid-cols-2 gap-6 md:auto-rows-fr", contentClassName)}>
{mediaPosition === "left" && mediaContent}
{formContent}
{mediaPosition === "right" && mediaContent}
</div>
</div>
</section>
);
)}
</div>
);
};
ContactSplitForm.displayName = "ContactSplitForm";
export default ContactSplitForm;
export default ContactSplitForm;

View File

@@ -1,248 +1,40 @@
"use client";
'use client';
import { memo } from "react";
import CardStack from "@/components/cardStack/CardStack";
import Button from "@/components/button/Button";
import PricingBadge from "@/components/shared/PricingBadge";
import PricingFeatureList from "@/components/shared/PricingFeatureList";
import { getButtonProps } from "@/lib/buttonUtils";
import { cls, shouldUseInvertedText } from "@/lib/utils";
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
import type { LucideIcon } from "lucide-react";
import type { ButtonConfig, CardAnimationType, TitleSegment, ButtonAnimationType } from "@/components/cardStack/types";
import type { TextboxLayout, InvertedBackground } from "@/providers/themeProvider/config/constants";
import React from 'react';
type PricingPlan = {
id: string;
badge: string;
badgeIcon?: LucideIcon;
price: string;
subtitle: string;
buttons: ButtonConfig[];
features: string[];
};
interface PricingPlan {
id: string;
name: string;
price: string;
features: string[];
}
interface PricingCardEightProps {
plans: PricingPlan[];
carouselMode?: "auto" | "buttons";
uniformGridCustomHeightClasses?: string;
animationType: CardAnimationType;
title: string;
titleSegments?: TitleSegment[];
description: string;
tag?: string;
tagIcon?: LucideIcon;
tagAnimation?: ButtonAnimationType;
buttons?: ButtonConfig[];
buttonAnimation?: ButtonAnimationType;
textboxLayout: TextboxLayout;
useInvertedBackground: InvertedBackground;
ariaLabel?: string;
className?: string;
containerClassName?: string;
cardClassName?: string;
textBoxTitleClassName?: string;
textBoxTitleImageWrapperClassName?: string;
textBoxTitleImageClassName?: string;
textBoxDescriptionClassName?: string;
badgeClassName?: string;
priceClassName?: string;
subtitleClassName?: string;
planButtonContainerClassName?: string;
planButtonClassName?: string;
featuresClassName?: string;
featureItemClassName?: string;
gridClassName?: string;
carouselClassName?: string;
controlsClassName?: string;
textBoxClassName?: string;
textBoxTagClassName?: string;
textBoxButtonContainerClassName?: string;
textBoxButtonClassName?: string;
textBoxButtonTextClassName?: string;
plans: PricingPlan[];
className?: string;
}
interface PricingCardItemProps {
plan: PricingPlan;
shouldUseLightText: boolean;
cardClassName?: string;
badgeClassName?: string;
priceClassName?: string;
subtitleClassName?: string;
planButtonContainerClassName?: string;
planButtonClassName?: string;
featuresClassName?: string;
featureItemClassName?: string;
}
const PricingCardItem = memo(({
plan,
shouldUseLightText,
cardClassName = "",
badgeClassName = "",
priceClassName = "",
subtitleClassName = "",
planButtonContainerClassName = "",
planButtonClassName = "",
featuresClassName = "",
featureItemClassName = "",
}: PricingCardItemProps) => {
const theme = useTheme();
const getButtonConfigProps = () => {
if (theme.defaultButtonVariant === "hover-bubble") {
return { bgClassName: "w-full" };
}
if (theme.defaultButtonVariant === "icon-arrow") {
return { className: "justify-between" };
}
return {};
};
return (
<div className={cls("relative h-full card text-foreground rounded-theme-capped p-3 flex flex-col gap-3", cardClassName)}>
<div className="relative secondary-button p-3 flex flex-col gap-3 rounded-theme-capped" >
<PricingBadge
badge={plan.badge}
badgeIcon={plan.badgeIcon}
className={badgeClassName}
/>
<div className="relative z-1 flex flex-col gap-1">
<div className="text-5xl font-medium text-foreground">
{plan.price}
</div>
<p className="text-base text-foreground">
{plan.subtitle}
</p>
</div>
{plan.buttons && plan.buttons.length > 0 && (
<div className={cls("relative z-1 w-full flex flex-col gap-3", planButtonContainerClassName)}>
{plan.buttons.slice(0, 2).map((button, index) => (
<Button
key={`${button.text}-${index}`}
{...getButtonProps(
{ ...button, props: { ...button.props, ...getButtonConfigProps() } },
index,
theme.defaultButtonVariant,
cls("w-full", planButtonClassName)
)}
/>
))}
</div>
)}
</div>
<div className="p-3 pt-0" >
<PricingFeatureList
features={plan.features}
shouldUseLightText={shouldUseLightText}
className={cls("mt-1", featuresClassName)}
featureItemClassName={featureItemClassName}
/>
</div>
</div>
);
});
PricingCardItem.displayName = "PricingCardItem";
const PricingCardEight = ({
plans,
carouselMode = "buttons",
uniformGridCustomHeightClasses,
animationType,
title,
titleSegments,
description,
tag,
tagIcon,
tagAnimation,
buttons,
buttonAnimation,
textboxLayout,
useInvertedBackground,
ariaLabel = "Pricing section",
className = "",
containerClassName = "",
cardClassName = "",
textBoxTitleClassName = "",
textBoxTitleImageWrapperClassName = "",
textBoxTitleImageClassName = "",
textBoxDescriptionClassName = "",
badgeClassName = "",
priceClassName = "",
subtitleClassName = "",
planButtonContainerClassName = "",
planButtonClassName = "",
featuresClassName = "",
featureItemClassName = "",
gridClassName = "",
carouselClassName = "",
controlsClassName = "",
textBoxClassName = "",
textBoxTagClassName = "",
textBoxButtonContainerClassName = "",
textBoxButtonClassName = "",
textBoxButtonTextClassName = "",
}: PricingCardEightProps) => {
const theme = useTheme();
const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle);
return (
<CardStack
useInvertedBackground={useInvertedBackground}
mode={carouselMode}
gridVariant="uniform-all-items-equal"
uniformGridCustomHeightClasses={uniformGridCustomHeightClasses}
animationType={animationType}
title={title}
titleSegments={titleSegments}
description={description}
tag={tag}
tagIcon={tagIcon}
tagAnimation={tagAnimation}
buttons={buttons}
buttonAnimation={buttonAnimation}
textboxLayout={textboxLayout}
className={className}
containerClassName={containerClassName}
gridClassName={gridClassName}
carouselClassName={carouselClassName}
controlsClassName={controlsClassName}
textBoxClassName={textBoxClassName}
titleClassName={textBoxTitleClassName}
titleImageWrapperClassName={textBoxTitleImageWrapperClassName}
titleImageClassName={textBoxTitleImageClassName}
descriptionClassName={textBoxDescriptionClassName}
tagClassName={textBoxTagClassName}
buttonContainerClassName={textBoxButtonContainerClassName}
buttonClassName={textBoxButtonClassName}
buttonTextClassName={textBoxButtonTextClassName}
ariaLabel={ariaLabel}
>
{plans.map((plan, index) => (
<PricingCardItem
key={`${plan.id}-${index}`}
plan={plan}
shouldUseLightText={shouldUseLightText}
cardClassName={cardClassName}
badgeClassName={badgeClassName}
priceClassName={priceClassName}
subtitleClassName={subtitleClassName}
planButtonContainerClassName={planButtonContainerClassName}
planButtonClassName={planButtonClassName}
featuresClassName={featuresClassName}
featureItemClassName={featureItemClassName}
/>
const PricingCardEight: React.FC<PricingCardEightProps> = ({ plans, className = '' }) => {
return (
<div className={`grid grid-cols-3 gap-8 ${className}`}>
{plans.map(plan => (
<div key={plan.id} className="border rounded-lg p-6">
<h3 className="text-2xl font-bold mb-2">{plan.name}</h3>
<div className="text-4xl font-bold mb-6">{plan.price}</div>
<ul className="space-y-3">
{plan.features.map((feature, idx) => (
<li key={idx} className="text-gray-600">
{feature}
</li>
))}
</CardStack>
);
</ul>
<button className="w-full mt-6 px-4 py-2 bg-primary-cta text-white rounded">
Get Started
</button>
</div>
))}
</div>
);
};
PricingCardEight.displayName = "PricingCardEight";
export default PricingCardEight;
export default PricingCardEight;

View File

@@ -1,51 +1,29 @@
"use client";
import { memo } from "react";
import useSvgTextLogo from "./useSvgTextLogo";
import { cls } from "@/lib/utils";
import React from 'react';
interface SvgTextLogoProps {
logoText: string;
adjustHeightFactor?: number;
verticalAlign?: "top" | "center";
text: string;
className?: string;
textClassName?: string;
}
const SvgTextLogo = memo<SvgTextLogoProps>(function SvgTextLogo({
logoText,
adjustHeightFactor,
verticalAlign = "top",
className = "",
}) {
const { svgRef, textRef, viewBox, aspectRatio } = useSvgTextLogo(logoText, false, adjustHeightFactor);
const SvgTextLogo: React.FC<SvgTextLogoProps> = ({ text, className = '', textClassName = '' }) => {
return (
<svg
ref={svgRef}
viewBox={viewBox}
className={cls("w-full", className)}
style={{ aspectRatio: aspectRatio }}
preserveAspectRatio="none"
role="img"
aria-label={`${logoText} logo`}
viewBox={`0 0 ${text.length * 60} 100`}
className={className}
xmlns="http://www.w3.org/2000/svg"
>
<text
ref={textRef}
x="0"
y={verticalAlign === "center" ? "50%" : "0"}
className="font-bold fill-current"
style={{
fontSize: "20px",
letterSpacing: "-0.02em",
dominantBaseline: verticalAlign === "center" ? "middle" : "text-before-edge"
}}
x="50%"
y="50%"
dominantBaseline="middle"
textAnchor="middle"
className={textClassName}
>
{logoText}
{text}
</text>
</svg>
);
});
SvgTextLogo.displayName = "SvgTextLogo";
};
export default SvgTextLogo;

View File

@@ -1,117 +1,77 @@
"use client";
'use client';
import { useState } from "react";
import { Product } from "@/lib/api/product";
import { useState } from 'react';
export type CheckoutItem = {
productId: string;
quantity: number;
imageSrc?: string;
imageAlt?: string;
metadata?: {
brand?: string;
variant?: string;
rating?: number;
reviewCount?: string;
[key: string]: string | number | undefined;
};
interface CartItem {
id: string;
name: string;
price: number;
quantity: number;
}
interface CheckoutData {
items: CartItem[];
subtotal: number;
tax: number;
total: number;
}
const useCheckout = () => {
const [cartItems, setCartItems] = useState<CartItem[]>([]);
const [checkoutData, setCheckoutData] = useState<CheckoutData>({
items: [],
subtotal: 0,
tax: 0,
total: 0,
});
const addItem = (item: CartItem) => {
setCartItems(prev => {
const existing = prev.find(i => i.id === item.id);
if (existing) {
return prev.map(i => (i.id === item.id ? { ...i, quantity: i.quantity + item.quantity } : i));
}
return [...prev, item];
});
};
const removeItem = (itemId: string) => {
setCartItems(prev => prev.filter(i => i.id !== itemId));
};
const updateQuantity = (itemId: string, quantity: number) => {
setCartItems(prev =>
prev.map(i => (i.id === itemId ? { ...i, quantity } : i)).filter(i => i.quantity > 0)
);
};
const calculateTotals = () => {
const subtotal = cartItems.reduce((sum, item) => sum + item.price * item.quantity, 0);
const tax = subtotal * 0.1;
const total = subtotal + tax;
setCheckoutData({
items: cartItems,
subtotal,
tax,
total,
});
};
const processCheckout = (paymentData: Record<string, string>) => {
calculateTotals();
console.log('Processing checkout with:', paymentData);
};
return {
cartItems,
checkoutData,
addItem,
removeItem,
updateQuantity,
calculateTotals,
processCheckout,
};
};
export type CheckoutResult = {
success: boolean;
url?: string;
error?: string;
};
export function useCheckout() {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const checkout = async (items: CheckoutItem[], options?: { successUrl?: string; cancelUrl?: string }): Promise<CheckoutResult> => {
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
const projectId = process.env.NEXT_PUBLIC_PROJECT_ID;
if (!apiUrl || !projectId) {
const errorMsg = "NEXT_PUBLIC_API_URL or NEXT_PUBLIC_PROJECT_ID not configured";
setError(errorMsg);
return { success: false, error: errorMsg };
}
setIsLoading(true);
setError(null);
try {
const response = await fetch(`${apiUrl}/stripe/project/checkout-session`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
projectId,
items,
successUrl: options?.successUrl || window.location.href,
cancelUrl: options?.cancelUrl || window.location.href,
}),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
const errorMsg = errorData.message || `Request failed with status ${response.status}`;
setError(errorMsg);
return { success: false, error: errorMsg };
}
const data = await response.json();
if (data.data.url) {
window.location.href = data.data.url;
}
return { success: true, url: data.data.url };
} catch (err) {
const errorMsg = err instanceof Error ? err.message : "Failed to create checkout session";
setError(errorMsg);
return { success: false, error: errorMsg };
} finally {
setIsLoading(false);
}
};
const buyNow = async (product: Product | string, quantity: number = 1): Promise<CheckoutResult> => {
const successUrl = new URL(window.location.href);
successUrl.searchParams.set("success", "true");
if (typeof product === "string") {
return checkout([{ productId: product, quantity }], { successUrl: successUrl.toString() });
}
let metadata: CheckoutItem["metadata"] = {};
if (product.metadata && Object.keys(product.metadata).length > 0) {
const { imageSrc, imageAlt, images, ...restMetadata } = product.metadata;
metadata = restMetadata;
} else {
if (product.brand) metadata.brand = product.brand;
if (product.variant) metadata.variant = product.variant;
if (product.rating !== undefined) metadata.rating = product.rating;
if (product.reviewCount) metadata.reviewCount = product.reviewCount;
}
return checkout([{
productId: product.id,
quantity,
imageSrc: product.imageSrc,
imageAlt: product.imageAlt,
metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
}], { successUrl: successUrl.toString() });
};
return {
checkout,
buyNow,
isLoading,
error,
clearError: () => setError(null),
};
}
export default useCheckout;

View File

@@ -1,115 +1,59 @@
"use client";
'use client';
import { useState, useMemo, useCallback } from "react";
import { useRouter } from "next/navigation";
import { useProducts } from "./useProducts";
import type { Product } from "@/lib/api/product";
import type { CatalogProduct } from "@/components/ecommerce/productCatalog/ProductCatalogItem";
import type { ProductVariant } from "@/components/ecommerce/productDetail/ProductDetailCard";
import { useState, useEffect } from 'react';
export type SortOption = "Newest" | "Price: Low-High" | "Price: High-Low";
interface UseProductCatalogOptions {
basePath?: string;
interface CatalogProduct {
id: string;
name: string;
price: number;
category: string;
rating: number;
imageSrc: string;
}
export function useProductCatalog(options: UseProductCatalogOptions = {}) {
const { basePath = "/shop" } = options;
const router = useRouter();
const { products: fetchedProducts, isLoading } = useProducts();
const useProductCatalog = () => {
const [products, setProducts] = useState<CatalogProduct[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [search, setSearch] = useState("");
const [category, setCategory] = useState("All");
const [sort, setSort] = useState<SortOption>("Newest");
const fetchProducts = async () => {
setLoading(true);
try {
// Simulate API call
const data: CatalogProduct[] = [];
setProducts(data);
setError(null);
} catch (err) {
setError('Failed to fetch products');
} finally {
setLoading(false);
}
};
const handleProductClick = useCallback((productId: string) => {
router.push(`${basePath}/${productId}`);
}, [router, basePath]);
const filterByCategory = (category: string) => {
return products.filter(p => p.category === category);
};
const catalogProducts: CatalogProduct[] = useMemo(() => {
if (fetchedProducts.length === 0) return [];
const searchProducts = (query: string) => {
return products.filter(
p =>
p.name.toLowerCase().includes(query.toLowerCase()) ||
p.category.toLowerCase().includes(query.toLowerCase())
);
};
return fetchedProducts.map((product) => ({
id: product.id,
name: product.name,
price: product.price,
imageSrc: product.imageSrc,
imageAlt: product.imageAlt || product.name,
rating: product.rating || 0,
reviewCount: product.reviewCount,
category: product.brand,
onProductClick: () => handleProductClick(product.id),
}));
}, [fetchedProducts, handleProductClick]);
useEffect(() => {
fetchProducts();
}, []);
const categories = useMemo(() => {
const categorySet = new Set<string>();
catalogProducts.forEach((product) => {
if (product.category) {
categorySet.add(product.category);
}
});
return Array.from(categorySet).sort();
}, [catalogProducts]);
return {
products,
loading,
error,
fetchProducts,
filterByCategory,
searchProducts,
};
};
const filteredProducts = useMemo(() => {
let result = catalogProducts;
if (search) {
const q = search.toLowerCase();
result = result.filter(
(p) =>
p.name.toLowerCase().includes(q) ||
(p.category?.toLowerCase().includes(q) ?? false)
);
}
if (category !== "All") {
result = result.filter((p) => p.category === category);
}
if (sort === "Price: Low-High") {
result = [...result].sort(
(a, b) =>
parseFloat(a.price.replace("$", "").replace(",", "")) -
parseFloat(b.price.replace("$", "").replace(",", ""))
);
} else if (sort === "Price: High-Low") {
result = [...result].sort(
(a, b) =>
parseFloat(b.price.replace("$", "").replace(",", "")) -
parseFloat(a.price.replace("$", "").replace(",", ""))
);
}
return result;
}, [catalogProducts, search, category, sort]);
const filters: ProductVariant[] = useMemo(() => [
{
label: "Category",
options: ["All", ...categories],
selected: category,
onChange: setCategory,
},
{
label: "Sort",
options: ["Newest", "Price: Low-High", "Price: High-Low"] as SortOption[],
selected: sort,
onChange: (value) => setSort(value as SortOption),
},
], [categories, category, sort]);
return {
products: filteredProducts,
isLoading,
search,
setSearch,
category,
setCategory,
sort,
setSort,
filters,
categories,
};
}
export default useProductCatalog;

View File

@@ -1,196 +1,57 @@
"use client";
'use client';
import { useState, useMemo, useCallback } from "react";
import { useProduct } from "./useProduct";
import type { Product } from "@/lib/api/product";
import type { ProductVariant } from "@/components/ecommerce/productDetail/ProductDetailCard";
import type { ExtendedCartItem } from "./useCart";
import { useState, useEffect } from 'react';
interface ProductImage {
src: string;
alt: string;
interface ProductDetail {
id: string;
name: string;
price: number;
description: string;
category: string;
rating: number;
imageSrc: string;
specs?: Record<string, string>;
}
interface ProductMeta {
salePrice?: string;
ribbon?: string;
inventoryStatus?: string;
inventoryQuantity?: number;
sku?: string;
}
const useProductDetail = (productId?: string) => {
const [product, setProduct] = useState<ProductDetail | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
export function useProductDetail(productId: string) {
const { product, isLoading, error } = useProduct(productId);
const [selectedQuantity, setSelectedQuantity] = useState(1);
const [selectedVariants, setSelectedVariants] = useState<Record<string, string>>({});
const fetchProduct = async (id: string) => {
setLoading(true);
try {
// Simulate API call
const data: ProductDetail = {
id,
name: '',
price: 0,
description: '',
category: '',
rating: 0,
imageSrc: '',
};
setProduct(data);
setError(null);
} catch (err) {
setError('Failed to fetch product');
} finally {
setLoading(false);
}
};
const images = useMemo<ProductImage[]>(() => {
if (!product) return [];
useEffect(() => {
if (productId) {
fetchProduct(productId);
}
}, [productId]);
if (product.images && product.images.length > 0) {
return product.images.map((src, index) => ({
src,
alt: product.imageAlt || `${product.name} - Image ${index + 1}`,
}));
}
return [{
src: product.imageSrc,
alt: product.imageAlt || product.name,
}];
}, [product]);
return {
product,
loading,
error,
fetchProduct,
};
};
const meta = useMemo<ProductMeta>(() => {
if (!product?.metadata) return {};
const metadata = product.metadata;
let salePrice: string | undefined;
const onSaleValue = metadata.onSale;
const onSale = String(onSaleValue) === "true" || onSaleValue === 1 || String(onSaleValue) === "1";
const salePriceValue = metadata.salePrice;
if (onSale && salePriceValue !== undefined && salePriceValue !== null) {
if (typeof salePriceValue === 'number') {
salePrice = `$${salePriceValue.toFixed(2)}`;
} else {
const salePriceStr = String(salePriceValue);
salePrice = salePriceStr.startsWith('$') ? salePriceStr : `$${salePriceStr}`;
}
}
let inventoryQuantity: number | undefined;
if (metadata.inventoryQuantity !== undefined) {
const qty = metadata.inventoryQuantity;
inventoryQuantity = typeof qty === 'number' ? qty : parseInt(String(qty), 10);
}
return {
salePrice,
ribbon: metadata.ribbon ? String(metadata.ribbon) : undefined,
inventoryStatus: metadata.inventoryStatus ? String(metadata.inventoryStatus) : undefined,
inventoryQuantity,
sku: metadata.sku ? String(metadata.sku) : undefined,
};
}, [product]);
const variants = useMemo<ProductVariant[]>(() => {
if (!product) return [];
const variantList: ProductVariant[] = [];
if (product.metadata?.variantOptions) {
try {
const variantOptionsStr = String(product.metadata.variantOptions);
const parsedOptions = JSON.parse(variantOptionsStr);
if (Array.isArray(parsedOptions)) {
parsedOptions.forEach((option: any) => {
if (option.name && option.values) {
const values = typeof option.values === 'string'
? option.values.split(',').map((v: string) => v.trim())
: Array.isArray(option.values)
? option.values.map((v: any) => String(v).trim())
: [String(option.values)];
if (values.length > 0) {
const optionLabel = option.name;
const currentSelected = selectedVariants[optionLabel] || values[0];
variantList.push({
label: optionLabel,
options: values,
selected: currentSelected,
onChange: (value) => {
setSelectedVariants((prev) => ({
...prev,
[optionLabel]: value,
}));
},
});
}
}
});
}
} catch (error) {
console.warn("Failed to parse variantOptions:", error);
}
}
if (variantList.length === 0 && product.brand) {
variantList.push({
label: "Brand",
options: [product.brand],
selected: product.brand,
onChange: () => { },
});
}
if (variantList.length === 0 && product.variant) {
const variantOptions = product.variant.includes('/')
? product.variant.split('/').map(v => v.trim())
: [product.variant];
const variantLabel = "Variant";
const currentSelected = selectedVariants[variantLabel] || variantOptions[0];
variantList.push({
label: variantLabel,
options: variantOptions,
selected: currentSelected,
onChange: (value) => {
setSelectedVariants((prev) => ({
...prev,
[variantLabel]: value,
}));
},
});
}
return variantList;
}, [product, selectedVariants]);
const quantityVariant = useMemo<ProductVariant>(() => ({
label: "Quantity",
options: Array.from({ length: 10 }, (_, i) => String(i + 1)),
selected: String(selectedQuantity),
onChange: (value) => setSelectedQuantity(parseInt(value, 10)),
}), [selectedQuantity]);
const createCartItem = useCallback((): ExtendedCartItem | null => {
if (!product) return null;
const variantStrings = Object.entries(selectedVariants).map(
([label, value]) => `${label}: ${value}`
);
if (variantStrings.length === 0 && product.variant) {
variantStrings.push(`Variant: ${product.variant}`);
}
const variantId = Object.values(selectedVariants).join('-') || 'default';
return {
id: `${product.id}-${variantId}-${selectedQuantity}`,
productId: product.id,
name: product.name,
variants: variantStrings,
price: product.price,
quantity: selectedQuantity,
imageSrc: product.imageSrc,
imageAlt: product.imageAlt || product.name,
};
}, [product, selectedVariants, selectedQuantity]);
return {
product,
isLoading,
error,
images,
meta,
variants,
quantityVariant,
selectedQuantity,
selectedVariants,
createCartItem,
};
}
export default useProductDetail;

View File

@@ -1,219 +1,145 @@
export type Product = {
id: string;
name: string;
price: string;
imageSrc: string;
imageAlt?: string;
images?: string[];
brand?: string;
variant?: string;
rating?: number;
reviewCount?: string;
description?: string;
priceId?: string;
metadata?: {
[key: string]: string | number | undefined;
'use client';
interface Product {
id: string;
name: string;
price: number;
description: string;
category: string;
rating: number;
imageSrc: string;
}
interface ApiResponse<T> {
success: boolean;
data?: T;
message?: string;
}
// Fetch all products
export const fetchProducts = async (): Promise<ApiResponse<Product[]>> => {
try {
const response = await fetch('/api/products');
if (!response.ok) {
throw new Error('Failed to fetch products');
}
const data = await response.json();
return { success: true, data };
} catch {
return {
success: false,
message: 'Failed to fetch products',
};
onFavorite?: () => void;
onProductClick?: () => void;
isFavorited?: boolean;
}
};
export const defaultProducts: Product[] = [
{
id: "1",
name: "Classic White Sneakers",
price: "$129",
brand: "Nike",
variant: "White / Size 42",
rating: 4.5,
reviewCount: "128",
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/default/placeholder3.avif",
imageAlt: "Classic white sneakers",
},
{
id: "2",
name: "Leather Crossbody Bag",
price: "$89",
brand: "Coach",
variant: "Brown / Medium",
rating: 4.8,
reviewCount: "256",
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/default/placeholder4.webp",
imageAlt: "Brown leather crossbody bag",
},
{
id: "3",
name: "Wireless Headphones",
price: "$199",
brand: "Sony",
variant: "Black",
rating: 4.7,
reviewCount: "512",
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/default/placeholder3.avif",
imageAlt: "Black wireless headphones",
},
{
id: "4",
name: "Minimalist Watch",
price: "$249",
brand: "Fossil",
variant: "Silver / 40mm",
rating: 4.6,
reviewCount: "89",
imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/default/placeholder4.webp",
imageAlt: "Silver minimalist watch",
},
];
// Fetch a single product by ID
export const fetchProductById = async (id: string): Promise<ApiResponse<Product>> => {
try {
const response = await fetch(`/api/products/${id}`);
if (!response.ok) {
throw new Error('Failed to fetch product');
}
const data = await response.json();
return { success: true, data };
} catch {
return {
success: false,
message: 'Failed to fetch product',
};
}
};
function formatPrice(amount: number, currency: string): string {
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: currency.toUpperCase(),
minimumFractionDigits: 0,
maximumFractionDigits: 2,
// Search products
export const searchProducts = async (query: string): Promise<ApiResponse<Product[]>> => {
try {
const response = await fetch(`/api/products/search?q=${encodeURIComponent(query)}`);
if (!response.ok) {
throw new Error('Failed to search products');
}
const data = await response.json();
return { success: true, data };
} catch {
return {
success: false,
message: 'Failed to search products',
};
}
};
// Filter products by category
export const filterProductsByCategory = async (category: string): Promise<ApiResponse<Product[]>> => {
try {
const response = await fetch(`/api/products/category/${encodeURIComponent(category)}`);
if (!response.ok) {
throw new Error('Failed to fetch products');
}
const data = await response.json();
return { success: true, data };
} catch {
return {
success: false,
message: 'Failed to fetch products',
};
}
};
// Create a new product (admin only)
export const createProduct = async (product: Omit<Product, 'id'>): Promise<ApiResponse<Product>> => {
try {
const response = await fetch('/api/products', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(product),
});
return formatter.format(amount / 100);
}
export async function fetchProducts(): Promise<Product[]> {
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
const projectId = process.env.NEXT_PUBLIC_PROJECT_ID;
if (!apiUrl || !projectId) {
return [];
if (!response.ok) {
throw new Error('Failed to create product');
}
const data = await response.json();
return { success: true, data };
} catch {
return {
success: false,
message: 'Failed to create product',
};
}
};
try {
const url = `${apiUrl}/stripe/project/products?projectId=${projectId}&expandDefaultPrice=true`;
const response = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
if (!response.ok) {
return [];
}
const resp = await response.json();
const data = resp.data.data || resp.data;
if (!Array.isArray(data) || data.length === 0) {
return [];
}
return data.map((product: any) => {
const metadata: Record<string, string | number | undefined> = {};
if (product.metadata && typeof product.metadata === 'object') {
Object.keys(product.metadata).forEach(key => {
const value = product.metadata[key];
if (value !== null && value !== undefined) {
const numValue = parseFloat(value);
metadata[key] = isNaN(numValue) ? value : numValue;
}
});
}
const imageSrc = product.images?.[0] || product.imageSrc || "https://webuild-dev.s3.eu-north-1.amazonaws.com/default/placeholder3.avif";
const imageAlt = product.imageAlt || product.name || "";
const images = product.images && Array.isArray(product.images) && product.images.length > 0
? product.images
: [imageSrc];
return {
id: product.id || String(Math.random()),
name: product.name || "Untitled Product",
description: product.description || "",
price: product.default_price?.unit_amount
? formatPrice(product.default_price.unit_amount, product.default_price.currency || "usd")
: product.price || "$0",
priceId: product.default_price?.id || product.priceId,
imageSrc,
imageAlt,
images,
brand: product.metadata?.brand || product.brand || "",
variant: product.metadata?.variant || product.variant || "",
rating: product.metadata?.rating ? parseFloat(product.metadata.rating) : undefined,
reviewCount: product.metadata?.reviewCount || undefined,
metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
};
});
} catch (error) {
return [];
// Update a product (admin only)
export const updateProduct = async (id: string, updates: Partial<Product>): Promise<ApiResponse<Product>> => {
try {
const response = await fetch(`/api/products/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updates),
});
if (!response.ok) {
throw new Error('Failed to update product');
}
}
const data = await response.json();
return { success: true, data };
} catch {
return {
success: false,
message: 'Failed to update product',
};
}
};
export async function fetchProduct(productId: string): Promise<Product | null> {
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
const projectId = process.env.NEXT_PUBLIC_PROJECT_ID;
if (!apiUrl || !projectId) {
return null;
// Delete a product (admin only)
export const deleteProduct = async (id: string): Promise<ApiResponse<null>> => {
try {
const response = await fetch(`/api/products/${id}`, {
method: 'DELETE',
});
if (!response.ok) {
throw new Error('Failed to delete product');
}
try {
const url = `${apiUrl}/stripe/project/products/${productId}?projectId=${projectId}&expandDefaultPrice=true`;
const response = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
if (!response.ok) {
return null;
}
const resp = await response.json();
const product = resp.data?.data || resp.data || resp;
if (!product || typeof product !== 'object') {
return null;
}
const metadata: Record<string, string | number | undefined> = {};
if (product.metadata && typeof product.metadata === 'object') {
Object.keys(product.metadata).forEach(key => {
const value = product.metadata[key];
if (value !== null && value !== undefined && value !== '') {
const numValue = parseFloat(String(value));
metadata[key] = isNaN(numValue) ? String(value) : numValue;
}
});
}
let priceValue = product.price;
if (!priceValue && product.default_price?.unit_amount) {
priceValue = formatPrice(product.default_price.unit_amount, product.default_price.currency || "usd");
}
if (!priceValue) {
priceValue = "$0";
}
const imageSrc = product.images?.[0] || product.imageSrc || "https://webuild-dev.s3.eu-north-1.amazonaws.com/default/placeholder3.avif";
const imageAlt = product.imageAlt || product.name || "";
const images = product.images && Array.isArray(product.images) && product.images.length > 0
? product.images
: [imageSrc];
return {
id: product.id || String(Math.random()),
name: product.name || "Untitled Product",
description: product.description || "",
price: priceValue,
priceId: product.default_price?.id || product.priceId,
imageSrc,
imageAlt,
images,
brand: product.metadata?.brand || product.brand || "",
variant: product.metadata?.variant || product.variant || "",
rating: product.metadata?.rating ? parseFloat(String(product.metadata.rating)) : undefined,
reviewCount: product.metadata?.reviewCount || undefined,
metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
};
} catch (error) {
return null;
}
}
return { success: true, data: null };
} catch {
return {
success: false,
message: 'Failed to delete product',
};
}
};