diff --git a/frontend/src/components/@drawer/RecordDrawer/RecordDrawer.tsx b/frontend/src/components/@drawer/RecordDrawer/RecordDrawer.tsx index 6bfa829f..0f019917 100644 --- a/frontend/src/components/@drawer/RecordDrawer/RecordDrawer.tsx +++ b/frontend/src/components/@drawer/RecordDrawer/RecordDrawer.tsx @@ -1,30 +1,19 @@ -import DocsItem from '@components/DocsItem/DocsItem'; -import { REST_TYPE } from '@constants/rest.constant'; -import { DocsItemDtoType } from '@customType/dto'; -import axios from 'axios'; -import React, { useEffect, useState } from 'react'; +import React from 'react'; -const initialDocsList = [ - { docsUUID: 'asdf', createdAt: new Date(), playTime: 20000 }, - { docsUUID: 'qwe', createdAt: new Date(), playTime: 454630 }, - { docsUUID: 'zxc', createdAt: new Date(), playTime: 798760 }, - { docsUUID: 'wer', createdAt: new Date(), playTime: 58760 }, -]; +import DocsItem from '@components/InterviewDocsItem/InterviewDocsItem'; +import { docsListQuery } from '@store/interviewDocs.store'; +import { roomUUIDState } from '@store/room.store'; + +import { useRecoilValue } from 'recoil'; const RecordDrawer = () => { - const [docsList, setDocsList] = useState(initialDocsList); - // useEffect(() => { - // const fetch = async () => { - // const res = await axios.get(REST_TYPE.INTERVIEW_DOCS_LIST); - // setDocsList(res.data); - // }; - // fetch(); - // }, []); + const roomUUID = useRecoilValue(roomUUIDState); + const docsList = useRecoilValue(docsListQuery(roomUUID)); return ( <> {docsList.map((docs, idx) => ( - + ))} ); diff --git a/frontend/src/components/@drawer/UserDrawer/UserDrawer.tsx b/frontend/src/components/@drawer/UserDrawer/UserDrawer.tsx index 9fb4ce5a..bc4d2c41 100644 --- a/frontend/src/components/@drawer/UserDrawer/UserDrawer.tsx +++ b/frontend/src/components/@drawer/UserDrawer/UserDrawer.tsx @@ -22,6 +22,13 @@ const UserDrawer = () => { return ( <>
+
+
{me.nickname}
+
+ + +
+
{others.map((other, i) => (
{other.nickname}
diff --git a/frontend/src/components/Modal/EnterRoomModal.tsx b/frontend/src/components/@modal/EnterRoomModal.tsx similarity index 92% rename from frontend/src/components/Modal/EnterRoomModal.tsx rename to frontend/src/components/@modal/EnterRoomModal.tsx index 41a0688d..07fa1e6d 100644 --- a/frontend/src/components/Modal/EnterRoomModal.tsx +++ b/frontend/src/components/@modal/EnterRoomModal.tsx @@ -7,7 +7,7 @@ import { UserType } from '@customType/user'; import useSafeNavigate from '@hooks/useSafeNavigate'; import { PAGE_TYPE } from '@constants/page.constant'; import { useSetRecoilState } from 'recoil'; -import { meInRoomState, othersInRoomState } from '@store/room.store'; +import { meInRoomState, othersInRoomState, roomUUIDState } from '@store/room.store'; interface attendRoomResponseType { success?: boolean; @@ -21,6 +21,7 @@ const EnterRoomModal = () => { const [errorMsg, setErrorMsg] = useState(''); const setOthers = useSetRecoilState(othersInRoomState); const setMe = useSetRecoilState(meInRoomState); + const setRoom = useSetRecoilState(roomUUIDState); const { closeModal } = useModal(); const { safeNavigate } = useSafeNavigate(); @@ -40,6 +41,7 @@ const EnterRoomModal = () => { closeModal(); setOthers(others); setMe(me); + setRoom(roomUUID); safeNavigate(PAGE_TYPE.LOBBY_PAGE); } catch (e) { setErrorMsg(e.message); diff --git a/frontend/src/components/@modal/InterviewDocsListModal/InterviewDocsListModal.style.ts b/frontend/src/components/@modal/InterviewDocsListModal/InterviewDocsListModal.style.ts new file mode 100644 index 00000000..1ac846f7 --- /dev/null +++ b/frontend/src/components/@modal/InterviewDocsListModal/InterviewDocsListModal.style.ts @@ -0,0 +1,18 @@ +import { css } from '@emotion/react'; + +export const docsListWrapperStyle = css` + display: flex; + flex-direction: column; + gap: 15px; + height: 80vh; + width: 100%; +`; + +export const docsListHeaderStyle = css` + display: flex; + justify-content: space-between; +`; + +export const docsListBodyStyle = css` + overflow: scroll; +`; diff --git a/frontend/src/components/@modal/InterviewDocsListModal/InterviewDocsListModal.tsx b/frontend/src/components/@modal/InterviewDocsListModal/InterviewDocsListModal.tsx new file mode 100644 index 00000000..7b703f92 --- /dev/null +++ b/frontend/src/components/@modal/InterviewDocsListModal/InterviewDocsListModal.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import Modal from '@components/@shared/Modal/Modal'; +import { useRecoilValue } from 'recoil'; +import { docsListQuery } from '@store/interviewDocs.store'; +import DocsItem from '@components/InterviewDocsItem/InterviewDocsItem'; +import Button from '@components/@shared/Button/Button'; +import { + docsListBodyStyle, + docsListHeaderStyle, + docsListWrapperStyle, +} from './InterviewDocsListModal.style'; + +const InterviewDocsListModal = () => { + const docsList = useRecoilValue(docsListQuery('')); + return ( + +
+
+
인터뷰 기록
+ +
+
+ {docsList.map((docs, idx) => ( + + ))} +
+
+
+ ); +}; + +export default InterviewDocsListModal; diff --git a/frontend/src/components/Modal/InterviewDocsModal.tsx b/frontend/src/components/@modal/InterviewDocsModal.tsx similarity index 100% rename from frontend/src/components/Modal/InterviewDocsModal.tsx rename to frontend/src/components/@modal/InterviewDocsModal.tsx diff --git a/frontend/src/components/@modal/InterviewDocsModal/InterviewDocsModal.style.ts b/frontend/src/components/@modal/InterviewDocsModal/InterviewDocsModal.style.ts new file mode 100644 index 00000000..feb4f63f --- /dev/null +++ b/frontend/src/components/@modal/InterviewDocsModal/InterviewDocsModal.style.ts @@ -0,0 +1,54 @@ +import { css } from '@emotion/react'; + +export const docsItemWrapperStyle = css` + display: flex; + flex-direction: column; + gap: 15px; + padding: 10px; + height: 80vh; + width: 100%; +`; + +export const docsItemHeaderStyle = css` + display: flex; + align-items: center; + justify-content: space-between; +`; + +export const docsItemBtnsStyle = css` + display: flex; +`; + +export const docsItemBodyStyle = css` + display: flex; + gap: 15px; +`; + +export const docsItemVideoAreaStyle = css` + display: flex; + flex-direction: column; + gap: 15px; +`; + +export const docsItemVideoInfoStyle = css` + display: flex; + flex-direction: column; + gap: 5px; + div { + display: 'flex'; + } +`; + +export const docsItemFbAreaStyle = css` + display: flex; + flex-direction: column; + height: 100%; +`; + +export const docsItemFbBtnsStyle = css` + display: flex; +`; + +export const docsItemFbListStyle = css` + overflow: scroll; +`; diff --git a/frontend/src/components/@modal/InterviewDocsModal/InterviewDocsModal.tsx b/frontend/src/components/@modal/InterviewDocsModal/InterviewDocsModal.tsx new file mode 100644 index 00000000..37cdc7c0 --- /dev/null +++ b/frontend/src/components/@modal/InterviewDocsModal/InterviewDocsModal.tsx @@ -0,0 +1,91 @@ +import React, { useState } from 'react'; +import Modal from '@components/@shared/Modal/Modal'; +import { useRecoilValue } from 'recoil'; +import { docsItemQuery } from '@store/interviewDocs.store'; +import Button from '@components/@shared/Button/Button'; +import Video from '@components/@shared/Video/Video'; +import FeedbackList from '@components/FeedbackList/FeedbackList'; +import { getFirstLabeledFbList } from '@utils/common.util'; +import { + docsItemBodyStyle, + docsItemBtnsStyle, + docsItemFbAreaStyle, + docsItemFbBtnsStyle, + docsItemHeaderStyle, + docsItemWrapperStyle, + docsItemVideoAreaStyle, + docsItemVideoInfoStyle, + docsItemFbListStyle, +} from './InterviewDocsModal.style'; + +interface Props { + docsUUID: string; + idx: number; +} + +const InterviewDocsModal = ({ docsUUID, idx }: Props) => { + const docsItem = useRecoilValue(docsItemQuery(docsUUID)); + const { createdAt, videoPlayTime, feedbacks } = docsItem; + const createdAtDate = new Date(createdAt); + const [docIdx, setDocIdx] = useState(0); + + const handleChangeDocIdx = (idx) => { + setDocIdx(idx); + }; + + return ( + +
+
+
#{idx}
+
Sync
+
+ + +
+
+
+
+
+
+
+ {feedbacks.map((fb, i) => ( + + ))} +
+
+ {feedbacks[docIdx] ? ( + + ) : ( +
작성된 피드백이 없습니다.
+ )} +
+
+
+
+
+ ); +}; + +export default InterviewDocsModal; diff --git a/frontend/src/components/@shared/RoundButton/RoundButton.stories.tsx b/frontend/src/components/@shared/RoundButton/RoundButton.stories.tsx index 4f5fd4de..c366331f 100644 --- a/frontend/src/components/@shared/RoundButton/RoundButton.stories.tsx +++ b/frontend/src/components/@shared/RoundButton/RoundButton.stories.tsx @@ -1,37 +1,42 @@ import React from 'react'; import { Story } from '@storybook/react'; -import Button, { buttonPropType } from '../Button/Button'; +import RoundButton, { roundButtonPropType } from './RoundButton'; import { ReactComponent as FolderIcon } from '@assets/icon/folder.svg'; +import { iconSmStyle } from '@styles/commonStyle'; +import theme from '@styles/theme'; export default { - component: Button, - title: '@shared/Button', + component: RoundButton, + title: '@shared/RoundButton', }; -const Template: Story = (args) => ( - + ); - export const Default = Template.bind({}); -Default.args = {}; +Default.args = { + style: { backgroundColor: theme.colors.primary, width: 100, height: 50 }, +}; -const IconTemplate: Story = (args) => ( - + ); - export const IconButton = IconTemplate.bind({}); -IconButton.args = {}; +IconButton.args = { + style: { backgroundColor: theme.colors.primary, width: 130, height: 50 }, +}; -const IconOnlyTemplate: Story = (args) => ( - +const IconOnlyTemplate: Story = (args) => ( + + + ); - export const IconOnlyButton = IconOnlyTemplate.bind({}); -IconButton.args = {}; +IconOnlyButton.args = { + style: { backgroundColor: theme.colors.primary, width: 50, height: 50 }, +}; diff --git a/frontend/src/components/@shared/RoundButton/RoundButton.style.ts b/frontend/src/components/@shared/RoundButton/RoundButton.style.ts index 12212e8e..0cb199e5 100644 --- a/frontend/src/components/@shared/RoundButton/RoundButton.style.ts +++ b/frontend/src/components/@shared/RoundButton/RoundButton.style.ts @@ -1,16 +1,20 @@ import { css } from '@emotion/react'; +import theme from '@styles/theme'; export const roundButtonStyle = (style) => css` display: flex; box-sizing: border-box; - justify-content: center; + justify-content: space-around; align-items: center; width: ${style.width}px; height: ${style.height}px; border-radius: ${style.height / 2}px; - background-color: ${style.backgroundColor}; - color: ${style.color}; - padding: 0px 30px; + background-color: ${style.backgroundColor || theme.colors.primary}; + color: ${style.color || 'white'}; font-size: ${style.fontsize || '24px'}; + + &:hover { + filter: ${style.color === 'black' ? `brightness(200%)` : `brightness(110%)`}; + } `; diff --git a/frontend/src/components/@shared/RoundButton/RoundButton.tsx b/frontend/src/components/@shared/RoundButton/RoundButton.tsx index 311f75d9..193bbae7 100644 --- a/frontend/src/components/@shared/RoundButton/RoundButton.tsx +++ b/frontend/src/components/@shared/RoundButton/RoundButton.tsx @@ -1,21 +1,19 @@ import React from 'react'; import { roundButtonStyle } from './RoundButton.style'; -export interface Prop { +export interface roundButtonPropType { children?: React.ReactNode | React.ReactNode[]; onClick?: React.MouseEventHandler; style: StyleType; } - interface StyleType { width?: number; height?: number; backgroundColor?: string; color?: string; - iconColor?: boolean; } -const RoundButton = (prop: Prop) => { +const RoundButton = (prop: roundButtonPropType) => { const { children, onClick, style } = prop; return ( +
- +
diff --git a/frontend/src/pages/Interviewee/Interviewee.tsx b/frontend/src/pages/Interviewee/Interviewee.tsx index c0eee756..d0fb8a9d 100644 --- a/frontend/src/pages/Interviewee/Interviewee.tsx +++ b/frontend/src/pages/Interviewee/Interviewee.tsx @@ -6,9 +6,9 @@ import IntervieweeVideo from '@components/IntervieweeVideo/IntervieweeVideo'; import Video from '@components/@shared/Video/Video'; import useSafeNavigate from '@hooks/useSafeNavigate'; import usePreventLeave from '@hooks/usePreventLeave'; -import { webRTCStreamSelector, webRTCUserMapState } from '@store/webRTC.store'; +import { webRTCStreamSelector } from '@store/webRTC.store'; import { currentVideoTimeState } from '@store/currentVideoTime.store'; -import { docsUUIDState, userRoleSelector } from '@store/room.store'; +import { docsUUIDState, userRoleSelector } from '@store/interview.store'; import { socket } from '@service/socket'; import mediaStreamer from '@service/mediaStreamer'; @@ -20,7 +20,6 @@ import { DocsReqDtoType } from '@customType/dto'; import { intervieweeWrapperStyle } from './Interviewee.style'; import BottomBar from '@components/BottomBar/BottomBar'; import RoundButton from '@components/@shared/RoundButton/RoundButton'; -import theme from '@styles/theme'; const Interviewee = () => { usePreventLeave(); @@ -28,7 +27,6 @@ const Interviewee = () => { const { startStream, stopStream } = mediaStreamer(); const { interviewee, interviewerList } = useRecoilValue(userRoleSelector); - const webRTCUserMap = useRecoilValue(webRTCUserMapState); const currentVideoTime = useRecoilValue(currentVideoTimeState); const streamList = useRecoilValue(webRTCStreamSelector); const setDocsUUID = useSetRecoilState(docsUUIDState); @@ -74,14 +72,12 @@ const Interviewee = () => { const endInterviewBtn = ( -
면접 종료
+ 면접 종료
); diff --git a/frontend/src/pages/Interviewer/Interviewer.tsx b/frontend/src/pages/Interviewer/Interviewer.tsx index 9d090d19..48fda1dd 100644 --- a/frontend/src/pages/Interviewer/Interviewer.tsx +++ b/frontend/src/pages/Interviewer/Interviewer.tsx @@ -1,5 +1,5 @@ import React, { useEffect } from 'react'; -import { useRecoilValue } from 'recoil'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; import IntervieweeVideo from '@components/IntervieweeVideo/IntervieweeVideo'; import FeedbackList from '@components/FeedbackList/FeedbackList'; @@ -7,7 +7,8 @@ import Video from '@components/@shared/Video/Video'; import useSafeNavigate from '@hooks/useSafeNavigate'; import usePreventLeave from '@hooks/usePreventLeave'; import { webRTCStreamSelector } from '@store/webRTC.store'; -import { userRoleSelector } from '@store/room.store'; +import { docsUUIDState, userRoleSelector } from '@store/interview.store'; +import { feedbackListSelector } from '@store/feedback.store'; import { socket } from '../../service/socket'; import { PAGE_TYPE } from '@constants/page.constant'; @@ -24,8 +25,10 @@ const Interviewer = () => { const { safeNavigate } = useSafeNavigate(); usePreventLeave(); + const feedbackList = useRecoilValue(feedbackListSelector); const { interviewee, interviewerList } = useRecoilValue(userRoleSelector); const streamList = useRecoilValue(webRTCStreamSelector); + const setDocsUUID = useSetRecoilState(docsUUIDState); const hadleEndInterview = () => { socketEmit(SOCKET_EVENT_TYPE.END_INTERVIEW); @@ -36,7 +39,8 @@ const Interviewer = () => { }; useEffect(() => { - socket.on(SOCKET_EVENT_TYPE.START_FEEDBACK, () => { + socket.on(SOCKET_EVENT_TYPE.START_FEEDBACK, ({ docsUUID }) => { + setDocsUUID(docsUUID); safeNavigate(PAGE_TYPE.FEEDBACK_PAGE); }); }, []); @@ -44,14 +48,12 @@ const Interviewer = () => { const endInterviewBtn = ( -
면접 종료
+ 면접 종료
); @@ -77,7 +79,7 @@ const Interviewer = () => { ))}
- +
diff --git a/frontend/src/pages/Landing/Landing.tsx b/frontend/src/pages/Landing/Landing.tsx index a0f4dee1..03620ed2 100644 --- a/frontend/src/pages/Landing/Landing.tsx +++ b/frontend/src/pages/Landing/Landing.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React from 'react'; import { useRecoilRefresher_UNSTABLE, useSetRecoilState } from 'recoil'; import axios from 'axios'; import { useNavigate } from 'react-router-dom'; @@ -53,8 +53,8 @@ const Landing = () => { const handleSignOut = async () => { //TODO TOAST로 교체 - const res = await axios.get('/api/auth/logout'); - alert(res.status); + await axios.get('/api/auth/logout'); + alert('로그아웃 되었습니다.'); refreshAuth(); naviagte(ROUTE_TYPE.LOGIN_ROUTE); }; @@ -84,7 +84,7 @@ const Landing = () => { + 면접 시작 + ); return ( diff --git a/frontend/src/pages/Waiting/Waiting.tsx b/frontend/src/pages/Waiting/Waiting.tsx index bf392062..729254a9 100644 --- a/frontend/src/pages/Waiting/Waiting.tsx +++ b/frontend/src/pages/Waiting/Waiting.tsx @@ -1,7 +1,8 @@ import React, { useEffect } from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; -import { completedFbCntState, othersInRoomState } from '@store/room.store'; +import { othersInRoomState } from '@store/room.store'; +import { completedFbCntState } from '@store/interview.store'; import usePreventLeave from '@hooks/usePreventLeave'; import useSafeNavigate from '@hooks/useSafeNavigate'; import { PAGE_TYPE } from '@constants/page.constant'; @@ -9,9 +10,11 @@ import { PAGE_TYPE } from '@constants/page.constant'; import { socket } from '../../service/socket'; import BottomBar from '@components/BottomBar/BottomBar'; import { waitingWrapperStyle } from './Waiting.style'; +import useCleanupInterview from '@hooks/useCleanupInterview'; const Waiting = () => { usePreventLeave(); + const cleanupInterview = useCleanupInterview(); const { safeNavigate } = useSafeNavigate(); const totalUser = useRecoilValue(othersInRoomState); const [completedFbCnt, setCompletedFbCnt] = useRecoilState(completedFbCntState); @@ -27,6 +30,10 @@ const Waiting = () => { }); }, []); + useEffect(() => { + return cleanupInterview; + }, []); + return ( <>
diff --git a/frontend/src/service/socket.ts b/frontend/src/service/socket.ts index 41d77f3a..bf939f71 100644 --- a/frontend/src/service/socket.ts +++ b/frontend/src/service/socket.ts @@ -1,3 +1,3 @@ import { io } from 'socket.io-client'; -export const socket = io('/socket'); \ No newline at end of file +export const socket = io('/socket'); diff --git a/frontend/src/store/auth.store.ts b/frontend/src/store/auth.store.ts index 5494a3a2..7fe1433c 100644 --- a/frontend/src/store/auth.store.ts +++ b/frontend/src/store/auth.store.ts @@ -1,3 +1,5 @@ +import { REST_TYPE } from '@constants/rest.constant'; +import { SC_TYPE } from '@constants/statusCode.constants'; import axios from 'axios'; import { selector } from 'recoil'; @@ -5,12 +7,10 @@ const isAuthQuery = selector({ key: 'isAUth', get: async () => { try { - const res = await axios.get('/api/auth/login'); - console.log(res); - return res.status === 200; + const res = await axios.get(REST_TYPE.LOGIN); + return res.status === SC_TYPE.OK; } catch (e) { - console.log(e); - if (e.response.status === 401) return false; + if (e.response.status === SC_TYPE.UNAUTHORIZED) return false; throw e; } }, diff --git a/frontend/src/store/feedback.store.ts b/frontend/src/store/feedback.store.ts index bf5ad399..ab7b090c 100644 --- a/frontend/src/store/feedback.store.ts +++ b/frontend/src/store/feedback.store.ts @@ -1,4 +1,5 @@ import { EditableFeedbackType } from '@customType/feedback'; +import { getFirstLabeledFbList } from '@utils/common.util'; import { atom, atomFamily, selector } from 'recoil'; export const feedbackState = atomFamily({ @@ -29,16 +30,9 @@ export const isFbSyncState = atom({ export const feedbackListSelector = selector({ key: 'feedbackListSelector', get: ({ get }) => { - let prev = -1; - const fbList = get(feedbackIdsState).map((id) => { - const fb = { ...get(feedbackState(id)), isFirst: false }; - if (fb.startTime > prev) { - prev = fb.startTime; - fb.isFirst = true; - } - return fb; - }); - return fbList; + const fbList = get(feedbackIdsState).map((id) => get(feedbackState(id))); + const firstLabeledFbList = getFirstLabeledFbList(fbList); + return firstLabeledFbList; }, }); diff --git a/frontend/src/store/interview.store.ts b/frontend/src/store/interview.store.ts new file mode 100644 index 00000000..92b30b77 --- /dev/null +++ b/frontend/src/store/interview.store.ts @@ -0,0 +1,29 @@ +import { atom, selector } from 'recoil'; +import { meInRoomState, othersInRoomState } from './room.store'; + +export const docsUUIDState = atom({ + key: 'docsUUIDState', + default: null, +}); + +export const completedFbCntState = atom({ + key: 'completedFbCntState', + default: 0, +}); + +export const userRoleSelector = selector({ + key: 'userRoleSelector', + get: ({ get }) => { + const totalUser = [get(meInRoomState), ...get(othersInRoomState)]; + + return totalUser.reduce( + (acc, cur) => { + if (cur.role === 'interviewee') acc.interviewee = cur; + else acc.interviewerList.push(cur); + + return acc; + }, + { interviewee: null, interviewerList: [] } + ); + }, +}); diff --git a/frontend/src/store/interviewDocs.store.ts b/frontend/src/store/interviewDocs.store.ts new file mode 100644 index 00000000..acb9435a --- /dev/null +++ b/frontend/src/store/interviewDocs.store.ts @@ -0,0 +1,32 @@ +import { REST_TYPE } from '@constants/rest.constant'; +import { SC_TYPE } from '@constants/statusCode.constants'; +import { DocsItemDtoType, DocsResDtoType } from '@customType/dto'; +import axios from 'axios'; +import { selectorFamily } from 'recoil'; + +export const docsListQuery = selectorFamily({ + key: 'docsListQuery', + get: (roomUUID) => async () => { + try { + const params = { 'room-uuid': roomUUID }; + const res = await axios.get(REST_TYPE.INTERVIEW_DOCS_LIST, { params }); + + if (res.status === SC_TYPE.OK) return res.data.data; + } catch (e) { + console.log(e); + } + }, +}); + +export const docsItemQuery = selectorFamily({ + key: 'docsItemQuery', + get: (docsUUID) => async () => { + try { + const res = await axios.get(REST_TYPE.INTERVIEW_DOCS + `/${docsUUID}`); + + if (res.status === SC_TYPE.OK) return res.data.data; + } catch (e) { + console.log(e); + } + }, +}); diff --git a/frontend/src/store/room.store.ts b/frontend/src/store/room.store.ts index 5505b874..c0f3b4e9 100644 --- a/frontend/src/store/room.store.ts +++ b/frontend/src/store/room.store.ts @@ -1,16 +1,11 @@ import { UserType } from '@customType/user'; -import { atom, selector } from 'recoil'; +import { atom } from 'recoil'; export const roomUUIDState = atom({ key: 'roomUUIDState', default: null, }); -export const docsUUIDState = atom({ - key: 'docsUUIDState', - default: null, -}); - export const othersInRoomState = atom({ key: 'othersInRoomState', default: [], @@ -20,25 +15,3 @@ export const meInRoomState = atom({ key: 'meInRoomState', default: null, }); - -export const completedFbCntState = atom({ - key: 'completedFbCntState', - default: 0, -}); - -export const userRoleSelector = selector({ - key: 'userRoleSelector', - get: ({ get }) => { - const totalUser = [get(meInRoomState), ...get(othersInRoomState)]; - - return totalUser.reduce( - (acc, cur) => { - if (cur.role === 'interviewee') acc.interviewee = cur; - else acc.interviewerList.push(cur); - - return acc; - }, - { interviewee: null, interviewerList: [] } - ); - }, -}); diff --git a/frontend/src/utils/common.util.ts b/frontend/src/utils/common.util.ts index 9a1e2b6b..73fad736 100644 --- a/frontend/src/utils/common.util.ts +++ b/frontend/src/utils/common.util.ts @@ -1,4 +1,5 @@ import { ONE_MINUTE, ONE_SECOND } from '@constants/time.constant'; +import { EditableFeedbackType, FeedbackItemType, FeedbackType } from '@customType/feedback'; export const mmssFormatter = (ms: number) => { const mm = paddingFormatter(ms / ONE_MINUTE); @@ -14,3 +15,19 @@ const paddingFormatter = (n: number) => { const sliceSize = l > 2 ? l : 2; return ('00' + n).slice(-sliceSize); }; + +export const getFirstLabeledFbList = ( + fbList: EditableFeedbackType[] | FeedbackType[] +): FeedbackItemType[] => { + let prev = -1; + return fbList.map((fb_) => { + const fb = { ...fb_ }; + fb.isFirst = false; + if (fb.startTime > prev) { + prev = fb.startTime; + fb.isFirst = true; + } + + return fb; + }); +};