bayarea/components/Services.tsx

167 lines
7.9 KiB
TypeScript

import React, { useState, useRef, useLayoutEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
const servicesData = [
{
id: 1,
category: 'IT Infrastructure',
title: 'Windows 11 Transition',
description: 'Seamless upgrades for your entire fleet. We handle compatibility checks, data backup, and deployment so your workflow never stutters.',
icon: 'desktop_windows',
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuBMpd_cFINnFibfNErBs8OVAAyDQYTRXix88YH91QImuGi11XGwlY_QUB2R9htcC1h_fTXUeftdEieGT-oi5p5TBjpAyW-86mSsXu-rqhRTBsJlAGuE37bxJES4DUayktXIToEcF-M4PyXdyyTPIYtpYrxK18b2-sPwMzuzCL0LpgJwd5EoYxAkrJQ7W4eBrIG2e9Cw9sY0dJpXJy-TRgwBG0nk-S7W4Y0s3U9w--AzE4fcUimeGMqWwdCncU5tnETmkrkDNFiCyKSA'
},
{
id: 2,
category: 'Security',
title: 'Web Services & Security',
description: 'From hosting to rigorous penetration testing. Secure your digital storefront with enterprise-grade protection and 99.9% uptime.',
icon: 'security',
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuCxibXNCB5mU7MdWE5znMWnQUc9-d2ZoYF7LXK1CMssnvaFz2ZsGzyxXMbqDmely-UfxapqILD5-Exeo1wlQZKg8T2MK4vjlyAMaehoJoqTy2hHh8rxj46i8CKb4-ILL2JswBc98nJt_Fo1DfcDH0dHH5Zz6H4R2Jm1deViSW8Sp2zNp1sTc4eRHy1URiSRQFcr1C8rca6dKiuNDuyDiUmmesqHobXGItaBeFjJC-0OatWpKbr0zF-Y5qvk9Yl5FY2KUcDY9AcTfelu'
},
{
id: 3,
category: 'Consulting',
title: 'Performance Upgrades',
description: 'Is your hardware holding you back? We analyze bottlenecks and implement strategic upgrades to memory, storage, and networks.',
icon: 'speed',
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuBs2fGGwp4EkMarA9Uvy7IOqyW0Pzxzt-94Bsr8Tkbem4uHPq-vMEmGgKuEmds2zKwPrw2nVcvL3MjjKYWieLSLh5pVUbbK6T9aDxt2xhvo4trARZobhzoQCJfI-r6aGW_aqfwC5XxOr9VA3YdnNnYEgkfW_TWrUWYa6mD8X0KdVG3sLimA8p7qWxIqUzFFV82twn60rP4OwLdIsc6t1OGnJzjemxL1Aw05aDo6Ckfr0a1oZ2kD4xKeTkG--zUhezvXB9I03l6f3b46'
}
];
const categories = ['All', 'IT Infrastructure', 'Web Development', 'Consulting', 'Security'];
const Services: React.FC = () => {
const [activeCategory, setActiveCategory] = useState('All');
const containerRef = useRef<HTMLDivElement>(null);
const imagesRef = useRef<(HTMLDivElement | null)[]>([]);
// Reset refs on render to handle filtering updates
imagesRef.current = [];
const filteredServices = activeCategory === 'All'
? servicesData
: servicesData.filter(s => s.category === activeCategory || (activeCategory === 'Web Development' && s.category === 'Security'));
useLayoutEffect(() => {
const ctx = gsap.context(() => {
imagesRef.current.forEach((imgWrapper) => {
if (!imgWrapper) return;
gsap.to(imgWrapper, {
yPercent: 30,
ease: "none",
scrollTrigger: {
trigger: imgWrapper.closest('.group'),
start: "top bottom",
end: "bottom top",
scrub: true
}
});
});
}, containerRef);
return () => ctx.revert();
}, [filteredServices]);
return (
<motion.section
ref={containerRef}
id="services"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.8, ease: "easeOut" }}
className="py-24 bg-white dark:bg-[#0f0f0f] border-t border-gray-100 dark:border-white/5"
>
<div className="max-w-7xl mx-auto px-6">
<div className="mb-16">
<span className="text-xs font-semibold uppercase tracking-widest text-gray-500 dark:text-gray-500 mb-2 block">Core Offerings</span>
<h2 className="font-display text-3xl md:text-4xl text-gray-900 dark:text-white">
Different paths to explore <span className="text-gray-400 dark:text-gray-600">all guided by one expert team.</span>
</h2>
</div>
<div className="flex gap-6 mb-12 border-b border-gray-200 dark:border-white/10 text-sm font-medium overflow-x-auto pb-2 no-scrollbar">
{categories.map((cat) => (
<button
key={cat}
onClick={() => setActiveCategory(cat)}
className={`pb-2 whitespace-nowrap transition-colors relative ${
activeCategory === cat
? 'text-gray-900 dark:text-white'
: 'text-gray-500 dark:text-gray-500 hover:text-gray-800 dark:hover:text-gray-300'
}`}
>
{cat}
{activeCategory === cat && (
<motion.div
layoutId="activeTab"
className="absolute bottom-0 left-0 right-0 h-0.5 bg-black dark:bg-white"
/>
)}
</button>
))}
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<AnimatePresence mode="popLayout">
{filteredServices.map((service) => (
<motion.div
key={service.id}
layout
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
whileHover={{ y: -10, transition: { duration: 0.3 } }}
className="group relative bg-gray-50 dark:bg-white/5 border border-gray-200 dark:border-white/10 rounded-xl overflow-hidden hover:border-blue-500/50 dark:hover:border-blue-500/50 hover:shadow-2xl transition-all duration-300"
>
{/* Image Container */}
<div className="h-48 bg-gray-200 dark:bg-black/40 overflow-hidden relative">
{/* Parallax Wrapper */}
<div
ref={el => { if (el) imagesRef.current.push(el); }}
className="w-full h-[140%] -mt-[20%]"
>
<img
src={service.image}
alt={service.title}
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110 opacity-80 group-hover:opacity-100"
/>
</div>
<div className="absolute inset-0 bg-gradient-to-t from-gray-50 dark:from-[#161616] to-transparent pointer-events-none"></div>
</div>
<div className="p-6 relative">
<motion.div
className="w-10 h-10 rounded-full bg-white dark:bg-white/10 flex items-center justify-center mb-4 border border-gray-200 dark:border-white/10"
whileHover={{ rotate: 360, backgroundColor: "#3b82f6", color: "white", borderColor: "#3b82f6" }}
transition={{ duration: 0.5 }}
>
<span className="material-symbols-outlined text-sm text-gray-900 dark:text-white group-hover:text-white">{service.icon}</span>
</motion.div>
<h3 className="font-display text-xl font-bold text-gray-900 dark:text-white mb-2">{service.title}</h3>
<p className="text-sm text-gray-600 dark:text-gray-400 leading-relaxed mb-4">
{service.description}
</p>
<a href="#" className="inline-flex items-center text-xs font-bold uppercase tracking-wide text-gray-900 dark:text-white group-hover:text-blue-500 transition-colors">
Learn More <motion.span
className="material-symbols-outlined text-xs ml-1"
animate={{ x: [0, 5, 0] }}
transition={{ repeat: Infinity, duration: 1.5, ease: "easeInOut", repeatDelay: 1 }}
>arrow_forward</motion.span>
</a>
</div>
</motion.div>
))}
</AnimatePresence>
</div>
</div>
</motion.section>
);
};
export default Services;