diff --git a/src/Pages/Events/CalendarView.js b/src/Pages/Events/CalendarView.js index a3e04b4b4..31be0df9e 100644 --- a/src/Pages/Events/CalendarView.js +++ b/src/Pages/Events/CalendarView.js @@ -27,6 +27,21 @@ function eventDateKey(event) { return toDateKey(d); } +function toTimeSortValue(event) { + if (!event?.time) return Number.POSITIVE_INFINITY; + const [hour, minute] = String(event.time).split(':').map(Number); + if (Number.isNaN(hour) || Number.isNaN(minute)) return Number.POSITIVE_INFINITY; + return hour * 60 + minute; +} + +function sortEventsForDay(events) { + return [...events].sort((a, b) => { + const timeDelta = toTimeSortValue(a) - toTimeSortValue(b); + if (timeDelta !== 0) return timeDelta; + return String(a.name || '').localeCompare(String(b.name || '')); + }); +} + function formatDate(dateStr) { if (!dateStr) return ''; const d = new Date(`${dateStr}T12:00:00`); @@ -432,17 +447,18 @@ function EventPopup({ event, onClose, isAdminView, user }) { // ─── Event pill ─────────────────────────────────────────────────────────────── -function EventPill({ event, onSelect, isAdminView }) { +function EventRow({ event, onSelect, isAdminView }) { const colors = pillColors(event, isAdminView); const badgeText = getBadgeText(event, isAdminView); + const timeLabel = formatTime(event.time); return ( ); } @@ -485,18 +506,23 @@ function DayLabels() { // ─── Calendar cell ──────────────────────────────────────────────────────────── function CalCell({ date, isCurrentMonth, isToday, events, onSelectEvent, isAdminView }) { + const sortedEvents = sortEventsForDay(events); + const mobileRows = sortedEvents.slice(0, 2); + const desktopOnlyRows = sortedEvents.slice(2, 4); + const hiddenCount = Math.max(sortedEvents.length - 4, 0); + return (
-
+
@@ -505,8 +531,8 @@ function CalCell({ date, isCurrentMonth, isToday, events, onSelectEvent, isAdmin
- {events.slice(0, 3).map((ev) => ( - ( + ))} - {events.length > 3 && ( - - +{events.length - 3} more + {desktopOnlyRows.map((ev) => ( +
+ +
+ ))} + + {hiddenCount > 0 && ( + + +{hiddenCount} more )}
@@ -524,6 +560,91 @@ function CalCell({ date, isCurrentMonth, isToday, events, onSelectEvent, isAdmin ); } +function MobileMonthAgenda({ monthEvents, onSelectEvent, isAdminView }) { + if (monthEvents.length === 0) { + return ( +
+ No events scheduled this month. +
+ ); + } + + const groupedEvents = monthEvents.reduce((groups, item) => { + const lastGroup = groups[groups.length - 1]; + if (!lastGroup || lastGroup.dateKey !== item.dateKey) { + groups.push({ dateKey: item.dateKey, events: [item.event] }); + return groups; + } + lastGroup.events.push(item.event); + return groups; + }, []); + + return ( +
+ {groupedEvents.map(({ dateKey, events }) => { + const [year, month, day] = dateKey.split('-'); + const date = new Date(Number(year), Number(month) - 1, Number(day)); + const weekday = date.toLocaleDateString('en-US', { weekday: 'short' }).toUpperCase(); + const dayOfMonth = String(date.getDate()); + + return ( +
+
+
+ {weekday} +
+
+ {dayOfMonth} +
+
+ +
+ {events.map((event) => { + const colors = pillColors(event, isAdminView); + const badgeText = getBadgeText(event, isAdminView); + const timeLabel = formatTime(event.time) || 'All day'; + + return ( + + ); + })} +
+
+ ); + })} +
+ ); +} + // ─── Main export ────────────────────────────────────────────────────────────── export default function CalendarView({ events, isAdminView = false, user, canCreateEvent = false }) { @@ -539,6 +660,9 @@ export default function CalendarView({ events, isAdminView = false, user, canCre if (!map[key]) map[key] = []; map[key].push(ev); }); + Object.keys(map).forEach((key) => { + map[key] = sortEventsForDay(map[key]); + }); return map; }, [events]); @@ -577,6 +701,17 @@ export default function CalendarView({ events, isAdminView = false, user, canCre .filter((c) => c.isCurrentMonth) .reduce((sum, c) => sum + c.events.length, 0); + const monthEvents = useMemo(() => { + return cells + .filter((c) => c.isCurrentMonth && c.events.length > 0) + .flatMap((c) => + c.events.map((event) => ({ + event, + dateKey: c.key, + })) + ); + }, [cells]); + return ( <> {selectedEvent && ( @@ -588,7 +723,7 @@ export default function CalendarView({ events, isAdminView = false, user, canCre /> )} -
+
@@ -675,17 +810,27 @@ export default function CalendarView({ events, isAdminView = false, user, canCre
- +
+ +
-
- {cells.map((cell) => ( - - ))} +
+ + +
+ {cells.map((cell) => ( + + ))} +
{isAdminView ? ( diff --git a/src/Pages/Events/Events.js b/src/Pages/Events/Events.js index bf949bb0e..6789f4200 100644 --- a/src/Pages/Events/Events.js +++ b/src/Pages/Events/Events.js @@ -283,6 +283,12 @@ export default function EventsPage() { const canCreateEvent = user?.accessLevel >= membershipState.OFFICER; const visibleEvents = events.filter((event) => canUserSeeEvent(event, user)); const isAdminView = canCreateEvent; + const pageContainerClass = isAdminView + ? 'relative h-dvh overflow-hidden bg-gradient-to-r from-gray-800 to-gray-600 text-white' + : 'relative h-[calc(100dvh-4rem)] overflow-hidden bg-gradient-to-r from-gray-800 to-gray-600 text-white'; + const calendarContainerClass = isAdminView + ? 'relative h-full w-full overflow-hidden px-3 py-4 sm:px-4 sm:py-5 lg:px-5' + : 'relative mx-auto h-full max-w-[120rem] overflow-y-auto px-4 py-6 sm:px-6 sm:py-8 lg:px-10'; useEffect(() => { async function fetchEvents() { @@ -304,7 +310,7 @@ export default function EventsPage() { }, []); return ( -
+
{/* Ambient blobs — unchanged from original */}
@@ -312,7 +318,7 @@ export default function EventsPage() {
{/* ── Calendar area ── */} -
+
{isLoading && (
Loading events...