Skip to content

Commit

Permalink
Merge pull request #44 from teamViNO/feature-022
Browse files Browse the repository at this point in the history
Feature 022 : api 연동
  • Loading branch information
whistleJs authored Feb 8, 2024
2 parents 8c8f20d + f053d9d commit 008da63
Show file tree
Hide file tree
Showing 12 changed files with 598 additions and 123 deletions.
3 changes: 3 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import HomePage from '@/pages/HomePage';
// import GuestPage from './pages/GuestPage';
import ProfilePage from '@/pages/ProfilePage';
import SearchPage from '@/pages/SearchPage';
import SearchResult from './pages/SearchResultPage';
import SignInPage from '@/pages/SignInPage';
import SignUpPage from '@/pages/SignUpPage';
import SignUpSuccessPage from '@/pages/SignUpSuccessPage';
Expand Down Expand Up @@ -51,7 +52,9 @@ const App = () => {
{userToken && (
<>
<Route path="/search" element={<SearchPage />} />
<Route path="/search/result" element={<SearchResult />} />
<Route path="/profile" element={<ProfilePage />} />

</>
)}

Expand Down
11 changes: 11 additions & 0 deletions src/apis/search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { APIResponse } from '@/models/config/axios';
import axios from './config/instance';

const PREFIX = '/search';

export const searchAPI = (type : string, keyword : string) => {
const paramType = type === 'keyword' ? 'keywordName' : 'hashtagName';
return axios.get<APIResponse<any>>(PREFIX + `/${type}/`, {
params : {[paramType] : keyword}
});
}
3 changes: 1 addition & 2 deletions src/apis/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export const checkEmailAPI = (data: CheckEmailRequest) => {
data,
);
};

export const joinAPI = (data: JoinRequest) => {
return axios.post<APIResponse<JoinResponse>>(PREFIX + '/join', data);
};
};
8 changes: 8 additions & 0 deletions src/assets/icons/search-notfound.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions src/components/SearchPage/SearchNotFound.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import SearchNotFoundIcon from '@/assets/icons/search-notfound.svg?react';
import styled from '@/styles/SearchResult';

type NotFoundprop = {
input : string
}

const SearchNotFound : React.FC<NotFoundprop> = ( {input} ) => {
return (
<styled.SearchNotFoundContainer>
<SearchNotFoundIcon width={156} height={156}/>
<div className='text'> <span className='user'>{input}</span>에 대한 검색 결과가 없어요</div>
<button>더 많은 영상 변환하기</button>
</styled.SearchNotFoundContainer>
);
}


export default SearchNotFound;
42 changes: 42 additions & 0 deletions src/components/SearchPage/SearchResultBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { IVideo } from '@/models/search';
import Styled from '@/styles/SearchResult';
import React, { ImgHTMLAttributes } from 'react';

interface SearchResultBoxProp {
video : IVideo
}
const SearchResultBox : React.FC<SearchResultBoxProp>= ({video}) => {
const date = video.created_at.toString().split('T')[0].split('-');
const handleImg = (event : React.SyntheticEvent<HTMLImageElement, Event>) => {
const target = event.target as HTMLImageElement;
target.style.display = 'none';
}
return (
<Styled.VideoCard style={{width : '910px', height : '254px'}}>
<div className="main" style={{width : '670px', height : '254px'}}>
<div className="user" style={{width : '114px', height : '20px'}}>
<span className="userName" style={{ height : '19px'}}>{video.user}</span>
<span className='contour' style={{width : '0px', height : '12px'}}></span>
<span className="userDate" style={{height :'19px'}}>
{`${date[0]}${date[1]}${date[2]}일`}
</span>
</div>
<div className="content" style={{width : '648px', height : '108px'}}>
<div className="title" style={{width : '648px', height : '26px'}} dangerouslySetInnerHTML={{__html : video.title}}></div>
<div className="subtitle" style={{width : '648px', height : '22px'}} dangerouslySetInnerHTML={{__html : video.description}}></div>
<div className="subcontent" style={{width : '648px' , height : '44px'}} dangerouslySetInnerHTML={{__html : video.content}}></div>
</div>
<div className='hashtag' style={{maxWidth : '648px',height : '31px'}}>
{video.tag.map((item, index) =>
<Styled.hashtagBox key = {index}>{item.name}</Styled.hashtagBox>
)}
</div>
</div>
<div className="imgBox" style={{width : '213px', height : '254px'}}>
<img src={video.image} style={{width : '213px', height : '254px'}} onError={(event) => handleImg(event)}></img>
</div>
</Styled.VideoCard>
);
}

export default SearchResultBox;
16 changes: 16 additions & 0 deletions src/models/search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

export interface IName {
name : string;
}

export interface IVideo {
video_id: number;
title: string;
description : string;
image: string;
created_at : Date;
name : string;
content : string;
user : string;
tag : IName[];
}
159 changes: 60 additions & 99 deletions src/pages/SearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,112 +4,73 @@ import SearchIcon from '@/assets/icons/search.svg?react';

import { useState } from 'react';
import TagInput from '@/components/SearchPage/SearchComponent';
import { useNavigate, createSearchParams } from 'react-router-dom';

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

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 변경
};
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 변경
}

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>
const handleSearch = () => {
const params = {
type : searchType === true ? 'keyword' : 'hashtag',
value: searchType ? input : tags.join('&')
};

searchNav({
pathname : '/search/result',
search : `?${createSearchParams(params)}`
})
}

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>

<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='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>
<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>
<button
className="search-btn"
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>
);
<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>
);
};

export default SearchPage;
Loading

0 comments on commit 008da63

Please sign in to comment.