From 789bce1e3b7fc1a3d3f90805697e5206ef800083 Mon Sep 17 00:00:00 2001 From: eternrust Date: Mon, 3 Jun 2024 23:00:49 +0900 Subject: [PATCH] ADD :: application search --- src/app/application/ShowSection.tsx | 113 +++++++++++++++++------- src/app/application/SideBar.tsx | 61 +++++++------ src/components/modal/TipModal.tsx | 1 + src/services/post/getApplicationData.ts | 19 ++++ src/services/post/index.ts | 3 +- src/types/post/index.ts | 3 +- src/types/post/postSearch.type.ts | 8 ++ 7 files changed, 142 insertions(+), 66 deletions(-) create mode 100644 src/services/post/getApplicationData.ts create mode 100644 src/types/post/postSearch.type.ts diff --git a/src/app/application/ShowSection.tsx b/src/app/application/ShowSection.tsx index 077fd42..2abe1fc 100644 --- a/src/app/application/ShowSection.tsx +++ b/src/app/application/ShowSection.tsx @@ -1,40 +1,69 @@ 'use client' import { ApplicationBox } from "@/components" +import { getApplicationData } from "@/services" import { ApplicationFileType, ApplicationPreviewType, MajorType } from "@/types" +import { getCookie, toast } from "@/utils" import { useSearchParams } from "next/navigation" -import { useEffect, useState } from "react" +import { useCallback, useEffect, useState } from "react" -const ApplyData: ApplicationPreviewType[] = [ - { - "post_id": 1, - "post_writer_id": "111899969632443851953", - "post_post_type": "Portfolio", - "post_title": "제목", - "post_major": "Backend", - "post_created_at": "2024-05-27T08:15:33.336Z" - } -] - -const majorData: MajorType[] = ['Frontend', 'Backend', 'Android', 'iOS', 'CrossPlatform', 'AI', 'DevOps', 'Design', 'Game', 'Blockchain'] +interface ApplicationDataModal { + kind: 'everything' | ApplicationFileType + major: MajorType, + keyword: string + sort: 'ASC' | 'DESC' +} const ShowSection = () => { - const [orderType, setOrderType] = useState<'first' | 'last'>('first') - const [selectedKind, setSelectedKind] = useState<'everything' | ApplicationFileType>('everything') - const [selectedMajor, setSelectedMajor] = useState>>({}) + const [hasToken, setHasToken] = useState(false) + const [orderType, setOrderType] = useState<'ASC' | 'DESC'>('ASC') const [searchWord, setSearchWord] = useState('') + const [applicationData, setApplicationData] = useState([]) const searchParams = useSearchParams() + const getData = useCallback(async (data: ApplicationDataModal) => { + const token = getCookie('access_token') + if (!token) { + toast.error('토큰이 없습니다!') + return + } + + const newData = await getApplicationData(token, data) + .then(res => { + toast.success('데이터를 성공적으로 불러왔습니다.') + return res.data.posts + }).catch(() => { + toast.error('데이터를 불러오는데 문제가 생겼습니다.') + return [] + }) + + setApplicationData(newData) + }, [orderType]) + useEffect(() => { + const token = getCookie('access_token') + const data: ApplicationDataModal = { + kind: 'everything', + major: 'Frontend', + keyword: '', + sort: orderType + } + if (searchParams.has('word')) { setSearchWord(searchParams.get('word') || '') + data.keyword = searchParams.get('word') || '' } if (searchParams.has('kind') && ['everything', 'Portfolio', 'PersonalStatement', 'Resume'].includes(searchParams.get('kind') as string)) { - setSelectedKind(searchParams.get('kind') as ('everything' | ApplicationFileType)) + data.kind = searchParams.get('kind') as ('everything' | ApplicationFileType) } - if (searchParams.has('major') && majorData.some(v => searchParams.getAll('major').includes(v))) { - setSelectedMajor(majorData.filter(v => searchParams.getAll('major').includes(v)).reduce((acc, v) => ({ ...acc, [v]: true }), {})) + if (searchParams.has('major')) { + data.major = searchParams.get('major') as MajorType + } + + if (token && data.keyword.length) { + getData(data) } - }, [searchParams]) + setHasToken(!!token) + }, [searchParams, orderType]) return (
@@ -44,21 +73,39 @@ const ShowSection = () => { 182개의 지원서 자료
-
{orderType === 'first' ? '최신순' : '오래된순'}
- setOrderType('first')}>최신순 - setOrderType('last')}>오래된순 +
{orderType === 'ASC' ? '최신순' : '오래된순'}
+ setOrderType('ASC')}>최신순 + setOrderType('DESC')}>오래된순
-
- { - ApplyData.map((item, index) => - - ) - } -
+ { + hasToken ? + searchWord ? +
+ { + applicationData.map((item, index) => + + ) + } +
+ : +
+
+ 검색어를 입력해주세요. + 그 후 Enter를 누르거나 검색 버튼을 클릭해주세요. +
+
+ : +
+
+ 로그인이 필요한 서비스입니다. + 먼저 로그인 후 이용해주세요. +
+
+ }
) } diff --git a/src/app/application/SideBar.tsx b/src/app/application/SideBar.tsx index 3649cbb..2ddb659 100644 --- a/src/app/application/SideBar.tsx +++ b/src/app/application/SideBar.tsx @@ -23,45 +23,44 @@ const tagToEnglish: Record = { const majorData: MajorType[] = ['Frontend', 'Backend', 'Android', 'iOS', 'CrossPlatform', 'AI', 'DevOps', 'Design', 'Game', 'Blockchain'] +interface ApplicationDataModal { + kind: KindType + major: MajorType, + searchWord: string +} + const SideBar = () => { - const [selectedKind, setSelectedKind] = useState('모든 종류') - // const [selectedMajor, setSelectedMajor] = useState>>({}) - const [selectedMajor, setSelectedMajor] = useState('Frontend') - const [searchWord, setSearchWord] = useState('') + const [data, setData] = useState({ + kind: '모든 종류', + major: 'Frontend', + searchWord: '' + }) const router = useRouter() const searchParams = useSearchParams() - const changeParams = useCallback((name: 'kind' | 'major' | 'word') => (value: string) => { + const changeData = useCallback((name: 'kind' | 'major' | 'searchWord') => (value: string) => { + setData(prev => ({ ...prev, [name]: value })) + }, []) + + const changeParams = useCallback(() => { const param = new URLSearchParams(searchParams.toString()) - if (name == 'major') { - // setSelectedMajor((prev) => ({ ...prev, [value as MajorType]: !prev[value as MajorType] })) - // const majorParams = [...param.getAll('major')] - setSelectedMajor(value as MajorType) - param.set(name, value) - // Object.entries(selectedMajor).forEach(v => v[1] && majorParams.includes(v[0]) && param.append(name, v[0])) - } else if (name === 'kind') { - setSelectedKind(value) - param.set(name, tagToEnglish[value as KindType]) - } else { - param.set(name, value) - } + param.set('major', data.major) + param.set('kind', tagToEnglish[data.kind as KindType]) + param.set('word', data.searchWord) router.push(`application?${param}`) - }, [searchParams]) + }, [searchParams, data]) useEffect(() => { if (searchParams.has('word')) { - setSearchWord(searchParams.get('word') || '') + changeData('searchWord')(searchParams.get('word') || '') } if (searchParams.has('kind') && ['everything', 'Portfolio', 'PersonalStatement', 'Resume'].includes(searchParams.get('kind') as string)) { - setSelectedKind(tagToKorean[searchParams.get('kind') as ('everything' | ApplicationFileType)]) + changeData('kind')(tagToKorean[searchParams.get('kind') as ('everything' | ApplicationFileType)]) } - // if (searchParams.has('major') && majorData.some(v => searchParams.getAll('major').includes(v))) { - // setSelectedMajor(majorData.filter(v => searchParams.getAll('major').includes(v)).reduce((acc, v) => ({ ...acc, [v]: true }), {})) - // } if (searchParams.has('major')) { - setSelectedMajor(searchParams.get('major') as MajorType) + changeData('major')(searchParams.get('major') as MajorType) } }, [searchParams]) @@ -73,9 +72,9 @@ const SideBar = () => { id="searchInput" placeholder="포트폴리오/자기소개서 검색" className="bg-transparent w-full border-none outline-none placeholder:text-gray500 text-black" - defaultValue={searchWord} - onChange={(e) => setSearchWord(e.currentTarget.value)} - onKeyDown={(e) => e.key === 'Enter' && changeParams('word')(searchWord)} + defaultValue={data.searchWord} + onChange={(e) => changeData('searchWord')(e.currentTarget.value)} + onKeyDown={(e) => e.key === 'Enter' && changeParams()} />
@@ -85,8 +84,8 @@ const SideBar = () => { icon={} title='지원서 종류' display={kindData} - value={selectedKind} - setValue={changeParams('kind')} + value={data.kind} + setValue={changeData('kind')} open />
@@ -95,8 +94,8 @@ const SideBar = () => { icon={} title='작성자 종류' display={majorData} - value={selectedMajor} - setValue={changeParams('major')} + value={data.major} + setValue={changeData('major')} />
diff --git a/src/components/modal/TipModal.tsx b/src/components/modal/TipModal.tsx index b65803e..5324427 100644 --- a/src/components/modal/TipModal.tsx +++ b/src/components/modal/TipModal.tsx @@ -66,6 +66,7 @@ export const TipModal = ({ click, change = () => { } }: TipModalType) => { placeholder="팁 제목을 입력해 주세요." value={data.title} change={(e) => changeData('title', e.currentTarget.value)} + err={data.title.length > 55} /> {/* 내용 입력 */} { + return await instance({ + method: 'GET', + url: '/post/search', + params: data, + headers: { + Authorization: `Bearer ${token}` + } + }) +} \ No newline at end of file diff --git a/src/services/post/index.ts b/src/services/post/index.ts index cfe97df..adee442 100644 --- a/src/services/post/index.ts +++ b/src/services/post/index.ts @@ -1,3 +1,4 @@ export * from './postLink' export * from './postFile' -export * from './getRecommend' \ No newline at end of file +export * from './getRecommend' +export * from './getApplicationData' \ No newline at end of file diff --git a/src/types/post/index.ts b/src/types/post/index.ts index 5b376aa..108e940 100644 --- a/src/types/post/index.ts +++ b/src/types/post/index.ts @@ -1,2 +1,3 @@ export { type PostLinkType } from './postLink.type' -export { type UploadTipType } from './uploadTip.type' \ No newline at end of file +export { type UploadTipType } from './uploadTip.type' +export { type PostSearchType } from './postSearch.type' \ No newline at end of file diff --git a/src/types/post/postSearch.type.ts b/src/types/post/postSearch.type.ts new file mode 100644 index 0000000..2f9a628 --- /dev/null +++ b/src/types/post/postSearch.type.ts @@ -0,0 +1,8 @@ +import { ApplicationFileType, MajorType } from ".." + +export interface PostSearchType { + keyword: string + kind: 'everything' | ApplicationFileType + major: MajorType, + sort: 'ASC' | 'DESC' +} \ No newline at end of file