박민정

[update] Fix countless error.. and admin page is deleted

Showing 103 changed files with 2227 additions and 477 deletions
This diff could not be displayed because it is too large.
...@@ -4,21 +4,25 @@ ...@@ -4,21 +4,25 @@
4 "private": true, 4 "private": true,
5 "dependencies": { 5 "dependencies": {
6 "antd": "^3.24.1", 6 "antd": "^3.24.1",
7 - "axios": "^0.19.2", 7 + "axios": "^0.18.0",
8 - "core-js": "^3.6.4", 8 + "bootstrap": "^4.6.0",
9 "formik": "^1.5.8", 9 "formik": "^1.5.8",
10 "moment": "^2.24.0", 10 "moment": "^2.24.0",
11 "react": "^16.8.6", 11 "react": "^16.8.6",
12 - "react-app-polyfill": "^1.0.6", 12 + "react-bootstrap": "^1.6.1",
13 "react-dom": "^16.8.6", 13 "react-dom": "^16.8.6",
14 - "react-dropzone": "^11.3.2", 14 + "react-dropzone": "^10.2.1",
15 "react-icons": "^3.7.0", 15 "react-icons": "^3.7.0",
16 + "react-image-gallery": "^1.0.3",
17 + "react-paypal-express-checkout": "^1.0.5",
16 "react-redux": "^7.1.0-rc.1", 18 "react-redux": "^7.1.0-rc.1",
17 "react-router-dom": "^5.0.1", 19 "react-router-dom": "^5.0.1",
18 - "react-scripts": "3.4.1", 20 + "react-scripts": "3.0.1",
19 "redux": "^4.0.0", 21 "redux": "^4.0.0",
22 + "redux-form": "^8.2.6",
20 "redux-promise": "^0.6.0", 23 "redux-promise": "^0.6.0",
21 "redux-thunk": "^2.3.0", 24 "redux-thunk": "^2.3.0",
25 + "socket.io-client": "^2.2.0",
22 "yup": "^0.27.0" 26 "yup": "^0.27.0"
23 }, 27 },
24 "scripts": { 28 "scripts": {
......
...@@ -19,7 +19,13 @@ ...@@ -19,7 +19,13 @@
19 work correctly both with client-side routing and a non-root public URL. 19 work correctly both with client-side routing and a non-root public URL.
20 Learn how to configure a non-root public URL by running `npm run build`. 20 Learn how to configure a non-root public URL by running `npm run build`.
21 --> 21 -->
22 - <title>React App</title> 22 + <link
23 + rel="stylesheet"
24 + href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css"
25 + integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x"
26 + crossorigin="anonymous"
27 + />
28 + <title>약 배달 서비스 : 약사</title>
23 </head> 29 </head>
24 <body> 30 <body>
25 <noscript>You need to enable JavaScript to run this app.</noscript> 31 <noscript>You need to enable JavaScript to run this app.</noscript>
......
1 -// type들만 관리하는 곳
2 -
3 export const LOGIN_USER = 'login_user'; 1 export const LOGIN_USER = 'login_user';
4 export const REGISTER_USER = 'register_user'; 2 export const REGISTER_USER = 'register_user';
5 export const AUTH_USER = 'auth_user'; 3 export const AUTH_USER = 'auth_user';
6 export const LOGOUT_USER = 'logout_user'; 4 export const LOGOUT_USER = 'logout_user';
5 +export const ADD_TO_CART_USER = 'add_to_cart_user';
6 +export const GET_CART_ITEMS_USER = 'get_cart_items_user';
7 +export const REMOVE_CART_ITEM_USER = 'remove_cart_item_user';
8 +export const ON_SUCCESS_BUY_USER = 'on_success_buy_user';
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -4,38 +4,36 @@ import { ...@@ -4,38 +4,36 @@ import {
4 REGISTER_USER, 4 REGISTER_USER,
5 AUTH_USER, 5 AUTH_USER,
6 LOGOUT_USER, 6 LOGOUT_USER,
7 + ADD_TO_CART_USER,
8 + GET_CART_ITEMS_USER,
9 + REMOVE_CART_ITEM_USER,
10 + ON_SUCCESS_BUY_USER
7 } from './types'; 11 } from './types';
8 import { USER_SERVER } from '../components/Config.js'; 12 import { USER_SERVER } from '../components/Config.js';
9 13
10 -export function loginUser(logInfo){ 14 +export function registerUser(dataToSubmit) {
11 - const request = axios.post(`${USER_SERVER}/login`,logInfo)// logInfo를 post로 전달 15 + const request = axios.post(`${USER_SERVER}/register`, dataToSubmit)
12 - .then(response => response.data); // 서버에서 받은 데이터를 request에 저장
13 -
14 -return { // return을 통해 Reducer로 보냄
15 - // Reducer에서 previousState, action을 이용해 nextState로 만들기 때문 :: (previousState, action) => nextState
16 - // request를 reducer로 보내는 작업
17 -
18 - // action은 type과 response을 넣어줘야 함
19 - type: LOGIN_USER,
20 - payload: request // payroad == response
21 -}
22 -}
23 -
24 -export function registerUser(dataToSubmit){
25 - const request = axios.post(`${USER_SERVER}/register`,dataToSubmit)
26 .then(response => response.data); 16 .then(response => response.data);
27 - 17 +
28 return { 18 return {
29 type: REGISTER_USER, 19 type: REGISTER_USER,
30 payload: request 20 payload: request
31 } 21 }
32 } 22 }
33 23
24 +export function loginUser(dataToSubmit) {
25 + const request = axios.post(`${USER_SERVER}/login`, dataToSubmit)
26 + .then(response => response.data);
34 27
28 + return {
29 + type: LOGIN_USER,
30 + payload: request
31 + }
32 +}
35 33
36 -export function auth(){ 34 +export function auth() {
37 const request = axios.get(`${USER_SERVER}/auth`) 35 const request = axios.get(`${USER_SERVER}/auth`)
38 - .then(response => response.data); 36 + .then(response => response.data);
39 37
40 return { 38 return {
41 type: AUTH_USER, 39 type: AUTH_USER,
...@@ -43,9 +41,9 @@ export function auth(){ ...@@ -43,9 +41,9 @@ export function auth(){
43 } 41 }
44 } 42 }
45 43
46 -export function logoutUser(){ 44 +export function logoutUser() {
47 const request = axios.get(`${USER_SERVER}/logout`) 45 const request = axios.get(`${USER_SERVER}/logout`)
48 - .then(response => response.data); 46 + .then(response => response.data);
49 47
50 return { 48 return {
51 type: LOGOUT_USER, 49 type: LOGOUT_USER,
...@@ -53,3 +51,80 @@ export function logoutUser(){ ...@@ -53,3 +51,80 @@ export function logoutUser(){
53 } 51 }
54 } 52 }
55 53
54 +
55 +export function addToCart(_id) {
56 + const request = axios.get(`${USER_SERVER}/addToCart?productId=${_id}`)
57 + .then(response => response.data);
58 +
59 + return {
60 + type: ADD_TO_CART_USER,
61 + payload: request
62 + }
63 +}
64 +
65 +
66 +
67 +export function getCartItems(cartItems, userCart) {
68 + const request = axios.get(`/api/product/products_by_id?id=${cartItems}&type=array`)
69 + .then(response => {
70 +
71 +
72 + //Make CartDetail inside Redux Store
73 + // We need to add quantity data to Product Information that come from Product Collection.
74 +
75 + userCart.forEach(cartItem => {
76 + response.data.forEach((productDetail, i) => {
77 + if (cartItem.id === productDetail._id) {
78 + response.data[i].quantity = cartItem.quantity;
79 + }
80 + })
81 + })
82 +
83 + return response.data;
84 + });
85 +
86 + return {
87 + type: GET_CART_ITEMS_USER,
88 + payload: request
89 + }
90 +}
91 +
92 +
93 +
94 +
95 +export function removeCartItem(id) {
96 + const request = axios.get(`/api/users/removeFromCart?_id=${id}`)
97 + .then(response => {
98 +
99 + response.data.cart.forEach(item => {
100 + response.data.cartDetail.forEach((k, i) => {
101 + if (item.id === k._id) {
102 + response.data.cartDetail[i].quantity = item.quantity
103 + }
104 + })
105 + })
106 + return response.data;
107 + });
108 +
109 + return {
110 + type: REMOVE_CART_ITEM_USER,
111 + payload: request
112 + }
113 +}
114 +
115 +
116 +export function onSuccessBuy(data) {
117 +
118 + const request = axios.post(`${USER_SERVER}/successBuy`, data)
119 + .then(response => response.data);
120 +
121 + return {
122 + type: ON_SUCCESS_BUY_USER,
123 + payload: request
124 + }
125 +}
126 +
127 +
128 +
129 +
130 +
......
1 -import { combineReducers } from 'redux'; 1 +import { combineReducers } from "redux";
2 -import user from './user_reducer'; 2 +import user from "./user_reducer";
3 3
4 const rootReducer = combineReducers({ 4 const rootReducer = combineReducers({
5 - user, 5 + user,
6 }); 6 });
7 7
8 -export default rootReducer;
...\ No newline at end of file ...\ No newline at end of file
8 +export default rootReducer;
......
...@@ -3,19 +3,54 @@ import { ...@@ -3,19 +3,54 @@ import {
3 REGISTER_USER, 3 REGISTER_USER,
4 AUTH_USER, 4 AUTH_USER,
5 LOGOUT_USER, 5 LOGOUT_USER,
6 + ADD_TO_CART_USER,
7 + GET_CART_ITEMS_USER,
8 + REMOVE_CART_ITEM_USER,
9 + ON_SUCCESS_BUY_USER
6 } from '../_actions/types'; 10 } from '../_actions/types';
7 -
8 11
9 -export default function(state={},action){ 12 +
10 - switch(action.type){ 13 +export default function (state = {}, action) {
14 + switch (action.type) {
11 case REGISTER_USER: 15 case REGISTER_USER:
12 - return {...state, register: action.payload } 16 + return { ...state, register: action.payload }
13 case LOGIN_USER: 17 case LOGIN_USER:
14 return { ...state, loginSucces: action.payload } 18 return { ...state, loginSucces: action.payload }
15 case AUTH_USER: 19 case AUTH_USER:
16 - return {...state, userData: action.payload } 20 + return { ...state, userData: action.payload }
17 case LOGOUT_USER: 21 case LOGOUT_USER:
18 - return {...state } 22 + return { ...state }
23 + case ADD_TO_CART_USER:
24 + return {
25 + ...state, userData: {
26 + ...state.userData,
27 + cart: action.payload
28 + }
29 + }
30 + case GET_CART_ITEMS_USER:
31 + return {
32 + ...state, cartDetail: action.payload
33 + }
34 + case REMOVE_CART_ITEM_USER:
35 + return {
36 + ...state,
37 + cartDetail: action.payload.cartDetail,
38 + userData: {
39 + ...state.userData,
40 + cart: action.payload.cart
41 + }
42 +
43 + }
44 + case ON_SUCCESS_BUY_USER:
45 + return {
46 + ...state,
47 + userData: {
48 + ...state.userData,
49 + cart: action.payload.cart
50 + },
51 + cartDetail: action.payload.cartDetail
52 + }
53 +
19 default: 54 default:
20 return state; 55 return state;
21 } 56 }
......
1 -import React, { Suspense } from 'react'; 1 +import React, { Suspense } from "react";
2 import { Route, Switch } from "react-router-dom"; 2 import { Route, Switch } from "react-router-dom";
3 import Auth from "../hoc/auth"; 3 import Auth from "../hoc/auth";
4 // pages for this product 4 // pages for this product
5 import LandingPage from "./views/LandingPage/LandingPage.js"; 5 import LandingPage from "./views/LandingPage/LandingPage.js";
6 import LoginPage from "./views/LoginPage/LoginPage.js"; 6 import LoginPage from "./views/LoginPage/LoginPage.js";
7 import RegisterPage from "./views/RegisterPage/RegisterPage.js"; 7 import RegisterPage from "./views/RegisterPage/RegisterPage.js";
8 -import UploadPage from './views/UploadPage/UploadPage';
9 import NavBar from "./views/NavBar/NavBar"; 8 import NavBar from "./views/NavBar/NavBar";
10 -import Footer from "./views/Footer/Footer" 9 +import Footer from "./views/Footer/Footer";
11 - 10 +import UploadProductPage from "./views/UploadProductPage/UploadProductPage";
12 -//null Anyone Can go inside 11 +import DetailProductPage from "./views/DetailProductPage/DetailProductPage";
13 -//true only logged in user can go inside 12 +import CartPage from "./views/CartPage/CartPage";
14 -//false logged in user can't go inside 13 +import HistoryPage from "./views/HistoryPage/HistoryPage";
14 +import adminPage from "./views/adminPage/adminPage";
15 15
16 function App() { 16 function App() {
17 return ( 17 return (
18 - <Suspense fallback={(<div>Loading...</div>)}> 18 + <Suspense fallback={<div>Loading...</div>}>
19 <NavBar /> 19 <NavBar />
20 - <div style={{ paddingTop: '69px', minHeight: 'calc(100vh - 80px)' }}> 20 + <div style={{ paddingTop: "75px", minHeight: "calc(100vh - 80px)" }}>
21 <Switch> 21 <Switch>
22 <Route exact path="/" component={Auth(LandingPage, null)} /> 22 <Route exact path="/" component={Auth(LandingPage, null)} />
23 <Route exact path="/login" component={Auth(LoginPage, false)} /> 23 <Route exact path="/login" component={Auth(LoginPage, false)} />
24 <Route exact path="/register" component={Auth(RegisterPage, false)} /> 24 <Route exact path="/register" component={Auth(RegisterPage, false)} />
25 - <Route exact path="/upload" component={Auth(UploadPage, true)} /> 25 + <Route exact path="/product/upload" component={Auth(UploadProductPage, true)} />
26 + <Route exact path="/product/:productId" component={Auth(DetailProductPage, null)} />
27 + <Route exact path="/user/cart" component={Auth(CartPage, true)} />
28 + <Route exact path="/history" component={Auth(HistoryPage, true)} />
29 + <Route exact path="/admin" component={Auth(adminPage, true)} />
26 </Switch> 30 </Switch>
27 </div> 31 </div>
28 <Footer /> 32 <Footer />
......
1 +#test {
2 + width: 300px;
3 + height: 200px;
4 + border: 1px solid lightgray;
5 + border-radius: 1em;
6 + display: flex;
7 + position: relative;
8 +}
9 +#alert {
10 + width: 300px;
11 + height: 200px;
12 + border: 1px solid lightgray;
13 + border-radius: 1em;
14 + display: flex;
15 + justify-content: center;
16 + align-items: center;
17 +}
18 +
19 +#test:hover {
20 + animation: del 0.2s forwards;
21 +}
22 +
23 +@keyframes del {
24 + to {
25 + filter: brightness(50%);
26 + }
27 +}
28 +
29 +#alert {
30 + position: relative;
31 + bottom: 200px;
32 + opacity: 0;
33 +}
34 +
35 +#alert:hover {
36 + background-color: black;
37 + color: white;
38 + animation: fadeInUP 1s forwards;
39 +}
40 +
41 +@keyframes fadeInUP {
42 + to {
43 + opacity: 0.5;
44 + }
45 +}
1 +import React, { useState } from "react";
2 +import Dropzone from "react-dropzone";
3 +import { Icon } from "antd";
4 +import Axios from "axios";
5 +import "./FileUpload.css";
6 +
7 +function FileUpload(props) {
8 + const [Images, setImages] = useState([]);
9 +
10 + const onDrop = files => {
11 + let formData = new FormData();
12 + const config = {
13 + header: { "content-type": "multipart/form-data" },
14 + };
15 + formData.append("file", files[0]);
16 + Axios.post("/api/product/uploadImage", formData, config).then(response => {
17 + if (response.data.success) {
18 + setImages([...Images, response.data.image]);
19 + props.refreshFunction([...Images, response.data.image]);
20 + } else {
21 + alert("Failed to save the Image in Server");
22 + }
23 + });
24 + };
25 +
26 + const onDelete = image => {
27 + const currentIndex = Images.indexOf(image);
28 +
29 + let newImages = [...Images];
30 + newImages.splice(currentIndex, 1);
31 +
32 + setImages(newImages);
33 + props.refreshFunction(newImages);
34 + };
35 +
36 + return (
37 + <div style={{ display: "flex", justifyContent: "space-between" }}>
38 + <Dropzone onDrop={onDrop} multiple={false} maxSize={800000000}>
39 + {({ getRootProps, getInputProps }) => (
40 + <div
41 + style={{
42 + width: "300px",
43 + height: "240px",
44 + border: "1px solid lightgray",
45 + borderRadius: '15px',
46 + display: "flex",
47 + alignItems: "center",
48 + justifyContent: "center",
49 + }}
50 + {...getRootProps()}
51 + >
52 + {console.log("getRootProps", { ...getRootProps() })}
53 + {console.log("getInputProps", { ...getInputProps() })}
54 + <input {...getInputProps()} />
55 + <Icon type="plus" style={{ fontSize: "3rem" }} />
56 + </div>
57 + )}
58 + </Dropzone>
59 +
60 + <div style={{ display: "flex", width: "350px", height: "240px", overflowX: "scroll" }}>
61 + {Images.map((image, index) => (
62 + <div onClick={() => onDelete(image)}>
63 + <img id="test" src={`http://localhost:5000/${image}`} />
64 + <div id="alert">클릭하면 삭제돼요!</div>
65 + </div>
66 + ))}
67 + </div>
68 + </div>
69 + );
70 +}
71 +
72 +export default FileUpload;
1 +import React from "react";
2 +import { Carousel } from "antd";
3 +
4 +function ImageSlider(props) {
5 + return (
6 + <div>
7 + <Carousel autoplay>
8 + {props.images.map((image, index) => (
9 + <div key={index}>
10 + <img style={{ width: "100%", maxWidth: "300px", height: "300px" }} src={`http://localhost:5000/${image}`} alt="productImage" />
11 + </div>
12 + ))}
13 + </Carousel>
14 + </div>
15 + );
16 +}
17 +
18 +export default ImageSlider;
1 +#test {
2 + width: 300px;
3 + height: 200px;
4 + border: 1px solid rgb(155, 155, 155);
5 + border-radius: 1em;
6 + display: flex;
7 + position: relative;
8 +}
9 +#alert {
10 + width: 300px;
11 + height: 200px;
12 + border: 1px solid rgb(155, 155, 155);
13 + border-radius: 1em;
14 + display: flex;
15 + justify-content: center;
16 + align-items: center;
17 +}
18 +
19 +#test:hover {
20 + animation: del 0.7s forwards;
21 +}
22 +
23 +@keyframes del {
24 + to {
25 + filter: brightness(50%);
26 + }
27 +}
28 +
29 +#alert {
30 + position: relative;
31 + bottom: 200px;
32 + opacity: 0;
33 +}
34 +
35 +#alert:hover {
36 + background-color: black;
37 + color: white;
38 + animation: fadeInUP 0.2s forwards;
39 +}
40 +
41 +@keyframes fadeInUP {
42 + to {
43 + opacity: 0.5;
44 + }
45 +}
...@@ -2,6 +2,7 @@ import React from 'react' ...@@ -2,6 +2,7 @@ import React from 'react'
2 import Dropzone from 'react-dropzone' 2 import Dropzone from 'react-dropzone'
3 import axios from 'axios'; 3 import axios from 'axios';
4 import { useState } from 'react'; 4 import { useState } from 'react';
5 +import './ImageUpload.css'
5 6
6 function ImageUpload(props) { 7 function ImageUpload(props) {
7 8
...@@ -51,13 +52,14 @@ function ImageUpload(props) { ...@@ -51,13 +52,14 @@ function ImageUpload(props) {
51 <Dropzone onDrop={imageDropEvent}> 52 <Dropzone onDrop={imageDropEvent}>
52 {({getRootProps, getInputProps}) => ( 53 {({getRootProps, getInputProps}) => (
53 <section> 54 <section>
55 + {/* Dropzone */}
54 <div style={{ 56 <div style={{
55 width: 300, height: 200, border: '1px solid lightgray', borderRadius: '1em', display: 'flex', 57 width: 300, height: 200, border: '1px solid lightgray', borderRadius: '1em', display: 'flex',
56 alignItems: 'center', textAlign: 'center', justifyContent: 'center' 58 alignItems: 'center', textAlign: 'center', justifyContent: 'center'
57 }} 59 }}
58 {...getRootProps()}> 60 {...getRootProps()}>
59 <input {...getInputProps()} /> 61 <input {...getInputProps()} />
60 - <p>이곳을 클릭하여 <br/> 상품 사진을 업로드 해주세요.</p> 62 + <p>이곳을 클릭하여<br/>상품 사진을 업로드 해주세요.</p>
61 </div> 63 </div>
62 </section> 64 </section>
63 )} 65 )}
...@@ -72,11 +74,10 @@ function ImageUpload(props) { ...@@ -72,11 +74,10 @@ function ImageUpload(props) {
72 {Images.map((image, index) => ( 74 {Images.map((image, index) => (
73 75
74 <div onClick={ () => deleteEvent(image) } 76 <div onClick={ () => deleteEvent(image) }
75 - key={index}> 77 + key={index}>
76 - <img style={{ 78 + <img id="test" src={`http://localhost:5000/${image}`} />
77 - width: '300px', height: '200px', border: '1px solid lightgray', 79 + <div id="alert">클릭하면 삭제돼요!</div>
78 - borderRadius: '1em', display: 'flex'}} 80 +
79 - src={`http://localhost:5000/${image}`} />
80 </div> 81 </div>
81 ))} 82 ))}
82 </div> 83 </div>
......
1 +import React from 'react';
2 +import PaypalExpressBtn from 'react-paypal-express-checkout';
3 +
4 +export default class Paypal extends React.Component {
5 + render() {
6 + const onSuccess = (payment) => {
7 + // Congratulation, it came here means everything's fine!
8 + console.log("The payment was succeeded!", payment);
9 + // You can bind the "payment" object's value to your state or props or whatever here, please see below for sample returned data
10 + this.props.onSuccess(payment);
11 + }
12 +
13 + const onCancel = (data) => {
14 + // User pressed "cancel" or close Paypal's popup!
15 + console.log('The payment was cancelled!', data);
16 + // You can bind the "data" object's value to your state or props or whatever here, please see below for sample returned data
17 + }
18 +
19 + const onError = (err) => {
20 + // The main Paypal's script cannot be loaded or somethings block the loading of that script!
21 + console.log("Error!", err);
22 + // Because the Paypal's main script is loaded asynchronously from "https://www.paypalobjects.com/api/checkout.js"
23 + // => sometimes it may take about 0.5 second for everything to get set, or for the button to appear
24 + }
25 +
26 + let env = 'sandbox'; // you can set here to 'production' for production
27 + let currency = 'USD'; // or you can set this value from your props or state
28 + let total = this.props.toPay; // same as above, this is the total amount (based on currency) to be paid by using Paypal express checkout
29 + // Document on Paypal's currency code: https://developer.paypal.com/docs/classic/api/currency_codes/
30 +
31 + const client = {
32 + sandbox: 'ASbCsyjZeUzpNCkVbbqseQzcXivFRRoyPfpJK24688vFvIchTR-CCK79Ao5FB6zgqIO2r5Xw-a4Xh-44',
33 + production: 'YOUR-PRODUCTION-APP-ID',
34 + }
35 + // In order to get production's app-ID, you will have to send your app to Paypal for approval first
36 + // For sandbox app-ID (after logging into your developer account, please locate the "REST API apps" section, click "Create App"):
37 + // => https://developer.paypal.com/docs/classic/lifecycle/sb_credentials/
38 + // For production app-ID:
39 + // => https://developer.paypal.com/docs/classic/lifecycle/goingLive/
40 +
41 + // NB. You can also have many Paypal express checkout buttons on page, just pass in the correct amount and they will work!
42 + return (
43 + <PaypalExpressBtn
44 + env={env}
45 + client={client}
46 + currency={currency}
47 + total={total}
48 + onError={onError}
49 + onSuccess={onSuccess}
50 + onCancel={onCancel}
51 + style={{
52 + size:'large',
53 + color:'blue',
54 + shape: 'rect',
55 + label: 'checkout'
56 + }}
57 + />
58 + );
59 + }
60 +}
...\ No newline at end of file ...\ No newline at end of file
1 +import React, { useEffect, useState } from "react";
2 +import { useDispatch } from "react-redux";
3 +import { getCartItems, removeCartItem, onSuccessBuy } from "../../../_actions/user_actions";
4 +import UserCardBlock from "./Sections/UserCardBlock";
5 +import { Result, Empty } from "antd";
6 +import Axios from "axios";
7 +import Paypal from "../../utils/Paypal";
8 +import { Container } from "react-bootstrap";
9 +
10 +function CartPage(props) {
11 + const dispatch = useDispatch();
12 + const [Total, setTotal] = useState(0);
13 + const [ShowTotal, setShowTotal] = useState(false);
14 + const [ShowSuccess, setShowSuccess] = useState(false);
15 +
16 + useEffect(() => {
17 + let cartItems = [];
18 + if (props.user.userData && props.user.userData.cart) {
19 + if (props.user.userData.cart.length > 0) {
20 + props.user.userData.cart.forEach(item => {
21 + cartItems.push(item.id);
22 + });
23 + dispatch(getCartItems(cartItems, props.user.userData.cart)).then(response => {
24 + if (response.payload.length > 0) {
25 + calculateTotal(response.payload);
26 + }
27 + });
28 + }
29 + }
30 + }, [props.user.userData]);
31 +
32 + const calculateTotal = cartDetail => {
33 + let total = 0;
34 +
35 + cartDetail.map(item => {
36 + total += parseInt(item.price, 10) * item.quantity;
37 + });
38 +
39 + setTotal(total);
40 + setShowTotal(true);
41 + };
42 +
43 + const removeFromCart = productId => {
44 + dispatch(removeCartItem(productId)).then(response => {
45 + if (response.payload.cartDetail.length <= 0) {
46 + setShowTotal(false);
47 + } else {
48 + calculateTotal(response.payload.cartDetail);
49 + }
50 + });
51 + };
52 +
53 + const transactionSuccess = data => {
54 + dispatch(
55 + onSuccessBuy({
56 + cartDetail: props.user.cartDetail,
57 + paymentData: data,
58 + })
59 + ).then(response => {
60 + if (response.payload.success) {
61 + setShowSuccess(true);
62 + setShowTotal(false);
63 + }
64 + });
65 + };
66 +
67 + const transactionError = () => {
68 + console.log("Paypal error");
69 + };
70 +
71 + const transactionCanceled = () => {
72 + console.log("Transaction canceled");
73 + };
74 +
75 + return (
76 + <div style={{ width: "85%", margin: "3rem auto" }}>
77 + <h1>장바구니</h1>
78 + <div style={{ marginTop: "30px" }}>
79 + <UserCardBlock products={props.user.cartDetail} removeItem={removeFromCart} />
80 + {ShowTotal ? (
81 + <div style={{ marginTop: "3rem", textAlign: "right" }}>
82 + <h2> 금액: ${Total} </h2>
83 + </div>
84 + ) : ShowSuccess ? (
85 + <Result status="success" title="Successfully Purchased Items" />
86 + ) : (
87 + <div
88 + style={{
89 + width: "100%",
90 + display: "flex",
91 + flexDirection: "column",
92 + justifyContent: "center",
93 + }}
94 + >
95 + <br />
96 + <Empty description={false} />
97 + <p>장바구니가 비었습니다.</p>
98 + </div>
99 + )}
100 + </div>
101 +
102 + {ShowTotal && (
103 + <Container style={{ width: "max-content", marginTop: "100px" }}>
104 + <Paypal sty toPay={Total} onSuccess={transactionSuccess} transactionError={transactionError} transactionCanceled={transactionCanceled} />
105 + </Container>
106 + )}
107 + </div>
108 + );
109 +}
110 +
111 +export default CartPage;
1 +import React from "react";
2 +
3 +function UserCardBlock(props) {
4 + const renderCartImage = images => {
5 + if (images.length > 0) {
6 + let image = images[0];
7 + return `http://localhost:5000/${image}`;
8 + }
9 + };
10 +
11 + const renderItems = () =>
12 + props.products &&
13 + props.products.map(product => (
14 + <tr key={product._id} style={{ margin: "auto", backgroundColor: "white" }}>
15 + <td style={{ margin: "auto", textAlign: "center" }}>
16 + <img style={{ width: "70px" }} alt="product" src={renderCartImage(product.images)} />
17 + </td>
18 + <td style={{ margin: "auto", textAlign: "center" }}>{product.title}</td>
19 + <td style={{ margin: "auto", textAlign: "center" }}>{product.quantity} EA</td>
20 + <td style={{ margin: "auto", textAlign: "center" }}>$ {product.price} </td>
21 + <td style={{ margin: "auto", textAlign: "center" }}>
22 + <button onClick={() => props.removeItem(product._id)}></button>
23 + </td>
24 + </tr>
25 + ));
26 +
27 + return (
28 + <div>
29 + <table>
30 + <thead>
31 + <tr style={{ textAlign: "center" }}>
32 + <th>상품 이미지</th>
33 + <th>상품명</th>
34 + <th>개수</th>
35 + <th>가격</th>
36 + <th>장바구니에서 삭제하기</th>
37 + </tr>
38 + </thead>
39 + <tbody>{renderItems()}</tbody>
40 + </table>
41 + </div>
42 + );
43 +}
44 +
45 +export default UserCardBlock;
1 +import React, { useEffect, useState } from 'react'
2 +import Axios from 'axios'
3 +import { Row, Col } from 'antd';
4 +import ProductImage from './Sections/ProductImage';
5 +import ProductInfo from './Sections/ProductInfo';
6 +import { addToCart } from '../../../_actions/user_actions';
7 +import { useDispatch } from 'react-redux';
8 +function DetailProductPage(props) {
9 + const dispatch = useDispatch();
10 + const productId = props.match.params.productId
11 + const [Product, setProduct] = useState([])
12 +
13 + useEffect(() => {
14 + Axios.get(`/api/product/products_by_id?id=${productId}&type=single`)
15 + .then(response => {
16 + setProduct(response.data[0])
17 + })
18 +
19 + }, [])
20 +
21 + const addToCartHandler = (productId) => {
22 + dispatch(addToCart(productId))
23 +
24 + }
25 +
26 + return (
27 + <div className="postPage" style={{ width: '100%', padding: '3rem 4rem' }}>
28 +
29 + <div style={{ display: 'flex', justifyContent: 'center' }}>
30 + <h1>{Product.title}</h1>
31 + </div>
32 +
33 + <br />
34 +
35 + <Row gutter={[16, 16]} >
36 + <Col lg={12} xs={24}>
37 + <ProductImage detail={Product} style={{width :"300px"}} />
38 + </Col>
39 + <Col lg={12} xs={24}>
40 + <ProductInfo
41 + addToCart={addToCartHandler}
42 + detail={Product} />
43 + </Col>
44 + </Row>
45 + </div>
46 + )
47 +}
48 +
49 +export default DetailProductPage
1 +import React, { useEffect, useState } from 'react'
2 +import ImageGallery from 'react-image-gallery';
3 +
4 +function ProductImage(props) {
5 + const [Images, setImages] = useState([])
6 +
7 + useEffect(() => {
8 + if (props.detail.images && props.detail.images.length > 0) {
9 + let images = [];
10 +
11 + props.detail.images && props.detail.images.map(item => {
12 + images.push({
13 + original: `http://localhost:5000/${item}`,
14 + thumbnail: `http://localhost:5000/${item}`
15 + })
16 + })
17 + setImages(images)
18 + }
19 + }, [props.detail])
20 +
21 + return (
22 + <div>
23 + <ImageGallery showFullscreenButton={false} showPlayButton={false} items={Images} />
24 + </div>
25 + )
26 +}
27 +
28 +export default ProductImage
1 +import React, { useEffect, useState } from "react";
2 +import { Button, Descriptions } from "antd";
3 +import { Container } from "react-bootstrap";
4 +
5 +function ProductInfo(props) {
6 + const [Product, setProduct] = useState({});
7 +
8 + useEffect(() => {
9 + setProduct(props.detail);
10 + }, [props.detail]);
11 +
12 + const addToCarthandler = () => {
13 + props.addToCart(props.detail._id);
14 + };
15 +
16 + return (
17 +
18 + <Container style={{ paddingTop: "100px" }}>
19 + <Container style={{ textAlign: "center" }}>
20 + {/* <h7>{Product.description}</h7><br/> */}
21 + <h3>가격 : {Product.price}000 </h3>
22 + </Container>
23 +
24 + <br />
25 + <br />
26 + <div style={{ display: "flex", justifyContent: "center" }}>
27 + <Button size="large" shape="round" type="danger" onClick={addToCarthandler}>
28 + <a href="/user/cart">
29 + 장바구니에 담기
30 + </a>
31 + </Button>
32 + </div>
33 + </Container>
34 + );
35 +}
36 +
37 +export default ProductInfo;
1 +import React from 'react'
2 +import axios from 'axios'
3 +import {useEffect, useState} from 'react'
4 +import ProductImage from './Sections/ProductImage'
5 +import ProductInfo from './Sections/ProductInfo'
6 +import { Row, Col } from 'antd';
7 +function DetailProductPages(props) {
8 + const [Product, setProduct] = useState({})
9 + const productId = props.match.params.prouductID
10 + //console.log(props.match.params.prouductID) //->정상적으로 출력
11 +
12 + useEffect(() => {
13 + axios.get(`/api/product/products_by_id?id=${productId}&type=single`)
14 + .then(response => {
15 + if (response.data.success) {
16 + console.log(response.data)
17 + setProduct(response.data.goods[0]);
18 + }
19 + else {
20 + alert('Fail.');
21 + }
22 + })
23 + },[])
24 +
25 + return (
26 + <div style={{width:'100%', padding:'3rem 4rem'}}>
27 + <div style={{ display: 'flex', justifyContent: 'center' }}>
28 + <h1>{Product.title}</h1>
29 + </div>
30 +
31 + <br />
32 + <Row gutter={[16, 16]}>
33 + <Col lg={12} sm={24}>
34 + <ProductImage detail={Product} />
35 + </Col>
36 + <Col lg={12} sm={24}>
37 + <ProductImage />
38 + </Col>
39 + </Row>
40 + {/* image */}
41 +
42 +
43 + {/* info */}
44 + <ProductInfo />
45 + </div>
46 + )
47 +}
48 +
49 +export default DetailProductPages
1 +import React from 'react'
2 +import ImageGallery from 'react-image-gallery'
3 +import { useEffect, useState } from 'react'
4 +
5 +function ProductImage(props) {
6 +
7 + const [Images, setImages] = useState([])
8 +
9 +
10 +
11 + useEffect(() => {
12 + console.log('now ::::::: ', props.goods)
13 + let images = []
14 + if (props.goods.images && props.goods.images.length > 0) {
15 +
16 + props.goods.images.map(item => {
17 + images.push({
18 + original: `http://localhost:5000/${item}`,
19 + thumbnail: `http://localhost:5000/${item}`
20 + })
21 + })
22 + setImages(images)
23 + }
24 + }, [props.goods])
25 + return (
26 + <div>
27 + <ImageGallery items={Images} />
28 + </div>
29 + )
30 + // return (
31 + // <div>
32 + // ..
33 + // </div>
34 + // )
35 +
36 +
37 +}
38 +
39 +export default ProductImage
1 +import React from 'react'
2 +
3 +function ProductInfo() {
4 + return (
5 + <div>
6 + info
7 + </div>
8 + )
9 +}
10 +
11 +export default ProductInfo
1 -import React from 'react' 1 +import React from "react";
2 -import {Icon} from 'antd'; 2 +import { Icon } from "antd";
3 3
4 function Footer() { 4 function Footer() {
5 - return ( 5 + return (
6 - <div style={{ 6 + <div
7 - height: '80px', display: 'flex', 7 + style={{
8 - flexDirection: 'column', alignItems: 'center', 8 + height: "80px",
9 - justifyContent: 'center', fontSize:'1rem' 9 + display: "flex",
10 - }}> 10 + flexDirection: "column",
11 - <p> 2018110650 박민정</p> 11 + alignItems: "center",
12 - </div> 12 + justifyContent: "center",
13 - ) 13 + fontSize: "1rem",
14 + }}
15 + >
16 + <p> Copyright © 2021 Minjeong Park All rights reserved</p>
17 + </div>
18 + );
14 } 19 }
15 20
16 -export default Footer 21 +export default Footer;
......
1 +import React from "react";
2 +
3 +function HistoryPage(props) {
4 + return (
5 + <div style={{ width: "80%", margin: "3rem auto" }}>
6 + <div style={{ textAlign: "center" }}>
7 + <h1>구매내역</h1>
8 + </div>
9 + <br />
10 +
11 + <table>
12 + <thead>
13 + <tr>
14 + <th style={{ textAlign: "center" }}>구매 ID</th>
15 + <th style={{ textAlign: "center" }}>가격</th>
16 + <th style={{ textAlign: "center" }}>수량</th>
17 + <th style={{ textAlign: "center" }}>구매 날짜</th>
18 + </tr>
19 + </thead>
20 +
21 + <tbody>
22 + {props.user.userData &&
23 + props.user.userData.history &&
24 + props.user.userData.history.map(item => (
25 + <tr key={item.id}>
26 + <td style={{ textAlign: "center", margin: "auto" }}>{item.id}</td>
27 + <td style={{ textAlign: "center", margin: "auto" }}>{item.price}</td>
28 + <td style={{ textAlign: "center", margin: "auto" }}>{item.quantity}</td>
29 + <td style={{ textAlign: "center", margin: "auto" }}>{item.dateOfPurchase}</td>
30 + </tr>
31 + ))}
32 + </tbody>
33 + </table>
34 + </div>
35 + );
36 +}
37 +
38 +export default HistoryPage;
1 -import React from 'react' 1 +import React, { useEffect, useState } from "react";
2 -import { FaCode } from "react-icons/fa"; 2 +import Axios from "axios";
3 +import { Icon, Col, Card, Row } from "antd";
4 +import ImageSlider from "../../utils/ImageSlider";
5 +import CheckBox from "./Sections/CheckBox";
6 +import { medicines, price } from "./Sections/Datas";
7 +import SearchFeature from "./Sections/SearchFeature";
8 +import { Container } from "react-bootstrap";
9 +
10 +const { Meta } = Card;
3 11
4 function LandingPage() { 12 function LandingPage() {
13 + const [Products, setProducts] = useState([]);
14 + const [Skip, setSkip] = useState(0);
15 + const [Limit, setLimit] = useState(8);
16 + const [PostSize, setPostSize] = useState();
17 + const [SearchTerms, setSearchTerms] = useState("");
18 +
19 + const [Filters, setFilters] = useState({
20 + medicines: [],
21 + price: [],
22 + });
23 +
24 + useEffect(() => {
25 + const variables = {
26 + skip: Skip,
27 + limit: Limit,
28 + };
29 +
30 + getProducts(variables);
31 + }, []);
32 +
33 + const getProducts = variables => {
34 + Axios.post("/api/product/getProducts", variables).then(response => {
35 + if (response.data.success) {
36 + if (variables.loadMore) {
37 + setProducts([...Products, ...response.data.products]);
38 + } else {
39 + setProducts(response.data.products);
40 + }
41 + setPostSize(response.data.postSize);
42 + } else {
43 + alert("실패하였습니다.");
44 + }
45 + });
46 + };
47 +
48 + const onLoadMore = () => {
49 + let skip = Skip + Limit;
50 +
51 + const variables = {
52 + skip: skip,
53 + limit: Limit,
54 + loadMore: true,
55 + filters: Filters,
56 + searchTerm: SearchTerms,
57 + };
58 + getProducts(variables);
59 + setSkip(skip);
60 + };
61 +
62 + const renderCards = Products.map((product, index) => {
5 return ( 63 return (
6 - <> 64 + <Col style={{ marginTop: "30px" }} lg={6} md={8} xs={24}>
7 - <div className="app"> 65 + <Card
8 - .. 66 + style={{ margin: "5px" }}
9 - </div> 67 + hoverable={true}
10 - 68 + cover={
11 - </> 69 + <a href={`/product/${product._id}`}>
12 - ) 70 + <ImageSlider images={product.images} />
71 + </a>
72 + }
73 + >
74 + <Meta title={product.title} description={`${product.price}000원`} />
75 + </Card>
76 + </Col>
77 + );
78 + });
79 +
80 + const showFilteredResults = filters => {
81 + const variables = {
82 + skip: 0,
83 + limit: Limit,
84 + filters: filters,
85 + };
86 + getProducts(variables);
87 + setSkip(0);
88 + };
89 +
90 + const handlePrice = value => {
91 + const data = price;
92 + let array = [];
93 +
94 + for (let key in data) {
95 + if (data[key]._id === parseInt(value, 10)) {
96 + array = data[key].array;
97 + }
98 + }
99 + console.log("array", array);
100 + return array;
101 + };
102 +
103 + const handleFilters = (filters, category) => {
104 + const newFilters = { ...Filters };
105 +
106 + newFilters[category] = filters;
107 +
108 + if (category === "price") {
109 + let priceValues = handlePrice(filters);
110 + newFilters[category] = priceValues;
111 + }
112 +
113 + console.log(newFilters);
114 +
115 + showFilteredResults(newFilters);
116 + setFilters(newFilters);
117 + };
118 +
119 + const updateSearchTerms = newSearchTerm => {
120 + const variables = {
121 + skip: 0,
122 + limit: Limit,
123 + filters: Filters,
124 + searchTerm: newSearchTerm,
125 + };
126 +
127 + setSkip(0);
128 + setSearchTerms(newSearchTerm);
129 +
130 + getProducts(variables);
131 + };
132 +
133 + return (
134 + <div style={{ width: "75%", margin: "3rem auto" }}>
135 + <div style={{ textAlign: "center" }}>
136 + <img src={"/whatmedicine.png"} style={{ width: "500px" }} />
137 + {/* <h2>
138 + {" "}
139 + <Icon type="alert" />
140 + &nbsp; 어떤 약이 필요하신가요?&nbsp;<Icon type="alert" />{" "}
141 + </h2> */}
142 + </div>
143 +
144 +
145 + <Container style={{ width: "50%" }}>
146 + <CheckBox list={medicines} handleFilters={filters => handleFilters(filters, "medicines")} />
147 + </Container>
148 +
149 + {/* Search */}
150 +
151 + {Products.length === 0 ? (
152 + <div style={{ display: "flex", height: "300px", justifyContent: "center", alignItems: "center" }}>
153 +
154 +
155 + </div>
156 + ) : (
157 + <div>
158 + <Row gutter={[16, 16]}>{renderCards}</Row>
159 + </div>
160 + )}
161 + <br />
162 + <br />
163 +
164 + {PostSize >= Limit && (
165 + <div style={{ display: "flex", justifyContent: "center" }}>
166 + <button onClick={onLoadMore}>Load More</button>
167 + </div>
168 + )}
169 + </div>
170 + );
13 } 171 }
14 172
15 -export default LandingPage 173 +export default LandingPage;
......
1 +import React, { useState } from 'react'
2 +import { Checkbox, Collapse } from 'antd';
3 +
4 +const { Panel } = Collapse
5 +
6 +
7 +function CheckBox(props) {
8 +
9 + const [Checked, setChecked] = useState([])
10 +
11 + const handleToggle = (value) => {
12 +
13 + const currentIndex = Checked.indexOf(value);
14 + const newChecked = [...Checked];
15 +
16 + if (currentIndex === -1) {
17 + newChecked.push(value)
18 + } else {
19 + newChecked.splice(currentIndex, 1)
20 + }
21 +
22 + setChecked(newChecked)
23 + props.handleFilters(newChecked)
24 +
25 + }
26 +
27 + const renderCheckboxLists = () => props.list && props.list.map((value, index) => (
28 + <React.Fragment key={index}>
29 + <Checkbox
30 + onChange={() => handleToggle(value._id)}
31 + type="checkbox"
32 + checked={Checked.indexOf(value._id) === -1 ? false : true}
33 + />&nbsp;&nbsp;
34 + <span>{value.name}</span>
35 + </React.Fragment>
36 + ))
37 +
38 + return (
39 + <div>
40 + <Collapse defaultActiveKey={['0']} >
41 + <Panel header="약 종류" key="1">
42 + {renderCheckboxLists()}
43 + </Panel>
44 + </Collapse>
45 + </div>
46 + )
47 +}
48 +
49 +export default CheckBox
...\ No newline at end of file ...\ No newline at end of file
1 +const symtoms = [
2 + {
3 + "_id": 0,
4 + "name": "전체"
5 + },
6 + {
7 + "_id": 1,
8 + "name": "진통제"
9 + },
10 + {
11 + "_id": 2,
12 + "name": "소화제"
13 + },
14 + {
15 + "_id": 3,
16 + "name": "감기약"
17 + },
18 + {
19 + "_id": 4,
20 + "name": "해열제"
21 + },
22 + {
23 + "_id": 5,
24 + "name": "파스류"
25 + },
26 + {
27 + "_id": 6,
28 + "name": "상처치료"
29 + },
30 + {
31 + "_id": 7,
32 + "name": "기타"
33 + }
34 +]
35 +
36 +export {
37 + symtoms
38 +}
...\ No newline at end of file ...\ No newline at end of file
1 +
2 +
3 +const medicines = [
4 + { _id: 0, name: "전체" },
5 + {
6 + _id: 1,
7 + name: "진통제",
8 + },
9 + {
10 + _id: 2,
11 + name: "소화제",
12 + },
13 + {
14 + _id: 3,
15 + name: "감기약",
16 + },
17 + {
18 + _id: 4,
19 + name: "해열제",
20 + },
21 + {
22 + _id: 5,
23 + name: "파스류",
24 + },
25 + {
26 + _id: 6,
27 + name: "상처치료",
28 + },
29 + {
30 + _id: 7,
31 + name: "기타",
32 + },
33 +];
34 +
35 +
36 +
37 +
38 +const price = [
39 + {
40 + _id: 0,
41 + name: "Any",
42 + array: [],
43 + },
44 + {
45 + _id: 1,
46 + name: "배달비 무료",
47 + array: [0],
48 + },
49 + {
50 + _id: 2,
51 + name: "배달비 500원 ~ 1000원",
52 + array: [500, 1000],
53 + },
54 + {
55 + _id: 3,
56 + name: "배달비 1000원 ~ 2000원",
57 + array: [1001, 2000],
58 + },
59 + {
60 + _id: 4,
61 + name: "배달비 200원 ~ 3000원",
62 + array: [2001, 3000],
63 + },
64 + {
65 + _id: 5,
66 + name: "3000원 이상",
67 + array: [3000, 1500000],
68 + },
69 +];
70 +
71 +export { price, medicines };
1 +import React from 'react'
2 +
3 +function RadioBox() {
4 + return (
5 + <div>
6 +
7 + </div>
8 + )
9 +}
10 +
11 +export default RadioBox
1 +import React, { useState } from "react";
2 +import { Input } from "antd";
3 +
4 +const { Search } = Input;
5 +
6 +function SearchFeature(props) {
7 + const [SearchTerms, setSearchTerms] = useState("");
8 +
9 + const onChangeSearch = event => {
10 + setSearchTerms(event.currentTarget.value);
11 +
12 + props.refreshFunction(event.currentTarget.value);
13 + };
14 +
15 + return (
16 + <div style={{ margin: "auto" }}>
17 + <Search value={SearchTerms} onChange={onChangeSearch} placeholder="상품명" />
18 + </div>
19 + );
20 +}
21 +
22 +export default SearchFeature;
1 import React, { useState } from "react"; 1 import React, { useState } from "react";
2 import { withRouter } from "react-router-dom"; 2 import { withRouter } from "react-router-dom";
3 import { loginUser } from "../../../_actions/user_actions"; 3 import { loginUser } from "../../../_actions/user_actions";
4 -import { Formik } from 'formik'; 4 +import { Formik } from "formik";
5 -import * as Yup from 'yup'; 5 +import * as Yup from "yup";
6 -import { Form, Icon, Input, Button, Checkbox, Typography } from 'antd'; 6 +import { Form, Icon, Input, Button, Checkbox, Typography } from "antd";
7 import { useDispatch } from "react-redux"; 7 import { useDispatch } from "react-redux";
8 8
9 const { Title } = Typography; 9 const { Title } = Typography;
10 10
11 function LoginPage(props) { 11 function LoginPage(props) {
12 const dispatch = useDispatch(); 12 const dispatch = useDispatch();
13 - //const rememberMeChecked = localStorage.getItem("rememberMe") ? true : false; 13 + const rememberMeChecked = localStorage.getItem("rememberMe") ? true : false;
14 14
15 - const [Error, setError] = useState('') 15 + const [formErrorMessage, setFormErrorMessage] = useState("");
16 - //const [rememberMe, setRememberMe] = useState(rememberMeChecked) 16 + const [rememberMe, setRememberMe] = useState(rememberMeChecked);
17 17
18 - // const handleRememberMe = () => { 18 + const handleRememberMe = () => {
19 - // setRememberMe(!rememberMe) 19 + setRememberMe(!rememberMe);
20 - // }; 20 + };
21 21
22 - //const initialEmail = localStorage.getItem("rememberMe") ? localStorage.getItem("rememberMe") : '';
23 22
24 return ( 23 return (
25 <Formik 24 <Formik
26 initialValues={{ 25 initialValues={{
27 - email: '', 26 + email: "",
28 - password: '', 27 + password: "",
29 }} 28 }}
30 - // validationSchema={Yup.object().shape({ 29 + validationSchema={Yup.object().shape({
31 - // email: Yup.string() 30 + email: Yup.string().email("이메일이 유효하지 않습니다.").required("이메일을 입력해주세요."),
32 - // .email('Email is invalid') 31 + password: Yup.string().min(5, "비밀번호가 너무 짧습니다.").required("비밀번호를 입력해주세요."),
33 - // .required('Email is required'), 32 + })}
34 - // password: Yup.string()
35 - // .min(6, 'Password must be at least 6 characters')
36 - // .required('Password is required'),
37 - // })}
38 onSubmit={(values, { setSubmitting }) => { 33 onSubmit={(values, { setSubmitting }) => {
39 setTimeout(() => { 34 setTimeout(() => {
40 let dataToSubmit = { 35 let dataToSubmit = {
41 email: values.email, 36 email: values.email,
42 - password: values.password 37 + password: values.password,
43 }; 38 };
44 39
45 dispatch(loginUser(dataToSubmit)) 40 dispatch(loginUser(dataToSubmit))
46 .then(response => { 41 .then(response => {
47 if (response.payload.loginSuccess) { 42 if (response.payload.loginSuccess) {
48 - window.localStorage.setItem('userId', response.payload.userId); 43 + window.localStorage.setItem("userId", response.payload.userId);
49 - // if (rememberMe === true) { 44 + if (rememberMe === true) {
50 - // window.localStorage.setItem('rememberMe', values.id); 45 + window.localStorage.setItem("rememberMe", values.id);
51 - // } else { 46 + } else {
52 - // localStorage.removeItem('rememberMe'); 47 + localStorage.removeItem("rememberMe");
53 - // } 48 + }
54 props.history.push("/"); 49 props.history.push("/");
55 } else { 50 } else {
56 - setError('이메일 또는 비밀번호가 올바르지 않습니다.') 51 + setFormErrorMessage("Check out your Account or Password again");
57 } 52 }
58 }) 53 })
59 .catch(err => { 54 .catch(err => {
60 - setError('이메일 또는 비밀번호가 올바르지 않습니다.') 55 + setFormErrorMessage("Check out your Account or Password again");
61 setTimeout(() => { 56 setTimeout(() => {
62 - setError("") 57 + setFormErrorMessage("");
63 }, 3000); 58 }, 3000);
64 }); 59 });
65 setSubmitting(false); 60 setSubmitting(false);
...@@ -67,74 +62,51 @@ function LoginPage(props) { ...@@ -67,74 +62,51 @@ function LoginPage(props) {
67 }} 62 }}
68 > 63 >
69 {props => { 64 {props => {
70 - const { 65 + const { values, touched, errors, dirty, isSubmitting, handleChange, handleBlur, handleSubmit, handleReset } = props;
71 - values,
72 - touched,
73 - errors,
74 - dirty,
75 - isSubmitting,
76 - handleChange,
77 - handleBlur,
78 - handleSubmit,
79 - handleReset,
80 - } = props;
81 return ( 66 return (
82 <div className="app"> 67 <div className="app">
83 -
84 <Title level={2}>로그인</Title> 68 <Title level={2}>로그인</Title>
85 - <form onSubmit={handleSubmit} style={{ width: '350px' }}> 69 + <form onSubmit={handleSubmit} style={{ width: "350px" }}>
86 -
87 <Form.Item required> 70 <Form.Item required>
88 <Input 71 <Input
89 id="email" 72 id="email"
90 - prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} 73 + prefix={<Icon type="user" style={{ color: "rgba(0,0,0,.25)" }} />}
91 placeholder="Enter your email" 74 placeholder="Enter your email"
92 type="email" 75 type="email"
93 value={values.email} 76 value={values.email}
94 onChange={handleChange} 77 onChange={handleChange}
95 onBlur={handleBlur} 78 onBlur={handleBlur}
96 - className={ 79 + className={errors.email && touched.email ? "text-input error" : "text-input"}
97 - errors.email && touched.email ? 'text-input error' : 'text-input'
98 - }
99 /> 80 />
100 - {errors.email && touched.email && ( 81 + {errors.email && touched.email && <div className="input-feedback">{errors.email}</div>}
101 - <div className="input-feedback">{errors.email}</div>
102 - )}
103 </Form.Item> 82 </Form.Item>
104 83
105 <Form.Item required> 84 <Form.Item required>
106 <Input 85 <Input
107 id="password" 86 id="password"
108 - prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} 87 + prefix={<Icon type="lock" style={{ color: "rgba(0,0,0,.25)" }} />}
109 placeholder="Enter your password" 88 placeholder="Enter your password"
110 type="password" 89 type="password"
111 value={values.password} 90 value={values.password}
112 onChange={handleChange} 91 onChange={handleChange}
113 onBlur={handleBlur} 92 onBlur={handleBlur}
114 - className={ 93 + className={errors.password && touched.password ? "text-input error" : "text-input"}
115 - errors.password && touched.password ? 'text-input error' : 'text-input'
116 - }
117 /> 94 />
118 - {errors.password && touched.password && ( 95 + {errors.password && touched.password && <div className="input-feedback">{errors.password}</div>}
119 - <div className="input-feedback">{errors.password}</div>
120 - )}
121 </Form.Item> 96 </Form.Item>
122 97
123 - {Error && ( 98 + {formErrorMessage && (
124 - <label ><p style={{ color: '#508DFF', fontSize: '0.8rem' }}>{Error}</p></label> 99 + <label>
100 + <p style={{ color: "#ff0000bf", fontSize: "0.7rem", border: "1px solid", padding: "1rem", borderRadius: "10px" }}>{formErrorMessage}</p>
101 + </label>
125 )} 102 )}
126 103
127 <Form.Item> 104 <Form.Item>
128 - {/* <Checkbox id="rememberMe" onChange={handleRememberMe} checked={rememberMe} >Remember me</Checkbox>
129 - <a className="login-form-forgot" href="/reset_user" style={{ float: 'right' }}>
130 - forgot password
131 - </a> */}
132 <div> 105 <div>
133 - <Button type="primary" htmlType="submit" className="login-form-button" style={{ minWidth: '100%' }} disabled={isSubmitting} onSubmit={handleSubmit}> 106 + <Button type="primary" htmlType="submit" className="login-form-button" style={{ minWidth: "100%" }} disabled={isSubmitting} onSubmit={handleSubmit}>
134 로그인 107 로그인
135 - </Button> 108 + </Button>
136 </div> 109 </div>
137 - <a href="/register" style={{ minWidth: '100%' }}>회원가입</a>
138 </Form.Item> 110 </Form.Item>
139 </form> 111 </form>
140 </div> 112 </div>
...@@ -142,8 +114,6 @@ function LoginPage(props) { ...@@ -142,8 +114,6 @@ function LoginPage(props) {
142 }} 114 }}
143 </Formik> 115 </Formik>
144 ); 116 );
145 -}; 117 +}
146 118
147 export default withRouter(LoginPage); 119 export default withRouter(LoginPage);
148 -
149 -
......
1 -import React, { useState } from 'react'; 1 +import React, { useState } from "react";
2 -import LeftMenu from './Sections/LeftMenu'; 2 +import RightMenu from "./Sections/RightMenu";
3 -import RightMenu from './Sections/RightMenu'; 3 +import { Drawer, Button, Icon } from "antd";
4 -import { Drawer, Button, Icon } from 'antd'; 4 +import "./Sections/Navbar.css";
5 -import './Sections/Navbar.css'; 5 +import { Container, Navbar, Nav } from "react-bootstrap";
6 6
7 function NavBar() { 7 function NavBar() {
8 - const [visible, setVisible] = useState(false) 8 + const [visible, setVisible] = useState(false);
9 9
10 const showDrawer = () => { 10 const showDrawer = () => {
11 - setVisible(true) 11 + setVisible(true);
12 }; 12 };
13 13
14 const onClose = () => { 14 const onClose = () => {
15 - setVisible(false) 15 + setVisible(false);
16 }; 16 };
17 17
18 return ( 18 return (
19 - <nav className="menu" style={{ position: 'fixed', zIndex: 5, width: '100%' }}> 19 + <Navbar bg="white" variant="light" style={{ position: "fixed", zIndex: 5, width: "100%", border: "1px solid lightgray" }}>
20 - <div className="menu__logo"> 20 + <Container>
21 - <a href="/">Logo</a> 21 + <Navbar.Brand href="/">
22 - </div> 22 + <img src={"/logo.png"} style={{ width: "120px", marginLeft:"50px"}} />
23 - <div className="menu__container"> 23 + </Navbar.Brand>
24 - <div className="menu_left"> 24 + <Nav className="mx-3" style={{ position: "relative", right: "0" }}>
25 - <LeftMenu mode="horizontal" /> 25 + <div className="menu__container">
26 - </div> 26 + <div className="menu_left"></div>
27 - <div className="menu_rigth"> 27 + <div className="menu_rigth">
28 - <RightMenu mode="horizontal" /> 28 + <RightMenu mode="horizontal" />
29 - </div> 29 + </div>
30 - <Button 30 + <Button className="menu__mobile-button" type="primary" onClick={showDrawer}>
31 - className="menu__mobile-button" 31 + <Icon type="align-right" />
32 - type="primary" 32 + </Button>
33 - onClick={showDrawer} 33 + <Drawer title="Basic Drawer" placement="right" className="menu_drawer" closable={false} onClose={onClose} visible={visible}>
34 - > 34 +
35 - <Icon type="align-right" /> 35 + <RightMenu mode="inline" />
36 - </Button> 36 + </Drawer>
37 - <Drawer 37 + </div>
38 - title="Basic Drawer" 38 + </Nav>
39 - placement="right" 39 + </Container>
40 - className="menu_drawer" 40 + </Navbar>
41 - closable={false} 41 + );
42 - onClose={onClose}
43 - visible={visible}
44 - >
45 - <LeftMenu mode="inline" />
46 - <RightMenu mode="inline" />
47 - </Drawer>
48 - </div>
49 - </nav>
50 - )
51 } 42 }
52 43
53 -export default NavBar
...\ No newline at end of file ...\ No newline at end of file
44 +export default NavBar;
......
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
50 padding: 6px; 50 padding: 6px;
51 margin-top: 8px; 51 margin-top: 8px;
52 display: none !important; /* use of important to overwrite ant-btn */ 52 display: none !important; /* use of important to overwrite ant-btn */
53 - background: #3e91f7; 53 + background: lightgray;
54 } 54 }
55 55
56 .menu_drawer .ant-drawer-body { 56 .menu_drawer .ant-drawer-body {
......
1 - 1 +/* eslint-disable jsx-a11y/anchor-is-valid */
2 -import React from 'react'; 2 +import React from "react";
3 -import { Menu } from 'antd'; 3 +import { Menu, Icon, Badge } from "antd";
4 -import axios from 'axios'; 4 +import axios from "axios";
5 -import { USER_SERVER } from '../../../Config'; 5 +import { USER_SERVER } from "../../../Config";
6 -import { withRouter } from 'react-router-dom'; 6 +import { withRouter } from "react-router-dom";
7 import { useSelector } from "react-redux"; 7 import { useSelector } from "react-redux";
8 8
9 function RightMenu(props) { 9 function RightMenu(props) {
10 - const user = useSelector(state => state.user) 10 + const user = useSelector(state => state.user);
11 11
12 const logoutHandler = () => { 12 const logoutHandler = () => {
13 axios.get(`${USER_SERVER}/logout`).then(response => { 13 axios.get(`${USER_SERVER}/logout`).then(response => {
14 if (response.status === 200) { 14 if (response.status === 200) {
15 props.history.push("/login"); 15 props.history.push("/login");
16 } else { 16 } else {
17 - alert('Log Out Failed') 17 + alert("로그아웃에 실패했습니다.");
18 } 18 }
19 }); 19 });
20 }; 20 };
21 21
22 - //console.log(user.userData);
23 - //console.log(!user.userData.isAuth);
24 22
25 if (user.userData && !user.userData.isAuth) { 23 if (user.userData && !user.userData.isAuth) {
24 +
26 return ( 25 return (
27 - <Menu mode={props.mode}> 26 + <Menu style={{ paddingTop: "10px" }} mode={props.mode}>
28 <Menu.Item key="mail"> 27 <Menu.Item key="mail">
29 - <a href="/login">로그인</a> 28 + <a href="/login"><h6>로그인</h6></a>
30 </Menu.Item> 29 </Menu.Item>
31 <Menu.Item key="app"> 30 <Menu.Item key="app">
32 - <a href="/register">회원가입</a> 31 + <a href="/register"><h6>회원가입</h6></a>
33 </Menu.Item> 32 </Menu.Item>
34 </Menu> 33 </Menu>
35 - ) 34 + );
36 } else { 35 } else {
37 return ( 36 return (
38 - <Menu mode={props.mode}> 37 + <Menu mode={props.mode} style={{ paddingTop: "5px" }}>
38 + <Menu.Item key="history">
39 + <a href="/history">
40 + <h6>구매내역</h6>
41 + </a>
42 + </Menu.Item>
43 +
39 <Menu.Item key="upload"> 44 <Menu.Item key="upload">
40 - <a href="/upload">업로드</a> 45 + <a href="/product/upload">
46 + <h6>등록하기</h6>
47 + </a>
41 </Menu.Item> 48 </Menu.Item>
49 +
50 + <Menu.Item key="cart" style={{ paddingBottom: -4 }}>
51 +
52 + <a href="/user/cart" style={{ marginRight: -22, paddingbottom: "30px", color: "#667777" }}>
53 + <Icon type="shopping-cart" style={{ fontSize: 30, marginBottom: 3 }} />
54 + </a>
55 +
56 + </Menu.Item>
57 +
42 <Menu.Item key="logout"> 58 <Menu.Item key="logout">
43 - <a onClick={logoutHandler}>로그아웃</a> 59 + <a onClick={logoutHandler}><h6>로그아웃</h6></a>
44 </Menu.Item> 60 </Menu.Item>
45 </Menu> 61 </Menu>
46 - ) 62 + );
47 } 63 }
48 } 64 }
49 65
50 export default withRouter(RightMenu); 66 export default withRouter(RightMenu);
51 -
......
1 import React from "react"; 1 import React from "react";
2 import moment from "moment"; 2 import moment from "moment";
3 -import { Formik } from 'formik'; 3 +import { Formik } from "formik";
4 -import * as Yup from 'yup'; 4 +import * as Yup from "yup";
5 import { registerUser } from "../../../_actions/user_actions"; 5 import { registerUser } from "../../../_actions/user_actions";
6 import { useDispatch } from "react-redux"; 6 import { useDispatch } from "react-redux";
7 7
8 -import { 8 +import { Form, Input, Button } from "antd";
9 - Form, 9 +const { TextArea } = Input;
10 - Input,
11 - Button,
12 -} from 'antd';
13 -
14 const formItemLayout = { 10 const formItemLayout = {
15 labelCol: { 11 labelCol: {
16 xs: { span: 24 }, 12 xs: { span: 24 },
...@@ -37,139 +33,121 @@ const tailFormItemLayout = { ...@@ -37,139 +33,121 @@ const tailFormItemLayout = {
37 function RegisterPage(props) { 33 function RegisterPage(props) {
38 const dispatch = useDispatch(); 34 const dispatch = useDispatch();
39 return ( 35 return (
40 -
41 <Formik 36 <Formik
42 initialValues={{ 37 initialValues={{
43 - email: '', 38 + email: "",
44 - 39 + name: "",
45 - name: '', 40 + address: "",
46 - password: '', 41 + password: "",
47 - confirmPassword: '' 42 + confirmPassword: "",
48 }} 43 }}
49 validationSchema={Yup.object().shape({ 44 validationSchema={Yup.object().shape({
50 - name: Yup.string() 45 + name: Yup.string().required("이름을 입력해주세요."),
51 - .required('이름이 올바르게 입력되지 않았습니다.'), 46 + email: Yup.string().email("이메일 형식이 올바르지 않습니다.").required("이메일을 입력해주세요."),
52 - 47 + password: Yup.string().min(5, "비밀번호는 5자리 이상이어야 합니다.").required("비밀번호를 입력해주세요."),
53 - email: Yup.string()
54 - .required('이메일이 올바르게 입력되지 않았습니다.'),
55 - password: Yup.string()
56 - .min(3, '비밀번호는 3자리 이상이어야 합니다.')
57 - .required('비밀번호를 입력해주세요.'),
58 confirmPassword: Yup.string() 48 confirmPassword: Yup.string()
59 - .oneOf([Yup.ref('password'), null], '비밀번호를 다시 확인해주세요.') 49 + .oneOf([Yup.ref("password"), null], "비밀번호가 일치하지 않습니다.")
60 - .required('비밀번호 확인이 올바르게 입력되지 않았습니다.') 50 + .required("비밀번호를 한번 더 입력해주세요."),
51 + adress: Yup.string().required("주소를 입력해주세요."),
61 })} 52 })}
62 onSubmit={(values, { setSubmitting }) => { 53 onSubmit={(values, { setSubmitting }) => {
63 setTimeout(() => { 54 setTimeout(() => {
64 -
65 let dataToSubmit = { 55 let dataToSubmit = {
66 email: values.email, 56 email: values.email,
67 password: values.password, 57 password: values.password,
68 name: values.name, 58 name: values.name,
69 - 59 + adress: values.adress,
70 - //image: `http://gravatar.com/avatar/${moment().unix()}?d=identicon` 60 + image: `http://gravatar.com/avatar/${moment().unix()}?d=identicon`,
71 }; 61 };
72 62
73 dispatch(registerUser(dataToSubmit)).then(response => { 63 dispatch(registerUser(dataToSubmit)).then(response => {
74 if (response.payload.success) { 64 if (response.payload.success) {
75 props.history.push("/login"); 65 props.history.push("/login");
76 } else { 66 } else {
77 - alert(response.payload.err.errmsg) 67 + alert(response.payload.err.errmsg);
78 } 68 }
79 - }) 69 + });
80 70
81 setSubmitting(false); 71 setSubmitting(false);
82 }, 500); 72 }, 500);
83 }} 73 }}
84 > 74 >
85 {props => { 75 {props => {
86 - const { 76 + const { values, touched, errors, dirty, isSubmitting, handleChange, handleBlur, handleSubmit, handleReset } = props;
87 - values,
88 - touched,
89 - errors,
90 - dirty,
91 - isSubmitting,
92 - handleChange,
93 - handleBlur,
94 - handleSubmit,
95 - handleReset,
96 - } = props;
97 return ( 77 return (
98 <div className="app"> 78 <div className="app">
99 - <h2>회원가입</h2> 79 + <h3>회원가입</h3>
100 - <Form style={{ minWidth: '375px' }} {...formItemLayout} onSubmit={handleSubmit} > 80 + <Form style={{ minWidth: "375px" }} {...formItemLayout} onSubmit={handleSubmit}>
101 - 81 + <Form.Item required label="이름">
102 - <Form.Item required label="Name">
103 <Input 82 <Input
104 id="name" 83 id="name"
105 - placeholder="이름을 입력하세요." 84 + placeholder="이름을 입력하세요"
106 type="text" 85 type="text"
107 value={values.name} 86 value={values.name}
108 onChange={handleChange} 87 onChange={handleChange}
109 onBlur={handleBlur} 88 onBlur={handleBlur}
110 - className={ 89 + className={errors.name && touched.name ? "text-input error" : "text-input"}
111 - errors.name && touched.name ? 'text-input error' : 'text-input'
112 - }
113 /> 90 />
114 - {errors.name && touched.name && ( 91 + {errors.name && touched.name && <div className="input-feedback">{errors.name}</div>}
115 - <div className="input-feedback">{errors.name}</div> 92 + </Form.Item>
116 - )} 93 +
117 - </Form.Item> 94 +
118 95
119 - <Form.Item required label="Email" hasFeedback> 96 + <Form.Item required label="이메일">
120 <Input 97 <Input
121 id="email" 98 id="email"
122 - placeholder="이메일을 입력세요." 99 + placeholder="이메일을 입력해주세요."
123 type="email" 100 type="email"
124 value={values.email} 101 value={values.email}
125 onChange={handleChange} 102 onChange={handleChange}
126 onBlur={handleBlur} 103 onBlur={handleBlur}
127 - className={ 104 + className={errors.email && touched.email ? "text-input error" : "text-input"}
128 - errors.email && touched.email ? 'text-input error' : 'text-input'
129 - }
130 /> 105 />
131 - {errors.email && touched.email && ( 106 + {errors.email && touched.email && <div className="input-feedback">{errors.email}</div>}
132 - <div className="input-feedback">{errors.email}</div>
133 - )}
134 </Form.Item> 107 </Form.Item>
135 108
136 - <Form.Item required label="Password" hasFeedback> 109 + <Form.Item required label="비밀번호">
137 <Input 110 <Input
138 id="password" 111 id="password"
139 - placeholder="비밀번호를 입력세요." 112 + placeholder="비밀번호를 입력해주세요."
140 type="password" 113 type="password"
141 value={values.password} 114 value={values.password}
142 onChange={handleChange} 115 onChange={handleChange}
143 onBlur={handleBlur} 116 onBlur={handleBlur}
144 - className={ 117 + className={errors.password && touched.password ? "text-input error" : "text-input"}
145 - errors.password && touched.password ? 'text-input error' : 'text-input'
146 - }
147 /> 118 />
148 - {errors.password && touched.password && ( 119 + {errors.password && touched.password && <div className="input-feedback">{errors.password}</div>}
149 - <div className="input-feedback">{errors.password}</div>
150 - )}
151 </Form.Item> 120 </Form.Item>
152 121
153 - <Form.Item required label="Confirm" hasFeedback> 122 + <Form.Item required label="비밀번호 확인">
154 <Input 123 <Input
155 id="confirmPassword" 124 id="confirmPassword"
156 - placeholder="비밀번호를 다시 한 번 입력해주세요." 125 + placeholder="비밀번호를 한번 더 입력해주세요."
157 type="password" 126 type="password"
158 value={values.confirmPassword} 127 value={values.confirmPassword}
159 onChange={handleChange} 128 onChange={handleChange}
160 onBlur={handleBlur} 129 onBlur={handleBlur}
161 - className={ 130 + className={errors.confirmPassword && touched.confirmPassword ? "text-input error" : "text-input"}
162 - errors.confirmPassword && touched.confirmPassword ? 'text-input error' : 'text-input' 131 + />
163 - } 132 + {errors.confirmPassword && touched.confirmPassword && <div className="input-feedback">{errors.confirmPassword}</div>}
133 + </Form.Item>
134 +
135 + <Form.Item required label="주소">
136 + <TextArea
137 + id="adress"
138 + placeholder="주소를 입력하세요"
139 + type="text"
140 + value={values.adress}
141 + onChange={handleChange}
142 + onBlur={handleBlur}
143 + className={errors.adress && touched.adress ? "text-input error" : "text-input"}
164 /> 144 />
165 - {errors.confirmPassword && touched.confirmPassword && ( 145 + {errors.adress && touched.adress && <div className="input-feedback">{errors.adress}</div>}
166 - <div className="input-feedback">{errors.confirmPassword}</div>
167 - )}
168 </Form.Item> 146 </Form.Item>
169 147
170 <Form.Item {...tailFormItemLayout}> 148 <Form.Item {...tailFormItemLayout}>
171 <Button onClick={handleSubmit} type="primary" disabled={isSubmitting}> 149 <Button onClick={handleSubmit} type="primary" disabled={isSubmitting}>
172 - Submit 150 + 가입
173 </Button> 151 </Button>
174 </Form.Item> 152 </Form.Item>
175 </Form> 153 </Form>
...@@ -178,7 +156,6 @@ function RegisterPage(props) { ...@@ -178,7 +156,6 @@ function RegisterPage(props) {
178 }} 156 }}
179 </Formik> 157 </Formik>
180 ); 158 );
181 -}; 159 +}
182 -
183 160
184 -export default RegisterPage 161 +export default RegisterPage;
......
...@@ -2,16 +2,21 @@ import React from 'react'; ...@@ -2,16 +2,21 @@ import React from 'react';
2 import { useState } from 'react'; 2 import { useState } from 'react';
3 import { Typography, Button, Form, Input } from 'antd'; // css 3 import { Typography, Button, Form, Input } from 'antd'; // css
4 import ImageUpload from '../../utils/ImageUpload' 4 import ImageUpload from '../../utils/ImageUpload'
5 +import Axios from 'axios';
5 6
6 const { TextArea } = Input; // 박스크기 조절을 사용자가 임의로 가능하게 함. 7 const { TextArea } = Input; // 박스크기 조절을 사용자가 임의로 가능하게 함.
7 8
8 -// Select Options 9 +// Select symtoms
9 -const options = [{ key: 1, value: "a" }, 10 +const symtoms = [{ key: 1, value: "진통제" },
10 - { key: 2, value: "b" }, 11 + { key: 2, value: "소화제" },
11 - {key: 3, value : "c"} 12 + { key: 3, value: "감기약" },
13 + { key: 4, value: "해열제" },
14 + { key: 5, value: "파스류" },
15 + { key: 6, value: "상처치료" },
16 + { key: 7, value: "기타" }
12 ] 17 ]
13 18
14 -function UploadPage() { 19 +function UploadPage(props) {
15 20
16 // OnChange Function 21 // OnChange Function
17 22
...@@ -47,6 +52,35 @@ function UploadPage() { ...@@ -47,6 +52,35 @@ function UploadPage() {
47 setImage(newImages); 52 setImage(newImages);
48 } 53 }
49 54
55 + const submitEvent = (event) => {
56 + event.preventDefault(); // 확인버튼을 누를 때 리프레시 되지 않도록
57 +
58 + if (!Title || !Info || !Cost || !Option || !Image) {
59 + return alert("모두 입력해주세요.")
60 + }
61 +
62 + // 서버에 보낼 값들
63 + const body = {
64 + seller: props.user.userData._id,
65 + title: Title,
66 + info: Info,
67 + price: Cost,
68 + images: Image,
69 + symtoms: Option
70 + }
71 +
72 + Axios.post("/api/product", body)
73 + .then(response => {
74 + if (response.data.success) {
75 + alert("업로드가 완료되었습니다.");
76 + props.history.push('/'); // 상품 업로드가 성공하면 메인페이지로 돌아감.
77 + }
78 + else {
79 + alert("업로드에 실패했습니다.")
80 + }
81 + })
82 + }
83 +
50 84
51 return ( 85 return (
52 <div style={{ maxWidth: '700px', margin: '2rem auto' }}> 86 <div style={{ maxWidth: '700px', margin: '2rem auto' }}>
...@@ -56,7 +90,7 @@ function UploadPage() { ...@@ -56,7 +90,7 @@ function UploadPage() {
56 90
57 </div> 91 </div>
58 92
59 - <Form> 93 + <Form onSubmit={submitEvent}>
60 {/* 파일업로드 부분은 코드가 길어서 따로 컴포넌트로 만들어버리기~! */} 94 {/* 파일업로드 부분은 코드가 길어서 따로 컴포넌트로 만들어버리기~! */}
61 <ImageUpload refreshFunction={updateImages}/> 95 <ImageUpload refreshFunction={updateImages}/>
62 <br /> 96 <br />
...@@ -75,14 +109,14 @@ function UploadPage() { ...@@ -75,14 +109,14 @@ function UploadPage() {
75 <br /> 109 <br />
76 <br /> 110 <br />
77 <select onChange={optionEvent} value={ Option}> 111 <select onChange={optionEvent} value={ Option}>
78 - {options.map(item => ( 112 + {symtoms.map(item => (
79 <option key={item.key} value={item.key}>{ item.value}</option> 113 <option key={item.key} value={item.key}>{ item.value}</option>
80 ))} 114 ))}
81 <option></option> 115 <option></option>
82 </select> 116 </select>
83 <br /> 117 <br />
84 <br /> 118 <br />
85 - <Button>확인</Button> 119 + <Button onClick={submitEvent}>확인</Button>
86 120
87 </Form> 121 </Form>
88 122
...@@ -90,4 +124,4 @@ function UploadPage() { ...@@ -90,4 +124,4 @@ function UploadPage() {
90 ) 124 )
91 } 125 }
92 126
93 -export default UploadPage 127 + export default UploadPage;
......
1 +import React, { useState } from "react";
2 +import {Button, Form, message, Input, Icon } from "antd";
3 +import FileUpload from "../../utils/FileUpload";
4 +import Axios from "axios";
5 +
6 +const { TextArea } = Input;
7 +
8 +const Medicines = [
9 + { key: 1, value: "진통제" },
10 + { key: 2, value: "소화제" },
11 + { key: 3, value: "감기약" },
12 + { key: 4, value: "해열제" },
13 + { key: 5, value: "파스류" },
14 + { key: 6, value: "상처치료" },
15 + { key: 7, value: "기타" }
16 +];
17 +
18 +function UploadProductPage(props) {
19 + const [TitleValue, setTitleValue] = useState("");
20 + const [DescriptionValue, setDescriptionValue] = useState("");
21 + const [PriceValue, setPriceValue] = useState(0);
22 + const [MedicinesValue, setMedicinesValue] = useState(1);
23 +
24 + const [Images, setImages] = useState([]);
25 +
26 + const onTitleChange = event => {
27 + setTitleValue(event.currentTarget.value);
28 + };
29 +
30 + const onDescriptionChange = event => {
31 + setDescriptionValue(event.currentTarget.value);
32 + };
33 +
34 + const onPriceChange = event => {
35 + setPriceValue(event.currentTarget.value);
36 + };
37 +
38 + const onMedicinesSelectChange = event => {
39 + setMedicinesValue(event.currentTarget.value);
40 + };
41 +
42 + const updateImages = newImages => {
43 + setImages(newImages);
44 + };
45 + const onSubmit = event => {
46 + event.preventDefault();
47 +
48 + if (!TitleValue || !DescriptionValue || !PriceValue || !MedicinesValue || !Images) {
49 + return alert("fill all the fields first!");
50 + }
51 +
52 + const variables = {
53 + writer: props.user.userData._id,
54 + title: TitleValue,
55 + description: DescriptionValue,
56 + price: PriceValue,
57 + images: Images,
58 + medicines: MedicinesValue,
59 + };
60 +
61 + Axios.post("/api/product/uploadProduct", variables).then(response => {
62 + if (response.data.success) {
63 + alert("Product Successfully Uploaded");
64 + props.history.push("/");
65 + } else {
66 + alert("Failed to upload Product");
67 + }
68 + });
69 + };
70 +
71 + return (
72 + <div style={{ maxWidth: "700px", margin: "2rem auto" }}>
73 + <div style={{ textAlign: "center", marginBottom: "2rem" }}>
74 + <h3> 등록하기</h3>
75 + </div>
76 +
77 + <Form onSubmit={onSubmit}>
78 + {/* DropZone */}
79 + <FileUpload refreshFunction={updateImages} />
80 +
81 + <br />
82 + <br />
83 + <label> 이름</label>
84 + <Input onChange={onTitleChange} value={TitleValue} />
85 + <br />
86 + <br />
87 + <label>설명</label>
88 + <TextArea onChange={onDescriptionChange} value={DescriptionValue} />
89 + <br />
90 + <br />
91 + <label>가격($)</label>
92 + <Input onChange={onPriceChange} value={PriceValue} type="number" />
93 + <br />
94 + <br />
95 + <select onChange={onMedicinesSelectChange} value={MedicinesValue}>
96 + {Medicines.map(item => (
97 + <option key={item.key} value={item.key}>{item.value} </option>
98 + ))}
99 + </select>
100 + <br />
101 + <br />
102 + <Button onClick={onSubmit}>등록</Button>
103 + </Form>
104 + </div>
105 + );
106 +}
107 +
108 +export default UploadProductPage;
1 +import React, { Component } from 'react'
2 +import { Typography, Button, Form, Input } from 'antd';
3 +import axios from 'axios';
4 +import FileUpload from '../../utils/FileUpload';
5 +
6 +const { Title } = Typography;
7 +const { TextArea } = Input;
8 +
9 +const Medicines = [
10 + { key: 1, value: "진통제" },
11 + { key: 2, value: "소화제" },
12 + { key: 3, value: "감기약" },
13 + { key: 4, value: "해열제" },
14 + { key: 5, value: "파스류" },
15 + { key: 6, value: "상처치료" },
16 + { key: 7, value: "기타" }
17 +]
18 +
19 +export class UploadProductPage extends Component {
20 +
21 + state = {
22 + title: '',
23 + description: '',
24 + medicines: 1,
25 + images: [],
26 + price: 0
27 + }
28 +
29 + handleChangeTitle = (event) => {
30 + this.setState({ title: event.currentTarget.value })
31 + }
32 +
33 + handleChangePrice = (event) => {
34 + this.setState({ price: parseInt(event.currentTarget.value, 10) })
35 + }
36 +
37 + handleChangeDecsription = (event) => {
38 + // console.log(event.currentTarget.value)
39 + this.setState({ description: event.currentTarget.value })
40 + }
41 +
42 + handleChangeMedicines = (event) => {
43 + this.setState({ medicines: event.currentTarget.value })
44 + }
45 +
46 + onSubmit = (event) => {
47 + event.preventDefault();
48 +
49 + if (this.props.user.userData && !this.props.user.userData.isAuth) {
50 + return alert('!! 접근할 수 없습니다 !!')
51 + }
52 +
53 + if (!this.state.title || !this.state.description ||
54 + !this.state.medicines || !this.state.images
55 + || !this.state.price) {
56 + return alert('모든 항목을 채워주세요.')
57 + }
58 +
59 + const variables = {
60 + writer: this.props.user.userData._id,
61 + title: this.state.title,
62 + description: this.state.description,
63 + images: this.state.images,
64 + medicines: this.state.medicines,
65 + price: this.state.price
66 + }
67 +
68 + axios.post('/api/product/uploadProduct', variables)
69 + .then(response => {
70 + if (response.data.success) {
71 + alert('성공적으로 업로드 했습니다.')
72 + setTimeout(() => {
73 + this.props.history.push('/')
74 + }, 1000);
75 + } else {
76 + alert('업로드에 실패했습니다.')
77 + }
78 + })
79 + }
80 +
81 + updateFiles = (newImages) => {
82 + this.setState({ images: newImages })
83 + }
84 +
85 +
86 + render() {
87 + return (
88 + <div style={{ maxWidth: '700px', margin: '2rem auto' }}>
89 + <div style={{ textAlign: 'center', marginBottom: '2rem' }}>
90 + <Title level={2} > 배달 서비스 : 약사</Title>
91 + </div>
92 +
93 + <Form onSubmit={this.onSubmit}>
94 +
95 + <FileUpload refreshFunction={this.updateFiles} />
96 +
97 + <br /><br />
98 + <label>제품명</label>
99 + <Input
100 + onChange={this.handleChangeTitle}
101 + value={this.state.title}
102 + />
103 + <br /><br />
104 + <label>설명</label>
105 + <TextArea
106 + onChange={this.handleChangeDecsription}
107 + value={this.state.description}
108 + />
109 + <br /><br />
110 + <label>가격</label>
111 + <Input
112 + type="number"
113 + onChange={this.handleChangePrice}
114 + value={this.state.price}
115 + />
116 + <br /><br />
117 + <select onChange={this.handleChangeMedicines}>
118 + {Medicines.map(item => (
119 + <option key={item.key} value={item.key}>{item.value}</option>
120 + ))}
121 + </select>
122 + <br /><br />
123 +
124 +
125 + <Button type="primary" size="large" onClick={this.onSubmit}>
126 + Submit
127 + </Button>
128 + </Form>
129 + </div>
130 + )
131 + }
132 +}
133 +
134 +export default UploadProductPage
1 +import React from "react";
2 +import axios from 'axios';
3 +
4 +function adminPage(props) {
5 +
6 +}
7 +
8 +export default adminPage;
1 -/* eslint-disable react-hooks/exhaustive-deps */
2 import React, { useEffect } from 'react'; 1 import React, { useEffect } from 'react';
3 import { auth } from '../_actions/user_actions'; 2 import { auth } from '../_actions/user_actions';
4 import { useSelector, useDispatch } from "react-redux"; 3 import { useSelector, useDispatch } from "react-redux";
5 4
6 -export default function (SpecificComponent, option, adminRoute = null) { 5 +export default function (ComposedClass, reload, adminRoute = null) {
7 function AuthenticationCheck(props) { 6 function AuthenticationCheck(props) {
8 7
9 let user = useSelector(state => state.user); 8 let user = useSelector(state => state.user);
10 const dispatch = useDispatch(); 9 const dispatch = useDispatch();
11 10
12 useEffect(() => { 11 useEffect(() => {
13 - //To know my current status, send Auth request 12 +
14 - dispatch(auth()).then(response => { 13 + dispatch(auth()).then(async response => {
15 - //Not Loggined in Status 14 + if (await !response.payload.isAuth) {
16 - if (!response.payload.isAuth) { 15 + if (reload) {
17 - if (option) {
18 props.history.push('/login') 16 props.history.push('/login')
19 } 17 }
20 - //Loggined in Status
21 } else { 18 } else {
22 - //supposed to be Admin page, but not admin person wants to go inside
23 if (adminRoute && !response.payload.isAdmin) { 19 if (adminRoute && !response.payload.isAdmin) {
24 props.history.push('/') 20 props.history.push('/')
25 } 21 }
26 - //Logged in Status, but Try to go into log in page
27 else { 22 else {
28 - if (option === false) { 23 + if (reload === false) {
29 props.history.push('/') 24 props.history.push('/')
30 } 25 }
31 } 26 }
32 } 27 }
33 }) 28 })
34 - 29 +
35 - }, []) 30 + }, [dispatch, props.history, user.googleAuth])
36 31
37 return ( 32 return (
38 - <SpecificComponent {...props} user={user} /> 33 + <ComposedClass {...props} user={user} />
39 ) 34 )
40 } 35 }
41 return AuthenticationCheck 36 return AuthenticationCheck
......
1 +@import "~react-image-gallery/styles/css/image-gallery.css";
2 +@font-face {
3 + font-family: "BRBA_B";
4 + src: url("https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_seven@1.2/BRBA_B.woff")
5 + format("woff");
6 + font-weight: normal;
7 + font-style: normal;
8 +}
9 +
10 +@font-face {
11 + font-family: "InfinitySans-RegularA1";
12 + src: url("https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_20-04@2.1/InfinitySans-RegularA1.woff")
13 + format("woff");
14 + font-weight: normal;
15 + font-style: normal;
16 +}
17 +
18 +@font-face {
19 + font-family: "GongGothicMedium";
20 + src: url("https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_20-10@1.0/GongGothicMedium.woff")
21 + format("woff");
22 + font-weight: normal;
23 + font-style: normal;
24 +}
25 +
1 body { 26 body {
2 margin: 0; 27 margin: 0;
3 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 28 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
...@@ -38,3 +63,28 @@ input.error { ...@@ -38,3 +63,28 @@ input.error {
38 height: 5px; 63 height: 5px;
39 margin-top: -12px; 64 margin-top: -12px;
40 } 65 }
66 +
67 +table {
68 + font-family: arial, sans-serif;
69 + border-collapse: collapse;
70 + width: 100%;
71 +}
72 +
73 +td,
74 +th {
75 + border: 1px solid #dddddd;
76 + text-align: left;
77 + padding: 8px;
78 +}
79 +
80 +tr:nth-child(even) {
81 + background-color: #dddddd;
82 +}
83 +
84 +h1 {
85 + font-family: "GongGothicMedium";
86 +}
87 +
88 +h3 {
89 + font-family: "InfinitySans-RegularA1";
90 +}
......
1 -import 'react-app-polyfill/ie9'; 1 +import React from "react";
2 -import 'react-app-polyfill/ie11'; 2 +import ReactDOM from "react-dom";
3 -import 'core-js'; 3 +import "./index.css";
4 - 4 +import App from "./components/App";
5 -import React from 'react'; 5 +import * as serviceWorker from "./serviceWorker";
6 -import ReactDOM from 'react-dom';
7 -import './index.css';
8 -import App from './components/App';
9 -import * as serviceWorker from './serviceWorker';
10 import { BrowserRouter } from "react-router-dom"; 6 import { BrowserRouter } from "react-router-dom";
11 7
12 -import Reducer from './_reducers'; 8 +import Reducer from "./_reducers";
13 -import { Provider } from 'react-redux'; 9 +import { Provider } from "react-redux";
14 -import { createStore, applyMiddleware } from 'redux'; 10 +import { createStore, applyMiddleware } from "redux";
15 -import promiseMiddleware from 'redux-promise'; 11 +import promiseMiddleware from "redux-promise";
16 -import ReduxThunk from 'redux-thunk'; 12 +import ReduxThunk from "redux-thunk";
17 13
18 const createStoreWithMiddleware = applyMiddleware(promiseMiddleware, ReduxThunk)(createStore); 14 const createStoreWithMiddleware = applyMiddleware(promiseMiddleware, ReduxThunk)(createStore);
19 15
20 ReactDOM.render( 16 ReactDOM.render(
21 - <Provider 17 + <Provider store={createStoreWithMiddleware(Reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())}>
22 - store={createStoreWithMiddleware( 18 + <BrowserRouter>
23 - Reducer, 19 + <App />
24 - window.__REDUX_DEVTOOLS_EXTENSION__ && 20 + </BrowserRouter>
25 - window.__REDUX_DEVTOOLS_EXTENSION__() 21 + </Provider>,
26 - )} 22 + document.getElementById("root")
27 - > 23 +);
28 - <BrowserRouter>
29 - <App />
30 - </BrowserRouter>
31 - </Provider>
32 - , document.getElementById('root'));
33 // If you want your app to work offline and load faster, you can change 24 // If you want your app to work offline and load faster, you can change
34 // unregister() to register() below. Note this comes with some pitfalls. 25 // unregister() to register() below. Note this comes with some pitfalls.
35 // Learn more about service workers: https://bit.ly/CRA-PWA 26 // Learn more about service workers: https://bit.ly/CRA-PWA
......
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.
...@@ -10,25 +10,24 @@ ...@@ -10,25 +10,24 @@
10 "scripts": { 10 "scripts": {
11 "start": "node server/index.js", 11 "start": "node server/index.js",
12 "backend": "nodemon server/index.js", 12 "backend": "nodemon server/index.js",
13 - "frontend": "npm run start --prefix client", 13 + "frontend": "npm run front --prefix client",
14 "dev": "concurrently \"npm run backend\" \"npm run start --prefix client\"" 14 "dev": "concurrently \"npm run backend\" \"npm run start --prefix client\""
15 }, 15 },
16 "author": "John ahn", 16 "author": "John ahn",
17 "license": "ISC", 17 "license": "ISC",
18 "dependencies": { 18 "dependencies": {
19 + "async": "^3.1.0",
19 "bcrypt": "^3.0.6", 20 "bcrypt": "^3.0.6",
20 "body-parser": "^1.18.3", 21 "body-parser": "^1.18.3",
21 "cookie-parser": "^1.4.3", 22 "cookie-parser": "^1.4.3",
22 "cors": "^2.8.5", 23 "cors": "^2.8.5",
23 - "debug": "^4.1.1",
24 "express": "^4.17.1", 24 "express": "^4.17.1",
25 "jsonwebtoken": "^8.5.1", 25 "jsonwebtoken": "^8.5.1",
26 "moment": "^2.24.0", 26 "moment": "^2.24.0",
27 "mongoose": "^5.4.20", 27 "mongoose": "^5.4.20",
28 "multer": "^1.4.2", 28 "multer": "^1.4.2",
29 "react-redux": "^5.0.7", 29 "react-redux": "^5.0.7",
30 - "saslprep": "^1.0.3", 30 + "socket.io": "^2.2.0"
31 - "supports-color": "^7.1.0"
32 }, 31 },
33 "devDependencies": { 32 "devDependencies": {
34 "concurrently": "^4.1.0", 33 "concurrently": "^4.1.0",
......
1 +dev.js
...\ No newline at end of file ...\ No newline at end of file
1 -// development모드이면 process.env.NODE_ENV는 development라고 나오고 1 +if (process.env.NODE_ENV === 'production') {
2 -// production모드이면 production이라고 나옴 2 + module.exports = require('./prod');
3 -if(process.env.NODE_ENV === 'production') // === 는 형변환을 하지 않음. 3 +} else {
4 - // ex) true == 1 -> return true 4 + module.exports = require('./dev');
5 - // true === 1 -> return false
6 -{
7 - module.exports = require('./prod')
8 -}
9 -
10 -else
11 -{
12 - module.exports = require('./dev')
13 } 5 }
...\ No newline at end of file ...\ No newline at end of file
......
1 -module.exports ={ 1 +module.exports = {
2 - mongoURI: process.env.mongo_URI 2 + mongoURI:process.env.MONGO_URI
3 } 3 }
...\ No newline at end of file ...\ No newline at end of file
......
1 -const express = require("express") 1 +const express = require("express");
2 -const app = express() 2 +const app = express();
3 -const port = process.env.PORT || 5000
4 -
5 const path = require("path"); 3 const path = require("path");
6 const cors = require('cors') 4 const cors = require('cors')
7 5
8 -// body-parser 가져옴 6 +const bodyParser = require("body-parser");
9 -const bodyParser = require('body-parser')
10 -// bodyParser option
11 -app.use(bodyParser.urlencoded({ extended: true })) //application/x-www-form-urlencoded로 된 데이터를 분석해서 가져옴
12 -app.use(bodyParser.json()) // application/json 타입으로 된 데이터를 분석해서 가져옴
13 const cookieParser = require("cookie-parser"); 7 const cookieParser = require("cookie-parser");
14 -app.use(cookieParser()); 8 +
15 const config = require("./config/key"); 9 const config = require("./config/key");
16 10
11 +// const mongoose = require("mongoose");
12 +// mongoose
13 +// .connect(config.mongoURI, { useNewUrlParser: true })
14 +// .then(() => console.log("DB connected"))
15 +// .catch(err => console.error(err));
17 16
18 const mongoose = require("mongoose"); 17 const mongoose = require("mongoose");
19 - 18 +const connect = mongoose.connect(config.mongoURI, { useNewUrlParser: true, useUnifiedTopology: true })
20 - 19 + .then(() => console.log('MongoDB Connected...'))
21 -const connect = mongoose.connect(config.mongoURI,
22 - {
23 - useNewUrlParser: true, useUnifiedTopology: true,
24 - useCreateIndex: true, useFindAndModify: false
25 - })
26 - .then(() => console.log('MongoDB ---> Connected'))
27 .catch(err => console.log(err)); 20 .catch(err => console.log(err));
28 21
29 app.use(cors()) 22 app.use(cors())
30 23
24 +app.use(bodyParser.urlencoded({ extended: true }));
25 +app.use(bodyParser.json());
26 +app.use(cookieParser());
31 27
32 app.use('/api/users', require('./routes/users')); 28 app.use('/api/users', require('./routes/users'));
33 app.use('/api/product', require('./routes/product')); 29 app.use('/api/product', require('./routes/product'));
34 30
35 -// 업로드 하려고 31 +
32 +//use this to show the image you have in node js server to client (react js)
33 +//https://stackoverflow.com/questions/48914987/send-image-path-from-node-js-express-server-to-react-client
36 app.use('/uploads', express.static('uploads')); 34 app.use('/uploads', express.static('uploads'));
37 35
36 +// Serve static assets if in production
38 if (process.env.NODE_ENV === "production") { 37 if (process.env.NODE_ENV === "production") {
38 +
39 + // Set static folder
39 app.use(express.static("client/build")); 40 app.use(express.static("client/build"));
41 +
42 + // index.html for all page routes
40 app.get("*", (req, res) => { 43 app.get("*", (req, res) => {
41 res.sendFile(path.resolve(__dirname, "../client", "build", "index.html")); 44 res.sendFile(path.resolve(__dirname, "../client", "build", "index.html"));
42 }); 45 });
43 } 46 }
44 47
48 +const port = process.env.PORT || 5000
45 49
46 app.listen(port, () => { 50 app.listen(port, () => {
47 - console.log(`Server ---> http://localhost:${port}`) 51 + console.log(`Server Running at ${port}`)
48 -}); 52 +});
...\ No newline at end of file ...\ No newline at end of file
......
1 const { User } = require('../models/User'); 1 const { User } = require('../models/User');
2 2
3 let auth = (req, res, next) => { 3 let auth = (req, res, next) => {
4 - // 인증 처리
5 - // 1. client 쿠키에서 토큰을 가져옴.
6 let token = req.cookies.w_auth; 4 let token = req.cookies.w_auth;
7 5
8 - // 2. 토큰을 복호화한 후 유저를 찾는다. (User.js에 findByToken(); 있음) 6 + User.findByToken(token, (err, user) => {
9 - User.findByToken(token, (err, user)=>{ 7 + if (err) throw err;
10 - // 에러가 있으면 8 + if (!user)
11 - if(err) throw err; 9 + return res.json({
12 - // 유저가 없으면 10 + isAuth: false,
13 - if(!user) return res.json({ isAuth:false, error: true}) 11 + error: true
14 - // 에러도 없고 유저도 있으면 12 + });
15 - req.token = token; // token과 user를 request에 넣어줌으로써 index.js에서 request 사용할 수 있음 13 +
14 + req.token = token;
16 req.user = user; 15 req.user = user;
17 next(); 16 next();
18 -}); 17 + });
19 - 18 +};
20 -// 3. 유저가 있으면 인증OK, 유저가 없으면 인증No!
21 -}
22 19
23 -// 이 auth를 다른 파일에서도 쓸 수 있도록
24 -module.exports = { auth };
...\ No newline at end of file ...\ No newline at end of file
20 +module.exports = { auth };
......
1 +const mongoose = require('mongoose');
2 +
3 +const paymentSchema = mongoose.Schema({
4 + user: { // 구매자 정보
5 + type: Array,
6 + default: []
7 + },
8 + data: { // 구매일자 정보
9 + type: Array,
10 + default: []
11 + },
12 + product: { // 상품 정보
13 + type: Array,
14 + default: []
15 + }
16 +
17 +
18 +}, { timestamps: true })
19 +
20 +
21 +const Payment = mongoose.model('Payment', paymentSchema);
22 +
23 +module.exports = { Payment }
...\ No newline at end of file ...\ No newline at end of file
1 +const mongoose = require('mongoose');
2 +const Schema = mongoose.Schema;
3 +
4 +const productSchema = mongoose.Schema({
5 + writer: {
6 + type: Schema.Types.ObjectId,
7 + ref: 'User'
8 + },
9 + title: {
10 + type: String,
11 + maxlength: 50
12 + },
13 + description: {
14 + type: String
15 + },
16 + price: {
17 + type: Number,
18 + default: 0
19 + },
20 + images: {
21 + type: Array,
22 + default: []
23 + },
24 + continents: {
25 + type: Number,
26 + default: 1
27 + },
28 + sold: {
29 + type: Number,
30 + maxlength: 100,
31 + default: 0
32 + },
33 + views: {
34 + type: Number,
35 + default: 0
36 + }
37 +}, { timestamps: true })
38 +
39 +
40 +productSchema.index({
41 + title:'text',
42 + description: 'text',
43 +}, {
44 + weights: {
45 + name: 5,
46 + description: 1,
47 + }
48 +})
49 +
50 +const Product = mongoose.model('Product', productSchema);
51 +
52 +module.exports = { Product }
...\ No newline at end of file ...\ No newline at end of file
1 -// monggoDB Model and Schema
2 const mongoose = require('mongoose'); 1 const mongoose = require('mongoose');
3 -// bcrypt 가져옴 2 +const bcrypt = require('bcrypt');
4 -const bcrypt = require('bcrypt') 3 +const saltRounds = 10;
5 -// bcrypt 사용하기 위해 salt를 생성하고 그걸 이용해 암호화 시킴
6 -const saltRounds = 10 // salt를 몇글자 할 건지
7 -
8 const jwt = require('jsonwebtoken'); 4 const jwt = require('jsonwebtoken');
9 -const moment = require("moment"); 5 +const moment = require('moment');
10 6
11 const userSchema = mongoose.Schema({ 7 const userSchema = mongoose.Schema({
12 name: { 8 name: {
13 - type:String, 9 + type: String,
14 - maxlength:50 10 + maxlength: 50
15 }, 11 },
16 email: { 12 email: {
17 - type:String, 13 + type: String,
18 - trim:true, 14 + trim: true,
19 - unique: 1 15 + unique: 1
20 }, 16 },
21 password: { 17 password: {
22 type: String, 18 type: String,
23 - minglength: 3 19 + minlength: 5
24 }, 20 },
25 - lastname: { 21 + adress: {
26 - type:String, 22 + type: String,
27 - maxlength: 50 23 + maxlength: 100
24 + },
25 + role: {
26 + type: Number,
27 + default: 0
28 }, 28 },
29 - role : { 29 + cart: {
30 - type:Number, // Number==1 이면 관리자, number==0 이면 일반 유저 30 + type: Array,
31 - default: 0 // default는 0 31 + default: []
32 + },
33 + history: {
34 + type: Array,
35 + default: []
32 }, 36 },
33 image: String, 37 image: String,
34 - token : { 38 + token: {
35 type: String, 39 type: String,
36 }, 40 },
37 - tokenExp :{ 41 + tokenExp: {
38 type: Number 42 type: Number
39 } 43 }
40 }) 44 })
41 45
42 -// index.js의 app.post('/register', (req, res)에 있는 46 +userSchema.pre('save', function (next) {
43 -// user model에 user 정보를 저장하기 전에 무엇을 한다는 것
44 -// function( next )를 해서 얘네가 끝난 다음에 다음걸 실행해라~
45 -userSchema.pre('save', function( next ) {
46 var user = this; 47 var user = this;
47 - 48 +
48 - if(user.isModified('password')){ // password를 변경할 때만 적용되도록.. 49 + if (user.isModified('password')) {
49 - 50 + console.log('password changed')
50 - // 비밀번호 암호화 (https://www.npmjs.com/package/bcrypt 에서 가져옴) 51 + bcrypt.genSalt(saltRounds, function (err, salt) {
51 - bcrypt.genSalt(saltRounds, (err, salt) => // salt를 만드는 함수 52 + if (err) return next(err);
52 - { 53 +
53 - if(err) return next(err) // 에러 나면 return err 54 + bcrypt.hash(user.password, salt, function (err, hash) {
54 - bcrypt.hash(user.password, salt, (err, hash) => { // bcrypt.hash(암호화되지 않은 pw, salt, function(err, 암호화된 비밀번호)) 55 + if (err) return next(err);
55 - if(err) return next(err) // 에러 나면 return err 56 + user.password = hash
56 - user.password = hash // 성공하면 user.password를 hash로 교체 57 + next()
57 - next()
58 }) 58 })
59 }) 59 })
60 } else { 60 } else {
...@@ -62,44 +62,35 @@ userSchema.pre('save', function( next ) { ...@@ -62,44 +62,35 @@ userSchema.pre('save', function( next ) {
62 } 62 }
63 }); 63 });
64 64
65 -userSchema.methods.comparePassword = function(plainPassword,cb){ 65 +userSchema.methods.comparePassword = function (plainPassword, cb) {
66 - 66 + bcrypt.compare(plainPassword, this.password, function (err, isMatch) {
67 - // 1. plainPassword가 1234567 암호화된 비밀번호 가 같은지 체크해야함 67 + if (err) return cb(err);
68 - // 그러면 plainPassword도 암호화해서 비교해야함. (복호화 할 수 없기 때문에) 68 + cb(null, isMatch)
69 - bcrypt.compare(plainPassword, this.password, function(err, isMatch)
70 - { // 에러가 나면 err callback, 아니면 isMatch
71 - if(err) return cb(err);
72 - cb(null, isMatch);
73 }) 69 })
74 } 70 }
75 71
76 -userSchema.methods.generateToken = function(cb) { 72 +userSchema.methods.generateToken = function (cb) {
77 var user = this; 73 var user = this;
78 - // jsonwebtoken을 이용해서 token 생성 74 + var token = jwt.sign(user._id.toHexString(), 'secret')
79 - var token = jwt.sign(user._id.toHexString(), 'secretToken') //database에 있는 id라서 _id
80 var oneHour = moment().add(1, 'hour').valueOf(); 75 var oneHour = moment().add(1, 'hour').valueOf();
81 76
82 user.tokenExp = oneHour; 77 user.tokenExp = oneHour;
83 user.token = token; 78 user.token = token;
84 - user.save(function (err, user){ 79 + user.save(function (err, user) {
85 - if(err) return cb(err)// 에러가 있다면 callback으로 에러 전달 80 + if (err) return cb(err)
86 - cb(null, user) // 에러가 없다면 err는 없고 user정보만 전달 81 + cb(null, user);
87 }) 82 })
88 } 83 }
89 84
90 userSchema.statics.findByToken = function (token, cb) { 85 userSchema.statics.findByToken = function (token, cb) {
91 var user = this; 86 var user = this;
92 87
93 - // 1. 토큰을 decoding 88 + jwt.verify(token, 'secret', function (err, decode) {
94 - jwt.verify(token, 'secretToken', function(err, decoded) { 89 + user.findOne({ "_id": decode, "token": token }, function (err, user) {
95 - // 2. 유저 아이디를 이용해서 유저를 찾은 다음에 클라이언트에서 가져온 토큰과 DB에 보관된 토큰이 일치하는지 확인. 90 + if (err) return cb(err);
96 - user.findOne({"_id": decoded, "token": token}, function(err, user){ // findOne :: mongoDB에 이미 있는 method 91 + cb(null, user);
97 - // 에러가 나면 92 + })
98 - if(err) return cb(err); 93 + })
99 - // 에러가 안나면
100 - cb(null, user)
101 - })
102 -})
103 } 94 }
104 95
105 const User = mongoose.model('User', userSchema); 96 const User = mongoose.model('User', userSchema);
......
1 const express = require('express'); 1 const express = require('express');
2 -const { User } = require("../models/User");
3 -const { auth } = require("../middleware/auth");
4 const router = express.Router(); 2 const router = express.Router();
3 +const { Product } = require("../models/Product");
5 const multer = require('multer'); 4 const multer = require('multer');
6 5
6 +const { auth } = require("../middleware/auth");
7 7
8 var storage = multer.diskStorage({ 8 var storage = multer.diskStorage({
9 - destination: function (req, file, cb) { 9 + destination: (req, file, cb) => {
10 - cb(null, 'uploads/') // 어느 폴더에 저장할건지 10 + cb(null, 'uploads/')
11 + },
12 + filename: (req, file, cb) => {
13 + cb(null, `${Date.now()}_${file.originalname}`)
11 }, 14 },
12 - filename: function (req, file, cb) { 15 + fileFilter: (req, file, cb) => {
13 - cb(null, Date.now() + '_' + file.originalname) // 이미지 이름 16 + const ext = path.extname(file.originalname)
14 - } 17 + if (ext !== '.jpg' || ext !== '.png') {
15 - }) 18 + return cb(res.status(400).end('JPG, PNG 확장자만 가능합니다.'), false);
16 - 19 + }
17 -var upload = multer({ storage: storage }).single("file"); 20 + cb(null, true)
18 -
19 -router.post('/image', (req, res) => {
20 -
21 - // 클라이언트로부터 받은 이미지 저장
22 - upload(req, res, (err) => {
23 - if (err) {
24 - return req.json({ success: false, err })
25 - }
26 - return res.json({ success: true, filePath: res.req.file.path, fileName: res.req.file.filename })
27 } 21 }
28 - )
29 -
30 }) 22 })
31 23
24 +var upload = multer({ storage: storage }).single("file")
25 +
26 +
27 +router.post("/uploadImage", auth, (req, res) => {
28 +
29 + upload(req, res, err => {
30 + if (err) {
31 + return res.json({ success: false, err })
32 + }
33 + return res.json({ success: true, image: res.req.file.path, fileName: res.req.file.filename })
34 + })
35 +
36 +});
37 +
38 +
39 +router.post("/uploadProduct", auth, (req, res) => {
40 +
41 + //save all the data we got from the client into the DB
42 + const product = new Product(req.body)
43 +
44 + product.save((err) => {
45 + if (err) return res.status(400).json({ success: false, err })
46 + return res.status(200).json({ success: true })
47 + })
48 +
49 +});
50 +
51 +
52 +router.post("/getProducts", (req, res) => {
53 +
54 + let order = req.body.order ? req.body.order : "desc";
55 + let sortBy = req.body.sortBy ? req.body.sortBy : "_id";
56 + let limit = req.body.limit ? parseInt(req.body.limit) : 100;
57 + let skip = parseInt(req.body.skip);
58 +
59 + let findArgs = {};
60 + let term = req.body.searchTerm;
61 +
62 + for (let key in req.body.filters) {
63 +
64 + if (req.body.filters[key].length > 0) {
65 + if (key === "price") {
66 + findArgs[key] = {
67 + $gte: req.body.filters[key][0],
68 + $lte: req.body.filters[key][1]
69 + }
70 + } else {
71 + findArgs[key] = req.body.filters[key];
72 + }
73 + }
74 + }
75 +
76 + console.log(findArgs)
77 +
78 + if (term) {
79 + Product.find(findArgs)
80 + .find({ $text: { $search: term } })
81 + .populate("writer")
82 + .sort([[sortBy, order]])
83 + .skip(skip)
84 + .limit(limit)
85 + .exec((err, products) => {
86 + if (err) return res.status(400).json({ success: false, err })
87 + res.status(200).json({ success: true, products, postSize: products.length })
88 + })
89 + } else {
90 + Product.find(findArgs)
91 + .populate("writer")
92 + .sort([[sortBy, order]])
93 + .skip(skip)
94 + .limit(limit)
95 + .exec((err, products) => {
96 + if (err) return res.status(400).json({ success: false, err })
97 + res.status(200).json({ success: true, products, postSize: products.length })
98 + })
99 + }
100 +
101 +});
102 +
103 +
104 +//?id=${productId}&type=single
105 +//id=12121212,121212,1212121 type=array
106 +router.get("/products_by_id", (req, res) => {
107 + let type = req.query.type
108 + let productIds = req.query.id
109 +
110 + console.log("req.query.id", req.query.id)
111 +
112 + if (type === "array") {
113 + let ids = req.query.id.split(',');
114 + productIds = [];
115 + productIds = ids.map(item => {
116 + return item
117 + })
118 + }
119 +
120 + console.log("productIds", productIds)
121 +
122 +
123 + //we need to find the product information that belong to product Id
124 + Product.find({ '_id': { $in: productIds } })
125 + .populate('writer')
126 + .exec((err, product) => {
127 + if (err) return res.status(400).send(err)
128 + return res.status(200).send(product)
129 + })
130 +});
131 +
132 +
133 +
32 module.exports = router; 134 module.exports = router;
......
1 const express = require('express'); 1 const express = require('express');
2 +const router = express.Router();
2 const { User } = require("../models/User"); 3 const { User } = require("../models/User");
4 +const { Product } = require('../models/Product');
3 const { auth } = require("../middleware/auth"); 5 const { auth } = require("../middleware/auth");
4 -const router = express.Router(); 6 +const { Payment } = require('../models/Payment');
7 +
8 +const async = require('async');
9 +
10 +
5 11
6 router.get("/auth", auth, (req, res) => { 12 router.get("/auth", auth, (req, res) => {
7 res.status(200).json({ 13 res.status(200).json({
...@@ -10,9 +16,11 @@ router.get("/auth", auth, (req, res) => { ...@@ -10,9 +16,11 @@ router.get("/auth", auth, (req, res) => {
10 isAuth: true, 16 isAuth: true,
11 email: req.user.email, 17 email: req.user.email,
12 name: req.user.name, 18 name: req.user.name,
13 - lastname: req.user.lastname, 19 + address: req.user.address,
14 role: req.user.role, 20 role: req.user.role,
15 image: req.user.image, 21 image: req.user.image,
22 + cart: req.user.cart,
23 + history: req.user.history
16 }); 24 });
17 }); 25 });
18 26
...@@ -33,12 +41,12 @@ router.post("/login", (req, res) => { ...@@ -33,12 +41,12 @@ router.post("/login", (req, res) => {
33 if (!user) 41 if (!user)
34 return res.json({ 42 return res.json({
35 loginSuccess: false, 43 loginSuccess: false,
36 - message: "Auth failed, email not found" 44 + message: "존재하지 않는 계정입니다."
37 }); 45 });
38 46
39 user.comparePassword(req.body.password, (err, isMatch) => { 47 user.comparePassword(req.body.password, (err, isMatch) => {
40 if (!isMatch) 48 if (!isMatch)
41 - return res.json({ loginSuccess: false, message: "Wrong password" }); 49 + return res.json({ loginSuccess: false, message: "비밀번호가 다릅니다." });
42 50
43 user.generateToken((err, user) => { 51 user.generateToken((err, user) => {
44 if (err) return res.status(400).send(err); 52 if (err) return res.status(400).send(err);
...@@ -63,4 +71,185 @@ router.get("/logout", auth, (req, res) => { ...@@ -63,4 +71,185 @@ router.get("/logout", auth, (req, res) => {
63 }); 71 });
64 }); 72 });
65 73
74 +
75 +router.get('/addToCart', auth, (req, res) => {
76 +
77 + User.findOne({ _id: req.user._id }, (err, userInfo) => {
78 + let duplicate = false;
79 +
80 + console.log(userInfo)
81 +
82 + userInfo.cart.forEach((item) => {
83 + if (item.id == req.query.productId) {
84 + duplicate = true;
85 + }
86 + })
87 +
88 +
89 + if (duplicate) {
90 + User.findOneAndUpdate(
91 + { _id: req.user._id, "cart.id": req.query.productId },
92 + { $inc: { "cart.$.quantity": 1 } },
93 + { new: true },
94 + (err, userInfo) => {
95 + if (err) return res.json({ success: false, err });
96 + res.status(200).json(userInfo.cart)
97 + }
98 + )
99 + } else {
100 + User.findOneAndUpdate(
101 + { _id: req.user._id },
102 + {
103 + $push: {
104 + cart: {
105 + id: req.query.productId,
106 + quantity: 1,
107 + date: Date.now()
108 + }
109 + }
110 + },
111 + { new: true },
112 + (err, userInfo) => {
113 + if (err) return res.json({ success: false, err });
114 + res.status(200).json(userInfo.cart)
115 + }
116 + )
117 + }
118 + })
119 +});
120 +
121 +
122 +router.get('/removeFromCart', auth, (req, res) => {
123 +
124 + User.findOneAndUpdate(
125 + { _id: req.user._id },
126 + {
127 + "$pull":
128 + { "cart": { "id": req.query._id } }
129 + },
130 + { new: true },
131 + (err, userInfo) => {
132 + let cart = userInfo.cart;
133 + let array = cart.map(item => {
134 + return item.id
135 + })
136 +
137 + Product.find({ '_id': { $in: array } })
138 + .populate('writer')
139 + .exec((err, cartDetail) => {
140 + return res.status(200).json({
141 + cartDetail,
142 + cart
143 + })
144 + })
145 + }
146 + )
147 +})
148 +
149 +
150 +router.get('/userCartInfo', auth, (req, res) => {
151 + User.findOne(
152 + { _id: req.user._id },
153 + (err, userInfo) => {
154 + let cart = userInfo.cart;
155 + let array = cart.map(item => {
156 + return item.id
157 + })
158 +
159 +
160 + Product.find({ '_id': { $in: array } })
161 + .populate('writer')
162 + .exec((err, cartDetail) => {
163 + if (err) return res.status(400).send(err);
164 + return res.status(200).json({ success: true, cartDetail, cart })
165 + })
166 +
167 + }
168 + )
169 +})
170 +
171 +
172 +
173 +
174 +router.post('/successBuy', auth, (req, res) => {
175 + let history = [];
176 + let transactionData = {};
177 +
178 + req.body.cartDetail.forEach((item) => {
179 + history.push({
180 + dateOfPurchase: Date.now(),
181 + name: item.title,
182 + id: item._id,
183 + price: item.price,
184 + quantity: item.quantity,
185 + paymentId: req.body.paymentData.paymentID
186 + })
187 + })
188 +
189 + // 페이팔에서 받아온 데이터 넣음
190 + transactionData.user = {
191 + id: req.user._id,
192 + name: req.user.name,
193 + address: req.user.address,
194 + email: req.user.email
195 + }
196 +
197 + transactionData.data = req.body.paymentData;
198 + transactionData.product = history
199 +
200 +
201 + User.findOneAndUpdate(
202 + { _id: req.user._id },
203 + { $push: { history: history }, $set: { cart: [] } },
204 + { new: true },
205 + (err, user) => {
206 + if (err) return res.json({ success: false, err });
207 +
208 +
209 + const payment = new Payment(transactionData)
210 + payment.save((err, doc) => {
211 + if (err) return res.json({ success: false, err });
212 + let products = [];
213 + doc.product.forEach(item => {
214 + products.push({ id: item.id, quantity: item.quantity })
215 + })
216 +
217 + async.eachSeries(products, (item, callback) => {
218 + Product.update(
219 + { _id: item.id },
220 + {
221 + $inc: {
222 + "sold": item.quantity
223 + }
224 + },
225 + { new: false },
226 + callback
227 + )
228 + }, (err) => {
229 + if (err) return res.json({ success: false, err })
230 + res.status(200).json({
231 + success: true,
232 + cart: user.cart,
233 + cartDetail: []
234 + })
235 + })
236 +
237 + })
238 + }
239 + )
240 +})
241 +
242 +
243 +router.get('/getHistory', auth, (req, res) => {
244 + User.findOne(
245 + { _id: req.user._id },
246 + (err, doc) => {
247 + let history = doc.history;
248 + if (err) return res.status(400).send(err)
249 + return res.status(200).json({ success: true, history })
250 + }
251 + )
252 +})
253 +
254 +
66 module.exports = router; 255 module.exports = router;
......