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

Ho list page #36

Merged
merged 11 commits into from
Mar 31, 2024
68 changes: 38 additions & 30 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { useAuth } from './api';
import { useShoppingListData, useShoppingLists } from './api';

import { useStateWithStorage } from './utils';
import ProtectedRoutes from './utils/protectedRoutes';
import AuthProvider from './AuthContext';

export function App() {
/**
Expand Down Expand Up @@ -43,37 +45,43 @@ export function App() {
const data = useShoppingListData(listPath);

return (
<Router>
<Routes>
<Route path="/" element={<Layout />}>
<Route
index
element={
<Home
data={lists}
setListPath={setListPath}
userId={userId}
userEmail={userEmail}
<AuthProvider>
<Router>
<Routes>
<Route path="/" element={<Layout />}>
<Route
index
element={
<Home
data={lists}
setListPath={setListPath}
userId={userId}
userEmail={userEmail}
/>
}
// Pass userId and userEmail as props
/>
<Route element={<ProtectedRoutes />}>
<Route
path="/list"
element={<List data={data} listPath={listPath} />}
/>
}
// Pass userId and userEmail as props
/>
<Route
path="/list"
element={<List data={data} listPath={listPath} />}
/>
<Route
path="/manage-list"
element={
<ManageList
listPath={listPath}
userId={userId}
existingItems={data}
</Route>
<Route element={<ProtectedRoutes />}>
<Route
path="/manage-list"
element={
<ManageList
listPath={listPath}
userId={userId}
existingItems={data}
/>
}
/>
}
/>
</Route>
</Routes>
</Router>
</Route>
</Route>
</Routes>
</Router>
</AuthProvider>
);
}
32 changes: 32 additions & 0 deletions src/AuthContext.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { createContext, useState, useEffect } from 'react';
import { onAuthStateChanged } from 'firebase/auth';
import { auth } from './api/config';

export const AuthContext = createContext();

export default function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState(null);
const [pending, setPending] = useState(true);

useEffect(() => {
onAuthStateChanged(auth, (user) => {
setCurrentUser(user);
setPending(false);
});
}, []);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice use of React Context 💫. We already have this code, for fetching and detecting user changes, in the useAuth custom hook so this is a duplication. I would recommend to move the pending state/logic to the useAuth hook and use the hook here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you Reda for pointing that out. I took a closer look at the code and i figured that the using a context wasn't necessary so I removed the context as a whole. The route protection works just fine regardless. Please review

if (pending) {
return (
<div className="flex justify-center items-center h-[100vh]">
Loading.....Please wait
</div>
);
}

return (
<AuthContext.Provider value={{ currentUser }}>
{children}
</AuthContext.Provider>
);
}
13 changes: 11 additions & 2 deletions src/api/useAuth.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import { addUserToDatabase } from './firebase.js';
export const SignInButton = () => (
<button
type="button"
onClick={() => signInWithRedirect(auth, new GoogleAuthProvider())}
onClick={() => {
signInWithRedirect(auth, new GoogleAuthProvider());
}}
>
Sign In
</button>
Expand All @@ -21,7 +23,14 @@ export const SignInButton = () => (
* A button that signs the user out of the app using Firebase Auth.
*/
export const SignOutButton = () => (
<button type="button" onClick={() => auth.signOut()}>
<button
type="button"
onClick={() => {
auth.signOut();
localStorage.clear();
window.location.reload();
}}
>
Sign Out
</button>
);
Expand Down
41 changes: 29 additions & 12 deletions src/components/ListItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { updateItem, deleteItem } from '../api/firebase';
import { useState, useEffect } from 'react';
import { getDaysBetweenDates } from '../utils';
import { CgDanger } from 'react-icons/cg';
import Checkbox from '@mui/material/Checkbox';
import { FaTrashAlt } from 'react-icons/fa';
import { Button } from '@mui/material';

export function ListItem({ id, listPath, itemData, timeNow }) {
const [itemChecked, setItemChecked] = useState(false);
Expand All @@ -17,6 +20,7 @@ export function ListItem({ id, listPath, itemData, timeNow }) {
}
}
}, [id]);

const setItemPurchased = () => {
setItemChecked((prev) => !prev);
updateItem(listPath, id, itemData);
Expand Down Expand Up @@ -58,22 +62,22 @@ export function ListItem({ id, listPath, itemData, timeNow }) {
}
if (daysTillNextPurchase <= 7) {
return (
<span>
<span className="flex items-center gap-2">
--- <CgDanger className="soon" />
Soon
</span>
);
}
if (daysTillNextPurchase > 7 && daysTillNextPurchase <= 30) {
return (
<span>
<span className="flex items-center gap-2">
--- <CgDanger className="kind-of-soon" />
Kind of soon
</span>
);
} else {
return (
<span>
<span className="flex items-center gap-2">
--- <CgDanger className="not-soon" />
Not soon
</span>
Expand All @@ -82,18 +86,31 @@ export function ListItem({ id, listPath, itemData, timeNow }) {
};

return (
<li className="ListItem">
<label htmlFor={name}>
<input
name={name}
type="checkbox"
checked={itemChecked}
<li className="flex gap-10">
<label htmlFor={name} className="flex items-center gap-2">
<Checkbox
size="3"
name="name"
onChange={setItemPurchased}
checked={itemChecked}
/>
{name}{' '}
{getUrgencyIndicator(timeNow, dateNextPurchased, dateLastPurchased)}
<p className={`${itemChecked ? 'line-through text-white/50' : ''}`}>
{name}
</p>
{itemChecked ? (
<></>
) : (
getUrgencyIndicator(timeNow, dateNextPurchased, dateLastPurchased)
)}
</label>
<button onClick={handleDelete}>Delete item</button>
<Button
onClick={handleDelete}
className="flex gap-2 md:gap-3 items-center"
variant="contained"
>
<FaTrashAlt className="h-5 w-5 md:h-7 md:w-7" />
<span className="md:text-lg font-bold">Delete item</span>
</Button>
</li>
);
}
1 change: 1 addition & 0 deletions src/components/SingleList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import './SingleList.css';
export function SingleList({ name, path, setListPath }) {
function handleClick() {
setListPath(path);
localStorage.setItem('list', name);
}

return (
Expand Down
8 changes: 8 additions & 0 deletions src/utils/protectedRoutes.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React, { useContext } from 'react';
import { Navigate, Outlet } from 'react-router-dom';
import { AuthContext } from '../AuthContext';

export default function ProtectedRoutes() {
const { currentUser } = useContext(AuthContext);
return currentUser ? <Outlet /> : <Navigate to="/" />;
}
3 changes: 3 additions & 0 deletions src/views/Layout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export function Layout() {
)}
</header>
<main className="Layout-main">
<h1 className="text-center">
{localStorage.getItem('list') || 'No list selected'}
</h1>
<Outlet />
</main>
<nav className="Nav">
Expand Down
57 changes: 30 additions & 27 deletions src/views/List.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom';
import { getDaysBetweenDates } from '../utils';
import { comparePurchaseUrgency } from '../api/firebase';
import { ListItem } from '../components/ListItem';
import Button from '@mui/material/Button';

export function List({ data, listPath }) {
const [searchInput, setSearchInput] = useState('');
Expand Down Expand Up @@ -55,7 +56,7 @@ export function List({ data, listPath }) {
};

return (
<>
<div className="mt-10 flex justify-center">
{data.length < 1 ? (
<div className="welcome-prompt">
<h2>Welcome to Your List!</h2>
Expand All @@ -68,33 +69,35 @@ export function List({ data, listPath }) {
</button>
</div>
) : (
<>
<p>
Hello from the <code>/list</code> page!
</p>
<form>
<label htmlFor="search">Search: </label>
<input
type="search"
id="search"
value={searchInput}
onChange={(e) => setSearchInput(e.target.value)}
/>
<button type="button" onClick={clearSearchInput}>
X
</button>
<div>
<form className="flex flex-col md:flex-row items-center gap-5">
<div>
<label htmlFor="search">Search: </label>
<input
type="search"
id="search"
value={searchInput}
onChange={(e) => setSearchInput(e.target.value)}
className="p-3 focus:outline-none"
/>
</div>
<Button onClick={clearSearchInput} variant="contained">
<span className="text-lg font-bold">Clear</span>
</Button>
</form>
{items.map((item) => (
<ListItem
key={item.id}
id={item.id}
listPath={listPath}
itemData={item}
timeNow={timeNow}
/>
))}
</>
<div className="mt-20 flex flex-col gap-5">
{items.map((item) => (
<ListItem
key={item.id}
id={item.id}
listPath={listPath}
itemData={item}
timeNow={timeNow}
/>
))}
</div>
</div>
)}
</>
</div>
);
}
Loading