diff --git a/src/components/GroupView/UserChart.tsx b/src/components/GroupView/UserChart.tsx index 768dd74..4e3de0d 100644 --- a/src/components/GroupView/UserChart.tsx +++ b/src/components/GroupView/UserChart.tsx @@ -24,6 +24,7 @@ interface UserChartProps { React.Dispatch>, ]; calendarHeight: number | null; + hideHeader?: boolean; } /** @@ -39,6 +40,7 @@ const UserChart: React.FC = ({ thePeopleStatus, theParticipantToggleClicked, calendarHeight, + hideHeader = false, }) => { const [chartedUsers, setChartedUsers] = chartedUsersData || [ { available: [], unavailable: [] }, @@ -101,26 +103,28 @@ const UserChart: React.FC = ({ )}
- - - - - - + {!hideHeader && ( + + + + + + + )} {rows.map(([available, unavailable], idx) => ( void; + hideUserChart?: boolean; } export default function SharedSidebar({ @@ -109,8 +110,10 @@ export default function SharedSidebar({ onUserSignIn, userHasFilled, onDecline, + hideUserChart = false, }: SharedSidebarProps) { const navigate = useNavigate(); + const hovering = hideUserChart ? false : (chartedUsers?.hovering ?? false); const [alertMessage, setAlertMessage] = useState(null); const [isDeclinePopupOpen, setIsDeclinePopupOpen] = useState(false); const [emailNotifications, setEmailNotifications] = useState(getEmailAdmin()); @@ -208,11 +211,11 @@ export default function SharedSidebar({ /> {/* Event Title & Description */} - {!chartedUsers?.hovering && ( + {!hovering && (
Share @@ -282,7 +285,7 @@ export default function SharedSidebar({ )} {/* Admin Settings Section */} - {isAdmin && !chartedUsers?.hovering && ( + {isAdmin && !hovering && (
Settings @@ -318,7 +321,7 @@ export default function SharedSidebar({ {/* Google Calendar Section - only show when user is signed in */} - {!chartedUsers?.hovering && userHasSignedIn && ( + {!hovering && userHasSignedIn && (
Decline Invitation{' '} @@ -420,7 +423,7 @@ export default function SharedSidebar({ {/* Participants Section */} - {!chartedUsers?.hovering && allPeople && allPeople.length > 0 && ( + {!hovering && allPeople && allPeople.length > 0 && (
Participants ( @@ -495,7 +498,7 @@ export default function SharedSidebar({ {/* Locations Section - grouped together */} - {locationOptions.length > 0 && !chartedUsers?.hovering && ( + {locationOptions.length > 0 && !hovering && (
Locations @@ -536,7 +539,7 @@ export default function SharedSidebar({ )} {/* Hover User Chart - shows available/unavailable when hovering on calendar */} - {chartedUsers?.hovering && ( + {!hideUserChart && chartedUsers?.hovering && (
(null); + // Mobile tab state + const [mobileTab, setMobileTab] = useState<'yours' | 'group' | 'info'>('info'); + + // Mobile sticky tab bar + calendar header logic + const [mobileColumnsPerPage, setMobileColumnsPerPage] = useState(4); + // Google Calendar hook for fetching events const { hasAccess, requestAccess, getEvents, getCalendars } = useGoogleCalendar(); @@ -436,6 +444,231 @@ export default function SideBySideView({ return (
+ {/* ── MOBILE LAYOUT (hidden on lg+) ── */} +
+ {/* Grouped sticky header: tabs + calendar nav arrows */} +
+ {/* Tab row */} +
+ + {userHasSignedIn && ( + + )} + +
+ {/* Calendar nav + date bar row — left/right offsets match the scroll container (pl-7 + 3rem time col, pr-9) */} +
+ {/* Left arrow — absolutely positioned at left edge */} +
+ {sharedPage !== 0 ? ( + setSharedPage(Math.max(sharedPage - mobileColumnsPerPage, 0))} + size={45} + className="text-outline dark:text-text-dark p-3 ml-6 rounded-lg cursor-pointer" + /> + ) : ( +
+ )} +
+ {/* Date bar — padded to align with calendar columns (pl-7 + 3rem time col on left, pr-9 on right) */} +
+ +
+ {/* Right arrow — absolutely positioned at right edge */} +
+ {sharedPage + mobileColumnsPerPage < calendarFramework.dates.flat().length ? ( + setSharedPage(Math.min( + sharedPage + mobileColumnsPerPage, + calendarFramework.dates.flat().length - mobileColumnsPerPage + ))} + size={45} + className="text-outline dark:text-text-dark p-3 mr-3 rounded-lg cursor-pointer" + /> + ) : ( +
+ )} +
+
+
+ {/* Calendar area — extra bottom padding when sheet is open so bottom cells remain reachable */} +
+ {mobileTab === 'yours' && userHasSignedIn && ( + {}} + theShowUserChart={undefined} + isGeneralDays={isGeneralDays} + setCalendarHeight={setCalendarHeight} + currentStartPage={sharedPage} + onPageChange={setSharedPage} + scrollRef={leftScrollRef} + onScroll={handleLeftScroll} + hideHeader + hideDateBar + onColumnsPerPage={setMobileColumnsPerPage} + /> + )} + {mobileTab === 'group' && ( + { + if (showUserChart === true) return; + setShowUserChart(true); + setTimeout(() => setShowUserChart(false), 3000); + }} + theShowUserChart={[showUserChart, setShowUserChart]} + isGeneralDays={false} + setChartedUsers={setChartedUsers} + chartedUsers={chartedUsers} + currentStartPage={sharedPage} + onPageChange={setSharedPage} + scrollRef={rightScrollRef} + onScroll={handleRightScroll} + hideHeader + hideDateBar + onColumnsPerPage={setMobileColumnsPerPage} + /> + )} + {mobileTab === 'info' && ( +
+ setUserHasSignedIn(true)} + userHasFilled={userHasFilled} + onDecline={onDecline} + hideUserChart + /> +
+ )} +
+ + {/* Bottom sheet: availability panel on group tab tap */} + {mobileTab === 'group' && chartedUsers?.hovering && ( +
+ {/* Sticky header: Available / Unavailable counts + X */} +
+
+ + Available ({filteredChartedUsers.available?.length ?? 0}) + + + Unavailable ({filteredChartedUsers.unavailable?.length ?? 0}) + +
+ +
+
+ +
+
+ )} + + {/* Floating save button */} + {userHasSignedIn && hasUnsavedChanges && ( +
+ +
+ )} +
+ + {/* ── DESKTOP LAYOUT (hidden below lg) ── */} +
{/* Main layout: 4-column grid matching GroupView/TimeSelect */}
{/* Sidebar - col-span-1 (same as GroupView/TimeSelect) */} @@ -657,6 +890,7 @@ export default function SideBySideView({
+
); } diff --git a/src/components/UnifiedAvailabilityPage/UnifiedAvailabilityPage.tsx b/src/components/UnifiedAvailabilityPage/UnifiedAvailabilityPage.tsx index 23d420a..9b4ca98 100644 --- a/src/components/UnifiedAvailabilityPage/UnifiedAvailabilityPage.tsx +++ b/src/components/UnifiedAvailabilityPage/UnifiedAvailabilityPage.tsx @@ -1,8 +1,6 @@ import { useState, useEffect, useCallback } from 'react'; -import TimeSelectPage from '../TimeSelect/TimeSelectPage'; -import ConditionalGroupViewRenderer from '../GroupView/ConditionalGroupViewRenderer'; import { SideBySideView } from '../SideBySideView'; -import { useLocation, useNavigate, useParams } from 'react-router-dom'; +import { useNavigate, useParams } from 'react-router-dom'; import eventAPI from '../../backend/eventAPI'; import { getEventOnPageload, @@ -38,12 +36,8 @@ import { calendar_v3 } from 'googleapis'; const notifiedEventsThisSession = new Set(); export default function UnifiedAvailabilityPage() { - const location = useLocation(); const { code } = useParams(); - const initialMode = location.state?.isEditing ?? false; - const [isEditing, setIsEditing] = useState(initialMode); - const [isAdmin, setIsAdmin] = useState(false); const [loading, setLoading] = useState(true); @@ -119,8 +113,6 @@ export default function UnifiedAvailabilityPage() { timeSelectCalendarFramework?.dates?.[0]?.[0]?.date?.getFullYear() === 2000 ); const [areSelectingGeneralDays, setAreSelectingGeneralDays] = useState(false); - const [promptUserForLogin, setPromptUserForLogin] = useState(false); - const nav = useNavigate(); // Get current user index for saving @@ -301,123 +293,45 @@ export default function UnifiedAvailabilityPage() { return (
- {/* Desktop: Side-by-side view */} -
- fetchData(false)} - isSaving={isSaving} - userHasFilled={userHasFilled} - hasUnsavedChanges={hasUnsavedChanges} - setHasUnsavedChanges={setHasUnsavedChanges} - /> -
- - {/* Mobile: Current behavior (toggle between edit/view) */} -
- {isEditing ? ( - { - setIsEditing(false); - fetchData(false); - }} - onFetchComplete={() => { - if (getAccountName() === '' || getAccountName() === undefined) { - setPromptUserForLogin(true); - } - }} - code={code} - chartedUsers={chartedUsers} - setChartedUsers={setChartedUsers} - calendarState={timeSelectCalendarState} - setCalendarState={setTimeSelectCalendarState} - calendarFramework={timeSelectCalendarFramework} - setCalendarFramework={setTimeSelectCalendarFramework} - loading={loading} - setLoading={setLoading} - eventName={eventName} - setEventName={setEventName} - eventDescription={eventDescription} - setEventDescription={setEventDescription} - locationOptions={locationOptions} - setLocationOptions={setLocationOptions} - areSelectingGeneralDays={areSelectingGeneralDays} - setAreSelectingGeneralDays={setAreSelectingGeneralDays} - isGeneralDays={isGeneralDays} - setIsGeneralDays={setIsGeneralDays} - hasAvailability={hasAvailability} - setHasAvailability={setHasAvailability} - /> - ) : ( - { - setIsEditing(true); - fetchData(false); - }} - calendarState={groupViewCalendarState} - setCalendarState={setGroupViewCalendarState} - calendarFramework={groupViewCalendarFramework} - setCalendarFramework={setGroupViewCalendarFramework} - code={code} - chartedUsers={chartedUsers} - setChartedUsers={setChartedUsers} - eventName={eventName} - setEventName={setEventName} - eventDescription={eventDescription} - setEventDescription={setEventDescription} - locationVotes={locationVotes} - setLocationVotes={setLocationVotes} - locationOptions={locationOptions} - setLocationOptions={setLocationOptions} - adminChosenLocation={adminChosenLocation} - setAdminChosenLocation={setAdminChosenLocation} - loading={loading} - setLoading={setLoading} - allPeople={allPeople} - setAllPeople={setAllPeople} - declinedPeople={declinedPeople} - peopleStatus={peopleStatus} - setPeopleStatus={setPeopleStatus} - allUsers={allUsers} - setAllUsers={setAllUsers} - userHasFilled={userHasFilled} - setUserHasFilled={setUserHasFilled} - /> - )} -
+ fetchData(false)} + isSaving={isSaving} + userHasFilled={userHasFilled} + hasUnsavedChanges={hasUnsavedChanges} + setHasUnsavedChanges={setHasUnsavedChanges} + />
); } diff --git a/src/components/selectCalendarComponents/CalBlock.tsx b/src/components/selectCalendarComponents/CalBlock.tsx index e1544f2..a8b01f2 100644 --- a/src/components/selectCalendarComponents/CalBlock.tsx +++ b/src/components/selectCalendarComponents/CalBlock.tsx @@ -756,7 +756,7 @@ export default function CalBlock({
void; scrollRef?: React.RefObject; onScroll?: (scrollTop: number) => void; + hideHeader?: boolean; + hideDateBar?: boolean; + onColumnsPerPage?: (n: number) => void; } export default function Calendar({ @@ -72,6 +75,9 @@ export default function Calendar({ onPageChange, scrollRef, onScroll, + hideHeader = false, + hideDateBar = false, + onColumnsPerPage, }: CalendarProps) { const [calendarFramework, setCalendarFramework] = theCalendarFramework; const [calendarState, setCalendarState] = theCalendarState; @@ -141,6 +147,10 @@ export default function Calendar({ }; }, [compactMode]); + React.useEffect(() => { + onColumnsPerPage?.(numberOfColumnsPerPage); + }, [numberOfColumnsPerPage, onColumnsPerPage]); + const [internalStartPage, setInternalStartPage] = React.useState(0); const currentStartPage = controlledPage ?? internalStartPage; @@ -171,41 +181,42 @@ export default function Calendar({
{ handleStopHover(); - console.log("Mouse Left Calendar"); }} ref={ref} > -
- {currentStartPage !== 0 ? ( - - ) : ( -
- )} + {!hideHeader && ( +
+ {currentStartPage !== 0 ? ( + + ) : ( +
+ )} - {calendarLabel && ( - - {calendarLabel} - - )} + {calendarLabel && ( + + {calendarLabel} + + )} - {currentStartPage + numberOfColumnsPerPage < - calendarFramework.dates.flat().length ? ( - - ) : ( -
- )} -
+ {currentStartPage + numberOfColumnsPerPage < + calendarFramework.dates.flat().length ? ( + + ) : ( +
+ )} +
+ )}
onScroll((e.target as HTMLDivElement).scrollTop) : undefined} > -
-
+
+
{/* handles aligning it with the cal */} <> @@ -289,6 +299,7 @@ export default function Calendar({ isGeneralDays={isGeneralDays} setChartedUsers={setChartedUsers} chartedUsers={chartedUsers} + hideDateBar={hideDateBar} />
diff --git a/src/components/selectCalendarComponents/SelectCalendar.tsx b/src/components/selectCalendarComponents/SelectCalendar.tsx index 00ed840..36e3f4b 100644 --- a/src/components/selectCalendarComponents/SelectCalendar.tsx +++ b/src/components/selectCalendarComponents/SelectCalendar.tsx @@ -57,6 +57,7 @@ interface SelectCalanderProps { isGeneralDays: boolean; setChartedUsers?: Dispatch>; chartedUsers?: userData; + hideDateBar?: boolean; } function SelectCalander({ @@ -77,6 +78,7 @@ function SelectCalander({ isGeneralDays, setChartedUsers, chartedUsers, + hideDateBar = false, }: SelectCalanderProps) { const [timeBlocks, setTimeBlocks] = React.useState( generateTimeBlocks(startDate, endDate) @@ -130,12 +132,14 @@ const handleDisappear = useCallback(() => { return (
-
-
-
- + {!hideDateBar && ( +
+
+
+ +
-
+ )}
diff --git a/src/components/utils/components/EventOptionsMenu.tsx b/src/components/utils/components/EventOptionsMenu.tsx index 7683e06..c57dbf5 100644 --- a/src/components/utils/components/EventOptionsMenu.tsx +++ b/src/components/utils/components/EventOptionsMenu.tsx @@ -1,6 +1,11 @@ import { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; -import { IconDotsVertical, IconPencil, IconTrash, IconX } from '@tabler/icons-react'; +import { + IconDotsVertical, + IconPencil, + IconTrash, + IconX, +} from '@tabler/icons-react'; import { deleteEvent } from '../../../backend/events'; import DeletePopup from './DeletePopup'; @@ -11,7 +16,12 @@ interface EventOptionsMenuProps { onCancel?: () => Promise; } -export default function EventOptionsMenu({ eventCode, isAdmin, userHasFilled, onCancel }: EventOptionsMenuProps) { +export default function EventOptionsMenu({ + eventCode, + isAdmin, + userHasFilled, + onCancel, +}: EventOptionsMenuProps) { const [isMenuOpen, setIsMenuOpen] = useState(false); const [showDeletePopup, setShowDeletePopup] = useState(false); const [showCancelPopup, setShowCancelPopup] = useState(false); @@ -56,7 +66,10 @@ export default function EventOptionsMenu({ eventCode, isAdmin, userHasFilled, on className="p-1 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors" aria-label="Event options" > - + {isMenuOpen && (
- Decline Event + Decline Event )}
diff --git a/tailwind.config.js b/tailwind.config.js index 086aa0a..771b1b1 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -23,6 +23,10 @@ module.exports = { '0%': { opacity: '0', transform: 'translateY(30px)' }, '100%': { opacity: '1', transform: 'translateY(0)' } }, + 'sheet-up': { + '0%': { transform: 'translateY(100%)' }, + '100%': { transform: 'translateY(0)' } + }, 'slide-in-right': { '0%': { opacity: '0', transform: 'translateX(30px)' }, '100%': { opacity: '1', transform: 'translateX(0)' } @@ -40,6 +44,7 @@ module.exports = { 'slide-up-delay-1': 'slide-up 0.6s ease-out 0.1s forwards', 'slide-up-delay-2': 'slide-up 0.6s ease-out 0.2s forwards', 'slide-up-delay-3': 'slide-up 0.6s ease-out 0.3s forwards', + 'sheet-up': 'sheet-up 0.25s ease-out forwards', 'slide-in-right': 'slide-in-right 0.6s ease-out forwards', 'float': 'float 3s ease-in-out infinite' },