Skip to content
Snippets Groups Projects
Commit 109fb541 authored by shan jiangchuan's avatar shan jiangchuan
Browse files

followed react-petshop-4

parents
No related branches found
No related tags found
No related merge requests found
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
\ No newline at end of file
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
package-lock.json
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# React PetShop - React.js로 만드는 애완용품 샵
- BootStrap 3을 사용합니다.
- [Vue.js in Action 의 프로젝트] (http://github.com/gilbutITbook/007024) 를 React.js로 변경한 코드입니다.
{
"name": "react-boilerplate",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"scripts": {
"start": "webpack-dev-server --open --mode development --port 3000",
"build": "webpack --mode production"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"devDependencies": {
"@babel/core": "^7.2.2",
"@babel/preset-env": "^7.3.1",
"@babel/preset-react": "^7.0.0",
"babel-loader": "^8.0.5",
"css-loader": "^6.7.3",
"style-loader": "^3.3.2",
"file-loader": "^6.2.0",
"webpack": "^5.77.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.13.1"
}
}
This diff is collapsed.
/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous" >
<title>React PetShop</title>
</head>
<body>
<div id="root"></div>
<script src="./app.bundle.js"></script>
</body>
</html>
{
"products":[
{
"id": 1001,
"title": "고양이 사료, 25파운드",
"description": "당신의 고양이를 위한 <em>거부할 수 없는</em>, 유기농 25파운드 사료입니다.",
"price": 2000,
"image": "assets/images/product-fullsize.png",
"availableInventory": 10,
"rating": 1
},
{
"id": 1002,
"title": "실뭉치",
"description": "실뭉치로 당신의 고양이에게 <strong>오랜</strong> 놀이 시간을 주세요!",
"price": 299,
"image": "assets/images/yarn.jpg",
"availableInventory": 7,
"rating": 1
},
{
"id": 1003,
"title": "고양이 화장실",
"description": "당신의 고양이를 위한 최고급 화장실입니다.",
"price": 1100,
"image": "assets/images/cat-litter.jpg",
"availableInventory": 99,
"rating": 4
},
{
"id": 1004,
"title": "고양이 집",
"description": "고양이가 놀 수 있는 장소!",
"price": 799,
"image": "assets/images/cat-house.jpg",
"availableInventory": 11,
"rating": 5
},
{
"id": 1005,
"title": "레이저 포인터",
"description": "이 <em>놀라운</em> 상품으로 고양이와 놀아주세요.",
"price": 4999,
"image": "assets/images/laser-pointer.jpg",
"availableInventory": 25,
"rating": 1
}
]
}
import React, { useState, useEffect } from 'react';
import './assets/css/app.css';
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([]);
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,
};
*/
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 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;
}
}
return (
<>
<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>
<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>
}
</main>
</>
);
}
export default App;
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
var container = document.getElementById('root');
var root = ReactDOM.createRoot(container);
root.render(<App sitename="React PetShop"/>);
module.hot.accept()
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
app: './src/index.js'
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'public')
},
module: {
rules: [{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|jpe?g|gif)$/i,
loader: 'file-loader',
}]
},
devServer: {
static: {
directory: path.join(__dirname, 'public'),
},
allowedHosts: 'all',
},
plugins: [
new webpack.HotModuleReplacementPlugin()
],
watchOptions: {
ignored: '**/node_modules',
},
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment