Skip to content

Commit

Permalink
Merge pull request #30 from Pro-Pofol/feature/#28-application_api
Browse files Browse the repository at this point in the history
application api 연동(미완)
  • Loading branch information
eternrust authored May 27, 2024
2 parents 8d8c36f + ab285d4 commit 2c4a36e
Show file tree
Hide file tree
Showing 20 changed files with 345 additions and 169 deletions.
86 changes: 48 additions & 38 deletions src/app/application/ShowSection.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,71 @@
'use client'
import { ApplicationBox } from "@/components"
import { useState } from "react"
import { ApplicationFileType, ApplicationPreviewType, MajorType } from "@/types"
import { useSearchParams } from "next/navigation"
import { useEffect, useState } from "react"

interface ApplicationBoxProps {
tag: "포트폴리오" | "자기소개서" | "이력서"
title: string
name: string
date: string
mainMajor?: string
subMajor?: string
}

const ApplyData: ApplicationBoxProps[] = [
{
tag: '포트폴리오',
title: '개인적으로 완벽한 포트폴리오',
name: '이강혁',
date: '2024-04-16',
mainMajor: 'Frontend',
subMajor: 'Backend'
},
const ApplyData: ApplicationPreviewType[] = [
{
tag: '자기소개서',
title: '자기소개의 참된 예를 잘 보여주는 글',
name: '강진현',
date: '2023-04-16',
mainMajor: 'Frontend'
"post_id": 1,
"post_post_type": "Portfolio",
"post_title": "엄청나고 위대한 포트폴리오 자료",
"post_major": "Backend",
"post_created_at": "2024-05-27T02:48:52.347Z",
"user_oauth_id": "110159413387878573726"
},
{
tag: '이력서',
title: '올바른 형식의 이력서 예시',
name: '임태곤',
date: '2024-04-06'
"post_id": 2,
"post_post_type": "Portfolio",
"post_title": "야호",
"post_major": "Frontend",
"post_created_at": "2024-05-27T04:26:28.899Z",
"user_oauth_id": "107359038156703139645"
},
{
tag: '포트폴리오',
title: '개인적으로 완벽한 포트폴리오',
name: '이강혁',
date: '2023-04-18',
mainMajor: 'Frontend',
subMajor: 'Backend'
"post_id": 3,
"post_post_type": "Portfolio",
"post_title": "테스트...",
"post_major": "Frontend",
"post_created_at": "2024-05-27T04:47:00.694Z",
"user_oauth_id": "107359038156703139645"
}
]

const majorData: MajorType[] = ['Frontend', 'Backend', 'Android', 'iOS', 'CrossPlatform', 'AI', 'DevOps', 'Design', 'Game', 'Blockchain']

const ShowSection = () => {
const [orderType, setOrderType] = useState<'first' | 'last'>('first')
const [selectedKind, setSelectedKind] = useState<'everything' | ApplicationFileType>('everything')
const [selectedMajor, setSelectedMajor] = useState<Partial<Record<MajorType, boolean>>>({})
const [searchWord, setSearchWord] = useState<string>('')
const searchParams = useSearchParams()

useEffect(() => {
if (searchParams.has('word')) {
setSearchWord(searchParams.get('word') || '')
}
if (searchParams.has('kind') && ['everything', 'Portfolio', 'PersonalStatement', 'Resume'].includes(searchParams.get('kind') as string)) {
setSelectedKind(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 }), {}))
}
}, [searchParams])

return (
<section className="relative pb-[120px] w-full">
<div className="py-6 px-10 flex justify-between items-center sticky bg-white top-0 left-0 w-full h-fit">
<span className="text-bodyLarge text-black">182개의 지원서 자료</span>
<div className="p-1 gap-0.5 flex rounded-full border h-12 border-gray200 bg-gray50 text-bodySmall relative text-gray600">
<div className="py-6 px-10 flex justify-between items-center sticky bg-white top-0 left-0 w-full h-fit flex-wrap gap-2">
<div className="flex gap-x-3 gap-y-2 flex-wrap break-keep">
{searchWord && <span className="text-bodyLarge text-blue500">{searchWord}”에 대한</span>}
<span className="text-bodyLarge text-black">182개의 지원서 자료</span>
</div>
<div className="p-1 gap-0.5 flex rounded-full border h-12 border-gray200 bg-gray50 text-bodySmall relative text-gray600 ml-auto">
<div className={`absolute top-[2px] ${orderType === 'first' ? 'left-[3px]' : 'left-[80px]'} border border-gray100 bg-white py-2 px-4 text-transparent rounded-full transition-all`}>{orderType === 'first' ? '최신순' : '오래된순'}</div>
<span className={`transition-all py-2 px-4 z-10 cursor-pointer ${orderType === 'first' ? 'text-blue500' : 'text-gray600'}`} onClick={() => setOrderType('first')}>최신순</span>
<span className={`transition-all py-2 px-4 z-10 cursor-pointer ${orderType === 'last' ? 'text-blue500' : 'text-gray600'}`} onClick={() => setOrderType('last')}>오래된순</span>
</div>
</div>
<section className="grid grid-cols-3 gap-x-3 gap-y-6 px-10">
<section className="grid grid-cols-3 md:grid-cols-2 sm:grid-cols-1 gap-x-3 gap-y-6 px-10">
{
ApplyData.map((item, index) =>
<ApplicationBox
Expand Down
68 changes: 61 additions & 7 deletions src/app/application/SideBar.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,77 @@
'use client'
import { Bag, Portfolio, Search } from "@/assets"
import { useState } from "react"
import { useCallback, useEffect, useState } from "react"
import { SideSelect } from "./SideSelect"
import { usePathname, useRouter, useSearchParams } from "next/navigation"
import { ApplicationFileType, MajorType } from "@/types"

type KindType = '모든 종류' | '포트폴리오' | '자기소개서' | '이력서'
const kindData: KindType[] = ['모든 종류', '포트폴리오', '자기소개서', '이력서']
const tagToKorean: Record<'everything' | ApplicationFileType, KindType> = {
everything: '모든 종류',
Portfolio: '포트폴리오',
PersonalStatement: '자기소개서',
Resume: '이력서',
}

const tagToEnglish: Record<KindType, 'everything' | ApplicationFileType> = {
'모든 종류': 'everything',
포트폴리오: 'Portfolio',
자기소개서: 'PersonalStatement',
이력서: 'Resume',
}

type MajorType = 'Frontend' | 'Backend' | 'Android' | 'iOS' | 'CrossPlatform' | 'Ai' | 'DevOps' | 'Embeded' | 'Design' | 'Game' | 'BlockChain'
const majorData: MajorType[] = ['Frontend', 'Backend', 'Android', 'iOS', 'CrossPlatform', 'Ai', 'DevOps', 'Embeded', 'Design', 'Game', 'BlockChain']
const majorData: MajorType[] = ['Frontend', 'Backend', 'Android', 'iOS', 'CrossPlatform', 'AI', 'DevOps', 'Design', 'Game', 'Blockchain']

const SideBar = () => {
const [selectedKind, setSelectedKind] = useState<string>('모든 종류')
const [selectedMajor, setSelectedMajor] = useState<{ [key: string]: boolean }>({})
const [selectedMajor, setSelectedMajor] = useState<Partial<Record<MajorType, boolean>>>({})
const [searchWord, setSearchWord] = useState<string>('')
const router = useRouter()
const searchParams = useSearchParams()

const changeParams = useCallback((name: 'kind' | 'major' | 'word') => (value: string) => {
const param = new URLSearchParams(searchParams.toString())

if (name == 'major') {
setSelectedMajor((prev) => ({ ...prev, [value as MajorType]: !prev[value as MajorType] }))
const majorParams = [...param.getAll('major')]
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)
}

router.push(`application?${param}`)
}, [searchParams])

useEffect(() => {
if (searchParams.has('word')) {
setSearchWord(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)])
}
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 }), {}))
}
}, [searchParams])

return (
<section className="py-6 px-10 gap-6 flex flex-col border-r border-gray200 w-[400px] h-[1000px] sticky top-0 left-0">
<div className="py-3 px-5 border border-gray200 rounded-full bg-gray50 flex gap-3 h-fit focus-within:bg-blue50 focus-within:border-blue400">
<label htmlFor="searchInput"><Search /></label>
<input id="searchInput" placeholder="포트폴리오/자기소개서 검색" className="bg-transparent w-full border-none outline-none placeholder:text-gray500 text-black" />
<input
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)}
/>
</div>
<div className="gap-2 flex flex-col flex-2 w-full">
<div className="w-full h-px bg-gray200" />
Expand All @@ -27,7 +81,7 @@ const SideBar = () => {
title='지원서 종류'
display={kindData}
value={selectedKind}
setValue={(v) => setSelectedKind(v)}
setValue={changeParams('kind')}
open
/>
<div className="w-full h-px bg-gray200" />
Expand All @@ -37,7 +91,7 @@ const SideBar = () => {
title='작성자 종류'
display={majorData}
value={selectedMajor}
setValue={(v) => setSelectedMajor((prev) => ({ ...prev, [v]: !prev[v] }))}
setValue={changeParams('major')}
/>
</div>
</section>
Expand Down
7 changes: 5 additions & 2 deletions src/app/application/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { ApplicationBanner } from "./Banner";
import SideBar from "./SideBar";
import ShowSection from "./ShowSection";
import { Suspense } from "react";

export default function Application() {
return (
<main>
<ApplicationBanner />
<section className="flex py-6 relative">
<SideBar />
<ShowSection />
<Suspense>
<SideBar />
<ShowSection />
</Suspense>
</section>
</main>
)
Expand Down
47 changes: 4 additions & 43 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,7 @@
import { Bulb, Portfolio } from "@/assets";
import { MainBanner } from "./main";
import { Button, SubTitleSection, ApplicationBox, TipBox } from "@/components";

interface ApplicationBoxProps {
tag: "포트폴리오" | "자기소개서" | "이력서"
title: string
name: string
date: string
mainMajor?: string
subMajor?: string
}

const ApplyData: ApplicationBoxProps[] = [
{
tag: '포트폴리오',
title: '개인적으로 완벽한 포트폴리오',
name: '이강혁',
date: '2024-04-16',
mainMajor: 'Frontend',
subMajor: 'Backend'
},
{
tag: '자기소개서',
title: '자기소개의 참된 예를 잘 보여주는 글',
name: '강진현',
date: '2023-04-16',
mainMajor: 'Frontend'
},
{
tag: '이력서',
title: '올바른 형식의 이력서 예시',
name: '임태곤',
date: '2024-04-06'
},
{
tag: '포트폴리오',
title: '개인적으로 완벽한 포트폴리오',
name: '이강혁',
date: '2023-04-18',
mainMajor: 'Frontend',
subMajor: 'Backend'
}
]
import { getRecommend } from "@/services";

interface TipBoxProps {
title: string;
Expand Down Expand Up @@ -73,7 +33,8 @@ const TipData: TipBoxProps[] = [
},
]

export default function Home() {
export default async function Home() {
const applicationData = await getRecommend().then(res => res.data.posts) || []
return (
<main>
<MainBanner />
Expand All @@ -86,7 +47,7 @@ export default function Home() {
</div>
<div className="grid grid-cols-2 sm:grid-cols-1 gap-4">
{
ApplyData.map((item, index) =>
applicationData.slice(0, 4).map((item, index) =>
<ApplicationBox
key={index}
{...item}
Expand Down
40 changes: 25 additions & 15 deletions src/components/ApplicationBox.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client'

import { ApplicationFileType, ApplicationPreviewType } from "@/types"
import { dateToString } from "@/utils"
import { useRouter } from "next/navigation"

Expand All @@ -12,10 +13,16 @@ interface ApplicationBoxProps {
subMajor?: string
}

const tagColor = {
이력서: 'bg-gray100 text-gray500',
포트폴리오: 'bg-attentionBackground text-attention',
자기소개서: 'bg-coutionBackground text-coution',
const tagToKorean: Record<ApplicationFileType, string> = {
Portfolio: '포트폴리오',
PersonalStatement: '자기소개서',
Resume: '이력서',
}

const tagColor: Record<ApplicationFileType, string> = {
Portfolio: 'bg-attentionBackground text-attention',
PersonalStatement: 'bg-coutionBackground text-coution',
Resume: 'bg-gray100 text-gray500',
}

/**
Expand All @@ -28,22 +35,25 @@ const tagColor = {
<ApplicationBox tag="포트폴리오" title="제목" name="유저이름" mainMajor="Frontend" />
```
*/
export const ApplicationBox = ({ tag, title, name, date, mainMajor, subMajor }: ApplicationBoxProps) => {

const router = useRouter();
export const ApplicationBox = ({ post_id, post_title, post_post_type, user_oauth_id, post_major, post_created_at }: ApplicationPreviewType) => {
const router = useRouter()

return (
<div onClick={()=>router.push('/application/1')} className="flex flex-col w-full p-8 gap-3 border border-gray100 bg-white rounded-3xl cursor-pointer">
<div className={`px-[11px] w-fit h-fit py-1 rounded-full text-labelLarge ${tagColor[tag]}`}>{tag}</div>
<div onClick={() => router.push(`/application/${post_id}`)} className="flex flex-col w-full p-8 gap-3 border border-gray100 bg-white rounded-3xl cursor-pointer">
<div className={`px-[11px] w-fit h-fit py-1 rounded-full text-labelLarge ${tagColor[post_post_type]}`}>{tagToKorean[post_post_type]}</div>
<div className="flex flex-col gap-2">
<span className="text-titleMedium text-black truncate">{title}</span>
<span className="text-titleMedium text-black truncate">{post_title}</span>
<div className="flex justify-between items-center flex-wrap text-nowrap">
<div className="text-bodySmall text-gray600 flex items-center gap-2">
<span>{name}</span>
<span>{user_oauth_id}</span>
<div className="w-0.5 h-0.5 bg-gray600 rounded-full" />
<span>{dateToString(date)}</span>
<span>{dateToString(post_created_at)}</span>
</div>
{tag !== '이력서' && (
{
post_post_type !== 'Resume' &&
<span className="text-labelLarge text-gray500">{post_major}</span>
}
{/* {tag !== '이력서' && (
<div className="text-labelLarge text-gray500 flex items-center gap-2">
<span>{mainMajor}</span>
{
Expand All @@ -54,9 +64,9 @@ export const ApplicationBox = ({ tag, title, name, date, mainMajor, subMajor }:
</>
}
</div>
)}
)} */}
</div>
</div>
</div>
</div >
)
}
Loading

0 comments on commit 2c4a36e

Please sign in to comment.