Skip to content

Commit 4f173bd

Browse files
authored
[#227] ✨ 프로젝트 상세 페이지 (#235)
* [#227] ✨ add community id page * [#227] 💄 change people plus icon * [#227] ✨ add calculate date diff util funcs * [#227] 🔧 update types * [#227] ✨ add constants * [#227] ✨ portfolio detail page * [#227] ✨ project detail page * [#227] 🐛 solve param relates issues * [#227] ♻️ refactor content viewer * [#227] 🐛 solve build warnings * [#227] 🐛 solve content viewer any type
1 parent a2edaa5 commit 4f173bd

File tree

21 files changed

+389
-278
lines changed

21 files changed

+389
-278
lines changed

src/app/(pages)/community/[id]/page.tsx

Lines changed: 11 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,24 @@
1-
import { useState } from 'react'
1+
'use client'
2+
3+
import { useParams } from 'next/navigation'
24

35
import {
4-
IcAnswerBlue,
56
IcBin,
67
IcComment,
78
IcEdit,
89
IcEyeOpen,
910
IcHeart,
10-
IcPencil,
11-
IcPeopleMinus,
12-
IcPeoplePlus,
13-
IcSearch,
1411
IcShare,
1512
} from '@/assets/IconList'
16-
import { recruitmentStatusMap } from '@/constants/stateToLabelMaps'
17-
import { cn } from '@/lib/utils'
1813
import { CommunityDetail } from '@/types/api/Community.types'
19-
import { TeamRecruitmentListItem, TeamType } from '@/types/api/Team.types'
20-
import { CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight'
21-
import { Highlight as TiptapHighlight } from '@tiptap/extension-highlight'
22-
import SubScript from '@tiptap/extension-subscript'
23-
import { Superscript } from '@tiptap/extension-superscript'
24-
import { TextAlign } from '@tiptap/extension-text-align'
25-
import { TextStyle } from '@tiptap/extension-text-style'
26-
import { Underline } from '@tiptap/extension-underline'
27-
import { StarterKit } from '@tiptap/starter-kit'
28-
import hljs from 'highlight.js'
29-
import bash from 'highlight.js/lib/languages/bash'
30-
import css from 'highlight.js/lib/languages/css'
31-
import js from 'highlight.js/lib/languages/javascript'
32-
import json from 'highlight.js/lib/languages/json'
33-
import ts from 'highlight.js/lib/languages/typescript'
34-
import html from 'highlight.js/lib/languages/xml'
35-
import 'highlight.js/styles/github.css'
36-
import parse, { Element } from 'html-react-parser'
37-
import { all, createLowlight } from 'lowlight'
3814

3915
import { Avatar } from '@/components/common/avatar'
4016
import { Button, Clickable } from '@/components/common/button'
4117
import { Chip } from '@/components/common/chip'
42-
import { Box, Container, Grid } from '@/components/common/containers'
18+
import { Container } from '@/components/common/containers'
4319
import { Divider } from '@/components/common/divider'
44-
import { TextInput } from '@/components/common/input'
45-
import { Switch } from '@/components/common/switch/Switch'
46-
import { Highlight, Text } from '@/components/common/text'
47-
import { Pagination } from '@/components/shared/pagination'
48-
import { Select } from '@/components/shared/select'
49-
import { CareerSelect } from '@/components/shared/select/CareerSelect'
50-
import { TeamRecruitmentCard } from '@/components/team/TeamRecruitmentCard'
51-
52-
import { usePagination } from '@/hooks/usePagination'
53-
import { useToggle } from '@/hooks/useToggle'
54-
55-
const stackOptions = [
56-
{ label: '자바스크립트', value: 'Javascript' },
57-
{ label: 'Css', value: 'Css' },
58-
{ label: 'HTML', value: 'HTML' },
59-
{ label: '타입스크립트', value: 'Typescript' },
60-
]
61-
const positionOptions = [
62-
{ label: '프론트엔드', value: 'frontend' },
63-
{ label: '백엔드', value: 'backend' },
64-
{ label: '풀스택', value: 'fullstack' },
65-
]
66-
67-
const teamTypeMap: Record<TeamType, string> = {
68-
STUDY: '스터디',
69-
MENTORING: '멘토링',
70-
PROJECT: '프로젝트',
71-
}
72-
73-
interface CommunityDetailPageProps {
74-
params: {
75-
id: string
76-
}
77-
}
20+
import { Text } from '@/components/common/text'
21+
import { ContentViewer } from '@/components/shared/contentViewer'
7822

7923
const dummyCommunityDetail: CommunityDetail = {
8024
id: 1,
@@ -133,21 +77,12 @@ const useToggle = ({ initialValue = false }: UseToggleProps) => {
13377
isComment: true,
13478
}
13579

136-
// create a lowlight instance with all languages loaded
137-
const lowlight = createLowlight(all)
80+
export default function CommunityDetailPage(): JSX.Element {
81+
const params = useParams<{ id: string }>()
13882

139-
// Register specific languages
140-
lowlight.register('html', html)
141-
lowlight.register('css', css)
142-
lowlight.register('js', js)
143-
lowlight.register('ts', ts)
144-
lowlight.register('json', json)
145-
lowlight.register('bash', bash)
83+
const { id } = params
84+
console.log(id)
14685

147-
export default async function CommunityDetailPage({
148-
params,
149-
}: CommunityDetailPageProps): Promise<JSX.Element> {
150-
const { id } = await params
15186
const data = dummyCommunityDetail
15287
const {
15388
communityTitle,
@@ -168,35 +103,6 @@ export default async function CommunityDetailPage({
168103
}
169104

170105
// HTML 파싱 옵션 설정
171-
const options = {
172-
replace: domNode => {
173-
if (
174-
domNode instanceof Element &&
175-
domNode.name === 'code' &&
176-
domNode.attribs.class
177-
) {
178-
// language-xxx 형식에서 언어 추출
179-
const language = domNode.attribs.class.replace('language-', '')
180-
try {
181-
// 코드 내용 하이라이팅
182-
const highlightedCode = hljs.highlight(
183-
domNode.children[0].data || '',
184-
{ language }
185-
).value
186-
187-
return (
188-
<code
189-
className={domNode.attribs.class}
190-
dangerouslySetInnerHTML={{ __html: highlightedCode }}
191-
/>
192-
)
193-
} catch (e) {
194-
// 하이라이팅 실패 시 원본 반환
195-
return domNode
196-
}
197-
}
198-
},
199-
}
200106

201107
return (
202108
<Container className='mx-auto my-80 flex flex-col gap-20'>
@@ -234,7 +140,7 @@ export default async function CommunityDetailPage({
234140
{communityTitle}
235141
</Text.Heading>
236142
</div>
237-
<div className='tiptap mb-20'>{parse(communityContent, options)}</div>
143+
<ContentViewer content={communityContent} />
238144
<div className='flex items-center gap-8'>
239145
{isComment && (
240146
<Clickable
@@ -291,5 +197,3 @@ export default async function CommunityDetailPage({
291197
</Container>
292198
)
293199
}
294-
295-
// ...

src/app/(pages)/community/new/page.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33
import { Controller, useForm } from 'react-hook-form'
44

55
import { commuintyCategoryOptions } from '@/constants/selectOptions'
6-
import {
7-
COMMUNITY_EDITOR_CONTENT,
8-
TEAM_RECRUITMENT_EDITOR_CONTENT,
9-
} from '@/constants/tiptap'
6+
import { COMMUNITY_EDITOR_CONTENT } from '@/constants/tiptap'
107
import { TipTapEditor } from '@/lib/tiptap/TipTapEditor'
118
import { CreateCommunityRequest } from '@/types/api/Community.types'
129

src/app/(pages)/portfolio/[id]/page.tsx

Lines changed: 18 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
'use client'
2+
13
import Image from 'next/image'
24
import Link from 'next/link'
5+
import { useParams } from 'next/navigation'
36

47
import {
58
IcBin,
@@ -16,27 +19,20 @@ import {
1619
} from '@/constants/stateToLabelMaps'
1720
import { LINK_ICON_MAP } from '@/constants/valueIconMap'
1821
import { CreatePortfolioResponse } from '@/types/api/Portfolio.types'
19-
import hljs from 'highlight.js'
20-
import parse, { Element } from 'html-react-parser'
2122

2223
import { Avatar } from '@/components/common/avatar'
2324
import { Button, Clickable } from '@/components/common/button'
2425
import { Chip } from '@/components/common/chip'
2526
import { Container } from '@/components/common/containers'
2627
import { Divider } from '@/components/common/divider'
2728
import { Text } from '@/components/common/text'
29+
import { ContentViewer } from '@/components/shared/contentViewer'
2830

2931
import {
3032
calculatePeriod,
3133
calculateTotalCareerPeriod,
3234
} from '@/utils/calculatePeriod'
3335

34-
interface PortfolioDetailPageProps {
35-
params: {
36-
id: string
37-
}
38-
}
39-
4036
const dummyPortfolioDetail: CreatePortfolioResponse = {
4137
id: 1,
4238
writer: {
@@ -140,10 +136,9 @@ const dummyPortfolioDetail: CreatePortfolioResponse = {
140136
],
141137
}
142138

143-
export default async function PortfolioDetailPage({
144-
params,
145-
}: PortfolioDetailPageProps): Promise<JSX.Element> {
146-
const { id } = await params
139+
export default function PortfolioDetailPage(): JSX.Element {
140+
const params = useParams<{ id: string }>()
141+
const { id } = params
147142
console.log(id)
148143
const data = dummyPortfolioDetail
149144
const {
@@ -163,31 +158,6 @@ export default async function PortfolioDetailPage({
163158
awards,
164159
answers,
165160
} = data
166-
const options = {
167-
replace: domNode => {
168-
if (
169-
domNode instanceof Element &&
170-
domNode.name === 'code' &&
171-
domNode.attribs.class
172-
) {
173-
const language = domNode.attribs.class.replace('language-', '')
174-
try {
175-
const highlightedCode = hljs.highlight(
176-
domNode.children[0].data || '',
177-
{ language }
178-
).value
179-
return (
180-
<code
181-
className={domNode.attribs.class}
182-
dangerouslySetInnerHTML={{ __html: highlightedCode }}
183-
/>
184-
)
185-
} catch (e) {
186-
return domNode
187-
}
188-
}
189-
},
190-
}
191161

192162
return (
193163
<Container className='mx-auto my-80 flex flex-col gap-20'>
@@ -230,14 +200,6 @@ export default async function PortfolioDetailPage({
230200
{portTitle}
231201
</Text.Heading>
232202
</div>
233-
<div className='relative mb-20 h-400 w-full overflow-hidden rounded-16'>
234-
<Image
235-
src={portImageUrl}
236-
alt={portTitle}
237-
fill
238-
className='object-cover'
239-
/>
240-
</div>
241203
{links && links.length > 0 && (
242204
<section>
243205
<header className='h-50 border-y-1 border-solid border-gray-200 bg-gray-100 px-20 py-12'>
@@ -417,21 +379,25 @@ export default async function PortfolioDetailPage({
417379
</Text.Body>
418380
</div>
419381
{career.description && (
420-
<div className='tiptap'>
421-
{parse(career.description, options)}
422-
</div>
382+
<ContentViewer content={career.description} />
423383
)}
424384
</div>
425385
</li>
426386
))}
427387
</ul>
428388
</section>
429389
)}
430-
<div className='tiptap mb-20'>{parse(portContent, options)}</div>
390+
<div className='relative mb-20 h-400 w-full overflow-hidden rounded-16'>
391+
<Image
392+
src={portImageUrl}
393+
alt={portTitle}
394+
fill
395+
className='object-cover'
396+
/>
397+
</div>
398+
<ContentViewer content={portContent} />
431399
<div className='mb-12 flex gap-10'>
432-
{tags.map(tag => (
433-
<Chip key={tag} label={`#${tag}`} />
434-
))}
400+
{tags?.map(tag => <Chip key={tag} label={`#${tag}`} />)}
435401
</div>
436402
<div className='flex items-center gap-8'>
437403
<Clickable

src/app/(pages)/portfolio/new/page.tsx

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,11 @@
22

33
import { Controller, useForm } from 'react-hook-form'
44

5-
import { positionOptions, techStackOptions } from '@/constants/selectOptions'
6-
import {
7-
PORTFOLIO_EDITOR_CONTENT,
8-
TEAM_RECRUITMENT_EDITOR_CONTENT,
9-
} from '@/constants/tiptap'
10-
import { LINK_ICON_MAP } from '@/constants/valueIconMap'
5+
import { PORTFOLIO_EDITOR_CONTENT } from '@/constants/tiptap'
116
import { TipTapEditor } from '@/lib/tiptap/TipTapEditor'
12-
import {
13-
CreatePortfolioRequest,
14-
PortfolioDetail,
15-
} from '@/types/api/Portfolio.types'
16-
import { CreateTeamRecruitmentRequest } from '@/types/api/Team.types'
7+
import { CreatePortfolioRequest } from '@/types/api/Portfolio.types'
178

189
import { Button, Link } from '@/components/common/button'
19-
import { DeletableChip } from '@/components/common/chip'
2010
import { Container } from '@/components/common/containers'
2111
import { Label } from '@/components/common/label'
2212
import { Text } from '@/components/common/text'

0 commit comments

Comments
 (0)