Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fetching data from firestore. ProtectedRoute, LoadingCircle. #10

Merged
merged 4 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,32 @@ import Streak from '@pages/Streak';

import Footer from '@components/common/Footer';
import Header from '@components/common/Header';
import { UserProvider } from '@contexts/UserContext';
import LoadingCircle from '@components/common/LoadingCircle';
import { UserProvider, useUser } from '@contexts/UserContext';
import { Typography } from '@mui/material';
import './App.css';

const ProtectedRoute = ({ element }) => {
const { user, loading } = useUser();
if (loading) {
return <LoadingCircle />;
}

return user ? (
element
) : (
<Typography
variant="h6"
sx={{
textAlign: 'center',
marginTop: '2rem',
}}
>
Please sign in to view this page
</Typography>
);
};

const App = () => {
return (
<ThemeProvider theme={theme}>
Expand All @@ -21,8 +44,8 @@ const App = () => {
<div className="content">
{/* Main content area where pages will render */}
<Routes>
<Route path="/" element={<Home />} />
<Route path="/streak" element={<Streak />} />
<Route path="/" element={<ProtectedRoute element={<Home />} />} />
<Route path="/streak" element={<ProtectedRoute element={<Streak />} />} />
</Routes>
</div>

Expand Down
144 changes: 54 additions & 90 deletions src/components/Home/GoalTracker.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useUser } from '@contexts/UserContext';
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import {
Expand Down Expand Up @@ -39,62 +40,70 @@ const ProgressIndicator = ({ value, size = 40, thickness = 4 }) => (
const Task = ({ task, onToggle }) => (
<ListItem dense>
<Checkbox edge="start" checked={task.completed} onChange={onToggle} />
<ListItemText primary={task.title} />
<ListItemText primary={task.name} />
</ListItem>
);

const MicroGoal = ({ microGoal, onToggleTask, onToggleExpand }) => {
const MicroGoal = ({ microGoal, macroGoalIndex, microGoalIndex, onToggleTask, onToggleExpand }) => {
const progress =
(microGoal.tasks.filter((t) => t.completed).length / microGoal.tasks.length) * 100;

return (
<Paper elevation={2} sx={{ p: 2, mb: 2, bgcolor: 'background.default' }}>
<Box display="flex" alignItems="center">
<ProgressIndicator value={progress} size={32} thickness={3} />
<Typography variant="subtitle1">{microGoal.title}</Typography>
<IconButton onClick={() => onToggleExpand(microGoal.id)} size="small" sx={{ ml: 'auto' }}>
<Typography variant="subtitle1">{microGoal.name}</Typography>
<IconButton
onClick={() => onToggleExpand(macroGoalIndex, microGoalIndex)}
size="small"
sx={{ ml: 'auto' }}
>
{microGoal.expanded ? <ExpandLessIcon /> : <ExpandMoreIcon />}
</IconButton>
</Box>
{microGoal.expanded && (
<List sx={{ mt: 1, pl: 4 }}>
{microGoal.tasks.map((task) => (
<Task key={task.id} task={task} onToggle={() => onToggleTask(microGoal.id, task.id)} />
{microGoal.tasks.map((task, taskIndex) => (
<Task
key={`${macroGoalIndex}-${microGoalIndex}-${taskIndex}`} // Unique key for tasks
task={task}
onToggle={() => onToggleTask(macroGoalIndex, microGoalIndex, taskIndex)}
/>
))}
</List>
)}
</Paper>
);
};

const MacroGoal = ({ macroGoal, onToggleTask, onToggleExpand }) => {
const MacroGoal = ({ macroGoal, macroGoalIndex, onToggleTask, onToggleExpand }) => {
const progress =
(macroGoal.microGoals.reduce((acc, mg) => acc + mg.tasks.filter((t) => t.completed).length, 0) /
macroGoal.microGoals.reduce((acc, mg) => acc + mg.tasks.length, 0)) *
(macroGoal.microgoals.reduce((acc, mg) => acc + mg.tasks.filter((t) => t.completed).length, 0) /
macroGoal.microgoals.reduce((acc, mg) => acc + mg.tasks.length, 0)) *
100;

return (
<Paper elevation={3} sx={{ p: 3, mb: 4, bgcolor: 'background.paper' }}>
<Box display="flex" alignItems="center" mb={2}>
<ProgressIndicator value={progress} size={48} thickness={4} />
<Typography variant="h5" sx={{ fontWeight: 'bold' }}>
{macroGoal.title}
{macroGoal.name}
</Typography>
<IconButton onClick={() => onToggleExpand(macroGoal.id)} size="small" sx={{ ml: 'auto' }}>
<IconButton onClick={() => onToggleExpand(macroGoalIndex)} size="small" sx={{ ml: 'auto' }}>
{macroGoal.expanded ? <ExpandLessIcon /> : <ExpandMoreIcon />}
</IconButton>
</Box>
<Divider sx={{ mb: 2 }} />
{macroGoal.expanded && (
<List sx={{ pl: 2 }}>
{macroGoal.microGoals.map((microGoal) => (
{macroGoal.microgoals.map((microGoal, microGoalIndex) => (
<MicroGoal
key={microGoal.id}
key={`${macroGoalIndex}-${microGoalIndex}`} // Unique key for microgoals
microGoal={microGoal}
onToggleTask={(microGoalId, taskId) =>
onToggleTask(macroGoal.id, microGoalId, taskId)
}
onToggleExpand={(microGoalId) => onToggleExpand(macroGoal.id, microGoalId)}
macroGoalIndex={macroGoalIndex}
microGoalIndex={microGoalIndex} // Pass the micro goal index
onToggleTask={onToggleTask}
onToggleExpand={onToggleExpand}
/>
))}
</List>
Expand All @@ -104,71 +113,21 @@ const MacroGoal = ({ macroGoal, onToggleTask, onToggleExpand }) => {
};

export default function GoalTracker() {
const [goals, setGoals] = useState([
{
id: '1',
title: 'Improve Coding Skills',
expanded: true,
microGoals: [
{
id: '1-1',
title: 'Learn React',
expanded: true,
tasks: [
{ id: '1-1-1', title: 'Complete React tutorial', completed: true },
{ id: '1-1-2', title: 'Build a small project', completed: false },
],
},
{
id: '1-2',
title: 'Master JavaScript',
expanded: false,
tasks: [
{ id: '1-2-1', title: 'Study ES6+ features', completed: false },
{ id: '1-2-2', title: 'Practice coding challenges', completed: false },
],
},
],
},
{
id: '2',
title: 'Get Fit',
expanded: false,
microGoals: [
{
id: '2-1',
title: 'Cardio',
expanded: false,
tasks: [
{ id: '2-1-1', title: 'Run 5k', completed: false },
{ id: '2-1-2', title: 'Swim 1k', completed: false },
],
},
{
id: '2-2',
title: 'Strength Training',
expanded: false,
tasks: [
{ id: '2-2-1', title: 'Bench press 100lbs', completed: false },
{ id: '2-2-2', title: 'Squat 150lbs', completed: false },
],
},
],
},
]);
const { user, loading } = useUser();
const [goals, setGoals] = useState(user.goals || []);

const handleToggleTask = (macroGoalId, microGoalId, taskId) => {
const handleToggleTask = (macroGoalIndex, microGoalIndex, taskIndex) => {
setGoals((prevGoals) =>
prevGoals.map((macroGoal) =>
macroGoal.id === macroGoalId
prevGoals.map((macroGoal, mgi) =>
mgi === macroGoalIndex
? {
...macroGoal,
microGoals: macroGoal.microGoals.map((microGoal) =>
microGoal.id === microGoalId
microgoals: macroGoal.microgoals.map((microGoal, mgi) =>
mgi === microGoalIndex
? {
...microGoal,
tasks: microGoal.tasks.map((task) =>
task.id === taskId ? { ...task, completed: !task.completed } : task,
tasks: microGoal.tasks.map((task, ti) =>
ti === taskIndex ? { ...task, completed: !task.completed } : task,
),
}
: microGoal,
Expand All @@ -179,15 +138,15 @@ export default function GoalTracker() {
);
};

const handleToggleExpand = (macroGoalId, microGoalId) => {
const handleToggleExpand = (macroGoalIndex, microGoalIndex) => {
setGoals((prevGoals) =>
prevGoals.map((macroGoal) =>
macroGoal.id === macroGoalId
? microGoalId
prevGoals.map((macroGoal, mgi) =>
mgi === macroGoalIndex
? microGoalIndex !== undefined
? {
...macroGoal,
microGoals: macroGoal.microGoals.map((microGoal) =>
microGoal.id === microGoalId
microgoals: macroGoal.microgoals.map((microGoal, mgi) =>
mgi === microGoalIndex
? { ...microGoal, expanded: !microGoal.expanded }
: microGoal,
),
Expand All @@ -200,14 +159,19 @@ export default function GoalTracker() {

return (
<Box sx={{ maxWidth: 800, margin: 'auto', pt: 4 }}>
{goals.map((macroGoal) => (
<MacroGoal
key={macroGoal.id}
macroGoal={macroGoal}
onToggleTask={handleToggleTask}
onToggleExpand={handleToggleExpand}
/>
))}
{loading ? (
<CircularProgress />
) : (
goals.map((macroGoal, macroGoalIndex) => (
<MacroGoal
key={macroGoalIndex} // Use the index as the key
macroGoal={macroGoal}
macroGoalIndex={macroGoalIndex} // Pass the index
onToggleTask={handleToggleTask}
onToggleExpand={handleToggleExpand}
/>
))
)}
</Box>
);
}
11 changes: 11 additions & 0 deletions src/components/common/LoadingCircle.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Box, CircularProgress } from '@mui/material';

const LoadingCircle = () => {
return (
<Box display="flex" justifyContent="center" alignItems="center" minHeight="100vh">
<CircularProgress />
</Box>
);
};

export default LoadingCircle;
10 changes: 2 additions & 8 deletions src/contexts/UserContext.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box, CircularProgress } from '@mui/material';
import LoadingCircle from '@components/common/LoadingCircle';
import { signInWithGoogle } from '@utils/firebase/authUtils';
import { fetchUserProfile, updateUserProfile } from '@utils/firebase/createUserProfile';
import { auth } from '@utils/firebaseConfig';
Expand Down Expand Up @@ -75,13 +75,7 @@ export const UserProvider = ({ children }) => {
updateProfile,
}}
>
{!loading ? (
children
) : (
<Box display="flex" justifyContent="center" alignItems="center" minHeight="100vh">
<CircularProgress />
</Box>
)}
{!loading ? children : <LoadingCircle />}
</UserContext.Provider>
);
};
4 changes: 2 additions & 2 deletions src/hooks/useGoalsUpdater.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const useGoalsManager = () => {
try {
const newGoal = {
name: goalName,
progress: 0,
expanded: false,
microgoals: [],
};

Expand All @@ -38,7 +38,7 @@ const useGoalsManager = () => {
// Define the new microgoal structure
const newMicrogoal = {
name: microgoalName,
progress: 0,
expanded: false,
tasks: [],
};

Expand Down
6 changes: 1 addition & 5 deletions src/pages/Home.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,7 @@ const Home = () => {

return (
<Box sx={{ width: '90%', margin: 'auto' }}>
<GoalTracker
macroGoals={goals}
onToggleTask={handleToggleTask}
onToggleExpand={handleToggleExpand}
/>
<GoalTracker />
</Box>
);
};
Expand Down
Loading