Skip to content

Commit 9c33516

Browse files
committed
발표 상세 페이지 추가
1 parent 3395b12 commit 9c33516

File tree

11 files changed

+162
-22
lines changed

11 files changed

+162
-22
lines changed

frontend/components/service/Program/CategoryListItem.tsx

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import styled from 'styled-components'
33
import { ITalkItem } from '../../../interfaces/IProgram'
44
import { media } from '../../../assets/styles/mixin'
55
import { Heading4 } from '../../../assets/styles/typo'
6-
7-
const DEFAULT_PROFILE_PATH = '/images/pyconkr2022-profile.png'
6+
import Link from 'next/link'
7+
import { DEFAULT_PROFILE_PATH } from '../../../data/constants/config'
88

99
const TalkList = styled.ul`
1010
margin-top: 1.5rem;
@@ -17,8 +17,8 @@ const TalkListItem = styled.li`
1717
`)}
1818
}
1919
`
20-
const TalkLink = styled.a`
21-
display: inline-block;
20+
const TalkLink = styled(Link)`
21+
display: block;
2222
`
2323
const TalkBlock = styled.div`
2424
display: flex;
@@ -49,18 +49,20 @@ const CategoryListItem = (props: { list: ITalkItem[] }) => {
4949
{props.list.map((talk, index) => (
5050
<TalkListItem key={talk.id}>
5151
<TalkLink href={`/program/talks/${talk.id}`}>
52-
<TalkBlock>
53-
<SpeakerProfile
54-
image={
55-
talk.speaker_profile_img ??
56-
DEFAULT_PROFILE_PATH
57-
}
58-
/>
59-
<PersonBlock>
60-
<Title>{talk.title}</Title>
61-
<Speaker>{talk.user_name}</Speaker>
62-
</PersonBlock>
63-
</TalkBlock>
52+
<a>
53+
<TalkBlock>
54+
<SpeakerProfile
55+
image={
56+
talk.speaker_profile_img ??
57+
DEFAULT_PROFILE_PATH
58+
}
59+
/>
60+
<PersonBlock>
61+
<Title>{talk.title}</Title>
62+
<Speaker>{talk.user_name}</Speaker>
63+
</PersonBlock>
64+
</TalkBlock>
65+
</a>
6466
</TalkLink>
6567
</TalkListItem>
6668
))}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { ISpeaker } from '../../../interfaces/IProgram'
2+
import styled from 'styled-components'
3+
import { media } from '../../../assets/styles/mixin'
4+
import Linkify from 'react-linkify'
5+
6+
const SpeakerContainer = styled.div`
7+
display: flex;
8+
${media.mobile(`
9+
display: block;
10+
`)}
11+
`
12+
const SpeakerImage = styled.img`
13+
display: inline-flex;
14+
width: 200px;
15+
height: auto;
16+
${media.mobile(`
17+
width: 100%;
18+
`)}
19+
`
20+
const SpeakerInfo = styled.div`
21+
margin-left: 1rem;
22+
${media.mobile(`
23+
margin: 0;
24+
`)}
25+
`
26+
const SpeakerName = styled.div`
27+
font-weight: bold;
28+
${media.mobile(`
29+
margin-top: 1rem;
30+
`)}
31+
`
32+
const SpeakerIntro = styled.p`
33+
margin-top: 0.8rem;
34+
a {
35+
color: ${(props) => props.theme.colors.blue0}
36+
}
37+
`
38+
39+
const Speaker = (props: { item: ISpeaker }) => {
40+
const { item } = props
41+
42+
return (
43+
<SpeakerContainer>
44+
<SpeakerImage src={item.imageUrl} alt={item.name} />
45+
<SpeakerInfo>
46+
<SpeakerName>{item.name}</SpeakerName>
47+
<SpeakerIntro>
48+
<Linkify>{item.introduction}</Linkify>
49+
</SpeakerIntro>
50+
</SpeakerInfo>
51+
</SpeakerContainer>
52+
)
53+
}
54+
55+
export default Speaker

frontend/data/constants/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
// TODO: move endpoint path
22
export const API_SERVER = 'https://api.2022.pycon.kr/api'
3+
export const DEFAULT_PROFILE_PATH = '/images/pyconkr2022-profile.png'

frontend/interfaces/IProgram.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,9 @@ export interface ICategoryListItem {
1010
export interface ITalkList {
1111
list: ITalkItem[]
1212
}
13+
14+
export interface ISpeaker {
15+
imageUrl: string
16+
name: string
17+
introduction: string
18+
}

frontend/interfaces/api/IApiPrograms.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ export interface IApiTalkItem {
1313
track_num: number | null
1414
introduction: string
1515
category: string
16-
speaker_profile_img: string | null
16+
speaker_profile_img: string
1717
}

frontend/locales/en/label.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,9 @@ export default {
3636
contact: {
3737
3838
sponsor: '[email protected]'
39-
}
39+
},
40+
category: 'Category',
41+
difficulty: 'Difficulty',
42+
duration: 'Duration',
43+
language: 'Language',
4044
}

frontend/locales/ko/label.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,9 @@ export default {
3636
contact: {
3737
3838
sponsor: '[email protected]'
39-
}
39+
},
40+
category: '카테고리',
41+
difficulty: '난이도',
42+
duration: '발표 시간',
43+
language: '언어',
4044
}

frontend/next.config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ const nextConfig = {
1515
use: 'raw-loader'
1616
})
1717
return config
18+
},
19+
images: {
20+
domains: ['pyconweb2022-static.s3.amazonaws.com']
1821
}
1922
// experimental: {
2023
// outputStandalone: true,

frontend/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"react": "17.0.2",
2626
"react-dom": "17.0.2",
2727
"react-i18next": "^11.16.1",
28+
"react-linkify": "^1.0.0-alpha",
2829
"react-markdown": "^8.0.3",
2930
"remark-gfm": "^3.0.1",
3031
"serverless": "^2.72.3",
@@ -45,4 +46,4 @@
4546
"raw-loader": "^4.0.2",
4647
"typescript": "4.6.2"
4748
}
48-
}
49+
}

frontend/pages/program/talks/[id].tsx

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,55 @@
11
import { GetServerSideProps, GetServerSidePropsContext, NextPage } from 'next'
22
import React from 'react'
33
import { LocalePage } from '../../../interfaces/PageProps'
4-
import { ITalkItem } from '../../../interfaces/IProgram'
4+
import { ISpeaker, ITalkItem } from '../../../interfaces/IProgram'
55
import { getTalkById } from '../../api/program'
66
import PageTitle from '../../../components/core/PageTitle'
7+
import { useTranslation } from 'react-i18next'
8+
import Speaker from '../../../components/service/Program/Speaker'
9+
import { DEFAULT_PROFILE_PATH } from '../../../data/constants/config'
10+
import styled from 'styled-components'
11+
import { media } from '../../../assets/styles/mixin'
712

813
interface TalkListDetailProps extends LocalePage<ITalkItem> {
914
locale: string
1015
}
1116

17+
const SpeakerContainer = styled.div`
18+
margin-top: 4rem;
19+
${media.mobile(`
20+
margin-top: 6rem;
21+
`)}
22+
`
23+
const Description = styled.div`
24+
margin-top: 1rem;
25+
`
26+
27+
1228
const TalkListDetail: NextPage = (props: TalkListDetailProps) => {
13-
return <PageTitle title={props.ko.title} />
29+
const { t } = useTranslation()
30+
31+
const item: ITalkItem = props[props.locale]
32+
const speaker: ISpeaker = {
33+
imageUrl: item.speaker_profile_img ?? DEFAULT_PROFILE_PATH,
34+
name: item.user_name,
35+
introduction: item.introduction
36+
}
37+
38+
return (
39+
<>
40+
<PageTitle title={item.title} />
41+
<div>
42+
{t('label:category')}: {item.category}
43+
</div>
44+
<div>{t('label:difficulty')}: {item.difficulty}</div>
45+
<div>{t('label:duration')}: {item.duration}</div>
46+
<div>{t('label:language')}: {item.language}</div>
47+
<Description>{item.desc}</Description>
48+
<SpeakerContainer>
49+
<Speaker item={speaker} />
50+
</SpeakerContainer>
51+
</>
52+
)
1453
}
1554

1655
export const getServerSideProps: GetServerSideProps = async (

0 commit comments

Comments
 (0)