diff --git a/frontend/components/service/Program/DifficultyBadge.tsx b/frontend/components/service/Program/DifficultyBadge.tsx
new file mode 100644
index 0000000..fecc651
--- /dev/null
+++ b/frontend/components/service/Program/DifficultyBadge.tsx
@@ -0,0 +1,22 @@
+import styled from 'styled-components'
+
+const DifficultyBadge = styled.span<{ level: string }>`
+ display: inline-block;
+ vertical-align: top;
+ padding: 0.3em 0.5em;
+ font-size: 0.7rem;
+ border-radius: 4px;
+ line-height: 1;
+ ${({ level }) => {
+ if (level === '초급') {
+ return `background-color: #139d2b;`
+ } else if (level === '중급') {
+ return `background-color: #ff7f00;`
+ } else if (level === '고급') {
+ return `background-color: #cf3535;`
+ }
+ return `display: none;`
+ }}
+`
+
+export default DifficultyBadge
diff --git a/frontend/components/service/Program/Speaker.tsx b/frontend/components/service/Program/Speaker.tsx
index 0526e10..8535344 100644
--- a/frontend/components/service/Program/Speaker.tsx
+++ b/frontend/components/service/Program/Speaker.tsx
@@ -1,3 +1,4 @@
+import React from 'react'
import { ISpeaker } from '../../../interfaces/IProgram'
import styled from 'styled-components'
import { media } from '../../../assets/styles/mixin'
diff --git a/frontend/components/service/Program/TalkTable.tsx b/frontend/components/service/Program/TalkTable.tsx
new file mode 100644
index 0000000..8945eb3
--- /dev/null
+++ b/frontend/components/service/Program/TalkTable.tsx
@@ -0,0 +1,124 @@
+import React from 'react'
+import { ITalkItem, ITalkTableList } from '../../../interfaces/IProgram'
+import styled from 'styled-components'
+import { format } from 'date-fns'
+import Link from 'next/link'
+import DifficultyBadge from './DifficultyBadge'
+import Resources from '../../../data/constants/resources'
+
+const Table = styled.table`
+ width: 100%;
+ border-collapse: collapse;
+ table-layout: fixed;
+`
+const LeftColumn = styled.div`
+ width: 90px;
+`
+
+const TableHeader = styled.thead`
+ border-top: 2px solid ${(props) => props.theme.colors.white};
+ border-bottom: 2px solid ${(props) => props.theme.colors.white};
+`
+const TableRow = styled.tr`
+ & + & {
+ border-top: 1px solid ${(props) => props.theme.colors.white};
+ }
+`
+const TableCell = styled.div`
+ padding: 1rem;
+ text-align: ${(props) => props.align ?? 'center'};
+`
+const BadgeWrap = styled.div`
+ margin-top: 0.5rem;
+`
+const Title = styled.div`
+ margin: 0.4rem 0;
+`
+const Speaker = styled.div`
+ color: ${(props) => props.theme.colors.violet0};
+`
+const BoldText = styled.div`
+ font-weight: bold;
+`
+
+const TalkTableItem = (props: { item: ITalkItem }) => {
+ const { item } = props
+ const isKeynote = item.category === Resources.KEYNOTE_NAME
+
+ return (
+
+
+ {isKeynote && [키노트]}
+ {item.title}
+ {item.user_name}
+
+
+ )
+}
+
+const TalkTable = (props: {
+ day: string
+ headers: string[]
+ list: ITalkTableList[]
+}) => {
+ const { list } = props
+
+ const sortByTrack = (list: ITalkItem[]) => {
+ return list.sort((a, b) => a.track_num - b.track_num)
+ }
+
+ const getTime = (timeString: string): string => {
+ return format(new Date(timeString), 'HH:mm')
+ }
+
+ return (
+
+
+
+
+
+
+ 시간
+
+ (KST)
+
+ |
+ {props.headers.map((header) => (
+ {header} |
+ ))}
+
+
+
+ {list.map((item, index) => (
+
+
+
+ {getTime(item.video_open_at)}
+
+ |
+ {item.talkList.length > 1 ? (
+ sortByTrack(item.talkList).map((talkItem) => (
+
+
+
+
+ |
+ ))
+ ) : (
+
+
+
+
+ |
+ )}
+
+ ))}
+
+
+
+ )
+}
+
+export default TalkTable
diff --git a/frontend/components/service/Program/TalkTableToggleButton.tsx b/frontend/components/service/Program/TalkTableToggleButton.tsx
new file mode 100644
index 0000000..ec03506
--- /dev/null
+++ b/frontend/components/service/Program/TalkTableToggleButton.tsx
@@ -0,0 +1,53 @@
+import React, { useState } from 'react'
+import styled from 'styled-components'
+
+const ButtonGroup = styled.div`
+ display: flex;
+ width: 100%;
+ align-items: center;
+ justify-content: center;
+ border-bottom: 1px solid ${(props) => props.theme.colors.white};
+`
+const Button = styled.button<{ selected: boolean }>`
+ border-radius: 4px;
+ font-size: 1rem;
+ padding: 1rem 2rem;
+ background: inherit;
+ cursor: pointer;
+ border: 0;
+ cursor: pointer;
+ color: ${(props) => props.theme.colors.white};
+ text-decoration: ${(props) => (props.selected ? 'underline' : 'none')};
+`
+
+interface ToggleProps {
+ handleClick: (day: string) => void
+}
+
+const TalkTableToggleButton: React.FC = ({ handleClick }) => {
+ const [checked, setChecked] = useState('day1')
+
+ const handleToggle = (day: string): void => {
+ setChecked(day)
+ handleClick(day)
+ }
+
+ return (
+
+
+
+
+ )
+}
+
+export default TalkTableToggleButton
diff --git a/frontend/interfaces/IProgram.ts b/frontend/interfaces/IProgram.ts
index 3490389..d54b8e6 100644
--- a/frontend/interfaces/IProgram.ts
+++ b/frontend/interfaces/IProgram.ts
@@ -1,5 +1,10 @@
import { IApiTalkItem } from './api/IApiPrograms'
+export interface ITalkTableList {
+ [key: string]: any
+ talkList: ITalkItem[]
+}
+
export interface ITalkItem extends IApiTalkItem {}
export interface ICategoryListItem {
diff --git a/frontend/locales/en/sponsorLevel.ts b/frontend/locales/en/sponsorLevel.ts
index 571eb6b..e113e5e 100644
--- a/frontend/locales/en/sponsorLevel.ts
+++ b/frontend/locales/en/sponsorLevel.ts
@@ -2,7 +2,7 @@ import { SponsorLevel } from '../../data/enums/SponsorLevel'
export default {
[SponsorLevel.LEVEL_1]: 'Keystone',
- [SponsorLevel.LEVEL_2]: 'Partner A',
+ [SponsorLevel.LEVEL_2]: 'Partner',
[SponsorLevel.LEVEL_3]: 'Partner B',
[SponsorLevel.LEVEL_4]: 'Startup',
[SponsorLevel.LEVEL_5]: 'Community'
diff --git a/frontend/locales/ko/sponsorLevel.ts b/frontend/locales/ko/sponsorLevel.ts
index 4eccf80..b370c37 100644
--- a/frontend/locales/ko/sponsorLevel.ts
+++ b/frontend/locales/ko/sponsorLevel.ts
@@ -2,7 +2,7 @@ import { SponsorLevel } from '../../data/enums/SponsorLevel'
export default {
[SponsorLevel.LEVEL_1]: '키스톤',
- [SponsorLevel.LEVEL_2]: '파트너A',
+ [SponsorLevel.LEVEL_2]: '파트너',
[SponsorLevel.LEVEL_3]: '파트너B',
[SponsorLevel.LEVEL_4]: '스타트업',
[SponsorLevel.LEVEL_5]: '커뮤니티'
diff --git a/frontend/package.json b/frontend/package.json
index 8d937bb..2bcb187 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -15,6 +15,7 @@
"@sls-next/serverless-component": "^3.7.0",
"axios": "^0.27.2",
"babel-plugin-styled-components": "^2.0.6",
+ "date-fns": "^2.29.3",
"env-cmd": "^10.1.0",
"gray-matter": "^4.0.3",
"i18next": "^21.6.14",
diff --git a/frontend/pages/program/talk-schedule.tsx b/frontend/pages/program/talk-schedule.tsx
index 883576b..6130440 100644
--- a/frontend/pages/program/talk-schedule.tsx
+++ b/frontend/pages/program/talk-schedule.tsx
@@ -1,25 +1,100 @@
-import React from 'react'
+import React, { useState } from 'react'
import type { NextPage, GetServerSideProps } from 'next'
import { useTranslation } from 'react-i18next'
import { PageName } from '../../data/enums/PageName'
import PageTitle from '../../components/core/PageTitle'
import { PageProps } from '../../interfaces/PageProps'
+import { getTalkList } from '../api/program'
+import { GetServerSidePropsContext } from 'next'
+import { ITalkItem, ITalkList, ITalkTableList } from '../../interfaces/IProgram'
+import { compareAsc, isSameDay } from 'date-fns'
+import TalkTableToggleButton from '../../components/service/Program/TalkTableToggleButton'
+import TalkTable from '../../components/service/Program/TalkTable'
-const TalkSchedule: NextPage = (props: PageProps) => {
+interface TalkTableProps extends PageProps {
+ data: ITalkList
+}
+
+const TalkSchedule: NextPage = (props: TalkTableProps) => {
const { t } = useTranslation()
+ const { pageName, data } = props
+ const [selectedDay, setSelectedDay] = useState('day1')
+
+ const updateSelectedDay = (day: string) => {
+ setSelectedDay(day)
+ }
+
+ const groupByProperty = (
+ array: ITalkItem[],
+ property: string
+ ): ITalkTableList[] => {
+ const groupByValue: { [key: string]: ITalkItem[] } = array.reduce(
+ (obj, item) => {
+ obj[item[property]] = obj[item[property]] || []
+ obj[item[property]].push(item)
+ return obj
+ },
+ {}
+ )
+
+ return Object.keys(groupByValue).map((key: string) => ({
+ [property]: key,
+ talkList: groupByValue[key]
+ }))
+ }
+
+ const tableData: ITalkItem[] = data.list.sort((a, b) =>
+ compareAsc(new Date(a.video_open_at), new Date(b.video_open_at))
+ )
+
+ const day1tableList: ITalkItem[] = tableData.filter((item) =>
+ isSameDay(new Date(item.video_open_at), new Date(2022, 9, 1))
+ )
+ const day2tableList: ITalkItem[] = tableData.filter((item) =>
+ isSameDay(new Date(item.video_open_at), new Date(2022, 9, 2))
+ )
return (
-
- {t('label:preparing')}
+
+
+ {selectedDay === 'day1' ? (
+
+ ) : (
+
+ )}
)
}
-export const getServerSideProps: GetServerSideProps = async () => {
- return {
- props: {
- title: PageName.TalkSchedule
+export const getServerSideProps: GetServerSideProps = async (
+ context: GetServerSidePropsContext
+) => {
+ const { locale } = context
+ try {
+ const data: ITalkList = await getTalkList()
+
+ return {
+ props: {
+ title: PageName.TalkSchedule,
+ locale,
+ data
+ }
+ }
+ } catch (error) {
+ // TODO: Add error interface
+ if (error.notFound) {
+ return {
+ notFound: true
+ }
}
}
}
diff --git a/pyconweb2022/sponsor/viewsets.py b/pyconweb2022/sponsor/viewsets.py
index 6ee01d4..3d53702 100644
--- a/pyconweb2022/sponsor/viewsets.py
+++ b/pyconweb2022/sponsor/viewsets.py
@@ -21,7 +21,9 @@ def get_queryset(self):
return Sponsor.objects.all()
def list(self, request, *args, **kwargs):
- queryset = Sponsor.objects.filter(accepted=True) # 모든 절차가 완료된 후원사만 리스팅
+ queryset = Sponsor.objects.filter(accepted=True).order_by(
+ "name"
+ ) # 모든 절차가 완료된 후원사만 리스팅
serializer = SponsorListSerializer(queryset, many=True)
return Response(serializer.data)
diff --git a/yarn.lock b/yarn.lock
index d86a0df..3ecb678 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3831,6 +3831,11 @@ data-uri-to-buffer@3.0.1:
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636"
integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==
+date-fns@^2.29.3:
+ version "2.29.3"
+ resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
+ integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
+
dayjs@^1.10.4, dayjs@^1.10.7:
version "1.11.2"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.2.tgz#fa0f5223ef0d6724b3d8327134890cfe3d72fbe5"