Skip to content

Commit 263ec45

Browse files
committed
Added 7days calender in habit tracker
1 parent 9075e01 commit 263ec45

3 files changed

Lines changed: 101 additions & 23 deletions

File tree

package-lock.json

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"@testing-library/jest-dom": "^6.9.1",
88
"@testing-library/react": "^16.3.0",
99
"@testing-library/user-event": "^13.5.0",
10+
"date-fns": "^4.1.0",
1011
"react": "^19.2.0",
1112
"react-dom": "^19.2.0",
1213
"react-scripts": "5.0.1",

src/App.js

Lines changed: 89 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import logo from './logo.svg';
22
import './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

510
const 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

1015
function 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

Comments
 (0)