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

Feature 022 : 변경사항 및 에러사항 수정 #90

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
6 changes: 5 additions & 1 deletion src/apis/search.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { APIResponse } from '@/models/config/axios';
import axios from './config/instance';
import { ResponseSearch } from '@/models/search';
import { ITag, ResponseSearch } from '@/models/search';

const PREFIX = '/search';

Expand All @@ -9,4 +9,8 @@ export const searchAPI = (type : string, keyword : string) => {
return axios.get<APIResponse<ResponseSearch>>(PREFIX + `/${type}/`, {
params : {[paramType] : keyword}
});
}

export const tagAPI = () => {
return axios.get<APIResponse<ITag[]>>('/videos/tag');
}
33 changes: 29 additions & 4 deletions src/components/SearchPage/SearchComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import Container from '@/styles/SearchComponent';
import useBoolean from '@/hooks/useBoolean';
import useIndex from '@/hooks/useIndex';
import CloseIcon from '@/assets/icons/close-btn.svg?react'
import SearchIcon from '@/assets/icons/search.svg?react';
import { createSearchParams, useNavigate } from 'react-router-dom';

const placeholder = '검색하고 싶은 키워드를 입력해주세요'

interface BaseTagInputProps {
Expand All @@ -27,7 +30,7 @@ const SearchComponent : React.FC<TagInputProps> = ({tags, input, searchType, sel
const [hoverdIndex, setHoveredIndex, setLeaveIndex] = useIndex(null);
const [hoverBtnIndex, setHoverBtnIndex, setLeaveBtnIndex] = useIndex(null);
const [tagIndex, setRemovingTagIndex, setNullTagIndex] = useIndex(null);

const searchNav = useNavigate();
useEffect(() => {
if (tags.length > 3) {
const lastValue = tags[3]
Expand All @@ -45,16 +48,20 @@ const SearchComponent : React.FC<TagInputProps> = ({tags, input, searchType, sel
}, [tags, setTags]);

const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
if (event.code === 'Enter'){
handleSearch();
}
if(!searchType){
if (event.key === 'Enter' && !isComposing) {

if ((event.code === 'Comma' || event.code === 'Space') && !isComposing) {
event.preventDefault();
if (input) {
tags.length > 0 && !input.startsWith('#') ? setTags([...tags, '#' + input]) : setTags([...tags, input])
if(selectedHashtags && setSelectedHashtags && !selectedHashtags.includes(input))
input.startsWith('#') ? setSelectedHashtags([...selectedHashtags, input.substring(1)]) : setSelectedHashtags([...selectedHashtags, input])
setInput('');
}
} else if (event.key === 'Backspace' && !input) {
} else if ((event.key === 'Backspace' || event.code === 'Backspace') && !input) {
if(tags.length > 0){
const lastValue = tags[tags.length - 1]
if(selectedHashtags && setSelectedHashtags && selectedHashtags.includes(lastValue.substring(1)))
Expand Down Expand Up @@ -90,8 +97,23 @@ const SearchComponent : React.FC<TagInputProps> = ({tags, input, searchType, sel
}, 500)
}


const handleSearch = () => {
const params = {
type : searchType === true ? 'keyword' : 'hashtag',
value: searchType ? input : tags.join('&')
};
window.scrollTo(0, 0);
searchNav({
pathname : '/search/result',
search : `?${createSearchParams(params)}`
})

}

return (
<div style={{display : 'flex', flexDirection : 'row'}}>
<div className='input' style={{width : '770px', height : '36px'}}>
<SearchIcon width={36} height={36}/>
<Container className="tag-container" style={{width : '700px', height : '36px'}}>
{tags.map((tag : string, index : number) => (
<span className={`tag ${hoverdIndex === index ? 'hovered' : ''} ${ (index >= 3 || index === tagIndex) ? 'exceed' : ''}`} key={index} onMouseEnter={() => setHoverBtnIndex(index)} onMouseLeave={setLeaveBtnIndex}>
Expand All @@ -110,6 +132,9 @@ const SearchComponent : React.FC<TagInputProps> = ({tags, input, searchType, sel
onChange={(e) => handleOnchage(e)}
/>
</Container>
</div>
<button className='search-btn' onClick={handleSearch} disabled={(input.length === 0 && tags.length === 0)} style={{width : '90px', height : '36px'}}>Search</button>
</div>
);
};

Expand Down
8 changes: 7 additions & 1 deletion src/components/SearchPage/SearchNotFound.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import SearchNotFoundIcon from '@/assets/icons/search-notfound.svg?react';
import styled from '@/styles/SearchResult';
import { useNavigate } from 'react-router-dom';

type NotFoundprop = {
input : string
}

const SearchNotFound : React.FC<NotFoundprop> = ( {input} ) => {
const navigate = useNavigate();

const handleBtn = () => {
navigate('/');
}
return (
<styled.SearchNotFoundContainer>
<SearchNotFoundIcon width={156} height={156}/>
<div className='text'> <span className='user'>{input}</span>에 대한 검색 결과가 없어요</div>
<button>더 많은 영상 변환하기</button>
<button onClick={handleBtn}>더 많은 영상 변환하기</button>
</styled.SearchNotFoundContainer>
);
}
Expand Down
7 changes: 6 additions & 1 deletion src/models/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,9 @@ export interface IVideo {

export interface ResponseSearch {
videos : IVideo[];
}
}

export interface ITag {
id : number;
name : string;
}
192 changes: 76 additions & 116 deletions src/pages/SearchPage.tsx
Original file line number Diff line number Diff line change
@@ -1,130 +1,90 @@
import { Container, HashtagBox } from '@/styles/SearchPage';
import TooltipImg from '@/assets/icons/tooltip.svg?react';
import SearchIcon from '@/assets/icons/search.svg?react';

import { useState } from 'react';
import { Tooltip } from '@/components/common';
import { useEffect, useState } from 'react';
import TagInput from '@/components/SearchPage/SearchComponent';
import { useNavigate, createSearchParams } from 'react-router-dom';
import { tagAPI } from '@/apis/search';

const SearchPage = () => {
const [tags, setTags] = useState<string[]>([]);
const [input, setInput] = useState<string>('');
const [searchType, setSearchType] = useState(true); // True : keyword | False : hashTag
const userHashTag = [
'기획',
'광고',
'마케팅',
'트렌드',
'기업',
'광고',
'마케팅',
'트렌드',
'기업',
'광고',
]; // 사용자의 해시태그 데이터 10개 <임의 데이터>
const [selectedHashtags, setSelectedHashtags] = useState<string[]>([]);
const searchNav = useNavigate();
const [tags, setTags] = useState<string[]>([]);
const [input, setInput] = useState<string>('');
const [searchType, setSearchType] = useState(true); // True : keyword | False : hashTag
const [userHashTag, setUserHashTag] = useState<string[]>([]);
const [selectedHashtags, setSelectedHashtags] = useState<string[]>([]);

useEffect(() => {
const handleTagAPI = async () => {
try {
const {data} = (await tagAPI());
const extraData = data.result.map((item) => {
return item.name;
});
const shuffleData = sortShuffle(extraData).slice(0,10);
setUserHashTag(shuffleData);

const handleHashtagBox = (value: string) => {
const isSelected = selectedHashtags.includes(value);
setSelectedHashtags((prev) =>
isSelected ? prev.filter((idx) => idx !== value) : [...prev, value],
);
isSelected
? setTags(tags.filter((prev) => prev !== '#' + value))
: setTags([...tags, `#${value}`]);
setSearchType(false); // 박스를 클릭했을 때도 type 변경
};
} catch(e) {
setUserHashTag(['A','B','C','D','E','F','G','H','I','J']);
}
}
handleTagAPI();
}, []);

const handleSearch = () => {
const params = {
type: searchType === true ? 'keyword' : 'hashtag',
value: searchType ? input : tags.join('&'),
};
const handleHashtagBox = (value : string) => {
const isSelected = selectedHashtags.includes(value);
setSelectedHashtags(prev =>
isSelected ? prev.filter(idx => idx !== value) : [...prev, value]
);
isSelected ? setTags(tags.filter((prev) => prev !== '#'+value)) : setTags([...tags, `#${value}`]);
setSearchType(false); // 박스를 클릭했을 때도 type 변경
}

searchNav({
pathname: '/search/result',
search: `?${createSearchParams(params)}`,
});
};
const sortShuffle = (arr : string[]) => {
return arr.sort(() => Math.random() - 0.5);
}

return (
<Container style={{width : '100vw', height : '100vh'}}>
<div className='wrap' style={{width : '908px', height : '450px'}}>
<div className="search" style={{width : '908px', height : '288px'}}>
<div className="search-inner" style={{width : '908px', height : '204px'}}>
<div className='header' style={{width : '508px', height : '92px'}}>
<span className='header3' style={{width: '508px', height: '58px'}}>찾고 싶은 키워드가 있나요?</span>
<span className='header5' style={{width: '508px', height: '26px'}}>찾고자 하는 키워드를 검색하면 관련 영상을 찾아드릴게요</span>
</div>

return (
<Container style={{ width: '100%', height: '100vh' }}>
<div className="wrap" style={{ width: '908px', height: '450px' }}>
<div className="search" style={{ width: '908px', height: '288px' }}>
<div
className="search-inner"
style={{ width: '908px', height: '204px' }}
>
<div className="header" style={{ width: '508px', height: '92px' }}>
<span
className="header3"
style={{ width: '508px', height: '58px' }}
>
찾고 싶은 키워드가 있나요?
</span>
<span
className="header5"
style={{ width: '508px', height: '26px' }}
>
찾고자 하는 키워드를 검색하면 관련 영상을 찾아드릴게요
</span>
</div>
<div className='inputwrap' style={{width : '908px', height : '72px'}}>
<div className='input-inner' style={{width : '861px', height : '36px'}}>
<div className='input' style={{width : '770px', height : '36px'}}>
<TagInput tags={tags} input={input} searchType={searchType} selectedHashtags={selectedHashtags}
setTags={setTags} setInput={setInput} setSearchType={setSearchType} setSelectedHashtags={setSelectedHashtags}/>
</div>
</div>
</div>
</div>
{(input.length === 0 && tags.length === 0) ? (
<Tooltip direction='up'>
<>
1. 키워드 검색 : 키워드를 검색하면 해당 키워드가 언급 된 영상들을 찾아드려요!
<br/>
2. 해시태그 검색 : #을 함께 검색하면 해당 해시태그가 있는 영상들을 찾아드려요!
</>
</Tooltip>
)
: ''
}
</div>

<div
className="inputwrap"
style={{ width: '908px', height: '72px' }}
>
<div
className="input-inner"
style={{ width: '861px', height: '36px' }}
>
<div
className="input"
style={{ width: '770px', height: '36px' }}
>
<SearchIcon width={36} height={36} />
<TagInput
tags={tags}
input={input}
searchType={searchType}
selectedHashtags={selectedHashtags}
setTags={setTags}
setInput={setInput}
setSearchType={setSearchType}
setSelectedHashtags={setSelectedHashtags}
/>
<div className="hashtag" style={{width : '572px', height : '102px'}}>
{
userHashTag.map((value : string, idx : number) => {
return(<HashtagBox key={idx} onClick={() => handleHashtagBox(value)}
className={selectedHashtags.includes(value) ? 'toggle' : ''}>{'#' + value}</HashtagBox>)
})
}
</div>
<button
className="search-btn"
onClick={handleSearch}
disabled={input.length === 0 && tags.length === 0}
style={{ width: '90px', height: '36px' }}
>
Search
</button>
</div>
</div>
</div>
{input.length === 0 && tags.length === 0 ? <TooltipImg /> : ''}
</div>

<div className="hashtag" style={{ width: '572px', height: '102px' }}>
{userHashTag.map((value: string, idx: number) => {
return (
<HashtagBox
key={idx}
onClick={() => handleHashtagBox(value)}
className={selectedHashtags.includes(value) ? 'toggle' : ''}
>
{'#' + value}
</HashtagBox>
);
})}
</div>
</div>
</Container>
);
</Container>
);
};

export default SearchPage;
24 changes: 1 addition & 23 deletions src/pages/SearchResultPage.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { escapeHTML } from '@/utils/string';
import { useState, useEffect } from 'react';
import { createSearchParams, useLocation } from 'react-router-dom';
import { useLocation } from 'react-router-dom';
import Styled from '@/styles/SearchResult';
import TagInput from '@/components/SearchPage/SearchComponent';
import SearchNotFound from '@/components/SearchPage/SearchNotFound';
import SearchIcon from '@/assets/icons/search.svg?react';
import { IVideo } from '@/models/search';
import { searchAPI } from '@/apis/search';
import { useNavigate } from 'react-router-dom';

import SearchResultBox from '@/components/SearchPage/SearchResultBox';

Expand All @@ -19,7 +18,6 @@ const SearchResult = () => {
const [errormsg, setErrormsg] = useState('');
const [data, setData] = useState<IVideo[]>([]);
const location = useLocation();
const searchNav = useNavigate();

useEffect(() => {
const searchParams = new URLSearchParams(location.search);
Expand Down Expand Up @@ -111,18 +109,6 @@ const SearchResult = () => {
});
mappingData;
};
const handleReSearch = () => {
const params = {
type: searchType === true ? 'keyword' : 'hashtag',
value: searchType ? input : tags.join('&'),
};

searchNav({
pathname: '/search/result',
search: `?${createSearchParams(params)}`,
});
window.location.reload();
};

if (loading) {
return (
Expand Down Expand Up @@ -153,14 +139,6 @@ const SearchResult = () => {
setSearchType={setSearchType}
/>
</div>
<button
className="search-btn"
style={{ width: '90px', height: '36px' }}
disabled={input.length === 0 && tags.length === 0}
onClick={handleReSearch}
>
Search
</button>
</div>
</div>
</div>
Expand Down
Loading
Loading