diff --git a/src/cart/CartActions.ts b/src/cart/CartActions.ts index 7171b8608060cef640dd513f51127d8d918d0861..528f1f71119965c07f15a8c1c7c576df033c0cb5 100644 --- a/src/cart/CartActions.ts +++ b/src/cart/CartActions.ts @@ -9,7 +9,7 @@ export function addToCart(cart: Product[], dispatch: Dispatch<ActionType>, produ if (existingProduct) { existingProduct.quantity = existingProduct.quantity ? existingProduct.quantity + 1 : 1; - existingProduct.stockPrice = existingProduct.stockPrice ? existingProduct.stockPrice + existingProduct.price : existingProduct.price; + existingProduct.stockPrice = existingProduct.price * existingProduct.quantity; return setCart(newCart, dispatch); } @@ -24,11 +24,12 @@ export function removeFromCart(cart: Product[], dispatch: Dispatch<ActionType>, const existingProduct = newCart.find(p => p.name === product.name); if (existingProduct) { existingProduct.quantity = existingProduct.quantity ? existingProduct.quantity - 1 : 0; - existingProduct.stockPrice = existingProduct.stockPrice ? existingProduct.stockPrice - existingProduct.price : existingProduct.price; + existingProduct.stockPrice = existingProduct.price * existingProduct.quantity; // If quantity is 0, remove product from cart if (existingProduct.quantity === 0) { return setCart(newCart.filter(p => p.name !== product.name), dispatch); } + return setCart(newCart, dispatch); } return setCart(cart, dispatch); @@ -41,6 +42,12 @@ export function updateProductQuantity(cart: Product[], dispatch: Dispatch<Action const existingProduct = newCart.find(p => p.name === product.name); if (existingProduct) { existingProduct.quantity = quantity; + existingProduct.stockPrice = existingProduct.price * quantity; + + if (existingProduct.quantity === 0) { + return setCart(newCart.filter(p => p.name !== product.name), dispatch); + } + return setCart(newCart, dispatch); } return setCart(newCart, dispatch); diff --git a/src/cart/CartReducer.ts b/src/cart/CartReducer.ts index 8741d814dd761914ea4d1e0e56290aa9ce0d54f1..5ddffeb87d76c66a09c737e1390bc32ee30aaae3 100644 --- a/src/cart/CartReducer.ts +++ b/src/cart/CartReducer.ts @@ -4,13 +4,13 @@ import { initialState } from "./CartStore"; const Reducer = (state: CartType, action: ActionType): any => { switch (action.type) { case "SET_CART": - console.log("SET_CART"); + //console.log("SET_CART"); return { ...state, cart: action.payload, }; case "TOGGLE_CART": - console.log("TOGGLE_CART"); + //console.log("TOGGLE_CART"); return { ...state, showCart: !state.showCart, diff --git a/src/components/sell/cart.tsx b/src/components/sell/cart.tsx index 6f351164f73f670a46d6538911ad80b52e15852a..219c3351fb0898cb05b978b91ac335e7036a3904 100644 --- a/src/components/sell/cart.tsx +++ b/src/components/sell/cart.tsx @@ -1,24 +1,21 @@ -import {Fragment, useContext, useEffect, useState} from 'react' +import {Fragment, useContext} from 'react' import { Dialog, Transition } from '@headlessui/react' +import { ArrowLeftIcon, EmojiSadIcon } from '@heroicons/react/outline'; import { XIcon } from '@heroicons/react/outline' import { cartContext } from '../../cart/CartStore'; import { - addToCart, - removeFromCart, - // updateProductQuantity + updateProductQuantity } from '../../cart/CartActions'; -import {Product} from "../../cart/CartTypes"; function Cart() { const { cartState, dispatch } = useContext(cartContext); - function removeProductFromCart(cartItem: Product) { - cartItem.stock++; - cartState.totalPrice -= cartItem.price; - removeFromCart(cartState.cart, dispatch, cartItem); + function renderPrice(price: number): string { + // Render price with currency and fix displaying of price + return `${price.toFixed(2)} €`; } return ( @@ -74,71 +71,93 @@ function Cart() { </div> <div className="mt-6 relative flex-1 px-4 sm:px-6"> <div className="bg-white"> - <div className="max-w-4xl mx-auto py-16 px-4 sm:py-24 sm:px-6 lg:px-8"> - <form className="mt-12"> - <div> - <ul className="border-t border-b border-gray-200 divide-y divide-gray-200"> - {cartState.cart.length <= 0 && <p>Aucun produit dans le panier</p>} - {cartState.cart.map(cartItem => ( - <li key={cartItem.name} className="flex py-6 sm:py-10"> - <div className="relative ml-4 flex-1 flex flex-col justify-between sm:ml-6"> - <div> - <div className="flex justify-between sm:grid sm:grid-cols-2"> - <div className="pr-6"> - <h3 className="text-sm"> - <p className="font-medium text-gray-700 hover:text-gray-800"> - {cartItem.name} - </p> - </h3> - <p className="mt-1 text-sm text-gray-500">x{cartItem.quantity}</p> - </div> - - <p className="text-sm font-medium text-gray-900 text-right">{cartItem.stockPrice}€</p> - </div> + <div className="mt-8"> + <div className="flow-root"> + <form className="mt-12"> + <div> + <ul className="-my-6 divide-y divide-gray-200"> + {cartState.cart.length <= 0 && <div className="text-center w-full mt-10 pb-10"> + <EmojiSadIcon className="mx-auto h-12 w-12 text-gray-400" /> + <h3 className="mt-2 text-sm font-medium text-gray-900">Aucun produit dans le panier !</h3> + <p className="mt-1 text-sm text-gray-500">Rajoutez-en ^^</p> + <div className="mt-6"> + <button + type="button" + onClick={() => dispatch({ type: 'TOGGLE_CART' })} + className="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" + > + <ArrowLeftIcon className="-ml-1 mr-2 h-5 w-5" aria-hidden="true" /> + Retourner aux produits + </button> + </div> + </div>} + {cartState.cart.map(cartItem => ( + <li key={cartItem.name} className="py-6 flex"> + <div className="ml-4 flex-1 flex flex-col"> + <div> + <div className="flex justify-between text-base font-medium text-gray-900"> + <h3> + <a href={cartItem.name}>{cartItem.name}</a> + </h3> + <p className="ml-4">{cartItem.stockPrice && renderPrice(cartItem.stockPrice)}</p> + </div> + <p className="mt-1 text-sm text-gray-500">{cartItem.category}</p> + </div> + <div className="flex-1 flex items-end justify-between text-sm pt-4"> + {/* <p className="text-gray-500">Qty {cartItem.quantity}</p> */} + <div className="flex flex-row items-baseline"> + <label htmlFor={`quantity-${cartItem.name}`} className="sr-only"> + Quantity, {cartItem.name} + </label> + <input + id={`quantity-${cartItem.name}`} + name={`quantity-${cartItem.name}`} + type="number" + max={cartItem.stock} + min={1} + value={cartItem.quantity} + onChange={(e) => {updateProductQuantity(cartState.cart, dispatch, cartItem, parseInt(e.target.value))}} + className="max-w-full rounded-md border border-gray-300 py-1.5 text-base leading-5 font-medium text-gray-700 text-left shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" + /><span className="text-sm pl-1">/ {cartItem.stock} unités</span></div> - <div - className="mt-4 flex items-center sm:block sm:absolute sm:top-0 sm:left-1/2 sm:mt-0"> - <button - type="button" - className="ml-4 text-sm font-medium text-indigo-600 hover:text-indigo-500 sm:ml-0 sm:mt-3" - //onClick={() => removeFromCart(cartState.cart, dispatch, cartItem)} - onClick={() => removeProductFromCart(cartItem)} - > - <span>Supprimer</span> - </button> - </div> - </div> - </div> - </li> - ))} - </ul> - </div> + <div className="flex"> + <button type="button" className="font-medium text-indigo-600 hover:text-indigo-500" onClick={() => updateProductQuantity(cartState.cart, dispatch, cartItem, 0)}> + Supprimer + </button> + </div> + </div> + </div> + </li> + ))} + </ul> + </div> - {/* Order summary */} - <div className="mt-10 sm:ml-32 sm:pl-6"> - <div className="bg-gray-50 rounded-lg px-4 py-6 sm:p-6 lg:p-8"> - <h2 className="sr-only">Panier</h2> + {/* Order summary */} + <div className="mt-10 sm:ml-32 sm:pl-6"> + <div className="bg-gray-50 rounded-lg px-4 py-6 sm:p-6 lg:p-8"> + <h2 className="sr-only">Panier</h2> - <div className="flow-root"> - <dl className="-my-4 text-sm divide-y divide-gray-200"> - <div className="py-4 flex items-center justify-between"> - <dt className="text-gray-600">Total : </dt> - <dd className="font-medium text-gray-900">{cartState.totalPrice}€</dd> - </div> - </dl> + <div className="flow-root"> + <dl className="-my-4 text-sm divide-y divide-gray-200"> + <div className="py-4 flex items-center justify-between"> + <dt className="text-gray-600">Total : </dt> + <dd className="font-medium text-gray-900">{renderPrice(0)}</dd> </div> - </div> - <div className="mt-10"> - <button - type="submit" - className="w-full bg-indigo-600 border border-transparent rounded-md shadow-sm py-3 px-4 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-50 focus:ring-indigo-500" - > - Valider - </button> - </div> + </dl> </div> - </form> + </div> + <div className="mt-10"> + <button + type="submit" + className="w-full bg-indigo-600 border border-transparent rounded-md shadow-sm py-3 px-4 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-50 focus:ring-indigo-500" + > + Valider + </button> + </div> </div> + </form> + </div> + </div> </div> </div> </div> diff --git a/src/components/sellListItem.tsx b/src/components/sellListItem.tsx index c2af9c3c136570fbf00f6ebf513cea2019add1b0..5443f83ea780ad96996c19d8919dc36ef1df9230 100644 --- a/src/components/sellListItem.tsx +++ b/src/components/sellListItem.tsx @@ -1,6 +1,7 @@ -import {useContext, useState} from 'react'; +import {useContext, useState, useEffect} from 'react'; import { + CubeTransparentIcon, ShoppingCartIcon, } from '@heroicons/react/outline'; @@ -12,9 +13,18 @@ import { cartContext } from '../cart/CartStore'; const SellListItem = ({product}: {product: Product}) => { const { cartState, dispatch } = useContext(cartContext); + const [item, setItem] = useState(product); + useEffect(() => { + // Set item from product in cartState.cart + let itemInCart = cartState.cart.find(item => item.name === product.name); + if (itemInCart) { + setItem(itemInCart); + } + }, [cartState.cart, product.name]); + function addProductToCart() { - product.stock--; - cartState.totalPrice += product.price; + // product.stock--; + // cartState.totalPrice += product.price; addToCart(cartState.cart, dispatch, product); } @@ -23,30 +33,49 @@ const SellListItem = ({product}: {product: Product}) => { return `${price.toFixed(2)} €`; } + function renderStock(stock: number): string { + if (stock === 0) { + return 'bg-red-100 text-red-800'; + } else if (stock <= 5) { + return 'bg-orange-100 text-orange-800'; + } else { + return 'bg-green-100 text-green-800'; + } + } + + function getStockWithCart(element: Product): number { + if (element.quantity !== undefined) { + return element.stock - element.quantity; + } + return element.stock; + } + return ( <li className="col-span-1 bg-white rounded-lg shadow divide-y divide-gray-200"> <div className="w-full flex items-center justify-between p-6 space-x-6"> <div className="flex-1 truncate"> <div className="flex items-center space-x-3"> - <h3 className="text-gray-900 text-sm font-medium truncate">{product.name}</h3> - <span className="flex-shrink-0 inline-block px-2 py-0.5 text-green-800 text-xs font-medium bg-green-100 rounded-full"> - {product.stock} + <h3 className="text-gray-900 text-sm font-medium truncate">{item.name}</h3> + <span className={"flex-shrink-0 inline-block px-2 py-0.5 text-xs font-medium rounded-full " + renderStock(getStockWithCart(item))}> + {getStockWithCart(item)} </span> </div> - <p className="mt-1 text-gray-500 text-sm truncate">{renderPrice(product.price)}</p> + <p className="mt-1 text-gray-500 text-sm truncate">{renderPrice(item.price)}</p> </div> </div> <div> <div className="-mt-px flex divide-x divide-gray-200"> <div className="-ml-px w-0 flex-1 flex"> - <button - //onClick={() => addToCart(cartState.cart, dispatch, product)} + {getStockWithCart(item) > 0 ? <button + //onClick={() => addToCart(cartState.cart, dispatch, item)} onClick={() => addProductToCart()} - className="relative w-0 flex-1 inline-flex items-center justify-center py-4 text-sm text-gray-700 font-medium border border-transparent rounded-br-lg hover:text-gray-500" + disabled={getStockWithCart(item) === 0} + className="relative w-0 flex-1 inline-flex items-center justify-center py-4 text-sm text-gray-700 font-medium border border-transparent rounded-b-lg hover:text-gray-500 active:bg-indigo-100 active:text-gray-900 focus:shadow-outline-indigo hover:border-indigo-700 transition ease-in-out duration-150" > - <ShoppingCartIcon className="w-5 h-5 mr-2 text-gray-400" aria-hidden="true"/> + <ShoppingCartIcon className="w-5 h-5 mr-2" aria-hidden="true"/> Ajouter - </button> + </button> : + <span className="relative w-0 flex-1 inline-flex items-center justify-center py-4 text-sm text-red-700 font-medium border border-transparent rounded-b-lg"><CubeTransparentIcon className="w-5 h-5 mr-2" aria-hidden="true"/>Rupture de stock</span>} </div> </div> </div> diff --git a/tailwind.config.js b/tailwind.config.js index 9766a9f67dea685223ed7bfa7d08a882903abfec..dd60a949565a5494913dbf72e1fd431b4f56a161 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,12 +1,21 @@ // tailwind.config.js +const colors = require('tailwindcss/colors') + module.exports = { purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'], darkMode: false, // or 'media' or 'class' theme: { - extend: {}, + extend: { + colors: { + orange: colors.orange + } + }, }, variants: { - extend: {}, + extend: { + backgroundColor: ['active'], + textColor: ['active'] + }, }, plugins: [ require('@tailwindcss/forms')