Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

상품검색 페이지 #105

Merged
merged 6 commits into from
Dec 20, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions web-apps/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"axios": "^0.19.0",
"clsx": "^1.0.4",
"immer": "^5.0.0",
"inko": "^1.1.1",
"intersection-observer": "^0.7.0",
"prop-types": "^15.7.2",
"react": "^16.12.0",
Expand Down
2 changes: 2 additions & 0 deletions web-apps/client/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Home from './pages/main';
import Filters from './pages/filters';
import Location from './pages/area';
import NewProduct from './pages/newProduct';
import Test from './pages/search';

import { FilterProvider } from './contexts/filters';
import { SnackbarProvider } from './contexts/snackbar';
Expand Down Expand Up @@ -41,6 +42,7 @@ export default () => {
<Route exact path='/' component={Home} />
<Route exact path='/category' component={Filters} />
<Route exact path='/location' component={Location} />
<Route exact path='/search' component={Test} />
</ThemeProvider>
<ThemeProvider theme={muiTheme}>
<Route exact path='/chat' component={TmpChat} />
Expand Down
73 changes: 73 additions & 0 deletions web-apps/client/src/components/search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React, { useRef, forwardRef, useImperativeHandle } from 'react';
import PropTypes from 'prop-types';

import { Grid } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';

import ClearIcon from '@material-ui/icons/HighlightOff';

const useStyles = makeStyles({
root: {
backgroundColor: 'rgb(244,244,244)',
padding: '0.2rem',
borderRadius: '0.5rem',
},
input: {
backgroundColor: 'rgb(244,244,244)',
border: 0,
flex: 1,
padding: 0,
height: '1.5rem',
},
icon: {
color: '#bebebe'
}
});

const Search = ({ onChange, onKeyDown }, ref) => {
const inputRef = useRef(null);
const classes = useStyles({});

useImperativeHandle(ref, () => ({
set: (input) => {
inputRef.current.value = input;
},
get: () => {
return inputRef.current.value;
},
}));

const onClearInput = () => {
inputRef.current.value = '';
};

return (
<Grid
container
justify='space-around'
alignItems='center'
className={classes.root}
>
<input
ref={inputRef}
placeholder='검색어를 입력하세요.'
className={classes.input}
onChange={onChange}
onKeyDown={onKeyDown}
/>
<ClearIcon onClick={onClearInput} className={classes.icon} />
</Grid>
);
};

Search.propTypes = {
onChange: PropTypes.func,
onKeyDown: PropTypes.func,
};

Search.defaultProps = {
onChange: () => { },
onKeyDown: () => { },
}

export default forwardRef(Search);
5 changes: 5 additions & 0 deletions web-apps/client/src/contexts/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const filterInfo = {
distance: 0,
localname: '전체',
CATEGORYLABEL: [],
keyword: '',
};

const TYPE = {
Expand All @@ -23,6 +24,7 @@ const TYPE = {
LOCAL_NAME: 5,
COORDINATE: 6,
DISTANCE: 7,
KEYWORD: 8,
};

const filterReducer = (state, { type, payload }) => {
Expand Down Expand Up @@ -60,6 +62,9 @@ const filterReducer = (state, { type, payload }) => {
}
return { ...state, distance: +payload };

case TYPE.KEYWORD:
return { ...state, keyword: payload };

default:
return state;
}
Expand Down
6 changes: 5 additions & 1 deletion web-apps/client/src/pages/main/fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ const makeQuery = ({
coordinates,
distance,
from,
limits, }) => {
limits,
keyword }) => {
const queries = [];
if (from) {
queries.push(['from', from]);
Expand All @@ -42,6 +43,9 @@ const makeQuery = ({
if (end || start) {
queries.push(['price', `${start}${end ? `,${end}` : ''}`]);
}
if (keyword.length) {
queries.push(['keyword', keyword]);
}
const query = queries.map((q) => q.join('=')).join('&');
return query;
};
Expand Down
2 changes: 1 addition & 1 deletion web-apps/client/src/pages/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const Main = () => {
const [settings, setSettings] = useState({ from: 0, limits: 10 });

const buttons = [
getButtons('검색', '/', <SearchIcon />),
getButtons('검색', '/search', <SearchIcon />),
getButtons('필터', '/category', <FilterIcon />),
getButtons('알림', '/', <NotifyIcon />)
];
Expand Down
115 changes: 115 additions & 0 deletions web-apps/client/src/pages/search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import axios from 'axios';
import Inko from 'inko';
import React, { useRef, useState, useEffect, useContext } from 'react';

import { useHistory } from 'react-router-dom';

import { List, ListItem } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';

import Input from '../components/search';
import ToolBar from '../components/tool-bar';

import { filterContext } from '../contexts/filters';

const useStyles = makeStyles({
'root': {
width: '100%',
'& h6': {
width: '100%',
},
}
});

const getKeywordList = async (keyword) => {
const response = await axios.get('http://localhost:5000/info/keyword', {
params: { keyword }
})
return response.data;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

api 호출은 custom Hooks 등의 방법으로 외부로 분리하여야 할 것 같네요. uri는 또 별도의 파일로 관리가 필요하겠고요.


const isEnglishOnly = (keyword) => !(/[가-힣]/.test(keyword));
const isKoreanOnly = (keyword) => !(/[a-zA-Z]/.test(keyword));

export default () => {
const SEARCH_DELAY = 500;
const classes = useStyles({});
const inputRef = useRef({ current: '' });
const { filter: { keyword }, dispatch, TYPE } = useContext(filterContext);
const [list, setList] = useState([]);
const [recommend, setRecommned] = useState('');
const timer = { current: '' };
const histroy = useHistory();

useEffect(() => {
if (keyword.length) {
inputRef.current.set(keyword);
}
}, []);

const inputKeyword = () => {
const keyword = inputRef.current.get();
if (timer.current) {
clearTimeout(timer.current);
}
timer.current = setTimeout(async () => {
const data = await getKeywordList(keyword);
const list = data.map(({ _source: { word } }) => word);
setList(list);
}, SEARCH_DELAY);
}

useEffect(() => {
const keyword = inputRef.current.get();
let recommendKeyword = '';
if (list.length >= 1 || keyword.length <= 1) {
setRecommned('');
return;
}
if (isEnglishOnly(keyword)) {
recommendKeyword = new Inko().en2ko(keyword);
}
if (isKoreanOnly(keyword)) {
recommendKeyword = new Inko().ko2en(keyword);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분에 대해서는 추가적으로 개선작업이 반드시 필요할 것으로 생각됩니다.
한글과 영어가 섞이는 과정은 꽤 많을 것으로 예상됩니다.
ex) 갤럭시S10 -> roffjrtlㄴ7

Copy link
Contributor Author

@kgpyo kgpyo Dec 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혼동을 피하는 메소드로 변경하겠습니다.
해당 메소드는 영어만 사용되지 않으면 변환됩니다.

영어 섞이는 경우는 다른 방법으로 고려해보겠습니다.

if (recommendKeyword.length > 1) {
setRecommned(recommendKeyword);
};
}, [list]);

const onTransferKeyword = () => {
inputRef.current.set(recommend);
inputKeyword();
};

const updateKeyword = (event) => {
const name = event.target.innerText;
dispatch({ type: TYPE.KEYWORD, payload: name });
histroy.goBack();
};

const enterEvent = (event) => {
if (event.key !== 'Enter') {
return;
}
const name = inputRef.current.get();
dispatch({ type: TYPE.KEYWORD, payload: name });
histroy.goBack();
}

return (
<>
<div className={classes.root}>
<ToolBar title={<Input ref={inputRef} onChange={inputKeyword} onKeyDown={enterEvent} />} />
</div>
<List className={classes.root}>
{recommend.length > 0 &&
(<ListItem onClick={onTransferKeyword}>{recommend}를 입력하려 하셨나요?</ListItem>)
}
{list.map((name) => (
<ListItem onClick={updateKeyword} divider button key={name}>{name}</ListItem>
))}
</List>
</>
);
};