deafmissoula-website/client/src/components/Meet-Board.tsx
2024-10-17 11:05:26 -06:00

220 lines
No EOL
9.6 KiB
TypeScript

import React, { useState, useEffect, useRef } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { FaPlay, FaPause, FaStop } from 'react-icons/fa';
interface BoardMember {
name: string;
position: string;
email: string;
image: string;
bio: string;
}
const boardMembers: BoardMember[] = [
{
name: "Eliza Kragh",
position: "President",
email: "eliza.kragh@deafmissoula.org",
image: "/headshots/eliza.jpg",
bio: "Eliza Kragh is a devoted advocate for Deaf rights and a passionate outdoor enthusiast. When she's not paddling through serene lakes or hiking through untamed wilderness with her trusty sidekick Khaja, you can find Eliza pushing the boundaries of inclusivity and accessibility. Her dry wit and introspective nature often catch people off guard, but one thing is clear: Eliza leads by example, inspiring others to stand up for what's right. With a heart full of conviction and a spirit that refuses to be invisible, she's a true champion of Deaf excellence."
},
{
name: "Rita Brandborg",
position: "Vice President",
email: "rita.brandborg@deafmissoula.org",
image: "/headshots/rita.jpg",
bio: "Meet Rita Brandborg, the proud mom of two tiny tornados (Levi and Josie) who keep her on her toes! When she's not wrangling her mini-me's or teaching Sign 102 at the University of Montana, you can find Rita snapping photos with her trusty camera or reeling in the big ones fly fishing. This Deaf momma is all about spreading joy, love, and a little bit of chaos wherever she goes! With a heart full of laughter and a mind full of stories, Rita is living life to the fullest - and loving every minute of it!"
},
{
name: "Aubz M",
position: "Secretary",
email: "aubz.m@deafmissoula.org",
image: "/headshots/aubz.jpg",
bio: "Aubz M is a shining star at the local veterinary hospital, where they're paving their way to become a certified vet tech. This animal whisperer's heart beats for creatures great and small, but it also swells with love for self-care and coziness. When they're not snuggling Kanga (their adorable pup), Aubz can be found soaking up wellness vibes or cracking jokes that'll leave you giggling. With a quick wit and thoughtful spirit, this Deaf rockstar is spreading kindness and compassion wherever they go!"
},
{
name: "Kevin Cooley",
position: "Treasurer",
email: "kevin.cooley@deafmissoula.org",
image: "/headshots/kevin.jpg",
bio: "Kevin Cooley is a force to be reckoned with in the Deaf community. When he's not digging up dirt (literally) as an excavator operator, you can find him reeling in trout or decorating his pad with skulls (because why not?). Halloween? The holiday of all holidays! Kevin loves living life on his own terms and making those around him smile. With a heart of gold and a mind sharp enough to cut through any situation, he's the kind of friend who'll be there for you no matter what."
},
{
name: "Tessa Williams",
position: "Event Coordinator",
email: "tessa.williams@deafmissoula.org",
image: "/headshots/tessa.jpg",
bio: "Meet Tessa Williams, the sparkplug of the University of Montana Social Worker's program. By day, she's a social work student with a heart of gold; by night, she's a sass-spewing machine who can take down anyone with her quick wit and sharp humor. When she's not advocating for Deaf rights or making her friends laugh, Tessa can be found sipping on a matcha latte with honey (her happy place). Don't mess with this tiny firecracker - she's got love for all, except maybe for those who can't keep up with her sass."
}
];
const shuffleArray = (array: BoardMember[]) => {
const shuffled = [...array];
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
return shuffled;
};
const MeetBoard: React.FC = () => {
const [selectedMember, setSelectedMember] = useState<BoardMember | null>(null);
const [shuffledMembers, setShuffledMembers] = useState<BoardMember[]>([]);
const videoRef = useRef<HTMLVideoElement>(null);
const [isPlaying, setIsPlaying] = useState(false);
const [showControls, setShowControls] = useState(true);
const [progress, setProgress] = useState(0);
const controlsTimeoutRef = useRef<NodeJS.Timeout | null>(null);
useEffect(() => {
setShuffledMembers(shuffleArray(boardMembers));
}, []);
const togglePlay = () => {
if (videoRef.current) {
if (isPlaying) {
videoRef.current.pause();
} else {
videoRef.current.play();
}
setIsPlaying(!isPlaying);
}
};
const handleStop = () => {
if (videoRef.current) {
videoRef.current.pause();
videoRef.current.currentTime = 0;
setIsPlaying(false);
}
};
const handleTimeUpdate = () => {
if (videoRef.current) {
const progress = (videoRef.current.currentTime / videoRef.current.duration) * 100;
setProgress(progress);
}
};
const handleSeek = (e: React.ChangeEvent<HTMLInputElement>) => {
if (videoRef.current) {
const seekTime = (parseFloat(e.target.value) / 100) * videoRef.current.duration;
videoRef.current.currentTime = seekTime;
}
};
const handleMouseMove = () => {
setShowControls(true);
if (controlsTimeoutRef.current) {
clearTimeout(controlsTimeoutRef.current);
}
controlsTimeoutRef.current = setTimeout(() => {
if (isPlaying) {
setShowControls(false);
}
}, 3000);
};
return (
<div className="container mx-auto px-4 py-8">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<h1 className="text-3xl md:text-4xl font-bold text-[#8C1D40] mb-8">Meet MCDi Board</h1>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 md:gap-8">
{shuffledMembers.map((member, index) => (
<BoardMemberCard key={index} member={member} onClick={() => setSelectedMember(member)} />
))}
</div>
<div className="mt-12">
<div
className="relative rounded-lg overflow-hidden shadow-lg border-4 border-[#8C1D40]"
onMouseMove={handleMouseMove}
onMouseLeave={() => isPlaying && setShowControls(false)}
>
<video
ref={videoRef}
className="w-full"
onTimeUpdate={handleTimeUpdate}
onEnded={() => setIsPlaying(false)}
>
<source src="/videos/mcdi-intro.mp4" type="video/mp4" />
Your browser does not support the video tag.
</video>
<AnimatePresence>
{showControls && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="absolute bottom-0 left-0 right-0 bg-black bg-opacity-50 p-2 md:p-4"
>
<div className="flex items-center justify-between">
<button onClick={handleStop} className="text-white mr-2">
<FaStop />
</button>
<button onClick={togglePlay} className="text-white mr-2">
{isPlaying ? <FaPause /> : <FaPlay />}
</button>
<input
type="range"
min="0"
max="100"
value={progress}
onChange={handleSeek}
className="w-full mx-2"
/>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
</motion.div>
<AnimatePresence>
{selectedMember && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50"
onClick={() => setSelectedMember(null)}
>
<motion.div
initial={{ y: -50, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: -50, opacity: 0 }}
className="bg-white rounded-lg p-4 md:p-8 max-w-md w-full max-h-[90vh] overflow-y-auto"
onClick={(e) => e.stopPropagation()}
>
<h2 className="text-xl md:text-2xl font-semibold text-[#8C1D40] mb-4">{selectedMember.name}</h2>
<p className="text-base md:text-lg text-gray-700 mb-2">{selectedMember.position}</p>
<p className="text-sm md:text-base text-gray-600 mb-4">{selectedMember.bio}</p>
<a href={`mailto:${selectedMember.email}`} className="text-blue-500 hover:underline text-sm md:text-base">{selectedMember.email}</a>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</div>
);
};
const BoardMemberCard: React.FC<{ member: BoardMember; onClick: () => void }> = ({ member, onClick }) => (
<motion.div
className="bg-white rounded-lg shadow-md p-4 md:p-6 flex flex-col items-center cursor-pointer"
whileHover={{ scale: 1.05 }}
transition={{ duration: 0.2 }}
onClick={onClick}
>
<h2 className="text-xl md:text-2xl font-semibold text-[#8C1D40] mb-2 md:mb-4">{member.name}</h2>
<img src={member.image} alt={member.name} className="w-32 h-32 md:w-48 md:h-48 object-cover rounded-full mb-2 md:mb-4" />
<p className="text-base md:text-lg text-gray-700">{member.position}</p>
</motion.div>
);
export default MeetBoard;