186 lines
6.6 KiB
TypeScript
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;
|