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

Feat/add item quantity #46

Open
wants to merge 42 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
c27cfb9
feat: Add item quantity parameter to addItem function in firebase.ts.…
RossaMania Sep 25, 2024
e6a00cf
feat: Add logging of name and itemQuantity for successful item additi…
RossaMania Sep 25, 2024
f2789d6
feat: Add item quantity parameter to addItem function in AddItemForm.…
RossaMania Sep 25, 2024
f175b7b
feat: Add quantity input to AddItemForm.tsx with type "number". Wire …
RossaMania Sep 25, 2024
170d3e3
feat: Add item quantity property to ListItemModel in firebase.ts. Thi…
RossaMania Sep 25, 2024
912f056
feat: Add item quantity input to AddItemForm.tsx and create ItemQuant…
RossaMania Sep 25, 2024
2097649
feat: Add ItemQuantityForm component to ListItem.tsx. Import ItemQuan…
RossaMania Sep 26, 2024
7c848c3
feat: Add updateItemQuantity function to firebase.ts. The function re…
RossaMania Sep 26, 2024
153c3f0
feat: Update the updateItemQuantity function in firebase.ts with upda…
RossaMania Sep 26, 2024
266c2cc
feat: Update updateItemQuantity function in firebase.ts to include lo…
RossaMania Sep 26, 2024
ed049b5
fix: updated console logs for better readability.
RossaMania Sep 27, 2024
c41b19a
feat: Update item quantity functionality.
RossaMania Sep 27, 2024
dd7d557
feat: Update ItemQuantityForm to initialize item quantity from ListItem.
RossaMania Sep 27, 2024
f4eb386
feat: Update AddItemForm to pass item prop to ItemQuantityForm.
RossaMania Sep 27, 2024
ccde792
fix: Update the ItemQuantityForm component to handle cases where the …
RossaMania Sep 27, 2024
cb1804c
feat: Handle case where item quantity is 0 in ListItemCheckBox compon…
RossaMania Sep 27, 2024
49c2029
refactor: Remove unnecessary div element in ItemQuantityForm component
RossaMania Sep 28, 2024
9d5342b
Merge latest changes from main into feat/add-item-quantity. Render It…
RossaMania Sep 28, 2024
ff80a47
Refactor: Remove unnecessary check for listPath in editItemQuantity f…
RossaMania Sep 28, 2024
0dd580d
Refactor: Remove unnecessary section element in ItemQuantityForm comp…
RossaMania Sep 28, 2024
1755648
Refactor: Update label htmlFor attribute in ItemQuantityForm componen…
RossaMania Sep 28, 2024
94bd6df
Refactor: Change ItemQuantityForm from a default export to a named ex…
RossaMania Sep 28, 2024
0781346
Refactor: Update label in ItemQuantityForm component to "How many?"
RossaMania Sep 28, 2024
9a8cd06
Refactor: Update import statement for ItemQuantityForm in ListItem co…
RossaMania Sep 28, 2024
1c48aaa
Refactor: Update default item quantity in ItemQuantityForm component …
RossaMania Sep 28, 2024
006fc39
fix: Show error toast then set default item quantity to 1 if it is le…
RossaMania Sep 28, 2024
a20b47d
Refactor: Update error message in AddItemForm component when addItem …
RossaMania Sep 28, 2024
a46682a
Fix: Set default item quantity to 1 if it is less than 1 in AddItemFo…
RossaMania Sep 28, 2024
d8ca614
Refactor: Remove unnecessary deleteItemHandler call in ListItemCheckB…
RossaMania Sep 28, 2024
6d98f71
Refactor: Update ItemQuantityForm component to show error toast and s…
RossaMania Sep 28, 2024
f5f9ee6
Refactor: Update error message in AddItemForm component when item qua…
RossaMania Sep 28, 2024
eb19b32
Refactor: Add validation for item quantity in the editItemQuantity fu…
RossaMania Sep 28, 2024
9e7d038
Refactor: Update ItemQuantityForm component to use fragment instead o…
RossaMania Sep 29, 2024
d42c05a
Change the name of firebase function from updateItemQuantity to store…
RossaMania Sep 29, 2024
5beeed5
Refactor: Update console log message in AddItemForm component to be m…
RossaMania Sep 29, 2024
1883d22
Refactor: Have AddItemForm just use an in-line label and input for ad…
RossaMania Sep 29, 2024
b3ac8ef
Refactor: Remove unused 'item' prop from AddItemForm component
RossaMania Sep 29, 2024
1ee02d3
Refactor: Remove unnecessary checks for item quantity in AddItemForm …
RossaMania Sep 29, 2024
0778dd5
Refactor: Remove label for item quantity in ItemQuantityForm component.
RossaMania Sep 29, 2024
525ac74
Refactor: Update ItemQuantityForm component to include cancel button …
RossaMania Sep 29, 2024
b582e82
Refactor: Merge main branch changes into feat/add-item-quantity branc…
RossaMania Oct 2, 2024
5427158
Refactor ListItem component to display item quantity and name togethe…
RossaMania Oct 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/api/firebase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export function useShoppingLists(user: User | null) {
const ListItemModel = t.type({
id: t.string,
name: t.string,
itemQuantity: t.number,
RossaMania marked this conversation as resolved.
Show resolved Hide resolved
dateLastPurchased: t.union([FirebaseTimestamp, t.null]),
dateNextPurchased: FirebaseTimestamp,
totalPurchases: t.number,
Expand Down Expand Up @@ -233,6 +234,7 @@ export async function addItem(
listPath: string,
name: string,
daysUntilNextPurchase: number,
itemQuantity: number,
) {
const listCollectionRef = collection(db, listPath, "items");

Expand All @@ -244,8 +246,10 @@ export async function addItem(
dateLastPurchased: null,
dateNextPurchased: getFutureDate(daysUntilNextPurchase),
name,
itemQuantity,
totalPurchases: 0,
});
console.log("Item added successfully!", name, itemQuantity);
} catch (error) {
console.error("Error adding an item", error);
throw error;
Expand Down Expand Up @@ -295,6 +299,28 @@ export async function updateItem(listPath: string, item: ListItem) {
}
}

export async function updateItemQuantity(
listPath: string,
item: ListItem,
newQuantity: number,
) {
const itemDocRef = doc(db, listPath, "items", item.id);
const oldItemQuantity = item.itemQuantity;
console.log("Old item quantity from Firebase:", oldItemQuantity);

const updates = {
itemQuantity: newQuantity,
};

try {
await updateDoc(itemDocRef, updates);
console.log("Item quantity updated to", newQuantity);
} catch (error) {
console.error("Error updating quantity", error);
throw error;
}
}

//delete an item from the list
export async function deleteItem(listPath: string, item: ListItem) {
//reference the item document
Expand Down
28 changes: 27 additions & 1 deletion src/components/ListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import "./ListItem.css";
import { updateItem, deleteItem, ListItem } from "../api";
import { updateItem, deleteItem, ListItem, updateItemQuantity } from "../api";
import { useState } from "react";
import toast from "react-hot-toast";
import { moreThan24HoursPassed, getDaysBetweenDates } from "../utils";
import ItemQuantityForm from "./forms/ItemQuantityForm";

interface Props {
item: ListItem;
Expand Down Expand Up @@ -83,6 +84,30 @@ export function ListItemCheckBox({ item, listPath }: Props) {
}
};

Copy link
Collaborator

@bbland1 bbland1 Sep 28, 2024

Choose a reason for hiding this comment

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

suggestion: i think we could move this logic for updating the item quantity into the ItemQuantityForm. reading through the code right now it feels like there is some duplication of logic, kind of adding logic to components to make another component work.

moving this to be handled by the ItemQuantityForm it could make it a bit more reusable because we could just place it into the location it is needed and not write logic within that component to get it to work. it would be like this check box and other forms the logic to do the updating and steps the form is doing is housed within the logic.

To get the item prop needed with these changes on the list view we could try moving the <ItemQuantityForm saveItemQuantity={editItemQuantity} item={item} /> x{" "} call into the List component with this logic

{filteredListItems.map((item) => (<ListItemCheckBox key={item.id} item={item} listPath={listPath} />))}

and I believe it would get the same result.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Great idea, that way the logic outside of firebase for quantity is all referable in one place.

const editItemQuantity = async (quantity: number) => {
console.log("Item quantity edited:", quantity);

if (!listPath) {
RossaMania marked this conversation as resolved.
Show resolved Hide resolved
toast.error("Error: listPath is missing or invalid.");
return;
}

if (quantity === 0) {
deleteItemHandler();
}
RossaMania marked this conversation as resolved.
Show resolved Hide resolved

try {
await toast.promise(updateItemQuantity(listPath, item, quantity), {
loading: `Updating ${item.name} quantity!`,
success: `${item.name} quantity updated!`,
error: `Failed to update ${item.name} quantity. Please try again!`,
});
} catch (error) {
console.error(`Error updating ${item.name} quantity`, error);
alert("Error updating item quantity!");
}
};

const deleteItemHandler = () => {
const isConfirmed = window.confirm("Do you want to delete this item?");

Expand Down Expand Up @@ -113,6 +138,7 @@ export function ListItemCheckBox({ item, listPath }: Props) {
aria-checked={isChecked}
disabled={isChecked}
/>
<ItemQuantityForm saveItemQuantity={editItemQuantity} item={item} /> x{" "}
{item.name}
</label>

Expand Down
32 changes: 28 additions & 4 deletions src/components/forms/AddItemForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import { validateItemName } from "../../utils";
import toast from "react-hot-toast";

import { useNavigate } from "react-router-dom";
import ItemQuantityForm from "./ItemQuantityForm";

interface Props {
listPath: string | null;
data: ListItem[];
item: ListItem;
}

enum PurchaseTime {
Expand All @@ -22,14 +24,20 @@ const purchaseTimelines = {
[PurchaseTime.notSoon]: 30,
};

export function AddItemForm({ listPath, data: unfilteredListItems }: Props) {
export function AddItemForm({
listPath,
data: unfilteredListItems,
item,
}: Props) {
const navigate = useNavigate();

const [itemName, setItemName] = useState("");
const [itemNextPurchaseTimeline, setItemNextPurchaseTimeline] = useState(
PurchaseTime.soon,
);

const [itemQuantity, setItemQuantity] = useState(1);

const handleItemNameTextChange = (e: ChangeEvent<HTMLInputElement>) => {
setItemName(e.target.value);
};
Expand All @@ -38,6 +46,11 @@ export function AddItemForm({ listPath, data: unfilteredListItems }: Props) {
setItemNextPurchaseTimeline(changed);
};

const handleItemQuantityChange = (quantity: number) => {
setItemQuantity(quantity);
console.log("Item quantity entered:", quantity);
};

const handleSubmit = async (
e: FormEvent<HTMLFormElement>,
listPath: string,
Expand All @@ -63,23 +76,30 @@ export function AddItemForm({ listPath, data: unfilteredListItems }: Props) {
return;
}

if (itemQuantity < 1) {
RossaMania marked this conversation as resolved.
Show resolved Hide resolved
toast.error("Oops! Item quantity must be more than 0!");
return;
}
RossaMania marked this conversation as resolved.
Show resolved Hide resolved

const daysUntilNextPurchase = purchaseTimelines[itemNextPurchaseTimeline];

try {
await toast.promise(
addItem(listPath, itemName, daysUntilNextPurchase), // saving original input
addItem(listPath, itemName, daysUntilNextPurchase, itemQuantity), // saving original input
{
loading: "Adding item to list.",
success: () => {
setItemName("");
setItemNextPurchaseTimeline(PurchaseTime.soon);
return `${itemName} successfully added to your list!`; // showing original input
setItemQuantity(1);
return `${itemQuantity} ${itemName} successfully added to your list!`; // showing original input
},
error: () => {
return `${itemName} failed to add to your list. Please try again!`;
return `${itemQuantity} ${itemName} failed to add to your list. Please try again!`;
},
},
);
console.log("Item quantity added:", itemQuantity);
} catch (error) {
console.error("Failed to add item:", error);
}
Expand All @@ -94,6 +114,10 @@ export function AddItemForm({ listPath, data: unfilteredListItems }: Props) {
<>
<form onSubmit={(e) => handleSubmit(e, listPath)}>
<h3>First, add your item!</h3>
<ItemQuantityForm
saveItemQuantity={handleItemQuantityChange}
item={item}
/>
<label htmlFor="item-name">
Item:
<input
Expand Down
60 changes: 60 additions & 0 deletions src/components/forms/ItemQuantityForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { FormEvent, useState } from "react";
import { ListItem } from "../../api";

interface ItemQuantityFormProps {
saveItemQuantity: (quantity: number) => void;
item: ListItem;
}

const ItemQuantityForm: React.FC<ItemQuantityFormProps> = ({
RossaMania marked this conversation as resolved.
Show resolved Hide resolved
saveItemQuantity,
item,
}) => {
// A state variable to store the item quantity.
const [itemQuantity, setItemQuantity] = useState<number>(item?.itemQuantity);
RossaMania marked this conversation as resolved.
Show resolved Hide resolved

// A state variable to store the edit mode.
const [edit, setEdit] = useState<boolean>(false);

// A function that will toggle the edit mode.
const toggleEdit = (e: FormEvent<HTMLButtonElement>): void => {
e.preventDefault();
setEdit(!edit);
console.log("Toggle edit mode:", edit);
};

// A function that will save the item quantity.
const updateItemQuantity = (e: FormEvent<HTMLButtonElement>): void => {
e.preventDefault();
setEdit(!edit);
saveItemQuantity(itemQuantity);
console.log("Item quantity saved:", itemQuantity);
};

return (
<section>
RossaMania marked this conversation as resolved.
Show resolved Hide resolved
<form>
<label htmlFor="item-quantity">How many:</label>{" "}
RossaMania marked this conversation as resolved.
Show resolved Hide resolved
<input
id="item-quantity"
aria-label="Item quantity"
type="number"
name="item-quantity"
max="100"
value={itemQuantity}
onChange={(e) => setItemQuantity(Number(e.target.value))}
disabled={!edit}
/>
<div>
{edit ? (
RossaMania marked this conversation as resolved.
Show resolved Hide resolved
<button onClick={updateItemQuantity}>Save</button>
) : (
<button onClick={toggleEdit}>Edit</button>
)}
</div>
</form>
</section>
);
};

export default ItemQuantityForm;