Complete Next.js website with Docker containerization: - Next.js 14 with TypeScript and Tailwind CSS - Responsive design with modern UI components - Hero section, features showcase, testimonials - FAQ section with comprehensive content - Contact forms and newsletter signup - Docker production build with Nginx - Health checks and monitoring support - SEO optimization and performance tuning Ready for integration as git submodule in main CHORUS project. Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
139 lines
4.5 KiB
TypeScript
139 lines
4.5 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState, useEffect } from 'react';
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
import { Tooltip } from 'antd';
|
|
import {
|
|
RocketIcon,
|
|
ZapIcon,
|
|
DatabaseIcon,
|
|
BrainCircuitIcon,
|
|
NetworkIcon,
|
|
ChevronUpIcon
|
|
} from 'lucide-react';
|
|
|
|
interface SectionNavigationProps {
|
|
className?: string;
|
|
}
|
|
|
|
const sections = [
|
|
{ id: 'whoosh', name: 'WHOOSH', icon: RocketIcon, color: '#007aff' },
|
|
{ id: 'bzzz', name: 'BZZZ', icon: ZapIcon, color: '#30d158' },
|
|
{ id: 'slurp', name: 'SLURP', icon: DatabaseIcon, color: '#eab308' },
|
|
{ id: 'cooee', name: 'COOEE', icon: BrainCircuitIcon, color: '#a855f7' },
|
|
{ id: 'integration', name: 'Integration', icon: NetworkIcon, color: '#f97316' },
|
|
];
|
|
|
|
export default function SectionNavigation({ className = '' }: SectionNavigationProps) {
|
|
const [activeSection, setActiveSection] = useState('');
|
|
const [isVisible, setIsVisible] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const handleScroll = () => {
|
|
const scrollY = window.scrollY;
|
|
setIsVisible(scrollY > 800);
|
|
|
|
// Find active section
|
|
const sectionElements = sections.map(section =>
|
|
document.getElementById(section.id)
|
|
).filter(Boolean);
|
|
|
|
for (let i = sectionElements.length - 1; i >= 0; i--) {
|
|
const element = sectionElements[i];
|
|
if (element && element.offsetTop <= scrollY + 200) {
|
|
setActiveSection(sections[i].id);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
window.addEventListener('scroll', handleScroll);
|
|
handleScroll(); // Check initial position
|
|
|
|
return () => window.removeEventListener('scroll', handleScroll);
|
|
}, []);
|
|
|
|
const scrollToSection = (sectionId: string) => {
|
|
const element = document.getElementById(sectionId);
|
|
if (element) {
|
|
const headerOffset = 80; // Account for fixed header
|
|
const elementPosition = element.offsetTop - headerOffset;
|
|
|
|
window.scrollTo({
|
|
top: elementPosition,
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
};
|
|
|
|
const scrollToTop = () => {
|
|
window.scrollTo({
|
|
top: 0,
|
|
behavior: 'smooth'
|
|
});
|
|
};
|
|
|
|
return (
|
|
<AnimatePresence>
|
|
{isVisible && (
|
|
<motion.div
|
|
initial={{ opacity: 0, x: 100 }}
|
|
animate={{ opacity: 1, x: 0 }}
|
|
exit={{ opacity: 0, x: 100 }}
|
|
transition={{ duration: 0.3 }}
|
|
className={`fixed right-6 top-1/2 transform -translate-y-1/2 z-50 ${className}`}
|
|
>
|
|
<div className="glass-effect rounded-full p-2 border border-gray-600">
|
|
{/* Section navigation dots */}
|
|
{sections.map((section) => {
|
|
const Icon = section.icon;
|
|
const isActive = activeSection === section.id;
|
|
|
|
return (
|
|
<Tooltip
|
|
key={section.id}
|
|
title={section.name}
|
|
placement="left"
|
|
overlayClassName="section-nav-tooltip"
|
|
>
|
|
<motion.button
|
|
onClick={() => scrollToSection(section.id)}
|
|
className={`block w-12 h-12 rounded-full mb-2 last:mb-0 flex items-center justify-center border-2 transition-all duration-300 ${
|
|
isActive
|
|
? 'border-white shadow-lg'
|
|
: 'border-transparent hover:border-gray-400'
|
|
}`}
|
|
style={{
|
|
backgroundColor: isActive ? `${section.color}20` : 'transparent'
|
|
}}
|
|
whileHover={{ scale: 1.1 }}
|
|
whileTap={{ scale: 0.95 }}
|
|
>
|
|
<Icon
|
|
size={20}
|
|
style={{ color: isActive ? section.color : '#9ca3af' }}
|
|
/>
|
|
</motion.button>
|
|
</Tooltip>
|
|
);
|
|
})}
|
|
|
|
{/* Scroll to top button */}
|
|
<div className="border-t border-gray-600 mt-2 pt-2">
|
|
<Tooltip title="Back to Top" placement="left">
|
|
<motion.button
|
|
onClick={scrollToTop}
|
|
className="w-12 h-12 rounded-full flex items-center justify-center border-2 border-transparent hover:border-gray-400 transition-all duration-300"
|
|
whileHover={{ scale: 1.1 }}
|
|
whileTap={{ scale: 0.95 }}
|
|
>
|
|
<ChevronUpIcon size={20} className="text-gray-400" />
|
|
</motion.button>
|
|
</Tooltip>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
);
|
|
} |