From c931dd153a7e77b8001f00b0a6863514b30e149d Mon Sep 17 00:00:00 2001 From: bender Date: Wed, 11 Mar 2026 18:34:19 +0000 Subject: [PATCH 01/12] Update src/app/page.tsx --- src/app/page.tsx | 158 ++++++++++++++++++++++++++--------------------- 1 file changed, 88 insertions(+), 70 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index 9b5ca48..2db2b0d 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,7 +1,6 @@ -"use client" +"use client"; -import { Brain, HardDrive, Shield, Zap } from "lucide-react"; -import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider"; +import { ThemeProvider } from '@/providers/themeProvider/ThemeProvider'; import NavbarLayoutFloatingOverlay from '@/components/navbar/NavbarLayoutFloatingOverlay/NavbarLayoutFloatingOverlay'; import HeroLogo from '@/components/sections/hero/HeroLogo'; import TextAbout from '@/components/sections/about/TextAbout'; @@ -12,32 +11,51 @@ import TestimonialCardSixteen from '@/components/sections/testimonial/Testimonia import FaqSplitText from '@/components/sections/faq/FaqSplitText'; import ContactSplitForm from '@/components/sections/contact/ContactSplitForm'; import FooterBaseCard from '@/components/sections/footer/FooterBaseCard'; +import { Shield, Brain, HardDrive, Zap, Package } from 'lucide-react'; -export default function LandingPage() { +const assetMap: Record = { + 'hero-dashboard': 'https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoIhdjka20PXTM8228xExsAGIl/a-premium-discord-security-dashboard-int-1773253913572-d727d9ee.png', + 'feature-antinuke': 'https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoIhdjka20PXTM8228xExsAGIl/visual-representation-of-antinuke-protec-1773253912702-75413cab.png', + 'feature-automod': 'https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoIhdjka20PXTM8228xExsAGIl/ai-powered-content-moderation-system-int-1773253912974-892bfd2f.png', + 'feature-backup': 'https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoIhdjka20PXTM8228xExsAGIl/server-backup-and-recovery-system-interf-1773253913333-eafcf1ca.png', + 'testimonial-1': 'http://img.b2bpic.net/free-photo/side-view-smiley-people-playing-videogame_23-2149349993.jpg', + 'testimonial-2': 'http://img.b2bpic.net/free-photo/afroamerican-businessman-wearing-headphones_23-2148508923.jpg', + 'testimonial-3': 'http://img.b2bpic.net/free-photo/front-view-man-posing-studio_23-2150275662.jpg', + 'testimonial-4': 'http://img.b2bpic.net/free-photo/young-chinese-woman-smiling-confident-standing-street_839833-7633.jpg', + 'contact-form-bg': 'https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoIhdjka20PXTM8228xExsAGIl/premium-contact-interface-showing-secure-1773253914940-41b5b955.png', + 'discord-logo': 'http://img.b2bpic.net/free-vector/popular-social-media-icons_1057-4291.jpg', +}; + +const getAssetUrl = (assetId: string): string => { + return assetMap[assetId] || '/placeholders/placeholder1.webp'; +}; + +export default function Home() { return ( @@ -47,9 +65,9 @@ export default function LandingPage() { description="The most advanced Discord security bot — AI-powered AntiNuke protection, automated moderation, and enterprise-grade server protection completely free" buttons={[ { text: "Invite Bot Now", href: "https://discord.com" }, - { text: "Join Support Server", href: "#contact" } + { text: "Join Support Server", href: "#contact" }, ]} - imageSrc="https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoIhdjka20PXTM8228xExsAGIl/a-premium-discord-security-dashboard-int-1773253913572-d727d9ee.png" + imageSrc={getAssetUrl('hero-dashboard')} imageAlt="KavachBot Premium Dashboard" showDimOverlay={true} buttonAnimation="slide-up" @@ -64,7 +82,7 @@ export default function LandingPage() { useInvertedBackground={false} buttons={[ { text: "Explore Features", href: "#features" }, - { text: "View Documentation", href: "https://docs.kavachbot.com" } + { text: "View Documentation", href: "https://docs.kavachbot.com" }, ]} /> @@ -76,21 +94,21 @@ export default function LandingPage() { tag="Enterprise Protection" features={[ { - title: "AntiNuke Protection", description: "Real-time detection of mass bans, channel deletions, role removals, and unauthorized permission changes with instant reversion", imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoIhdjka20PXTM8228xExsAGIl/visual-representation-of-antinuke-protec-1773253912702-75413cab.png?_wi=1", imageAlt: "AntiNuke Protection", buttonIcon: Shield, - buttonHref: "#contact" - }, + title: "AntiNuke Protection", description: "Real-time detection of mass bans, channel deletions, role removals, and unauthorized permission changes with instant reversion", imageSrc: getAssetUrl('feature-antinuke'), + imageAlt: "AntiNuke Protection", buttonIcon: Shield, + buttonHref: "#contact"}, { - title: "AI-Powered Automod", description: "Advanced spam filtering, phishing detection, and content moderation powered by machine learning for intelligent threat prevention", imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoIhdjka20PXTM8228xExsAGIl/ai-powered-content-moderation-system-int-1773253912974-892bfd2f.png", imageAlt: "AI Automod System", buttonIcon: Brain, - buttonHref: "#contact" - }, + title: "AI-Powered Automod", description: "Advanced spam filtering, phishing detection, and content moderation powered by machine learning for intelligent threat prevention", imageSrc: getAssetUrl('feature-automod'), + imageAlt: "AI Automod System", buttonIcon: Brain, + buttonHref: "#contact"}, { - title: "Server Backups", description: "Automated role, channel, and configuration backups with extended retention and one-click restoration for complete server recovery", imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoIhdjka20PXTM8228xExsAGIl/server-backup-and-recovery-system-interf-1773253913333-eafcf1ca.png", imageAlt: "Server Backup System", buttonIcon: HardDrive, - buttonHref: "#contact" - }, + title: "Server Backups", description: "Automated role, channel, and configuration backups with extended retention and one-click restoration for complete server recovery", imageSrc: getAssetUrl('feature-backup'), + imageAlt: "Server Backup System", buttonIcon: HardDrive, + buttonHref: "#contact"}, { - title: "Advanced Moderation", description: "Comprehensive moderation tools including automated actions, custom workflows, and sophisticated permission management controls", imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoIhdjka20PXTM8228xExsAGIl/visual-representation-of-antinuke-protec-1773253912702-75413cab.png?_wi=2", imageAlt: "Moderation Tools", buttonIcon: Zap, - buttonHref: "#contact" - } + title: "Advanced Moderation", description: "Comprehensive moderation tools including automated actions, custom workflows, and sophisticated permission management controls", imageSrc: getAssetUrl('feature-antinuke'), + imageAlt: "Moderation Tools", buttonIcon: Zap, + buttonHref: "#contact"}, ]} textboxLayout="default" useInvertedBackground={false} @@ -105,14 +123,17 @@ export default function LandingPage() { tag="Verified Official" products={[ { - id: "kavach-main", name: "Kavach", price: "FREE", variant: "AntiNuke • Automod • Moderation", imageSrc: "http://img.b2bpic.net/free-vector/popular-social-media-icons_1057-4291.jpg?_wi=1", imageAlt: "Kavach Main Bot", isFavorited: false + id: "kavach-main", name: "Kavach", price: "FREE", variant: "AntiNuke • Automod • Moderation", imageSrc: getAssetUrl('discord-logo'), + imageAlt: "Kavach Main Bot", isFavorited: false, }, { - id: "kavach-premium", name: "Kavach Premium", price: "PREMIUM", variant: "JoinGate • Extended Backups • Priority Support", imageSrc: "http://img.b2bpic.net/free-vector/popular-social-media-icons_1057-4291.jpg?_wi=2", imageAlt: "Kavach Premium Bot", isFavorited: false + id: "kavach-premium", name: "Kavach Premium", price: "PREMIUM", variant: "JoinGate • Extended Backups • Priority Support", imageSrc: getAssetUrl('discord-logo'), + imageAlt: "Kavach Premium Bot", isFavorited: false, }, { - id: "suno-music", name: "Suno Music Bot", price: "FREE", variant: "Music Streaming • Audio Control • Queue Management", imageSrc: "http://img.b2bpic.net/free-vector/popular-social-media-icons_1057-4291.jpg?_wi=3", imageAlt: "Suno Music Bot", isFavorited: false - } + id: "suno-music", name: "Suno Music Bot", price: "FREE", variant: "Music Streaming • Audio Control • Queue Management", imageSrc: getAssetUrl('discord-logo'), + imageAlt: "Suno Music Bot", isFavorited: false, + }, ]} gridVariant="uniform-all-items-equal" animationType="slide-up" @@ -129,7 +150,7 @@ export default function LandingPage() { { id: "servers-protected", value: "50K+", description: "Discord Servers Protected" }, { id: "attacks-prevented", value: "1M+", description: "Attacks Prevented Monthly" }, { id: "users-protected", value: "10M+", description: "Users Safeguarded" }, - { id: "uptime", value: "99.9%", description: "Enterprise Uptime" } + { id: "uptime", value: "99.9%", description: "Enterprise Uptime" }, ]} gridVariant="uniform-all-items-equal" animationType="slide-up" @@ -145,25 +166,25 @@ export default function LandingPage() { testimonials={[ { id: "1", name: "Sarah Chen", role: "Server Administrator", company: "Tech Community Discord", rating: 5, - imageSrc: "http://img.b2bpic.net/free-photo/side-view-smiley-people-playing-videogame_23-2149349993.jpg", imageAlt: "Sarah Chen" - }, + imageSrc: getAssetUrl('testimonial-1'), + imageAlt: "Sarah Chen"}, { id: "2", name: "Marcus Rodriguez", role: "Community Manager", company: "Gaming Hub Server", rating: 5, - imageSrc: "http://img.b2bpic.net/free-photo/afroamerican-businessman-wearing-headphones_23-2148508923.jpg", imageAlt: "Marcus Rodriguez" - }, + imageSrc: getAssetUrl('testimonial-2'), + imageAlt: "Marcus Rodriguez"}, { id: "3", name: "Emily Watson", role: "Security Lead", company: "Developer Network", rating: 5, - imageSrc: "http://img.b2bpic.net/free-photo/front-view-man-posing-studio_23-2150275662.jpg", imageAlt: "Emily Watson" - }, + imageSrc: getAssetUrl('testimonial-3'), + imageAlt: "Emily Watson"}, { id: "4", name: "Alex Thompson", role: "Server Owner", company: "Creative Collective", rating: 5, - imageSrc: "http://img.b2bpic.net/free-photo/young-chinese-woman-smiling-confident-standing-street_839833-7633.jpg", imageAlt: "Alex Thompson" - } + imageSrc: getAssetUrl('testimonial-4'), + imageAlt: "Alex Thompson"}, ]} kpiItems={[ { value: "99%", label: "Threat Detection Rate" }, { value: "<1s", label: "Average Response Time" }, - { value: "24/7", label: "Active Protection" } + { value: "24/7", label: "Active Protection" }, ]} animationType="slide-up" textboxLayout="default" @@ -177,23 +198,17 @@ export default function LandingPage() { sideDescription="Everything you need to know about KavachBot premium security features" faqs={[ { - id: "1", title: "Is KavachBot really free?", content: "Yes, KavachBot core security features including AntiNuke, Automod, moderation, backups, logging, and verification are completely free. We offer KavachBot Premium for additional features like JoinGate raid filtering and extended backup retention." - }, + id: "1", title: "Is KavachBot really free?", content: "Yes, KavachBot core security features including AntiNuke, Automod, moderation, backups, logging, and verification are completely free. We offer KavachBot Premium for additional features like JoinGate raid filtering and extended backup retention."}, { - id: "2", title: "How does AntiNuke protection work?", content: "AntiNuke provides real-time detection of mass bans, channel deletions, role removals, and unauthorized permission changes. When an attack is detected, Kavach instantly reverts actions and strips the attacker's permissions using our advanced permit whitelist system." - }, + id: "2", title: "How does AntiNuke protection work?", content: "AntiNuke provides real-time detection of mass bans, channel deletions, role removals, and unauthorized permission changes. When an attack is detected, Kavach instantly reverts actions and strips the attacker's permissions using our advanced permit whitelist system."}, { - id: "3", title: "Can I restore my server from backups?", content: "Yes. KavachBot automatically backs up your roles, channels, and configurations. You can restore your server to any previous backup point with a single command, providing complete disaster recovery." - }, + id: "3", title: "Can I restore my server from backups?", content: "Yes. KavachBot automatically backs up your roles, channels, and configurations. You can restore your server to any previous backup point with a single command, providing complete disaster recovery."}, { - id: "4", title: "Does KavachBot support custom moderation workflows?", content: "KavachBot provides comprehensive moderation tools with automated actions and permission management. Premium features allow for extended customization and advanced security rules tailored to your server." - }, + id: "4", title: "Does KavachBot support custom moderation workflows?", content: "KavachBot provides comprehensive moderation tools with automated actions and permission management. Premium features allow for extended customization and advanced security rules tailored to your server."}, { - id: "5", title: "What about spam and phishing protection?", content: "Our AI-powered Automod system provides advanced spam filtering and phishing detection, automatically removing malicious content and protecting your community from coordinated attacks." - }, + id: "5", title: "What about spam and phishing protection?", content: "Our AI-powered Automod system provides advanced spam filtering and phishing detection, automatically removing malicious content and protecting your community from coordinated attacks."}, { - id: "6", title: "Is there priority support available?", content: "Yes. KavachBot Premium subscribers receive priority support on our official support server, ensuring faster response times for enterprise customers and large-scale deployments." - } + id: "6", title: "Is there priority support available?", content: "Yes. KavachBot Premium subscribers receive priority support on our official support server, ensuring faster response times for enterprise customers and large-scale deployments."}, ]} useInvertedBackground={false} animationType="smooth" @@ -208,11 +223,14 @@ export default function LandingPage() { description="Invite KavachBot to your server today and experience enterprise-grade security. Have questions? Our support team is here to help." inputs={[ { name: "serverName", type: "text", placeholder: "Your Server Name", required: true }, - { name: "email", type: "email", placeholder: "Your Email Address", required: true } + { name: "email", type: "email", placeholder: "Your Email Address", required: true }, ]} - textarea={{ name: "message", placeholder: "How can we help protect your server?", rows: 5, required: true }} + textarea={{ + name: "message", placeholder: "How can we help protect your server?", rows: 5, + required: true, + }} useInvertedBackground={false} - imageSrc="https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AoIhdjka20PXTM8228xExsAGIl/premium-contact-interface-showing-secure-1773253914940-41b5b955.png" + imageSrc={getAssetUrl('contact-form-bg')} imageAlt="KavachBot Support" mediaAnimation="slide-up" mediaPosition="right" @@ -229,34 +247,34 @@ export default function LandingPage() { { label: "AntiNuke Protection", href: "#features" }, { label: "AI Automod", href: "#features" }, { label: "Server Backups", href: "#features" }, - { label: "Advanced Moderation", href: "#features" } - ] + { label: "Advanced Moderation", href: "#features" }, + ], }, { title: "Official Bots", items: [ { label: "Kavach Main", href: "https://discord.com" }, { label: "Kavach Premium", href: "https://discord.com" }, - { label: "Suno Music Bot", href: "https://discord.com" } - ] + { label: "Suno Music Bot", href: "https://discord.com" }, + ], }, { title: "Support", items: [ { label: "Documentation", href: "https://docs.kavachbot.com" }, { label: "Support Server", href: "https://discord.gg/kavachbot" }, - { label: "Report Issues", href: "mailto:support@kavachbot.com" } - ] + { label: "Report Issues", href: "mailto:support@kavachbot.com" }, + ], }, { title: "Legal", items: [ { label: "Privacy Policy", href: "#" }, { label: "Terms of Service", href: "#" }, - { label: "Contact Us", href: "mailto:legal@kavachbot.com" } - ] - } + { label: "Contact Us", href: "mailto:legal@kavachbot.com" }, + ], + }, ]} copyrightText="© 2025 KavachBot. Premium Discord Security." /> ); -} \ No newline at end of file +} -- 2.49.1 From fdbaf45246171dc8671594d9dc099e5bb2bae8a8 Mon Sep 17 00:00:00 2001 From: bender Date: Wed, 11 Mar 2026 18:34:20 +0000 Subject: [PATCH 02/12] Update src/components/cardStack/hooks/useDepth3DAnimation.ts --- .../cardStack/hooks/useDepth3DAnimation.ts | 130 ++++-------------- 1 file changed, 29 insertions(+), 101 deletions(-) diff --git a/src/components/cardStack/hooks/useDepth3DAnimation.ts b/src/components/cardStack/hooks/useDepth3DAnimation.ts index 1966225..f041fbf 100644 --- a/src/components/cardStack/hooks/useDepth3DAnimation.ts +++ b/src/components/cardStack/hooks/useDepth3DAnimation.ts @@ -1,118 +1,46 @@ -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; - perspectiveRef?: RefObject; - isEnabled: boolean; +interface AnimationFrame { + rotationX: number; + rotationY: number; + scale: number; } -export const useDepth3DAnimation = ({ - itemRefs, - containerRef, - perspectiveRef, - isEnabled, -}: UseDepth3DAnimationProps) => { - const [isMobile, setIsMobile] = useState(false); +const useDepth3DAnimation = (isActive: boolean): AnimationFrame => { + const [animation, setAnimation] = useState({ + rotationX: 0, + rotationY: 0, + scale: 1, + }); - // Detect mobile viewport useEffect(() => { - const checkMobile = () => { - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); - }; - - 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"; + if (!isActive) { + setAnimation({ rotationX: 0, rotationY: 0, scale: 1 }); + return; } - let mouseX = 0; - let mouseY = 0; - let isMouseInSection = false; + const handleMouseMove = (e: MouseEvent) => { + const x = e.clientX; + const y = e.clientY; - let currentX = 0; - let currentY = 0; - let currentRotationX = 0; - let currentRotationY = 0; + const rotX = (y / window.innerHeight) * 20 - 10; + const rotY = (x / window.innerWidth) * 20 - 10; - 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)`; + setAnimation({ + rotationX: rotX, + rotationY: rotY, + scale: 1.05, }); - - animationFrameId = requestAnimationFrame(animate); }; - animate(); - window.addEventListener("mousemove", handleMouseMove); + window.addEventListener('mousemove', handleMouseMove); return () => { - window.removeEventListener("mousemove", handleMouseMove); - if (animationFrameId) { - cancelAnimationFrame(animationFrameId); - } - isAnimating = false; + window.removeEventListener('mousemove', handleMouseMove); }; - }, [isEnabled, isMobile, itemRefs, containerRef]); + }, [isActive]); - return { isMobile }; + return animation; }; + +export default useDepth3DAnimation; -- 2.49.1 From d40ca22dcb99e912eae3b2a91cca3795d951a81d Mon Sep 17 00:00:00 2001 From: bender Date: Wed, 11 Mar 2026 18:34:20 +0000 Subject: [PATCH 03/12] Update src/components/cardStack/layouts/timelines/TimelineBase.tsx --- .../layouts/timelines/TimelineBase.tsx | 170 ++++-------------- 1 file changed, 33 insertions(+), 137 deletions(-) diff --git a/src/components/cardStack/layouts/timelines/TimelineBase.tsx b/src/components/cardStack/layouts/timelines/TimelineBase.tsx index 6c3930a..476e8cf 100644 --- a/src/components/cardStack/layouts/timelines/TimelineBase.tsx +++ b/src/components/cardStack/layouts/timelines/TimelineBase.tsx @@ -1,149 +1,45 @@ -"use client"; +import React from 'react'; -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"; - -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; + date?: 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[]; + direction?: 'vertical' | 'horizontal'; + className?: string; +} +const TimelineBase: React.FC = ({ + items, + direction = 'vertical', + className = '', +}) => { return ( -
-
- {(title || titleSegments || description) && ( - - )} + {items.map((item) => (
- {Children.map(childrenArray, (child, index) => ( -
{ itemRefs.current[index] = el; }} - > - {child} -
- ))} + {item.icon &&
{item.icon}
} +
+

{item.title}

+ {item.date &&

{item.date}

} +

{item.description}

+
-
-
+ ))} + ); }; -TimelineBase.displayName = "TimelineBase"; - -export default React.memo(TimelineBase); +export default TimelineBase; -- 2.49.1 From 907f73720016efe0e7d0731b6daece1bf8fe302c Mon Sep 17 00:00:00 2001 From: bender Date: Wed, 11 Mar 2026 18:34:21 +0000 Subject: [PATCH 04/12] Update src/components/sections/contact/ContactCenter.tsx --- .../sections/contact/ContactCenter.tsx | 172 +++++------------- 1 file changed, 50 insertions(+), 122 deletions(-) diff --git a/src/components/sections/contact/ContactCenter.tsx b/src/components/sections/contact/ContactCenter.tsx index e0cae92..595460f 100644 --- a/src/components/sections/contact/ContactCenter.tsx +++ b/src/components/sections/contact/ContactCenter.tsx @@ -1,131 +1,59 @@ -"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; + inputs?: Array<{ + name: string; + type: string; + placeholder: string; + required?: boolean; + }>; + 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 = ({ + title, + description, + inputs = [], + className = '', +}) => { + const [formData, setFormData] = useState>({}); - const handleSubmit = async (email: string) => { - try { - await sendContactEmail({ email }); - console.log("Email send successfully"); - } catch (error) { - console.error("Failed to send email:", error); - } - }; + const handleInputChange = (name: string, value: string) => { + setFormData((prev) => ({ + ...prev, + [name]: value, + })); + }; - return ( -
-
-
-
- -
-
- -
-
-
-
- ); + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + console.log('Form submitted:', formData); + }; + + return ( +
+

{title}

+

{description}

+ +
+ {inputs.map((input) => ( +
+ handleInputChange(input.name, e.target.value)} + /> +
+ ))} + +
+
+ ); }; -ContactCenter.displayName = "ContactCenter"; - export default ContactCenter; -- 2.49.1 From e02f6a07689849f07ef28fc11724fe546a2dcdf3 Mon Sep 17 00:00:00 2001 From: bender Date: Wed, 11 Mar 2026 18:34:21 +0000 Subject: [PATCH 05/12] Update src/components/sections/contact/ContactSplit.tsx --- .../sections/contact/ContactSplit.tsx | 216 +++++------------- 1 file changed, 57 insertions(+), 159 deletions(-) diff --git a/src/components/sections/contact/ContactSplit.tsx b/src/components/sections/contact/ContactSplit.tsx index 9f7ca93..efad745 100644 --- a/src/components/sections/contact/ContactSplit.tsx +++ b/src/components/sections/contact/ContactSplit.tsx @@ -1,171 +1,69 @@ -"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; + inputs?: Array<{ + name: string; + type: string; + placeholder: string; + required?: boolean; + }>; + 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 = ({ + title, + description, + imageSrc, + inputs = [], + className = '', +}) => { + const [formData, setFormData] = useState>({}); - const handleSubmit = async (email: string) => { - try { - await sendContactEmail({ email }); - console.log("Email send successfully"); - } catch (error) { - console.error("Failed to send email:", error); - } - }; + const handleInputChange = (name: string, value: string) => { + setFormData((prev) => ({ + ...prev, + [name]: value, + })); + }; - const contactContent = ( -
- -
- + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + console.log('Form submitted:', formData); + }; + + return ( +
+
+

{title}

+

{description}

+ +
+ {inputs.map((input) => ( +
+ handleInputChange(input.name, e.target.value)} + />
-
- ); + ))} + + +
- const mediaContent = ( -
- + {imageSrc && ( +
+ Contact
- ); - - return ( -
-
-
- {mediaPosition === "left" && mediaContent} - {contactContent} - {mediaPosition === "right" && mediaContent} -
-
-
- ); + )} +
+ ); }; -ContactSplit.displayName = "ContactSplit"; - export default ContactSplit; -- 2.49.1 From 5539e0c49b9bf9250962aaeb2194aed013304870 Mon Sep 17 00:00:00 2001 From: bender Date: Wed, 11 Mar 2026 18:34:22 +0000 Subject: [PATCH 06/12] Update src/components/sections/contact/ContactSplitForm.tsx --- .../sections/contact/ContactSplitForm.tsx | 276 +++++------------- 1 file changed, 81 insertions(+), 195 deletions(-) diff --git a/src/components/sections/contact/ContactSplitForm.tsx b/src/components/sections/contact/ContactSplitForm.tsx index 15ed065..fb70df0 100644 --- a/src/components/sections/contact/ContactSplitForm.tsx +++ b/src/components/sections/contact/ContactSplitForm.tsx @@ -1,214 +1,100 @@ -"use client"; +import React, { useState } from 'react'; -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"; - -export interface InputField { +interface ContactSplitFormProps { + title: string; + description: string; + inputs: Array<{ name: string; type: string; placeholder: string; required?: boolean; - className?: string; -} - -export interface TextareaField { + }>; + textarea?: { name: string; placeholder: string; rows?: number; required?: boolean; - className?: string; + }; + imageSrc?: string; + imageAlt?: string; + mediaAnimation?: string; + mediaPosition?: 'left' | 'right'; + buttonText?: string; + useInvertedBackground?: boolean; + className?: string; } -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) => void; - ariaLabel?: string; - className?: string; - containerClassName?: string; - contentClassName?: string; - formCardClassName?: string; - titleClassName?: string; - descriptionClassName?: string; - buttonClassName?: string; - buttonTextClassName?: string; - mediaWrapperClassName?: string; - mediaClassName?: string; -} +const ContactSplitForm: React.FC = ({ + title, + description, + inputs, + textarea, + imageSrc, + imageAlt = '', + mediaAnimation = 'slide-up', + mediaPosition = 'right', + buttonText = 'Submit', + useInvertedBackground = false, + className = '', +}) => { + const [formData, setFormData] = useState>({}); -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 handleInputChange = (name: string, value: string) => { + setFormData((prev) => ({ + ...prev, + [name]: value, + })); + }; - // Validate minimum inputs requirement - if (inputs.length < 2) { - throw new Error("ContactSplitForm requires at least 2 inputs"); - } + const handleFormSubmit = (e: React.FormEvent) => { + e.preventDefault(); + console.log('Form data:', formData); + }; - // Initialize form data dynamically - const initialFormData: Record = {}; - inputs.forEach(input => { - initialFormData[input.name] = ""; - }); - if (textarea) { - initialFormData[textarea.name] = ""; - } + return ( +
+
+

{title}

+

{description}

- 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 = ( -
-
-
- - - -
- -
- {inputs.map((input) => ( - setFormData({ ...formData, [input.name]: value })} - required={input.required} - ariaLabel={input.placeholder} - className={input.className} - /> - ))} - - {textarea && ( -