diff --git a/package-lock.json b/package-lock.json index 186c080..9bcd3c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "react-bootstrap": "^2.10.5", "react-dom": "^18.3.1", "react-hot-toast": "^2.4.1", + "react-icons": "^5.3.0", "react-router-dom": "^6.26.2" }, "devDependencies": { @@ -8985,6 +8986,14 @@ "react-dom": ">=16" } }, + "node_modules/react-icons": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz", + "integrity": "sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", diff --git a/package.json b/package.json index 7c50e44..24a73ef 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "react-bootstrap": "^2.10.5", "react-dom": "^18.3.1", "react-hot-toast": "^2.4.1", + "react-icons": "^5.3.0", "react-router-dom": "^6.26.2" }, "devDependencies": { diff --git a/src/components/FilterListInput.tsx b/src/components/FilterListInput.tsx index 1f7aadb..1cab06a 100644 --- a/src/components/FilterListInput.tsx +++ b/src/components/FilterListInput.tsx @@ -25,9 +25,11 @@ export function FilterListInput({ }; return ( -
- - Filter List: + + + + Filter List: + + ); diff --git a/src/components/ListItem.scss b/src/components/ListItem.scss index 0ec3352..0527427 100644 --- a/src/components/ListItem.scss +++ b/src/components/ListItem.scss @@ -1,15 +1,66 @@ -.ListItem { +@import "../styles/_variables"; + +.ListItemBox { align-items: baseline; - display: flex; - flex-direction: row; - font-size: 1.2em; + font-size: 1.5em; justify-content: space-between; + background-color: $primary-blue; + color: $primary-beige; + border-radius: 4px; + max-width: 100%; + box-sizing: border-box; +} + +.UrgencyStatus { + display: block; + background-color: $secondary-blue; + color: $primary-beige; + font-size: 1.25em; + max-width: 50%; + width: 80%; + border-radius: 4px; + + // Default styles for urgency status + font-weight: bold; + + &.inactive { + color: gray; + } + + &.overdue { + color: red; + } + + &.soon { + color: orange; + } + + &.kind-of-soon { + color: yellow; + } + + &.not-soon { + color: green; + } +} + +.DeleteButton { + width: fit-content; } -.ListItem-checkbox { - accent-color: var(--color-accent); +.custom-borders { + border: 2px solid $secondary-blue !important; + border-radius: 4px !important; + padding: 20px; + margin-top: 20px; + position: relative; } -.ListItem-label { - margin-left: 0.2em; +@media (max-width: 576px) { + .EditItem { + flex-direction: column; + } + .PurchaseItem { + font-size: 0.75em; + } } diff --git a/src/components/ListItem.tsx b/src/components/ListItem.tsx index b023f03..ea199b8 100644 --- a/src/components/ListItem.tsx +++ b/src/components/ListItem.tsx @@ -6,6 +6,7 @@ import { moreThan24HoursPassed, getDaysBetweenDates } from "../utils"; import { ItemQuantityForm } from "./forms/ItemQuantityForm"; import Button from "react-bootstrap/Button"; import Form from "react-bootstrap/Form"; +import { MdOutlineDeleteForever } from "react-icons/md"; interface Props { item: ListItem; @@ -63,6 +64,8 @@ export function ListItemCheckBox({ item, listPath }: Props) { return "kind of soon"; }; + const urgencyStatus = getUrgencyStatus(item); + const handleCheckChange = async (e: React.ChangeEvent) => { const newCheckedState = e.target.checked; @@ -119,25 +122,41 @@ export function ListItemCheckBox({ item, listPath }: Props) { }; return ( -
- - x{" "} - {item.name}{" "} - - {getUrgencyStatus(item)} - +
+ + {urgencyStatus} +
+
+ + {item.name} +
+ +
+ + + +
+
); } diff --git a/src/components/NavBar.scss b/src/components/NavBar.scss index e96e4a5..4b222f3 100644 --- a/src/components/NavBar.scss +++ b/src/components/NavBar.scss @@ -29,10 +29,10 @@ color: $secondary-blue; } } +} - .navbar .navbar-toggler { - background-color: $primary-beige; - } +.navbar .navbar-toggler { + background-color: $primary-beige; } .navbar button { diff --git a/src/components/authenticated/AuthenticatedNavBar.tsx b/src/components/authenticated/AuthenticatedNavBar.tsx index 78cc1b6..ea72c06 100644 --- a/src/components/authenticated/AuthenticatedNavBar.tsx +++ b/src/components/authenticated/AuthenticatedNavBar.tsx @@ -24,14 +24,7 @@ export function AuthenticatedNavBar() { Home - - Manage List - + ) => { setItemName(e.target.value); }; @@ -90,29 +91,25 @@ export function AddItemForm({ listPath, data: unfilteredListItems }: Props) { }, }, ); + navigate(`/list/${listName}`); console.log("Quantity added from Add Item form:", addedQuantity); } catch (error) { console.error("Failed to add item:", error); } }; - const navigateToListPage = () => { - if (listPath) { - const listName = listPath.split("/").pop(); - navigate(`/list/${listName}`); - } - }; - return ( -
+
handleSubmit(e, listPath)}> -

First, add your item!

+

New Item

+ - Item: +

Item Name:

-
-

Next, pick when you plan on buying this item again!

-
- When to buy: - + +
+ When to buy: + +
- +
- +
+
- + -

Let's go look at your list!

- +
); } diff --git a/src/components/forms/ItemQuantityForm.tsx b/src/components/forms/ItemQuantityForm.tsx index cf55b3c..086323e 100644 --- a/src/components/forms/ItemQuantityForm.tsx +++ b/src/components/forms/ItemQuantityForm.tsx @@ -3,6 +3,9 @@ import { ListItem } from "../../api"; import { toast } from "react-hot-toast"; import Button from "react-bootstrap/Button"; import Form from "react-bootstrap/Form"; +import { FaEdit } from "react-icons/fa"; +import { IoMdCheckmark } from "react-icons/io"; +import { GiCancel } from "react-icons/gi"; interface ItemQuantityFormProps { saveItemQuantity: (quantity: number) => void; @@ -43,8 +46,9 @@ export function ItemQuantityForm({ }; return ( - <> +
setItemQuantity(Number(e.target.value))} disabled={!edit} /> - {edit ? ( - - {" "} - {" "} + + + ) : ( + - - ) : ( - - )} - + )} +
+
); } diff --git a/src/components/forms/ShareListForm.scss b/src/components/forms/ShareListForm.scss new file mode 100644 index 0000000..f1cea1c --- /dev/null +++ b/src/components/forms/ShareListForm.scss @@ -0,0 +1,13 @@ +@import "../../styles/variables"; + +.custom-borders { + border: 2px solid $secondary-blue !important; + border-radius: 4px !important; + padding: 20px; + margin-top: 20px; + position: relative; +} + +.Share-ListForm { + background-color: rgb(241, 230, 204); +} diff --git a/src/components/forms/ShareListForm.tsx b/src/components/forms/ShareListForm.tsx index a49f647..e279bcf 100644 --- a/src/components/forms/ShareListForm.tsx +++ b/src/components/forms/ShareListForm.tsx @@ -1,10 +1,11 @@ +import "./ShareListForm.scss"; import { ChangeEvent, FormEvent, useState } from "react"; import { shareList } from "../../api"; import { getUser } from "../ProtectedRoute"; import Button from "react-bootstrap/Button"; import Form from "react-bootstrap/Form"; import { InputGroup } from "react-bootstrap"; - +import { FaRegShareSquare } from "react-icons/fa"; import toast from "react-hot-toast"; interface Props { @@ -43,8 +44,16 @@ const ShareListForm = ({ listPath }: Props) => { }; return ( -
handleInvite(e, listPath)}> - Recipient Email: + handleInvite(e, listPath)} + > + + Invite others to view your list! + { aria-label="Enter the user email address to share list" aria-required /> +
diff --git a/src/styles/global.scss b/src/styles/global.scss index 9126035..2305c4f 100644 --- a/src/styles/global.scss +++ b/src/styles/global.scss @@ -5,6 +5,7 @@ $theme-colors: ( "secondary": $primary-blue, "info": $tertiary-blue, "dark": $secondary-blue, + "danger": #dc3545, ); $font-size-root: null; @@ -26,7 +27,6 @@ body { text-align: center; border: none; padding: 0.5rem; - width: 11rem; &:hover { background-color: $secondary-blue; @@ -34,6 +34,6 @@ body { } } -a svg { +svg { fill: $primary-beige; } diff --git a/src/views/Layout.scss b/src/views/Layout.scss index abe0aab..b926927 100644 --- a/src/views/Layout.scss +++ b/src/views/Layout.scss @@ -21,6 +21,7 @@ padding-block: 0; padding-block-end: 6.26rem; width: min(72ch, 100%); + //width: 100%; height: 100dvh; } } diff --git a/src/views/authenticated/List.scss b/src/views/authenticated/List.scss new file mode 100644 index 0000000..da651ca --- /dev/null +++ b/src/views/authenticated/List.scss @@ -0,0 +1,25 @@ +@import "../../styles/variables"; + +.ListName { + color: $primary-beige; + background-color: $primary-blue; + text-align: center; + border-radius: 4px; + font-size: 2.5em; + max-width: 100%; +} + +.ListPageContainer { + width: 100%; + display: flex; + flex-wrap: wrap; + box-sizing: border-box; + flex-direction: column; + flex-grow: 1; + flex-shrink: 1; +} + +.list-functions { + background-color: rgb(241, 230, 204); + z-index: 1020; +} diff --git a/src/views/authenticated/List.tsx b/src/views/authenticated/List.tsx index 066b499..71e677f 100644 --- a/src/views/authenticated/List.tsx +++ b/src/views/authenticated/List.tsx @@ -1,9 +1,13 @@ +import "./List.scss"; import { useParams, useNavigate } from "react-router-dom"; -import { useState, useMemo } from "react"; +import { useState, useMemo, useRef } from "react"; import { ListItemCheckBox } from "../../components/ListItem"; import { FilterListInput } from "../../components/FilterListInput"; import { ListItem, comparePurchaseUrgency } from "../../api"; +import Container from "react-bootstrap/Container"; import Button from "react-bootstrap/Button"; +import ShareListForm from "../../components/forms/ShareListForm"; +import { AddItemForm } from "../../components/forms/AddItemForm"; interface Props { data: ListItem[]; @@ -25,8 +29,10 @@ export function List({ data: unfilteredListItems, listPath }: Props) { const Header = () => { return ( -

- Hello from the /list page! +

+ Your list items are organized based on when you need to buy them. If an + items purchase date has passed, it will be marked as overdue and placed + at the top of the list.

); }; @@ -38,11 +44,10 @@ export function List({ data: unfilteredListItems, listPath }: Props) { // Early return if the list is empty if (unfilteredListItems.length === 0) { return ( - <> -

{listName}

-
-
-

+ +

{listName}

+
+

You haven’t added any items yet.
Let’s get started by adding your first item! @@ -55,38 +60,59 @@ export function List({ data: unfilteredListItems, listPath }: Props) { {"Get started!"}

- +
); } + const viewListRef = useRef(null); + + // Function to handle scrolling to the Add-ShareList section + const scrollToViewList = () => { + if (viewListRef.current) { + viewListRef.current.scrollIntoView({ behavior: "smooth" }); + } + }; + // Main content when list is not empty return ( - <> -

{listName}

+ +
+
+

{listName}

+
-
+
+ +
-
- {unfilteredListItems.length > 0 && ( - - )} -

Want to add more items to your list?

+
+ {unfilteredListItems.length > 0 && ( + + )} +
+ +
+
+ {filteredListItems.map((item) => ( + + ))} +
+
+
+ +
-

-
- {filteredListItems.map((item) => ( - - ))} -
- + + + + ); } diff --git a/src/views/authenticated/ManageList.tsx b/src/views/authenticated/ManageList.tsx index 9cdd3cf..4131859 100644 --- a/src/views/authenticated/ManageList.tsx +++ b/src/views/authenticated/ManageList.tsx @@ -11,7 +11,13 @@ export function ManageList({ listPath, data }: Props) { const Header = () => { return (

- Hello from the /manage-list page! + Your list items are organized based on when you need to buy them. Items + that need to be purchased soonest are at the top. If two or more items + are due at the same time, they will be sorted alphabetically. If an + items purchase date has passed, it will be marked as overdue and placed + at the top of the list. Additionally, items that have not been used + recently will be labeled as inactive and moved to the bottom of your + list.

); }; @@ -22,9 +28,7 @@ export function ManageList({ listPath, data }: Props) { return (
-
-
); }