Commit e08aede8 authored by Kim, Chaerin's avatar Kim, Chaerin
Browse files

Merge remote-tracking branch 'origin/lsy' into cherry

parents 491a0815 5e8130f8
[{"C:\\Users\\sag06\\Desktop\\project\\search-page\\client\\src\\reportWebVitals.js":"1","C:\\Users\\sag06\\Desktop\\project\\search-page\\client\\src\\Pages\\Search.js":"2","C:\\Users\\sag06\\Desktop\\project\\search-page\\client\\src\\index.js":"3","C:\\Users\\sag06\\Desktop\\project\\search-page\\client\\src\\Components\\Paginations.js":"4","C:\\Users\\sag06\\Desktop\\project\\search-page\\client\\src\\Pages\\App.js":"5","C:\\Users\\sag06\\Desktop\\project\\search-page\\client\\src\\Pages\\Place.js":"6"},{"size":362,"mtime":1608689675665,"results":"7","hashOfConfig":"8"},{"size":7672,"mtime":1611722932802,"results":"9","hashOfConfig":"8"},{"size":976,"mtime":1611896115229,"results":"10","hashOfConfig":"8"},{"size":2199,"mtime":1610325754517,"results":"11","hashOfConfig":"8"},{"size":1456,"mtime":1611495914736,"results":"12","hashOfConfig":"8"},{"size":2146,"mtime":1611734244694,"results":"13","hashOfConfig":"8"},{"filePath":"14","messages":"15","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"1n20xnc",{"filePath":"16","messages":"17","errorCount":0,"warningCount":8,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"18","messages":"19","errorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"20","messages":"21","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"22","messages":"23","errorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"24","messages":"25","errorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},"C:\\Users\\sag06\\Desktop\\project\\search-page\\client\\src\\reportWebVitals.js",[],"C:\\Users\\sag06\\Desktop\\project\\search-page\\client\\src\\Pages\\Search.js",["26","27","28","29","30","31","32","33"],"C:\\Users\\sag06\\Desktop\\project\\search-page\\client\\src\\index.js",["34","35"],"C:\\Users\\sag06\\Desktop\\project\\search-page\\client\\src\\Components\\Paginations.js",[],"C:\\Users\\sag06\\Desktop\\project\\search-page\\client\\src\\Pages\\App.js",["36","37","38"],"C:\\Users\\sag06\\Desktop\\project\\search-page\\client\\src\\Pages\\Place.js",["39","40"],{"ruleId":"41","severity":1,"message":"42","line":2,"column":16,"nodeType":"43","messageId":"44","endLine":2,"endColumn":24},{"ruleId":"41","severity":1,"message":"45","line":13,"column":12,"nodeType":"43","messageId":"44","endLine":13,"endColumn":19},{"ruleId":"41","severity":1,"message":"46","line":13,"column":21,"nodeType":"43","messageId":"44","endLine":13,"endColumn":31},{"ruleId":"47","severity":1,"message":"48","line":33,"column":8,"nodeType":"49","endLine":33,"endColumn":10,"suggestions":"50"},{"ruleId":"47","severity":1,"message":"51","line":37,"column":8,"nodeType":"49","endLine":37,"endColumn":21,"suggestions":"52"},{"ruleId":"47","severity":1,"message":"53","line":52,"column":8,"nodeType":"49","endLine":52,"endColumn":10,"suggestions":"54"},{"ruleId":"41","severity":1,"message":"55","line":67,"column":11,"nodeType":"43","messageId":"44","endLine":67,"endColumn":17},{"ruleId":"56","severity":1,"message":"57","line":128,"column":35,"nodeType":"58","endLine":128,"endColumn":76},{"ruleId":"41","severity":1,"message":"59","line":7,"column":8,"nodeType":"43","messageId":"44","endLine":7,"endColumn":13},{"ruleId":"41","severity":1,"message":"42","line":13,"column":3,"nodeType":"43","messageId":"44","endLine":13,"endColumn":11},{"ruleId":"41","severity":1,"message":"60","line":1,"column":17,"nodeType":"43","messageId":"44","endLine":1,"endColumn":26},{"ruleId":"41","severity":1,"message":"61","line":2,"column":10,"nodeType":"43","messageId":"44","endLine":2,"endColumn":14},{"ruleId":"41","severity":1,"message":"62","line":4,"column":64,"nodeType":"43","messageId":"44","endLine":4,"endColumn":75},{"ruleId":"41","severity":1,"message":"63","line":3,"column":26,"nodeType":"43","messageId":"44","endLine":3,"endColumn":32},{"ruleId":"47","severity":1,"message":"64","line":34,"column":6,"nodeType":"49","endLine":34,"endColumn":8,"suggestions":"65"},"no-unused-vars","'Redirect' is defined but never used.","Identifier","unusedVar","'showSet' is assigned a value but never used.","'setShowSet' is assigned a value but never used.","react-hooks/exhaustive-deps","React Hook useEffect has a missing dependency: 'getAssociation'. Either include it or remove the dependency array.","ArrayExpression",["66"],"React Hook useEffect has a missing dependency: 'index'. Either include it or remove the dependency array. You can also replace multiple useState variables with useReducer if 'setPagePlace' needs the current value of 'index'.",["67"],"React Hook useEffect has missing dependencies: 'getAssociation', 'props.history', 'search', and 'state'. Either include them or remove the dependency array.",["68"],"'places' is assigned a value but never used.","react/jsx-no-duplicate-props","No duplicate props allowed","JSXAttribute","'axios' is defined but never used.","'useEffect' is defined but never used.","'Link' is defined but never used.","'FormControl' is defined but never used.","'Button' is defined but never used.","React Hook useEffect has missing dependencies: 'getReview' and 'infiniteScroll'. Either include them or remove the dependency array.",["69"],{"desc":"70","fix":"71"},{"desc":"72","fix":"73"},{"desc":"74","fix":"75"},{"desc":"76","fix":"77"},"Update the dependencies array to be: [getAssociation]",{"range":"78","text":"79"},"Update the dependencies array to be: [association, index]",{"range":"80","text":"81"},"Update the dependencies array to be: [getAssociation, props.history, search, state]",{"range":"82","text":"83"},"Update the dependencies array to be: [getReview, infiniteScroll]",{"range":"84","text":"85"},[1248,1250],"[getAssociation]",[1358,1371],"[association, index]",[1954,1956],"[getAssociation, props.history, search, state]",[1102,1104],"[getReview, infiniteScroll]"]
\ No newline at end of file
[{"C:\\Users\\sag06\\Desktop\\project\\search-page\\client\\src\\index.js":"1","C:\\Users\\sag06\\Desktop\\project\\search-page\\client\\src\\Bookmark.js":"2"},{"size":1382,"mtime":1611896266675,"results":"3","hashOfConfig":"4"},{"size":5395,"mtime":1611896266500,"results":"5","hashOfConfig":"4"},{"filePath":"6","messages":"7","errorCount":1,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},"1n20xnc",{"filePath":"8","messages":"9","errorCount":0,"warningCount":12,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},"C:\\Users\\sag06\\Desktop\\project\\search-page\\client\\src\\index.js",["10"],"C:\\Users\\sag06\\Desktop\\project\\search-page\\client\\src\\Bookmark.js",["11","12","13","14","15","16","17","18","19","20","21","22"],{"ruleId":null,"fatal":true,"severity":2,"message":"23","line":26,"column":2},{"ruleId":"24","severity":1,"message":"25","line":2,"column":10,"nodeType":"26","messageId":"27","endLine":2,"endColumn":15},{"ruleId":"24","severity":1,"message":"28","line":2,"column":39,"nodeType":"26","messageId":"27","endLine":2,"endColumn":43},{"ruleId":"24","severity":1,"message":"29","line":7,"column":8,"nodeType":"26","messageId":"27","endLine":7,"endColumn":13},{"ruleId":"24","severity":1,"message":"30","line":16,"column":12,"nodeType":"26","messageId":"27","endLine":16,"endColumn":16},{"ruleId":"24","severity":1,"message":"31","line":16,"column":18,"nodeType":"26","messageId":"27","endLine":16,"endColumn":25},{"ruleId":"24","severity":1,"message":"32","line":17,"column":12,"nodeType":"26","messageId":"27","endLine":17,"endColumn":17},{"ruleId":"24","severity":1,"message":"33","line":17,"column":19,"nodeType":"26","messageId":"27","endLine":17,"endColumn":27},{"ruleId":"24","severity":1,"message":"34","line":18,"column":12,"nodeType":"26","messageId":"27","endLine":18,"endColumn":17},{"ruleId":"24","severity":1,"message":"35","line":19,"column":12,"nodeType":"26","messageId":"27","endLine":19,"endColumn":17},{"ruleId":"24","severity":1,"message":"36","line":19,"column":19,"nodeType":"26","messageId":"27","endLine":19,"endColumn":27},{"ruleId":"24","severity":1,"message":"37","line":22,"column":12,"nodeType":"26","messageId":"27","endLine":22,"endColumn":19},{"ruleId":"38","severity":1,"message":"39","line":64,"column":8,"nodeType":"40","endLine":64,"endColumn":10,"suggestions":"41"},"Parsing error: Unexpected token\n\n 24 | <Route exact path=\"/\" component={App} />\n 25 | <Route path=\"/search\" component={Search} />\n> 26 | <<<<<<< HEAD\n | ^\n 27 | <Route path=\"/place\" component={Place} />\n 28 | =======\n 29 | <Route path='/signup' component={Signup}/>","no-unused-vars","'Alert' is defined but never used.","Identifier","unusedVar","'Form' is defined but never used.","'Place' is defined but never used.","'page' is assigned a value but never used.","'setPage' is assigned a value but never used.","'index' is assigned a value but never used.","'setIndex' is assigned a value but never used.","'error' is assigned a value but never used.","'state' is assigned a value but never used.","'setState' is assigned a value but never used.","'showSet' is assigned a value but never used.","react-hooks/exhaustive-deps","React Hook useEffect has a missing dependency: 'getBookmark'. Either include it or remove the dependency array.","ArrayExpression",["42"],{"desc":"43","fix":"44"},"Update the dependencies array to be: [getBookmark]",{"range":"45","text":"46"},[2225,2227],"[getBookmark]"]
\ No newline at end of file
......@@ -6,7 +6,7 @@ This project was bootstrapped with [Create React App](https://github.com/faceboo
In the project directory, you can run:
### `yarn start`
### `npm start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
......@@ -14,12 +14,12 @@ Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.\
You will also see any lint errors in the console.
### `yarn test`
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `yarn build`
### `npm run build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
......@@ -29,7 +29,7 @@ Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `yarn eject`
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
......@@ -65,6 +65,6 @@ This section has moved here: [https://facebook.github.io/create-react-app/docs/a
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
### `yarn build` fails to minify
### `npm run build` fails to minify
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
import React, { useState, useEffect } from 'react'
import { Alert, Col, Card, Container, Form, Row, Button, Nav, Navbar } from "react-bootstrap"
import axios from "axios"
import catchErrors from './utils/catchErrors.js'
import { isAuthenticated } from './utils/auth'
import * as Icon from 'react-bootstrap-icons';
import Place from './Components/Place.js'
const INIT_PAGE = {
bookmark: []
}
function Bookmark() {
const [page, setPage] = useState(INIT_PAGE)
const [index, setIndex] = useState(1);
const [error, setError] = useState('')
const [state, setState] = useState(false);
const [bookmark, setBookmark] = useState([false, false, false, false])
const [pagePlace, setPagePlace] = useState([])
const [showSet, setShowSet] = useState([false, false, false, false]);
const user = isAuthenticated()
async function getBookmark() {
try {
const response = await axios.get(`/api/users/bookmark?ID=${user}`)
setPagePlace(response.data.bookmark)
} catch (error) {
catchErrors(error, setError)
}
}
async function handleBookmark(index) {
if (!bookmark[index]) {
console.log(pagePlace[index])
try {
const response = await axios.put(`/api/users/bookmark?ID=${user}&place=${pagePlace[index]._id}`)
alert(response.data, '북마크가 저장되었습니다.')
const showArr = bookmark
showArr[index] = true
setBookmark(showArr)
console.log("bookmark=", bookmark)
} catch (error) {
catchErrors(error, setError)
}
} else {
try {
const response = await axios.delete(`/api/users/bookmark?ID=${user}`)
alert(response.data, '저장된 북마크가 삭제되었습니다.')
const showArr = bookmark
showArr[index] = false
setBookmark(showArr)
console.log("bookmark=", bookmark)
} catch (error) {
catchErrors(error, setError)
}
}
}
useEffect(() => {
getBookmark()
}, [])
return (
<Container>
<Navbar bg="info" variant="dark">
<Navbar.Brand href="/">북마크</Navbar.Brand>
<Nav className="mr-auto">
<Nav.Link href="/">Home</Nav.Link>
</Nav>
</Navbar>
<Row className="d-flex flex-wrap">
{console.log("#####################33", pagePlace)}
{pagePlace.map((place, index) => {
return (
<Col key={index} md={6} >
<Card align="center" border="info" style={{ margin: "3%" }}>
<Card.Title className="d-flex justify-content-center" style={{ margin: "3%", fontSize: '200%', fontWeight: 'bold' }} >{place.name}
{user ?
<Button
variant={bookmark[index] ? "primary" : "light"}
onClick={() => handleBookmark(index, place)}>
<Icon.BookmarkStarFill size={35} />
{console.log("bookmark", bookmark)}
{console.log("bookmark[index]", bookmark[index])}</Button> : null}
</Card.Title>
<Card.Img variant="top" style={{ padding: "5%", width: "100%", height: "340px" }} src={place.img} />
<Card.Body >
<Card.Text style={{ overflow: 'auto', fontSize: '25px', width: '100%', height: "80px" }} >
{place.address} </Card.Text>
<Button variant="info" onClick={() => {
const showArr = [false, false, false, false]
showArr[index] = true
setShowSet(showArr)
}}>{place.name} 자세히 살펴보기</Button>
{/* <Place place={place} index={index} show={showSet[index]} onHide={() => setShowSet([false, false, false, false])} /> */}
</Card.Body>
</Card>
</Col>
)
})}
</Row>
</Container>
)
}
export default Bookmark
// async function handleSubmit(e){
// setState(true); //버튼이 눌려서 handlesubmit이될때 setState값이 true로 바뀐다
// try { //respons 서버에 post로 요청하여 데이터를 받아온다
// const response = await axios.post('/api/users/bookmark', page)
// setSuccess(true)
// } catch (error) {
// console.log(error)
// catchErrors(error, setError)
// }
// }
// useEffect(() => {
// getBookmark(user)
// }, [user])
\ No newline at end of file
import React, { useState, useEffect } from 'react'
import { Alert, Col, Container, Form, Row, Button, Spinner } from "react-bootstrap"
import axios from "axios"
import catchErrors from '../utils/catchErrors'
import { Redirect } from 'react-router-dom'
import { handleLogin } from '../utils/auth'
const INIT_USER = {
email: '',
password: ''
} //초기 유저에 이메일 비밀번호 설정
function Login() {
//const [<상태 값 저장 변수>, <상태 값 갱신 함수>] = useState(<상태 초기 값>);
const [user, setUser] = useState(INIT_USER)
const [disabled, setDisabled] = useState(true)
const [error, setError] = useState('')
const [success, setSuccess] = useState(false)
const [loading, setLoading] = useState(false)
useEffect(() => { //참거짓 판단 값들의 원소들이 element로 들어간다
const isUser = Object.values(user).every(el => Boolean(el))
isUser ? setDisabled(false) : setDisabled(true)
}, [user])
function handleChange(event) {
const {name, value} = event.target //{}안에 값을 이벤트.타겟에 지정?한다
setUser({...user, [name]: value})
}
async function handleSubmit(event) {
event.preventDefault() //리셋을 막는다
try {
setLoading(true)
setError('') //값을 배열로 설정
const response = await axios.post('/api/auth/login', user)
console.log(response.data)
handleLogin(response.data.userId)//로그인했을때 userid data를 넣는다
setSuccess(true)
} catch (error) {
catchErrors(error, setError)
} finally {
setLoading(false)
}
}
if (success) {
console.log('success', success)
return <Redirect to= '/'/> //성공하면 홈화면으로 간다
}
return (
<Container>
<Row className= 'vh-100 flex-column align-items-center justify-content-center'>
<h2>로그인</h2>
<Col md={6}>
{error && <Alert variant='danger'>
{error}
</Alert>}
<Form onSubmit={handleSubmit}>
<Form.Group>
<Form.Label>이메일</Form.Label>
<Form.Control name='email' type='email'value={user.email} onChange={handleChange}/>
</Form.Group>
<Form.Group>
<Form.Label>비밀번호</Form.Label>
<Form.Control name='password' type='password' value={user.password} onChange={handleChange}/>
</Form.Group>
<Button disabled={disabled || setLoading} type='submit'
block>
{loading && <Spinner as= 'span' animation= 'border' size='sm'
role= 'status' aria-hidden= 'true' />}{' '}확인</Button>
</Form>
</Col>
</Row>
</Container>
)
}
export default Login
......@@ -6,27 +6,47 @@ import React from 'react';
function Paginations(props) {
return (
<Pagination>
<>
{(props.endPage > 5) ?
<Pagination >
<Pagination.First onClick={() => props.handlePage(1)} />
{props.index === 1 ? <Pagination.Prev onClick={()=>props.handlePage(props.index)} /> : <Pagination.Prev onClick={()=>props.handlePage(props.index - 1)} />}
{props.index === props.endPage-1 ? <Pagination.Item onClick={()=>props.handlePage(props.index - 3)}>{props.index - 3}</Pagination.Item> : ""}
{props.index === props.endPage ? <Pagination.Item onClick={()=>props.handlePage(props.index - 4)}>{props.index - 4}</Pagination.Item> : ""}
{props.index === props.endPage ? <Pagination.Item onClick={()=>props.handlePage(props.index - 3)}>{props.index - 3}</Pagination.Item> : ""}
{props.index < 3 ? "" : <Pagination.Item onClick={()=>props.handlePage(props.index - 2)}>{props.index - 2}</Pagination.Item>}
{props.index === 1 ? "" : <Pagination.Item onClick={()=>props.handlePage(props.index - 1)}>{props.index - 1}</Pagination.Item>}
{props.index === 1 ? <Pagination.Prev onClick={() => props.handlePage(props.index)} /> : <Pagination.Prev onClick={() => props.handlePage(props.index - 1)} />}
{props.index === props.endPage - 1 ? <Pagination.Item onClick={() => props.handlePage(props.index - 3)}>{props.index - 3}</Pagination.Item> : ""}
{props.index === props.endPage ? <Pagination.Item onClick={() => props.handlePage(props.index - 4)}>{props.index - 4}</Pagination.Item> : ""}
{props.index === props.endPage ? <Pagination.Item onClick={() => props.handlePage(props.index - 3)}>{props.index - 3}</Pagination.Item> : ""}
{props.index < 3 ? "" : <Pagination.Item onClick={() => props.handlePage(props.index - 2)}>{props.index - 2}</Pagination.Item>}
{props.index === 1 ? "" : <Pagination.Item onClick={() => props.handlePage(props.index - 1)}>{props.index - 1}</Pagination.Item>}
<Pagination.Item active>{props.index}</Pagination.Item>
{props.index === props.endPage ? "" : <Pagination.Item onClick={()=>props.handlePage(props.index + 1)}>{props.index + 1}</Pagination.Item>}
{props.index > props.endPage-2 ? "" : <Pagination.Item onClick={()=>props.handlePage(props.index + 2)}>{props.index + 2}</Pagination.Item>}
{props.index === 1 ? <Pagination.Item onClick={()=>props.handlePage(props.index + 3)}>{props.index + 3}</Pagination.Item> : ""}
{props.index === 1 ? <Pagination.Item onClick={()=>props.handlePage(props.index + 4)}>{props.index + 4}</Pagination.Item> : ""}
{props.index === 2 ? <Pagination.Item onClick={()=>props.handlePage(props.index + 3)}>{props.index + 3}</Pagination.Item> : ""}
{props.index === props.endPage ? "" : <Pagination.Next onClick={()=>props.handlePage(props.index + 1)} />}
{props.index === props.endPage ? "" : <Pagination.Item onClick={() => props.handlePage(props.index + 1)}>{props.index + 1}</Pagination.Item>}
{props.index > props.endPage - 2 ? "" : <Pagination.Item onClick={() => props.handlePage(props.index + 2)}>{props.index + 2}</Pagination.Item>}
{props.index === 1 ? <Pagination.Item onClick={() => props.handlePage(props.index + 3)}>{props.index + 3}</Pagination.Item> : ""}
{props.index === 1 ? <Pagination.Item onClick={() => props.handlePage(props.index + 4)}>{props.index + 4}</Pagination.Item> : ""}
{props.index === 2 ? <Pagination.Item onClick={() => props.handlePage(props.index + 3)}>{props.index + 3}</Pagination.Item> : ""}
{props.index === props.endPage ? "" : <Pagination.Next onClick={() => props.handlePage(props.index + 1)} />}
<Pagination.Last onClick={() => props.handlePage(props.endPage)} />
</Pagination>
:
<Pagination>
{props.index === 1 ? "" : <Pagination.First onClick={() => props.handlePage(1)} />}
{props.index === 1 ? "" : <Pagination.Prev onClick={() => props.handlePage(props.index - 1)} />}
<Pagination.Item onClick={() => props.handlePage(1)} active={props.index === 1}>1</Pagination.Item>
{props.endPage > 1 ? <Pagination.Item onClick={() => props.handlePage(2)} active={props.index === 2}>2</Pagination.Item> : ""}
{props.endPage > 2 ? <Pagination.Item onClick={() => props.handlePage(3)} active={props.index === 3}>3</Pagination.Item> : ""}
{props.endPage > 3 ? <Pagination.Item onClick={() => props.handlePage(4)} active={props.index === 4}>4</Pagination.Item> : ""}
{props.endPage > 4 ? <Pagination.Item onClick={() => props.handlePage(5)} active={props.index === 5}>5</Pagination.Item> : ""}
{props.index === props.endPage ? "" : <Pagination.Next onClick={() => props.handlePage(props.index + 1)} />}
{props.index === props.endPage ? "" : <Pagination.Last onClick={() => props.handlePage(props.endPage)} />}
<Pagination.Last onClick={() =>props.handlePage(props.endPage)} />
</Pagination>
}
</>
)
}
......
import React from 'react'
import { Route, Redirect } from 'react-router-dom'
import { isAuthenticated } from "../utils/auth";
function PrivateRoute({path, children}) {
if (isAuthenticated()) {
return (
<Route path={path}>
{children}
</Route>
)
} else {
return (
<Redirect to='/login' />
)
}
}
export default PrivateRoute
import React, { useState, useEffect } from 'react'
import { Col, Container, Form, Row, Button, Alert } from "react-bootstrap"
import axios from "axios"
import catchErrors from '../utils/catchErrors'
import { Redirect } from 'react-router-dom'
const INIT_USER = {
name: '',
email: '',
password: ''
} // 초기유저에 이름, 이메일, 비밀번호를 빈배열로 지정한다.
function Signup() {
const [user, setUser] = useState(INIT_USER)
const [disabled, setDisabled] = useState(true)
const [error, setError] = useState('')
const [success, setSuccess] = useState(false)
useEffect(() => {
const isUser = Object.values(user).every(el => Boolean(el))
isUser ? setDisabled(false) : setDisabled(true)
}, [user])
//바뀌는것이 있을때 이벤트가 발생하는 함수
function handleChange(event) {
const {name, value} = event.target
setUser({...user, [name]: value})
}
async function handleSubmit(event) {
event.preventDefault() //submit을 누르면 이벤트가 실행
try {
setError('') //post로 경로를 지정해서 서버에서 값을 받는다
const response = await axios.post('/api/users/signup', user)
console.log(response.data)
console.log(user)
setSuccess(true)
} catch (error) {
console.log(error)
catchErrors(error, setError)
}
}
//성공시 알림창이 뜨고 원래 페이지로 돌아간다
if (success) {
alert('회원가입 되었습니다.')
return <Redirect to='/'/>
}
return (
<Container>
<Row className= 'vh-100 flex-column align-items-center justify-content-center'>
<h2>회원가입</h2>
<Col md={5}>
{error && <Alert variant='danger'>
{error}
</Alert>}
<Form onSubmit={handleSubmit}>
<Form.Group>
<Form.Label>이름</Form.Label>
<Form.Control name='name' value={user.name} onChange={handleChange}/>
</Form.Group>
<Form.Group>
<Form.Label>이메일</Form.Label>
<Form.Control name='email' type='email'value={user.email} onChange={handleChange}/>
</Form.Group>
<Form.Group>
<Form.Label>비밀번호</Form.Label>
<Form.Control name='password' type='password' value={user.password} onChange={handleChange}/>
</Form.Group>
<Button disabled={disabled} type='submit' block>확인</Button>
</Form>
</Col>
</Row>
</Container>
)
}
export default Signup
import React, { useState } from 'react';
import { Redirect } from 'react-router-dom';
import ohuh from '../ohuh.PNG';
import { Container, Row, Form, Image, InputGroup, Button, Col } from 'react-bootstrap';
import ohuh from '../ohuh.PNG'
import { Container, Row, Form, Image, InputGroup, Button, Col, Nav } from 'react-bootstrap';
import { handleLogout, isAuthenticated } from '../utils/auth.js'
function App() {
const [state, setState] = useState(false);
const [search, setSearch] = useState("");
const user = isAuthenticated()
if (state !== false) {
return <Redirect to={`/search?keyword=${search}`} />;
......@@ -13,21 +15,31 @@ function App() {
const handleChange = (e) => {
setSearch(e.target.value);
}
} //바뀌는 것이 있을때 이벤트 발생
const handleSubmit = () => {
setState(true);
}
} //submit 버튼을 누르면 state 값을 true로 바뀐다
return (
<Container className="vh-100 d-flex justify-content-md-center align-items-center">
<Col md={6} lassName=" d-flex justify-content-center">
<Row style={{marginBottom:20}}>
<Col md={6} style={{ marginTop: 140 }}>
<Nav className="justify-content-end" bg="#fff" variant="light" >
{user ? <Nav.Link onClick={() => handleLogout()}>로그아웃</Nav.Link>
: (
<>
<Nav.Link href="/signup">회원가입</Nav.Link>
<Nav.Link href="/login">로그인</Nav.Link>
</>
)}
<Nav.Link href='/bookmark'>북마크</Nav.Link>
</Nav>
<Row style={{ marginBottom: 20 }}>
<Image src={ohuh} />
</Row>
<Row style={{marginBottom:500}}>
<Row style={{ marginBottom: 500 }}>
<Form className="vw-100" onSubmit={handleSubmit}>
<InputGroup>
<InputGroup style={{ width: 560 }}>
<Form.Control
size="lg"
placeholder="검색어를 입력하세요."
......
import React, { useState, useEffect } from 'react';
import { Link, Redirect } from 'react-router-dom';
import ohuh from '../ohuh-sm.PNG';
import { Container, Form, Row, Col, Card, Image, InputGroup, FormControl, Button } from 'react-bootstrap';
import Place from '../Components/Place';
import { Container, Form, Row, Col, Card, Image, InputGroup, FormControl, Button, Nav } from 'react-bootstrap';
import Paginations from '../Components/Paginations';
import axios from 'axios';
import queryString from 'query-string' // react route dom 에 있다. 확인해보기
import queryString from 'query-string'
import * as Icon from 'react-bootstrap-icons';
import { isAuthenticated } from '../utils/auth';
import catchErrors from '../utils/catchErrors'
function Search(props) {
const endPage = 10;
const [state, setState] = useState(false);
const [index, setIndex] = useState(1);
const [showSet, setShowSet] = useState([false, false, false, false]);
const [search, setSearch] = useState(queryString.parse(props.location.search).keyword);
const [pagePlace, setPagePlace] = useState([]);
const [association, setAssociation] = useState([])
const [bookmark, setBookmark] = useState([false, false, false, false])
const user = isAuthenticated()
const [mobile, setMobile] = useState();
const [association, setAssociation] = useState([{ name: " ", address: " ", img: " " }])
const [pagePlace, setPagePlace] = useState([{ name: " ", address: " ", img: " " }, { name: " ", address: " ", img: " " }])
const [endPage, setEndPage] = useState(1)
const [error, setError] = useState('')
async function getBookmark() {
try {
const response = await axios.get(`/api/users/bookmark?ID=${user}`)
// setBookmark(response.data.bookmark)
} catch (error) {
catchErrors(error, setError)
}
}
const getAssociation = () => {
axios.get(`/api/search/association?keyword=${search}`)
......@@ -33,8 +51,17 @@ function Search(props) {
}, []);
useEffect(() => {
if (association.length < 3) {
setPagePlace(paginate(association, index, association.length))
}, [association]);
}
else {
setPagePlace(paginate(association, index, 4))
}
console.log("뿌릴 data1", pagePlace)
setEndPage(Math.floor((association.length / 4)))
console.log("7489309484839", endPage)
}, [association, index])
useEffect(() => {
getAssociation()
......@@ -45,11 +72,11 @@ function Search(props) {
// state: { id: search },
// }} />;
props.history.push('/search?keyword=' + search)
// setState(false)
setState(false)
// console.log("search야", search)
} window.addEventListener("scroll", infiniteScroll);
return () => { window.removeEventListener("scroll", infiniteScroll); }
}, []);
}, [state]);
const infiniteScroll = () => {
const { documentElement, body } = document;
......@@ -99,6 +126,8 @@ function Search(props) {
const handlePage = (num) => {
setIndex(num);
console.log("pagenation num", num)
console.log(index)
}
......@@ -108,17 +137,49 @@ function Search(props) {
const handleSubmit = (e) => {
e.preventDefault()
setState(true);
setState(true)
setIndex(1)
}
function paginate(items, pageNumber, itemNumber) {
const page = [];
const startIndex = (pageNumber - 1) * itemNumber
for (var i = 0; i < itemNumber; i++) {
page.push(items[(startIndex + i)])
}
console.log("뿌릴 data22222222222222222", page)
return page
}
//usestate 쓰거나 한번에 useeffect에 넣기
async function handlebookmark(index) {
if (!bookmark[index]) {
console.log(pagePlace[index])
try {
const response = await axios.put(`/api/users/bookmark?ID=${user}&place=${pagePlace[index]._id}`)
alert(response.data, '북마크가 저장되었습니다.')
const showArr = bookmark
showArr[index] = true
setBookmark(showArr)
console.log("bookmark=", bookmark)
} catch (error) {
catchErrors(error, setError)
}
} else {
try {
const response = await axios.delete(`/api/users/bookmark?ID=${user}&place=${pagePlace[index]._id}`)
alert(response.data, '저장된 북마크가 삭제되었습니다.')
const showArr = bookmark
showArr[index] = false
setBookmark(showArr)
console.log("bookmark=", bookmark)
} catch (error) {
catchErrors(error, setError)
}
}
}
let time = new Date()
......@@ -141,13 +202,24 @@ function Search(props) {
</InputGroup>
</Form>
</Row>
{time.toLocaleString()}
{/* {time.toLocaleString()} */}
<Row className="d-flex flex-wrap">
{console.log("#####################33", pagePlace)}
{pagePlace.map((place, index) => {
return (
<Col key={index} md={6} >
<Card align="center" border="info" style={{ margin: "3%" }}>
<Card.Title style={{ margin: "3%", fontSize: '200%', fontWeight: 'bold' }} >{place.name}</Card.Title>
<Card.Title className="d-flex justify-content-center" style={{ margin: "3%", fontSize: '200%', fontWeight: 'bold' }} >{place.name}
{user ?
<Button
variant={bookmark[index] ? "primary" : "light"}
onClick={() => handlebookmark(index, place)}>
<Icon.BookmarkStarFill size={35} />
{console.log("bookmark", bookmark)}
{console.log("bookmark[index]", bookmark[index])}</Button> : null}
</Card.Title>
<Card.Img variant="top" style={{ padding: "5%", width: "100%", height: "340px" }} src={place.img} />
<Card.Body>
<Card.Text style={{ overflow: 'auto', fontSize: '25px', width: '100%', height: "80px" }} >
......
......@@ -2,10 +2,13 @@ import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import App from './Pages/App';
import App from './Pages/App'
import Search from './Pages/Search';
import axios from 'axios';
import reportWebVitals from './reportWebVitals';
import Signup from './Components/Signup'
import Login from './Components/Login'
import PrivateRoute from "./Components/PrivateRoute";
import Bookmark from "./Bookmark"
import {
BrowserRouter as Router,
Switch,
......@@ -21,6 +24,10 @@ ReactDOM.render(
<Route exact path="/" component={App} />
<Route path="/search" component={Search} />
<Route path="/place" component={Place} />
<Route path='/signup' component={Signup}/>
<Route path='/login' component={Login} />
<PrivateRoute path='/bookmark'><Bookmark/></PrivateRoute>
<Redirect path="/search" to="/search" />
</Switch>
</Router>
</React.StrictMode>,
......
import axios from "axios"
export function handleLogin(userId) { //로그인할때 로컬스토리지에 유저 아이디를 설정한다
localStorage.setItem('loginStatus', userId)
}
export async function handleLogout() {
localStorage.removeItem('loginStatus') //로컬스토리지에서 로그인상태를 지운다
await axios.get('/api/auth/logout')
window.location.href='/' //경로 지정
}
//유저가 로그인 했는 지 확인하는 함수
export function isAuthenticated() {
const userId = localStorage.getItem('loginStatus') //유저아이디를 로컬스토리지에서 가져와서 저장
if (userId) {
return userId
}else{
return false
}
}
\ No newline at end of file
function catchErrors(error, displayError) {
let errorMsg
if (error.response) {
errorMsg = error.response.data
console.log(errorMsg)
}else if (error.requset) {
errorMsg = error.requset
console.log(errorMsg)
} else {
errorMsg = error.message
console.log(errorMsg)
}
displayError(errorMsg)
}
export default catchErrors
\ No newline at end of file
......@@ -2,7 +2,8 @@ const config = {
env: process.env.NODE_ENV || 'development',
port: process.env.PORT || 3001,
jwtSecret: process.env.JWT_SECRET || 'My_Secret_Key',
mongoDbUri: process.env.MONGODB_URI || 'mongodb://localhost/search_page'
mongoDbUri: process.env.MONGODB_URI || 'mongodb://localhost/search_page',
cookieMaxAge: 60 * 60 * 24 * 7 * 1000
}
export default config
\ No newline at end of file
import User from "../models/User.js"
import bcrypt from "bcryptjs"
import jwt from "jsonwebtoken"
import config from "../config.js"
const login = async (req, res) => {
const { email, password } = req.body //구조분해해서 하나씩
console.log( email, password)
try {
const user = await User.findOne({email}).select('+password')
if (!user) {
return res.status(404).send(`${email}이 없습니다`)
}
const passwordMatch = await bcrypt.compare(password, user.password)
if (passwordMatch) {
const token = jwt.sign({userId: user._id}, config.jwtSecret,{
expiresIn: '7d'
})
res.cookie('token', token, {
maxAge: config.cookieMaxAge,
httpOnly: true,
secure: config.env === 'production'
})
res.json({userId: user._id})
} else {
res.status(401).send('비밀번호가 일치하지 않습니다')
}
} catch (error) {
console.log(error)
res.status(500).send('로그인 에러')
}
}
const logout = (req, res) => {
res.clearCookie('token')
res.send('Logout Successful')
}
export default { login, logout}
\ No newline at end of file
import Places from '../models/Place.js'
import cheerio from 'cheerio'
import fs from 'fs'
import axios from 'axios';
const searchPlace = async (req, res, next,) => {
......@@ -31,12 +30,10 @@ const searchPlace = async (req, res, next,) => {
const searchImg = async (req, res, next) => {
console.log("d213532513212osfnlagm2214124", req.places)
if (req.places.img !== "https://t1.daumcdn.net/thumb/R600x0/?fname=http%3A%2F%2Ft1.daumcdn.net%2Fqna%2Fimage%2F4b035cdf8372d67108f7e8d339660479dfb41bbd") {
console.log("333333333333333333333333333IMG@@@@@@@@@@@@@@@@@@@ 기존이미지줄력중")
req.places.times.push(new Date().toLocaleString())
console.log("세이브 전 111111111111111111", req.places)
const newPlaces = await new Places(req.places).save()
next()
} else if (req.places.img === "https://t1.daumcdn.net/thumb/R600x0/?fname=http%3A%2F%2Ft1.daumcdn.net%2Fqna%2Fimage%2F4b035cdf8372d67108f7e8d339660479dfb41bbd") {
......@@ -56,7 +53,6 @@ const searchImg = async (req, res, next) => {
req.places.times.push(new Date().toLocaleString())
// await Places.updateOne({ name: req.query.keyword }, { img: images, times: Place2.times })
// res.send(images)
console.log("세이브 전 222222222222222", req.places)
const newPlaces = await new Places(req.places).save()
next()
})
......@@ -64,48 +60,8 @@ const searchImg = async (req, res, next) => {
console.log("이미지 생성 오류발생!!")
}
}
// if (DuplicateCheckImg !== "https://t1.daumcdn.net/thumb/R600x0/?fname=http%3A%2F%2Ft1.daumcdn.net%2Fqna%2Fimage%2F4b035cdf8372d67108f7e8d339660479dfb41bbd") {
// console.log("333333333333333333333333333IMG@@@@@@@@@@@@@@@@@@@ 기존이미지줄력중")
// }
// else if (DuplicateCheckImg === "https://t1.daumcdn.net/thumb/R600x0/?fname=http%3A%2F%2Ft1.daumcdn.net%2Fqna%2Fimage%2F4b035cdf8372d67108f7e8d339660479dfb41bbd") {
// console.log("4444444444444444444444444444444444444444444444444")
// const imgUrl = "https://www.google.com/search?q=" + encodeURI(req.query.keyword) + "+site:tistory.com/&sxsrf=ALeKk023Dv08KQDodRmpB5222lQuzw2Vaw:1610612821100&source=lnms&tbm=isch"
// axios.get(imgUrl)
// .then(async (response) => {
// const html = response.data
// let name = req.query.keyword
// let $1 = cheerio.load(html);
// let images = $1('.RAyV4b').find('img').attr('src')
// req.places.img = images
// //사진만 업데이트
// // let Place2 = await Places.findOne({ name: req.query.keyword })
// req.places.times.push(new Date().toLocaleString())
// // await Places.updateOne({ name: req.query.keyword }, { img: images, times: Place2.times })
// // res.send(images)
// console.log("세이브 전 111111111111111111", req.places)
// const newPlaces = await new Places(req.places).save()
// next()
// })
// } else {
// console.log("IMG에러")
// }
// req.places.times.push(new Date().toLocaleString())
// console.log("세이브 전222222222222222222222")
// next()
// }
//맨 처음 검색하는 지역의 관광지의 경우 association을 받아올 수 없다.
const searchAssociation = async (req, res) => {
// let Place3 = await Places.findOne({ name: req.query.keyword })
let Place3 = req.places.address
// if (!Place) {
// res.send([])
// }
if (!Place3) {
console.log("asdfasdfasdf222222222222222222dsaf2222222222214123q5", Place3)
res.send({ error: "Place.address is null" })
......@@ -116,15 +72,10 @@ const searchAssociation = async (req, res) => {
let addressPlaces = new RegExp(`${addresse}`)
let responsePlaces = await Places.find({ address: addressPlaces }).sort({ updatedAt: -1 })
//몽구스나 몽고디비에 있는 sort 확인해보고 나열하기.
console.log("$$$$$$$$$$$4", responsePlaces)
// console.log("$$$$$$$$$$$4", responsePlaces)
res.send(responsePlaces)
}
// responsePlaces.map(Association => {
// AssociationsId.push(Association._id)
// })
// console.log("Associations = ", Associations)
// res.send(AssociationsId)
}
export default { searchImg, searchPlace, searchAssociation }
export default { searchImg, searchPlace, searchAssociation }
import User from "../models/User.js"
import isLength from 'validator/lib/isLength.js'
import isEmail from "validator/lib/isEmail.js"
import bcrypt from "bcryptjs";
const signup = async (req, res) => {
const { name, email, password } = req.body //구조분해해서 하나씩
console.log(name, email, password)
try {
if (!isLength(name, { min: 3, max: 10 })) {
return res.status(422).send('이름은 3-10자 사이입니다')
} else if (!isLength(password, { min: 6 })) {
return res.status(422).send('비밀번호는 6자 이상입니다')
} else if (!isEmail(email)) {
return res.status(422).send('유효하지 않은 이메일 형식입니다')
}
const user = await User.findOne({ email })
if (user) {
return res.status(422).send(`${email}이 이미 사용중입니다`)
}
const hash = await bcrypt.hash(password, 10)
const newUser = await new User({
name,
email,
password: hash
}).save() //save하면 몽고 db에 들어간다 save method가 promise
console.log(newUser)
res.json(newUser) //json형식으로 바꿔서 문자열로 보낸다 client쪽으로
} catch (error) { //다른 과정에서 에러가 나면 실행
console.log(error)
res.status(500).send('회원가입 에러')
}
}
const userById = async (req, res, next, id) => {
try {
const user = await User.findById(id)
if (!user) {
res.status(404).send('사용자를 찾을 수 없습니다')
}
req.profile = user
next()
} catch (error) {
console.log(error)
res.status(500).send('사용자 아이디 검색 실패')
}
}
const getBookmark = async (req, res) => {
await User.findOne({ _id: req.query.ID }).populate('bookmark').exec((err, bookmark) => {
console.log(bookmark, "dkssud")
res.send(bookmark)
})
}
const setBookmark = async (req, res) => {
console.log(req.query.ID, req.query.place, "여기에요 여기!!!!")
await User.updateOne({ _id: req.query.ID }, { $push: { bookmark: req.query.place } })
res.send("추가완료")
}
const deleteBookmark = async (req, res) => {
console.log(req.query.ID, req.query.place)
await User.updateOne({ _id: req.query.ID }, { $pull: { bookmark: req.query.place } })
res.send("삭제완료")
}
export default { signup, userById, getBookmark, setBookmark, deleteBookmark }
\ No newline at end of file
import mongoose from "mongoose";
const { String, Array, ObjectId } = mongoose.Schema.Types
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
select: false,
},
role: {
type: String,
required: true,
default: 'user',
enum: ['user', 'admin', 'root']
},
bookmark: [{
type: ObjectId,
ref: 'Place'
}]
}, {
timestamps: true
})
export default mongoose.models.User || mongoose.model('User', UserSchema)
import express from "express"
import authCtrl from "../controllers/auth.controller.js"
const router = express.Router()
router.route('/api/auth/login')
.post(authCtrl.login)
router.route('/api/auth/logout')
.get(authCtrl.logout)
export default router
\ No newline at end of file
import express from "express"
import userCtrl from '../controllers/user.controller.js'
const router = express.Router()
router.route('/api/users/signup')
.post(userCtrl.signup)
router.route('/api/users/bookmark')
.get(userCtrl.getBookmark)
.put(userCtrl.setBookmark)
.delete(userCtrl.deleteBookmark)
router.param('userId', userCtrl.userById)
export default router
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment