@@ -8,6 +8,11 @@ import startOfMonth from 'date-fns/startOfMonth';
88import endOfMonth from 'date-fns/endOfMonth' ;
99import format from 'date-fns/format' ;
1010import isToday from 'date-fns/isToday' ;
11+ import subWeeks from 'date-fns/subWeeks' ;
12+ import addWeeks from 'date-fns/addWeeks' ;
13+ import subMonths from 'date-fns/subMonths' ;
14+ import addMonths from 'date-fns/addMonths' ;
15+ import { useSwipeable } from 'react-swipeable' ;
1116import { Award , Star , Medal } from 'lucide-react' ;
1217import { motion } from 'framer-motion' ;
1318
@@ -95,6 +100,7 @@ function App() {
95100 const [ newlyUnlocked , setNewlyUnlocked ] = useState ( [ ] ) ;
96101 const [ modalOpen , setModalOpen ] = useState ( false ) ;
97102 const [ selectedHabit , setSelectedHabit ] = useState ( null ) ;
103+ const [ currentDate , setCurrentDate ] = useState ( new Date ( ) ) ;
98104
99105 const filteredHabits = selectedCategory === 'All'
100106 ? habits
@@ -105,12 +111,12 @@ function App() {
105111
106112 useEffect ( ( ) => {
107113 if ( calendarMode === 'weekly' ) {
108- const start = startOfWeek ( new Date ( ) , { weekStartsOn : 0 } ) ; // Sunday
109- const end = endOfWeek ( new Date ( ) , { weekStartsOn : 0 } ) ;
114+ const start = startOfWeek ( currentDate , { weekStartsOn : 0 } ) ; // Sunday
115+ const end = endOfWeek ( currentDate , { weekStartsOn : 0 } ) ;
110116 setCurrentWeekDays ( eachDayOfInterval ( { start, end } ) ) ;
111117 } else if ( calendarMode === 'monthly' ) {
112- const start = startOfMonth ( new Date ( ) ) ;
113- const end = endOfMonth ( new Date ( ) ) ;
118+ const start = startOfMonth ( currentDate ) ;
119+ const end = endOfMonth ( currentDate ) ;
114120 const daysInMonth = eachDayOfInterval ( { start, end } ) ;
115121
116122 // Pad days to start on Sunday and end on Saturday
@@ -125,7 +131,7 @@ function App() {
125131
126132 setCurrentWeekDays ( paddedDays ) ;
127133 }
128- } , [ calendarMode ] ) ;
134+ } , [ calendarMode , currentDate ] ) ;
129135
130136 // Toggle calendar mode handler
131137 const toggleCalendarMode = ( ) => {
@@ -136,6 +142,11 @@ function App() {
136142 } ) ;
137143 } ;
138144
145+ const goToPrevWeek = ( ) => setCurrentDate ( prev => subWeeks ( prev , 1 ) ) ;
146+ const goToNextWeek = ( ) => setCurrentDate ( prev => addWeeks ( prev , 1 ) ) ;
147+ const goToPrevMonth = ( ) => setCurrentDate ( prev => subMonths ( prev , 1 ) ) ;
148+ const goToNextMonth = ( ) => setCurrentDate ( prev => addMonths ( prev , 1 ) ) ;
149+
139150 // Check if habit is completed on a given day
140151 const isCompletedOn = ( habit , day ) => {
141152 return habit . completions . some ( date => format ( date , 'yyyy-MM-dd' ) === format ( day , 'yyyy-MM-dd' ) ) ;
@@ -168,6 +179,16 @@ function App() {
168179 toggleCompletion ( habitId , day ) ;
169180 } ;
170181
182+ const weeklySwipeHandlers = useSwipeable ( {
183+ onSwipedLeft : goToNextWeek ,
184+ onSwipedRight : goToPrevWeek ,
185+ } ) ;
186+
187+ const monthlySwipeHandlers = useSwipeable ( {
188+ onSwipedLeft : goToNextMonth ,
189+ onSwipedRight : goToPrevMonth ,
190+ } ) ;
191+
171192 return (
172193 < div className = "App" >
173194 < header className = "App-header" >
@@ -246,6 +267,13 @@ function App() {
246267 </ button >
247268 </ div >
248269
270+ { calendarMode !== '90day' && (
271+ < div className = "mb-4 flex gap-2" >
272+ < button onClick = { calendarMode === 'weekly' ? goToPrevWeek : goToPrevMonth } className = "px-4 py-2 bg-gray-600 text-white rounded" > Prev</ button >
273+ < button onClick = { calendarMode === 'weekly' ? goToNextWeek : goToNextMonth } className = "px-4 py-2 bg-gray-600 text-white rounded" > Next</ button >
274+ </ div >
275+ ) }
276+
249277 { /* Display current habits */ }
250278 < div style = { { marginTop : '20px' , textAlign : 'left' } } >
251279 < h3 > Current Habits:</ h3 >
@@ -254,37 +282,37 @@ function App() {
254282 ) : (
255283 < >
256284 { calendarMode === 'weekly' ? (
257- < div className = "overflow-x-auto" >
258- < div className = "grid grid-cols-7 gap-2 text-center font-semibold mb-2 min-w-max" >
259- { currentWeekDays . map ( day => (
260- < div key = { day ? day . toISOString ( ) : 'empty-' + Math . random ( ) } > { day ? format ( day , 'EEE' ) : '' } </ div >
285+ < div className = "overflow-x-auto" { ...weeklySwipeHandlers } >
286+ < div className = "grid grid-cols-7 gap-2 text-center font-semibold mb-2 min-w-max" >
287+ { currentWeekDays . map ( day => (
288+ < div key = { day ? day . toISOString ( ) : 'empty-' + Math . random ( ) } > { day ? format ( day , 'EEE' ) : '' } </ div >
289+ ) ) }
290+ </ div >
291+ { filteredHabits . map ( habit => (
292+ < div key = { habit . id } className = "mb-4" >
293+ < div className = "font-bold" > { habit . name } </ div >
294+ < div className = "grid grid-cols-7 gap-2 text-center min-w-max" >
295+ { currentWeekDays . map ( day => {
296+ if ( ! day ) return < div key = { 'empty-' + Math . random ( ) } className = "min-h-11 min-w-11 rounded bg-gray-100 pointer-events-none p-1" /> ;
297+ const done = isCompletedOn ( habit , day ) ;
298+ const today = isToday ( day ) ;
299+ return (
300+ < div
301+ key = { day . toISOString ( ) }
302+ className = { `min-h-11 min-w-11 rounded cursor-pointer p-1 ${
303+ done ? 'bg-green-500' : today ? 'bg-blue-500' : 'bg-gray-200'
304+ } `}
305+ title = { format ( day , 'yyyy-MM-dd' ) }
306+ onClick = { ( ) => handleCompletionClick ( habit . id , day ) }
307+ />
308+ ) ;
309+ } ) }
310+ </ div >
311+ </ div >
261312 ) ) }
262313 </ div >
263- { filteredHabits . map ( habit => (
264- < div key = { habit . id } className = "mb-4" >
265- < div className = "font-bold" > { habit . name } </ div >
266- < div className = "grid grid-cols-7 gap-2 text-center min-w-max" >
267- { currentWeekDays . map ( day => {
268- if ( ! day ) return < div key = { 'empty-' + Math . random ( ) } className = "h-8 w-8 rounded bg-gray-100 pointer-events-none" /> ;
269- const done = isCompletedOn ( habit , day ) ;
270- const today = isToday ( day ) ;
271- return (
272- < div
273- key = { day . toISOString ( ) }
274- className = { `h-8 w-8 rounded cursor-pointer ${
275- done ? 'bg-green-500' : today ? 'bg-blue-500' : 'bg-gray-200'
276- } `}
277- title = { format ( day , 'yyyy-MM-dd' ) }
278- onClick = { ( ) => handleCompletionClick ( habit . id , day ) }
279- />
280- ) ;
281- } ) }
282- </ div >
283- </ div >
284- ) ) }
285- </ div >
286314 ) : calendarMode === 'monthly' ? (
287- < div className = "overflow-x-auto" >
315+ < div className = "overflow-x-auto" { ... monthlySwipeHandlers } >
288316 < div className = "grid grid-cols-7 gap-2 text-center font-semibold mb-2 min-w-max" >
289317 { [ 'Sun' , 'Mon' , 'Tue' , 'Wed' , 'Thu' , 'Fri' , 'Sat' ] . map ( day => (
290318 < div key = { day } > { day } </ div >
@@ -296,14 +324,14 @@ function App() {
296324 < div className = "grid grid-cols-7 gap-2 text-center min-w-max" >
297325 { currentWeekDays . map ( ( day , idx ) => {
298326 if ( day === null ) {
299- return < div key = { idx } className = "h-6 w-6 rounded bg-gray-100 pointer-events-none" /> ;
327+ return < div key = { idx } className = "min-h-11 min-w-11 rounded bg-gray-100 pointer-events-none p-1 " /> ;
300328 }
301329 const done = isCompletedOn ( habit , day ) ;
302330 const today = isToday ( day ) ;
303331 return (
304332 < div
305333 key = { idx }
306- className = { `h-6 w-6 rounded cursor-pointer ${
334+ className = { `min-h-11 min-w-11 rounded cursor-pointer p-1 ${
307335 done ? 'bg-green-500' : today ? 'bg-blue-500' : 'bg-gray-200'
308336 } `}
309337 title = { format ( day , 'yyyy-MM-dd' ) }
@@ -316,7 +344,7 @@ function App() {
316344 ) ) }
317345 </ div >
318346 ) : (
319- < div className = "habit-list" >
347+ < div className = "habit-list overflow-y-auto " >
320348 { filteredHabits . map ( ( habit ) => (
321349 < div key = { habit . id } className = "habit-card p-3 sm:p-4 border rounded mb-4 w-full" >
322350 < div className = "font-bold text-sm sm:text-base" > { habit . name } - { habit . category } </ div >
0 commit comments