Skip to content

Commit 6595597

Browse files
authored
Merge pull request #116 from pythonkr/devdev
세션 시간표 반영
2 parents 370b23e + bc17438 commit 6595597

File tree

8 files changed

+294
-8
lines changed

8 files changed

+294
-8
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import styled from 'styled-components'
2+
3+
const DifficultyBadge = styled.span<{ level: string }>`
4+
display: inline-block;
5+
vertical-align: top;
6+
padding: 0.3em 0.5em;
7+
font-size: 0.7rem;
8+
border-radius: 4px;
9+
line-height: 1;
10+
${({ level }) => {
11+
if (level === '초급') {
12+
return `background-color: #139d2b;`
13+
} else if (level === '중급') {
14+
return `background-color: #ff7f00;`
15+
} else if (level === '고급') {
16+
return `background-color: #cf3535;`
17+
}
18+
return `display: none;`
19+
}}
20+
`
21+
22+
export default DifficultyBadge

frontend/components/service/Program/Speaker.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import React from 'react'
12
import { ISpeaker } from '../../../interfaces/IProgram'
23
import styled from 'styled-components'
34
import { media } from '../../../assets/styles/mixin'
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import React from 'react'
2+
import { ITalkItem, ITalkTableList } from '../../../interfaces/IProgram'
3+
import styled from 'styled-components'
4+
import { format } from 'date-fns'
5+
import Link from 'next/link'
6+
import DifficultyBadge from './DifficultyBadge'
7+
import Resources from '../../../data/constants/resources'
8+
9+
const Table = styled.table`
10+
width: 100%;
11+
border-collapse: collapse;
12+
table-layout: fixed;
13+
`
14+
const LeftColumn = styled.div`
15+
width: 90px;
16+
`
17+
18+
const TableHeader = styled.thead`
19+
border-top: 2px solid ${(props) => props.theme.colors.white};
20+
border-bottom: 2px solid ${(props) => props.theme.colors.white};
21+
`
22+
const TableRow = styled.tr`
23+
& + & {
24+
border-top: 1px solid ${(props) => props.theme.colors.white};
25+
}
26+
`
27+
const TableCell = styled.div`
28+
padding: 1rem;
29+
text-align: ${(props) => props.align ?? 'center'};
30+
`
31+
const BadgeWrap = styled.div`
32+
margin-top: 0.5rem;
33+
`
34+
const Title = styled.div`
35+
margin: 0.4rem 0;
36+
`
37+
const Speaker = styled.div`
38+
color: ${(props) => props.theme.colors.violet0};
39+
`
40+
const BoldText = styled.div`
41+
font-weight: bold;
42+
`
43+
44+
const TalkTableItem = (props: { item: ITalkItem }) => {
45+
const { item } = props
46+
const isKeynote = item.category === Resources.KEYNOTE_NAME
47+
48+
return (
49+
<Link href={`/program/talks/${item.id}`}>
50+
<a>
51+
{isKeynote && <BoldText>[키노트]</BoldText>}
52+
<Title>{item.title}</Title>
53+
<Speaker>{item.user_name}</Speaker>
54+
</a>
55+
</Link>
56+
)
57+
}
58+
59+
const TalkTable = (props: {
60+
day: string
61+
headers: string[]
62+
list: ITalkTableList[]
63+
}) => {
64+
const { list } = props
65+
66+
const sortByTrack = (list: ITalkItem[]) => {
67+
return list.sort((a, b) => a.track_num - b.track_num)
68+
}
69+
70+
const getTime = (timeString: string): string => {
71+
return format(new Date(timeString), 'HH:mm')
72+
}
73+
74+
return (
75+
<div>
76+
<Table>
77+
<TableHeader>
78+
<tr>
79+
<th>
80+
<LeftColumn>
81+
시간
82+
<br />
83+
(KST)
84+
</LeftColumn>
85+
</th>
86+
{props.headers.map((header) => (
87+
<th key={header}>{header}</th>
88+
))}
89+
</tr>
90+
</TableHeader>
91+
<tbody>
92+
{list.map((item, index) => (
93+
<TableRow key={`time-${index}`}>
94+
<td>
95+
<TableCell align={'left'}>
96+
{getTime(item.video_open_at)}
97+
</TableCell>
98+
</td>
99+
{item.talkList.length > 1 ? (
100+
sortByTrack(item.talkList).map((talkItem) => (
101+
<td key={`talk-${talkItem.id}`}>
102+
<TableCell>
103+
<TalkTableItem item={talkItem} />
104+
</TableCell>
105+
</td>
106+
))
107+
) : (
108+
<td colSpan={2}>
109+
<TableCell>
110+
<TalkTableItem
111+
item={item.talkList[0]}
112+
/>
113+
</TableCell>
114+
</td>
115+
)}
116+
</TableRow>
117+
))}
118+
</tbody>
119+
</Table>
120+
</div>
121+
)
122+
}
123+
124+
export default TalkTable
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React, { useState } from 'react'
2+
import styled from 'styled-components'
3+
4+
const ButtonGroup = styled.div`
5+
display: flex;
6+
width: 100%;
7+
align-items: center;
8+
justify-content: center;
9+
border-bottom: 1px solid ${(props) => props.theme.colors.white};
10+
`
11+
const Button = styled.button<{ selected: boolean }>`
12+
border-radius: 4px;
13+
font-size: 1rem;
14+
padding: 1rem 2rem;
15+
background: inherit;
16+
cursor: pointer;
17+
border: 0;
18+
cursor: pointer;
19+
color: ${(props) => props.theme.colors.white};
20+
text-decoration: ${(props) => (props.selected ? 'underline' : 'none')};
21+
`
22+
23+
interface ToggleProps {
24+
handleClick: (day: string) => void
25+
}
26+
27+
const TalkTableToggleButton: React.FC<ToggleProps> = ({ handleClick }) => {
28+
const [checked, setChecked] = useState<string>('day1')
29+
30+
const handleToggle = (day: string): void => {
31+
setChecked(day)
32+
handleClick(day)
33+
}
34+
35+
return (
36+
<ButtonGroup>
37+
<Button
38+
selected={checked === 'day1'}
39+
onClick={() => handleToggle('day1')}
40+
>
41+
10/1 (토)
42+
</Button>
43+
<Button
44+
selected={checked === 'day2'}
45+
onClick={() => handleToggle('day2')}
46+
>
47+
10/2 (일)
48+
</Button>
49+
</ButtonGroup>
50+
)
51+
}
52+
53+
export default TalkTableToggleButton

frontend/interfaces/IProgram.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { IApiTalkItem } from './api/IApiPrograms'
22

3+
export interface ITalkTableList {
4+
[key: string]: any
5+
talkList: ITalkItem[]
6+
}
7+
38
export interface ITalkItem extends IApiTalkItem {}
49

510
export interface ICategoryListItem {

frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"@sls-next/serverless-component": "^3.7.0",
1616
"axios": "^0.27.2",
1717
"babel-plugin-styled-components": "^2.0.6",
18+
"date-fns": "^2.29.3",
1819
"env-cmd": "^10.1.0",
1920
"gray-matter": "^4.0.3",
2021
"i18next": "^21.6.14",

frontend/pages/program/talk-schedule.tsx

Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,100 @@
1-
import React from 'react'
1+
import React, { useState } from 'react'
22
import type { NextPage, GetServerSideProps } from 'next'
33
import { useTranslation } from 'react-i18next'
44
import { PageName } from '../../data/enums/PageName'
55
import PageTitle from '../../components/core/PageTitle'
66
import { PageProps } from '../../interfaces/PageProps'
7+
import { getTalkList } from '../api/program'
8+
import { GetServerSidePropsContext } from 'next'
9+
import { ITalkItem, ITalkList, ITalkTableList } from '../../interfaces/IProgram'
10+
import { compareAsc, isSameDay } from 'date-fns'
11+
import TalkTableToggleButton from '../../components/service/Program/TalkTableToggleButton'
12+
import TalkTable from '../../components/service/Program/TalkTable'
713

8-
const TalkSchedule: NextPage = (props: PageProps) => {
14+
interface TalkTableProps extends PageProps {
15+
data: ITalkList
16+
}
17+
18+
const TalkSchedule: NextPage = (props: TalkTableProps) => {
919
const { t } = useTranslation()
20+
const { pageName, data } = props
21+
const [selectedDay, setSelectedDay] = useState<string>('day1')
22+
23+
const updateSelectedDay = (day: string) => {
24+
setSelectedDay(day)
25+
}
26+
27+
const groupByProperty = (
28+
array: ITalkItem[],
29+
property: string
30+
): ITalkTableList[] => {
31+
const groupByValue: { [key: string]: ITalkItem[] } = array.reduce(
32+
(obj, item) => {
33+
obj[item[property]] = obj[item[property]] || []
34+
obj[item[property]].push(item)
35+
return obj
36+
},
37+
{}
38+
)
39+
40+
return Object.keys(groupByValue).map((key: string) => ({
41+
[property]: key,
42+
talkList: groupByValue[key]
43+
}))
44+
}
45+
46+
const tableData: ITalkItem[] = data.list.sort((a, b) =>
47+
compareAsc(new Date(a.video_open_at), new Date(b.video_open_at))
48+
)
49+
50+
const day1tableList: ITalkItem[] = tableData.filter((item) =>
51+
isSameDay(new Date(item.video_open_at), new Date(2022, 9, 1))
52+
)
53+
const day2tableList: ITalkItem[] = tableData.filter((item) =>
54+
isSameDay(new Date(item.video_open_at), new Date(2022, 9, 2))
55+
)
1056

1157
return (
1258
<div>
13-
<PageTitle title={props.pageName} />
14-
{t('label:preparing')}
59+
<PageTitle title={pageName} />
60+
<TalkTableToggleButton handleClick={updateSelectedDay} />
61+
{selectedDay === 'day1' ? (
62+
<TalkTable
63+
day="day1"
64+
headers={['트랙1', '트랙2']}
65+
list={groupByProperty(day1tableList, 'video_open_at')}
66+
/>
67+
) : (
68+
<TalkTable
69+
day="day2"
70+
headers={['트랙3', '트랙4']}
71+
list={groupByProperty(day2tableList, 'video_open_at')}
72+
/>
73+
)}
1574
</div>
1675
)
1776
}
1877

19-
export const getServerSideProps: GetServerSideProps = async () => {
20-
return {
21-
props: {
22-
title: PageName.TalkSchedule
78+
export const getServerSideProps: GetServerSideProps = async (
79+
context: GetServerSidePropsContext
80+
) => {
81+
const { locale } = context
82+
try {
83+
const data: ITalkList = await getTalkList()
84+
85+
return {
86+
props: {
87+
title: PageName.TalkSchedule,
88+
locale,
89+
data
90+
}
91+
}
92+
} catch (error) {
93+
// TODO: Add error interface
94+
if (error.notFound) {
95+
return {
96+
notFound: true
97+
}
2398
}
2499
}
25100
}

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3831,6 +3831,11 @@ [email protected]:
38313831
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636"
38323832
integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==
38333833

3834+
date-fns@^2.29.3:
3835+
version "2.29.3"
3836+
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
3837+
integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
3838+
38343839
dayjs@^1.10.4, dayjs@^1.10.7:
38353840
version "1.11.2"
38363841
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.2.tgz#fa0f5223ef0d6724b3d8327134890cfe3d72fbe5"

0 commit comments

Comments
 (0)