Skip to content

Commit

Permalink
Merge pull request #30 from the-collab-lab/dtp-nr-sorting
Browse files Browse the repository at this point in the history
Issue #13: Sort Items by Purchase Urgency
  • Loading branch information
dterceroparker authored Sep 21, 2024
2 parents a0a270f + 8279ed4 commit 55e8bc8
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 51 deletions.
6 changes: 3 additions & 3 deletions src/api/firebase.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
41 changes: 26 additions & 15 deletions src/components/ListItem.css
Original file line number Diff line number Diff line change
@@ -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;
}
41 changes: 26 additions & 15 deletions src/components/ListItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export function ListItem({
dateLastPurchased,
purchaseInterval,
dateCreated,
sortCriteria,
setMessage,
}) {
const [purchased, setPurchased] = useToggle(false);
Expand Down Expand Up @@ -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}?`,
);

Expand All @@ -77,23 +79,32 @@ export function ListItem({
}
};

const urgencyClass = sortCriteria.tag.toLowerCase().replace(/\s/g, '');

return (
<>
<li className="ListItem">
<div className="item-name">{name}</div>
<Toggle
toggle={handleToggle}
on={purchased}
name={name}
isDisabled={isDisabled}
dateLastPurchased={dateLastPurchased}
/>

{dateLastPurchased ? dateLastPurchased.toDate().toLocaleString() : ''}
<button onClick={handleDelete} aria-label={`Delete ${name}`}>
<tr className="ListItem">
<td>{name}
<button onClick={handleDelete} aria-label={`Delete ${name}`}>
Delete
</button>
</li>
</td>

<td>
<Toggle
toggle={handleToggle}
on={purchased}
name={name}
isDisabled={isDisabled}
dateLastPurchased={dateLastPurchased}
/>
</td>
<td>
{dateLastPurchased ? dateLastPurchased.toDate().toLocaleString() : ''}
</td>
<td className={urgencyClass}>{sortCriteria.tag}</td>
</tr>

</>
);
}
68 changes: 65 additions & 3 deletions src/utils/dates.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
40 changes: 25 additions & 15 deletions src/views/List.jsx
Original file line number Diff line number Diff line change
@@ -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('');
Expand All @@ -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;
Expand Down Expand Up @@ -60,10 +63,18 @@ export function List({ data, listPath }) {
</button>
)}
</div>
<ul>
{filterList.length ? (
filterList.map((item) => {
return (
{filterList.length ? (
<table>
<thead>
<tr>
<th scope="col">Product</th>
<th scope="col">Buy Now</th>
<th scope="col">Last Purchase Date</th>
<th scope="col">Urgency</th>
</tr>
</thead>
<tbody>
{filterList.map((item) => (
<ListItem
key={item.id}
name={item.name}
Expand All @@ -73,19 +84,18 @@ export function List({ data, listPath }) {
dateLastPurchased={item.dateLastPurchased}
purchaseInterval={item.purchaseInterval}
dateCreated={item.dateCreated}
setMessage={setMessage}
setMessage={setMessage}
sortCriteria={item.sortCriteria}
/>
);
})
) : (
<li>
{' '}
No items found! <NavLink to="/manage-list"> Add item</NavLink>
</li>
)}
))}
</tbody>
</table>
) : (
<p>No items to display</p>
)}
<br />
<span>{message}</span>
</ul>

</>
);
}

0 comments on commit 55e8bc8

Please sign in to comment.