안형욱

Merge branch 'feat/advanced-search' into 'develop'

Feat/advanced search



See merge request !16
...@@ -126,7 +126,11 @@ const Header = () => { ...@@ -126,7 +126,11 @@ const Header = () => {
126 float="left" 126 float="left"
127 fontsize="20px" 127 fontsize="20px"
128 height="50px" 128 height="50px"
129 - options={[{ id: 1, name: '전체' }]} 129 + options={[
130 + { id: 0, name: '전체' },
131 + { id: 1, name: '작성자' },
132 + { id: 2, name: '내용' },
133 + ]}
130 /> 134 />
131 </DropDownWrap> 135 </DropDownWrap>
132 </DropDownContainer> 136 </DropDownContainer>
......
...@@ -8,7 +8,7 @@ const Background = styled.div``; ...@@ -8,7 +8,7 @@ const Background = styled.div``;
8 8
9 const ModalWrapper = styled.div` 9 const ModalWrapper = styled.div`
10 width: 400px; 10 width: 400px;
11 - height: 300px; 11 + height: 194px;
12 `; 12 `;
13 13
14 const ModalContent = styled.div` 14 const ModalContent = styled.div`
...@@ -23,13 +23,13 @@ const ModalContent = styled.div` ...@@ -23,13 +23,13 @@ const ModalContent = styled.div`
23 const SearchWrap = styled.div` 23 const SearchWrap = styled.div`
24 position: absolute; 24 position: absolute;
25 top: 80%; 25 top: 80%;
26 - left: 20%; 26 + right: 20%;
27 `; 27 `;
28 28
29 const CloseWrap = styled.div` 29 const CloseWrap = styled.div`
30 position: absolute; 30 position: absolute;
31 top: 80%; 31 top: 80%;
32 - right: 20%; 32 + left: 20%;
33 `; 33 `;
34 34
35 const StandardWrap = styled.div` 35 const StandardWrap = styled.div`
...@@ -51,7 +51,6 @@ const TextWrap = styled.div` ...@@ -51,7 +51,6 @@ const TextWrap = styled.div`
51 width: 360px; 51 width: 360px;
52 top: ${props => props.top}; 52 top: ${props => props.top};
53 right: ${props => props.right}; 53 right: ${props => props.right};
54 - border-bottom: 2px solid #dee2e6;
55 padding-left: 20px; 54 padding-left: 20px;
56 padding-bottom: ${props => props.bottom}; 55 padding-bottom: ${props => props.bottom};
57 `; 56 `;
...@@ -59,8 +58,7 @@ const TextWrap = styled.div` ...@@ -59,8 +58,7 @@ const TextWrap = styled.div`
59 const Modal = ({ showModal, setShowModal }) => { 58 const Modal = ({ showModal, setShowModal }) => {
60 const [query, setQuery] = useState(''); 59 const [query, setQuery] = useState('');
61 const [keywordQuery, setKeywordQuery] = useState(''); 60 const [keywordQuery, setKeywordQuery] = useState('');
62 - const [writerQuery, setWriterQuery] = useState(''); 61 + const [writer, setWriter] = useState('');
63 - const [dateQuery, setDateQuery] = useState('');
64 const history = useHistory(); 62 const history = useHistory();
65 const modalRef = useRef(); 63 const modalRef = useRef();
66 const closeModal = e => { 64 const closeModal = e => {
...@@ -76,7 +74,7 @@ const Modal = ({ showModal, setShowModal }) => { ...@@ -76,7 +74,7 @@ const Modal = ({ showModal, setShowModal }) => {
76 <Card shadow="lg"> 74 <Card shadow="lg">
77 <ModalWrapper> 75 <ModalWrapper>
78 <ModalContent> 76 <ModalContent>
79 - <TextWrap top="6%" right="10%" bottom="4%" height="40px"> 77 + <TextWrap top="8%" right="10%" bottom="4%" height="40px">
80 기본검색 78 기본검색
81 </TextWrap> 79 </TextWrap>
82 <StandardWrap> 80 <StandardWrap>
...@@ -103,7 +101,7 @@ const Modal = ({ showModal, setShowModal }) => { ...@@ -103,7 +101,7 @@ const Modal = ({ showModal, setShowModal }) => {
103 }} 101 }}
104 /> 102 />
105 </StandardWrap> 103 </StandardWrap>
106 - <TextWrap top="24%" right="10%" bottom="31%"> 104 + <TextWrap top="34%" right="10%" bottom="31%">
107 고급검색 105 고급검색
108 </TextWrap> 106 </TextWrap>
109 <AdvancedWrap> 107 <AdvancedWrap>
...@@ -124,28 +122,29 @@ const Modal = ({ showModal, setShowModal }) => { ...@@ -124,28 +122,29 @@ const Modal = ({ showModal, setShowModal }) => {
124 }} 122 }}
125 placeholder="작성자" 123 placeholder="작성자"
126 type="text" 124 type="text"
127 - value={decodeURIComponent(writerQuery)} 125 + value={decodeURIComponent(writer)}
128 - onChange={e => setWriterQuery(e.target.value)} 126 + onChange={e => setWriter(e.target.value)}
129 - />
130 - <TextInput
131 - inputStyle={{
132 - marginBottom: 18,
133 - fontSize: 15,
134 - }}
135 - placeholder="생성 날짜"
136 - type="text"
137 - value={decodeURIComponent(dateQuery)}
138 - onChange={e => setDateQuery(e.target.value)}
139 /> 127 />
140 </AdvancedWrap> 128 </AdvancedWrap>
141 </ModalContent> 129 </ModalContent>
142 <CloseWrap onClick={() => setShowModal(prev => !prev)}> 130 <CloseWrap onClick={() => setShowModal(prev => !prev)}>
143 - <Button width="100px" color="blue"> 131 + <Button width="100px" color="gray">
144 닫기 132 닫기
145 </Button> 133 </Button>
146 </CloseWrap> 134 </CloseWrap>
147 - <SearchWrap> 135 + <SearchWrap
148 - <Button width="100px" color="gray"> 136 + onClick={() => {
137 + const searchQuery = {};
138 + if (query !== '') searchQuery.query = query;
139 + if (keywordQuery !== '') searchQuery.keyword = keywordQuery;
140 + if (writer !== '') searchQuery.writer = writer;
141 + const params = new URLSearchParams(searchQuery);
142 + history.push(
143 + `search?${decodeURIComponent(params.toString())}`
144 + );
145 + }}
146 + >
147 + <Button width="100px" color="blue">
149 검색 148 검색
150 </Button> 149 </Button>
151 </SearchWrap> 150 </SearchWrap>
......
...@@ -18,10 +18,16 @@ const Buttons = ({ ...@@ -18,10 +18,16 @@ const Buttons = ({
18 width, 18 width,
19 fontsize, 19 fontsize,
20 icon = '', 20 icon = '',
21 + onClick,
21 }) => { 22 }) => {
22 return ( 23 return (
23 <ButtonBlock float={float} width={width} fontsize={fontsize}> 24 <ButtonBlock float={float} width={width} fontsize={fontsize}>
24 - <Button size={size} color={color || 'blue'} rightIcon={icon}> 25 + <Button
26 + onClick={onClick}
27 + size={size}
28 + color={color || 'blue'}
29 + rightIcon={icon}
30 + >
25 {children} 31 {children}
26 </Button> 32 </Button>
27 </ButtonBlock> 33 </ButtonBlock>
......
1 import React, { useState, useEffect } from 'react'; 1 import React, { useState, useEffect } from 'react';
2 import { TiArrowSortedDown } from 'react-icons/ti'; 2 import { TiArrowSortedDown } from 'react-icons/ti';
3 import { Menu, MenuItem } from '@mantine/core'; 3 import { Menu, MenuItem } from '@mantine/core';
4 +import { useDispatch } from 'react-redux';
4 import styled from 'styled-components'; 5 import styled from 'styled-components';
5 import { dropdownHeaderColorMap } from '../../lib/styles/palette'; 6 import { dropdownHeaderColorMap } from '../../lib/styles/palette';
7 +import { setSearchOption } from '../../features/searchOption';
6 8
7 const DropDownBlock = styled.div` 9 const DropDownBlock = styled.div`
8 margin: 0 auto; 10 margin: 0 auto;
...@@ -41,6 +43,7 @@ const DropDown = ({ ...@@ -41,6 +43,7 @@ const DropDown = ({
41 size, 43 size,
42 }) => { 44 }) => {
43 const [menuTitle, setTitle] = useState(''); 45 const [menuTitle, setTitle] = useState('');
46 + const dispatch = useDispatch();
44 useEffect(() => { 47 useEffect(() => {
45 setTitle(title); 48 setTitle(title);
46 }, []); 49 }, []);
...@@ -62,14 +65,15 @@ const DropDown = ({ ...@@ -62,14 +65,15 @@ const DropDown = ({
62 </DropDownWrap> 65 </DropDownWrap>
63 } 66 }
64 > 67 >
65 - {options.map(friend => ( 68 + {options.map(option => (
66 <MenuItem 69 <MenuItem
67 - value={friend.id} 70 + value={option.id}
68 onClick={() => { 71 onClick={() => {
69 - setTitle(friend.name); 72 + dispatch(setSearchOption(option.id));
73 + setTitle(option.name);
70 }} 74 }}
71 > 75 >
72 - {friend.name} 76 + {option.name}
73 </MenuItem> 77 </MenuItem>
74 ))} 78 ))}
75 </DropDownHeader> 79 </DropDownHeader>
......
...@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'; ...@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
2 import { TextInput } from '@mantine/core'; 2 import { TextInput } from '@mantine/core';
3 import styled from 'styled-components'; 3 import styled from 'styled-components';
4 import { useHistory, useLocation } from 'react-router-dom'; 4 import { useHistory, useLocation } from 'react-router-dom';
5 -import { useDispatch } from 'react-redux'; 5 +import { useDispatch, useSelector } from 'react-redux';
6 import SearchBox from './SearchBox'; 6 import SearchBox from './SearchBox';
7 import { inputColorMap } from '../../lib/styles/palette'; 7 import { inputColorMap } from '../../lib/styles/palette';
8 import { esApi } from '../../lib/api/elasticsearch'; 8 import { esApi } from '../../lib/api/elasticsearch';
...@@ -43,18 +43,31 @@ const MyInput = ({ ...@@ -43,18 +43,31 @@ const MyInput = ({
43 }) => { 43 }) => {
44 const [query, setQuery] = useState(''); 44 const [query, setQuery] = useState('');
45 const history = useHistory(); 45 const history = useHistory();
46 - const search = useLocation(); 46 + const { search } = useLocation();
47 - const name = decodeURIComponent(search.search.substring(7)); 47 + const name = decodeURIComponent(
48 + new URLSearchParams(search).get('query') || ''
49 + );
50 + const option = decodeURIComponent(
51 + new URLSearchParams(search).get('option') || ''
52 + );
48 const dispatch = useDispatch(); 53 const dispatch = useDispatch();
54 + const { option: currentSearchOption } = useSelector(
55 + state => state.searchOption
56 + );
49 57
50 useEffect(() => { 58 useEffect(() => {
51 const setSearchDatas = async () => { 59 const setSearchDatas = async () => {
60 + if (option === '') {
52 const { results } = await esApi.search(name); 61 const { results } = await esApi.search(name);
53 dispatch(setParsedDocuments(results)); 62 dispatch(setParsedDocuments(results));
63 + } else {
64 + const { results } = await esApi.searchWithOption(name, option);
65 + dispatch(setParsedDocuments(results));
66 + }
54 }; 67 };
55 setQuery(name); 68 setQuery(name);
56 setSearchDatas(); 69 setSearchDatas();
57 - }, [name]); 70 + }, [name, option]);
58 71
59 return ( 72 return (
60 <InputBlock 73 <InputBlock
...@@ -82,7 +95,13 @@ const MyInput = ({ ...@@ -82,7 +95,13 @@ const MyInput = ({
82 alert('검색어를 입력 해 주세요.'); 95 alert('검색어를 입력 해 주세요.');
83 return; 96 return;
84 } 97 }
85 - const params = new URLSearchParams({ query }); 98 + const searchRequest = {};
99 + searchRequest.query = query;
100 + if (currentSearchOption === 'WRITER')
101 + searchRequest.option = 'writer';
102 + if (currentSearchOption === 'CONTENT')
103 + searchRequest.option = 'content';
104 + const params = new URLSearchParams(searchRequest);
86 history.push(`search?${decodeURIComponent(params.toString())}`); 105 history.push(`search?${decodeURIComponent(params.toString())}`);
87 } 106 }
88 }} 107 }}
...@@ -94,7 +113,13 @@ const MyInput = ({ ...@@ -94,7 +113,13 @@ const MyInput = ({
94 alert('검색어를 입력 해 주세요.'); 113 alert('검색어를 입력 해 주세요.');
95 return; 114 return;
96 } 115 }
97 - const params = new URLSearchParams({ query }); 116 + const searchRequest = {};
117 + searchRequest.query = query;
118 + if (currentSearchOption === 'WRITER') searchRequest.option = 'writer';
119 + if (currentSearchOption === 'CONTENT')
120 + searchRequest.option = 'content';
121 +
122 + const params = new URLSearchParams(searchRequest);
98 history.push(`search?${decodeURIComponent(params.toString())}`); 123 history.push(`search?${decodeURIComponent(params.toString())}`);
99 }} 124 }}
100 > 125 >
......
...@@ -15,7 +15,6 @@ const ImageWrapper = styled.div` ...@@ -15,7 +15,6 @@ const ImageWrapper = styled.div`
15 `; 15 `;
16 const Thumbnails = ({ srcs }) => { 16 const Thumbnails = ({ srcs }) => {
17 const [lists, setLists] = useState([]); 17 const [lists, setLists] = useState([]);
18 - console.log(srcs);
19 const placeholder = () => { 18 const placeholder = () => {
20 if (srcs.length < 4) { 19 if (srcs.length < 4) {
21 const list = []; 20 const list = [];
......
1 +import { createSlice } from '@reduxjs/toolkit';
2 +
3 +const OPTION = ['ALL', 'WRITER', 'CONTENT'];
4 +
5 +const searchOptionSlice = createSlice({
6 + name: 'searchOption',
7 + initialState: {
8 + option: OPTION[0],
9 + },
10 + reducers: {
11 + setSearchOption: (state, action) => {
12 + state.option = OPTION[action.payload];
13 + },
14 + },
15 +});
16 +
17 +export const { setSearchOption } = searchOptionSlice.actions;
18 +
19 +export default searchOptionSlice.reducer;
...@@ -19,4 +19,17 @@ export const esApi = { ...@@ -19,4 +19,17 @@ export const esApi = {
19 19
20 return res.data; 20 return res.data;
21 }, 21 },
22 + searchWithOption: async (searchWord, option) => {
23 + const res = await esInstance.post(
24 + `/api/as/v1/engines/${process.env.REACT_APP_ENGINE_NAME}/search`,
25 + {
26 + query: searchWord,
27 + search_fields: {
28 + [option]: {},
29 + },
30 + }
31 + );
32 +
33 + return res.data;
34 + },
22 }; 35 };
......
...@@ -51,9 +51,9 @@ const HomePage = () => { ...@@ -51,9 +51,9 @@ const HomePage = () => {
51 fontsize="20px" 51 fontsize="20px"
52 height="50px" 52 height="50px"
53 options={[ 53 options={[
54 - { id: 1, name: '전체' }, 54 + { id: 0, name: '전체' },
55 - { id: 2, name: '개인' }, 55 + { id: 1, name: '작성자' },
56 - { id: 3, name: '부서' }, 56 + { id: 2, name: '내용' },
57 ]} 57 ]}
58 /> 58 />
59 <Input color="blue" paddingsize="10px" width="100%" display /> 59 <Input color="blue" paddingsize="10px" width="100%" display />
......
1 import { combineReducers } from 'redux'; 1 import { combineReducers } from 'redux';
2 import parsedDocumentsReducer from '../features/parsedDocumentsSlice'; 2 import parsedDocumentsReducer from '../features/parsedDocumentsSlice';
3 +import searchOptionReducer from '../features/searchOption';
3 4
4 export default combineReducers({ 5 export default combineReducers({
5 parsedDocuments: parsedDocumentsReducer, 6 parsedDocuments: parsedDocumentsReducer,
7 + searchOption: searchOptionReducer,
6 }); 8 });
......