diff --git a/src/components/calendar-month/MonthCalendar.tsx b/src/components/calendar-month/MonthCalendar.tsx index 98a5bc8..7221207 100644 --- a/src/components/calendar-month/MonthCalendar.tsx +++ b/src/components/calendar-month/MonthCalendar.tsx @@ -7,10 +7,11 @@ import { capitalizeFirstLetter } from '@utils'; import clsx from 'clsx'; import React from 'react'; import { type AriaButtonProps, useCalendar, useLocale } from 'react-aria'; +import { useLocation } from 'react-router-dom'; import { useCalendarState } from 'react-stately'; -import CalendarGrid from './CalendarGrid'; -import CalendarHeader from './CalendarHeader'; +import MonthCalendarGrid from './MonthCalendarGrid'; +import MonthCalendarHeader from './MonthCalendarHeader'; import NoteDialog from './NoteDialog'; import OccurrenceDialog from './OccurrenceDialog'; @@ -26,12 +27,12 @@ const createCalendar = (identifier: string) => { const MonthCalendar = () => { const { onRangeChange } = useOccurrencesStore(); const { locale } = useLocale(); - const state = useCalendarState({ + const calendarState = useCalendarState({ locale, createCalendar, }); const { calendarProps, prevButtonProps, nextButtonProps, title } = - useCalendar({}, state); + useCalendar({}, calendarState); const { isOpen: isNoteDialogOpen, onOpen: openNoteDialog, @@ -43,17 +44,36 @@ const MonthCalendar = () => { onClose: closeOccurrenceDialog, } = useDisclosure(); const [activeDate, setActiveDate] = React.useState(null); + const { state: locationState } = useLocation(); + + const setFocusedDate = React.useCallback( + (year: number, month: number, day: number) => { + const nextFocusedDate = new CalendarDate(year, month, day); + calendarState.setFocusedDate(nextFocusedDate); + }, + [calendarState] + ); + + React.useEffect(() => { + if (!locationState) { + return; + } + + const { year, month } = locationState; + + setFocusedDate(year, month, 1); + }, [locationState, setFocusedDate]); React.useEffect(() => { onRangeChange( generateCalendarRange( - state.visibleRange.start.year, - state.visibleRange.start.month + calendarState.visibleRange.start.year, + calendarState.visibleRange.start.month ) ); }, [ - state.visibleRange.start.year, - state.visibleRange.start.month, + calendarState.visibleRange.start.year, + calendarState.visibleRange.start.month, onRangeChange, ]); @@ -63,18 +83,13 @@ const MonthCalendar = () => { `${activeMonthLabel.slice(0, 3)} ${activeYear} | Habitrack Calendar` ); - const setFocusedDate = (year: number, month: number, day: number) => { - const nextFocusedDate = new CalendarDate(year, month, day); - state.setFocusedDate(nextFocusedDate); - }; - const navigateToMonth = (month: number) => { - const { year, day } = state.focusedDate; + const { year, day } = calendarState.focusedDate; setFocusedDate(year, month, day); }; const navigateToYear = (year: number) => { - const { month, day } = state.focusedDate; + const { month, day } = calendarState.focusedDate; setFocusedDate(year, month, day); }; @@ -133,21 +148,21 @@ const MonthCalendar = () => { return ( <>
- - diff --git a/src/components/calendar-month/CalendarCell.tsx b/src/components/calendar-month/MonthCalendarCell.tsx similarity index 94% rename from src/components/calendar-month/CalendarCell.tsx rename to src/components/calendar-month/MonthCalendarCell.tsx index 69ce000..0e4f3c9 100644 --- a/src/components/calendar-month/CalendarCell.tsx +++ b/src/components/calendar-month/MonthCalendarCell.tsx @@ -45,7 +45,7 @@ type CalendarCellProps = { position: CellPosition; }; -const CalendarCell = ({ +const MonthCalendarCell = ({ dateNumber, monthNumber, fullYear, @@ -64,6 +64,7 @@ const CalendarCell = ({ const screenSize = useScreenSize(); const cellDate = new Date(fullYear, monthNumber - 1, dateNumber); const isTodayCell = isToday(cellDate); + const isFutureCell = isFuture(cellDate); const date = format(cellDate, 'yyyy-MM-dd'); const isDesktop = screenSize >= 1024; @@ -97,7 +98,10 @@ const CalendarCell = ({ } } - if (isMobile || e?.currentTarget instanceof HTMLButtonElement) { + if ( + !isFutureCell && + (isMobile || e?.currentTarget instanceof HTMLButtonElement) + ) { return onAddOccurrence(dateNumber, monthNumber, fullYear); } }, @@ -113,6 +117,7 @@ const CalendarCell = ({ user, isMobile, isTodayCell, + isFutureCell, ] ); @@ -161,7 +166,7 @@ const CalendarCell = ({ position === 'bottom-left' && 'rounded-bl-md', position === 'bottom-right' && 'rounded-br-md', isTodayCell && - 'bg-background-200 hover:bg-background-300 dark:bg-background-800 dark:hover:bg-background-700' + 'bg-background-100 hover:bg-background-300 dark:bg-background-700 dark:hover:bg-background-700' ); const cellHeaderClassName = clsx( @@ -182,13 +187,13 @@ const CalendarCell = ({
{rangeStatus === 'in-range' && !isMobile && (
- {!isFuture(cellDate) && ( + {!isFutureCell && ( @@ -150,7 +151,7 @@ const CalendarGrid = ({ const position = getCellPosition(weekIndex, dayIndex); return ( - { - const props: CalendarHeaderProps = { +describe(MonthCalendarHeader.name, () => { + const props: MonthCalendarHeaderProps = { activeMonthLabel: 'January', activeYear: '2022', prevButtonProps: { @@ -23,13 +25,13 @@ describe(CalendarHeader.name, () => { }; it.skip('should render month and year', () => { - const { getByText } = render(); + const { getByText } = render(); expect(getByText('January 2022')).toBeInTheDocument(); }); it.skip('should disable previous button', () => { const { getByRole } = render( - @@ -39,7 +41,7 @@ describe(CalendarHeader.name, () => { it.skip('should disable next button', () => { const { getByRole } = render( - @@ -48,13 +50,13 @@ describe(CalendarHeader.name, () => { }); it.skip('should call onNavigateBack', () => { - const { getByRole } = render(); + const { getByRole } = render(); getByRole('navigate-back').click(); expect(props.onNavigateBack).toHaveBeenCalled(); }); it.skip('should call onNavigateForward', () => { - const { getByRole } = render(); + const { getByRole } = render(); getByRole('navigate-forward').click(); expect(props.onNavigateForward).toHaveBeenCalled(); }); diff --git a/src/components/calendar-month/CalendarHeader.tsx b/src/components/calendar-month/MonthCalendarHeader.tsx similarity index 57% rename from src/components/calendar-month/CalendarHeader.tsx rename to src/components/calendar-month/MonthCalendarHeader.tsx index 32477ad..c1bad12 100644 --- a/src/components/calendar-month/CalendarHeader.tsx +++ b/src/components/calendar-month/MonthCalendarHeader.tsx @@ -1,8 +1,19 @@ +import { TraitChip } from '@components'; import { useScreenSize } from '@hooks'; -import { Select, SelectItem, Button, SelectSection } from '@nextui-org/react'; +import type { Habit, Trait } from '@models'; +import type { SelectedItems } from '@nextui-org/react'; +import { + ListboxItem, + Select, + SelectItem, + Button, + SelectSection, +} from '@nextui-org/react'; import { ArrowFatLeft, ArrowFatRight } from '@phosphor-icons/react'; import { useTraitsStore, useHabitsStore, useOccurrencesStore } from '@stores'; import { useUser } from '@supabase/auth-helpers-react'; +import { getHabitIconUrl } from '@utils'; +import clsx from 'clsx'; import React from 'react'; type NavigationButtonProps = { @@ -10,7 +21,7 @@ type NavigationButtonProps = { 'aria-label': string; }; -export type CalendarHeaderProps = { +export type MonthCalendarHeaderProps = { activeMonthLabel: string; activeYear: string; prevButtonProps: NavigationButtonProps; @@ -39,7 +50,7 @@ const MONTHS = [ const YEARS = Array.from({ length: 31 }, (_, i) => 2000 + i); -const CalendarHeader = ({ +const MonthCalendarHeader = ({ activeMonthLabel, activeYear, prevButtonProps, @@ -49,7 +60,7 @@ const CalendarHeader = ({ onNavigateToMonth, onNavigateToYear, onResetFocusedDate, -}: CalendarHeaderProps) => { +}: MonthCalendarHeaderProps) => { const { habits } = useHabitsStore(); const { traits } = useTraitsStore(); const { filteredBy, filterBy } = useOccurrencesStore(); @@ -59,6 +70,10 @@ const CalendarHeader = ({ const shouldRenderFilters = !!user && habits.length > 0 && traits.length > 0; + const habitsByTraitName = React.useMemo(() => { + return Object.groupBy(habits, (habit) => habit.trait?.name || 'Unknown'); + }, [habits]); + const handleMonthChange: React.ChangeEventHandler = ( event ) => { @@ -93,11 +108,14 @@ const CalendarHeader = ({ const isDesktop = screenSize > 1024; return ( -
-
-
+
+
+
-
+
@@ -157,10 +186,12 @@ const CalendarHeader = ({ isIconOnly size={isDesktop ? 'md' : 'sm'} variant="light" + color="secondary" + radius="sm" isDisabled={nextButtonProps.disabled} aria-label={nextButtonProps['aria-label']} onClick={onNavigateForward} - className={isMobile ? 'w-6 min-w-fit p-0' : ''} + className={clsx('h-auto', isMobile && 'w-6 min-w-fit p-0')} role="navigate-forward" > @@ -168,9 +199,12 @@ const CalendarHeader = ({
{shouldRenderFilters && ( -
+