Skip to content
Snippets Groups Projects
Commit b9dc14c0 authored by yju's avatar yju
Browse files

first

parents
No related branches found
No related tags found
No related merge requests found
App.js 0 → 100755
import React, { useState, useEffect } from "react";
import "./assets/css/app.css";
import CheckoutPage from "./checkout";
import Products from "./products";
import Header from "./header";
import RegisterPage from "./RegisterProductPage";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { collection, getDocs, addDoc } from "firebase/firestore";
import { db } from "./firebase";
const App = (props) => {
let [cartItems, setCartItems] = useState([]);
let [products, setProducts] = useState([]);
let [user, setUser] = useState(null);
const fetchData = async () => {
/* firestore security rule change :
allow read : if request.auth != null;
allow write : if request != null;
*/
const querySnapshot = await getDocs(collection(db, "petshop"));
let products = [];
querySnapshot.forEach((doc) => {
products.push(doc.data());
});
setProducts([...products]);
console.log(products);
};
useEffect(() => {
fetchData();
}, []);
const handleDeleteItem = (index) => {
const updatedCartItems = cartItems.filter((item, i) => i !== index);
setCartItems(updatedCartItems);
};
const DeleteAllItem = () => {
setCartItems([]);
};
const updateInventory = () => {
cartItems.forEach((item) => {
console.log("item" + item);
products.forEach((product) => {
console.log("product" + product);
if (item.id === product.id) product.availableInventory -= 1;
});
});
setCartItems([]);
};
const addProduct = async (product) => {
const docRef = await addDoc(collection(db, "petshop"), product);
console.log(`document id : ${docRef.id}`);
setProducts([...products, product]);
};
return (
<BrowserRouter>
<header>
<Header
name={props.sitename}
nItems={cartItems.length}
user={user}
onSignIn={setUser}
onSignOut={() => setUser(null)}
/>
</header>
<main>
<Routes>
<Route
path="/"
element={
<Products
products={products}
cart={cartItems}
onSetCart={setCartItems}
user={user}
/>
}
></Route>
<Route
path="/checkout"
element={
<CheckoutPage
cart={cartItems}
onDeleteItem={handleDeleteItem}
deleteAll={DeleteAllItem}
updateInventory={updateInventory}
/>
}
></Route>
<Route
path="/register-product"
element={
<RegisterPage
addProduct={addProduct}
/>
}
></Route>
</Routes>
</main>
</BrowserRouter>
);
};
export default App;
import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
const RegisterProduct = (props) => {
const imageOptions = [
{ value: "assets/images/product-fullsize.png", text: "产品全尺寸图片" },
{ value: "assets/images/yarn.jpg", text: "线团图片" },
{ value: "assets/images/cat-litter.jpg", text: "猫砂图片" },
{ value: "assets/images/cat-house.jpg", text: "猫屋图片" },
{ value: "assets/images/laser-pointer.jpg", text: "激光笔图片" },
{
value: "assets/images/Mindy_Mouse_cat_toy.jpg",
text: "Mindy Mouse猫玩具图片",
},
];
let navigate = useNavigate();
const [product, setProduct] = useState({
id: 0,
title: "",
description: "",
price: 0,
image: imageOptions[0].value,
availableInventory: 0,
rating: 0,
});
const handleInputChange = (event) => {
const { name, value } = event.target;
setProduct({ ...product, [name]: value });
};
const handleFormSubmit = (event) => {
event.preventDefault();
// 验证规则
if (product.title.trim().length === 0) {
alert("상품정보를 바르게 입력하세요");
return;
}
if (product.description.length < 3) {
alert("상품정보를 바르게 입력하세요");
return;
}
if (product.price <= 0) {
alert("상품정보를 바르게 입력하세요");
return;
}
if (product.availableInventory <= 0) {
alert("상품정보를 바르게 입력하세요");
return;
}
// 构建产品对象
const productData = {
id: product.id,
title: product.title,
description: product.description,
price: product.price,
image: product.image,
availableInventory: product.availableInventory,
rating: product.rating,
};
props.addProduct(productData);
navigate(-1);
};
return (
<div>
<h3>제품 등록</h3>
<form>
<div className="form-group">
<label htmlFor="id">id:</label>
<input
type="number"
id="id"
name="id"
className="form-control"
value={product.id}
onChange={handleInputChange}
minLength={1}
required
/>
</div>
<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}
required
/>
</div>
<div className="form-group">
<label htmlFor="description">설명:</label>
<input
type="text"
id="description"
name="description"
className="form-control"
value={product.description}
onChange={handleInputChange}
minLength={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}
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}
required
/>
</div>
<div className="form-group">
<label htmlFor="availableInventory">재고량:</label>
<input
type="number"
id="availableInventory"
name="availableInventory"
className="form-control"
value={product.availableInventory}
onChange={handleInputChange}
min={0}
required
/>
</div>
<div className="form-group">
<label htmlFor="availableInventory">rating:</label>
<input
type="number"
id="rating"
name="rating"
className="form-control"
value={product.rating}
onChange={handleInputChange}
min={0}
required
/>
</div>
<div>
<button type="submit" className="btn btn-primary" onClick={handleFormSubmit}>
등록하기
</button>
<Link to="/">
<button type="button" className="btn btn-secondary">
취소
</button>
</Link>
</div>
</form>
</div>
);
};
export default RegisterProduct;
\ No newline at end of file
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;
}
}
.btn-add-product {
font-size: 30px;
padding: 3px 100px;
}
\ No newline at end of file
assets/images/product-fullsize.png

88.1 KiB

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;
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;
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
import { getFirestore } from 'firebase/firestore';
import { getAuth ,GoogleAuthProvider } from 'firebase/auth';
const firebaseConfig = {
apiKey: "AIzaSyAMGZojwvf5o32lokZ1SkF3JRFYcD2NQcQ",
authDomain: "petshop-f62e0.firebaseapp.com",
projectId: "petshop-f62e0",
storageBucket: "petshop-f62e0.appspot.com",
messagingSenderId: "305060990130",
appId: "1:305060990130:web:98797b3fbb420f080a8dab",
measurementId: "G-CCV33S1HTF"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);
export const db = getFirestore(app);
export const auth = getAuth();
export const auth_provider = new GoogleAuthProvider();
\ No newline at end of file
import React from 'react';
import { Link, useLocation } from 'react-router-dom';
import {
GoogleAuthProvider, signInWithPopup,
getAdditionalUserInfo, signOut
} from 'firebase/auth';
import { auth, auth_provider } from './firebase';
const Header = ({ name, nItems, user, onSignIn, onSignOut }) => {
const location = useLocation();
const hasItemsInCart = nItems > 0;
const isCheckoutPage = location.pathname.startsWith('/checkout');
const checkoutButtonText = isCheckoutPage && hasItemsInCart ? '더 사기' : '체크아웃';
const handleCheckoutClick = () => {
if (isCheckoutPage && hasItemsInCart) {
}
};
const handleSignIn = () => {
signInWithPopup(auth, auth_provider)
.then(res => {
const credential = GoogleAuthProvider.credentialFromResult(res);
const token = credential.accessToken;
const user = res.user;
const userInfo = getAdditionalUserInfo(res);
console.log(userInfo.profile);
onSignIn(userInfo.profile);
}).catch(error => {
console.log(`[${error.code}] ${error.message}`);
})
}
const handleSignOut = () => {
signOut(auth).then(() => {
console.log(`Bye ${user.name}`);
onSignOut();
}).catch(error => {
console.log(`[${error.code}] ${error.message}`);
})
}
return (
<div className="navbar navbar-default">
<div className="navbar-header">
<h1>{name}</h1>
</div>
<div className="nav navbar-nav navbar-right cart">
<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>
{(user) ?
<button type="button" className="btn btn-default btn-lg"
style={{ margin: '5px' }}
onClick={handleSignOut} >
<span style={{ margin: '5px' }}>
<img className="rounded-circle" src={user.picture}
style={{ width: '20px', objectFit: 'cover' }} />
</span>
{user.name} 로그아웃
</button>
:
<button type="button" className="btn btn-default btn-lg"
onClick={handleSignIn} >
로그인
</button>
}
</div>
</div>
);
};
export default Header;
index.js 0 → 100755
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()
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
const Products = ({ products, cart, onSetCart,user }) => {
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">
{user!= null&&<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;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment