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

17. implement toast notifications on all messages not requiring user input #49

Merged
merged 1 commit into from
Oct 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 22 additions & 1 deletion package-lock.json

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

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.3.0",
"react-router-dom": "^6.26.0"
"react-router-dom": "^6.26.0",
"react-toastify": "^10.0.5"
},
"devDependencies": {
"@nabla/vite-plugin-eslint": "^2.0.4",
Expand Down
2 changes: 1 addition & 1 deletion src/api/firebase.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export async function shareList(listPath, currentUserId, recipientEmail) {
sharedLists: arrayUnion(listDocumentRef),
});
return Promise.resolve(
`You have successfully shared your list to ${recipientEmail}`,
`You have successfully shared your list to: \n${recipientEmail}`,
);
}

Expand Down
11 changes: 3 additions & 8 deletions src/components/ListItem.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react';
import { useToggle } from '@uidotdev/usehooks';
import { Toggle } from './Toggle.jsx';
import { notify } from '../utils/notifications';
import './ListItem.css';
import { updateItem, deleteItem } from '../api/firebase.js';
import { FaTrashAlt } from 'react-icons/fa';
Expand All @@ -15,7 +16,6 @@ export function ListItem({
purchaseInterval,
dateCreated,
sortCriteria,
setMessage,
}) {
const [purchased, setPurchased] = useToggle(false);
const [isDisabled, setIsDisabled] = useState(false);
Expand Down Expand Up @@ -55,7 +55,7 @@ export function ListItem({
dateCreated,
});
console.log(`${name} updated successfully`);
alert(`${name} is purchased successfully`);
notify(`${name} has been purchased successfully!`, 'success');
setIsDisabled(true);
} catch (error) {
console.error('Error updating item:', error);
Expand All @@ -64,7 +64,6 @@ export function ListItem({
}
};

// handleDelete Function
const handleDelete = async () => {
const deleteConfirm = window.confirm(
`Are you sure you want to delete ${name}?`,
Expand All @@ -73,7 +72,7 @@ export function ListItem({
if (deleteConfirm) {
try {
await deleteItem(listPath, itemId);
setMessage(`${name} has been deleted successfully!`);
notify(`${name} has been deleted successfully!`, 'success');
} catch (error) {
console.log(`Error:`, error);
}
Expand All @@ -95,15 +94,11 @@ export function ListItem({
{name}

<div className={urgencyClass}>{sortCriteria.tag}</div>
{/* <RiDeleteBin5Fill onClick={handleDelete} aria-label={`Delete ${name}`}>
Delete
</RiDeleteBin5Fill> */}

<IconButton
aria-label={`Delete ${name}`}
as="button"
className="delete-icon"
// label="Add"
IconComponent={FaTrashAlt}
onClick={handleDelete}
/>
Expand Down
5 changes: 4 additions & 1 deletion src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
--color-border: hsla(220, 13%, 32%, 1);
--color-error: var(--color-red);
--color-text: var(--color-white);

/**
* Set the value of 1rem to 10px to make relative units
* easier to work with.
Expand Down Expand Up @@ -103,3 +102,7 @@ code {
:root.theme-light code {
--color-bg: var(--color-gray-light);
}

.share-div {
padding-bottom: 100px;
}
18 changes: 18 additions & 0 deletions src/utils/notifications.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

/**
* calls a toast notification to pop up
* @param {string} text The text that should appear in the notification
* @param {string} type The styling for the notification: "info", "success", "error", "warning", "default"
*/
export const notify = (text, type) =>
toast(text, {
role: 'alert',
autoClose: 3000,
type,
draggable: true,
closeOnClick: true,
pauseOnHover: true,
hideProgressBar: true,
});
19 changes: 11 additions & 8 deletions src/views/Home.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import './Home.css';
import { ToastContainer } from 'react-toastify';
import { FaPlusSquare, FaShareAlt } from 'react-icons/fa';
import { FaAngleRight, FaAngleDown } from 'react-icons/fa6';
import { NavLink } from 'react-router-dom';
import { useState } from 'react';
import { notify } from '../utils/notifications';
import { createList } from '../api';
import { FaAngleRight, FaAngleDown } from 'react-icons/fa6';
import { Disclosure } from './Disclosure';
import { List } from './List';
import { IconButton } from '../components/IconButton';
import { FaPlusSquare, FaShareAlt } from 'react-icons/fa';
import { NavLink } from 'react-router-dom';
import './Home.css';

export function Home({ data, lists, listPath, setListPath, user }) {
const userId = user?.uid;
const userEmail = user?.email;

const [listName, setListName] = useState('');

const handleChange = (event) => {
Expand All @@ -23,19 +24,21 @@ export function Home({ data, lists, listPath, setListPath, user }) {
if (listName) {
try {
const newListPath = await createList(userId, userEmail, listName);
alert('List is sucessfully created');
notify('List is sucessfully created', 'success');
setListPath(newListPath);
} catch {
alert('There was an error adding your list to db');
notify('There was an error adding your list', 'error');
}
} else {
alert('Please enter a valid name');
notify('Please enter a valid name', 'warning');
}
setListName('');
};

return (
<div className="Home">
<div>
<ToastContainer />
<form onSubmit={handleSubmit}>
<label htmlFor="list-name">Create a List </label>
<input
Expand Down
15 changes: 1 addition & 14 deletions src/views/List.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { ListItem } from '../components';
import { comparePurchaseUrgency } from '../utils/dates.js';

export function List({ data, listPath }) {
const [searchInput, setSearchInput] = useState('');
const [message, setMessage] = useState('');

const handleInputChange = (e) => {
setSearchInput(e.target.value);
setMessage('');
};

const clearSearchInput = () => {
Expand All @@ -23,14 +21,6 @@ export function List({ data, listPath }) {
: item;
});

useEffect(() => {
if (message !== '') {
setInterval(() => {
setMessage('');
}, 5000);
}
}, [message]);

return (
<>
<div className="listSearch">
Expand Down Expand Up @@ -60,16 +50,13 @@ export function List({ data, listPath }) {
dateLastPurchased={item.dateLastPurchased}
purchaseInterval={item.purchaseInterval}
dateCreated={item.dateCreated}
setMessage={setMessage}
sortCriteria={item.sortCriteria}
/>
))}
</div>
) : (
<p>No items to display</p>
)}
<br />
<span>{message}</span>
</>
);
}
24 changes: 10 additions & 14 deletions src/views/ManageList.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { ToastContainer } from 'react-toastify';
import { useState, useMemo } from 'react';
import { addItem, shareList } from '../api/firebase';
import { FaPlusSquare } from 'react-icons/fa';
import { IconButton } from '../components/IconButton';
import { FaEnvelope } from 'react-icons/fa6';
import { notify } from '../utils/notifications';

export function ManageList({ listPath, user, data }) {
const currentUserId = user?.uid;
const [itemName, setItemName] = useState('');
const [daysUntilNextPurchase, setDaysUntilNextPurchase] = useState(7);
const [message, setMessage] = useState('');
const [recipientEmail, setRecipientEmail] = useState('');

const messages = {
Expand All @@ -34,14 +35,14 @@ export function ManageList({ listPath, user, data }) {
const normalizedItemName = normalizeString(itemName.trim());

if (!normalizedItemName) {
setMessage('empty');
notify(messages['empty'], 'warning');
return;
}

const itemMatch = normalizedData.includes(normalizedItemName);

if (itemMatch) {
setMessage('duplicate');
notify(messages['duplicate'], 'warning');
return;
}

Expand All @@ -50,29 +51,30 @@ export function ManageList({ listPath, user, data }) {
itemName: normalizedItemName,
daysUntilNextPurchase,
});
setMessage('added');
setItemName('');
setDaysUntilNextPurchase(7);
notify(messages['added'], 'success');
} catch (error) {
console.error('Error adding item:', error);
setMessage('failed');
notify(messages['failed'], 'error');
}
};

const handleShare = (event) => {
event.preventDefault();
shareList(listPath, currentUserId, recipientEmail)
.then((result) => {
alert(result);
notify(result, 'success');
setRecipientEmail('');
})
.catch((error) => {
alert(error);
notify(error, 'error');
});
};

return (
<div>
<ToastContainer />
<h1>Manage Your Shopping List for {extractedListName}</h1>
<form onSubmit={handleSubmit}>
<label htmlFor="itemName">Item Name:</label>
Expand Down Expand Up @@ -123,14 +125,8 @@ export function ManageList({ listPath, user, data }) {
</fieldset>
<br />
</form>
<br></br>
{message && (
<p aria-live="assertive" role="alert">
{messages[message] || ''}
</p>
)}

<div>
<div className="share-div">
<form onSubmit={handleShare}>
<label htmlFor="recipientEmail"> Recipient Email: </label>
<input
Expand Down