diff --git a/src/api/firebase.js b/src/api/firebase.js index 394860c..7fd8e7f 100644 --- a/src/api/firebase.js +++ b/src/api/firebase.js @@ -11,7 +11,7 @@ import { } from 'firebase/firestore'; import { useEffect, useState } from 'react'; import { db } from './config'; -import { getFutureDate, getDaysBetweenDates } from '../utils'; +import { getFutureDate, calculateDaysDifferenceFromNow } from '../utils'; import { calculateEstimate } from '@the-collab-lab/shopping-list-utils'; /** * A custom hook that subscribes to the user's shopping lists in our Firestore @@ -210,9 +210,9 @@ export async function updateItem( let daysSinceLastPurchase; if (dateLastPurchased) { - daysSinceLastPurchase = getDaysBetweenDates(dateLastPurchased); + daysSinceLastPurchase = calculateDaysDifferenceFromNow(dateLastPurchased); } else { - daysSinceLastPurchase = getDaysBetweenDates(dateCreated); + daysSinceLastPurchase = calculateDaysDifferenceFromNow(dateCreated); } // Calculate days until next purchase diff --git a/src/components/ListItem.css b/src/components/ListItem.css index 492e345..6a4200d 100644 --- a/src/components/ListItem.css +++ b/src/components/ListItem.css @@ -1,22 +1,33 @@ -.ListItem { - display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; - padding: 8px; - border-bottom: 1px solid var(--color-border); - font-size: 1.2em; +td { + border-bottom: 1px solid whitesmoke; } -.ListItem-checkbox { - accent-color: var(--color-accent); +th { + background-color: #3e27ed; + border-bottom: 2px solid #ddd; } -.ListItem-label { - margin-left: 0.2em; +.duesoon { + color: orange; + font-weight: bold; } -.item-name { - flex-grow: 1; - margin-right: 10px; +.duekindofsoon { + color: yellow; + font-weight: bold; +} + +.notduesoon { + color: green; + font-weight: bold; +} + +.nolongeractive { + color: gray; + font-weight: bold; +} + +.overdue { + color: red; + font-weight: bold; } diff --git a/src/components/ListItem.jsx b/src/components/ListItem.jsx index d9e7e23..4327f20 100644 --- a/src/components/ListItem.jsx +++ b/src/components/ListItem.jsx @@ -12,6 +12,7 @@ export function ListItem({ dateLastPurchased, purchaseInterval, dateCreated, + sortCriteria, setMessage, }) { const [purchased, setPurchased] = useToggle(false); @@ -60,10 +61,11 @@ export function ListItem({ } } }; - + + // handleDelete Function const handleDelete = async () => { - const deleteConfirm = window.confirm( + const deleteConfirm = window.confirm( `Are you sure you want to delete ${name}?`, ); @@ -77,23 +79,32 @@ export function ListItem({ } }; + const urgencyClass = sortCriteria.tag.toLowerCase().replace(/\s/g, ''); + return ( <> -
  • -
    {name}
    - - - {dateLastPurchased ? dateLastPurchased.toDate().toLocaleString() : ''} - -
  • + + + + + + + {dateLastPurchased ? dateLastPurchased.toDate().toLocaleString() : ''} + + {sortCriteria.tag} + + ); } diff --git a/src/utils/dates.js b/src/utils/dates.js index 513bdac..020da79 100644 --- a/src/utils/dates.js +++ b/src/utils/dates.js @@ -10,9 +10,71 @@ const ONE_DAY_IN_MILLISECONDS = 86400000; export function getFutureDate(offset) { return new Date(Date.now() + offset * ONE_DAY_IN_MILLISECONDS); } -export function getDaysBetweenDates(previousPurchaseDate) { - const pastDate = previousPurchaseDate.toDate(); +export function calculateDaysDifferenceFromNow(dateToCompare) { + const comparisonDate = dateToCompare.toDate(); const presentDate = new Date(); - const diffInMilliseconds = presentDate.getTime() - pastDate.getTime(); + const diffInMilliseconds = presentDate.getTime() - comparisonDate.getTime(); return Math.round(diffInMilliseconds / ONE_DAY_IN_MILLISECONDS); } + +export function comparePurchaseUrgency(list) { + const inactive = []; + const overdue = []; + const future = []; + //iterate through the list and categorize each item + list.forEach((item) => { + //positive numbers represent the past, negative numbers represent the future + const days = calculateDaysDifferenceFromNow(item.dateNextPurchased); + if (days >= 60) { + /* + -If sixty or more days have passed since the last purchase, we consider the item inactive. + -We flip the days to negative* to represent inactivity because inactive items should be sorted in reverse order from overdue and future items. + -For instance, an item that is 62 days overdue will be placed below an item that is 61 days overdue. It is less relevant to the user because more time has elapsed since they engaged with it. + */ + item.sortCriteria = { + tag: 'No longer active', + daysUntilNextPurchase: -days, // * flip the days to negative + }; + inactive.push(item); + } else if (days < 60 && days > 0) { + item.sortCriteria = { tag: 'Overdue', daysUntilNextPurchase: days }; + overdue.push(item); + } else if (days <= 0 && days >= -7) { + item.sortCriteria = { tag: 'Due soon', daysUntilNextPurchase: days }; + future.push(item); + } else if (days < -7 && days >= -30) { + item.sortCriteria = { + tag: 'Due kind of soon', + daysUntilNextPurchase: days, + }; + future.push(item); + } else if (days < -30) { + item.sortCriteria = { tag: 'Not due soon', daysUntilNextPurchase: days }; + future.push(item); + } + }); + //function to sort lists by days until next purchase and alphabetically if days are equal + const sortList = (list) => { + const sortedList = [...list].sort((a, b) => { + if ( + a.sortCriteria.daysUntilNextPurchase === + b.sortCriteria.daysUntilNextPurchase + ) { + //sorts alphabetically if days are the same + return a.name.localeCompare(b.name); + } + return ( + //sort by days until next purchase + b.sortCriteria.daysUntilNextPurchase - + a.sortCriteria.daysUntilNextPurchase + ); + }); + return sortedList; + }; + + const sortedOverdue = sortList(overdue); + const sortedFuture = sortList(future); + const sortedInactive = sortList(inactive); + + return sortedOverdue.concat(sortedFuture).concat(sortedInactive); +} diff --git a/src/views/List.jsx b/src/views/List.jsx index 04e9d1b..82074e2 100644 --- a/src/views/List.jsx +++ b/src/views/List.jsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react'; import { ListItem } from '../components'; import { NavLink } from 'react-router-dom'; +import { comparePurchaseUrgency } from '../utils/dates.js'; export function List({ data, listPath }) { const [searchInput, setSearchInput] = useState(''); @@ -15,7 +16,9 @@ export function List({ data, listPath }) { setSearchInput(''); }; - const filterList = data.filter((item) => { + const sortedByUrgency = comparePurchaseUrgency(data); + + const filterList = sortedByUrgency.filter((item) => { return searchInput ? item.name.toLowerCase().includes(searchInput.toLowerCase()) : item; @@ -60,10 +63,18 @@ export function List({ data, listPath }) { )} - + ); }