11import logo from './logo.svg' ;
22import './App.css' ;
3- import { useState } from 'react' ;
3+ import { useState , useEffect } from 'react' ;
4+ import eachDayOfInterval from 'date-fns/eachDayOfInterval' ;
5+ import startOfWeek from 'date-fns/startOfWeek' ;
6+ import endOfWeek from 'date-fns/endOfWeek' ;
7+ import format from 'date-fns/format' ;
8+ import isToday from 'date-fns/isToday' ;
49
510const initialHabits = [
6- { id : 1 , name : 'Code daily' , category : 'General' } ,
7- { id : 2 , name : 'Read tech articles' , category : 'General' } ,
11+ { id : 1 , name : 'Code daily' , category : 'General' , completions : [ new Date ( ) ] } ,
12+ { id : 2 , name : 'Read tech articles' , category : 'General' , completions : [ ] } ,
813] ;
914
1015function App ( ) {
@@ -17,45 +22,64 @@ function App() {
1722 console . error ( 'Category cannot be empty' ) ;
1823 return ;
1924 }
20- const habitToAdd = { ...newHabit , id : Date . now ( ) } ;
25+ const habitToAdd = { ...newHabit , id : Date . now ( ) , completions : [ ] } ;
2126 setHabits ( [ ...habits , habitToAdd ] ) ;
2227 setNewHabit ( { name : '' , category : 'General' } ) ;
2328 console . log ( 'Habits:' , habits ) ;
2429 } ;
2530
2631 const [ selectedCategory , setSelectedCategory ] = useState ( 'All' ) ;
32+ const [ calendarMode , setCalendarMode ] = useState ( '90day' ) ; // '90day' | 'weekly' | 'monthly'
2733
2834 const filteredHabits = selectedCategory === 'All'
2935 ? habits
3036 : habits . filter ( habit => habit . category === selectedCategory ) ;
3137
38+ // Calculate current week days
39+ const [ currentWeekDays , setCurrentWeekDays ] = useState ( [ ] ) ;
40+
41+ useEffect ( ( ) => {
42+ if ( calendarMode === 'weekly' ) {
43+ const start = startOfWeek ( new Date ( ) , { weekStartsOn : 0 } ) ; // Sunday
44+ const end = endOfWeek ( new Date ( ) , { weekStartsOn : 0 } ) ;
45+ setCurrentWeekDays ( eachDayOfInterval ( { start, end } ) ) ;
46+ }
47+ } , [ calendarMode ] ) ;
48+
49+ // Toggle calendar mode handler
50+ const toggleCalendarMode = ( ) => {
51+ setCalendarMode ( prev => ( prev === '90day' ? 'weekly' : '90day' ) ) ;
52+ } ;
53+
54+ // Check if habit is completed on a given day
55+ const isCompletedOn = ( habit , day ) => {
56+ return habit . completions . some ( date => format ( date , 'yyyy-MM-dd' ) === format ( day , 'yyyy-MM-dd' ) ) ;
57+ } ;
58+
3259 return (
3360 < div className = "App" >
3461 < header className = "App-header" >
35- { /* <img src={logo} className="App-logo" alt="logo" />
36- <p>
37- Edit <code>src/App.js</code> and save to reload.
38- </p> */ }
39-
40- { /* Input for new habit name */ }
62+ { /* Habit input and category select */ }
4163 < input
4264 type = "text"
4365 placeholder = "Habit name"
4466 value = { newHabit . name }
4567 onChange = { ( e ) => setNewHabit ( { ...newHabit , name : e . target . value } ) }
68+ className = "mb-2 p-2 border border-gray-300 rounded-md w-full max-w-sm"
4669 />
47-
48- { /* Select for new habit category */ }
4970 < select
5071 value = { newHabit . category }
5172 onChange = { ( e ) => setNewHabit ( { ...newHabit , category : e . target . value } ) }
52- className = "w-full max-w-sm p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 "
73+ className = "mb-2 p-2 border border-gray-300 rounded-md w-full max-w-sm "
5374 >
5475 { categories . map ( cat => < option key = { cat } value = { cat } > { cat } </ option > ) }
5576 </ select >
56-
57- { /* Button to add habit */ }
58- < button onClick = { addHabit } > Add Habit</ button >
77+ < button
78+ onClick = { addHabit }
79+ className = "mb-4 px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"
80+ >
81+ Add Habit
82+ </ button >
5983
6084 { /* Filter buttons */ }
6185 < div className = "flex flex-wrap gap-2 mb-4" >
@@ -72,19 +96,61 @@ function App() {
7296 ) ) }
7397 </ div >
7498
99+ { /* Calendar mode toggle */ }
100+ < div className = "mb-4" >
101+ < button
102+ onClick = { toggleCalendarMode }
103+ className = "px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700"
104+ >
105+ Toggle to { calendarMode === '90day' ? 'Weekly' : '90-day' } View
106+ </ button >
107+ </ div >
108+
75109 { /* Display current habits */ }
76110 < div style = { { marginTop : '20px' , textAlign : 'left' } } >
77111 < h3 > Current Habits:</ h3 >
78112 { filteredHabits . length === 0 ? (
79113 < p > No habits in this category</ p >
80114 ) : (
81- < ul >
82- { filteredHabits . map ( ( habit ) => (
83- < li key = { habit . id } >
84- { habit . name } - Category: { habit . category }
85- </ li >
86- ) ) }
87- </ ul >
115+ < >
116+ { calendarMode === 'weekly' ? (
117+ < div >
118+ < div className = "grid grid-cols-7 gap-2 text-center font-semibold mb-2" >
119+ { currentWeekDays . map ( day => (
120+ < div key = { day . toISOString ( ) } > { format ( day , 'EEE' ) } </ div >
121+ ) ) }
122+ </ div >
123+ { filteredHabits . map ( habit => (
124+ < div key = { habit . id } className = "mb-4" >
125+ < div className = "font-bold" > { habit . name } </ div >
126+ < div className = "grid grid-cols-7 gap-2 text-center" >
127+ { currentWeekDays . map ( day => {
128+ const done = isCompletedOn ( habit , day ) ;
129+ const today = isToday ( day ) ;
130+ return (
131+ < div
132+ key = { day . toISOString ( ) }
133+ className = { `h-8 w-8 rounded ${
134+ done ? 'bg-green-500' : today ? 'bg-blue-500' : 'bg-gray-200'
135+ } `}
136+ title = { format ( day , 'yyyy-MM-dd' ) }
137+ />
138+ ) ;
139+ } ) }
140+ </ div >
141+ </ div >
142+ ) ) }
143+ </ div >
144+ ) : (
145+ < ul >
146+ { filteredHabits . map ( ( habit ) => (
147+ < li key = { habit . id } >
148+ { habit . name } - Category: { habit . category }
149+ </ li >
150+ ) ) }
151+ </ ul >
152+ ) }
153+ </ >
88154 ) }
89155 </ div >
90156
0 commit comments