diff --git a/package.json b/package.json index afb7c3529109d68c468a516d91d11608bc28f2ad..f93b7411aab4a105d80af145cd87f38973d3dcda 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "private": true, "dependencies": { "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.11.2" }, "scripts": { "start": "webpack-dev-server --open --mode development --port 3000", diff --git a/public/assets/images/Mindy_Mouse_cat_toy.jpg b/public/assets/images/Mindy_Mouse_cat_toy.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a305116c1f72e3ef410c0281f6164c502586d0e1 Binary files /dev/null and b/public/assets/images/Mindy_Mouse_cat_toy.jpg differ diff --git a/public/assets/images/cat-house.jpg b/public/assets/images/cat-house.jpg new file mode 100644 index 0000000000000000000000000000000000000000..372052afdf4a510477af54ef9ba72dc195f2a302 Binary files /dev/null and b/public/assets/images/cat-house.jpg differ diff --git a/public/assets/images/cat-litter.jpg b/public/assets/images/cat-litter.jpg new file mode 100644 index 0000000000000000000000000000000000000000..736b9a8c61399536314d6b717f29791388a9e65f Binary files /dev/null and b/public/assets/images/cat-litter.jpg differ diff --git a/public/assets/images/laser-pointer.jpg b/public/assets/images/laser-pointer.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fa5d389dce41c18205c89dba2c092eb829feacc2 Binary files /dev/null and b/public/assets/images/laser-pointer.jpg differ diff --git a/public/assets/images/yarn.jpg b/public/assets/images/yarn.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e51159071a5bee535275c79276ed445e34498f44 Binary files /dev/null and b/public/assets/images/yarn.jpg differ diff --git a/src/App.js b/src/App.js index 03560b6b4f127a34ab016c8f4ca76de76ee36649..d5afe050643c4beab53ae39b5d73839a828471ae 100644 --- a/src/App.js +++ b/src/App.js @@ -1,228 +1,65 @@ import React, { useState, useEffect } from 'react'; import './assets/css/app.css'; +import { BrowserRouter, Routes, Route } from 'react-router-dom'; +import CheckoutPage from './Checkout'; +import Products from './Products'; +import Header from './Header'; +import ProductRegistrationPage from './RegisterProductPage'; const App = (props) => { - let [cartItems, setCartItems] = useState([]); - let [showProduct, setShowProduct] = useState(true); - let [order, setOrder] = useState({ firstName: '', lastName: '', state: 'CA', address: '' }); - let [products, setProducts] = useState([]); + const [cartItems, setCartItems] = useState([]); + const [formattedProducts, setFormattedProducts] = useState([]); useEffect(() => { /* wget https://raw.githubusercontent.com/gilbutITbook/007024/master/chapter-05/products.json */ fetch('./products.json') .then((r) => r.json()) - .then(data => { - setProducts([...data.products]); - console.log(products); - }) - }, []) - - let states = ['AL', 'AR', 'CA', 'NV', 'NY', 'FL']; -/* - let product = { - id: 1001, - title: "고양이 사료, 25파운드", - description: "당신의 고양이를 위한 거부할 수 없는, 유기농 25파운드 사료입니다.", - price: 2000, - image: "./assets/images/product-fullsize.png", - availableInventory: 5, - rating: 4, + .then((data) => { + setFormattedProducts([...data.products]); + console.log(data.products); + }); + }, []); + + const handleAddProduct = (product) => { + setFormattedProducts([...formattedProducts, product]); }; -*/ - - const formatPrice = (price) => { - if (!parseInt(price)) { return ""; } - if (price > 99999) { - var priceString = (price / 100).toFixed(2); - var priceArray = priceString.split("").reverse(); - var index = 3; - while (priceArray.length > index + 3) { - priceArray.splice(index + 3, 0, ","); - index += 4; - } - return "$" + priceArray.reverse().join(""); - } else { - return "$" + (price / 100).toFixed(2); - } - } - - const addToShoppingCart = (product) => { - setCartItems([...cartItems, product]); - } - - const canAddToCart = (product) => { - if (product.availableInventory) { - return product.availableInventory > cartItems.filter((p) => p.id == product.id).length; - } - return false; - } - - const updateOrder = (event) => { - let control = event.target; - if (control.name == "gift") { - setOrder({ ...order, gift: control.checked }); - return; - } - setOrder({ ...order, [control.name]: control.value }); - } - const checkRating = (n, product) => { - return product.rating - n >= 0; - } + const clearCart = () => { + setCartItems([]); + }; - const nameOrder = (p, q) => { - let [pLow, qLow] = [p.title.toLowerCase(), q.title.toLowerCase()] - if (pLow > qLow) { - return 1; - } - else if(pLow == qLow) { - return 0; - } - else { - return -1; - } - } + const updateInventory = (productId, quantity) => { + setFormattedProducts((prevProducts) => { + return prevProducts.map((product) => { + if (product.id === productId) { + return { + ...product, + availableInventory: product.availableInventory - quantity + }; + } + return product; + }); + }); + }; return ( - <> + <BrowserRouter> <header> - <div className="navbar navbar-default"> - <div className="navbar-header"> - <h1> - {props.sitename} - </h1> - </div> - <div className="nav navbar-nav navbar-right cart"> - <button type="button" className="btn btn-default btn-lg" - onClick={() => setShowProduct(!showProduct)} > - <span className="glyphicon glyphicon-shopping-cart"> - {cartItems.length || ''} - </span> - 체크아웃 - </button> - </div> - </div> + <Header name={props.sitename} nItems={cartItems.length} clearCart={clearCart} /> </header> <main> - {showProduct && products && - products.sort(nameOrder).map(product => { - let nProductInCart = cartItems.filter((p) => p.id == product.id).length; - console.log(product.title, nProductInCart) - return ( - <div className="row product"> - <div className="col-md-5 col-md-offset-0"> - <figure> - <img className="product" src={product.image} /> - {/* ./public/assests/images 폴더에서 - wget https://raw.githubusercontent.com/gilbutITbook/007024/master/chapter-05/assets/images/cat-litter.jpg - wget https://raw.githubusercontent.com/gilbutITbook/007024/master/chapter-05/assets/images/Mindy_Mouse_cat_toy.jpg - wget https://raw.githubusercontent.com/gilbutITbook/007024/master/chapter-05/assets/images/cat-house.jpg - wget https://raw.githubusercontent.com/gilbutITbook/007024/master/chapter-05/assets/images/laser-pointer.jpg - wget https://raw.githubusercontent.com/gilbutITbook/007024/master/chapter-05/assets/images/yarn.jpg - */} - </figure> - </div> - <div className="col-md-6 col-md-offset-0 description"> - <h1> {product.title} </h1> - <p> {product.description} </p> - <p className="price"> {formatPrice(product.price)} </p> - {canAddToCart(product) && - <button className="btn btn-primary btn-lg" - onClick={() => addToShoppingCart(product)}> 장바구니 담기 - </button> - } - - <span className="inventory-message"> - {product.availableInventory == nProductInCart && `품절!(Sold out)`} - {nProductInCart == 0 && `지금 구매하세요.`} - {product.availableInventory > nProductInCart && nProductInCart > 0 && - `${product.availableInventory - nProductInCart} 남았습니다.` } - </span> - <div className="rating"> - {[1, 2, 3, 4, 5].map((i) => { - return <span className={checkRating(i, product) ? "rating-active" : ""}>☆</span> - }) - } - </div> - </div> - </div> - ) - }) - } - - {!showProduct && /* Checkout 페이지 들어갈 자리 */ - <div> - <div className="col-md-6"> - <strong>이름</strong> - <input className="form-control" name="firstName" value={order.firstName} onChange={updateOrder} /> - </div> - <div className="col-md-6"> - <strong>성</strong> - <input className="form-control" name="lastName" value={order.lastName} onChange={updateOrder} /> - </div> - - <div className="form-group"> - <div className="col-md-12"> <strong>주소:</strong></div> - <div className="col-md-12"> - <input className="form-control" name="address" value={order.address} onChange={updateOrder} /> - </div> - </div> - - <div className="form-group"> - <div className="col-md-12"> <strong>주:</strong> - <select className="form-control" name="state" value={order.state} onChange={updateOrder}> - {states.map((st) => <option value={st}> {st}</option>)} - </select> - </div> - </div> - - <div className="form-group"> - <div className="col-md-6 boxes"> - <input type="checkbox" name="gift" id="gift" value={true} onChange={updateOrder} /> - <label htmlFor="gift">선물로 보내기?</label> - </div> - </div> - - <div className="form-group"> - <div className="col-md-9 boxes"> - <div className="col-md-3"> - <input key="radio-1" type="radio" name="method" id="home" value="자택" defaultChecked={true} onChange={updateOrder} /> - <label htmlFor="home">자택</label> - </div> - <div className="col-md-3"> - <input key="radio-2" type="radio" name="method" id="business" value="직장" onChange={updateOrder} /> - <label htmlFor="business">직장</label> - </div> - </div> - </div> - - <div className="form-group"> - <div className="col-md-12"> - <input type="submit" name="submit" className="btn btn-lg btn-primary submit" - onClick={() => { alert("제출완료") }} /> - </div> - </div> - - <div className="col-md-12 verify"> - <pre> - 이름 : {order.firstName} - <br /> - 성 : {order.lastName} - <br /> - 주소 : {order.address} - <br /> - 주 : {order.state} - <br /> - 배송지: {order.method} - <br /> - 선물 : {order.gift ? "선물" : "선물아님"} - </pre> - </div> - </div> - } + <Routes> + <Route + path="/" + element={<Products products={formattedProducts} cart={cartItems} onSetCart={setCartItems} />} + /> + <Route path="/checkout" element={<CheckoutPage products={formattedProducts} cartItems={cartItems} + onSetCartItems={setCartItems} onSetProducts={setFormattedProducts} />} /> + <Route path="/register-product" element={<ProductRegistrationPage addProduct={handleAddProduct} />} /> + </Routes> </main> - </> + </BrowserRouter> ); -} +}; -export default App; +export default App; \ No newline at end of file diff --git a/src/Checkout.js b/src/Checkout.js new file mode 100644 index 0000000000000000000000000000000000000000..2065a5f0649046bb8895e5d64c7a632c705f9c7a --- /dev/null +++ b/src/Checkout.js @@ -0,0 +1,147 @@ +import React, { useEffect, useState } from 'react'; +import { Link,useNavigate } from 'react-router-dom'; + +const CheckoutPage = ({ products, cartItems, onSetCartItems, onSetProducts }) => { + const [order, setOrder] = useState({ firstName: '', lastName: '', state: 'CA', address: '', gift: false }); + const states = ['AL', 'AR', 'CA', 'NV', 'NY', 'FL']; + + const updateOrder = (event) => { + const { name, value, type, checked } = event.target; + const newValue = type === 'checkbox' ? checked : value; + setOrder({ ...order, [name]: newValue }); + }; + + const navigate = useNavigate(); + + const [totalAmount, setTotalAmount] = useState(0); + + useEffect(() => { + let total = 0; + cartItems.forEach((item) => { + total += item.price; + }); + setTotalAmount(total); + }, [cartItems]); + + const handleDelete = (index) => { + const updatedItems = [...cartItems]; + updatedItems.splice(index, 1); + onSetCartItems(updatedItems); + }; + + const handleCancel = () => { + onSetCartItems([]); + }; + + const handleSubmit = (e) => { + e.preventDefault(); + cartItems.forEach((item) => { + let product = products.find(p => p.id == item.id); + product.availableInventory -= 1; + // const updatedProducts = products.map((product) => { + // if (product.id === item.id) { + // return { + // ...product, + // availableInventory: product.availableInventory - 1, + // }; + // } + // return product; + // }); + onSetProducts([...products]); + }); + onSetCartItems([]); + navigate('/'); + }; + + + const nItems = cartItems.length; + + return ( + <div> + <table> + <tbody> + {cartItems.map((item, i) => ( + <tr key={i}> + <td>{item.title}</td> + <td>{item.price}</td> + <td>1</td> + <td> + <button onClick={() => handleDelete(i)}>삭제</button> + </td> + </tr> + ))} + </tbody> + </table> + <div>합계:{totalAmount}</div> + + + <form> + <div className="col-md-6"> + <strong>이름</strong> + <input className="form-control" name="firstName" value={order.firstName} onChange={updateOrder} /> + </div> + <div className="col-md-6"> + <strong>성</strong> + <input className="form-control" name="lastName" value={order.lastName} onChange={updateOrder} /> + </div> + <div className="form-group"> + <div className="col-md-12"> + <strong>주소:</strong> + </div> + <div className="col-md-12"> + <input className="form-control" name="address" value={order.address} onChange={updateOrder} /> + </div> + </div> + <div className="form-group"> + <div className="col-md-12"> + <strong>주:</strong> + <select className="form-control" name="state" value={order.state} onChange={updateOrder}> + {states.map((st) => ( + <option key={st} value={st}> + {st} + </option> + ))} + </select> + </div> + </div> + <div className="form-group"> + <div className="col-md-6 boxes"> + <input type="checkbox" name="gift" id="gift" checked={order.gift} onChange={updateOrder} /> + <label htmlFor="gift">선물로 보내기?</label> + </div> + </div> + <div className="form-group"> + <div className="col-md-9 boxes"> + <div className="col-md-3"> + <input key="radio-1" type="radio" name="method" id="home" value="자택" defaultChecked={true} onChange={updateOrder} /> + <label htmlFor="home">자택</label> + </div> + <div className="col-md-3"> + <input key="radio-2" type="radio" name="method" id="business" value="직장" onChange={updateOrder} /> + <label htmlFor="business">직장</label> + </div> + </div> + </div> + </form> + <div className="form-group"> + <div className="col-md-12"> + + <button type="button" className="btn btn-default btn-lg" onClick={handleSubmit}> + <span className="glyphicon glyphicon-shopping-cart"> + {nItems || ''} + </span> + 제출 + </button> + + <Link to="/"> + <button type="button" className="btn btn-default btn-lg" onClick={handleCancel}> + 구매취소 + </button> + </Link> + </div> + </div> + </div> + ); +}; + +export default CheckoutPage; \ No newline at end of file diff --git a/src/Header.js b/src/Header.js new file mode 100644 index 0000000000000000000000000000000000000000..9fad7865ab66f26b50ad8e3d9b4a330145b8a977 --- /dev/null +++ b/src/Header.js @@ -0,0 +1,41 @@ +import React from 'react'; +import { Link, useLocation } from 'react-router-dom'; + +const Header = ({ name, nItems, products, setProducts, clearCart }) => { + const location = useLocation(); + const hasItemsInCart = nItems > 0; + const isCheckoutPage = location.pathname.startsWith('/checkout'); + const checkoutButtonText = isCheckoutPage && hasItemsInCart ? '더 사기' : '체크아웃'; + + const handleCheckoutClick = () => { + if (isCheckoutPage && hasItemsInCart) { + + } + }; + + return ( + <div className="navbar navbar-default"> + <div className="navbar-header"> + <h1>{name}</h1> + </div> + <div className="nav navbar-nav navbar-right cart"> + {nItems > 0 && ( + <Link to={isCheckoutPage ? '/' : '/checkout'}> + <button + type="button" + className="btn btn-default btn-lg" + onClick={handleCheckoutClick} + > + <span className="glyphicon glyphicon-shopping-cart"> + {nItems || ''} + </span> + {checkoutButtonText} + </button> + </Link> + )} + </div> + </div> + ); +}; + +export default Header; \ No newline at end of file diff --git a/src/Products.js b/src/Products.js new file mode 100644 index 0000000000000000000000000000000000000000..3c857a4fee39196117ecdc390ea84414f6347db6 --- /dev/null +++ b/src/Products.js @@ -0,0 +1,103 @@ +import React, { useState, useEffect } from 'react'; +import { Link } from 'react-router-dom'; + +const Products = ({ products, cart, onSetCart }) => { + + + const formatPrice = (price) => { + if (!parseInt(price)) { + return ''; + } + if (price > 99999) { + var priceString = (price / 100).toFixed(2); + var priceArray = priceString.split('').reverse(); + var index = 3; + while (priceArray.length > index + 3) { + priceArray.splice(index + 3, 0, ','); + index += 4; + } + return '$' + priceArray.reverse().join(''); + } else { + return '$' + (price / 100).toFixed(2); + } + }; + + const addToShoppingCart = (product) => { + console.log(cart); + onSetCart([...cart, product]); + }; + + const canAddToCart = (product) => { + if (product.availableInventory) { + return product.availableInventory > cart.filter((p) => p.id === product.id).length; + } + return false; + }; + + const checkRating = (n, product) => { + return product.rating - n >= 0; + }; + + const nameOrder = (p, q) => { + let [pLow, qLow] = [p.title.toLowerCase(), q.title.toLowerCase()]; + if (pLow > qLow) { + return 1; + } else if (pLow < qLow) { + return -1; + } else { + return 0; + } + }; + + return ( + <> + <div className="row product"> + <div className="col-md-12 text-center"> + <Link to="/register-product" className="btn btn-primary btn-lg btn-add-product"> + 상품추가 + </Link> + </div> + </div> + {products && + products.sort(nameOrder).map((product) => { + let nProductInCart = cart.filter((p) => p.id === product.id).length; + console.log(product.title, nProductInCart); + return ( + <div className="row product" key={product.id}> + <div className="col-md-5 col-md-offset-0"> + <figure> + <img className="product" src={product.image} alt={product.title} /> + </figure> + </div> + <div className="col-md-6 col-md-offset-0 description"> + <h1>{product.title}</h1> + <p>{product.description}</p> + <p className="price">{formatPrice(product.price)}</p> + {canAddToCart(product) && ( + <button className="btn btn-primary btn-lg" onClick={() => addToShoppingCart(product)}> + 장바구니 담기 + </button> + )} + + <span className="inventory-message"> + {product.availableInventory === nProductInCart && `품절!(Sold out)`} + {/*nProductInCart === 0 && `지금 구매하세요.`*/} + {product.availableInventory > nProductInCart && + nProductInCart >= 0 && + `${product.availableInventory - nProductInCart} 남았습니다.`} + </span> + <div className="rating"> + {[1, 2, 3, 4, 5].map((i) => ( + <span className={checkRating(i, product) ? "rating-active" : ""} key={i}> + ☆ + </span> + ))} + </div> + </div> + </div> + ); + })} + </> + ); +}; +export default Products; \ No newline at end of file diff --git a/src/RegisterProductPage.js b/src/RegisterProductPage.js new file mode 100644 index 0000000000000000000000000000000000000000..48ec8b97295c85763322a3fd43c24245e4619d87 --- /dev/null +++ b/src/RegisterProductPage.js @@ -0,0 +1,151 @@ +import React, { useState } from 'react'; +import { Link, useNavigate } from 'react-router-dom'; + +const RegisterProduct = (props) => { + const [product, setProduct] = useState({ + title: '', + description: '', + category: '', + image: '', + price: '', + availableInventory: '', + }); + const navigate = useNavigate(); // useNavigate 훅(Hook) 함수 사용 + + const handleInputChange = (event) => { + const { name, value } = event.target; + setProduct({ ...product, [name]: value }); + }; + + const handleSubmit = (event) => { + event.preventDefault(); + console.log(validateProduct()); + if (validateProduct()) { + console.log(product); + props.addProduct(product); + setProduct({ + title: '', + description: '', + category: '', + image: '', + price: '', + availableInventory: '', + }); + navigate('/'); // Product Page로 이동 + } else { + alert('상품정보를 바르게 입력하세요'); // Validate 되지 않을 때 alert 메시지 표시 + } + }; + + const validateProduct = () => { + const { title, description, category, image, price, availableInventory } = product; + if ( + title.length >= 1 && + description.length >= 3 && + category.length >= 3 && + image.length >= 3 && + Number(price) > 0 && + Number(availableInventory) > 0 + ) { + return true; + } + return false; + }; + + return ( + <div> + <h3>제품 등록</h3> + <form onSubmit={handleSubmit}> + <div className="form-group"> + <label htmlFor="title">이름:</label> + <input + type="text" + id="title" + name="title" + className="form-control" + value={product.title} + onChange={handleInputChange} + minLength={1} // 최소 길이 1 + required + /> + </div> + <div className="form-group"> + <label htmlFor="description">설명:</label> + <textarea + id="description" + name="description" + className="form-control" + value={product.description} + onChange={handleInputChange} + minLength={3} // 최소 길이 3 + required + ></textarea> + </div> + <div className="form-group"> + <label htmlFor="category">상품:</label> + <input + type="text" + id="category" + name="category" + className="form-control" + value={product.category} + onChange={handleInputChange} + minLength={3} // 최소 길이 3 + required + /> + </div> + <div className="form-group"> + <label htmlFor="image">이미지:</label> + <input + type="text" + id="image" + name="image" + className="form-control" + value={product.image} + onChange={handleInputChange} + minLength={3} // 최소 길이 3 + required + /> + </div> + <div className="form-group"> + <label htmlFor="price">가격:</label> + <input + type="number" + id="price" + name="price" + className="form-control" + value={product.price} + onChange={handleInputChange} + min={0} // 0보다 커야 함 + required + /> + </div> + <div className="form-group"> + <label htmlFor="availableInventory">재고량:</label> + <input + type="number" + id="availableInventory" + name="availableInventory" + className="form-control" + value={product.stock} + onChange={handleInputChange} + min={0} // 0보다 커야 함 + required + /> + </div> + <div> + <button type="submit" className="btn btn-primary"> + 등록하기 + </button> + <Link to="/"> + <button type="button" className="btn btn-secondary"> + 취소 + </button> + </Link> + </div> + </form> + </div> + ); +}; + +export default RegisterProduct; \ No newline at end of file diff --git a/src/assets/css/app.css b/src/assets/css/app.css new file mode 100644 index 0000000000000000000000000000000000000000..c881ca9484ce22987f147aa17014780ca3661d8a --- /dev/null +++ b/src/assets/css/app.css @@ -0,0 +1,76 @@ +header h1 { + padding: 10px 20px; +} + +body { + max-width: 970px; +} + +label { + padding: 3px; +} + +.cart { + padding: 20px 50px; +} + +.boxes { + margin-top: 20px; +} + +.verify { + margin-top: 20px; +} + +.submit { + margin-top: 20px; + float: right; +} + +.pagecheckout { + padding: 20px 50px; +} + +.description { + font-size: 150%; + margin-top: 50px; +} + +.inventory-message { + margin-left: 20px; + font-weight: bold; + font-size: 120%; +} + +.product { + margin-top: 30px; + margin-left: 20px; + max-height: 300px; + max-width: 100%; +} + +.figure {} + +.rating-active:before { + content: "\2605"; + position: absolute; +} + +.rating { + display: inline; + margin-left: 10px; + margin-top: 10px; + float: right; +} + +.rating>span { + display: inline-block; + position: relative; + width: 1.1em; +} + +@media (min-width: 1200px) { + .container { + max-width: 970px; + } +} \ No newline at end of file diff --git a/src/assets/images/Mindy_Mouse_cat_toy.jpg b/src/assets/images/Mindy_Mouse_cat_toy.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a305116c1f72e3ef410c0281f6164c502586d0e1 Binary files /dev/null and b/src/assets/images/Mindy_Mouse_cat_toy.jpg differ diff --git a/src/assets/images/cat-house.jpg b/src/assets/images/cat-house.jpg new file mode 100644 index 0000000000000000000000000000000000000000..372052afdf4a510477af54ef9ba72dc195f2a302 Binary files /dev/null and b/src/assets/images/cat-house.jpg differ diff --git a/src/assets/images/cat-litter.jpg b/src/assets/images/cat-litter.jpg new file mode 100644 index 0000000000000000000000000000000000000000..736b9a8c61399536314d6b717f29791388a9e65f Binary files /dev/null and b/src/assets/images/cat-litter.jpg differ diff --git a/src/assets/images/laser-pointer.jpg b/src/assets/images/laser-pointer.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fa5d389dce41c18205c89dba2c092eb829feacc2 Binary files /dev/null and b/src/assets/images/laser-pointer.jpg differ diff --git a/src/assets/images/yarn.jpg b/src/assets/images/yarn.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e51159071a5bee535275c79276ed445e34498f44 Binary files /dev/null and b/src/assets/images/yarn.jpg differ diff --git a/src/index.js b/src/index.js index 0d944304d2b0bce6d117c625ffd199400fde889c..2216672ed8d3e68230a00dadcad51f1664337a7c 100644 --- a/src/index.js +++ b/src/index.js @@ -7,4 +7,4 @@ var root = ReactDOM.createRoot(container); root.render(<App sitename="React PetShop"/>); -module.hot.accept() +module.hot.accept(); \ No newline at end of file