Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b2eb7bcb9e | |||
| 3a50853045 | |||
| 1aec378997 | |||
| aa6c9de82e | |||
| 290b651435 | |||
| 3ee5a3cfba | |||
| 4f96977d28 | |||
| 2e27f3fdf9 | |||
| e3e977c0ce | |||
| 5f0303afed | |||
| 973b38f7a0 | |||
| 9a3a179ad4 | |||
| 2745a435fb | |||
| 174847fd3e | |||
| b1bfb8dacf | |||
| e9df669943 | |||
| 3f01f55a30 | |||
| 576fa3574e | |||
| a85a6847ae |
2734
src/app/layout.tsx
2734
src/app/layout.tsx
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,7 @@ import TestimonialCardFive from '@/components/sections/testimonial/TestimonialCa
|
||||
import ContactSplitForm from '@/components/sections/contact/ContactSplitForm';
|
||||
import FooterBaseCard from '@/components/sections/footer/FooterBaseCard';
|
||||
import { Sparkles } from "lucide-react";
|
||||
import NavbarStyleApple from "@/components/navbar/NavbarStyleApple/NavbarStyleApple";
|
||||
|
||||
export default function LandingPage() {
|
||||
return (
|
||||
@@ -26,7 +27,7 @@ export default function LandingPage() {
|
||||
headingFontWeight="bold"
|
||||
>
|
||||
<div id="nav" data-section="nav">
|
||||
<NavbarStyleCentered
|
||||
<NavbarStyleApple
|
||||
navItems={[{name: 'Hero', id: 'hero-section'}, {name: 'About', id: 'about-section'}, {name: 'Feature', id: 'features-section'}, {name: 'Product', id: 'products-section'}, {name: 'Testimonial', id: 'testimonials-section'}, {name: 'Contact', id: 'contact-section'}]}
|
||||
button={{text: 'Order Now', href: '#products-section'}}
|
||||
brandName="WarmCrust Bakery"
|
||||
|
||||
@@ -6,6 +6,7 @@ import HeroBackgrounds, { type HeroBackgroundVariantProps } from "@/components/b
|
||||
import LogoMarquee, { type MarqueeItem } from "@/components/shared/LogoMarquee";
|
||||
import { cls } from "@/lib/utils";
|
||||
import { useButtonAnimation } from "@/components/hooks/useButtonAnimation";
|
||||
import { useState } from "react";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import type { ButtonConfig, ButtonAnimationType } from "@/types/button";
|
||||
import type { Avatar } from "@/components/shared/AvatarGroup";
|
||||
@@ -61,7 +62,7 @@ interface HeroBillboardProps {
|
||||
buttonTextClassName?: string;
|
||||
mediaWrapperClassName?: string;
|
||||
imageClassName?: string;
|
||||
marqueeClassName?: string;
|
||||
marqueeClassName?: string;
|
||||
marqueeItemClassName?: string;
|
||||
marqueeCardClassName?: string;
|
||||
marqueeImageClassName?: string;
|
||||
@@ -70,45 +71,46 @@ interface HeroBillboardProps {
|
||||
}
|
||||
|
||||
const HeroBillboard = ({
|
||||
title,
|
||||
description,
|
||||
background,
|
||||
tag,
|
||||
tagIcon,
|
||||
tagAnimation,
|
||||
buttons,
|
||||
buttonAnimation,
|
||||
avatars,
|
||||
avatarText,
|
||||
imageSrc,
|
||||
videoSrc,
|
||||
imageAlt = "",
|
||||
videoAriaLabel = "Hero video",
|
||||
mediaAnimation = "none",
|
||||
marqueeItems,
|
||||
marqueeSpeed = 30,
|
||||
showMarqueeCard = true,
|
||||
ariaLabel = "Hero section",
|
||||
className = "",
|
||||
containerClassName = "",
|
||||
textBoxClassName = "",
|
||||
titleClassName = "",
|
||||
descriptionClassName = "",
|
||||
tagClassName = "",
|
||||
avatarGroupClassName = "",
|
||||
buttonContainerClassName = "",
|
||||
buttonClassName = "",
|
||||
buttonTextClassName = "",
|
||||
mediaWrapperClassName = "",
|
||||
imageClassName = "",
|
||||
marqueeClassName = "",
|
||||
marqueeItemClassName = "",
|
||||
marqueeCardClassName = "",
|
||||
marqueeImageClassName = "",
|
||||
marqueeTextClassName = "",
|
||||
marqueeIconClassName = "",
|
||||
title,
|
||||
description,
|
||||
background,
|
||||
tag,
|
||||
tagIcon,
|
||||
tagAnimation,
|
||||
buttons,
|
||||
buttonAnimation,
|
||||
avatars,
|
||||
avatarText,
|
||||
imageSrc,
|
||||
videoSrc,
|
||||
imageAlt = "",
|
||||
videoAriaLabel = "Hero video",
|
||||
mediaAnimation = "none",
|
||||
marqueeItems,
|
||||
marqueeSpeed = 30,
|
||||
showMarqueeCard = true,
|
||||
ariaLabel = "Hero section",
|
||||
className = "",
|
||||
containerClassName = "",
|
||||
textBoxClassName = "",
|
||||
titleClassName = "",
|
||||
descriptionClassName = "",
|
||||
tagClassName = "",
|
||||
avatarGroupClassName = "",
|
||||
buttonContainerClassName = "",
|
||||
buttonClassName = "",
|
||||
buttonTextClassName = "",
|
||||
mediaWrapperClassName = "",
|
||||
imageClassName = "",
|
||||
marqueeClassName = "",
|
||||
marqueeItemClassName = "",
|
||||
marqueeCardClassName = "",
|
||||
marqueeImageClassName = "",
|
||||
marqueeTextClassName = "",
|
||||
marqueeIconClassName = "",
|
||||
}: HeroBillboardProps) => {
|
||||
const { containerRef: mediaContainerRef } = useButtonAnimation({ animationType: mediaAnimation });
|
||||
const { containerRef: mediaContainerRef } = useButtonAnimation({ animationType: mediaAnimation });
|
||||
const [isHovering, setIsHovering] = useState(false);
|
||||
|
||||
return (
|
||||
<section
|
||||
@@ -139,15 +141,35 @@ const HeroBillboard = ({
|
||||
center={true}
|
||||
/>
|
||||
<div className="flex flex-col gap-6" >
|
||||
<div ref={mediaContainerRef} className={cls("w-full overflow-hidden rounded-theme-capped card p-4", mediaWrapperClassName)}>
|
||||
<MediaContent
|
||||
imageSrc={imageSrc}
|
||||
videoSrc={videoSrc}
|
||||
imageAlt={imageAlt}
|
||||
videoAriaLabel={videoAriaLabel}
|
||||
imageClassName={cls("z-1 aspect-square md:aspect-video", imageClassName)}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
ref={mediaContainerRef}
|
||||
className={cls("w-full overflow-hidden rounded-theme-capped card p-4 [perspective:1000px]", mediaWrapperClassName)}
|
||||
onMouseEnter={() => setIsHovering(true)}
|
||||
onMouseLeave={() => setIsHovering(false)}
|
||||
>
|
||||
<style>{`
|
||||
@keyframes infiniteSpin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
.spin-on-hover {
|
||||
animation: infiniteSpin 8s linear infinite;
|
||||
}
|
||||
`}</style>
|
||||
<div className={cls("w-full h-full [transform-style:preserve-3d]", isHovering && "spin-on-hover")}>
|
||||
<MediaContent
|
||||
imageSrc={imageSrc}
|
||||
videoSrc={videoSrc}
|
||||
imageAlt={imageAlt}
|
||||
videoAriaLabel={videoAriaLabel}
|
||||
imageClassName={cls("z-1 aspect-square md:aspect-video", imageClassName)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{marqueeItems && marqueeItems.length > 0 && (
|
||||
<LogoMarquee
|
||||
items={marqueeItems}
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
import { memo, useCallback } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { ArrowUpRight } from "lucide-react";
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import CardStack from "@/components/cardStack/CardStack";
|
||||
import ProductImage from "@/components/shared/ProductImage";
|
||||
import { cls, shouldUseInvertedText } from "@/lib/utils";
|
||||
@@ -63,6 +66,95 @@ interface ProductCardItemProps {
|
||||
cardPriceClassName?: string;
|
||||
}
|
||||
|
||||
const ProductCardItemFlip = memo(({
|
||||
product,
|
||||
shouldUseLightText,
|
||||
cardClassName = "",
|
||||
imageClassName = "",
|
||||
cardNameClassName = "",
|
||||
cardPriceClassName = "",
|
||||
}: ProductCardItemProps) => {
|
||||
const [isFlipped, setIsFlipped] = useState(false);
|
||||
|
||||
return (
|
||||
<article
|
||||
className="h-full perspective cursor-pointer"
|
||||
onMouseEnter={() => setIsFlipped(true)}
|
||||
onMouseLeave={() => setIsFlipped(false)}
|
||||
>
|
||||
<div
|
||||
className="relative w-full h-full transition-transform duration-500 ease-out"
|
||||
style={{
|
||||
transformStyle: "preserve-3d",
|
||||
transform: isFlipped ? "rotateY(180deg)" : "rotateY(0deg)",
|
||||
}}
|
||||
>
|
||||
{/* Front side */}
|
||||
<div
|
||||
className={cls(
|
||||
"w-full h-full",
|
||||
cardClassName
|
||||
)}
|
||||
style={{ backfaceVisibility: "hidden" }}
|
||||
>
|
||||
<div className={cls("relative overflow-hidden", imageClassName)}>
|
||||
<ProductImage
|
||||
src={product.image}
|
||||
alt={product.name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<h3 className={cls("font-semibold truncate", cardNameClassName)}>
|
||||
{product.name}
|
||||
</h3>
|
||||
<p className={cls("text-sm font-medium", cardPriceClassName)}>
|
||||
${product.price}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Back side */}
|
||||
<div
|
||||
className={cls(
|
||||
"w-full h-full p-4 flex flex-col justify-center",
|
||||
cardClassName
|
||||
)}
|
||||
style={{
|
||||
backfaceVisibility: "hidden",
|
||||
transform: "rotateY(180deg)",
|
||||
}}
|
||||
>
|
||||
<div className="space-y-3">
|
||||
<h3 className={cls("font-semibold text-lg", cardNameClassName)}>
|
||||
{product.name}
|
||||
</h3>
|
||||
<p className={cls("text-sm", shouldUseLightText ? "text-gray-300" : "text-gray-600")}>
|
||||
{product.description || "Premium quality product with exceptional craftsmanship"}
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
<p className={cls("font-semibold", cardPriceClassName)}>
|
||||
${product.price}
|
||||
</p>
|
||||
{product.specs && (
|
||||
<ul className="text-xs space-y-1">
|
||||
{product.specs.map((spec, idx) => (
|
||||
<li key={idx} className={shouldUseLightText ? "text-gray-400" : "text-gray-500"}>
|
||||
• {spec}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
});
|
||||
|
||||
ProductCardItemFlip.displayName = "ProductCardItemFlip";
|
||||
|
||||
const ProductCardItem = memo(({
|
||||
product,
|
||||
shouldUseLightText,
|
||||
@@ -72,7 +164,27 @@ const ProductCardItem = memo(({
|
||||
cardPriceClassName = "",
|
||||
}: ProductCardItemProps) => {
|
||||
return (
|
||||
<article
|
||||
<ProductCardItemFlip
|
||||
product={product}
|
||||
shouldUseLightText={shouldUseLightText}
|
||||
cardClassName={cardClassName}
|
||||
imageClassName={imageClassName}
|
||||
cardNameClassName={cardNameClassName}
|
||||
cardPriceClassName={cardPriceClassName}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const ProductCardItemOld = memo(({
|
||||
product,
|
||||
shouldUseLightText,
|
||||
cardClassName = "",
|
||||
imageClassName = "",
|
||||
cardNameClassName = "",
|
||||
cardPriceClassName = "",
|
||||
}: ProductCardItemProps) => {
|
||||
return (
|
||||
<article className="transition-all duration-300 ease-out hover:scale-105 hover:shadow-lg"
|
||||
className={cls("card group relative h-full flex flex-col gap-4 cursor-pointer p-4 rounded-theme-capped", cardClassName)}
|
||||
onClick={product.onProductClick}
|
||||
role="article"
|
||||
|
||||
Reference in New Issue
Block a user