Skip to content
1 change: 1 addition & 0 deletions package-lock.json

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

102 changes: 56 additions & 46 deletions src/Components/Cart.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { Dialog, Transition } from "@headlessui/react";
import { XIcon } from "@heroicons/react/outline";
import React, { Fragment } from "react";
import { Dialog, Transition } from "@headlessui/react"
import { XIcon } from "@heroicons/react/outline"
import React, { Fragment } from "react"

export default function Cart({ open, setOpen, cart, updateCart }) {
let subtotal = cart.reduce((acc, product) => (acc + product.price*product.quantity), 0)
return (
<Transition.Root show={open} as={Fragment}>
<Dialog
as="div"
className="fixed inset-0 overflow-hidden z-10"
onClose={() => {
setOpen;
setOpen
}}
>
<div className="absolute inset-0 overflow-hidden">
Expand All @@ -21,6 +22,7 @@ export default function Cart({ open, setOpen, cart, updateCart }) {
leave="ease-in-out duration-500"
leaveFrom="opacity-100"
leaveTo="opacity-0"
onClick={() => setOpen(false)}
>
<Dialog.Overlay className="absolute inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
</Transition.Child>
Expand Down Expand Up @@ -53,59 +55,67 @@ export default function Cart({ open, setOpen, cart, updateCart }) {
</div>

<div className="mt-8">
<div className="flow-root">
<ul role="list" className="-my-6 divide-y divide-gray-200">
{cart.map((product) => (
<li key={product.id} className="flex py-6">
<div className="h-24 w-24 flex-shrink-0 overflow-hidden rounded-md border border-gray-200">
<img
src={product.imageSrc}
alt={product.imageAlt}
className="h-full w-full object-cover object-center"
/>
</div>
{cart.length > 0 ? (

<div className="ml-4 flex flex-1 flex-col">
<div>
<div className="flex justify-between text-base font-medium text-gray-900">
<h3>{product.name}</h3>
<p className="ml-4">${product.price}</p>
</div>
<div className="flow-root">
<ul role="list" className="-my-6 divide-y divide-gray-200">
{cart.map((product) => (
<li key={product.id} className="flex py-6">
<div className="h-24 w-24 flex-shrink-0 overflow-hidden rounded-md border border-gray-200">
<img
src={product.imageSrc}
alt={product.imageAlt}
className="h-full w-full object-cover object-center"
/>
</div>
<div className="flex flex-1 items-end justify-between text-sm">
<p className="text-gray-500">Qty {product.quantity}</p>

<div className="flex">
<button
onClick={() => {
let newCart = cart.filter((p) => {
if (p.id === product.id) {
p.quantity -= 1;
}
<div className="ml-4 flex flex-1 flex-col">
<div>
<div className="flex justify-between text-base font-medium text-gray-900">
<h3>{product.name}</h3>
<p className="ml-4">${product.price}</p>
</div>
</div>
<div className="flex flex-1 items-end justify-between text-sm">
<p className="text-gray-500">Qty {product.quantity}</p>

<div className="flex">
<button
onClick={() => {
let newCart = cart.filter((p) => {
if (p.id === product.id) {
p.quantity -= 1
}

return p.quantity > 0;
});
updateCart(newCart);
}}
type="button"
className="font-medium text-gray-500 hover:text-black"
>
Remove
</button>
return p.quantity > 0
})
updateCart(newCart)
}}
type="button"
className="font-medium text-gray-500 hover:text-black"
>
Remove
</button>
</div>
</div>
</div>
</div>
</li>
))}
</ul>
</div>
</li>
))}
</ul>
</div>
) : (
<div className="text-gray-500">
Empty cart
</div>
)
}
</div>
</div>

<div className="border-t border-gray-200 py-6 px-4 sm:px-6">
<div className="flex justify-between text-base font-medium text-gray-900">
<p>Subtotal</p>
<p>$262.00</p>
<p>${subtotal}</p>
</div>
<p className="mt-0.5 text-sm text-gray-500">Shipping and taxes calculated at checkout.</p>
<div className="mt-6">
Expand Down Expand Up @@ -136,5 +146,5 @@ export default function Cart({ open, setOpen, cart, updateCart }) {
</div>
</Dialog>
</Transition.Root>
);
)
}
4 changes: 2 additions & 2 deletions src/Components/NavBar.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ShoppingBagIcon } from "@heroicons/react/outline";
import React from "react";

export default function NavBar({ setOpen }) {
export default function NavBar({ setOpen, items }) {
return (
<div className="bg-white">
<header className="relative">
Expand Down Expand Up @@ -41,7 +41,7 @@ export default function NavBar({ setOpen }) {
className="flex-shrink-0 h-6 w-6 text-gray-400 group-hover:text-gray-500"
aria-hidden="true"
/>
<span className="ml-2 text-sm font-medium text-gray-700 group-hover:text-gray-800">0</span>
<span className="ml-2 text-sm font-medium text-gray-700 group-hover:text-gray-800">{items}</span>
<span className="sr-only">items in cart, view bag</span>
</button>
</div>
Expand Down
44 changes: 39 additions & 5 deletions src/Components/ProductFilters.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ function classNames(...classes) {
}

export default function ProductFilters({ filterOptions, setFilterOptions, sortOptions, setSortOptions }) {
const activePriceFilterOption = filterOptions.price.find((option) => option.checked)
const activeColorFilterOptions = filterOptions.color.find((option) => option.checked)

return (
<Disclosure
as="section"
Expand All @@ -24,11 +27,18 @@ export default function ProductFilters({ filterOptions, setFilterOptions, sortOp
className="flex-none w-5 h-5 mr-2 text-gray-400 group-hover:text-gray-500"
aria-hidden="true"
/>
0 Filters
{!!activePriceFilterOption + !!activeColorFilterOptions } Filters
</Disclosure.Button>
</div>
<div className="pl-6">
<button type="button" className="text-gray-500">
<button type="button" className="text-gray-500"
onClick={() => {
const newPriceFilterOptions = filterOptions.price.map(filterOption => ({ ...filterOption, checked: false }))
const newColorFilterOptions = filterOptions.color.map(filterOption => ({ ...filterOption, checked: false }))

setFilterOptions({ ...filterOptions, price: newPriceFilterOptions, color: newColorFilterOptions })
}}
>
Clear all
</button>
</div>
Expand All @@ -48,7 +58,17 @@ export default function ProductFilters({ filterOptions, setFilterOptions, sortOp
defaultValue={option.minValue}
type="checkbox"
className="flex-shrink-0 h-4 w-4 border-gray-300 rounded text-black focus:ring-black"
defaultChecked={option.checked}
checked={option.checked}
onChange={(e) => {
const newPriceFilterOption = filterOptions.price.map(filterOption => {
if (filterOption.label === option.label) {
return { ...filterOption, checked: e.target.checked }
} else {
return { ...filterOption, checked: false }
}
})
setFilterOptions({ ...filterOptions, price: newPriceFilterOption })
}}
/>
<label htmlFor={`price-${optionIdx}`} className="ml-3 min-w-0 flex-1 text-gray-600">
{option.label}
Expand All @@ -65,7 +85,18 @@ export default function ProductFilters({ filterOptions, setFilterOptions, sortOp
<input
id={`color-${optionIdx}`}
name="color[]"
defaultValue={option.value}
checked={option.checked}
onChange={(e) => {
const newColorFilterOption = filterOptions.color.map(filterOption => {
if (filterOption.label === option.label) {
return { ...filterOption, checked: e.target.checked }
} else {
return { ...filterOption, checked: false }
}
})
console.log(newColorFilterOption)
setFilterOptions({ ...filterOptions, color: newColorFilterOption })
}}
type="checkbox"
className="flex-shrink-0 h-4 w-4 border-gray-300 rounded text-black focus:ring-black"
defaultChecked={option.checked}
Expand Down Expand Up @@ -109,7 +140,10 @@ export default function ProductFilters({ filterOptions, setFilterOptions, sortOp
{({ active }) => (
<button
onClick={() => {
// TODO
const newSortOptions = sortOptions.map(
(sortOption) => sortOption.name === option.name ? { ...sortOption, current: true } : { ...sortOption, current: false }
)
setSortOptions(newSortOptions)
}}
className={classNames(
option.current ? "font-medium text-gray-900" : "text-gray-500",
Expand Down
73 changes: 50 additions & 23 deletions src/Components/ProductTable.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from "react";
import ProductFilters from "./ProductFilters";
import React, { useEffect, useState } from "react"
import ProductFilters from "./ProductFilters"

const getDefaultFilterOptions = () => {
return {
Expand All @@ -17,31 +17,58 @@ const getDefaultFilterOptions = () => {
{ value: "gray", label: "Gray", checked: false },
{ value: "teal", label: "Teal", checked: false },
],
};
};
}
}

const getDefaultSortOptions = () => {
return [
{ name: "Price", current: false },
{ name: "Newest", current: false },
];
};
]
}

export default function ProductTable({ cart, updateCart }) {
let [products, setProducts] = useState([]);
let [products, setProducts] = useState([])

const [filterOptions, setFilterOptions] = useState(getDefaultFilterOptions())
const [sortOptions, setSortOptions] = useState(getDefaultSortOptions())
const activeSortOption = sortOptions.find((option) => option.current)
const activePriceFilterOption = filterOptions.price.find((option) => option.checked)
const activeColorFilterOptions = filterOptions.color.find((option) => option.checked)

const [filterOptions, setFilterOptions] = useState(getDefaultFilterOptions());
const [sortOptions, setSortOptions] = useState(getDefaultSortOptions());
const buildPriceQuery = () => {
let priceQuery = activeSortOption ? '&' : ''
if (activePriceFilterOption) {
priceQuery = `price_gte=${activePriceFilterOption.minValue}`
}
if (activePriceFilterOption && !activePriceFilterOption.label.endsWith('+')) {
priceQuery += `&price_lte=${activePriceFilterOption.maxValue}`
}
return priceQuery
}

const buildColorQuery = () => {
let colorQuery = (activeSortOption || activePriceFilterOption) ? '&' : ''
if (activeColorFilterOptions) {
colorQuery += `color=${activeColorFilterOptions.value}`
}
return colorQuery
}

useEffect(() => {
let fetchProducts = async () => {
console.info("Fetching Products...");
let res = await fetch("http://localhost:3001/products");
let body = await res.json();
setProducts(body);
};
fetchProducts();
});
console.info("Fetching Products...")
const sortQuery = activeSortOption ? `&_sort=${activeSortOption.name.toLowerCase()}` : ``
const priceQuery = buildPriceQuery()
const colorQuery = buildColorQuery()

let res = await fetch(`http://localhost:3001/products?${sortQuery}${priceQuery}${colorQuery}`)
let body = await res.json()
console.log(priceQuery)
setProducts(body)
}
fetchProducts()
}, [sortOptions, filterOptions])

return (
<div className="bg-white">
Expand All @@ -62,20 +89,20 @@ export default function ProductTable({ cart, updateCart }) {
type="button"
className="hidden group-hover:block group-hover:opacity-50 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-black"
onClick={() => {
let newCart = cart.slice();
let newCart = cart.slice()

if (!newCart.includes(product)) {
product.quantity = 1;
newCart.push(product);
product.quantity = 1
newCart.push(product)
} else {
newCart.map((p) => {
if (p.id === product.id) {
p.quantity += 1;
p.quantity += 1
}
});
})
}

updateCart(newCart);
updateCart(newCart)
}}
>
Add To Cart
Expand All @@ -88,5 +115,5 @@ export default function ProductTable({ cart, updateCart }) {
</div>
</div>
</div>
);
)
}
14 changes: 11 additions & 3 deletions src/Pages/Home.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import Cart from "../Components/Cart";
import NavBar from "../Components/NavBar";
import ProductTable from "../Components/ProductTable";

function Home() {
const [open, setOpen] = useState(false);
const [cart, updateCart] = useState([]);
const [cart, updateCart] = useState(() => {
const cart = localStorage.getItem("cart");
return cart ? JSON.parse(cart) : [];
});
const items = cart.reduce((acc, product) => (acc + product.quantity), 0)
useEffect(() => {
const cartJSON = JSON.stringify(cart);
localStorage.setItem("cart", cartJSON);
}, [cart])

return (
<main>
<NavBar {...{ setOpen }} />
<NavBar {...{ setOpen, items }} />
<Cart {...{ open, setOpen, cart, updateCart }} />
<ProductTable {...{ cart, updateCart }} />
</main>
Expand Down