From 86e6712c6f05db3f763f27be96ecd9d90b6ec82a Mon Sep 17 00:00:00 2001 From: wyna Date: Tue, 10 Sep 2024 16:04:22 +0200 Subject: [PATCH 1/4] initial logic to get date interval --- package-lock.json | 11 +++++++++++ package.json | 1 + src/api/firebase.js | 23 ++++++++++++++++++++++- src/components/ListItem.jsx | 13 ++++++++++--- src/views/List.jsx | 1 + 5 files changed, 45 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1ee7cf2..21b162c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,6 +6,7 @@ "": { "name": "smart-shopping-list-next", "dependencies": { + "@the-collab-lab/shopping-list-utils": "^2.2.0", "firebase": "^10.12.5", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -3888,6 +3889,16 @@ "@testing-library/dom": ">=7.21.4" } }, + "node_modules/@the-collab-lab/shopping-list-utils": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@the-collab-lab/shopping-list-utils/-/shopping-list-utils-2.2.0.tgz", + "integrity": "sha512-nEN1z/SEOIWO+8JWIgPDNUkrXmXqNomSh5aiZDNdiaUH/JYqyNeXmEOldtMqAfLicSRiFkapcAIlrUUnPzNaog==", + "license": "MIT", + "peerDependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", diff --git a/package.json b/package.json index d5128ae..43afd21 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "npm": ">=8.19.0" }, "dependencies": { + "@the-collab-lab/shopping-list-utils": "^2.2.0", "firebase": "^10.12.5", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/src/api/firebase.js b/src/api/firebase.js index 1cc9c22..1eba0ff 100644 --- a/src/api/firebase.js +++ b/src/api/firebase.js @@ -12,6 +12,8 @@ import { useEffect, useState } from 'react'; import { db } from './config'; import { getFutureDate } from '../utils'; import toast from 'react-hot-toast'; +import { calculateEstimate } from '@the-collab-lab/shopping-list-utils'; +import { ONE_DAY_IN_MILLISECONDS } from '../utils/dates'; /** * A custom hook that subscribes to the user's shopping lists in our Firestore * database and returns new data whenever the lists change. @@ -181,13 +183,14 @@ export async function addItem(listPath, { itemName, daysUntilNextPurchase }) { // We'll use updateItem to put a Date here when the item is purchased! dateLastPurchased: null, dateNextPurchased: getFutureDate(daysUntilNextPurchase), + dayInterval: daysUntilNextPurchase, name: itemName, totalPurchases: 0, checked: false, }); } -export async function updateItem(listPath, id, checked) { +export async function updateItem(listPath, id, checked, dayInterval) { const listCollectionRef = collection(db, listPath, 'items'); const itemRef = doc(listCollectionRef, id); @@ -195,12 +198,30 @@ export async function updateItem(listPath, id, checked) { const itemDoc = await getDoc(itemRef); const data = itemDoc.data(); const currentTotalPurchases = data.totalPurchases; + const currentDayInterval = data.dayInterval; + const dateLastPurchasedInMillis = data.dateLastPurchased + ? data.dateLastPurchased.toMillis() + : data.dateCreated.toMillis(); + const daysSinceLastPurchase = Date.now() - dateLastPurchasedInMillis; + console.log('DaysSinceLastPurchase ', daysSinceLastPurchase); + + //conditional assignment, if x < 1 -> 1, else drop decimals return whole number of days + console.log( + 'DaysSinceLastPurchase sb ms', + daysSinceLastPurchase / ONE_DAY_IN_MILLISECONDS, + ); if (checked) { await updateDoc(itemRef, { dateLastPurchased: new Date(), totalPurchases: currentTotalPurchases + 1, checked: checked, + dayInterval: daysSinceLastPurchase, + dateNextPurchased: calculateEstimate( + currentDayInterval, + daysSinceLastPurchase, + currentTotalPurchases, + ), //format Timestamps -> September 4, 2024 at 11:11:11 AM UTC+2 }); } else { await updateDoc(itemRef, { diff --git a/src/components/ListItem.jsx b/src/components/ListItem.jsx index 1b7334e..4094928 100644 --- a/src/components/ListItem.jsx +++ b/src/components/ListItem.jsx @@ -3,12 +3,19 @@ import { updateItem } from '../api'; import { useEffect } from 'react'; import { ONE_DAY_IN_MILLISECONDS } from '../utils/dates'; -export function ListItem({ name, listPath, id, isChecked, datePurchased }) { +export function ListItem({ + name, + listPath, + id, + isChecked, + datePurchased, + dayInterval, +}) { const handleOnChange = async (event) => { let { checked } = event.target; if (!checked) return; - await updateItem(listPath, id, checked); + await updateItem(listPath, id, checked, dayInterval); }; useEffect(() => { @@ -16,7 +23,7 @@ export function ListItem({ name, listPath, id, isChecked, datePurchased }) { const datePurchasedInMillis = datePurchased?.toMillis(); if (isChecked && today - datePurchasedInMillis >= ONE_DAY_IN_MILLISECONDS) { - updateItem(listPath, id, !isChecked); + updateItem(listPath, id, !isChecked, dayInterval); } }, []); diff --git a/src/views/List.jsx b/src/views/List.jsx index 394e538..877325a 100644 --- a/src/views/List.jsx +++ b/src/views/List.jsx @@ -28,6 +28,7 @@ export function List({ data, listPath }) { id={item.id} isChecked={item.checked} datePurchased={item.dateLastPurchased} + dayInterval={item.dayInterval} /> ))} From a2b3500f428a2ff4b9dae7c0842a1f6466bb712a Mon Sep 17 00:00:00 2001 From: marshjaja <114920895+marshjaja@users.noreply.github.com> Date: Wed, 11 Sep 2024 17:00:05 +0100 Subject: [PATCH 2/4] completed logic to get interval --- src/api/firebase.js | 38 +++++++++++++++++++------------------ src/components/ListItem.jsx | 15 ++++----------- src/utils/dates.js | 8 ++++++++ src/views/List.jsx | 1 - 4 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/api/firebase.js b/src/api/firebase.js index 1eba0ff..f5a1e0e 100644 --- a/src/api/firebase.js +++ b/src/api/firebase.js @@ -7,13 +7,14 @@ import { onSnapshot, updateDoc, addDoc, + Timestamp, } from 'firebase/firestore'; import { useEffect, useState } from 'react'; import { db } from './config'; import { getFutureDate } from '../utils'; import toast from 'react-hot-toast'; import { calculateEstimate } from '@the-collab-lab/shopping-list-utils'; -import { ONE_DAY_IN_MILLISECONDS } from '../utils/dates'; +import { getDaysBetweenDates } from '../utils/dates'; /** * A custom hook that subscribes to the user's shopping lists in our Firestore * database and returns new data whenever the lists change. @@ -190,38 +191,39 @@ export async function addItem(listPath, { itemName, daysUntilNextPurchase }) { }); } -export async function updateItem(listPath, id, checked, dayInterval) { +export async function updateItem(listPath, id, checked) { const listCollectionRef = collection(db, listPath, 'items'); const itemRef = doc(listCollectionRef, id); try { const itemDoc = await getDoc(itemRef); const data = itemDoc.data(); + const today = new Date(); const currentTotalPurchases = data.totalPurchases; const currentDayInterval = data.dayInterval; - const dateLastPurchasedInMillis = data.dateLastPurchased - ? data.dateLastPurchased.toMillis() - : data.dateCreated.toMillis(); - const daysSinceLastPurchase = Date.now() - dateLastPurchasedInMillis; - console.log('DaysSinceLastPurchase ', daysSinceLastPurchase); - - //conditional assignment, if x < 1 -> 1, else drop decimals return whole number of days - console.log( - 'DaysSinceLastPurchase sb ms', - daysSinceLastPurchase / ONE_DAY_IN_MILLISECONDS, + const dateLastPurchasedJavaScriptObject = data.dateLastPurchased + ? data.dateLastPurchased.toDate() + : today; + + const daysSinceLastPurchase = getDaysBetweenDates( + today, + dateLastPurchasedJavaScriptObject, + ); + const estimate = calculateEstimate( + currentDayInterval, + daysSinceLastPurchase, + currentTotalPurchases, ); if (checked) { await updateDoc(itemRef, { - dateLastPurchased: new Date(), + dateLastPurchased: Timestamp.fromDate(new Date()), totalPurchases: currentTotalPurchases + 1, checked: checked, dayInterval: daysSinceLastPurchase, - dateNextPurchased: calculateEstimate( - currentDayInterval, - daysSinceLastPurchase, - currentTotalPurchases, - ), //format Timestamps -> September 4, 2024 at 11:11:11 AM UTC+2 + dateNextPurchased: Timestamp.fromMillis( + today.setDate(today.getDate() + estimate), + ), }); } else { await updateDoc(itemRef, { diff --git a/src/components/ListItem.jsx b/src/components/ListItem.jsx index 4094928..2af1941 100644 --- a/src/components/ListItem.jsx +++ b/src/components/ListItem.jsx @@ -3,27 +3,20 @@ import { updateItem } from '../api'; import { useEffect } from 'react'; import { ONE_DAY_IN_MILLISECONDS } from '../utils/dates'; -export function ListItem({ - name, - listPath, - id, - isChecked, - datePurchased, - dayInterval, -}) { +export function ListItem({ name, listPath, id, isChecked, datePurchased }) { const handleOnChange = async (event) => { let { checked } = event.target; if (!checked) return; - await updateItem(listPath, id, checked, dayInterval); + await updateItem(listPath, id, checked); }; useEffect(() => { const today = new Date().getTime(); - const datePurchasedInMillis = datePurchased?.toMillis(); + const datePurchasedInMillis = datePurchased; if (isChecked && today - datePurchasedInMillis >= ONE_DAY_IN_MILLISECONDS) { - updateItem(listPath, id, !isChecked, dayInterval); + updateItem(listPath, id, !isChecked); } }, []); diff --git a/src/utils/dates.js b/src/utils/dates.js index dc66d95..0d680a6 100644 --- a/src/utils/dates.js +++ b/src/utils/dates.js @@ -10,3 +10,11 @@ export const ONE_DAY_IN_MILLISECONDS = 86400000; export function getFutureDate(offset) { return new Date(Date.now() + offset * ONE_DAY_IN_MILLISECONDS); } +export function getDaysBetweenDates(startDate, endDate) { + const differenceInMillis = startDate.getTime() - endDate.getTime(); + const daysSinceLastPurchase = + Math.round(differenceInMillis / ONE_DAY_IN_MILLISECONDS) === 0 + ? 1 + : Math.round(differenceInMillis / ONE_DAY_IN_MILLISECONDS); + return daysSinceLastPurchase; +} diff --git a/src/views/List.jsx b/src/views/List.jsx index 877325a..394e538 100644 --- a/src/views/List.jsx +++ b/src/views/List.jsx @@ -28,7 +28,6 @@ export function List({ data, listPath }) { id={item.id} isChecked={item.checked} datePurchased={item.dateLastPurchased} - dayInterval={item.dayInterval} /> ))} From 7311bc8c2cde487b9d42c5058ea1cffb1a3564d5 Mon Sep 17 00:00:00 2001 From: wyna Date: Sun, 15 Sep 2024 00:40:22 +0200 Subject: [PATCH 3/4] requested changes implemented --- src/api/firebase.js | 8 +++----- src/components/ListItem.jsx | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/api/firebase.js b/src/api/firebase.js index f5a1e0e..c33add9 100644 --- a/src/api/firebase.js +++ b/src/api/firebase.js @@ -203,7 +203,7 @@ export async function updateItem(listPath, id, checked) { const currentDayInterval = data.dayInterval; const dateLastPurchasedJavaScriptObject = data.dateLastPurchased ? data.dateLastPurchased.toDate() - : today; + : data.dateCreated.toDate(); const daysSinceLastPurchase = getDaysBetweenDates( today, @@ -217,13 +217,11 @@ export async function updateItem(listPath, id, checked) { if (checked) { await updateDoc(itemRef, { - dateLastPurchased: Timestamp.fromDate(new Date()), + dateLastPurchased: today, totalPurchases: currentTotalPurchases + 1, checked: checked, dayInterval: daysSinceLastPurchase, - dateNextPurchased: Timestamp.fromMillis( - today.setDate(today.getDate() + estimate), - ), + dateNextPurchased: getFutureDate(estimate), }); } else { await updateDoc(itemRef, { diff --git a/src/components/ListItem.jsx b/src/components/ListItem.jsx index 2af1941..1b7334e 100644 --- a/src/components/ListItem.jsx +++ b/src/components/ListItem.jsx @@ -13,7 +13,7 @@ export function ListItem({ name, listPath, id, isChecked, datePurchased }) { useEffect(() => { const today = new Date().getTime(); - const datePurchasedInMillis = datePurchased; + const datePurchasedInMillis = datePurchased?.toMillis(); if (isChecked && today - datePurchasedInMillis >= ONE_DAY_IN_MILLISECONDS) { updateItem(listPath, id, !isChecked); From 0d8b397ed975cdad415149209de80d9970d418d4 Mon Sep 17 00:00:00 2001 From: marshjaja <114920895+marshjaja@users.noreply.github.com> Date: Sun, 15 Sep 2024 13:45:00 +0100 Subject: [PATCH 4/4] fix: implement and update requested changes for item purchase tracking --- src/api/firebase.js | 39 ++++++++++++++++++------------------- src/components/ListItem.jsx | 29 +++++++++++++++++++++++---- src/views/List.jsx | 5 ++++- 3 files changed, 48 insertions(+), 25 deletions(-) diff --git a/src/api/firebase.js b/src/api/firebase.js index c33add9..6f650de 100644 --- a/src/api/firebase.js +++ b/src/api/firebase.js @@ -191,30 +191,29 @@ export async function addItem(listPath, { itemName, daysUntilNextPurchase }) { }); } -export async function updateItem(listPath, id, checked) { +// checked is coming from the handleOnChange function in the ListItem.jsx,so it is not part of the item data. +export async function updateItem(listPath, checked, itemData) { + const { id } = itemData; const listCollectionRef = collection(db, listPath, 'items'); const itemRef = doc(listCollectionRef, id); + const today = new Date(); + const currentTotalPurchases = itemData.totalPurchases; + const currentDayInterval = itemData.dayInterval; + const dateLastPurchasedJavaScriptObject = itemData.dateLastPurchased + ? itemData.dateLastPurchased.toDate() + : itemData.dateCreated.toDate(); + + const daysSinceLastPurchase = getDaysBetweenDates( + today, + dateLastPurchasedJavaScriptObject, + ); + const estimate = calculateEstimate( + currentDayInterval, + daysSinceLastPurchase, + currentTotalPurchases, + ); try { - const itemDoc = await getDoc(itemRef); - const data = itemDoc.data(); - const today = new Date(); - const currentTotalPurchases = data.totalPurchases; - const currentDayInterval = data.dayInterval; - const dateLastPurchasedJavaScriptObject = data.dateLastPurchased - ? data.dateLastPurchased.toDate() - : data.dateCreated.toDate(); - - const daysSinceLastPurchase = getDaysBetweenDates( - today, - dateLastPurchasedJavaScriptObject, - ); - const estimate = calculateEstimate( - currentDayInterval, - daysSinceLastPurchase, - currentTotalPurchases, - ); - if (checked) { await updateDoc(itemRef, { dateLastPurchased: today, diff --git a/src/components/ListItem.jsx b/src/components/ListItem.jsx index 1b7334e..051e336 100644 --- a/src/components/ListItem.jsx +++ b/src/components/ListItem.jsx @@ -3,20 +3,41 @@ import { updateItem } from '../api'; import { useEffect } from 'react'; import { ONE_DAY_IN_MILLISECONDS } from '../utils/dates'; -export function ListItem({ name, listPath, id, isChecked, datePurchased }) { +export function ListItem({ + listPath, + name, + id, + isChecked, + dateLastPurchased, + totalPurchases, + dayInterval, + dateCreated, +}) { const handleOnChange = async (event) => { let { checked } = event.target; if (!checked) return; - await updateItem(listPath, id, checked); + await updateItem(listPath, checked, { + id, + dateLastPurchased, + totalPurchases, + dayInterval, + dateCreated, + }); }; useEffect(() => { const today = new Date().getTime(); - const datePurchasedInMillis = datePurchased?.toMillis(); + const datePurchasedInMillis = dateLastPurchased?.toMillis(); if (isChecked && today - datePurchasedInMillis >= ONE_DAY_IN_MILLISECONDS) { - updateItem(listPath, id, !isChecked); + updateItem(listPath, !isChecked, { + id, + dateLastPurchased, + totalPurchases, + dayInterval, + dateCreated, + }); } }, []); diff --git a/src/views/List.jsx b/src/views/List.jsx index 394e538..58bbdde 100644 --- a/src/views/List.jsx +++ b/src/views/List.jsx @@ -27,7 +27,10 @@ export function List({ data, listPath }) { listPath={listPath} id={item.id} isChecked={item.checked} - datePurchased={item.dateLastPurchased} + dateLastPurchased={item.dateLastPurchased} + totalPurchases={item.totalPurchases} + dayInterval={item.dayInterval} + dateCreated={item.dateCreated} /> ))}