313 lines
14 KiB
TypeScript
313 lines
14 KiB
TypeScript
|
|
'use client';
|
||
|
|
|
||
|
|
import { useState, useEffect, useRef } from 'react';
|
||
|
|
import { useRouter } from 'next/navigation';
|
||
|
|
import AWSProductsSection from './AWSProductsSection';
|
||
|
|
import Navbar from './Navbar';
|
||
|
|
import Footer from './Footer';
|
||
|
|
import { content, slides } from '../data/content';
|
||
|
|
|
||
|
|
export default function HomeClient({ lang }: { lang: string }) {
|
||
|
|
const router = useRouter();
|
||
|
|
const currentLang = lang;
|
||
|
|
|
||
|
|
const [currentSlide, setCurrentSlide] = useState(0);
|
||
|
|
const [isAutoPlaying, setIsAutoPlaying] = useState(true);
|
||
|
|
const autoPlayTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||
|
|
|
||
|
|
// Validate language and redirect if invalid
|
||
|
|
useEffect(() => {
|
||
|
|
const supportedLangs = ['zh-CN', 'zh-TW', 'en', 'ko', 'ja'];
|
||
|
|
if (!supportedLangs.includes(currentLang)) {
|
||
|
|
router.push('/en');
|
||
|
|
}
|
||
|
|
}, [currentLang, router]);
|
||
|
|
|
||
|
|
// Cleanup timeout on component unmount
|
||
|
|
useEffect(() => {
|
||
|
|
return () => {
|
||
|
|
if (autoPlayTimeoutRef.current) {
|
||
|
|
clearTimeout(autoPlayTimeoutRef.current);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const currentContent = content[currentLang as keyof typeof content] || content.en;
|
||
|
|
const currentSlideData = slides[currentSlide];
|
||
|
|
|
||
|
|
// Carousel auto-play functionality
|
||
|
|
useEffect(() => {
|
||
|
|
if (!isAutoPlaying) return;
|
||
|
|
|
||
|
|
const interval = setInterval(() => {
|
||
|
|
setCurrentSlide((prev) => (prev + 1) % slides.length);
|
||
|
|
}, 5000); // Change slide every 5 seconds
|
||
|
|
|
||
|
|
return () => clearInterval(interval);
|
||
|
|
}, [isAutoPlaying, slides.length]);
|
||
|
|
|
||
|
|
const resumeAutoPlayWithDelay = () => {
|
||
|
|
if (autoPlayTimeoutRef.current) {
|
||
|
|
clearTimeout(autoPlayTimeoutRef.current);
|
||
|
|
}
|
||
|
|
autoPlayTimeoutRef.current = setTimeout(() => {
|
||
|
|
setIsAutoPlaying(true);
|
||
|
|
}, 10000); // Resume auto-play after 10 seconds
|
||
|
|
};
|
||
|
|
|
||
|
|
// Carousel navigation functions
|
||
|
|
const nextSlide = () => {
|
||
|
|
setCurrentSlide((prev) => (prev + 1) % slides.length);
|
||
|
|
setIsAutoPlaying(false);
|
||
|
|
resumeAutoPlayWithDelay();
|
||
|
|
};
|
||
|
|
|
||
|
|
const prevSlide = () => {
|
||
|
|
setCurrentSlide((prev) => (prev - 1 + slides.length) % slides.length);
|
||
|
|
setIsAutoPlaying(false);
|
||
|
|
resumeAutoPlayWithDelay();
|
||
|
|
};
|
||
|
|
|
||
|
|
const goToSlide = (index: number) => {
|
||
|
|
setCurrentSlide(index);
|
||
|
|
setIsAutoPlaying(false);
|
||
|
|
resumeAutoPlayWithDelay();
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleLanguageChange = (lang: string) => {
|
||
|
|
router.push(`/${lang}`);
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="min-h-screen bg-gray-900 text-white">
|
||
|
|
<Navbar
|
||
|
|
currentLang={currentLang}
|
||
|
|
currentContent={currentContent}
|
||
|
|
handleLanguageChange={handleLanguageChange}
|
||
|
|
/>
|
||
|
|
|
||
|
|
{/* Hero Section */}
|
||
|
|
<div className="relative bg-black overflow-hidden min-h-screen flex items-center">
|
||
|
|
{/* Background with curved flowing design */}
|
||
|
|
<div className="absolute inset-0 transition-all duration-1000 ease-in-out">
|
||
|
|
<div
|
||
|
|
className={`absolute inset-0 bg-gradient-to-br ${currentSlideData.background.gradient} transition-all duration-1000 ease-in-out`}
|
||
|
|
></div>
|
||
|
|
<svg
|
||
|
|
className="absolute inset-0 w-full h-full"
|
||
|
|
viewBox="0 0 1200 800"
|
||
|
|
preserveAspectRatio="xMidYMid slice"
|
||
|
|
>
|
||
|
|
<defs>
|
||
|
|
<linearGradient
|
||
|
|
id={`gradient-${currentSlide}`}
|
||
|
|
x1="0%"
|
||
|
|
y1="0%"
|
||
|
|
x2="100%"
|
||
|
|
y2="100%"
|
||
|
|
>
|
||
|
|
<stop
|
||
|
|
offset="0%"
|
||
|
|
stopColor={currentSlideData.background.primaryColor}
|
||
|
|
/>
|
||
|
|
<stop
|
||
|
|
offset="100%"
|
||
|
|
stopColor={currentSlideData.background.accentColor}
|
||
|
|
/>
|
||
|
|
</linearGradient>
|
||
|
|
</defs>
|
||
|
|
<path
|
||
|
|
d="M0,400 Q300,200 600,400 T1200,400 L1200,800 L0,800 Z"
|
||
|
|
fill={`url(#gradient-${currentSlide})`}
|
||
|
|
className="transition-all duration-1000 ease-in-out"
|
||
|
|
/>
|
||
|
|
<path
|
||
|
|
d="M0,500 Q400,300 800,500 T1200,500 L1200,800 L0,800 Z"
|
||
|
|
fill={currentSlideData.background.secondaryColor}
|
||
|
|
className="transition-all duration-1000 ease-in-out"
|
||
|
|
/>
|
||
|
|
<path
|
||
|
|
d="M0,600 Q200,400 400,600 T800,600 Q1000,500 1200,600 L1200,800 L0,800 Z"
|
||
|
|
fill={currentSlideData.background.primaryColor}
|
||
|
|
className="transition-all duration-1000 ease-in-out"
|
||
|
|
/>
|
||
|
|
</svg>
|
||
|
|
|
||
|
|
{/* Additional animated elements for each slide */}
|
||
|
|
<div className="absolute inset-0 overflow-hidden">
|
||
|
|
{currentSlide === 0 && (
|
||
|
|
<div className="absolute top-20 right-20 w-32 h-32 bg-blue-500 rounded-full opacity-10 animate-pulse"></div>
|
||
|
|
)}
|
||
|
|
{currentSlide === 1 && (
|
||
|
|
<div className="absolute bottom-32 left-20 w-24 h-24 bg-purple-500 rounded-full opacity-20 animate-bounce"></div>
|
||
|
|
)}
|
||
|
|
{currentSlide === 2 && (
|
||
|
|
<div className="absolute top-32 left-32 w-28 h-28 bg-green-500 rounded-full opacity-15 animate-ping"></div>
|
||
|
|
)}
|
||
|
|
{currentSlide === 3 && (
|
||
|
|
<div className="absolute bottom-20 right-32 w-36 h-36 bg-orange-500 rounded-full opacity-10 animate-pulse"></div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Navigation arrows */}
|
||
|
|
<button
|
||
|
|
onClick={prevSlide}
|
||
|
|
className="absolute left-4 top-1/2 transform -translate-y-1/2 w-12 h-12 bg-black bg-opacity-50 rounded-full flex items-center justify-center text-white hover:bg-opacity-70 transition-all z-20"
|
||
|
|
>
|
||
|
|
<svg
|
||
|
|
className="w-6 h-6"
|
||
|
|
fill="none"
|
||
|
|
stroke="currentColor"
|
||
|
|
viewBox="0 0 24 24"
|
||
|
|
>
|
||
|
|
<path
|
||
|
|
strokeLinecap="round"
|
||
|
|
strokeLinejoin="round"
|
||
|
|
strokeWidth={2}
|
||
|
|
d="M15 19l-7-7 7-7"
|
||
|
|
/>
|
||
|
|
</svg>
|
||
|
|
</button>
|
||
|
|
<button
|
||
|
|
onClick={nextSlide}
|
||
|
|
className="absolute right-4 top-1/2 transform -translate-y-1/2 w-12 h-12 bg-black bg-opacity-50 rounded-full flex items-center justify-center text-white hover:bg-opacity-70 transition-all z-20"
|
||
|
|
>
|
||
|
|
<svg
|
||
|
|
className="w-6 h-6"
|
||
|
|
fill="none"
|
||
|
|
stroke="currentColor"
|
||
|
|
viewBox="0 0 24 24"
|
||
|
|
>
|
||
|
|
<path
|
||
|
|
strokeLinecap="round"
|
||
|
|
strokeLinejoin="round"
|
||
|
|
strokeWidth={2}
|
||
|
|
d="M9 5l7 7-7 7"
|
||
|
|
/>
|
||
|
|
</svg>
|
||
|
|
</button>
|
||
|
|
|
||
|
|
<div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-24 lg:py-32 z-10">
|
||
|
|
<div className="text-left max-w-3xl">
|
||
|
|
<div className="transition-all duration-500 ease-in-out">
|
||
|
|
<h1
|
||
|
|
className="text-4xl md:text-6xl font-bold leading-tight mb-6 text-white"
|
||
|
|
key={`title-${currentSlide}`}
|
||
|
|
>
|
||
|
|
{
|
||
|
|
currentSlideData.title[
|
||
|
|
currentLang as keyof typeof currentSlideData.title
|
||
|
|
]
|
||
|
|
}
|
||
|
|
</h1>
|
||
|
|
<h2
|
||
|
|
className="text-4xl md:text-6xl font-bold leading-tight mb-8 text-white"
|
||
|
|
key={`subtitle-${currentSlide}`}
|
||
|
|
>
|
||
|
|
{
|
||
|
|
currentSlideData.subtitle[
|
||
|
|
currentLang as keyof typeof currentSlideData.subtitle
|
||
|
|
]
|
||
|
|
}
|
||
|
|
</h2>
|
||
|
|
<p
|
||
|
|
className="text-lg text-gray-300 mb-12 leading-relaxed"
|
||
|
|
key={`description-${currentSlide}`}
|
||
|
|
>
|
||
|
|
{
|
||
|
|
currentSlideData.description[
|
||
|
|
currentLang as keyof typeof currentSlideData.description
|
||
|
|
]
|
||
|
|
}
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
<div className="flex flex-col sm:flex-row gap-4">
|
||
|
|
<button className="bg-blue-600 hover:bg-blue-700 text-white px-8 py-3 rounded text-lg font-medium transition-colors">
|
||
|
|
{currentContent.hero.cta}
|
||
|
|
</button>
|
||
|
|
<button className="bg-blue-600 hover:bg-blue-700 text-white px-8 py-3 rounded text-lg font-medium transition-colors">
|
||
|
|
{currentContent.hero.learnMore}
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Slide indicators */}
|
||
|
|
<div className="absolute bottom-8 left-1/2 transform -translate-x-1/2 flex space-x-2 z-20">
|
||
|
|
{slides.map((_, index) => (
|
||
|
|
<button
|
||
|
|
key={index}
|
||
|
|
onClick={() => goToSlide(index)}
|
||
|
|
className={`w-3 h-3 rounded-full transition-all duration-300 ${
|
||
|
|
index === currentSlide
|
||
|
|
? 'bg-white scale-125'
|
||
|
|
: 'bg-gray-500 hover:bg-gray-300'
|
||
|
|
}`}
|
||
|
|
/>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Leave a Message button */}
|
||
|
|
<div className="fixed bottom-8 right-8 z-50">
|
||
|
|
<button className="bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-lg shadow-lg flex items-center space-x-2 transition-colors">
|
||
|
|
<svg
|
||
|
|
className="w-5 h-5"
|
||
|
|
fill="currentColor"
|
||
|
|
viewBox="0 0 20 20"
|
||
|
|
>
|
||
|
|
<path
|
||
|
|
fillRule="evenodd"
|
||
|
|
d="M18 10c0 3.866-3.582 7-8 7a8.841 8.841 0 01-4.083-.98L2 17l1.338-3.123C2.493 12.767 2 11.434 2 10c0-3.866 3.582-7 8-7s8 3.134 8 7zM7 9H5v2h2V9zm8 0h-2v2h2V9zM9 9h2v2H9V9z"
|
||
|
|
clipRule="evenodd"
|
||
|
|
/>
|
||
|
|
</svg>
|
||
|
|
<span>Leave A Message</span>
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Features Section */}
|
||
|
|
<div className="py-24 bg-white">
|
||
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||
|
|
<div className="text-center mb-16">
|
||
|
|
<h2 className="text-3xl md:text-4xl font-bold mb-4 text-gray-900">
|
||
|
|
{currentContent.features.title}
|
||
|
|
</h2>
|
||
|
|
</div>
|
||
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
|
||
|
|
{currentContent.features.items.map((feature, index) => (
|
||
|
|
<div
|
||
|
|
key={index}
|
||
|
|
className="text-center p-6 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors"
|
||
|
|
>
|
||
|
|
<div className="w-12 h-12 bg-blue-600 rounded-lg flex items-center justify-center mx-auto mb-4">
|
||
|
|
<svg
|
||
|
|
className="w-6 h-6 text-white"
|
||
|
|
fill="currentColor"
|
||
|
|
viewBox="0 0 20 20"
|
||
|
|
>
|
||
|
|
<path
|
||
|
|
fillRule="evenodd"
|
||
|
|
d="M6.267 3.455a3.066 3.066 0 001.745-.723 3.066 3.066 0 013.976 0 3.066 3.066 0 001.745.723 3.066 3.066 0 012.812 2.812c.051.643.304 1.254.723 1.745a3.066 3.066 0 010 3.976 3.066 3.066 0 00-.723 1.745 3.066 3.066 0 01-2.812 2.812 3.066 3.066 0 00-1.745.723 3.066 3.066 0 01-3.976 0 3.066 3.066 0 00-1.745-.723 3.066 3.066 0 01-2.812-2.812 3.066 3.066 0 00-.723-1.745 3.066 3.066 0 010-3.976 3.066 3.066 0 00.723-1.745 3.066 3.066 0 012.812-2.812zm7.44 5.252a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
||
|
|
clipRule="evenodd"
|
||
|
|
/>
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
<h3 className="text-xl font-semibold mb-2 text-gray-900">
|
||
|
|
{feature.title}
|
||
|
|
</h3>
|
||
|
|
<p className="text-gray-600">{feature.desc}</p>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* AWS Products Section */}
|
||
|
|
<AWSProductsSection currentLang={currentLang} />
|
||
|
|
|
||
|
|
<Footer />
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|