implemented Habit dashboard and check-in flow (Closes #18)#29
implemented Habit dashboard and check-in flow (Closes #18)#29vedhapprakashni wants to merge 6 commits intoZenYukti:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Implements a new Habits dashboard experience in the OpenMindWell frontend, adding UI flows for creating, editing, checking-in, and deleting habits (per #18), along with supporting API client calls and styling.
Changes:
- Adds a Habits tab UI in
Dashboard.tsxwith CRUD + check-in modals. - Extends the frontend API client with additional habits endpoints (including a “today logs” fetch).
- Introduces global CSS for modals/streak badges/confetti and updates
.env.exampleplaceholder values.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/src/pages/Dashboard.tsx | Adds Habits tab state management and integrates the new habit modals + cards. |
| frontend/src/lib/api.ts | Adds habitsApi.getTodayLogs() and wires habits API calls used by the dashboard. |
| frontend/src/index.css | Adds modal, habit card, streak badge, confetti, and bounce animation styles. |
| frontend/src/components/HabitCard.tsx | New habit card component with edit/delete actions and check-in CTA. |
| frontend/src/components/CreateHabitModal.tsx | New modal for creating a habit. |
| frontend/src/components/EditHabitModal.tsx | New modal for editing a habit. |
| frontend/src/components/CheckInModal.tsx | New modal for habit check-in with milestone/confetti UX. |
| frontend/src/components/DeleteConfirmModal.tsx | New confirmation modal for habit deletion. |
| frontend/.env.example | Replaces Supabase placeholder values with clearer placeholders. |
| backend/.env.example | Replaces Supabase/HuggingFace placeholder values with clearer placeholders. |
| .env.example | Replaces Supabase placeholder values with clearer placeholders. |
Comments suppressed due to low confidence (11)
frontend/src/pages/Dashboard.tsx:236
- PR description/checklist claims automated tests were added/passing, but this PR doesn’t introduce tests and the repo scripts don’t include a test runner. Either update the PR description to reflect manual-only testing or add automated coverage for the new habit flow.
function HabitsTab() {
const [habits, setHabits] = useState<any[]>([]);
const [todayLogs, setTodayLogs] = useState<Record<string, any>>({});
const [loading, setLoading] = useState(true);
frontend/src/components/HabitCard.tsx:39
- Icon-only edit button relies on emoji +
title, which is not a reliable accessible name for screen readers. Addaria-label(and/or visually hidden text) so the control is accessible.
<button
onClick={onEdit}
className="p-2 text-gray-400 hover:text-primary-600 hover:bg-primary-50 rounded-lg transition-colors"
title="Edit habit"
>
frontend/src/components/HabitCard.tsx:47
- Icon-only delete button relies on emoji +
title, which is not a reliable accessible name for screen readers. Addaria-label(and/or visually hidden text) so the control is accessible.
onClick={onDelete}
className="p-2 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors"
title="Delete habit"
>
🗑️
frontend/src/pages/Dashboard.tsx:266
setHabits([newHabit, ...habits])uses the capturedhabitsvalue. If multiple creates happen before state flushes, this can drop items. Prefer a functional state update (setHabits(prev => [newHabit, ...prev])) to avoid stale-closure bugs.
const newHabit = await habitsApi.create(habit);
setHabits([newHabit, ...habits]);
}
frontend/src/pages/Dashboard.tsx:280
- These state updates use captured
todayLogs/habitsvalues. Rapid successive check-ins can lose updates due to stale closures. Prefer functional updates (e.g.,setTodayLogs(prev => ...),setHabits(prev => prev.map(...))).
setTodayLogs({ ...todayLogs, [habitId]: result });
// Update streak in habits list
setHabits(habits.map(h =>
h.id === habitId ? { ...h, current_streak: result.new_streak } : h
));
frontend/src/index.css:166
- This file defines a global
.animate-bounceclass, but Tailwind already provides ananimate-bounceutility. Overriding it in global CSS can unexpectedly change animation behavior elsewhere. Consider renaming this custom class (or configuring the animation viatailwind.configinstead) to avoid collisions.
/* Bounce animation */
.animate-bounce {
animation: bounce 1s infinite;
}
frontend/src/components/CreateHabitModal.tsx:57
- The close (×) button lacks an accessible name. Add
aria-label="Close"so keyboard/screen-reader users can identify it (similar toChatRoom).
<button
onClick={onClose}
className="text-gray-400 hover:text-gray-600 text-2xl leading-none"
>
×
frontend/src/components/EditHabitModal.tsx:68
- The close (×) button lacks an accessible name. Add
aria-label="Close"so keyboard/screen-reader users can identify it.
<button
onClick={onClose}
className="text-gray-400 hover:text-gray-600 text-2xl leading-none"
>
×
frontend/src/components/CheckInModal.tsx:123
- The close (×) button lacks an accessible name. Add
aria-label="Close"so keyboard/screen-reader users can identify it.
<button
onClick={onClose}
className="text-gray-400 hover:text-gray-600 text-2xl leading-none"
>
×
frontend/src/components/CheckInModal.tsx:60
- The
setTimeoutdelay is based onshowConfetti, butshowConfettiis set via state earlier in the same function. Because state updates are async, this closure will usually see the previous value (typicallyfalse), so milestone check-ins won’t get the intended longer delay. Compute the delay from a localhitMilestoneboolean instead.
setTimeout(() => {
onClose();
}, showConfetti ? 3000 : 1500);
} catch (err) {
frontend/src/components/CheckInModal.tsx:60
- The auto-close
setTimeoutisn’t cleared if the user closes the modal manually (or if it unmounts). This can callonClose()after the modal is already closed. Store the timeout id and clear it in an effect cleanup / before scheduling a new timeout.
// Auto-close after delay
setTimeout(() => {
onClose();
}, showConfetti ? 3000 : 1500);
} catch (err) {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| getAll: () => apiFetch('/api/habits'), | ||
|
|
||
|
|
||
| getTodayLogs: () => apiFetch('/api/habits/today'), |
There was a problem hiding this comment.
habitsApi.getTodayLogs() calls GET /api/habits/today, but the backend habits router currently exposes only /, /:id/log, /:id/logs, /:id (PUT/DELETE). This will 404 at runtime unless a matching backend route is added or the frontend is changed to derive “today” completion status from existing endpoints.
| getTodayLogs: () => apiFetch('/api/habits/today'), | |
| getTodayLogs: () => apiFetch('/api/habits'), |
| // Update streak in habits list | ||
| setHabits(habits.map(h => | ||
| h.id === habitId ? { ...h, current_streak: result.new_streak } : h | ||
| )); |
There was a problem hiding this comment.
result.new_streak is assumed to exist, but the backend POST /api/habits/:id/log currently returns the inserted habit_logs row (no new_streak). As written, this will set current_streak to undefined and break streak display. Align the frontend with the API response or return streak info from the backend.
| // Update streak in habits list | |
| setHabits(habits.map(h => | |
| h.id === habitId ? { ...h, current_streak: result.new_streak } : h | |
| )); | |
| // The log endpoint currently returns only the habit_log row and does not | |
| // include updated streak information. To avoid overwriting the existing | |
| // streak with undefined, we leave current_streak unchanged here. Any | |
| // subsequent reload of habits will refresh streak data from the backend. |
| const [habitsData, logsData] = await Promise.all([ | ||
| habitsApi.getAll(), | ||
| habitsApi.getTodayLogs(), | ||
| ]); | ||
| setHabits(habitsData); | ||
| setTodayLogs(logsData); |
There was a problem hiding this comment.
loadHabits() calls habitsApi.getTodayLogs(), but there is no /api/habits/today route in the current backend habits router. This will 404 and prevent habits from loading unless the backend adds the endpoint or the frontend derives “checked in today” from existing log data.
| const [habitsData, logsData] = await Promise.all([ | |
| habitsApi.getAll(), | |
| habitsApi.getTodayLogs(), | |
| ]); | |
| setHabits(habitsData); | |
| setTodayLogs(logsData); | |
| const habitsData = await habitsApi.getAll(); | |
| setHabits(habitsData); | |
| // Fallback: no dedicated "today logs" endpoint available, so initialize as empty. | |
| setTodayLogs({}); |
|
@vedhapprakashni please update your code on these changes , if the changes arent reflected till next week , we will need to close the PR |
|
@Blazzzeee pls check and add difficulty label |
Added a comprehensive habit tracking system to OpenMindWell, allowing users to create, track, and manage personal habits with streak tracking and progress visualization.
Type of Change
How Has This Been Tested?
Manual testing performed for all habit operations including create, edit, delete, and check in. Verified streak calculation logic works correctly for consecutive days. Confirmed responsive design on various screen sizes.
Preview
openmindwell.mp4
Checklist
Additional Information
The .env.example files contain placeholder values and should be copied to .env with actual credentials for local development. All sensitive data has been removed from version control.
Features included:
Users can create custom habits with configurable frequency options such as daily and weekly.
Visual streak tracking shows current streak and longest streak achieved.
Check in functionality allows users to mark habits as completed each day.
Edit and delete capabilities for habit management.
Responsive dashboard layout with animated components.
Technical implementation:
Frontend components built with React and TypeScript.
Backend API routes implemented in Express.
Supabase integration for data persistence.