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: Allowing user to add items to shopping list #22

Merged
merged 21 commits into from
Aug 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c8bcd2f
adding a form layout to manage list
bbland1 Aug 19, 2024
7d01c41
update item label tag
bbland1 Aug 19, 2024
493db35
installed react-hot-toast and added toast for successful item add
bbland1 Aug 19, 2024
95bab87
modified addItem in firebase.js and WIP: parameters of call in Manage…
bbland1 Aug 20, 2024
6167526
cleaning up form tags and adding select possiblity
bbland1 Aug 21, 2024
7d39f08
adding the listPath prop to the ManageList component
bbland1 Aug 22, 2024
80d6b32
making form controlled with state, adding the listPath info and contr…
bbland1 Aug 22, 2024
4efe30b
replaced console.log in addItem (firebase.js) with addDoc and updated…
bbland1 Aug 22, 2024
da88150
chore: Add console logs and error logging in MAnageList.jsx.
RossaMania Aug 22, 2024
e980494
chore: Add console log for listpath in ManageList.jsx
RossaMania Aug 22, 2024
32403a6
Added console log for adding item to list success.
RossaMania Aug 22, 2024
d1157c4
change the when to buy options to a fieldset
bbland1 Aug 23, 2024
2a2a0e6
adding aria-label to form submit button
bbland1 Aug 23, 2024
71c983f
refactor: Improve form submission by adding strings to radio button s…
RossaMania Aug 23, 2024
ef5aded
feat: Improve ManageList component by setting daysUntilNextPurchase a…
RossaMania Aug 23, 2024
d13a6b7
remoiving debug console.logs and resetting state or keeping during fo…
bbland1 Aug 23, 2024
bdee02d
refactor: removed unneeded setState calls in toast error func
bbland1 Aug 23, 2024
02538e0
fix: remove the unneeded parseInt in firebase.js
bbland1 Aug 25, 2024
6a22d8d
refactor: add validation for no empty items, added constants for time…
bbland1 Aug 25, 2024
8e914c7
refactor: added valid trim input util, added validation of text and r…
bbland1 Aug 25, 2024
1792e84
Merge branch 'main' into bb-rc-add-items-to-list
bbland1 Aug 25, 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
39 changes: 36 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"firebase": "^10.12.5",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hot-toast": "^2.4.1",
bbland1 marked this conversation as resolved.
Show resolved Hide resolved
"react-router-dom": "^6.26.0"
},
"devDependencies": {
Expand Down
5 changes: 4 additions & 1 deletion src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ export function App() {
element={<Home data={lists} setListPath={setListPath} />}
/>
<Route path="/list" element={<List data={data} />} />
<Route path="/manage-list" element={<ManageList />} />
<Route
path="/manage-list"
element={<ManageList listPath={listPath} />}
/>
</Route>
</Routes>
</Router>
Expand Down
8 changes: 4 additions & 4 deletions src/api/firebase.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
arrayUnion,
getDoc,
setDoc,
addDoc,
collection,
doc,
onSnapshot,
Expand Down Expand Up @@ -162,10 +163,9 @@ export async function shareList(listPath, currentUserId, recipientEmail) {
* @param {number} itemData.daysUntilNextPurchase The number of days until the user thinks they'll need to buy the item again.
*/
export async function addItem(listPath, { itemName, daysUntilNextPurchase }) {
const listCollectionRef = collection(db, listPath, "items");
// TODO: Replace this call to console.log with the appropriate
// Firebase function, so this information is sent to your database!
return console.log(listCollectionRef, {
const listCollectionRef = collection(db, listPath, 'items');

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!
Expand Down
5 changes: 3 additions & 2 deletions src/utils/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./dates";
export * from "./hooks";
export * from './dates';
export * from './hooks';
export * from './validateTrimmedString';
10 changes: 10 additions & 0 deletions src/utils/validateTrimmedString.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// makes sure the string passed into the function isn't an empty string
export function validateTrimmedString(input) {
const trimmedInput = input.trim();

if (trimmedInput.length === 0) {
return null;
}

return trimmedInput;
}
144 changes: 140 additions & 4 deletions src/views/ManageList.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,143 @@
export function ManageList() {
import { useState } from 'react';
import { addItem } from '../api/firebase';
import { validateTrimmedString } from '../utils';
import toast, { Toaster } from 'react-hot-toast';

const SOON = 'soon';
const KIND_OF_SOON = 'kindOfSoon';
const NOT_SOON = 'notSoon';

const purchaseTimelines = {
[SOON]: 7,
[KIND_OF_SOON]: 14,
[NOT_SOON]: 30,
};

export function ManageList({ listPath }) {
const [itemName, setItemName] = useState('');
const [itemNextPurchaseTimeline, setItemNextPurchaseTimeline] =
useState(SOON);

const handleItemNameTextChange = (e) => {
setItemName(e.target.value);
};

const handleNextPurchaseChange = (e) => {
setItemNextPurchaseTimeline(e.target.value);
};

const handleSubmit = async (e) => {
e.preventDefault();
bbland1 marked this conversation as resolved.
Show resolved Hide resolved
const trimmedItemName = validateTrimmedString(itemName);

if (!trimmedItemName) {
toast.error(
'Item name cannot be empty or just spaces. Please enter a valid name.',
);
return;
}

if (!(itemNextPurchaseTimeline in purchaseTimelines)) {
toast.error(
'Selected purchase timeline is invalid. Please choose a valid option.',
);
return;
}

const daysUntilNextPurchase = purchaseTimelines[itemNextPurchaseTimeline];
bbland1 marked this conversation as resolved.
Show resolved Hide resolved

try {
await toast.promise(
bbland1 marked this conversation as resolved.
Show resolved Hide resolved
addItem(listPath, {
itemName: trimmedItemName,
daysUntilNextPurchase,
}),
{
pending: 'Adding item to list.',
bbland1 marked this conversation as resolved.
Show resolved Hide resolved
success: () => {
setItemName('');
setItemNextPurchaseTimeline(SOON);
return `${itemName} successfully added to your list!`;
},
error: () => {
return `${itemName} failed to add to your list. Please try again!`;
},
},
);
} catch (error) {
console.error('Failed to add item:', error);
}
};

return (
<p>
Hello from the <code>/manage-list</code> page!
</p>
<div>
<p>
Hello from the <code>/manage-list</code> page!
</p>
<form onSubmit={handleSubmit}>
bbland1 marked this conversation as resolved.
Show resolved Hide resolved
<label htmlFor="item-name">
Item:
<input
id="item-name"
type="text"
name="item"
value={itemName}
onChange={handleItemNameTextChange}
required
aria-label="Enter the item name"
aria-required
/>
</label>
<br />
<fieldset>
<legend>When to buy:</legend>
<label htmlFor={SOON}>
<input
type="radio"
id={SOON}
name="when-to-buy"
value={SOON}
required
onChange={handleNextPurchaseChange}
checked={itemNextPurchaseTimeline === SOON}
aria-label={`Set buy to soon, within ${purchaseTimelines[SOON]} days`}
/>
Soon -- Within {purchaseTimelines[SOON]} days!
</label>
<br />
<label htmlFor={KIND_OF_SOON}>
<input
type="radio"
id={KIND_OF_SOON}
name="when-to-buy"
value={KIND_OF_SOON}
required
onChange={handleNextPurchaseChange}
checked={itemNextPurchaseTimeline === KIND_OF_SOON}
aria-label={`Set buy to kind of soon, within ${purchaseTimelines[KIND_OF_SOON]} days`}
/>
Kind of soon -- Within {purchaseTimelines[KIND_OF_SOON]} days!
</label>
<br />
<label htmlFor={NOT_SOON}>
<input
type="radio"
id={NOT_SOON}
name="when-to-buy"
value={NOT_SOON}
required
onChange={handleNextPurchaseChange}
checked={itemNextPurchaseTimeline === NOT_SOON}
aria-label={`Set buy to not soon, within ${purchaseTimelines[NOT_SOON]} days`}
/>
Not soon -- Within {purchaseTimelines[NOT_SOON]} days!
</label>
</fieldset>
<button type="submit" aria-label="Add item to shopping list">
Submit Item
</button>
</form>
<Toaster />
</div>
);
}