413 lines
14 KiB
TypeScript
413 lines
14 KiB
TypeScript
import { motion, AnimatePresence } from 'framer-motion'
|
|
import { ComposableMap, Geographies, Geography, Marker } from 'react-simple-maps'
|
|
import { useState, useEffect, useRef } from 'react'
|
|
import { FaFileAlt, FaGlobe, FaUsers, FaChalkboardTeacher, FaTimes, FaMapMarkerAlt, FaClock } from 'react-icons/fa'
|
|
|
|
const geoUrl = "https://cdn.jsdelivr.net/npm/us-atlas@3/states-10m.json"
|
|
|
|
type Coordinates = [number, number]
|
|
|
|
interface Event {
|
|
name: string
|
|
description: string
|
|
startDate: string
|
|
endDate: string
|
|
}
|
|
|
|
interface RelationshipPoint {
|
|
coordinates: Coordinates
|
|
city: string
|
|
state: string
|
|
events: Event[]
|
|
}
|
|
|
|
const relationshipPoints: RelationshipPoint[] = [
|
|
{
|
|
coordinates: [-106.6504, 35.0844] as Coordinates,
|
|
city: "Albuquerque",
|
|
state: "New Mexico",
|
|
events: [{
|
|
name: "NLTC",
|
|
description: "National Leadership Training Conference - NAD",
|
|
startDate: "September 21, 2023",
|
|
endDate: "September 24, 2023"
|
|
}]
|
|
},
|
|
{
|
|
coordinates: [-80.8431, 35.2271] as Coordinates,
|
|
city: "Charlotte",
|
|
state: "North Carolina",
|
|
events: [{
|
|
name: "SERID",
|
|
description: "Southeast Regional Institute on Deafness Conference",
|
|
startDate: "October 26, 2023",
|
|
endDate: "October 29, 2023"
|
|
}]
|
|
},
|
|
{
|
|
coordinates: [-104.9903, 39.7392] as Coordinates,
|
|
city: "Denver",
|
|
state: "Colorado",
|
|
events: [
|
|
{
|
|
name: "DBL",
|
|
description: "Deaf, Blind, Limitless Conference",
|
|
startDate: "March 12, 2024",
|
|
endDate: "March 15, 2024"
|
|
},
|
|
{
|
|
name: "EHDI",
|
|
description: "Early Hearing Detection Intervention Conference",
|
|
startDate: "March 17, 2024",
|
|
endDate: "March 19, 2024"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
coordinates: [-84.3879, 33.7490] as Coordinates,
|
|
city: "Atlanta",
|
|
state: "Georgia",
|
|
events: [{
|
|
name: "ADARA",
|
|
description: "American Deafness and Rehabilitation Association Conference",
|
|
startDate: "May 28, 2024",
|
|
endDate: "June 1, 2024"
|
|
}]
|
|
},
|
|
{
|
|
coordinates: [-93.2650, 44.9778] as Coordinates,
|
|
city: "Minneapolis",
|
|
state: "Minnesota",
|
|
events: [{
|
|
name: "ADA",
|
|
description: "Americans with Disabilities Act Conference",
|
|
startDate: "June 9, 2024",
|
|
endDate: "June 12, 2024"
|
|
}]
|
|
},
|
|
{
|
|
coordinates: [-87.6298, 41.8781] as Coordinates,
|
|
city: "Chicago",
|
|
state: "Illinois",
|
|
events: [{
|
|
name: "NAD",
|
|
description: "National Association of the Deaf Conference",
|
|
startDate: "July 1, 2024",
|
|
endDate: "July 6, 2024"
|
|
}]
|
|
},
|
|
{
|
|
coordinates: [-98.4936, 29.4241] as Coordinates,
|
|
city: "San Antonio",
|
|
state: "Texas",
|
|
events: [{
|
|
name: "NAP NTC",
|
|
description: "National Association of Parliament - National Training Conference",
|
|
startDate: "September 5, 2024",
|
|
endDate: "September 8, 2024"
|
|
}]
|
|
},
|
|
{
|
|
coordinates: [-89.4012, 43.0731] as Coordinates,
|
|
city: "Madison",
|
|
state: "Wisconsin",
|
|
events: [{
|
|
name: "ADA",
|
|
description: "Americans with Disabilities Act Conference",
|
|
startDate: "October 21, 2024",
|
|
endDate: "October 22, 2024"
|
|
}]
|
|
},
|
|
{
|
|
coordinates: [-94.8191, 38.8814] as Coordinates,
|
|
city: "Olathe",
|
|
state: "Kansas",
|
|
events: [{
|
|
name: "KSD",
|
|
description: "Kansas School for the Deaf",
|
|
startDate: "November 23, 2024",
|
|
endDate: "November 23, 2024"
|
|
}]
|
|
},
|
|
{
|
|
coordinates: [-115.1398, 36.1699] as Coordinates,
|
|
city: "Las Vegas",
|
|
state: "Nevada",
|
|
events: [{
|
|
name: "Engagement Conference",
|
|
description: "2024 Engagement Conference",
|
|
startDate: "December 15, 2024",
|
|
endDate: "December 15, 2024"
|
|
}]
|
|
},
|
|
{
|
|
coordinates: [-112.0740, 33.4484] as Coordinates,
|
|
city: "Phoenix",
|
|
state: "Arizona",
|
|
events: [
|
|
{
|
|
name: "DANE",
|
|
description: "Deaf Abuse Network and Education - Advocacy Round-table Discussion",
|
|
startDate: "May 31, 2025",
|
|
endDate: "May 31, 2025"
|
|
},
|
|
{
|
|
name: "NAP",
|
|
description: "National Association of Parliamentarians - NAP 45th Biennial Convention",
|
|
startDate: "August 20, 2025",
|
|
endDate: "August 24, 2025"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
coordinates: [-112.0362, 46.5958] as Coordinates,
|
|
city: "Helena",
|
|
state: "Montana",
|
|
events: [{
|
|
name: "AIP",
|
|
description: "American Institute of Parliamentarians - 2025 Spring Practicum (Online)",
|
|
startDate: "June 12, 2025",
|
|
endDate: "June 14, 2025"
|
|
}]
|
|
},
|
|
{
|
|
coordinates: [-97.3375, 37.6872] as Coordinates,
|
|
city: "Wichita",
|
|
state: "Kansas",
|
|
events: [{
|
|
name: "KAD",
|
|
description: "Kansas Association of the Deaf - 2025 General Business Meeting",
|
|
startDate: "June 21, 2025",
|
|
endDate: "June 21, 2025"
|
|
}]
|
|
},
|
|
{
|
|
coordinates: [-91.1871, 30.4515] as Coordinates,
|
|
city: "Baton Rouge",
|
|
state: "Louisiana",
|
|
events: [{
|
|
name: "LAD",
|
|
description: "Louisiana Association of the Deaf - 2025 Conference",
|
|
startDate: "June 26, 2025",
|
|
endDate: "June 28, 2025"
|
|
}]
|
|
},
|
|
{
|
|
coordinates: [-77.0369, 38.9072] as Coordinates,
|
|
city: "Washington",
|
|
state: "DC",
|
|
events: [{
|
|
name: "ASLTA",
|
|
description: "American Sign Language Teachers Association - National Professional Development Conference",
|
|
startDate: "July 9, 2025",
|
|
endDate: "July 12, 2025"
|
|
}]
|
|
},
|
|
{
|
|
coordinates: [-94.5786, 39.0997] as Coordinates,
|
|
city: "Kansas City",
|
|
state: "Missouri",
|
|
events: [{
|
|
name: "NBDA",
|
|
description: "National Black Deaf Advocates - Building Our Legacy: The Road To Success",
|
|
startDate: "August 13, 2025",
|
|
endDate: "August 16, 2025"
|
|
}]
|
|
}
|
|
]
|
|
|
|
const services = [
|
|
{
|
|
icon: <FaFileAlt className="text-4xl" />,
|
|
title: "ADA-Compliant Transcripts",
|
|
description: "High-quality, accurate transcripts with speaker identification and context differentiation."
|
|
},
|
|
{
|
|
icon: <FaGlobe className="text-4xl" />,
|
|
title: "Universal Website Design",
|
|
description: "Visually appealing and accessible websites adhering to universal design principles."
|
|
},
|
|
{
|
|
icon: <FaUsers className="text-4xl" />,
|
|
title: "Board Communication",
|
|
description: "Comprehensive protocol and tools for effective board communication."
|
|
},
|
|
{
|
|
icon: <FaChalkboardTeacher className="text-4xl" />,
|
|
title: "Training & Workshops",
|
|
description: "Leadership development and parliamentary procedure training."
|
|
}
|
|
]
|
|
|
|
const Services = () => {
|
|
const [selectedPoint, setSelectedPoint] = useState<typeof relationshipPoints[0] | null>(null)
|
|
const popupRef = useRef<HTMLDivElement>(null)
|
|
|
|
const isUpcoming = (event: { startDate: string, endDate: string }) => {
|
|
const eventEndDate = new Date(event.endDate)
|
|
const now = new Date()
|
|
// Set the end date to the end of the day
|
|
eventEndDate.setHours(23, 59, 59, 999)
|
|
return eventEndDate > now
|
|
}
|
|
|
|
useEffect(() => {
|
|
const handleClickOutside = (event: MouseEvent) => {
|
|
if (popupRef.current && !popupRef.current.contains(event.target as Node)) {
|
|
setSelectedPoint(null)
|
|
}
|
|
}
|
|
|
|
document.addEventListener('mousedown', handleClickOutside)
|
|
return () => document.removeEventListener('mousedown', handleClickOutside)
|
|
}, [])
|
|
|
|
return (
|
|
<div className="min-h-screen">
|
|
<section className="py-20 bg-accent-snow">
|
|
<div className="max-w-7xl mx-auto px-4">
|
|
<h1 className="text-4xl font-bold text-center text-secondary mb-16">
|
|
Our Services
|
|
</h1>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
|
|
{services.map((service, index) => (
|
|
<motion.div
|
|
key={index}
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ delay: index * 0.2 }}
|
|
className="bg-white p-6 rounded-lg shadow-lg hover:shadow-xl transition-shadow"
|
|
>
|
|
<div className="text-primary mb-4">{service.icon}</div>
|
|
<h3 className="text-xl font-semibold mb-2">{service.title}</h3>
|
|
<p className="text-accent-mountain">{service.description}</p>
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section className="py-20 bg-white">
|
|
<div className="max-w-7xl mx-auto px-4">
|
|
<h2 className="text-3xl font-bold text-center text-secondary mb-12">
|
|
Our Network & Relationships
|
|
</h2>
|
|
<div className="relative h-[820px]">
|
|
<ComposableMap projection="geoAlbersUsa">
|
|
<Geographies geography={geoUrl}>
|
|
{({ geographies }) =>
|
|
geographies.map(geo => (
|
|
<Geography
|
|
key={geo.rsmKey}
|
|
geography={geo}
|
|
fill="#647C90"
|
|
stroke="#FFFFFF"
|
|
strokeWidth={0.5}
|
|
style={{
|
|
default: { fill: "#647C90", outline: "none" },
|
|
hover: { fill: "#2C4A3E", outline: "none" },
|
|
pressed: { fill: "#A4C3D2", outline: "none" }
|
|
}}
|
|
/>
|
|
))
|
|
}
|
|
</Geographies>
|
|
|
|
{relationshipPoints.map((point) => {
|
|
const hasUpcomingEvent = point.events.some(event => isUpcoming(event))
|
|
return (
|
|
<Marker key={`${point.city}-${point.state}`} coordinates={point.coordinates}>
|
|
<motion.circle
|
|
r={6}
|
|
fill={hasUpcomingEvent ? "#4aa63f" : "#A4C3D2"}
|
|
stroke="#FFFFFF"
|
|
strokeWidth={2}
|
|
className="cursor-pointer"
|
|
whileHover={{ scale: 1.5 }}
|
|
onClick={() => setSelectedPoint(point)}
|
|
/>
|
|
</Marker>
|
|
)
|
|
})}
|
|
</ComposableMap>
|
|
|
|
<AnimatePresence>
|
|
{selectedPoint && (
|
|
<motion.div
|
|
ref={popupRef}
|
|
initial={{ opacity: 0, scale: 0.8 }}
|
|
animate={{ opacity: 1, scale: 1 }}
|
|
exit={{ opacity: 0, scale: 0.8 }}
|
|
className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2
|
|
backdrop-blur-lg bg-white/80 p-8 rounded-2xl shadow-2xl
|
|
border border-white/20 w-96
|
|
hover:shadow-accent-lake/20 hover:shadow-2xl
|
|
transition-all duration-300"
|
|
style={{
|
|
background: 'linear-gradient(145deg, rgba(255,255,255,0.9) 0%, rgba(255,255,255,0.8) 100%)',
|
|
}}
|
|
>
|
|
<motion.button
|
|
whileHover={{ rotate: 90 }}
|
|
transition={{ duration: 0.2 }}
|
|
onClick={() => setSelectedPoint(null)}
|
|
className="absolute top-4 right-4 text-accent-mountain hover:text-secondary
|
|
bg-white/50 p-2 rounded-full"
|
|
>
|
|
<FaTimes />
|
|
</motion.button>
|
|
|
|
<motion.div
|
|
initial={{ y: 20, opacity: 0 }}
|
|
animate={{ y: 0, opacity: 1 }}
|
|
transition={{ delay: 0.1 }}
|
|
>
|
|
<h3 className="text-2xl font-bold text-secondary mb-2 flex items-center gap-2">
|
|
<FaMapMarkerAlt className="text-primary" />
|
|
{selectedPoint.city}, {selectedPoint.state}
|
|
</h3>
|
|
|
|
<div className="space-y-4 mt-6">
|
|
{selectedPoint.events
|
|
.sort((a, b) => new Date(b.startDate).getTime() - new Date(a.startDate).getTime())
|
|
.map((event, index) => (
|
|
<motion.div
|
|
key={index}
|
|
initial={{ x: -20, opacity: 0 }}
|
|
animate={{ x: 0, opacity: 1 }}
|
|
transition={{ delay: index * 0.1 }}
|
|
className={`
|
|
relative overflow-hidden rounded-xl
|
|
${index !== 0 ? "mt-4" : ""}
|
|
bg-gradient-to-r from-white/90 to-white/80
|
|
p-4 hover:from-white/95 hover:to-white/85
|
|
transition-all duration-300
|
|
border border-white/30
|
|
`}
|
|
>
|
|
<div className="relative z-10">
|
|
<h4 className="font-bold text-secondary text-lg mb-1">{event.name}</h4>
|
|
<p className="text-accent-mountain text-sm mb-2 font-medium">{event.description}</p>
|
|
<div className="flex items-center gap-2 text-sm text-primary font-semibold">
|
|
<FaClock className="text-xs" />
|
|
{event.startDate === event.endDate
|
|
? event.startDate
|
|
: `${event.startDate} - ${event.endDate}`}
|
|
</div>
|
|
</div>
|
|
<div className="absolute inset-0 bg-gradient-to-r from-primary/5 to-secondary/5" />
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
</motion.div>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default Services
|