From da9bd83f3205bb74d81118212e231a4c27772a69 Mon Sep 17 00:00:00 2001 From: rathi Date: Thu, 21 Nov 2024 14:59:18 -0500 Subject: [PATCH] added user auth --- src/App.tsx | 190 +++++--------------------------- src/components/HabitList.tsx | 107 ++---------------- src/components/Login.tsx | 67 +++++++++++ src/components/SettingsView.tsx | 120 ++++++++++++++++++++ src/components/Sidebar.tsx | 17 ++- src/components/SignUp.tsx | 67 +++++++++++ src/contexts/AuthContext.tsx | 68 ++++++++++++ src/hooks/useHabits.ts | 151 ++++++++++++++++++------- src/hooks/useWeek.ts | 27 ++++- 9 files changed, 506 insertions(+), 308 deletions(-) create mode 100644 src/components/Login.tsx create mode 100644 src/components/SettingsView.tsx create mode 100644 src/components/SignUp.tsx create mode 100644 src/contexts/AuthContext.tsx diff --git a/src/App.tsx b/src/App.tsx index 0297334..e1c9279 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,12 +6,20 @@ import { Sidebar } from './components/Sidebar'; import { useHabits } from './hooks/useHabits'; import { useWeek } from './hooks/useWeek'; import { ThemeProvider, useThemeContext } from './contexts/ThemeContext'; +import { AuthProvider, useAuth } from './contexts/AuthContext'; +import { Login } from './components/Login'; +import { SignUp } from './components/SignUp'; +import { SettingsView } from './components/SettingsView'; + +const DAYS_OF_WEEK = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; function HabitTrackerContent() { const { theme, isDark, toggleDarkMode, defaultView, habitSort } = useThemeContext(); const [newHabit, setNewHabit] = useState(''); const [activeView, setActiveView] = useState<'habits' | 'calendar' | 'settings'>(defaultView); const [currentMonth, setCurrentMonth] = useState(new Date()); + const { user, loading, signOut } = useAuth(); + const [authView, setAuthView] = useState<'login' | 'signup'>('login'); const { habits, @@ -19,8 +27,7 @@ function HabitTrackerContent() { addHabit: addHabitApi, toggleHabit, updateHabit, - deleteHabit, - updateStreak + deleteHabit } = useHabits(); const { @@ -30,8 +37,6 @@ function HabitTrackerContent() { changeWeek } = useWeek(); - const daysOfWeek = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; - useEffect(() => { fetchHabits(); setCurrentWeek(getCurrentWeekDates()); @@ -122,11 +127,10 @@ function HabitTrackerContent() {

Keep up the good work! Consistency is key.

@@ -147,163 +151,17 @@ function HabitTrackerContent() { /> ); - const renderSettingsView = () => { - const { - theme, - isDark, - showStreaks, - dailyReminder, - defaultView, - habitSort, - toggleDarkMode, - toggleStreaks, - toggleDailyReminder, - setDefaultView, - setHabitSort - } = useThemeContext(); - - const handleReminderToggle = () => { - if (!dailyReminder && Notification.permission === 'default') { - Notification.requestPermission().then(permission => { - if (permission === 'granted') { - toggleDailyReminder(); - } - }); - } else { - toggleDailyReminder(); - } - }; + if (loading) { + return
Loading...
; + } - return ( -
-

Settings

-
-
- Dark Mode - -
- -
- Show Streaks - -
- -
-
- Daily Reminder -

Get notified at 10:00 AM daily

-
- -
- -
-
- Default View -

Choose your starting page

-
-
- - -
-
- -
-
- Sort Habits -

Choose how to order your habits

-
-
- - -
-
-
-
+ if (!user) { + return authView === 'login' ? ( + setAuthView('signup')} /> + ) : ( + setAuthView('login')} /> ); - }; + } return (
@@ -312,7 +170,7 @@ function HabitTrackerContent() {
{activeView === 'habits' && renderHabitsView()} {activeView === 'calendar' && renderCalendarView()} - {activeView === 'settings' && renderSettingsView()} + {activeView === 'settings' && }
@@ -321,8 +179,10 @@ function HabitTrackerContent() { export default function HabitTracker() { return ( - - - + + + + + ); } \ No newline at end of file diff --git a/src/components/HabitList.tsx b/src/components/HabitList.tsx index 91749dc..e2c3857 100644 --- a/src/components/HabitList.tsx +++ b/src/components/HabitList.tsx @@ -1,7 +1,8 @@ -import React, { useEffect } from 'react'; +import React from 'react'; import { Trash2 } from 'lucide-react'; import { Habit } from '../types'; import { useThemeContext } from '../contexts/ThemeContext'; +import { calculateStreak } from '../utils/streakCalculator'; interface HabitListProps { habits: Habit[]; @@ -10,83 +11,9 @@ interface HabitListProps { onToggleHabit: (id: number, date: string) => void; onUpdateHabit: (id: number, name: string) => void; onDeleteHabit: (id: number) => void; - onUpdateStreak: (id: number, newStreak: number) => Promise; + onUpdateStreak?: (id: number, newStreak: number) => Promise; } -const calculateStreak = (completedDates: string[]): { currentStreak: number; bestStreak: number } => { - if (!completedDates || completedDates.length === 0) { - return { currentStreak: 0, bestStreak: 0 }; - } - - // Get today at midnight in UTC - const today = new Date(); - const utcToday = new Date(Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate())); - - const todayStr = utcToday.toISOString().split('T')[0]; - - // Sort dates in descending order (most recent first) - const sortedDates = [...completedDates] - .filter(date => !isNaN(new Date(date).getTime())) - .sort((a, b) => new Date(b).getTime() - new Date(a).getTime()); - - let currentStreak = 0; - let bestStreak = 0; - let tempStreak = 0; - - // Check if today is completed to maintain streak - const hasTodayCompleted = sortedDates.includes(todayStr); - - // Initialize current streak based on today's completion - if (hasTodayCompleted) { - currentStreak = 1; - let checkDate = new Date(utcToday); - - // Check previous days - while (true) { - checkDate.setUTCDate(checkDate.getUTCDate() - 1); - const dateStr = checkDate.toISOString().split('T')[0]; - - if (sortedDates.includes(dateStr)) { - currentStreak++; - } else { - break; - } - } - } - - // Calculate best streak - for (let i = 0; i < sortedDates.length; i++) { - const currentDate = new Date(sortedDates[i]); - currentDate.setHours(0, 0, 0, 0); // Normalize time part for comparison - - if (i === 0) { - tempStreak = 1; - } else { - const prevDate = new Date(sortedDates[i - 1]); - const diffDays = Math.floor( - (prevDate.getTime() - currentDate.getTime()) / (1000 * 60 * 60 * 24) - ); - - if (diffDays === 1) { - tempStreak++; - } else if (diffDays === 0) { - // Same day, skip - continue; - } else { - // Reset streak on gap - bestStreak = Math.max(bestStreak, tempStreak); - tempStreak = 1; - } - } - } - - // Final check for best streak - bestStreak = Math.max(bestStreak, tempStreak); - bestStreak = Math.max(bestStreak, currentStreak); - - return { currentStreak, bestStreak }; -}; - export function HabitList({ habits, currentWeek, @@ -94,17 +21,15 @@ export function HabitList({ onToggleHabit, onUpdateHabit, onDeleteHabit, - onUpdateStreak, }: HabitListProps) { const { showStreaks } = useThemeContext(); - - useEffect(() => { - console.log('Current week dates:', - currentWeek.map(date => - `${new Date(date).toLocaleDateString()} (${daysOfWeek[new Date(date).getDay() === 0 ? 6 : new Date(date).getDay() - 1]})` - ) - ); - }, []); + + // Helper function to get day name + const getDayName = (dateStr: string) => { + const date = new Date(dateStr); + const dayIndex = date.getDay() === 0 ? 6 : date.getDay() - 1; + return daysOfWeek[dayIndex]; + }; return (
@@ -112,13 +37,11 @@ export function HabitList({ Habit - {currentWeek.map((dateStr, index) => { + {currentWeek.map((dateStr) => { const date = new Date(dateStr); - const dayIndex = date.getDay() === 0 ? 6 : date.getDay() - 1; - return ( -
{daysOfWeek[dayIndex]}
+
{getDayName(dateStr)}
{date.getDate()}
@@ -155,12 +78,6 @@ export function HabitList({ checked={habit.completedDates.includes(date)} onChange={() => { onToggleHabit(habit.id, date); - const newCompletedDates = habit.completedDates.includes(date) - ? habit.completedDates.filter(d => d !== date) - : [...habit.completedDates, date]; - - const { bestStreak } = calculateStreak(newCompletedDates); - onUpdateStreak(habit.id, bestStreak); }} aria-label={`Mark ${habit.name} as completed for ${date}`} className="sr-only" diff --git a/src/components/Login.tsx b/src/components/Login.tsx new file mode 100644 index 0000000..62eeda2 --- /dev/null +++ b/src/components/Login.tsx @@ -0,0 +1,67 @@ +import { useState } from 'react'; +import { useAuth } from '../contexts/AuthContext'; +import { useThemeContext } from '../contexts/ThemeContext'; + +export function Login({ onSwitchToSignUp }: { onSwitchToSignUp: () => void }) { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); + const { signIn } = useAuth(); + const { theme } = useThemeContext(); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + try { + setError(''); + await signIn(email, password); + } catch (error) { + setError('Failed to sign in'); + } + }; + + return ( +
+
+

Log In

+ {error &&
{error}
} +
+
+ setEmail(e.target.value)} + placeholder="Email" + className={`w-full px-4 py-2 rounded-lg ${theme.input}`} + required + /> +
+
+ setPassword(e.target.value)} + placeholder="Password" + className={`w-full px-4 py-2 rounded-lg ${theme.input}`} + required + /> +
+ +
+

+ Don't have an account?{' '} + +

+
+
+ ); +} \ No newline at end of file diff --git a/src/components/SettingsView.tsx b/src/components/SettingsView.tsx new file mode 100644 index 0000000..328e3b2 --- /dev/null +++ b/src/components/SettingsView.tsx @@ -0,0 +1,120 @@ +import React from 'react'; +import { Sun, Moon } from 'lucide-react'; +import { useThemeContext } from '../contexts/ThemeContext'; + +export function SettingsView() { + const { + theme, + isDark, + showStreaks, + dailyReminder, + defaultView, + habitSort, + toggleDarkMode, + toggleStreaks, + toggleDailyReminder, + setDefaultView, + setHabitSort + } = useThemeContext(); + + const handleReminderToggle = () => { + if (!dailyReminder && Notification.permission === 'default') { + Notification.requestPermission().then(permission => { + if (permission === 'granted') { + toggleDailyReminder(); + } + }); + } else { + toggleDailyReminder(); + } + }; + + return ( +
+

Settings

+ + {/* Theme Toggle */} +
+

Theme

+ +
+ + {/* Streaks Toggle */} +
+

Streaks

+