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

first

parents
Branches master
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