deafmissoula-website/client/src/components/Minutes.tsx

186 lines
6.6 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
interface MinuteEntry {
date: string;
meetingType: string;
location: string;
fileUrl: string;
}
const minutesData: MinuteEntry[] = [
{
date: '01/11/2025',
meetingType: 'Board Meeting',
location: 'Black Coffee Roasting Company',
fileUrl: '/minutes/minutes-01112025.pdf',
},
{
date: '03/08/2025',
meetingType: 'Board Meeting',
location: 'The Break Espresso',
fileUrl: '/minutes/minutes-03082025.pdf',
},
{
date: '02/08/2025',
meetingType: 'Board Meeting',
location: 'Book Exchange - Liquid Planet',
fileUrl: '/minutes/minutes-02082025.pdf',
},
{
date: '10/17/2024',
meetingType: 'Board Meeting',
location: 'UC Market (University)',
fileUrl: '/minutes/minutes-10172024.pdf',
},
{
date: '09/14/2024',
meetingType: 'Membership Meeting',
location: 'Dog Wash Cafe',
fileUrl: '/minutes/minutes-09142024.pdf',
},
{
date: '08/06/2024',
meetingType: 'Membership Meeting',
location: 'The Break Espresso',
fileUrl: '/minutes/minutes-08062024.pdf',
},
];
const Minutes: React.FC = () => {
const [sortColumn, setSortColumn] = useState<keyof MinuteEntry>('date');
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc');
const [selectedMinutes, setSelectedMinutes] = useState<MinuteEntry | null>(null);
const [isMobile, setIsMobile] = useState(false);
useEffect(() => {
const checkMobile = () => {
setIsMobile(window.innerWidth < 768);
};
checkMobile();
window.addEventListener('resize', checkMobile);
return () => window.removeEventListener('resize', checkMobile);
}, []);
const sortedMinutes = [...minutesData].sort((a, b) => {
if (sortColumn === 'date') {
// Convert date strings to Date objects for proper comparison
const dateA = new Date(a.date.split('/').map((part, i) => i === 0 ? part : i === 1 ? part : part).join('/'));
const dateB = new Date(b.date.split('/').map((part, i) => i === 0 ? part : i === 1 ? part : part).join('/'));
return sortDirection === 'asc' ? dateA.getTime() - dateB.getTime() : dateB.getTime() - dateA.getTime();
} else {
if (a[sortColumn] < b[sortColumn]) return sortDirection === 'asc' ? -1 : 1;
if (a[sortColumn] > b[sortColumn]) return sortDirection === 'asc' ? 1 : -1;
return 0;
}
});
const handleSort = (column: keyof MinuteEntry) => {
if (column === sortColumn) {
setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc');
} else {
setSortColumn(column);
setSortDirection('asc');
}
};
const handleMinutesClick = (entry: MinuteEntry) => {
if (isMobile) {
window.open(entry.fileUrl, '_blank');
} else {
setSelectedMinutes(entry);
}
};
const closePopup = () => {
setSelectedMinutes(null);
};
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 font-bold text-[#8C1D40] mb-6">Meeting Minutes</h1>
<div className="overflow-x-auto">
<table className="w-full bg-white shadow-md rounded-lg overflow-hidden">
<thead className="bg-[#8C1D40] text-white">
<tr>
<th className="py-3 px-4 text-left cursor-pointer hover:bg-[#6B1631]" onClick={() => handleSort('date')}>
Date {sortColumn === 'date' && <span className="ml-1">{sortDirection === 'asc' ? '▲' : '▼'}</span>}
</th>
<th className="py-3 px-4 text-left cursor-pointer hover:bg-[#6B1631]" onClick={() => handleSort('meetingType')}>
Meeting Type {sortColumn === 'meetingType' && <span className="ml-1">{sortDirection === 'asc' ? '▲' : '▼'}</span>}
</th>
<th className="py-3 px-4 text-left cursor-pointer hover:bg-[#6B1631]" onClick={() => handleSort('location')}>
Location {sortColumn === 'location' && <span className="ml-1">{sortDirection === 'asc' ? '▲' : '▼'}</span>}
</th>
<th className="py-3 px-4 text-left">Minutes</th>
</tr>
</thead>
<tbody>
{sortedMinutes.map((entry, index) => (
<tr key={index} className="border-b border-gray-200 hover:bg-gray-100">
<td className="py-3 px-4">{entry.date}</td>
<td className="py-3 px-4">{entry.meetingType}</td>
<td className="py-3 px-4">{entry.location}</td>
<td className="py-3 px-4">
<button
onClick={() => handleMinutesClick(entry)}
className="bg-[#8C1D40] text-white font-bold py-2 px-4 rounded hover:bg-[#6B1631] transition duration-300"
>
View Minutes
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</motion.div>
<AnimatePresence>
{selectedMinutes && !isMobile && (
<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 z-50"
onClick={closePopup}
>
<motion.div
initial={{ scale: 0.9, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.9, opacity: 0 }}
className="bg-white rounded-lg p-4 w-[90%] h-[90%] flex flex-col"
onClick={(e) => e.stopPropagation()}
>
<h2 className="text-2xl font-bold text-[#8C1D40] mb-4">
{selectedMinutes.meetingType} - {selectedMinutes.date}
</h2>
<div className="flex-grow">
<iframe
src={`${selectedMinutes.fileUrl}#view=FitH`}
className="w-full h-full rounded-md"
title={`Minutes for ${selectedMinutes.meetingType} on ${selectedMinutes.date}`}
/>
</div>
<button
onClick={closePopup}
className="mt-4 bg-[#8C1D40] text-white font-bold py-2 px-4 rounded hover:bg-[#6B1631] transition duration-300 self-end"
>
Close
</button>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</div>
);
};
export default Minutes;