Skip to content

Commit

Permalink
Merge pull request #35 from the-collab-lab/rt-code-cleanup
Browse files Browse the repository at this point in the history
Rt code cleanup
  • Loading branch information
GrannyYetta authored Sep 25, 2024
2 parents 422f1b3 + 7bddd57 commit 98452a7
Show file tree
Hide file tree
Showing 8 changed files with 30 additions and 119 deletions.
1 change: 1 addition & 0 deletions noun-home-icon-3574480.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 1 addition & 21 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,17 @@ import { useAuth, useShoppingListData, useShoppingLists } from './api';
import { useStateWithStorage } from './utils';

export function App() {
/**
* This custom hook takes the path of a shopping list
* in our database and syncs it with localStorage for later use.
* Check ./utils/hooks.js for its implementation.
*
* We'll later use `setListPath` when we allow a user
* to create and switch between lists.
*/
const [listPath, setListPath] = useStateWithStorage(
'tcl-shopping-list-path',
null,
);

/**
* This custom hook holds info about the current signed in user.
* Check ./api/useAuth.jsx for its implementation.
*/
const { user } = useAuth();
const userId = user?.uid;
const userEmail = user?.email;

/**
* This custom hook takes a user ID and email and fetches
* the shopping lists that the user has access to.
* Check ./api/firestore.js for its implementation.
*/
const lists = useShoppingLists(userId, userEmail);
/**
* This custom hook takes our token and fetches the data for our list.
* Check ./api/firestore.js for its implementation.
*/

const data = useShoppingListData(listPath);

return (
Expand Down
70 changes: 15 additions & 55 deletions src/api/firebase.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,17 @@ import { calculateEstimate } from '@the-collab-lab/shopping-list-utils';
* @returns
*/
export function useShoppingLists(userId, userEmail) {
// Start with an empty array for our data.
const initialState = [];
const [data, setData] = useState(initialState);

useEffect(() => {
// If we don't have a userId or userEmail (the user isn't signed in),
// we can't get the user's lists.
if (!userId || !userEmail) return;

// When we get a userEmail, we use it to subscribe to real-time updates
const userDocRef = doc(db, 'users', userEmail);

onSnapshot(userDocRef, (docSnap) => {
if (docSnap.exists()) {
const listRefs = docSnap.data().sharedLists;
const newData = listRefs.map((listRef) => {
// We keep the list's id and path so we can use them later.
return { name: listRef.id, path: listRef.path };
});
setData(newData);
Expand All @@ -56,36 +50,25 @@ export function useShoppingLists(userId, userEmail) {
* @see https://firebase.google.com/docs/firestore/query-data/listen
*/
export function useShoppingListData(listPath) {
// Start with an empty array for our data.
/** @type {import('firebase/firestore').DocumentData[]} */
const initialState = [];
const [data, setData] = useState(initialState);

useEffect(() => {
if (!listPath) return;

// When we get a listPath, we use it to subscribe to real-time updates
// from Firestore.
return onSnapshot(collection(db, listPath, 'items'), (snapshot) => {
// The snapshot is a real-time update. We iterate over the documents in it
// to get the data.
const nextData = snapshot.docs.map((docSnapshot) => {
// Extract the document's data from the snapshot.
const item = docSnapshot.data();

// The document's id is not in the data,
// but it is very useful, so we add it to the data ourselves.
item.id = docSnapshot.id;

return item;
});

// Update our React state with the new data.
setData(nextData);
});
}, [listPath]);

// Return the data so it can be used by our React components.
return data;
}

Expand All @@ -94,16 +77,11 @@ export function useShoppingListData(listPath) {
* @param {Object} user The user object from Firebase Auth.
*/
export async function addUserToDatabase(user) {
// Check if the user already exists in the database.
const userDoc = await getDoc(doc(db, 'users', user.email));
// If the user already exists, we don't need to do anything.

if (userDoc.exists()) {
return;
} else {
// If the user doesn't exist, add them to the database.
// We'll use the user's email as the document id
// because it's more likely that the user will know their email
// than their uid.
await setDoc(doc(db, 'users', user.email), {
email: user.email,
name: user.displayName,
Expand Down Expand Up @@ -138,18 +116,16 @@ export async function createList(userId, userEmail, listName) {
* @param {string} recipientEmail The email of the user to share the list with.
*/
export async function shareList(listPath, currentUserId, recipientEmail) {
// Check if current user is owner.
if (!listPath.includes(currentUserId)) {
throw new Error('You do not have permission to share this list');
}
// Get the document for the recipient user.

const usersCollectionRef = collection(db, 'users');
const recipientDoc = await getDoc(doc(usersCollectionRef, recipientEmail));
// If the recipient user doesn't exist, we can't share the list.

if (!recipientDoc.exists()) {
throw new Error('The user with the provided email does not exist');
}
// Add the list to the recipient user's sharedLists array.
const listDocumentRef = doc(db, listPath);
const userDocumentRef = doc(db, 'users', recipientEmail);
await updateDoc(userDocumentRef, {
Expand All @@ -167,13 +143,10 @@ export async function shareList(listPath, currentUserId, recipientEmail) {
*/
export async function addItem(listPath, { itemName, daysUntilNextPurchase }) {
const listCollectionRef = await collection(db, listPath, 'items');
// TODO: Replace this call to console.log with the appropriate
// Firebase function, so this information is sent to your database!

try {
await addDoc(listCollectionRef, {
dateCreated: new Date(),
// NOTE: This is null because the item has just been created.
// We'll use updateItem to put a Date here when the item is purchased!
dateLastPurchased: null,
dateNextPurchased: getFutureDate(daysUntilNextPurchase),
name: itemName,
Expand All @@ -185,14 +158,7 @@ export async function addItem(listPath, { itemName, daysUntilNextPurchase }) {
}

export async function updateItem(listPath, itemId, { totalPurchases }) {
/**
* TODO: Fill this out so that it uses the correct Firestore function
* to update an existing item. You'll need to figure out what arguments
* this function must accept!
*/

try {
// Get a reference to the specific item document in Firestore
const itemRef = doc(db, listPath, 'items', itemId);
const docSnap = await getDoc(itemRef);
const data = docSnap.data();
Expand Down Expand Up @@ -221,11 +187,6 @@ export async function updateItem(listPath, itemId, { totalPurchases }) {
}

export async function deleteItem(listPath, itemId) {
/**
* TODO: Fill this out so that it uses the correct Firestore function
* to delete an existing item. You'll need to figure out what arguments
* this function must accept!
*/
try {
const itemRef = doc(db, listPath, 'items', itemId);
await deleteDoc(itemRef);
Expand All @@ -239,12 +200,17 @@ export async function deleteItem(listPath, itemId) {
* Sorts the items in the list according to category using urgencyIndex and inactiveIndex to determine what category an item belongs.
* @param {string} data The path to the data object to sort.
*/

export async function comparePurchaseUrgency(data) {
const now = new Date();
const inactivityPeriod = 60;
const dayOfPurchase = 0;
const soon = 7;
const kindOfSoon = 30;

data.map((item) => {
const urgencyIndex = Math.ceil(
getDaysBetweenDates(now, item.dateNextPurchased.toDate()), //takes the difference between the current date and the date the item is next purchased
getDaysBetweenDates(now, item.dateNextPurchased.toDate()),
);

const lastPurchase = item.dateLastPurchased
Expand All @@ -255,26 +221,20 @@ export async function comparePurchaseUrgency(data) {
item.inactiveIndex = inactiveItem;
item.urgencyIndex = urgencyIndex;

if (inactiveItem > 60) {
if (inactiveItem > inactivityPeriod) {
item.category = 'inactive';
} else if (urgencyIndex < 0) {
} else if (urgencyIndex < dayOfPurchase) {
item.category = 'Overdue';
} else if (urgencyIndex <= 7) {
} else if (urgencyIndex <= soon) {
item.category = 'soon';
} else if (urgencyIndex < 30) {
} else if (urgencyIndex < kindOfSoon) {
item.category = 'kind of soon';
} else {
item.category = 'Not soon';
}
return item;
});
/**
* Function to implement custom sort based on inacrtivity, then on urgencyIndex and name
* 1- if urgency of a is inactive and b is not inactive, sort b ontop the top of a
* 2- if urgency of a is not inactive and b is inactive, sort a ontop of b
* 3- if urgency is the same, sort based on UrgencyIndex
* 4- if urgencyIndex is the same, sort based on name
*/

data.sort((a, b) => {
if (a.category === 'inactive' && b.category !== 'inactive') {
return 1;
Expand Down
8 changes: 0 additions & 8 deletions src/api/useAuth.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@ import { auth } from './config.js';
import { GoogleAuthProvider, signInWithPopup } from 'firebase/auth';
import { addUserToDatabase } from './firebase.js';

/**
* A button that signs the user in using Google OAuth. When clicked,
* the button redirects the user to the Google OAuth sign-in page.
* After the user signs in, they are redirected back to the app.
*/
export const SignInButton = () => (
<button
type="button"
Expand All @@ -17,9 +12,6 @@ export const SignInButton = () => (
</button>
);

/**
* A button that signs the user out of the app using Firebase Auth.
*/
export const SignOutButton = () => (
<button type="button" onClick={() => auth.signOut()}>
Sign Out
Expand Down
5 changes: 2 additions & 3 deletions src/components/ListItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ import './ListItem.css';
export function ListItem({ name, dateLastPurchased, onCheck, onDelete }) {
const [isChecked, setIsChecked] = useState(false);

// Update `isChecked` based on the `dateLastPurchased` value
useEffect(() => {
const checkStatus = () => {
if (dateLastPurchased) {
const purchaseDate = dateLastPurchased.toDate();
const timeSinceLastPurchase = new Date() - purchaseDate;
const hasBeenPurchasedRecently =
timeSinceLastPurchase < 24 * 60 * 60 * 1000; // 24 hours
timeSinceLastPurchase < 24 * 60 * 60 * 1000;
setIsChecked(hasBeenPurchasedRecently);
} else {
setIsChecked(false);
Expand All @@ -26,7 +25,7 @@ export function ListItem({ name, dateLastPurchased, onCheck, onDelete }) {
onDelete();
}
};

return (
<li className="ListItem">
<label>
Expand Down
8 changes: 0 additions & 8 deletions src/views/Layout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,6 @@ import { Outlet, NavLink, Link } from 'react-router-dom';
import './Layout.css';
import { SignInButton, SignOutButton, useAuth } from '../api/useAuth';

/**
* TODO: The links defined in this file don't work!
*
* Instead of anchor element, they should use a component
* from `react-router-dom` to navigate to the routes
* defined in `App.jsx`.
*/

export function Layout() {
const { user } = useAuth();
return (
Expand Down
26 changes: 9 additions & 17 deletions src/views/List.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,12 @@ export function List({ data, listPath, lists }) {
const [searchItem, setSearchItem] = useState('');
const [errorMsg, setErrorMsg] = useState('');

const [items, setItems] = useState([]); //to store the sorted items for display

//Code segment to sort items using the compareUrgency function from firebase.js
const [items, setItems] = useState([]);
useEffect(() => {
const fetchItems = async () => {
try {
const sortedItems = await comparePurchaseUrgency(data);
setItems(sortedItems);
} catch (error) {
console.log(error);
}
const sortedItems = await comparePurchaseUrgency(data);
setItems(sortedItems);
};

fetchItems();
}, [data]);

Expand Down Expand Up @@ -99,6 +92,7 @@ export function List({ data, listPath, lists }) {
id="search-item-in-list"
value={searchItem}
placeholder="Search item..."
aria-label="Search for items"
/>
{searchItem && (
<button type="button" onClick={clearSearch}>
Expand All @@ -107,7 +101,7 @@ export function List({ data, listPath, lists }) {
)}
</div>
</form>
{searchItem && (
{searchItem ? (
<ul>
{filterItems.map((item) => (
<ListItem
Expand All @@ -120,11 +114,7 @@ export function List({ data, listPath, lists }) {
/>
))}
</ul>
)}

{errorMsg && <p>{errorMsg}</p>}

{
) : (
<ul>
{Object.keys(groupedItems).map((category) => (
<li key={category}>
Expand All @@ -145,7 +135,9 @@ export function List({ data, listPath, lists }) {
</li>
))}
</ul>
}
)}

{errorMsg && <p>{errorMsg}</p>}
</>
)}
</>
Expand Down
9 changes: 2 additions & 7 deletions src/views/ManageList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export function ManageList({ listPath, userId, data }) {
nextPurchase: 0,
});
const [messageItem, setMessageItem] = useState('');

const [formAddUser, setFormAddUser] = useState('');
const [messageUser, setMessageUser] = useState('');

Expand All @@ -31,25 +30,21 @@ export function ManageList({ listPath, userId, data }) {
return;
}
try {
//Function to normalize the item name and convert to lowercase goes here
const normalizedName = (name) => {
return name
.toLowerCase()
.replace(/[^\w\s]|_/g, '') // for punctuation
.replace(/\s+/g, ''); // for spaces
.replace(/[^\w\s]|_/g, '')
.replace(/\s+/g, '');
};

// check if the item already exists
const itemExists = data.some(
(item) => normalizedName(item.name) === normalizedName(name),
);

// if the item already exists, show an error message
if (itemExists) {
setMessageItem(`${normalizedName(name)} is already in the list`);
return;
}
// if the item does not exist, add it to the list
await addItem(listPath, {
itemName: name,
daysUntilNextPurchase: nextPurchase,
Expand Down

0 comments on commit 98452a7

Please sign in to comment.