diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index 7395c72..e69de29 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -1,99 +0,0 @@ -import FooterSimpleCard from '@/components/sections/footer/FooterSimpleCard'; -import NavbarFloatingLogo from '@/components/ui/NavbarFloatingLogo'; -import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary"; -import SiteBackgroundSlot from "@/components/ui/SiteBackgroundSlot"; -import { Outlet } from 'react-router-dom'; -import { StyleProvider } from "@/components/ui/StyleProvider"; - -export default function Layout() { - const navItems = [ - { - "name": "Home", "href": "#home" - }, - { - "name": "About", "href": "#about" - }, - { - "name": "Rooms", "href": "#rooms" - }, - { - "name": "Amenities", "href": "#amenities" - }, - { - "name": "Reviews", "href": "#reviews" - }, - { - "name": "Contact", "href": "#contact" - }, - { - "name": "Metrics", "href": "#metrics" - } -]; - - return ( - - - - - -
- -
- - - -
- ); -} diff --git a/src/components/ecommerce/ProductCart.tsx b/src/components/ecommerce/ProductCart.tsx index 6ea56e9..e69de29 100644 --- a/src/components/ecommerce/ProductCart.tsx +++ b/src/components/ecommerce/ProductCart.tsx @@ -1,126 +0,0 @@ -import { useEffect } from "react"; -import { X, Plus, Minus, Trash2 } from "lucide-react"; -import { motion, AnimatePresence } from "motion/react"; -import Button from "@/components/ui/Button"; -import ImageOrVideo from "@/components/ui/ImageOrVideo"; - -type CartItem = { - id: string; - name: string; - price: string; - quantity: number; - imageSrc: string; -}; - -type ProductCartProps = { - isOpen: boolean; - onClose: () => void; - items: CartItem[]; - total: string; - onQuantityChange?: (id: string, quantity: number) => void; - onRemove?: (id: string) => void; - onCheckout?: () => void; -}; - -const ProductCart = ({ isOpen, onClose, items, total, onQuantityChange, onRemove, onCheckout }: ProductCartProps) => { - useEffect(() => { - if (!isOpen) return; - const onKeyDown = (e: KeyboardEvent) => e.key === "Escape" && onClose(); - document.addEventListener("keydown", onKeyDown); - return () => document.removeEventListener("keydown", onKeyDown); - }, [isOpen, onClose]); - - useEffect(() => { - document.body.style.overflow = isOpen ? "hidden" : ""; - return () => { document.body.style.overflow = ""; }; - }, [isOpen]); - - return ( - - {isOpen && ( -
- - - -
-

Cart ({items.length})

- -
- -
- -
- {items.length === 0 ? ( -

Your cart is empty

- ) : ( -
- {items.map((item) => ( -
-
- -
-
-
-

{item.name}

-

{item.price}

-
-
- - {item.quantity} - - -
-
-
- ))} -
- )} -
- -
-
-
- Total - {total} -
-
- -
- )} - - ); -}; - -export default ProductCart; -export type { CartItem }; diff --git a/src/components/sections/blog/BlogTagCards.tsx b/src/components/sections/blog/BlogTagCards.tsx index 9074837..e69de29 100644 --- a/src/components/sections/blog/BlogTagCards.tsx +++ b/src/components/sections/blog/BlogTagCards.tsx @@ -1,163 +0,0 @@ -import { ArrowUpRight, Loader2 } from "lucide-react"; -import ScrollReveal from "@/components/ui/ScrollReveal"; -import Button from "@/components/ui/Button"; -import TextAnimation from "@/components/ui/TextAnimation"; -import GridOrCarousel from "@/components/ui/GridOrCarousel"; -import ImageOrVideo from "@/components/ui/ImageOrVideo"; -import { useButtonClick } from "@/hooks/useButtonClick"; -import useBlogPosts from "@/hooks/useBlogPosts"; - -type BlogItem = { - title: string; - excerpt: string; - authorName: string; - authorImageSrc: string; - date: string; - tags: string[]; - imageSrc: string; - href?: string; - onClick?: () => void; -}; - -const BlogCardItem = ({ item }: { item: BlogItem }) => { - const handleClick = useButtonClick(item.href, item.onClick); - - return ( -
-
- -
- -
-
- -
-
-
- - - {item.authorName} • {item.date} - -
- -

{item.title}

-

{item.excerpt}

-
- -
- {item.tags.map((tag, index) => ( -
-

{tag}

-
- ))} -
-
-
- ); -}; - -type BlogTagCardsProps = { - tag: string; - title: string; - description: string; - primaryButton?: { text: string; href: string }; - secondaryButton?: { text: string; href: string }; - items?: BlogItem[]; -}; - -const BlogTagCards = ({ - tag, - title, - description, - primaryButton, - secondaryButton, - items: itemsProp, -}: BlogTagCardsProps) => { - const { posts, isLoading } = useBlogPosts(); - const isFromApi = posts.length > 0; - const items = isFromApi - ? posts.map((p) => ({ - title: p.title, - excerpt: p.excerpt, - authorName: p.authorName, - authorImageSrc: p.authorAvatar, - date: p.date, - tags: [p.category], - imageSrc: p.imageSrc, - onClick: p.onBlogClick, - })) - : itemsProp; - - if (isLoading && !itemsProp) { - return ( -
-
- -
-
- ); - } - - if (!items || items.length === 0) { - return null; - } - - return ( -
-
-
-
-

{tag}

-
- - - - - - {(primaryButton || secondaryButton) && ( -
- {primaryButton &&
- )} -
- - - - {items.map((item, index) => ( - - ))} - - -
-
- ); -}; - -export default BlogTagCards; diff --git a/src/components/sections/features/FeaturesComparison.tsx b/src/components/sections/features/FeaturesComparison.tsx index 51b97b1..e69de29 100644 --- a/src/components/sections/features/FeaturesComparison.tsx +++ b/src/components/sections/features/FeaturesComparison.tsx @@ -1,85 +0,0 @@ -import { Check, X } from "lucide-react"; -import Button from "@/components/ui/Button"; -import TextAnimation from "@/components/ui/TextAnimation"; -import ScrollReveal from "@/components/ui/ScrollReveal"; - -interface FeaturesComparisonProps { - tag: string; - title: string; - description: string; - primaryButton?: { text: string; href: string }; - secondaryButton?: { text: string; href: string }; - negativeItems: string[]; - positiveItems: string[]; -} - -const FeaturesComparison = ({ - tag, - title, - description, - primaryButton, - secondaryButton, - negativeItems, - positiveItems, -}: FeaturesComparisonProps) => { - return ( -
-
-
-
-

{tag}

-
- - - - - - {(primaryButton || secondaryButton) && ( -
- {primaryButton &&
- )} -
- - -
- {negativeItems.map((item) => ( -
-
- -
- {item} -
- ))} -
- -
- {positiveItems.map((item) => ( -
-
- -
- {item} -
- ))} -
-
-
-
- ); -}; - -export default FeaturesComparison; diff --git a/src/components/sections/features/FeaturesDetailedCards.tsx b/src/components/sections/features/FeaturesDetailedCards.tsx index cae75f1..e69de29 100644 --- a/src/components/sections/features/FeaturesDetailedCards.tsx +++ b/src/components/sections/features/FeaturesDetailedCards.tsx @@ -1,94 +0,0 @@ -import Button from "@/components/ui/Button"; -import TextAnimation from "@/components/ui/TextAnimation"; -import ImageOrVideo from "@/components/ui/ImageOrVideo"; -import ScrollReveal from "@/components/ui/ScrollReveal"; - -type FeatureItem = { - title: string; - description: string; - tags: string[]; -} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never }); - -interface FeaturesDetailedCardsProps { - tag: string; - title: string; - description: string; - primaryButton?: { text: string; href: string }; - secondaryButton?: { text: string; href: string }; - items: FeatureItem[]; -} - -const FeaturesDetailedCards = ({ - tag, - title, - description, - primaryButton, - secondaryButton, - items, -}: FeaturesDetailedCardsProps) => { - return ( -
-
-
-
-

{tag}

-
- - - - - - {(primaryButton || secondaryButton) && ( -
- {primaryButton &&
- )} -
- -
- {items.map((item) => ( - -
-

{item.title}

- -
-
- {item.tags.map((itemTag) => ( -
-

{itemTag}

-
- ))} -
-

{item.description}

-
-
- -
- -
-
- ))} -
-
-
- ); -}; - -export default FeaturesDetailedCards; diff --git a/src/components/sections/features/FeaturesFlipCards.tsx b/src/components/sections/features/FeaturesFlipCards.tsx index 176db46..e69de29 100644 --- a/src/components/sections/features/FeaturesFlipCards.tsx +++ b/src/components/sections/features/FeaturesFlipCards.tsx @@ -1,117 +0,0 @@ -import { useState } from "react"; -import { Plus } from "lucide-react"; -import TextAnimation from "@/components/ui/TextAnimation"; -import ImageOrVideo from "@/components/ui/ImageOrVideo"; -import GridOrCarousel from "@/components/ui/GridOrCarousel"; -import Button from "@/components/ui/Button"; -import ScrollReveal from "@/components/ui/ScrollReveal"; - -type FeatureItem = { - title: string; - descriptions: string[]; -} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never }); - -interface FeaturesFlipCardsProps { - tag: string; - title: string; - description: string; - primaryButton?: { text: string; href: string }; - secondaryButton?: { text: string; href: string }; - items: FeatureItem[]; -} - -const FeatureFlipCard = ({ item }: { item: FeatureItem }) => { - const [isFlipped, setIsFlipped] = useState(false); - - return ( -
setIsFlipped(!isFlipped)} - > -
-
-
-

{item.title}

-
- -
-
-
- -
-
- -
-
-

{item.title}

-
- -
-
-
- {item.descriptions.map((desc, index) => ( -

{desc}

- ))} -
-
-
-
- ); -}; - -const FeaturesFlipCards = ({ - tag, - title, - description, - primaryButton, - secondaryButton, - items, -}: FeaturesFlipCardsProps) => { - return ( -
-
-
-
-

{tag}

-
- - - - - - {(primaryButton || secondaryButton) && ( -
- {primaryButton &&
- )} -
- - - - {items.map((item) => ( - - ))} - - -
-
- ); -}; - -export default FeaturesFlipCards; diff --git a/src/components/sections/features/FeaturesRevealCards.tsx b/src/components/sections/features/FeaturesRevealCards.tsx index eea0e75..e69de29 100644 --- a/src/components/sections/features/FeaturesRevealCards.tsx +++ b/src/components/sections/features/FeaturesRevealCards.tsx @@ -1,103 +0,0 @@ -import { Info } from "lucide-react"; -import TextAnimation from "@/components/ui/TextAnimation"; -import ImageOrVideo from "@/components/ui/ImageOrVideo"; -import GridOrCarousel from "@/components/ui/GridOrCarousel"; -import Button from "@/components/ui/Button"; -import ScrollReveal from "@/components/ui/ScrollReveal"; - -type FeatureItem = { - title: string; - description: string; -} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never }); - -interface FeaturesRevealCardsProps { - tag: string; - title: string; - description: string; - primaryButton?: { text: string; href: string }; - secondaryButton?: { text: string; href: string }; - items: FeatureItem[]; -} - -const FeaturesRevealCards = ({ - tag, - title, - description, - primaryButton, - secondaryButton, - items, -}: FeaturesRevealCardsProps) => { - return ( -
-
-
-
-

{tag}

-
- - - - - - {(primaryButton || secondaryButton) && ( -
- {primaryButton &&
- )} -
- - - - {items.map((item, index) => ( -
- - -
-
-
-

{index + 1}

-
-
- -
-
-
- - -
- ); -}; - -export default FeaturesRevealCards; diff --git a/src/components/sections/features/FeaturesRevealCardsBentoSharp.tsx b/src/components/sections/features/FeaturesRevealCardsBentoSharp.tsx index 3e3a5e4..e69de29 100644 --- a/src/components/sections/features/FeaturesRevealCardsBentoSharp.tsx +++ b/src/components/sections/features/FeaturesRevealCardsBentoSharp.tsx @@ -1,109 +0,0 @@ -import Button from "@/components/ui/Button"; -import TextAnimation from "@/components/ui/TextAnimation"; -import ImageOrVideo from "@/components/ui/ImageOrVideo"; -import ScrollReveal from "@/components/ui/ScrollReveal"; -import { cls } from "@/lib/utils"; - -type FeatureItem = { - title: string; - description: string; - href: string; -} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never }); - -interface FeaturesRevealCardsBentoSharpProps { - tag: string; - title: string; - description: string; - primaryButton?: { text: string; href: string }; - secondaryButton?: { text: string; href: string }; - items: [FeatureItem, FeatureItem, FeatureItem, FeatureItem, FeatureItem, FeatureItem, FeatureItem]; -} - -const FeaturesRevealCardsBentoSharp = ({ tag, title, description, primaryButton, secondaryButton, items }: FeaturesRevealCardsBentoSharpProps) => { - const gridClasses = [ - "md:col-span-2", - "md:col-span-4", - "md:col-span-3", - "md:col-span-3", - "md:col-span-2", - "md:col-span-2", - "md:col-span-2", - ]; - - const staggerDelays = [ - 0, - 0.1, - 0, - 0.1, - 0, - 0.1, - 0.2, - ]; - - return ( -
-
-
-
-

{tag}

-
- - - - - - {(primaryButton || secondaryButton) && ( -
- {primaryButton &&
- )} -
- -
- {items.map((item, index) => ( - - -
- -
-
-
- ); -}; - -export default FeaturesRevealCardsBentoSharp; diff --git a/src/components/sections/features/FeaturesStickyCards.tsx b/src/components/sections/features/FeaturesStickyCards.tsx index d468834..e69de29 100644 --- a/src/components/sections/features/FeaturesStickyCards.tsx +++ b/src/components/sections/features/FeaturesStickyCards.tsx @@ -1,233 +0,0 @@ -"use client"; - -import { useLayoutEffect, useRef } from "react"; -import { gsap } from "gsap"; -import { ScrollTrigger } from "gsap/ScrollTrigger"; -import Button from "@/components/ui/Button"; -import ImageOrVideo from "@/components/ui/ImageOrVideo"; -import { cls } from "@/lib/utils"; - -gsap.registerPlugin(ScrollTrigger); - -type FeatureItem = { - title: string; - description: string; - primaryButton?: { text: string; href: string }; - secondaryButton?: { text: string; href: string }; -} & ( - | { leftImageSrc: string; leftVideoSrc?: never } - | { leftVideoSrc: string; leftImageSrc?: never } - ) & ( - | { rightImageSrc: string; rightVideoSrc?: never } - | { rightVideoSrc: string; rightImageSrc?: never } - ); - -interface FeaturesStickyCardsProps { - items: FeatureItem[]; -} - -const CardFrame = ({ - imageSrc, - videoSrc, - cardRef, - className = "", -}: { - imageSrc?: string; - videoSrc?: string; - cardRef: (el: HTMLDivElement | null) => void; - className?: string; -}) => ( -
- -
-); - -const FeaturesStickyCards = ({ - items, -}: FeaturesStickyCardsProps) => { - const imageRefs = useRef<(HTMLDivElement | null)[]>([]); - const mobileImageRefs = useRef<(HTMLDivElement | null)[]>([]); - const triggerRefs = useRef<(HTMLDivElement | null)[]>([]); - - useLayoutEffect(() => { - const mm = gsap.matchMedia(); - - const getAnimationConfig = (itemIndex: number, isLeftCard: boolean) => { - const isOddItem = itemIndex % 2 === 1; - if (isLeftCard) { - return { - from: { xPercent: -225, rotation: -45 }, - to: { rotation: isOddItem ? 10 : -10 }, - }; - } else { - return { - from: { xPercent: 225, rotation: 45 }, - to: { rotation: isOddItem ? -10 : 10 }, - }; - } - }; - - const animateCards = (isMobile: boolean) => { - items.forEach((_, itemIndex) => { - [0, 1].forEach((cardIndex) => { - const refIndex = itemIndex * 2 + cardIndex; - const element = isMobile - ? mobileImageRefs.current[refIndex] - : imageRefs.current[refIndex]; - - if (element) { - const isLeftCard = cardIndex === 0; - - const fromConfig = isMobile - ? { - xPercent: isLeftCard ? -150 : 150, - rotation: isLeftCard ? -25 : 25, - } - : getAnimationConfig(itemIndex, isLeftCard).from; - - const toConfig = isMobile - ? { - xPercent: 0, - rotation: 0, - duration: 1, - scrollTrigger: { - trigger: element, - start: "top 90%", - end: "top 50%", - scrub: 1, - }, - } - : { - xPercent: 0, - rotation: getAnimationConfig(itemIndex, isLeftCard).to.rotation, - scrollTrigger: { - trigger: triggerRefs.current[itemIndex], - start: "top bottom", - end: "top top", - scrub: 1, - }, - }; - - gsap.fromTo(element, fromConfig, toConfig); - } - }); - }); - }; - - mm.add("(max-width: 767px)", () => animateCards(true)); - mm.add("(min-width: 768px)", () => animateCards(false)); - - return () => { - mm.revert(); - imageRefs.current = []; - mobileImageRefs.current = []; - triggerRefs.current = []; - }; - }, [items]); - - const sectionHeightStyle = { height: `${items.length * 100}vh` }; - - return ( -
-
-
-
- {items.map((item, index) => ( -
{ triggerRefs.current[index] = el; }} - className="w-full mx-auto h-screen flex justify-center items-center" - > -
-
-

{index + 1}

-
-

{item.title}

-

{item.description}

- {(item.primaryButton || item.secondaryButton) && ( -
- {item.primaryButton &&
- )} -
-
- ))} -
- -
- {items.map((item, itemIndex) => ( -
-
- { - imageRefs.current[itemIndex * 2] = el; - }} - className="w-25/100 xl:w-27/100 2xl:w-29/100 h-[70vh]" - /> - { - imageRefs.current[itemIndex * 2 + 1] = el; - }} - className="w-25/100 xl:w-27/100 2xl:w-28/100 h-[70vh]" - /> -
-
- ))} -
-
- -
- {items.map((item, itemIndex) => ( -
-
-
-

{itemIndex + 1}

-
-

{item.title}

-

{item.description}

- {(item.primaryButton || item.secondaryButton) && ( -
- {item.primaryButton &&
- )} -
-
- { - mobileImageRefs.current[itemIndex * 2] = el; - }} - className="w-1/2 aspect-9/16" - /> - { - mobileImageRefs.current[itemIndex * 2 + 1] = el; - }} - className="w-1/2 aspect-9/16" - /> -
-
- ))} -
-
-
- ); -}; - -export default FeaturesStickyCards; diff --git a/src/components/sections/footer/FooterBrand.tsx b/src/components/sections/footer/FooterBrand.tsx index 309926f..e69de29 100644 --- a/src/components/sections/footer/FooterBrand.tsx +++ b/src/components/sections/footer/FooterBrand.tsx @@ -1,66 +0,0 @@ -import { ChevronRight } from "lucide-react"; -import { useButtonClick } from "@/hooks/useButtonClick"; -import AutoFillText from "@/components/ui/AutoFillText"; -import { cls } from "@/lib/utils"; - -type FooterLink = { - label: string; - href?: string; - onClick?: () => void; -}; - -type FooterColumn = { - items: FooterLink[]; -}; - -const FooterLinkItem = ({ label, href, onClick }: FooterLink) => { - const handleClick = useButtonClick(href, onClick); - - return ( -
-
- ); -}; - -const FooterBrand = ({ - brand, - columns, -}: { - brand: string; - columns: FooterColumn[]; -}) => { - return ( -
-
- {brand} - -
- {columns.map((column, index) => ( -
- {column.items.map((item) => ( - - ))} -
- ))} -
-
-
- ); -}; - -export default FooterBrand; diff --git a/src/components/sections/footer/FooterBrandReveal.tsx b/src/components/sections/footer/FooterBrandReveal.tsx index 6f0d18b..e69de29 100644 --- a/src/components/sections/footer/FooterBrandReveal.tsx +++ b/src/components/sections/footer/FooterBrandReveal.tsx @@ -1,101 +0,0 @@ -import { useRef, useEffect, useState } from "react"; -import { ChevronRight } from "lucide-react"; -import { useButtonClick } from "@/hooks/useButtonClick"; -import AutoFillText from "@/components/ui/AutoFillText"; -import { cls } from "@/lib/utils"; - -type FooterLink = { - label: string; - href?: string; - onClick?: () => void; -}; - -type FooterColumn = { - items: FooterLink[]; -}; - -const FooterLinkItem = ({ label, href, onClick }: FooterLink) => { - const handleClick = useButtonClick(href, onClick); - - return ( -
-
- ); -}; - -const FooterBrandReveal = ({ - brand, - columns, -}: { - brand: string; - columns: FooterColumn[]; -}) => { - const footerRef = useRef(null); - const [footerHeight, setFooterHeight] = useState(0); - - useEffect(() => { - const updateHeight = () => { - if (footerRef.current) { - setFooterHeight(footerRef.current.offsetHeight); - } - }; - - updateHeight(); - - const resizeObserver = new ResizeObserver(updateHeight); - if (footerRef.current) { - resizeObserver.observe(footerRef.current); - } - - return () => resizeObserver.disconnect(); - }, []); - - return ( -
-
-
-
- {brand} - -
- {columns.map((column, index) => ( -
- {column.items.map((item) => ( - - ))} -
- ))} -
-
-
-
-
- ); -}; - -export default FooterBrandReveal; diff --git a/src/components/sections/footer/FooterSimpleReveal.tsx b/src/components/sections/footer/FooterSimpleReveal.tsx index d8d8e06..e69de29 100644 --- a/src/components/sections/footer/FooterSimpleReveal.tsx +++ b/src/components/sections/footer/FooterSimpleReveal.tsx @@ -1,120 +0,0 @@ -import { useRef, useEffect, useState } from "react"; -import { useButtonClick } from "@/hooks/useButtonClick"; - -type FooterColumn = { - title: string; - items: { label: string; href?: string; onClick?: () => void }[]; -}; - -type FooterLink = { - label: string; - href?: string; - onClick?: () => void; -}; - -const FooterLinkItem = ({ label, href, onClick }: FooterLink) => { - const handleClick = useButtonClick(href, onClick); - - return ( - - ); -}; - -const FooterBottomLink = ({ label, href, onClick }: FooterLink) => { - const handleClick = useButtonClick(href, onClick); - - return ( - - ); -}; - -const FooterSimpleReveal = ({ - brand, - columns, - copyright, - links, -}: { - brand: string; - columns: FooterColumn[]; - copyright: string; - links: FooterLink[]; -}) => { - const footerRef = useRef(null); - const [footerHeight, setFooterHeight] = useState(0); - - useEffect(() => { - const updateHeight = () => { - if (footerRef.current) { - setFooterHeight(footerRef.current.offsetHeight); - } - }; - - updateHeight(); - - const resizeObserver = new ResizeObserver(updateHeight); - if (footerRef.current) { - resizeObserver.observe(footerRef.current); - } - - return () => resizeObserver.disconnect(); - }, []); - - return ( -
-
-
-
-
-

{brand}

- -
- {columns.map((column) => ( -
-

{column.title}

- {column.items.map((item) => ( - - ))} -
- ))} -
-
- -
- {copyright} -
- {links.map((link) => ( - - ))} -
-
-
-
-
-
- ); -}; - -export default FooterSimpleReveal; diff --git a/src/components/sections/hero/HeroBillboardBrand.tsx b/src/components/sections/hero/HeroBillboardBrand.tsx index d7b8f98..e69de29 100644 --- a/src/components/sections/hero/HeroBillboardBrand.tsx +++ b/src/components/sections/hero/HeroBillboardBrand.tsx @@ -1,52 +0,0 @@ -import Button from "@/components/ui/Button"; -import HeroBackgroundSlot from "@/components/ui/HeroBackgroundSlot"; -import TextAnimation from "@/components/ui/TextAnimation"; -import ImageOrVideo from "@/components/ui/ImageOrVideo"; -import AutoFillText from "@/components/ui/AutoFillText"; -import ScrollReveal from "@/components/ui/ScrollReveal"; - -type HeroBillboardBrandProps = { - brand: string; - description: string; - primaryButton: { text: string; href: string }; - secondaryButton: { text: string; href: string }; -} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never }); - -const HeroBillboardBrand = ({ - brand, - description, - primaryButton, - secondaryButton, - imageSrc, - videoSrc, -}: HeroBillboardBrandProps) => { - return ( -
- -
-
- {brand} - - - -
-
-
- - - - -
-
- ); -}; - -export default HeroBillboardBrand; diff --git a/src/components/sections/hero/HeroBillboardCarousel.tsx b/src/components/sections/hero/HeroBillboardCarousel.tsx index 5d474cb..e69de29 100644 --- a/src/components/sections/hero/HeroBillboardCarousel.tsx +++ b/src/components/sections/hero/HeroBillboardCarousel.tsx @@ -1,75 +0,0 @@ -import Button from "@/components/ui/Button"; -import HeroBackgroundSlot from "@/components/ui/HeroBackgroundSlot"; -import TextAnimation from "@/components/ui/TextAnimation"; -import ImageOrVideo from "@/components/ui/ImageOrVideo"; - -type HeroBillboardCarouselProps = { - tag: string; - title: string; - description: string; - primaryButton: { text: string; href: string }; - secondaryButton: { text: string; href: string }; - items: ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never })[]; -}; - -const HeroBillboardCarousel = ({ - tag, - title, - description, - primaryButton, - secondaryButton, - items, -}: HeroBillboardCarouselProps) => { - const duplicated = [...items, ...items, ...items, ...items]; - - return ( -
- -
-
-

{tag}

-
- - - - - -
-
-
- -
-
- {duplicated.map((item, i) => ( -
- -
- ))} -
-
-
- ); -}; - -export default HeroBillboardCarousel; diff --git a/src/components/sections/hero/HeroCenteredLogos.tsx b/src/components/sections/hero/HeroCenteredLogos.tsx index bb5cbf1..e69de29 100644 --- a/src/components/sections/hero/HeroCenteredLogos.tsx +++ b/src/components/sections/hero/HeroCenteredLogos.tsx @@ -1,80 +0,0 @@ -import Button from "@/components/ui/Button"; -import HeroBackgroundSlot from "@/components/ui/HeroBackgroundSlot"; -import TextAnimation from "@/components/ui/TextAnimation"; -import ImageOrVideo from "@/components/ui/ImageOrVideo"; -import AvatarGroup from "@/components/ui/AvatarGroup"; - -type HeroCenteredLogosProps = { - avatarsSrc: string[]; - avatarText: string; - title: string; - description: string; - primaryButton: { text: string; href: string }; - secondaryButton: { text: string; href: string }; - logos: string[]; - hideMedia?: boolean; -} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never }); - -const HeroCenteredLogos = ({ - avatarsSrc, - avatarText, - title, - description, - primaryButton, - secondaryButton, - logos, - imageSrc, - videoSrc, - hideMedia = false, -}: HeroCenteredLogosProps) => { - return ( -
- - {!hideMedia && ( -
- -
-
- )} - -
-
- - - - - - -
-
-
-
- -
-
- {[...logos, ...logos, ...logos, ...logos].map((logo, index) => ( -
- {logo} -
- ))} -
-
-
- ); -}; - -export default HeroCenteredLogos; diff --git a/src/components/sections/hero/HeroWorkScrollStack.tsx b/src/components/sections/hero/HeroWorkScrollStack.tsx index 97bdb72..e69de29 100644 --- a/src/components/sections/hero/HeroWorkScrollStack.tsx +++ b/src/components/sections/hero/HeroWorkScrollStack.tsx @@ -1,283 +0,0 @@ -import { useRef, useEffect } from "react"; -import { motion } from "motion/react"; -import { ArrowRight } from "lucide-react"; -import gsap from "gsap"; -import { ScrollTrigger } from "gsap/ScrollTrigger"; -import ImageOrVideo from "@/components/ui/ImageOrVideo"; -import HeroBackgroundSlot from "@/components/ui/HeroBackgroundSlot"; -import TextAnimation from "@/components/ui/TextAnimation"; -import { useButtonClick } from "@/hooks/useButtonClick"; - -gsap.registerPlugin(ScrollTrigger); - -interface HeroWorkScrollStackProps { - tag: string; - title: string; - titleHighlight: string; - description: string; - descriptionMuted: string; - primaryButton: { text: string; href: string; avatarSrc: string; avatarLabel: string }; - sectionTag: string; - sectionTitle: string; - sectionDescription: string; - items: [ - { title: string; description: string; imageSrc: string; tag: string }, - { title: string; description: string; imageSrc: string; tag: string }, - { title: string; description: string; imageSrc: string; tag: string } - ]; - secondaryButton?: { text: string; href: string }; - heroAnimationDelay?: number; -} - -const HeroWorkScrollStack = ({ - tag, - title, - titleHighlight, - description, - descriptionMuted, - primaryButton, - sectionTag, - sectionTitle, - sectionDescription, - items, - secondaryButton, - heroAnimationDelay, -}: HeroWorkScrollStackProps) => { - const animationRef = useRef(null); - const placeholderRef = useRef(null); - const card1Ref = useRef(null); - const card2Ref = useRef(null); - const card3Ref = useRef(null); - const handlePrimaryClick = useButtonClick(primaryButton.href); - const handleSecondaryClick = useButtonClick(secondaryButton?.href || "#"); - - useEffect(() => { - const isDesktop = window.matchMedia("(min-width: 768px)").matches; - - const ctx = gsap.context(() => { - const cardRefs = [card1Ref.current, card2Ref.current, card3Ref.current]; - const placeholder = placeholderRef.current; - if (!placeholder) return; - - const placeholderRect = placeholder.getBoundingClientRect(); - const placeholderCenterY = placeholderRect.top + placeholderRect.height / 2; - - if (isDesktop) { - // DESKTOP: Scrub animation tied to scroll position - const xOffsets = ["32rem", "14.5rem", "-1.8rem"]; - const yAdjustments = [0, -48, 0]; - const rotations = [-5, 0, 5]; - const scales = [1.35, 1.3, 1.25]; - const zIndexes = [30, 20, 10]; - - const tl = gsap.timeline({ - scrollTrigger: { - trigger: animationRef.current, - start: "top top", - end: "bottom bottom", - scrub: 1, - }, - }); - - cardRefs.forEach((card, i) => { - if (!card) return; - const cardRect = card.getBoundingClientRect(); - const cardCenterY = cardRect.top + cardRect.height / 2; - const yOffset = placeholderCenterY - cardCenterY; - - gsap.set(card, { - x: xOffsets[i], - y: yOffset + yAdjustments[i], - rotation: rotations[i], - scale: scales[i], - zIndex: zIndexes[i], - willChange: "transform", - force3D: true, - }); - - tl.to(card, { x: 0, y: 0, rotation: 0, scale: 1, duration: 0.4, ease: "none" }, 0); - tl.to(card, { zIndex: 1, duration: 0.1, ease: "none" }, 0.3); - }); - } else { - // MOBILE: Toggle animation - play/reverse on scroll - const xOffsets = ["2.5rem", "0.5rem", "-1rem"]; - const yAdjustments = [-10, -30, 10]; - const rotations = [-5, 0, 5]; - const scales = [0.65, 0.7, 0.75]; - const zIndexes = [30, 20, 10]; - - cardRefs.forEach((card, i) => { - if (!card) return; - const cardRect = card.getBoundingClientRect(); - const cardCenterY = cardRect.top + cardRect.height / 2; - const yOffset = placeholderCenterY - cardCenterY; - - gsap.set(card, { - x: xOffsets[i], - y: yOffset + yAdjustments[i], - rotation: rotations[i], - scale: scales[i], - zIndex: zIndexes[i], - willChange: "transform", - force3D: true, - }); - - gsap.to(card, { - x: 0, - y: 0, - rotation: 0, - scale: 1, - duration: 1.2, - ease: "power2.inOut", - scrollTrigger: { - trigger: placeholder, - start: "top 35%", - toggleActions: "play none none reverse", - }, - }); - }); - } - }, animationRef); - - return () => ctx.revert(); - }, []); - - return ( -
-
-
- - -
-
- -
- -

{tag}

-
- -

- - {title}{" "} - {titleHighlight} - -

- -

- {description}{" "} - {descriptionMuted} -

- - -
-
- -
-
-
- - + - -
- - {primaryButton.avatarLabel} - -
-
-
-
- {primaryButton.text} -
-
- -
-
-
-
-
-
-
- -
-
-
-
-
-

{sectionTag}

-
- - - - -
- -
- {items.map((item, index) => { - const cardRef = index === 0 ? card1Ref : index === 1 ? card2Ref : card3Ref; - return ( -
-
-
- - - {item.tag} - -
-
-

- {item.title}. - {item.description} -

-
- ); - })} -
- - {secondaryButton && ( - - )} -
-
-
-
- ); -}; - -export default HeroWorkScrollStack; diff --git a/src/components/sections/testimonial/TestimonialMarqueeCards.tsx b/src/components/sections/testimonial/TestimonialMarqueeCards.tsx index 497403d..e69de29 100644 --- a/src/components/sections/testimonial/TestimonialMarqueeCards.tsx +++ b/src/components/sections/testimonial/TestimonialMarqueeCards.tsx @@ -1,113 +0,0 @@ -import Button from "@/components/ui/Button"; -import TextAnimation from "@/components/ui/TextAnimation"; -import ImageOrVideo from "@/components/ui/ImageOrVideo"; -import ScrollReveal from "@/components/ui/ScrollReveal"; - -type Testimonial = { - name: string; - role: string; - quote: string; -} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never }); - -const TestimonialMarqueeCards = ({ - tag, - title, - description, - primaryButton, - secondaryButton, - testimonials, -}: { - tag: string; - title: string; - description: string; - primaryButton?: { text: string; href: string }; - secondaryButton?: { text: string; href: string }; - testimonials: Testimonial[]; -}) => { - const half = Math.ceil(testimonials.length / 2); - const topRow = testimonials.slice(0, half); - const bottomRow = testimonials.slice(half); - - return ( -
-
-
-
-

{tag}

-
- - - - - - {(primaryButton || secondaryButton) && ( -
- {primaryButton &&
- )} -
- - -
-
- {[...topRow, ...topRow, ...topRow, ...topRow].map((testimonial, index) => ( -
-

{testimonial.quote}

- -
- -
- {testimonial.name} - {testimonial.role} -
-
-
- ))} -
-
- -
-
- {[...bottomRow, ...bottomRow, ...bottomRow, ...bottomRow].map((testimonial, index) => ( -
-

{testimonial.quote}

- -
- -
- {testimonial.name} - {testimonial.role} -
-
-
- ))} -
-
-
-
-
- ); -}; - -export default TestimonialMarqueeCards; diff --git a/src/components/sections/testimonial/TestimonialRatingCards.tsx b/src/components/sections/testimonial/TestimonialRatingCards.tsx index 451973d..e69de29 100644 --- a/src/components/sections/testimonial/TestimonialRatingCards.tsx +++ b/src/components/sections/testimonial/TestimonialRatingCards.tsx @@ -1,101 +0,0 @@ -import { Star } from "lucide-react"; -import Button from "@/components/ui/Button"; -import TextAnimation from "@/components/ui/TextAnimation"; -import ImageOrVideo from "@/components/ui/ImageOrVideo"; -import ScrollReveal from "@/components/ui/ScrollReveal"; -import GridOrCarousel from "@/components/ui/GridOrCarousel"; -import { cls } from "@/lib/utils"; - -type Testimonial = { - name: string; - role: string; - quote: string; - rating: number; -} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never }); - -const TestimonialRatingCards = ({ - tag, - title, - description, - primaryButton, - secondaryButton, - testimonials, -}: { - tag: string; - title: string; - description: string; - primaryButton?: { text: string; href: string }; - secondaryButton?: { text: string; href: string }; - testimonials: Testimonial[]; -}) => { - return ( -
-
-
-
-

{tag}

-
- - - - - - {(primaryButton || secondaryButton) && ( -
- {primaryButton &&
- )} -
- - - - {testimonials.map((testimonial) => ( -
-
-
- {Array.from({ length: 5 }).map((_, index) => ( - - ))} -
- -

{testimonial.quote}

-
- -
- -
- {testimonial.name} - {testimonial.role} -
-
-
- ))} -
-
-
-
- ); -}; - -export default TestimonialRatingCards; diff --git a/src/components/sections/testimonial/TestimonialTrustCard.tsx b/src/components/sections/testimonial/TestimonialTrustCard.tsx index c00f1b0..e69de29 100644 --- a/src/components/sections/testimonial/TestimonialTrustCard.tsx +++ b/src/components/sections/testimonial/TestimonialTrustCard.tsx @@ -1,74 +0,0 @@ -import { Star } from "lucide-react"; -import TextAnimation from "@/components/ui/TextAnimation"; -import ImageOrVideo from "@/components/ui/ImageOrVideo"; -import ScrollReveal from "@/components/ui/ScrollReveal"; -import { cls } from "@/lib/utils"; - -type Avatar = { - name: string; -} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never }); - -const TestimonialTrustCard = ({ - quote, - rating, - author, - avatars, -}: { - quote: string; - rating: number; - author: string; - avatars: Avatar[]; -}) => { - const visibleAvatars = avatars.slice(0, 6); - const remainingCount = avatars.length - visibleAvatars.length; - - return ( -
-
- - {Array.from({ length: 5 }).map((_, index) => ( - - ))} - - - - - -

{author}

-
- - - {visibleAvatars.map((avatar, index) => ( -
0 && "-ml-5")} - style={{ zIndex: visibleAvatars.length - index }} - > - -
- ))} - {remainingCount > 0 && ( -
- +{remainingCount} -
- )} -
-
-
- ); -}; - -export default TestimonialTrustCard; diff --git a/src/components/ui/Card.tsx b/src/components/ui/Card.tsx index 3a35db3..e69de29 100644 --- a/src/components/ui/Card.tsx +++ b/src/components/ui/Card.tsx @@ -1,15 +0,0 @@ -import type { ReactNode } from "react"; -import { cls } from "@/lib/utils"; - -interface CardProps { - children: ReactNode; - className?: string; -} - -const Card = ({ children, className = "" }: CardProps) => ( -
- {children} -
-); - -export default Card; diff --git a/src/components/ui/HeroBackgroundSlot.tsx b/src/components/ui/HeroBackgroundSlot.tsx index ac1a468..e69de29 100644 --- a/src/components/ui/HeroBackgroundSlot.tsx +++ b/src/components/ui/HeroBackgroundSlot.tsx @@ -1,32 +0,0 @@ -"use client"; - -import { useStyle } from "@/components/ui/useStyle"; -import CornerGlowBackground from "@/components/ui/CornerGlowBackground"; -import GradientBarsBackground from "@/components/ui/GradientBarsBackground"; -import HorizonGlowBackground from "@/components/ui/HorizonGlowBackground"; -import LightRaysCenterBackground from "@/components/ui/LightRaysCenterBackground"; -import LightRaysCornerBackground from "@/components/ui/LightRaysCornerBackground"; -import RadialGradientBackground from "@/components/ui/RadialGradientBackground"; - -const HeroBackgroundSlot = () => { - const { heroBackground } = useStyle(); - - switch (heroBackground) { - case "cornerGlow": - return ; - case "gradientBars": - return ; - case "horizonGlow": - return ; - case "lightRaysCenter": - return ; - case "lightRaysCorner": - return ; - case "radialGradient": - return ; - default: - return null; - } -}; - -export default HeroBackgroundSlot; diff --git a/src/components/ui/OrbitingIcons.tsx b/src/components/ui/OrbitingIcons.tsx index 2945879..e69de29 100644 --- a/src/components/ui/OrbitingIcons.tsx +++ b/src/components/ui/OrbitingIcons.tsx @@ -1,37 +0,0 @@ -import type { LucideIcon } from "lucide-react"; -import { resolveIcon } from "@/utils/resolve-icon"; - -const OrbitingIcons = ({ centerIcon, items }: { centerIcon: string | LucideIcon; items: (string | LucideIcon)[] }) => { - const CenterIcon = resolveIcon(centerIcon); - return ( -
-
-
-
-
-
-
- -
- {items.map((iconInput, i) => { - const Icon = resolveIcon(iconInput); - return ( -
- -
- ); - })} -
-
-
-); -}; - -export default OrbitingIcons; diff --git a/src/components/ui/SiteBackgroundSlot.tsx b/src/components/ui/SiteBackgroundSlot.tsx index ee092e4..e69de29 100644 --- a/src/components/ui/SiteBackgroundSlot.tsx +++ b/src/components/ui/SiteBackgroundSlot.tsx @@ -1,32 +0,0 @@ -"use client"; - -import { useStyle } from "@/components/ui/useStyle"; -import AuroraBackground from "@/components/ui/AuroraBackground"; -import CornerGlowBackground from "@/components/ui/CornerGlowBackground"; -import FloatingGradientBackground from "@/components/ui/FloatingGradientBackground"; -import GridLinesBackground from "@/components/ui/GridLinesBackground"; -import NoiseBackground from "@/components/ui/NoiseBackground"; -import NoiseGradientBackground from "@/components/ui/NoiseGradientBackground"; - -const SiteBackgroundSlot = () => { - const { siteBackground } = useStyle(); - - switch (siteBackground) { - case "aurora": - return ; - case "cornerGlow": - return ; - case "floatingGradient": - return ; - case "gridLines": - return ; - case "noise": - return ; - case "noiseGradient": - return ; - default: - return null; - } -}; - -export default SiteBackgroundSlot; diff --git a/src/hooks/useProducts.ts b/src/hooks/useProducts.ts index ff1d6dc..e69de29 100644 --- a/src/hooks/useProducts.ts +++ b/src/hooks/useProducts.ts @@ -1,40 +0,0 @@ -import { useEffect, useState } from "react"; -import { fetchProducts, type Product } from "@/lib/api/product"; - -const useProducts = () => { - const [products, setProducts] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - let isMounted = true; - - const loadProducts = async () => { - try { - const data = await fetchProducts(); - if (isMounted) { - setProducts(data); - } - } catch (err) { - if (isMounted) { - setError(err instanceof Error ? err : new Error("Failed to fetch products")); - } - } finally { - if (isMounted) { - setIsLoading(false); - } - } - }; - - loadProducts(); - - return () => { - isMounted = false; - }; - }, []); - - return { products, isLoading, error }; -}; - -export default useProducts; -export type { Product }; diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index 80ed876..da1e534 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -19,7 +19,7 @@ export default function HomePage() { avatarsSrc={[ "http://img.b2bpic.net/free-photo/hotel-concierge-serving-coffee-client_482257-91383.jpg", "http://img.b2bpic.net/free-photo/smiling-woman-sitting-elegant-restaurant-with-tablet-talking-phone_1157-2100.jpg", "http://img.b2bpic.net/free-photo/smiling-tender-parisian-girl-stylish-outfit-sends-air-kiss-portrait-young-woman-with-expressive-look_197531-12004.jpg", "http://img.b2bpic.net/free-photo/front-view-valet-suit-working_23-2150274536.jpg"]} avatarText="Our dedicated team of hosts" - title="Experience Unrivaled Luxury at The Grand Hotel" + title="Unrivaled Luxury at The Grand Hotel" description="Indulge in sophisticated elegance, impeccable service, and breathtaking views. Your unforgettable escape begins here." primaryButton={{ text: "Book Your Stay", href: "#contact"}}