From e23f96590c9356466bdf52e224b69683696d52ed Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Thu, 23 Jan 2025 20:31:57 +0900 Subject: [PATCH 01/16] =?UTF-8?q?refactor:=20InformationPanel=20=EA=B0=81?= =?UTF-8?q?=20=EC=9A=94=EC=86=8C=20jsx=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/Information/InformationPanel.tsx | 122 ++---------------- .../page/detail/Information/constant.tsx | 112 ++++++++++++++++ 2 files changed, 125 insertions(+), 109 deletions(-) create mode 100644 src/components/page/detail/Information/constant.tsx diff --git a/src/components/page/detail/Information/InformationPanel.tsx b/src/components/page/detail/Information/InformationPanel.tsx index 2b92508b..75408f79 100644 --- a/src/components/page/detail/Information/InformationPanel.tsx +++ b/src/components/page/detail/Information/InformationPanel.tsx @@ -1,14 +1,9 @@ import { TabList } from '@components/@common/tabList/TabList'; import { styled } from 'stitches.config'; import { useDisplay } from '@hooks/useDisplay'; -import { parseTextToLink } from '@components/util/parseTextToLink'; -import dayjs from 'dayjs'; -import 'dayjs/locale/ko'; -dayjs.locale('ko'); -import { PART_NAME } from '@constants/option'; import { useCallback, useRef, useState } from 'react'; import { GetMeetingResponse } from '@api/API_LEGACY/meeting'; -import { Chip } from '@sopt-makers/ui'; +import { meetingDetailList } from '@components/page/detail/Information/constant'; interface InformationPanelProps { detailData: GetMeetingResponse; @@ -17,87 +12,35 @@ interface InformationPanelProps { const InformationPanel = ({ detailData }: InformationPanelProps) => { const { isMobile } = useDisplay(); const tabRef = useRef([]); - const detailList = [ - { - id: 0, - title: '모임 소개', - content: detailData?.desc, - }, - { - id: 1, - title: '활동 기간', - content: `${dayjs(detailData?.mStartDate ?? '').format('YYYY.MM.DD (ddd)')} ~ ${dayjs( - detailData?.mEndDate ?? '' - ).format('YYYY.MM.DD (ddd)')}`, - }, - { - id: 2, - title: '진행 방식', - content: detailData?.processDesc, - }, - { - id: 3, - title: '모집 대상', - generation: detailData?.canJoinOnlyActiveGeneration ? '활동 기수' : '전체 기수', - partList: detailData?.joinableParts?.map(key => PART_NAME[key]), - //'이런 사람을 찾아요' Input이 사라지면서 해당 객체의 content는 삭제 됨 - //렌더링은 되어야하므로 truthy값으로 문자열 'NULL'을 삽입 - content: 'NULL', - }, - { - id: 4, - title: '개설자 소개', - content: detailData?.leaderDesc, - }, - { - id: 5, - title: '유의사항', - content: detailData?.note, - }, - ]; - const [selectedTab, setSelectedTab] = useState(detailList[0]?.title); + const detailList = meetingDetailList(detailData); + const [selectedTab, setSelectedTab] = useState(detailList[0]?.key); const handleChange = useCallback((text: string) => { setSelectedTab(text); - tabRef.current[detailList.findIndex(item => item.title === text)]?.scrollIntoView({ behavior: 'smooth' }); + tabRef.current[detailList.findIndex(item => item.key === text)]?.scrollIntoView({ behavior: 'smooth' }); }, []); - const handleContent = (content: string) => { - if (content === 'NULL') return; - return parseTextToLink(content); - }; - return ( {isMobile && ( {detailList.map( - ({ id, title, content }) => - content && ( - - {title} + ({ key, Title, isValid }) => + isValid && ( + + </TabList.Item> ) )} </TabList> )} {detailList.map( - ({ id, title, generation, partList, content }) => - content && ( + ({ key, Title, Content, isValid }, idx) => + isValid && ( // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - <SDetail key={id} ref={element => (tabRef.current[id] = element!)}> - <STitle>{title}</STitle> - {title === '모집 대상' && ( - <STarget> - {partList?.map(part => ( - <Chip key={part} style={{ width: '80px', boxShadow: 'none' }} active> - {part} - </Chip> - ))} - {generation} - </STarget> - )} - <SDescription>{handleContent(content)}</SDescription> + <SDetail key={key} ref={element => (tabRef.current[idx] = element!)}> + <Title /> + <Content /> </SDetail> ) )} @@ -122,42 +65,3 @@ const SDetail = styled('section', { mt: '$56', }, }); - -const STitle = styled('h2', { - fontAg: '24_bold_100', - mb: '$24', - - '@tablet': { - fontStyle: 'H4', - mb: '$20', - }, -}); - -const SDescription = styled('p', { - fontAg: '22_regular_170', - whiteSpace: 'pre-line', - color: '$gray200', - - a: { - textDecoration: 'underline', - wordBreak: 'break-all', - }, - - '@tablet': { - fontStyle: 'B3', - }, -}); - -const STarget = styled(SDescription, { - display: 'flex', - alignItems: 'center', - gap: '$10', - color: '$gray10', - flexWrap: 'wrap', - - mb: '$24', - - '@tablet': { - mb: '$20', - }, -}); diff --git a/src/components/page/detail/Information/constant.tsx b/src/components/page/detail/Information/constant.tsx new file mode 100644 index 00000000..d51e53f0 --- /dev/null +++ b/src/components/page/detail/Information/constant.tsx @@ -0,0 +1,112 @@ +import { GetMeetingResponse } from '@api/API_LEGACY/meeting'; +import { PART_NAME } from '@constants/option'; +import { Chip } from '@sopt-makers/ui'; +import dayjs from 'dayjs'; +import 'dayjs/locale/ko'; +import { styled } from 'stitches.config'; +import { parseTextToLink } from '@components/util/parseTextToLink'; +dayjs.locale('ko'); + +export const meetingDetailList = (detailData: GetMeetingResponse) => [ + { + key: '모임 소개', + Title: () => <STitle>모임 소개</STitle>, + Content: () => <SDescription>{parseTextToLink(detailData?.desc)},</SDescription>, + isValid: detailData?.desc, + }, + { + key: '활동 기간', + Title: () => <STitle>활동 기간</STitle>, + Content: () => ( + <SDescription>{`${dayjs(detailData?.mStartDate ?? '').format('YYYY.MM.DD (ddd)')} ~ ${dayjs( + detailData?.mEndDate ?? '' + ).format('YYYY.MM.DD (ddd)')}`}</SDescription> + ), + isValid: detailData?.mStartDate && detailData?.mEndDate, + }, + { + key: '진행 방식', + Title: () => <STitle>진행 방식</STitle>, + Content: () => <SDescription>{parseTextToLink(detailData?.processDesc)}</SDescription>, + isValid: detailData?.processDesc, + }, + { + key: '모집 대상', + Title: () => <STitle>모집 대상</STitle>, + Content: () => ( + <STarget> + {detailData?.joinableParts + ?.map(key => PART_NAME[key]) + .map(part => ( + <Chip key={part} style={{ width: '80px', boxShadow: 'none' }} active> + {part} + </Chip> + ))} + {detailData?.canJoinOnlyActiveGeneration ? '활동 기수' : '전체 기수'} + </STarget> + ), + isValid: detailData?.joinableParts, + }, + { + key: '개설자 소개', + Title: () => <STitle>개설자 소개</STitle>, + Content: () => <SDescription>{detailData?.leaderDesc}</SDescription>, + isValid: detailData?.leaderDesc, + }, + { + key: '유의사항', + Title: () => <STitle>유의 사항</STitle>, + Content: () => <SDescription>{parseTextToLink(detailData?.note)}</SDescription>, + isValid: detailData?.note, + }, +]; + +export const LighteningDetailList = (detailData: GetMeetingResponse) => [ + { + key: '#환영 태그', + content: detailData?.note, + }, + { + key: '설명', + content: detailData.desc, + }, +]; + +const STitle = styled('h2', { + fontAg: '24_bold_100', + mb: '$24', + + '@tablet': { + fontStyle: 'H4', + mb: '$20', + }, +}); + +const SDescription = styled('p', { + fontAg: '22_regular_170', + whiteSpace: 'pre-line', + color: '$gray200', + + a: { + textDecoration: 'underline', + wordBreak: 'break-all', + }, + + '@tablet': { + fontStyle: 'B3', + }, +}); + +const STarget = styled(SDescription, { + display: 'flex', + alignItems: 'center', + gap: '$10', + color: '$gray10', + flexWrap: 'wrap', + + mb: '$24', + + '@tablet': { + mb: '$20', + }, +}); From 7c10b85f41aae76773bf8575bed26234b59fb26b Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Thu, 23 Jan 2025 20:43:21 +0900 Subject: [PATCH 02/16] =?UTF-8?q?feat:=20=EA=B8=B0=EA=B0=84=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20=EC=9A=B0=EC=84=A0=20=ED=83=AD=20=ED=99=9C?= =?UTF-8?q?=EC=84=B1=ED=99=94=20=ED=95=B4=EC=A0=9C=20=EB=B0=8F=20=EB=AA=A8?= =?UTF-8?q?=EC=9E=84=20=EC=83=81=EC=84=B8=20=ED=83=AD=20=EC=9E=AC=EB=B0=B0?= =?UTF-8?q?=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/detail/index.tsx | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/pages/detail/index.tsx b/pages/detail/index.tsx index 0d61bcd0..3f5092c0 100644 --- a/pages/detail/index.tsx +++ b/pages/detail/index.tsx @@ -14,14 +14,13 @@ import FeedPanel from '@components/page/detail/Feed/FeedPanel'; import { Fragment, useEffect, useState } from 'react'; import dayjs from 'dayjs'; import 'dayjs/locale/ko'; -import { ERecruitmentStatus } from '@constants/option'; import MeetingController from '@components/page/detail/MeetingController'; dayjs.locale('ko'); const enum SelectedTab { - FEED, INFORMATION, + FEED, } const DetailPage = () => { @@ -33,12 +32,6 @@ const DetailPage = () => { const { mutate: mutateDeleteApplication } = useMutationDeleteApplication({}); const [selectedIndex, setSelectedIndex] = useState(SelectedTab.INFORMATION); - useEffect(() => { - if (detailData) { - setSelectedIndex(detailData.status === ERecruitmentStatus.OVER ? SelectedTab.FEED : SelectedTab.INFORMATION); - } - }, [detailData]); - useEffect(() => { const script = document.createElement('script'); script.async = true; @@ -82,18 +75,18 @@ const DetailPage = () => { <Tab.Group selectedIndex={selectedIndex} onChange={index => setSelectedIndex(index)}> <STabList> <Tab as={Fragment}> - <STabButton isSelected={selectedIndex === SelectedTab.FEED}>피드</STabButton> + <STabButton isSelected={selectedIndex === SelectedTab.INFORMATION}>모임 안내</STabButton> </Tab> <Tab as={Fragment}> - <STabButton isSelected={selectedIndex === SelectedTab.INFORMATION}>모임 안내</STabButton> + <STabButton isSelected={selectedIndex === SelectedTab.FEED}>피드</STabButton> </Tab> </STabList> <Tab.Panels> <Tab.Panel> - <FeedPanel isMember={detailData?.approved || detailData?.host} /> + <InformationPanel detailData={detailData} /> </Tab.Panel> <Tab.Panel> - <InformationPanel detailData={detailData} /> + <FeedPanel isMember={detailData?.approved || detailData?.host} /> </Tab.Panel> </Tab.Panels> </Tab.Group> From fc6eae82f2d2df1cded189c65b28bbc8c23affeb Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Thu, 23 Jan 2025 21:32:59 +0900 Subject: [PATCH 03/16] =?UTF-8?q?feat:=20=EB=B2=88=EC=A9=8D=20detailList?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../page/detail/Information/constant.tsx | 58 ++++++++++++++++++- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/src/components/page/detail/Information/constant.tsx b/src/components/page/detail/Information/constant.tsx index d51e53f0..2b6a5f19 100644 --- a/src/components/page/detail/Information/constant.tsx +++ b/src/components/page/detail/Information/constant.tsx @@ -6,12 +6,14 @@ import 'dayjs/locale/ko'; import { styled } from 'stitches.config'; import { parseTextToLink } from '@components/util/parseTextToLink'; dayjs.locale('ko'); +import CalendarIcon from '@assets/svg/calendar.svg'; +import LocationIcon from '@assets/svg/location.svg'; export const meetingDetailList = (detailData: GetMeetingResponse) => [ { key: '모임 소개', Title: () => <STitle>모임 소개</STitle>, - Content: () => <SDescription>{parseTextToLink(detailData?.desc)},</SDescription>, + Content: () => <SDescription>{parseTextToLink(detailData?.desc)}</SDescription>, isValid: detailData?.desc, }, { @@ -64,14 +66,58 @@ export const meetingDetailList = (detailData: GetMeetingResponse) => [ export const LighteningDetailList = (detailData: GetMeetingResponse) => [ { key: '#환영 태그', - content: detailData?.note, + Title: () => <STitle>#환영 태그</STitle>, + Content: () => ( + <STarget> + {detailData?.welcomeMessageTypes.map(tag => ( + <Chip key={tag} style={{ width: '80px', boxShadow: 'none' }} active> + {tag} + </Chip> + ))} + </STarget> + ), + isValid: detailData?.joinableParts, }, { key: '설명', - content: detailData.desc, + Title: () => <STitle>설명</STitle>, + Content: () => <SDescription>{parseTextToLink(detailData?.desc)}</SDescription>, + isValid: detailData?.desc, + }, + { + key: '진행일', + Title: () => ( + <SIconTitleWrapper> + <CalendarIcon /> + <STitle>진행일</STitle> + </SIconTitleWrapper> + ), + isValid: detailData.activityStartDate, + }, + { + key: '장소', + Title: () => ( + <SIconTitleWrapper> + <LocationIcon /> + <STitle>장소</STitle> + </SIconTitleWrapper> + ), + Content: () => <SDescription>{`${parsePlaceType(detailData.placeType, detailData.place)}`}</SDescription>, + isValid: detailData.place, }, ]; +const parsePlaceType = (placeType: string, place: string) => { + switch (placeType) { + case '오프라인': + return place; + case '온라인': + return `온라인 , ${place}`; + case '협의 후 결정': + return placeType; + } +}; + const STitle = styled('h2', { fontAg: '24_bold_100', mb: '$24', @@ -82,6 +128,12 @@ const STitle = styled('h2', { }, }); +const SIconTitleWrapper = styled('div', { + display: 'flex', + alignItems: 'center', + gap: '$12', +}); + const SDescription = styled('p', { fontAg: '22_regular_170', whiteSpace: 'pre-line', From 260a6d879d5b97e25ef900ef53d2062afb112886 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Thu, 23 Jan 2025 22:39:14 +0900 Subject: [PATCH 04/16] =?UTF-8?q?feat:=20=EB=AA=A8=EC=9E=84=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C=20Chip=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/assets/svg/icon_bolt.svg | 3 ++ public/assets/svg/icon_event.svg | 3 ++ public/assets/svg/icon_seminar.svg | 6 +++ public/assets/svg/icon_study.svg | 3 ++ .../Card/DesktopSizeCard/CategoryChip.tsx | 53 +++++++++++++++++++ .../index.tsx} | 18 +++---- src/constants/option.ts | 1 + 7 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 public/assets/svg/icon_bolt.svg create mode 100644 public/assets/svg/icon_event.svg create mode 100644 public/assets/svg/icon_seminar.svg create mode 100644 public/assets/svg/icon_study.svg create mode 100644 src/components/page/list/Card/DesktopSizeCard/CategoryChip.tsx rename src/components/page/list/Card/{DesktopSizeCard.tsx => DesktopSizeCard/index.tsx} (92%) diff --git a/public/assets/svg/icon_bolt.svg b/public/assets/svg/icon_bolt.svg new file mode 100644 index 00000000..7a03b4d3 --- /dev/null +++ b/public/assets/svg/icon_bolt.svg @@ -0,0 +1,3 @@ +<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M17.776 35C17.1926 35 16.7426 34.4833 16.826 33.9L18.326 23.3333H12.4926C11.026 23.3333 11.9426 22.0833 11.976 22.0333C14.076 18.3167 17.226 12.8167 21.3926 5.48333C21.5593 5.18333 21.8926 5 22.226 5C22.8093 5 23.2593 5.51667 23.176 6.1L21.676 16.6667H27.526C28.1926 16.6667 28.5593 16.9833 28.1926 17.7667C22.7093 27.3333 19.526 32.9167 18.6093 34.5167C18.4426 34.8167 18.126 35 17.776 35Z" fill="currentColor"/> +</svg> diff --git a/public/assets/svg/icon_event.svg b/public/assets/svg/icon_event.svg new file mode 100644 index 00000000..d1d0b09d --- /dev/null +++ b/public/assets/svg/icon_event.svg @@ -0,0 +1,3 @@ +<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M27.5002 19.9999C29.8002 19.9999 31.6502 18.1333 31.6502 15.8333C31.6502 13.5333 29.8002 11.6666 27.5002 11.6666C25.2002 11.6666 23.3335 13.5333 23.3335 15.8333C23.3335 18.1333 25.2002 19.9999 27.5002 19.9999ZM15.0002 18.3333C17.7668 18.3333 19.9835 16.0999 19.9835 13.3333C19.9835 10.5666 17.7668 8.33325 15.0002 8.33325C12.2335 8.33325 10.0002 10.5666 10.0002 13.3333C10.0002 16.0999 12.2335 18.3333 15.0002 18.3333ZM27.5002 23.3333C24.4502 23.3333 18.3335 24.8666 18.3335 27.9166V29.9999C18.3335 30.9166 19.0835 31.6666 20.0002 31.6666H35.0002C35.9168 31.6666 36.6668 30.9166 36.6668 29.9999V27.9166C36.6668 24.8666 30.5502 23.3333 27.5002 23.3333ZM15.0002 21.6666C11.1168 21.6666 3.3335 23.6166 3.3335 27.4999V29.9999C3.3335 30.9166 4.0835 31.6666 5.00016 31.6666H15.0002V27.9166C15.0002 26.4999 15.5502 24.0166 18.9502 22.1333C17.5002 21.8333 16.1002 21.6666 15.0002 21.6666Z" fill="currentColor"/> +</svg> diff --git a/public/assets/svg/icon_seminar.svg b/public/assets/svg/icon_seminar.svg new file mode 100644 index 00000000..9c9ac90d --- /dev/null +++ b/public/assets/svg/icon_seminar.svg @@ -0,0 +1,6 @@ +<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M16.4336 22.5282C19.5909 22.5282 22.1504 19.9687 22.1504 16.8114C22.1504 13.6541 19.5909 11.0946 16.4336 11.0946C13.2763 11.0946 10.7168 13.6541 10.7168 16.8114C10.7168 19.9687 13.2763 22.5282 16.4336 22.5282Z" fill="currentColor"/> +<path d="M25.5662 26.187C23.1651 24.9578 20.0495 23.9574 16.4336 23.9574C12.8177 23.9574 9.70207 24.9578 7.30101 26.187C5.87181 26.9158 5 28.3879 5 29.9886V31.1034C5 32.6755 6.28628 33.9618 7.8584 33.9618H25.0088C26.5809 33.9618 27.8672 32.6755 27.8672 31.1034V29.9886C27.8672 28.3879 26.9954 26.9158 25.5662 26.187Z" fill="currentColor"/> +<path d="M32.312 6.5926C31.7832 5.90658 30.7685 5.84942 30.1682 6.46397C29.668 6.96419 29.6108 7.76455 30.0396 8.32193C31.7118 10.4657 33.8127 15.7967 30.0396 20.6274C29.6108 21.1847 29.668 21.9851 30.1682 22.4853C30.7828 23.0999 31.7832 23.0284 32.312 22.3567C34.4558 19.5983 37.0999 12.7525 32.312 6.5926Z" fill="currentColor"/> +<path d="M28.3103 10.8659C27.8386 10.0513 26.7239 9.90835 26.0521 10.5658C25.5805 11.0374 25.5376 11.7663 25.8663 12.3523C26.2236 12.9811 26.9239 14.7676 25.852 16.6256C25.5233 17.1973 25.5805 17.9119 26.0521 18.3692C26.7239 19.0409 27.8386 18.8694 28.3103 18.0548C29.9681 15.2107 29.1392 12.3094 28.3103 10.8659Z" fill="currentColor"/> +</svg> diff --git a/public/assets/svg/icon_study.svg b/public/assets/svg/icon_study.svg new file mode 100644 index 00000000..397f5d9a --- /dev/null +++ b/public/assets/svg/icon_study.svg @@ -0,0 +1,3 @@ +<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M20.0004 29.1993L26.917 33.3826C28.1837 34.1493 29.7337 33.016 29.4004 31.5826L27.567 23.716L33.6837 18.416C34.8004 17.4493 34.2004 15.616 32.7337 15.4993L24.6837 14.816L21.5337 7.38262C20.967 6.03262 19.0337 6.03262 18.467 7.38262L15.317 14.7993L7.26703 15.4826C5.80037 15.5993 5.20037 17.4326 6.31703 18.3993L12.4337 23.6993L10.6004 31.566C10.267 32.9993 11.817 34.1326 13.0837 33.366L20.0004 29.1993Z" fill="currentColor"/> +</svg> diff --git a/src/components/page/list/Card/DesktopSizeCard/CategoryChip.tsx b/src/components/page/list/Card/DesktopSizeCard/CategoryChip.tsx new file mode 100644 index 00000000..0927210f --- /dev/null +++ b/src/components/page/list/Card/DesktopSizeCard/CategoryChip.tsx @@ -0,0 +1,53 @@ +import { Tag } from '@sopt-makers/ui'; +import BoltIcon from '@assets/svg/icon_bolt.svg?rect'; +import StudyIcon from '@assets/svg/icon_study.svg?rect'; +import EventIcon from '@assets/svg/icon_event.svg?rect'; +import SeminarIcon from '@assets/svg/icon_seminar.svg?rect'; +import { CategoryKoType } from '@constants/option'; +import { styled } from 'stitches.config'; + +type CategoryChipProps = { + category: CategoryKoType; + welcomeMessage?: string[]; +}; + +export const CategoryChip = ({ category, welcomeMessage }: CategoryChipProps) => { + const CategoryIcon = { + 번쩍: BoltIcon, + 스터디: StudyIcon, + 행사: EventIcon, + 세미나: SeminarIcon, + }[category]; + + return ( + <TagWrapper> + <Tag shape="pill" size="sm" type="line"> + <CategoryIcon style={{ marginRight: '2px' }} width="18" height="18" fill="white" /> + {category} + </Tag> + {category === '번쩍' && welcomeMessage?.map(message => <WelcomeTag>{message}</WelcomeTag>)} + </TagWrapper> + ); +}; + +const TagWrapper = styled('div', { + display: 'flex', + alignItems: 'center', + gap: '$6', + + py: '$3', +}); + +const WelcomeTag = styled('span', { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + + padding: '$3 $10', + + borderRadius: '37px', + border: '1px solid $gray700', + + color: '$gray200', + fontStyle: 'T6', +}); diff --git a/src/components/page/list/Card/DesktopSizeCard.tsx b/src/components/page/list/Card/DesktopSizeCard/index.tsx similarity index 92% rename from src/components/page/list/Card/DesktopSizeCard.tsx rename to src/components/page/list/Card/DesktopSizeCard/index.tsx index 39ab1918..c0bdfdb0 100644 --- a/src/components/page/list/Card/DesktopSizeCard.tsx +++ b/src/components/page/list/Card/DesktopSizeCard/index.tsx @@ -1,11 +1,12 @@ import { Flex } from '@components/util/layout/Flex'; -import { RECRUITMENT_STATUS } from '@constants/option'; +import { CategoryKoType, RECRUITMENT_STATUS } from '@constants/option'; import dayjs from 'dayjs'; import { parsePartValueToLabel } from '@api/API_LEGACY/meeting'; import { styled } from 'stitches.config'; import ProfileDefaultIcon from '@assets/svg/profile_default.svg?rect'; import { getResizedImage } from '@utils/image'; import { paths } from '@/__generated__/schema2'; +import { CategoryChip } from '@components/page/list/Card/DesktopSizeCard/CategoryChip'; interface CardProps { meetingData: Omit< @@ -28,7 +29,10 @@ function DesktopSizeCard({ meetingData, isAllParts }: CardProps) { </ImageWrapper> <STitleSection> - <SCategory>{meetingData.category}</SCategory> + <CategoryChip + category={meetingData.category as CategoryKoType} + welcomeMessage={['YB 환영', 'OB 환영', '입문자 환영']} + /> <STitle>{meetingData.title}</STitle> </STitleSection> @@ -115,16 +119,6 @@ const STitleSection = styled('div', { }, }); -const SCategory = styled('p', { - display: 'inline-block', - fontStyle: 'T6', - color: '$gray200', - border: '1px solid $gray700', - borderRadius: '37px', - px: '$10', - py: '$3', - mr: '$5', -}); const SProfileWrapper = styled('div', { flexType: 'verticalCenter', color: '$gray10', diff --git a/src/constants/option.ts b/src/constants/option.ts index 7fd93bcc..6d2b19bb 100644 --- a/src/constants/option.ts +++ b/src/constants/option.ts @@ -71,6 +71,7 @@ interface StringKeyObject { } export type CategoryType = 'STUDY' | 'EVENT' | 'SEMINAR' | '번쩍'; +export type CategoryKoType = '스터디' | '행사' | '세미나' | '번쩍'; export const CATEGORY_NAME = (category: CategoryType) => { switch (category) { case '번쩍': From e5c9d1f58ca6ebd853ceda8df34e25969ed0f6cf Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Thu, 23 Jan 2025 22:59:47 +0900 Subject: [PATCH 05/16] =?UTF-8?q?feat:=20Tag=20=EC=BB=A4=EC=8A=A4=ED=85=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../page/list/Card/DesktopSizeCard/CategoryChip.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/page/list/Card/DesktopSizeCard/CategoryChip.tsx b/src/components/page/list/Card/DesktopSizeCard/CategoryChip.tsx index 0927210f..42142e75 100644 --- a/src/components/page/list/Card/DesktopSizeCard/CategoryChip.tsx +++ b/src/components/page/list/Card/DesktopSizeCard/CategoryChip.tsx @@ -21,8 +21,13 @@ export const CategoryChip = ({ category, welcomeMessage }: CategoryChipProps) => return ( <TagWrapper> - <Tag shape="pill" size="sm" type="line"> - <CategoryIcon style={{ marginRight: '2px' }} width="18" height="18" fill="white" /> + <Tag + shape="pill" + size="sm" + type="line" + style={{ display: 'flex', gap: '2px', justifyContent: 'center', padding: '3px 8px' }} + > + <CategoryIcon width="18" height="18" fill="white" /> {category} </Tag> {category === '번쩍' && welcomeMessage?.map(message => <WelcomeTag>{message}</WelcomeTag>)} From 573089cd6204ee65b946b526c939755a16a27153 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Thu, 23 Jan 2025 23:22:55 +0900 Subject: [PATCH 06/16] =?UTF-8?q?feat:=20RecruitmentStatusTag=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Tag/RecruitmentStatusTag.tsx | 18 ++++++++++ .../page/detail/MeetingController/index.tsx | 35 +++++-------------- .../page/list/Card/DesktopSizeCard/index.tsx | 28 ++------------- 3 files changed, 29 insertions(+), 52 deletions(-) create mode 100644 src/components/Tag/RecruitmentStatusTag.tsx diff --git a/src/components/Tag/RecruitmentStatusTag.tsx b/src/components/Tag/RecruitmentStatusTag.tsx new file mode 100644 index 00000000..ae7af08b --- /dev/null +++ b/src/components/Tag/RecruitmentStatusTag.tsx @@ -0,0 +1,18 @@ +import { Tag } from '@sopt-makers/ui'; +import { RECRUITMENT_STATUS } from '@constants/option'; +import { HTMLAttributes } from 'react'; + +interface RecruitmentStatusTagProps extends HTMLAttributes<HTMLDivElement> { + status: 0 | 1 | 2; +} +const RecruitmentStatusTag = ({ status, style }: RecruitmentStatusTagProps) => { + const tagVariant: ('default' | 'primary' | 'secondary')[] = ['secondary', 'primary', 'default']; + + return ( + <Tag style={style} size="md" shape="rect" variant={tagVariant[status]} type="solid"> + {RECRUITMENT_STATUS[status]} + </Tag> + ); +}; + +export default RecruitmentStatusTag; diff --git a/src/components/page/detail/MeetingController/index.tsx b/src/components/page/detail/MeetingController/index.tsx index 1e354824..20b8f673 100644 --- a/src/components/page/detail/MeetingController/index.tsx +++ b/src/components/page/detail/MeetingController/index.tsx @@ -23,6 +23,7 @@ import { useDialog } from '@sopt-makers/ui'; import { ReactNode } from 'react'; import ProfileAnchor from './ProfileAnchor'; import { useMutationPostEventApplication } from '@api/API_LEGACY/meeting/hooks'; +import RecruitmentStatusTag from '@components/Tag/RecruitmentStatusTag'; interface DetailHeaderProps { detailData: GetMeetingResponse; @@ -269,12 +270,12 @@ const MeetingController = ({ <> <SPanelWrapper> <SAbout> - <div> - <SRecruitStatus status={status}>{RECRUITMENT_STATUS[status]}</SRecruitStatus> + <SRecruitStatus> + <RecruitmentStatusTag status={status} /> <SPeriod> {dayjs(startDate).format('YY.MM.DD')} - {dayjs(endDate).format('YY.MM.DD')} </SPeriod> - </div> + </SRecruitStatus> <h1> <span>{category}</span> {title} @@ -401,32 +402,12 @@ const SAbout = styled('div', { }); const SRecruitStatus = styled('div', { - width: 'fit-content', - padding: '$7 $8', - mr: '$12', - borderRadius: '6px', - fontAg: '16_bold_100', + display: 'flex', + gap: '$12', + alignItems: 'center', '@tablet': { - padding: '$2 $6', - mr: '$8', - borderRadius: '5px', - fontStyle: 'B4', - }, - - variants: { - status: { - 0: { - backgroundColor: '$gray600', - }, - 1: { - backgroundColor: '$secondary', - color: '$gray950', - }, - 2: { - backgroundColor: '$gray700', - }, - }, + gap: '$8', }, }); diff --git a/src/components/page/list/Card/DesktopSizeCard/index.tsx b/src/components/page/list/Card/DesktopSizeCard/index.tsx index c0bdfdb0..33afac5d 100644 --- a/src/components/page/list/Card/DesktopSizeCard/index.tsx +++ b/src/components/page/list/Card/DesktopSizeCard/index.tsx @@ -1,5 +1,5 @@ import { Flex } from '@components/util/layout/Flex'; -import { CategoryKoType, RECRUITMENT_STATUS } from '@constants/option'; +import { CategoryKoType } from '@constants/option'; import dayjs from 'dayjs'; import { parsePartValueToLabel } from '@api/API_LEGACY/meeting'; import { styled } from 'stitches.config'; @@ -7,6 +7,7 @@ import ProfileDefaultIcon from '@assets/svg/profile_default.svg?rect'; import { getResizedImage } from '@utils/image'; import { paths } from '@/__generated__/schema2'; import { CategoryChip } from '@components/page/list/Card/DesktopSizeCard/CategoryChip'; +import RecruitmentStatusTag from '@components/Tag/RecruitmentStatusTag'; interface CardProps { meetingData: Omit< @@ -20,7 +21,7 @@ function DesktopSizeCard({ meetingData, isAllParts }: CardProps) { return ( <div> <ImageWrapper> - <SStatus recruitingStatus={meetingData.status}>{RECRUITMENT_STATUS[meetingData.status]}</SStatus> + <RecruitmentStatusTag status={meetingData.status} style={{ position: 'absolute', top: '16px', left: '16px' }} /> <SThumbnailImage css={{ backgroundImage: `url(${meetingData.imageURL[0]?.url})`, @@ -89,29 +90,6 @@ const SThumbnailImage = styled('div', { backgroundRepeat: 'no-repeat', }); -const SStatus = styled('div', { - position: 'absolute', - top: '16px', - left: '16px', - borderRadius: '$8', - padding: '$3 $8', - fontStyle: 'T5', - variants: { - recruitingStatus: { - 0: { - backgroundColor: '$gray600', - }, - 1: { - backgroundColor: '$secondary', - color: '$gray950', - }, - 2: { - backgroundColor: '$gray700', - }, - }, - }, -}); - const STitleSection = styled('div', { my: '$16', '@tablet': { From eca7a386b5c0eec71de0eb41088ad0db47cbe452 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Fri, 24 Jan 2025 02:52:03 +0900 Subject: [PATCH 07/16] refactor: bungae -> lightening --- pages/make/{bungae => lightening}/index.tsx | 22 ++++++------ src/api/lightning/hook.ts | 0 src/api/lightning/index.ts | 35 ++++++++++++++++++++ src/api/meeting/index.ts | 35 +------------------- src/components/form/Bungae/index.tsx | 12 +++---- src/components/modal/FloatingButtonModal.tsx | 2 +- src/data/options.ts | 6 ++-- src/types/form.ts | 4 +-- 8 files changed, 59 insertions(+), 57 deletions(-) rename pages/make/{bungae => lightening}/index.tsx (83%) create mode 100644 src/api/lightning/hook.ts create mode 100644 src/api/lightning/index.ts diff --git a/pages/make/bungae/index.tsx b/pages/make/lightening/index.tsx similarity index 83% rename from pages/make/bungae/index.tsx rename to pages/make/lightening/index.tsx index 0c2c3c3d..5b7280c8 100644 --- a/pages/make/bungae/index.tsx +++ b/pages/make/lightening/index.tsx @@ -1,5 +1,5 @@ import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'; -import { BungaeFormType, bungaeSchema } from '@type/form'; +import { LighteningFormType, lighteningSchema } from '@type/form'; import { styled } from 'stitches.config'; import { zodResolver } from '@hookform/resolvers/zod'; import { useRouter } from 'next/router'; @@ -10,21 +10,21 @@ import { ampli } from '@/ampli'; import { fontsObject } from '@sopt-makers/fonts'; import { colors } from '@sopt-makers/colors'; import Presentation from '@components/form/Bungae'; -import { createBungae } from '@api/meeting'; +import { createLightening } from '@api/lightning'; const DevTool = dynamic(() => import('@hookform/devtools').then(module => module.DevTool), { ssr: false, }); -const Bungae = () => { +const Lightening = () => { const router = useRouter(); - const formMethods = useForm<BungaeFormType>({ + const formMethods = useForm<LighteningFormType>({ mode: 'onChange', - resolver: zodResolver(bungaeSchema), + resolver: zodResolver(lighteningSchema), }); const { isValid, errors } = formMethods.formState; - const { mutateAsync: mutateCreateBungae, isLoading: isSubmitting } = useMutation({ - mutationFn: (formData: BungaeFormType) => createBungae(formData), + const { mutateAsync: mutateCreateLightening, isLoading: isSubmitting } = useMutation({ + mutationFn: (formData: LighteningFormType) => createLightening(formData), onError: () => { alert('번쩍을 개설하지 못했습니다.'); }, @@ -42,11 +42,11 @@ const Bungae = () => { formMethods.setValue('files', files); }; - const onSubmit: SubmitHandler<BungaeFormType> = async formData => { - const bungaeId = await mutateCreateBungae(formData); + const onSubmit: SubmitHandler<LighteningFormType> = async formData => { + const lighteningId = await mutateCreateLightening(formData); ampli.completedMakeGroup(); alert('번쩍을 개설했습니다.'); - router.push(`/detail?id=${bungaeId}`); + router.push(`/detail?id=${lighteningId}`); }; return ( @@ -78,7 +78,7 @@ const Bungae = () => { ); }; -export default Bungae; +export default Lightening; const SContainer = styled('div', { margin: '80px 0', diff --git a/src/api/lightning/hook.ts b/src/api/lightning/hook.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/api/lightning/index.ts b/src/api/lightning/index.ts new file mode 100644 index 00000000..38fa2bfb --- /dev/null +++ b/src/api/lightning/index.ts @@ -0,0 +1,35 @@ +import { api } from '@api/index'; +import { LighteningFormType } from '@type/form'; + +export const createLightening = async (formData: LighteningFormType) => { + const { + data: { lightningId }, + } = await api.post<{ lightningId: number }>('/lightning/v2', filterLighteningFormData(formData)); + return lightningId; +}; + +const filterLighteningFormData = (formData: LighteningFormType) => { + const convertedTags = formData.welcomeTags?.map(tag => { + return tag?.value; + }); + const convertedEndDate = + formData.timeInfo.time.value === '당일' ? formData.timeInfo.startDate : formData.timeInfo.endDate; + const convertedLightningPlace = + formData.placeInfo.place.value === '협의 후 결정' ? null : formData.placeInfo.placeDetail; + const data = { + lightningBody: { + title: formData.title, + desc: formData.desc, + lightningTimingType: formData.timeInfo.time.value, + activityStartDate: formData.timeInfo.startDate, + activityEndDate: convertedEndDate, + lightningPlaceType: formData.placeInfo.place.value, + lightningPlace: convertedLightningPlace, + minimumCapacity: formData.minCapacity, + maximumCapacity: formData.maxCapacity, + files: formData.files, + }, + welcomeMessageTypes: convertedTags?.length === 0 ? null : convertedTags, + }; + return data; +}; diff --git a/src/api/meeting/index.ts b/src/api/meeting/index.ts index c622d269..ae7a0015 100644 --- a/src/api/meeting/index.ts +++ b/src/api/meeting/index.ts @@ -1,7 +1,7 @@ import { ApplicationStatusType, UserResponse } from '@api/user'; import { api } from '..'; import { APPROVAL_STATUS_KOREAN_TO_ENGLISH } from '@constants/option'; -import { BungaeFormType } from '@type/form'; +import { LighteningFormType } from '@type/form'; interface PaginationType { page: number; take: number; @@ -48,36 +48,3 @@ export const getMeetingPeopleList = async ({ id, ...rest }: OptionData): Promise }) ).data; }; - -export const createBungae = async (formData: BungaeFormType) => { - const { - data: { lightningId }, - } = await api.post<{ lightningId: number }>('/lightning/v2', filterBungaeFormData(formData)); - return lightningId; -}; - -const filterBungaeFormData = (formData: BungaeFormType) => { - const convertedTags = formData.welcomeTags?.map(tag => { - return tag?.value; - }); - const convertedEndDate = - formData.timeInfo.time.value === '당일' ? formData.timeInfo.startDate : formData.timeInfo.endDate; - const convertedLightningPlace = - formData.placeInfo.place.value === '협의 후 결정' ? null : formData.placeInfo.placeDetail; - const data = { - lightningBody: { - title: formData.title, - desc: formData.desc, - lightningTimingType: formData.timeInfo.time.value, - activityStartDate: formData.timeInfo.startDate, - activityEndDate: convertedEndDate, - lightningPlaceType: formData.placeInfo.place.value, - lightningPlace: convertedLightningPlace, - minimumCapacity: formData.minCapacity, - maximumCapacity: formData.maxCapacity, - files: formData.files, - }, - welcomeMessageTypes: convertedTags?.length === 0 ? null : convertedTags, - }; - return data; -}; diff --git a/src/components/form/Bungae/index.tsx b/src/components/form/Bungae/index.tsx index 6839278d..28cfacdd 100644 --- a/src/components/form/Bungae/index.tsx +++ b/src/components/form/Bungae/index.tsx @@ -8,14 +8,14 @@ import HelpMessage from '../HelpMessage'; import Label from '../Label'; import Textarea from '../Textarea'; import TextInput from '../TextInput'; -import { BungaeFormType, MAX_FILE_SIZE } from '@type/form'; +import { LighteningFormType, MAX_FILE_SIZE } from '@type/form'; import { useRouter } from 'next/router'; import { getPresignedUrl, uploadImage } from '@api/API_LEGACY/meeting'; import { imageS3Bucket } from '@constants/url'; import CalendarInputForm from '../Calendar'; import { Chip, useDialog } from '@sopt-makers/ui'; import ImagePreview from '../Presentation/ImagePreview'; -import { bungaePlace, bungaeTags, bungaeTime } from '@data/options'; +import { lighteningPlace, lighteningTags, lighteningTime } from '@data/options'; import ErrorMessage from '../ErrorMessage'; interface PresentationProps { @@ -25,7 +25,7 @@ interface PresentationProps { handleDeleteImage: (index: number) => void; onSubmit: React.FormEventHandler<HTMLFormElement>; disabled?: boolean; - errors: FieldErrors<BungaeFormType>; + errors: FieldErrors<LighteningFormType>; } interface FileChangeHandler { imageUrls: string[]; @@ -176,7 +176,7 @@ function Presentation({ name="timeInfo.time" render={({ field: { value, onChange } }) => ( <> - {bungaeTime.map(time => ( + {lighteningTime.map(time => ( <Chip active={value.value === time.value} onClick={() => { @@ -249,7 +249,7 @@ function Presentation({ name="placeInfo.place" render={({ field: { value, onChange } }) => ( <> - {bungaePlace.map(place => ( + {lighteningPlace.map(place => ( <Chip active={value.value === place.value} onClick={() => { @@ -362,7 +362,7 @@ function Presentation({ const selectedTags = Array.isArray(value) ? value : []; return ( <> - {bungaeTags.map(tag => { + {lighteningTags.map(tag => { const isActive = selectedTags.some( (selectedTag: { label: string; value: string }) => selectedTag.value === tag.value ); diff --git a/src/components/modal/FloatingButtonModal.tsx b/src/components/modal/FloatingButtonModal.tsx index 75b6f07f..4b8fce6d 100644 --- a/src/components/modal/FloatingButtonModal.tsx +++ b/src/components/modal/FloatingButtonModal.tsx @@ -39,7 +39,7 @@ const FloatingButtonModal = (props: { isActive: boolean; handleOptionClose: () = const handleBoltCreateButtonClick = () => { //todo: 번쩍 개설을 위한 정보를 넘겨주면서 라우팅하기 ampli.clickMakeGroup({ location: router.pathname }); - router.push('/make/bungae'); + router.push('/make/lightening'); }; const handleFeedCreateButtonClick = () => { diff --git a/src/data/options.ts b/src/data/options.ts index f20283d5..d1c6c09f 100644 --- a/src/data/options.ts +++ b/src/data/options.ts @@ -33,18 +33,18 @@ export const parts = [ { label: '서버', value: 'SERVER', order: 7 }, ]; -export const bungaeTime = [ +export const lighteningTime = [ { label: '당일', value: '당일' }, { label: '예정 기간 (협의 후 결정)', value: '예정 기간 (협의 후 결정)' }, ]; -export const bungaePlace = [ +export const lighteningPlace = [ { label: '오프라인', value: '오프라인' }, { label: '온라인', value: '온라인' }, { label: '협의 후 결정', value: '협의 후 결정' }, ]; -export const bungaeTags = [ +export const lighteningTags = [ { label: 'YB 환영', value: 'YB 환영' }, { label: 'OB 환영', value: 'OB 환영' }, { label: '초면 환영', value: '초면 환영' }, diff --git a/src/types/form.ts b/src/types/form.ts index b52a68cd..21e9565f 100644 --- a/src/types/form.ts +++ b/src/types/form.ts @@ -87,7 +87,7 @@ export const MAX_FILE_SIZE = 20 * 1024 ** 2; // 5MB export const ACCEPTED_IMAGE_TYPES = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/gif']; -export const bungaeSchema = z.object({ +export const lighteningSchema = z.object({ title: z .string() .max(30, { message: '30자 까지 입력할 수 있습니다.' }) @@ -168,4 +168,4 @@ export const bungaeSchema = z.object({ .nullable(), }); -export type BungaeFormType = z.infer<typeof bungaeSchema>; +export type LighteningFormType = z.infer<typeof lighteningSchema>; From 1ce2838cc52063f2ffb70ed2c6a6180ff62995e5 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Fri, 24 Jan 2025 02:57:10 +0900 Subject: [PATCH 08/16] chore: lightening -> lightning --- .../make/{lightening => lightning}/index.tsx | 22 +++++++++---------- src/api/lightning/index.ts | 8 +++---- src/api/meeting/index.ts | 2 +- src/components/form/Bungae/index.tsx | 12 +++++----- src/components/modal/FloatingButtonModal.tsx | 2 +- .../page/detail/Information/constant.tsx | 2 +- src/data/options.ts | 6 ++--- src/types/form.ts | 4 ++-- 8 files changed, 29 insertions(+), 29 deletions(-) rename pages/make/{lightening => lightning}/index.tsx (83%) diff --git a/pages/make/lightening/index.tsx b/pages/make/lightning/index.tsx similarity index 83% rename from pages/make/lightening/index.tsx rename to pages/make/lightning/index.tsx index 5b7280c8..d2df464a 100644 --- a/pages/make/lightening/index.tsx +++ b/pages/make/lightning/index.tsx @@ -1,5 +1,5 @@ import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'; -import { LighteningFormType, lighteningSchema } from '@type/form'; +import { LightningFormType, lightningSchema } from '@type/form'; import { styled } from 'stitches.config'; import { zodResolver } from '@hookform/resolvers/zod'; import { useRouter } from 'next/router'; @@ -10,21 +10,21 @@ import { ampli } from '@/ampli'; import { fontsObject } from '@sopt-makers/fonts'; import { colors } from '@sopt-makers/colors'; import Presentation from '@components/form/Bungae'; -import { createLightening } from '@api/lightning'; +import { createLightning } from '@api/lightning'; const DevTool = dynamic(() => import('@hookform/devtools').then(module => module.DevTool), { ssr: false, }); -const Lightening = () => { +const Lightning = () => { const router = useRouter(); - const formMethods = useForm<LighteningFormType>({ + const formMethods = useForm<LightningFormType>({ mode: 'onChange', - resolver: zodResolver(lighteningSchema), + resolver: zodResolver(lightningSchema), }); const { isValid, errors } = formMethods.formState; - const { mutateAsync: mutateCreateLightening, isLoading: isSubmitting } = useMutation({ - mutationFn: (formData: LighteningFormType) => createLightening(formData), + const { mutateAsync: mutateCreateLightning, isLoading: isSubmitting } = useMutation({ + mutationFn: (formData: LightningFormType) => createLightning(formData), onError: () => { alert('번쩍을 개설하지 못했습니다.'); }, @@ -42,11 +42,11 @@ const Lightening = () => { formMethods.setValue('files', files); }; - const onSubmit: SubmitHandler<LighteningFormType> = async formData => { - const lighteningId = await mutateCreateLightening(formData); + const onSubmit: SubmitHandler<LightningFormType> = async formData => { + const bungaeId = await mutateCreateLightning(formData); ampli.completedMakeGroup(); alert('번쩍을 개설했습니다.'); - router.push(`/detail?id=${lighteningId}`); + router.push(`/detail?id=${bungaeId}`); }; return ( @@ -78,7 +78,7 @@ const Lightening = () => { ); }; -export default Lightening; +export default Lightning; const SContainer = styled('div', { margin: '80px 0', diff --git a/src/api/lightning/index.ts b/src/api/lightning/index.ts index 38fa2bfb..c9c43fac 100644 --- a/src/api/lightning/index.ts +++ b/src/api/lightning/index.ts @@ -1,14 +1,14 @@ import { api } from '@api/index'; -import { LighteningFormType } from '@type/form'; +import { LightningFormType } from '@type/form'; -export const createLightening = async (formData: LighteningFormType) => { +export const createLightning = async (formData: LightningFormType) => { const { data: { lightningId }, - } = await api.post<{ lightningId: number }>('/lightning/v2', filterLighteningFormData(formData)); + } = await api.post<{ lightningId: number }>('/lightning/v2', filterLightningFormData(formData)); return lightningId; }; -const filterLighteningFormData = (formData: LighteningFormType) => { +const filterLightningFormData = (formData: LightningFormType) => { const convertedTags = formData.welcomeTags?.map(tag => { return tag?.value; }); diff --git a/src/api/meeting/index.ts b/src/api/meeting/index.ts index ae7a0015..a41bd056 100644 --- a/src/api/meeting/index.ts +++ b/src/api/meeting/index.ts @@ -1,7 +1,7 @@ import { ApplicationStatusType, UserResponse } from '@api/user'; import { api } from '..'; import { APPROVAL_STATUS_KOREAN_TO_ENGLISH } from '@constants/option'; -import { LighteningFormType } from '@type/form'; +import { LightningFormType } from '@type/form'; interface PaginationType { page: number; take: number; diff --git a/src/components/form/Bungae/index.tsx b/src/components/form/Bungae/index.tsx index 28cfacdd..4247bd2e 100644 --- a/src/components/form/Bungae/index.tsx +++ b/src/components/form/Bungae/index.tsx @@ -8,14 +8,14 @@ import HelpMessage from '../HelpMessage'; import Label from '../Label'; import Textarea from '../Textarea'; import TextInput from '../TextInput'; -import { LighteningFormType, MAX_FILE_SIZE } from '@type/form'; +import { LightningFormType, MAX_FILE_SIZE } from '@type/form'; import { useRouter } from 'next/router'; import { getPresignedUrl, uploadImage } from '@api/API_LEGACY/meeting'; import { imageS3Bucket } from '@constants/url'; import CalendarInputForm from '../Calendar'; import { Chip, useDialog } from '@sopt-makers/ui'; import ImagePreview from '../Presentation/ImagePreview'; -import { lighteningPlace, lighteningTags, lighteningTime } from '@data/options'; +import { lightningPlace, lightningTags, lightningTime } from '@data/options'; import ErrorMessage from '../ErrorMessage'; interface PresentationProps { @@ -25,7 +25,7 @@ interface PresentationProps { handleDeleteImage: (index: number) => void; onSubmit: React.FormEventHandler<HTMLFormElement>; disabled?: boolean; - errors: FieldErrors<LighteningFormType>; + errors: FieldErrors<LightningFormType>; } interface FileChangeHandler { imageUrls: string[]; @@ -176,7 +176,7 @@ function Presentation({ name="timeInfo.time" render={({ field: { value, onChange } }) => ( <> - {lighteningTime.map(time => ( + {lightningTime.map(time => ( <Chip active={value.value === time.value} onClick={() => { @@ -249,7 +249,7 @@ function Presentation({ name="placeInfo.place" render={({ field: { value, onChange } }) => ( <> - {lighteningPlace.map(place => ( + {lightningPlace.map(place => ( <Chip active={value.value === place.value} onClick={() => { @@ -362,7 +362,7 @@ function Presentation({ const selectedTags = Array.isArray(value) ? value : []; return ( <> - {lighteningTags.map(tag => { + {lightningTags.map(tag => { const isActive = selectedTags.some( (selectedTag: { label: string; value: string }) => selectedTag.value === tag.value ); diff --git a/src/components/modal/FloatingButtonModal.tsx b/src/components/modal/FloatingButtonModal.tsx index 4b8fce6d..59cd402b 100644 --- a/src/components/modal/FloatingButtonModal.tsx +++ b/src/components/modal/FloatingButtonModal.tsx @@ -39,7 +39,7 @@ const FloatingButtonModal = (props: { isActive: boolean; handleOptionClose: () = const handleBoltCreateButtonClick = () => { //todo: 번쩍 개설을 위한 정보를 넘겨주면서 라우팅하기 ampli.clickMakeGroup({ location: router.pathname }); - router.push('/make/lightening'); + router.push('/make/lightning'); }; const handleFeedCreateButtonClick = () => { diff --git a/src/components/page/detail/Information/constant.tsx b/src/components/page/detail/Information/constant.tsx index 2b6a5f19..391ef2f2 100644 --- a/src/components/page/detail/Information/constant.tsx +++ b/src/components/page/detail/Information/constant.tsx @@ -63,7 +63,7 @@ export const meetingDetailList = (detailData: GetMeetingResponse) => [ }, ]; -export const LighteningDetailList = (detailData: GetMeetingResponse) => [ +export const LightningDetailList = (detailData: GetMeetingResponse) => [ { key: '#환영 태그', Title: () => <STitle>#환영 태그</STitle>, diff --git a/src/data/options.ts b/src/data/options.ts index d1c6c09f..c523d51c 100644 --- a/src/data/options.ts +++ b/src/data/options.ts @@ -33,18 +33,18 @@ export const parts = [ { label: '서버', value: 'SERVER', order: 7 }, ]; -export const lighteningTime = [ +export const lightningTime = [ { label: '당일', value: '당일' }, { label: '예정 기간 (협의 후 결정)', value: '예정 기간 (협의 후 결정)' }, ]; -export const lighteningPlace = [ +export const lightningPlace = [ { label: '오프라인', value: '오프라인' }, { label: '온라인', value: '온라인' }, { label: '협의 후 결정', value: '협의 후 결정' }, ]; -export const lighteningTags = [ +export const lightningTags = [ { label: 'YB 환영', value: 'YB 환영' }, { label: 'OB 환영', value: 'OB 환영' }, { label: '초면 환영', value: '초면 환영' }, diff --git a/src/types/form.ts b/src/types/form.ts index 21e9565f..e5a010e6 100644 --- a/src/types/form.ts +++ b/src/types/form.ts @@ -87,7 +87,7 @@ export const MAX_FILE_SIZE = 20 * 1024 ** 2; // 5MB export const ACCEPTED_IMAGE_TYPES = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/gif']; -export const lighteningSchema = z.object({ +export const lightningSchema = z.object({ title: z .string() .max(30, { message: '30자 까지 입력할 수 있습니다.' }) @@ -168,4 +168,4 @@ export const lighteningSchema = z.object({ .nullable(), }); -export type LighteningFormType = z.infer<typeof lighteningSchema>; +export type LightningFormType = z.infer<typeof lightningSchema>; From ede9ebda18fa52c948d224fa3e07222ea2e8f078 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Fri, 24 Jan 2025 07:13:12 +0900 Subject: [PATCH 09/16] =?UTF-8?q?feat:=20=EB=B2=88=EC=A9=8D=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20API=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 4 +- pages/detail/index.tsx | 9 +- public/assets/svg/location.svg | 3 - src/__generated__/schema2.d.ts | 422 ++++++++++++++++++ src/api/lightning/hook.ts | 22 + src/api/lightning/index.ts | 8 + src/api/meeting/index.ts | 1 - .../detail/Information/InformationPanel.tsx | 10 +- .../page/detail/Information/constant.tsx | 104 ++++- .../MeetingController/LightningAbout.tsx | 117 +++++ .../detail/MeetingController/MeetingAbout.tsx | 123 +++++ .../page/detail/MeetingController/constant.ts | 9 + .../page/detail/MeetingController/index.tsx | 141 +----- 13 files changed, 818 insertions(+), 155 deletions(-) delete mode 100644 public/assets/svg/location.svg create mode 100644 src/components/page/detail/MeetingController/LightningAbout.tsx create mode 100644 src/components/page/detail/MeetingController/MeetingAbout.tsx create mode 100644 src/components/page/detail/MeetingController/constant.ts diff --git a/package.json b/package.json index 31afa57c..73b1d9b4 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", "generate-types-v1": "openapi-typescript https://crew.api.dev.sopt.org/api-docs-json -o ./src/__generated__/schema1.d.ts", - "generate-types-v2": "openapi-typescript https://crew.api.develop.sopt.org/api-docs/json -o ./src/__generated__/schema2.d.ts", + "generate-types-v2": "openapi-typescript https://crew.api.dev.sopt.org/api-docs/json -o ./src/__generated__/schema2.d.ts", "generate-types": "cat ./src/__generated__/schema1.d.ts ./src/__generated__/schema2.d.ts > ./src/__generated__/schema.d.ts", "update-mds": "yarn up @sopt-makers/colors@latest @sopt-makers/fonts@latest @sopt-makers/playground-common@latest @sopt-makers/ui@latest @sopt-makers/icons@latest" }, @@ -93,4 +93,4 @@ "workspaces": [ "packages/*" ] -} +} \ No newline at end of file diff --git a/pages/detail/index.tsx b/pages/detail/index.tsx index 3f5092c0..30347150 100644 --- a/pages/detail/index.tsx +++ b/pages/detail/index.tsx @@ -15,6 +15,9 @@ import { Fragment, useEffect, useState } from 'react'; import dayjs from 'dayjs'; import 'dayjs/locale/ko'; import MeetingController from '@components/page/detail/MeetingController'; +import { useLightningByIdQuery } from '@api/lightning/hook'; +import { GetMeetingResponse } from '@api/API_LEGACY/meeting'; +import { GetLightningByIdResponse } from '@api/lightning'; dayjs.locale('ko'); @@ -26,7 +29,8 @@ const enum SelectedTab { const DetailPage = () => { const router = useRouter(); const id = router.query.id as string; - const { data: detailData } = useQueryGetMeeting({ params: { id } }); + const { data: meetingData } = useQueryGetMeeting({ params: { id } }); + const { data: lightningData } = useLightningByIdQuery({ meetingId: +id }); const { mutate: mutateDeleteMeeting } = useMutationDeleteMeeting({}); const { mutate: mutatePostApplication } = useMutationPostApplication({}); const { mutate: mutateDeleteApplication } = useMutationDeleteApplication({}); @@ -44,7 +48,7 @@ const DetailPage = () => { document.body.removeChild(script); }, []); - if (!detailData) { + if (!meetingData || !lightningData) { return ( <> <Loader /> @@ -61,6 +65,7 @@ const DetailPage = () => { </> ); } + const detailData: GetMeetingResponse | GetLightningByIdResponse = lightningData || meetingData; return ( <> diff --git a/public/assets/svg/location.svg b/public/assets/svg/location.svg deleted file mode 100644 index 8063bb61..00000000 --- a/public/assets/svg/location.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M6.00017 1.57501C4.13261 1.57501 2.61865 3.08897 2.61865 4.95653C2.61865 5.42621 2.77405 5.95562 3.0415 6.51213C3.30748 7.06556 3.67338 7.62343 4.06813 8.14376C4.79573 9.10285 5.60093 9.90796 6.00017 10.2883C6.39942 9.90796 7.20462 9.10285 7.93222 8.14376C8.32696 7.62343 8.69287 7.06556 8.95885 6.51213C9.2263 5.95562 9.3817 5.42621 9.3817 4.95653C9.3817 3.08897 7.86774 1.57501 6.00017 1.57501ZM1.86865 4.95653C1.86865 2.67476 3.7184 0.825012 6.00017 0.825012C8.28195 0.825012 10.1317 2.67476 10.1317 4.95653C10.1317 5.58151 9.92824 6.2265 9.63483 6.837C9.33995 7.45058 8.943 8.05232 8.52974 8.59706C7.7034 9.68629 6.78789 10.5787 6.41994 10.9235C6.18323 11.1452 5.81712 11.1452 5.58041 10.9235C5.21246 10.5787 4.29695 9.68629 3.47061 8.59706C3.05735 8.05232 2.6604 7.45058 2.36552 6.837C2.07211 6.2265 1.86865 5.58151 1.86865 4.95653ZM6.00033 3.97509C5.54469 3.97509 5.17533 4.34445 5.17533 4.80009C5.17533 5.25572 5.54469 5.62509 6.00033 5.62509C6.45596 5.62509 6.82533 5.25572 6.82533 4.80009C6.82533 4.34445 6.45596 3.97509 6.00033 3.97509ZM4.42533 4.80009C4.42533 3.93024 5.13048 3.22509 6.00033 3.22509C6.87018 3.22509 7.57533 3.93024 7.57533 4.80009C7.57533 5.66994 6.87018 6.37509 6.00033 6.37509C5.13048 6.37509 4.42533 5.66994 4.42533 4.80009Z" fill="#9D9DA4"/> -</svg> diff --git a/src/__generated__/schema2.d.ts b/src/__generated__/schema2.d.ts index b0dc9dcc..ddbff094 100644 --- a/src/__generated__/schema2.d.ts +++ b/src/__generated__/schema2.d.ts @@ -80,6 +80,10 @@ export interface paths { /** 일반 모임 지원 */ post: operations["applyGeneralMeeting"]; }; + "/lightning/v2": { + /** 번쩍 모임 생성 */ + post: operations["createLightning"]; + }; "/comment/v2": { /** 모임 게시글 댓글 리스트 조회 */ get: operations["getComments"]; @@ -129,6 +133,9 @@ export interface paths { /** 내가 신청한 모임 조회 */ get: operations["getAppliedMeetingByUser"]; }; + "/sentry": { + get: operations["testSentry"]; + }; "/post/v2/count": { /** 모임 게시글 개수 조회 */ get: operations["getPostCount"]; @@ -147,6 +154,13 @@ export interface paths { */ get: operations["getAppliesCsvFileUrl"]; }; + "/meeting/v2/recommend": { + /** + * 추천 모임 목록 조회 + * @description 추천 모임 목록 조회, 쿼리파라미터가 없는 경우 '지금 모집중인 모임' 반환 + */ + get: operations["getRecommendMeetingsByIds"]; + }; "/meeting/v2/presigned-url": { /** * Meeting 썸네일 업로드용 Pre-Signed URL 발급 @@ -162,6 +176,20 @@ export interface paths { /** 모임 둘러보기 조회 */ get: operations["getMeetingBanner"]; }; + "/lightning/v2/{meetingId}": { + /** 번쩍 모임 상세 조회 */ + get: operations["getLightningByMeetingId"]; + }; + "/internal/meetings": { + /** + * [Internal] 모임 전체 조회/검색/필터링 + * @description 모임 전체 조회/검색/필터링 + */ + get: operations["getMeetings_1"]; + }; + "/internal/meeting/stats/approved-studies/{orgId}": { + get: operations["getApprovedStudyCountByOrgId"]; + }; "/advertisement/v2": { /** * 광고 조회 @@ -480,6 +508,84 @@ export interface components { */ applyId: number; }; + /** @description 번쩍 모임 생성 및 수정 request body dto */ + LightningV2CreateLightningBodyDto: { + lightningBody: components["schemas"]["LightningV2CreateLightningBodyWithoutWelcomeMessageDto"]; + /** + * @description 환영 메시지 타입 리스트 + * @example [ + * "YB 환영", + * "OB 환영" + * ] + */ + welcomeMessageTypes?: string[]; + }; + /** @description 번쩍 모임 생성 및 수정 request body dto (환영 메시지 타입 제외) */ + LightningV2CreateLightningBodyWithoutWelcomeMessageDto: { + /** + * @description 번쩍 모임 제목 + * @example 알고보면 쓸데있는 개발 프로세스 + */ + title: string; + /** + * @description 번쩍 소개 + * @example api 가 터졌다고? 깃이 터졌다고? + */ + desc: string; + /** + * @description 번쩍 일정 결정 방식 + * @example 예정 기간(협의 후 결정) + */ + lightningTimingType: string; + /** + * @description 번쩍 활동 시작 날짜 + * @example 2025.10.29 + */ + activityStartDate: string; + /** + * @description 번쩍 활동 종료 날짜 + * @example 2025.10.30 + */ + activityEndDate: string; + /** + * @description 모임 장소 Tag + * @example 오프라인 + */ + lightningPlaceType: string; + /** + * @description 모임 장소 + * @example 잠실역 5번 출구 + */ + lightningPlace?: string; + /** + * Format: int32 + * @description 최소 모집 인원 + * @example 1 + */ + minimumCapacity: number; + /** + * Format: int32 + * @description 최대 모집 인원 + * @example 5 + */ + maximumCapacity: number; + /** + * @description 모임 이미지 리스트, 최대 1개 + * @example [ + * "https://makers-web-img.s3.ap-northeast-2.amazonaws.com/meeting/2023/04/12/7bd87736-b557-4b26-a0d5-9b09f1f1d7df" + * ] + */ + files: string[]; + }; + /** @description 번쩍 모임 생성 응답 Dto */ + LightningV2CreateLightningResponseDto: { + /** + * Format: int32 + * @description 모임 id - 번쩍 카테고리 + * @example 1 + */ + meetingId: number; + }; /** @description 댓글 생성 request body dto */ CommentV2CreateCommentBodyDto: { /** @@ -1582,6 +1688,11 @@ export interface components { */ url: string; }; + /** @description 추천 모임 목록 조회 응답 Dto */ + MeetingV2GetRecommendDto: { + /** @description 모임 객체 목록 */ + meetings: components["schemas"]["MeetingResponseDto"][]; + }; /** @description presigned 필드 Dto */ PreSignedUrlFieldResponseDto: { /** @@ -1792,6 +1903,188 @@ export interface components { */ profileImage?: string; }; + /** @description 번쩍 상세 조회 dto */ + LightningV2GetLightningByMeetingIdResponseDto: { + /** + * Format: int32 + * @description 모임 id + * @example 2 + */ + id: number; + /** + * Format: int32 + * @description 번쩍장 id + * @example 184 + */ + leaderUserId: number; + /** + * @description 번쩍 제목 + * @example 번쩍 제목입니다. + */ + title: string; + /** + * @description 모임 카테고리(번쩍) + * @example 번쩍 + */ + category: string; + /** + * @description 번쩍 이미지 + * @example [url 형식] + */ + imageURL: components["schemas"]["ImageUrlVO"][]; + /** + * Format: date-time + * @description 번쩍 신청 종료 시간 + */ + endDate: string; + /** + * Format: int32 + * @description 최소 모집 인원 + * @example 1 + */ + minimumCapacity: number; + /** + * Format: int32 + * @description 최대 모집 인원 + * @example 5 + */ + maximumCapacity: number; + /** @description 환영 메시지 타입 목록 */ + welcomeMessageTypes: string[]; + /** + * @description 번쩍 소개 + * @example 번쩍 소개 입니다. + */ + desc: string; + /** + * Format: date-time + * @description 번쩍 활동 시작 시간 + */ + activityStartDate: string; + /** + * Format: date-time + * @description 번쩍 활동 종료 시간 + */ + activityEndDate: string; + /** + * @description 번쩍 일시 타입 + * @example 예정 기간(협의 후 결정) + */ + timingType: string; + /** + * @description 번쩍 장소 타입 + * @example 온라인 + */ + placeType: string; + /** + * @description 번쩍 장소 + * @example Zoom 링크 + */ + place: string; + /** + * Format: int32 + * @description 개설 기수 + * @example 36 + */ + createdGeneration: number; + /** + * Format: int32 + * @description 번쩍 상태, 0: 모집전, 1: 모집중, 2: 모집종료 + * @example 1 + * @enum {integer} + */ + status: 0 | 1 | 2; + /** + * Format: int64 + * @description 승인된 신청 수 + * @example 7 + */ + approvedApplyCount: number; + /** + * @description 번쩍 개설자 여부 + * @example true + */ + host: boolean; + /** + * @description 번쩍 신청 여부 + * @example false + */ + apply: boolean; + /** + * @description 번쩍 승인 여부 + * @example false + */ + approved: boolean; + user: components["schemas"]["MeetingCreatorDto"]; + /** @description 신청 목록 */ + appliedInfo: components["schemas"]["ApplyWholeInfoDto"][]; + }; + /** @description [Internal] 모임 목록 조회 응답 Dto */ + InternalMeetingGetAllMeetingDto: { + /** @description 모임 객체 목록 */ + meetings: components["schemas"]["InternalMeetingResponseDto"][]; + meta: components["schemas"]["PageMetaDto"]; + }; + /** @description [Internal] 모임 조회 응답 Dto */ + InternalMeetingResponseDto: { + /** + * Format: int32 + * @description 모임 객체 목록 + */ + id?: number; + /** @description 모임 제목 */ + title?: string; + /** + * @description 활동 기수만 신청가능한 여부 + * @example false + */ + canJoinOnlyActiveGeneration?: boolean; + /** + * @description 모임 상태, BEFORE_START: 모집전, APPLY_ABLE: 모집중, RECRUITMENT_COMPLETE: 모집종료 + * @example APPLY_ABLE + * @enum {string} + */ + status?: "BEFORE_START" | "APPLY_ABLE" | "RECRUITMENT_COMPLETE"; + /** + * @description 모임 이미지 + * @example [url 형식] + */ + imageUrl?: string; + /** + * @description 모임 분류, [스터디 or 행사 or 세미나] + * @example 스터디 + * @enum {string} + */ + category: "스터디" | "행사" | "세미나"; + /** + * @description 대상 파트 목록 + * @example [ + * "ANDROID", + * "IOS" + * ] + */ + joinableParts: ("PM" | "DESIGN" | "IOS" | "ANDROID" | "SERVER" | "WEB")[]; + /** + * @description 모임 차단 여부 + * @example false + */ + isBlockedMeeting?: boolean; + }; + /** @description 승인된 스터디 수를 나타내는 DTO */ + ApprovedStudyCountResponseDto: { + /** + * Format: int32 + * @description 플레이그라운드 유저 ID(orgId) + * @example 1 + */ + orgId?: number; + /** + * Format: int64 + * @description 승인된 스터디 수 + * @example 5 + */ + approvedStudyCount?: number; + }; /** @description 댓글 객체 응답 Dto */ CommentDto: { /** @@ -2343,6 +2636,24 @@ export interface operations { 400: never; }; }; + /** 번쩍 모임 생성 */ + createLightning: { + requestBody: { + content: { + "application/json;charset=UTF-8": components["schemas"]["LightningV2CreateLightningBodyDto"]; + }; + }; + responses: { + /** @description lightningId: 10 */ + 201: { + content: { + "application/json;charset=UTF-8": components["schemas"]["LightningV2CreateLightningResponseDto"]; + }; + }; + /** @description VALIDATION_EXCEPTION */ + 400: never; + }; + }; /** 모임 게시글 댓글 리스트 조회 */ getComments: { parameters: { @@ -2526,6 +2837,16 @@ export interface operations { }; }; }; + testSentry: { + responses: { + /** @description OK */ + 200: { + content: { + "application/json;charset=UTF-8": string; + }; + }; + }; + }; /** 모임 게시글 개수 조회 */ getPostCount: { parameters: { @@ -2604,6 +2925,35 @@ export interface operations { }; }; }; + /** + * 추천 모임 목록 조회 + * @description 추천 모임 목록 조회, 쿼리파라미터가 없는 경우 '지금 모집중인 모임' 반환 + */ + getRecommendMeetingsByIds: { + parameters: { + query?: { + /** + * @description 추천할 모임들의 ID 리스트 + * @example [ + * 101, + * 102, + * 103 + * ] + */ + meetingIds?: number[]; + }; + }; + responses: { + /** @description 추천 모임 목록 조회 성공 */ + 200: { + content: { + "application/json;charset=UTF-8": components["schemas"]["MeetingV2GetRecommendDto"]; + }; + }; + /** @description 모임이 없습니다. */ + 400: never; + }; + }; /** * Meeting 썸네일 업로드용 Pre-Signed URL 발급 * @description Meeting 썸네일 업로드용 Pre-Signed URL 발급합니다. @@ -2666,6 +3016,78 @@ export interface operations { 204: never; }; }; + /** 번쩍 모임 상세 조회 */ + getLightningByMeetingId: { + parameters: { + path: { + meetingId: number; + }; + }; + responses: { + /** @description 번쩍 모임 상세 조회 성공 */ + 200: { + content: { + "application/json;charset=UTF-8": components["schemas"]["LightningV2GetLightningByMeetingIdResponseDto"]; + }; + }; + /** @description 번쩍 모임이 없습니다. */ + 400: never; + }; + }; + /** + * [Internal] 모임 전체 조회/검색/필터링 + * @description 모임 전체 조회/검색/필터링 + */ + getMeetings_1: { + parameters: { + query: { + orgId: number; + /** + * @description 페이지 + * @example 1 + */ + page: number; + /** + * @description 가져올 데이터 개수 + * @example 10 + */ + take: number; + /** + * @description 카테고리 + * @example [스터디, 행사, 세미나] + */ + category: string[]; + /** + * @description 활동기수만 참여여부 + * @example true + */ + isOnlyActiveGeneration: boolean; + }; + }; + responses: { + /** @description 모임 목록 조회 성공 */ + 200: { + content: { + "application/json;charset=UTF-8": components["schemas"]["InternalMeetingGetAllMeetingDto"]; + }; + }; + }; + }; + getApprovedStudyCountByOrgId: { + parameters: { + path: { + orgId: number; + }; + }; + responses: { + /** @description OK */ + 200: { + content: { + "application/json;charset=UTF-8": components["schemas"]["ApprovedStudyCountResponseDto"]; + }; + }; + }; + }; /** * 광고 조회 * @description 게시글 목록 페이지일 경우, ?category=POST <br /> 모임 목록 페이지일 경우, ?category=MEETING diff --git a/src/api/lightning/hook.ts b/src/api/lightning/hook.ts index e69de29b..a852c42e 100644 --- a/src/api/lightning/hook.ts +++ b/src/api/lightning/hook.ts @@ -0,0 +1,22 @@ +import { getLightningById, GetLightningByIdResponse } from '@api/lightning'; +import { useQuery, UseQueryResult } from '@tanstack/react-query'; +import { AxiosError } from 'axios'; + +type UseLightningByIdQueryProps = { + meetingId: number; +}; +export const useLightningByIdQuery = ({ + meetingId, +}: UseLightningByIdQueryProps): UseQueryResult<GetLightningByIdResponse> => { + return useQuery({ + queryKey: ['getLightning', meetingId], + queryFn: async () => { + try { + return await getLightningById(meetingId); + } catch (error) { + if (error instanceof AxiosError && error.response?.status === 404) return null; + throw error; + } + }, + }); +}; diff --git a/src/api/lightning/index.ts b/src/api/lightning/index.ts index c9c43fac..38e72b00 100644 --- a/src/api/lightning/index.ts +++ b/src/api/lightning/index.ts @@ -1,3 +1,4 @@ +import { paths } from '@/__generated__/schema2'; import { api } from '@api/index'; import { LightningFormType } from '@type/form'; @@ -33,3 +34,10 @@ const filterLightningFormData = (formData: LightningFormType) => { }; return data; }; + +export type GetLightningByIdResponse = + paths['/lightning/v2/{meetingId}']['get']['responses']['200']['content']['application/json;charset=UTF-8']; + +export const getLightningById = async (meetingId: number): Promise<GetLightningByIdResponse> => { + return (await api.get<GetLightningByIdResponse>(`/lightning/v2/${meetingId}`)).data; +}; diff --git a/src/api/meeting/index.ts b/src/api/meeting/index.ts index a41bd056..3c1ecc12 100644 --- a/src/api/meeting/index.ts +++ b/src/api/meeting/index.ts @@ -1,7 +1,6 @@ import { ApplicationStatusType, UserResponse } from '@api/user'; import { api } from '..'; import { APPROVAL_STATUS_KOREAN_TO_ENGLISH } from '@constants/option'; -import { LightningFormType } from '@type/form'; interface PaginationType { page: number; take: number; diff --git a/src/components/page/detail/Information/InformationPanel.tsx b/src/components/page/detail/Information/InformationPanel.tsx index 75408f79..32fceebc 100644 --- a/src/components/page/detail/Information/InformationPanel.tsx +++ b/src/components/page/detail/Information/InformationPanel.tsx @@ -3,16 +3,20 @@ import { styled } from 'stitches.config'; import { useDisplay } from '@hooks/useDisplay'; import { useCallback, useRef, useState } from 'react'; import { GetMeetingResponse } from '@api/API_LEGACY/meeting'; -import { meetingDetailList } from '@components/page/detail/Information/constant'; +import { LightningDetailList, MeetingDetailList } from '@components/page/detail/Information/constant'; +import { GetLightningByIdResponse } from '@api/lightning'; interface InformationPanelProps { - detailData: GetMeetingResponse; + detailData: GetMeetingResponse | GetLightningByIdResponse; } const InformationPanel = ({ detailData }: InformationPanelProps) => { const { isMobile } = useDisplay(); const tabRef = useRef<HTMLElement[]>([]); - const detailList = meetingDetailList(detailData); + const detailList = + detailData.category === '번쩍' + ? LightningDetailList(detailData as GetLightningByIdResponse) + : MeetingDetailList(detailData as GetMeetingResponse); const [selectedTab, setSelectedTab] = useState(detailList[0]?.key); const handleChange = useCallback((text: string) => { diff --git a/src/components/page/detail/Information/constant.tsx b/src/components/page/detail/Information/constant.tsx index 391ef2f2..c47a9d3f 100644 --- a/src/components/page/detail/Information/constant.tsx +++ b/src/components/page/detail/Information/constant.tsx @@ -6,10 +6,11 @@ import 'dayjs/locale/ko'; import { styled } from 'stitches.config'; import { parseTextToLink } from '@components/util/parseTextToLink'; dayjs.locale('ko'); -import CalendarIcon from '@assets/svg/calendar.svg'; -import LocationIcon from '@assets/svg/location.svg'; +import CalendarIcon from '@assets/svg/calendar.svg?rect'; +import { GetLightningByIdResponse } from '@api/lightning'; +import { IconLocation } from '@sopt-makers/icons'; -export const meetingDetailList = (detailData: GetMeetingResponse) => [ +export const MeetingDetailList = (detailData: GetMeetingResponse) => [ { key: '모임 소개', Title: () => <STitle>모임 소개</STitle>, @@ -63,46 +64,73 @@ export const meetingDetailList = (detailData: GetMeetingResponse) => [ }, ]; -export const LightningDetailList = (detailData: GetMeetingResponse) => [ +export const LightningDetailList = (detailData: GetLightningByIdResponse) => [ { key: '#환영 태그', Title: () => <STitle>#환영 태그</STitle>, - Content: () => ( - <STarget> - {detailData?.welcomeMessageTypes.map(tag => ( - <Chip key={tag} style={{ width: '80px', boxShadow: 'none' }} active> - {tag} - </Chip> - ))} - </STarget> - ), - isValid: detailData?.joinableParts, + Content: () => { + console.log(detailData.welcomeMessageTypes); + return ( + <STarget> + {detailData?.welcomeMessageTypes.map(tag => ( + <Chip key={tag} style={{ width: '80px', boxShadow: 'none' }} active> + {tag} + </Chip> + ))} + </STarget> + ); + }, + isValid: detailData?.welcomeMessageTypes, }, { key: '설명', Title: () => <STitle>설명</STitle>, - Content: () => <SDescription>{parseTextToLink(detailData?.desc)}</SDescription>, + Content: () => { + console.log(detailData.welcomeMessageTypes, detailData.welcomeMessageTypes.length); + return <SDescription>{parseTextToLink(detailData?.desc)}</SDescription>; + }, isValid: detailData?.desc, }, { key: '진행일', Title: () => ( <SIconTitleWrapper> - <CalendarIcon /> + <SIconCalendar /> <STitle>진행일</STitle> </SIconTitleWrapper> ), - isValid: detailData.activityStartDate, + Content: () => { + const isSingleDay = detailData.timingType === '당일'; + return ( + <SDescription style={{ color: 'white' }}> + {`${dayjs(detailData.activityStartDate).format('YYYY. MM. DD (dd)')}${ + isSingleDay ? '' : ` ~${dayjs(detailData.activityEndDate).format('YYYY. MM. DD (dd)')}` + }`} + {isSingleDay && ( + <> + <Divider /> + 기간 중 협의 후 결정 + </> + )} + </SDescription> + ); + }, + isValid: detailData.activityStartDate && detailData.activityEndDate, }, { key: '장소', Title: () => ( <SIconTitleWrapper> - <LocationIcon /> + <SIconLocation /> <STitle>장소</STitle> </SIconTitleWrapper> ), - Content: () => <SDescription>{`${parsePlaceType(detailData.placeType, detailData.place)}`}</SDescription>, + Content: () => ( + <SDescription style={{ color: 'white' }}>{`${parsePlaceType( + detailData.placeType, + detailData.place + )}`}</SDescription> + ), isValid: detailData.place, }, ]; @@ -128,6 +156,32 @@ const STitle = styled('h2', { }, }); +const SIconCalendar = styled(CalendarIcon, { + width: '24px', + height: '24px', + + mb: '$24', + + '& path': { + stroke: '$white', + }, + + '@tablet': { + mb: '$20', + }, +}); + +const SIconLocation = styled(IconLocation, { + width: '24px', + height: '24px', + + mb: '$24', + + '@tablet': { + mb: '$20', + }, +}); + const SIconTitleWrapper = styled('div', { display: 'flex', alignItems: 'center', @@ -149,6 +203,18 @@ const SDescription = styled('p', { }, }); +const Divider = styled('hr', { + display: 'inline-block', + + width: '$2', + height: '$16', + margin: '0 $16', + + backgroundColor: '$gray300', + color: '$gray300', + border: 'none', +}); + const STarget = styled(SDescription, { display: 'flex', alignItems: 'center', diff --git a/src/components/page/detail/MeetingController/LightningAbout.tsx b/src/components/page/detail/MeetingController/LightningAbout.tsx new file mode 100644 index 00000000..b8599f49 --- /dev/null +++ b/src/components/page/detail/MeetingController/LightningAbout.tsx @@ -0,0 +1,117 @@ +import { RECRUITMENT_STATUS } from '@constants/option'; +import React from 'react'; +import { styled } from 'stitches.config'; +import dayjs from 'dayjs'; +import ProfileAnchor from '@components/page/detail/MeetingController/ProfileAnchor'; +import { GetLightningByIdResponse } from '@api/lightning'; + +const LightningAbout = ({ detailData }: { detailData: GetLightningByIdResponse }) => { + const { + title, + status, + endDate, + user: { orgId: hostId, name: hostName, profileImage: hostProfileImage }, + category, + } = detailData; + + return ( + <SAbout> + <div> + <SRecruitStatus status={status}>{RECRUITMENT_STATUS[status]}</SRecruitStatus> + <SPeriod> + {`~${dayjs(endDate).format('YY.MM.DD')}`} + <span>|</span> + {dayjs(endDate).format('HH:mm')} + </SPeriod> + </div> + <h1> + <span>{category}</span> + {title} + </h1> + <SHostWrapper> + <ProfileAnchor profileData={{ orgId: hostId, userprofileImage: hostProfileImage, userName: hostName }} /> + </SHostWrapper> + </SAbout> + ); +}; + +export default LightningAbout; + +const SAbout = styled('div', { + mr: '$90', + + '@tablet': { + mr: '$0', + }, + + '& > div': { + flexType: 'verticalCenter', + mb: '$12', + }, + + '& > h1': { + span: { + color: '$gray400', + mr: '$8', + + '@tablet': { + mr: '$4', + }, + }, + + fontAg: '34_bold_140', + color: '$gray10', + mb: '$20', + + '@tablet': { + fontStyle: 'H3', + }, + }, +}); + +const SRecruitStatus = styled('div', { + width: 'fit-content', + padding: '$7 $8', + mr: '$12', + borderRadius: '6px', + fontAg: '16_bold_100', + + '@tablet': { + padding: '$2 $6', + mr: '$8', + borderRadius: '5px', + fontStyle: 'B4', + }, + + variants: { + status: { + 0: { + backgroundColor: '$gray600', + }, + 1: { + backgroundColor: '$secondary', + color: '$gray950', + }, + 2: { + backgroundColor: '$gray700', + }, + }, + }, +}); + +const SPeriod = styled('div', { + fontAg: '20_bold_100', + color: '$gray300', + + '@tablet': { + fontStyle: 'T6', + }, +}); + +const SHostWrapper = styled('div', { + position: 'relative', + gap: '16px', + '@mobile': { + gap: '6px', + }, +}); diff --git a/src/components/page/detail/MeetingController/MeetingAbout.tsx b/src/components/page/detail/MeetingController/MeetingAbout.tsx new file mode 100644 index 00000000..8058e615 --- /dev/null +++ b/src/components/page/detail/MeetingController/MeetingAbout.tsx @@ -0,0 +1,123 @@ +import { GetMeetingResponse } from '@api/API_LEGACY/meeting'; +import { RECRUITMENT_STATUS } from '@constants/option'; +import React from 'react'; +import { styled } from 'stitches.config'; +import dayjs from 'dayjs'; +import MentorTooltip from '@components/page/detail/MeetingController/MentorTooltip'; +import ProfileAnchor from '@components/page/detail/MeetingController/ProfileAnchor'; + +const MeetingAbout = ({ detailData }: { detailData: GetMeetingResponse }) => { + const { + title, + status, + startDate, + endDate, + user: { orgId: hostId, name: hostName, profileImage: hostProfileImage }, + category, + coMeetingLeaders, + isMentorNeeded, + } = detailData; + + return ( + <SAbout> + <div> + <SRecruitStatus status={status}>{RECRUITMENT_STATUS[status]}</SRecruitStatus> + <SPeriod> + {dayjs(startDate).format('YY.MM.DD')} - {dayjs(endDate).format('YY.MM.DD')} + </SPeriod> + </div> + <h1> + <span>{category}</span> + {title} + </h1> + <SHostWrapper> + <ProfileAnchor profileData={{ orgId: hostId, userprofileImage: hostProfileImage, userName: hostName }} /> + {coMeetingLeaders?.map((item: typeof coMeetingLeaders[number]) => ( + <ProfileAnchor profileData={item} /> + ))} + </SHostWrapper> + {isMentorNeeded && <MentorTooltip />} + </SAbout> + ); +}; + +export default MeetingAbout; + +const SAbout = styled('div', { + mr: '$90', + + '@tablet': { + mr: '$0', + }, + + '& > div': { + flexType: 'verticalCenter', + mb: '$12', + }, + + '& > h1': { + span: { + color: '$gray400', + mr: '$8', + + '@tablet': { + mr: '$4', + }, + }, + + fontAg: '34_bold_140', + color: '$gray10', + mb: '$20', + + '@tablet': { + fontStyle: 'H3', + }, + }, +}); + +const SRecruitStatus = styled('div', { + width: 'fit-content', + padding: '$7 $8', + mr: '$12', + borderRadius: '6px', + fontAg: '16_bold_100', + + '@tablet': { + padding: '$2 $6', + mr: '$8', + borderRadius: '5px', + fontStyle: 'B4', + }, + + variants: { + status: { + 0: { + backgroundColor: '$gray600', + }, + 1: { + backgroundColor: '$secondary', + color: '$gray950', + }, + 2: { + backgroundColor: '$gray700', + }, + }, + }, +}); + +const SPeriod = styled('div', { + fontAg: '20_bold_100', + color: '$gray300', + + '@tablet': { + fontStyle: 'T6', + }, +}); + +const SHostWrapper = styled('div', { + position: 'relative', + gap: '16px', + '@mobile': { + gap: '6px', + }, +}); diff --git a/src/components/page/detail/MeetingController/constant.ts b/src/components/page/detail/MeetingController/constant.ts new file mode 100644 index 00000000..294e18bf --- /dev/null +++ b/src/components/page/detail/MeetingController/constant.ts @@ -0,0 +1,9 @@ +import { GetMeetingResponse } from '@api/API_LEGACY/meeting'; +import { GetLightningByIdResponse } from '@api/lightning'; + +export const CAPACITY = (detailData: GetMeetingResponse | GetLightningByIdResponse) => { + if ('capacity' in detailData) { + return `${detailData.approvedApplyCount}/${detailData.capacity}명`; + } + return `${detailData.approvedApplyCount}/${detailData.minimumCapacity}~${detailData.maximumCapacity}명`; +}; diff --git a/src/components/page/detail/MeetingController/index.tsx b/src/components/page/detail/MeetingController/index.tsx index 1e354824..17d9d96a 100644 --- a/src/components/page/detail/MeetingController/index.tsx +++ b/src/components/page/detail/MeetingController/index.tsx @@ -4,7 +4,6 @@ import Link from 'next/link'; import { AxiosError, AxiosResponse } from 'axios'; import { UseMutateFunction, useQueryClient } from '@tanstack/react-query'; import { styled } from 'stitches.config'; -import dayjs from 'dayjs'; import { playgroundLink } from '@sopt-makers/playground-common'; import useModal from '@hooks/useModal'; import DefaultModal from '@components/modal/DefaultModal'; @@ -13,19 +12,21 @@ import GuestConfirmModal from './Modal/Confirm/GuestConfirmModal'; import ApplicationModalContent from './Modal/Content/ApplicationModalContent'; import RecruitmentStatusModalContent from './Modal/Content/RecruitmentStatusModalContent'; import { PostApplicationRequest, GetMeetingResponse } from '@api/API_LEGACY/meeting'; -import { ERecruitmentStatus, RECRUITMENT_STATUS } from '@constants/option'; +import { ERecruitmentStatus } from '@constants/option'; import ArrowSmallRightIcon from '@assets/svg/arrow_small_right.svg'; -import MentorTooltip from './MentorTooltip'; import { useQueryMyProfile } from '@api/API_LEGACY/user/hooks'; import { ampli } from '@/ampli'; import ButtonLoader from '@components/@common/loader/ButtonLoader'; import { useDialog } from '@sopt-makers/ui'; import { ReactNode } from 'react'; -import ProfileAnchor from './ProfileAnchor'; import { useMutationPostEventApplication } from '@api/API_LEGACY/meeting/hooks'; +import { GetLightningByIdResponse } from '@api/lightning'; +import MeetingAbout from '@components/page/detail/MeetingController/MeetingAbout'; +import LightningAbout from '@components/page/detail/MeetingController/LightningAbout'; +import { CAPACITY } from '@components/page/detail/MeetingController/constant'; interface DetailHeaderProps { - detailData: GetMeetingResponse; + detailData: GetMeetingResponse | GetLightningByIdResponse; mutateMeetingDeletion: UseMutateFunction< { statusCode: number; @@ -68,23 +69,8 @@ const MeetingController = ({ mutateApplication, mutateApplicationDeletion, }: DetailHeaderProps) => { - const { - status, - startDate, - endDate, - category, - title, - user: { orgId: hostId, name: hostName, profileImage: hostProfileImage }, - appliedInfo, - approved, - approvedApplyCount, - capacity, - isCoLeader, - coMeetingLeaders, - host: isHost, - apply: isApplied, - isMentorNeeded, - } = detailData; + const isLightning = detailData.category === '번쩍'; + const { status, category, appliedInfo, approved, host: isHost, apply: isApplied } = detailData; const { open: dialogOpen, close: dialogClose } = useDialog(); const { data: me } = useQueryMyProfile(); @@ -114,7 +100,7 @@ const MeetingController = ({ const handleRecruitmentStatusModal = () => { ampli.clickMemberStatus({ crew_status: approved || isHost }); handleDefaultModalOpen(); - setModalTitle(`모집 현황 (${approvedApplyCount}/${capacity}명)`); + setModalTitle(`모집 현황 (${CAPACITY(detailData)})`); }; const handleHostModalOpen = () => { @@ -268,32 +254,16 @@ const MeetingController = ({ return ( <> <SPanelWrapper> - <SAbout> - <div> - <SRecruitStatus status={status}>{RECRUITMENT_STATUS[status]}</SRecruitStatus> - <SPeriod> - {dayjs(startDate).format('YY.MM.DD')} - {dayjs(endDate).format('YY.MM.DD')} - </SPeriod> - </div> - <h1> - <span>{category}</span> - {title} - </h1> - <SHostWrapper> - <ProfileAnchor profileData={{ orgId: hostId, userprofileImage: hostProfileImage, userName: hostName }} /> - {coMeetingLeaders?.map((item: typeof coMeetingLeaders[number]) => ( - <ProfileAnchor profileData={item} /> - ))} - </SHostWrapper> - {isMentorNeeded && <MentorTooltip />} - </SAbout> + {isLightning ? ( + <LightningAbout detailData={detailData as GetLightningByIdResponse} /> + ) : ( + <MeetingAbout detailData={detailData as GetMeetingResponse} /> + )} <div> <SStatusButton onClick={handleRecruitmentStatusModal}> <div> <span>모집 현황</span> - <span> - {approvedApplyCount}/{capacity}명 - </span> + <span>{CAPACITY(detailData)}</span> </div> <ArrowSmallRightIcon /> </SStatusButton> @@ -342,7 +312,7 @@ const MeetingController = ({ meetingId={Number(meetingId)} appliedInfo={appliedInfo} isHost={isHost} - isCoLeader={isCoLeader} + isCoLeader={'isCoLeader' in detailData ? detailData.isCoLeader : false} isApplied={isApplied} /> )} @@ -368,85 +338,6 @@ const SPanelWrapper = styled('div', { }, }); -const SAbout = styled('div', { - mr: '$90', - - '@tablet': { - mr: '$0', - }, - - '& > div': { - flexType: 'verticalCenter', - mb: '$12', - }, - - '& > h1': { - span: { - color: '$gray400', - mr: '$8', - - '@tablet': { - mr: '$4', - }, - }, - - fontAg: '34_bold_140', - color: '$gray10', - mb: '$20', - - '@tablet': { - fontStyle: 'H3', - }, - }, -}); - -const SRecruitStatus = styled('div', { - width: 'fit-content', - padding: '$7 $8', - mr: '$12', - borderRadius: '6px', - fontAg: '16_bold_100', - - '@tablet': { - padding: '$2 $6', - mr: '$8', - borderRadius: '5px', - fontStyle: 'B4', - }, - - variants: { - status: { - 0: { - backgroundColor: '$gray600', - }, - 1: { - backgroundColor: '$secondary', - color: '$gray950', - }, - 2: { - backgroundColor: '$gray700', - }, - }, - }, -}); - -const SPeriod = styled('div', { - fontAg: '20_bold_100', - color: '$gray300', - - '@tablet': { - fontStyle: 'T6', - }, -}); - -const SHostWrapper = styled('div', { - position: 'relative', - gap: '16px', - '@mobile': { - gap: '6px', - }, -}); - const Button = styled('button', { width: '$300', height: '$60', From 49ba493c9ccf59c807a0fe0d1b3b61f09429029b Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Fri, 24 Jan 2025 07:34:44 +0900 Subject: [PATCH 10/16] =?UTF-8?q?feat:=20=EB=B2=88=EC=A9=8D=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A4=20=EB=9D=BC=EC=9A=B0=ED=8C=85=20=EC=97=B0=EA=B2=B0=20?= =?UTF-8?q?=EB=B0=8F=20alert=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/make/lightning/index.tsx | 10 ++++++---- src/api/lightning/index.ts | 6 +++--- .../GroupBrowsingCard/GroupBrowsingCard.tsx | 4 ++-- .../detail/MeetingController/LightningAbout.tsx | 14 +++++++++++++- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/pages/make/lightning/index.tsx b/pages/make/lightning/index.tsx index d2df464a..24df37b7 100644 --- a/pages/make/lightning/index.tsx +++ b/pages/make/lightning/index.tsx @@ -43,10 +43,12 @@ const Lightning = () => { }; const onSubmit: SubmitHandler<LightningFormType> = async formData => { - const bungaeId = await mutateCreateLightning(formData); - ampli.completedMakeGroup(); - alert('번쩍을 개설했습니다.'); - router.push(`/detail?id=${bungaeId}`); + mutateCreateLightning(formData, { + onSuccess: data => { + ampli.completedMakeGroup(); + router.push(`/detail?id=${data}`); + }, + }); }; return ( diff --git a/src/api/lightning/index.ts b/src/api/lightning/index.ts index 38e72b00..45a8f849 100644 --- a/src/api/lightning/index.ts +++ b/src/api/lightning/index.ts @@ -4,9 +4,9 @@ import { LightningFormType } from '@type/form'; export const createLightning = async (formData: LightningFormType) => { const { - data: { lightningId }, - } = await api.post<{ lightningId: number }>('/lightning/v2', filterLightningFormData(formData)); - return lightningId; + data: { meetingId }, + } = await api.post<{ meetingId: number }>('/lightning/v2', filterLightningFormData(formData)); + return meetingId; }; const filterLightningFormData = (formData: LightningFormType) => { diff --git a/src/components/groupBrowsing/GroupBrowsingCard/GroupBrowsingCard.tsx b/src/components/groupBrowsing/GroupBrowsingCard/GroupBrowsingCard.tsx index 514aefc0..d999cba4 100644 --- a/src/components/groupBrowsing/GroupBrowsingCard/GroupBrowsingCard.tsx +++ b/src/components/groupBrowsing/GroupBrowsingCard/GroupBrowsingCard.tsx @@ -1,7 +1,6 @@ import React, { FC } from 'react'; import { keyframes, styled } from 'stitches.config'; import CalendarIcon from '@assets/svg/calendar.svg'; -import LocationIcon from '@assets/svg/location.svg'; import Avatar from '@components/@common/avatar/Avatar'; import { Flex } from '@components/util/layout/Flex'; import { GroupBrowsingCardItem } from '@api/API_LEGACY/meeting'; @@ -12,6 +11,7 @@ import Link from 'next/link'; import { getResizedImage } from '@utils/image'; import { fontsObject } from '@sopt-makers/fonts'; import { Tag } from '@sopt-makers/ui'; +import { IconLocation } from '@sopt-makers/icons'; const GroupBrowsingCard: FC<GroupBrowsingCardItem> = ({ id, title, mStartDate, mEndDate, user, imageURL }) => { const imgSrc = imageURL[0]?.url && getResizedImage(imageURL[0].url, 285); @@ -43,7 +43,7 @@ const GroupBrowsingCard: FC<GroupBrowsingCardItem> = ({ id, title, mStartDate, m </SDesc> </Flex> <Flex align="center"> - <LocationIcon style={{ marginRight: '6px' }} /> + <IconLocation style={{ marginRight: '6px' }} /> <SDesc>건대입구역</SDesc> </Flex> <SChipWrapper> diff --git a/src/components/page/detail/MeetingController/LightningAbout.tsx b/src/components/page/detail/MeetingController/LightningAbout.tsx index b8599f49..86febfe5 100644 --- a/src/components/page/detail/MeetingController/LightningAbout.tsx +++ b/src/components/page/detail/MeetingController/LightningAbout.tsx @@ -20,7 +20,7 @@ const LightningAbout = ({ detailData }: { detailData: GetLightningByIdResponse } <SRecruitStatus status={status}>{RECRUITMENT_STATUS[status]}</SRecruitStatus> <SPeriod> {`~${dayjs(endDate).format('YY.MM.DD')}`} - <span>|</span> + <Divider /> {dayjs(endDate).format('HH:mm')} </SPeriod> </div> @@ -99,6 +99,18 @@ const SRecruitStatus = styled('div', { }, }); +const Divider = styled('hr', { + display: 'inline-block', + + width: '$1', + height: '$16', + margin: '0 $8', + + backgroundColor: '$gray500', + color: '$gray500', + border: 'none', +}); + const SPeriod = styled('div', { fontAg: '20_bold_100', color: '$gray300', From f7ea876e0860a1aed32d833e89f0374a80e4c730 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Fri, 24 Jan 2025 08:21:52 +0900 Subject: [PATCH 11/16] =?UTF-8?q?feat:=20Lightning=20=EC=BA=90=EB=9F=AC?= =?UTF-8?q?=EC=85=80=20API=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/detail/index.tsx | 2 +- src/api/lightning/hook.ts | 17 ++++++++- src/api/lightning/index.ts | 9 +++++ src/api/meeting/hook.ts | 1 + .../GroupBrowsingCard/GroupBrowsingCard.tsx | 2 +- .../page/detail/Information/constant.tsx | 3 +- .../MeetingController/LightningAbout.tsx | 37 ++++-------------- .../detail/MeetingController/MeetingAbout.tsx | 38 ++++--------------- 8 files changed, 45 insertions(+), 64 deletions(-) diff --git a/pages/detail/index.tsx b/pages/detail/index.tsx index 30347150..189c3293 100644 --- a/pages/detail/index.tsx +++ b/pages/detail/index.tsx @@ -48,7 +48,7 @@ const DetailPage = () => { document.body.removeChild(script); }, []); - if (!meetingData || !lightningData) { + if (!meetingData) { return ( <> <Loader /> diff --git a/src/api/lightning/hook.ts b/src/api/lightning/hook.ts index a852c42e..14410555 100644 --- a/src/api/lightning/hook.ts +++ b/src/api/lightning/hook.ts @@ -1,4 +1,4 @@ -import { getLightningById, GetLightningByIdResponse } from '@api/lightning'; +import { getLightningById, GetLightningByIdResponse, getLightningList } from '@api/lightning'; import { useQuery, UseQueryResult } from '@tanstack/react-query'; import { AxiosError } from 'axios'; @@ -20,3 +20,18 @@ export const useLightningByIdQuery = ({ }, }); }; + +export const useLightningListQuery = ({ page, take }: { page: number; take: number }) => { + return useQuery( + ['lightningList', page], + () => + getLightningList({ + page, + take, + }), + { + select: response => response.data, + suspense: true, + } + ); +}; diff --git a/src/api/lightning/index.ts b/src/api/lightning/index.ts index 45a8f849..4fdba281 100644 --- a/src/api/lightning/index.ts +++ b/src/api/lightning/index.ts @@ -41,3 +41,12 @@ export type GetLightningByIdResponse = export const getLightningById = async (meetingId: number): Promise<GetLightningByIdResponse> => { return (await api.get<GetLightningByIdResponse>(`/lightning/v2/${meetingId}`)).data; }; + +export type GetLightningListRequest = paths['/meeting/v2']['get']['parameters']['query']; +export type GetLightningListResponse = + paths['/meeting/v2']['get']['responses']['200']['content']['application/json;charset=UTF-8']; +export const getLightningList = async ({ page, take }: Partial<GetLightningListRequest>) => { + return api.get<GetLightningListResponse>( + `/meeting/v2?&page=${page}&take=${take}&part=["PM", "DESIGN","IOS","ANDROID","SERVER","WEB"]&category="번쩍"&isOnlyActiveGeneration=false` + ); +}; diff --git a/src/api/meeting/hook.ts b/src/api/meeting/hook.ts index 986163a1..1aa3e2c0 100644 --- a/src/api/meeting/hook.ts +++ b/src/api/meeting/hook.ts @@ -1,5 +1,6 @@ import { UseQueryOptions, UseQueryResult, useQuery } from '@tanstack/react-query'; import { MeetingPeopleResponse, getMeetingPeopleList } from '.'; +import { fetchMeetingListOfAll, getGroupBrowsingCard, GroupBrowsingCardResponse } from '@api/API_LEGACY/meeting'; interface UseQueryGetMeetingPeopleListParams { params: { diff --git a/src/components/groupBrowsing/GroupBrowsingCard/GroupBrowsingCard.tsx b/src/components/groupBrowsing/GroupBrowsingCard/GroupBrowsingCard.tsx index d999cba4..b6891056 100644 --- a/src/components/groupBrowsing/GroupBrowsingCard/GroupBrowsingCard.tsx +++ b/src/components/groupBrowsing/GroupBrowsingCard/GroupBrowsingCard.tsx @@ -43,7 +43,7 @@ const GroupBrowsingCard: FC<GroupBrowsingCardItem> = ({ id, title, mStartDate, m </SDesc> </Flex> <Flex align="center"> - <IconLocation style={{ marginRight: '6px' }} /> + <IconLocation style={{ width: '12px', height: '12px', marginRight: '6px', color: '#9D9DA4' }} /> <SDesc>건대입구역</SDesc> </Flex> <SChipWrapper> diff --git a/src/components/page/detail/Information/constant.tsx b/src/components/page/detail/Information/constant.tsx index c47a9d3f..e7756589 100644 --- a/src/components/page/detail/Information/constant.tsx +++ b/src/components/page/detail/Information/constant.tsx @@ -86,7 +86,6 @@ export const LightningDetailList = (detailData: GetLightningByIdResponse) => [ key: '설명', Title: () => <STitle>설명</STitle>, Content: () => { - console.log(detailData.welcomeMessageTypes, detailData.welcomeMessageTypes.length); return <SDescription>{parseTextToLink(detailData?.desc)}</SDescription>; }, isValid: detailData?.desc, @@ -104,7 +103,7 @@ export const LightningDetailList = (detailData: GetLightningByIdResponse) => [ return ( <SDescription style={{ color: 'white' }}> {`${dayjs(detailData.activityStartDate).format('YYYY. MM. DD (dd)')}${ - isSingleDay ? '' : ` ~${dayjs(detailData.activityEndDate).format('YYYY. MM. DD (dd)')}` + isSingleDay ? '' : ` ~ ${dayjs(detailData.activityEndDate).format('YYYY. MM. DD (dd)')}` }`} {isSingleDay && ( <> diff --git a/src/components/page/detail/MeetingController/LightningAbout.tsx b/src/components/page/detail/MeetingController/LightningAbout.tsx index 86febfe5..5727cd6f 100644 --- a/src/components/page/detail/MeetingController/LightningAbout.tsx +++ b/src/components/page/detail/MeetingController/LightningAbout.tsx @@ -4,6 +4,7 @@ import { styled } from 'stitches.config'; import dayjs from 'dayjs'; import ProfileAnchor from '@components/page/detail/MeetingController/ProfileAnchor'; import { GetLightningByIdResponse } from '@api/lightning'; +import RecruitmentStatusTag from '@components/Tag/RecruitmentStatusTag'; const LightningAbout = ({ detailData }: { detailData: GetLightningByIdResponse }) => { const { @@ -16,14 +17,14 @@ const LightningAbout = ({ detailData }: { detailData: GetLightningByIdResponse } return ( <SAbout> - <div> - <SRecruitStatus status={status}>{RECRUITMENT_STATUS[status]}</SRecruitStatus> + <SStatusWrapper> + <RecruitmentStatusTag status={status} /> <SPeriod> {`~${dayjs(endDate).format('YY.MM.DD')}`} <Divider /> {dayjs(endDate).format('HH:mm')} </SPeriod> - </div> + </SStatusWrapper> <h1> <span>{category}</span> {title} @@ -69,33 +70,11 @@ const SAbout = styled('div', { }, }); -const SRecruitStatus = styled('div', { - width: 'fit-content', - padding: '$7 $8', - mr: '$12', - borderRadius: '6px', - fontAg: '16_bold_100', - +const SStatusWrapper = styled('div', { + display: 'flex', + gap: '$12', '@tablet': { - padding: '$2 $6', - mr: '$8', - borderRadius: '5px', - fontStyle: 'B4', - }, - - variants: { - status: { - 0: { - backgroundColor: '$gray600', - }, - 1: { - backgroundColor: '$secondary', - color: '$gray950', - }, - 2: { - backgroundColor: '$gray700', - }, - }, + gap: '$8', }, }); diff --git a/src/components/page/detail/MeetingController/MeetingAbout.tsx b/src/components/page/detail/MeetingController/MeetingAbout.tsx index 8058e615..0d59445c 100644 --- a/src/components/page/detail/MeetingController/MeetingAbout.tsx +++ b/src/components/page/detail/MeetingController/MeetingAbout.tsx @@ -1,10 +1,10 @@ import { GetMeetingResponse } from '@api/API_LEGACY/meeting'; -import { RECRUITMENT_STATUS } from '@constants/option'; import React from 'react'; import { styled } from 'stitches.config'; import dayjs from 'dayjs'; import MentorTooltip from '@components/page/detail/MeetingController/MentorTooltip'; import ProfileAnchor from '@components/page/detail/MeetingController/ProfileAnchor'; +import RecruitmentStatusTag from '@components/Tag/RecruitmentStatusTag'; const MeetingAbout = ({ detailData }: { detailData: GetMeetingResponse }) => { const { @@ -20,12 +20,12 @@ const MeetingAbout = ({ detailData }: { detailData: GetMeetingResponse }) => { return ( <SAbout> - <div> - <SRecruitStatus status={status}>{RECRUITMENT_STATUS[status]}</SRecruitStatus> + <SStatusWrapper> + <RecruitmentStatusTag status={status} /> <SPeriod> {dayjs(startDate).format('YY.MM.DD')} - {dayjs(endDate).format('YY.MM.DD')} </SPeriod> - </div> + </SStatusWrapper> <h1> <span>{category}</span> {title} @@ -75,33 +75,11 @@ const SAbout = styled('div', { }, }); -const SRecruitStatus = styled('div', { - width: 'fit-content', - padding: '$7 $8', - mr: '$12', - borderRadius: '6px', - fontAg: '16_bold_100', - +const SStatusWrapper = styled('div', { + display: 'flex', + gap: '$12', '@tablet': { - padding: '$2 $6', - mr: '$8', - borderRadius: '5px', - fontStyle: 'B4', - }, - - variants: { - status: { - 0: { - backgroundColor: '$gray600', - }, - 1: { - backgroundColor: '$secondary', - color: '$gray950', - }, - 2: { - backgroundColor: '$gray700', - }, - }, + gap: '$8', }, }); From 34725a9950f62076f967b70acf61f7e6c499e4bd Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Fri, 24 Jan 2025 08:35:48 +0900 Subject: [PATCH 12/16] =?UTF-8?q?style:=20=ED=99=98=EC=98=81=20=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/lightning/index.ts | 8 +++++--- src/components/page/detail/Information/constant.tsx | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/api/lightning/index.ts b/src/api/lightning/index.ts index 4fdba281..d14e4ac9 100644 --- a/src/api/lightning/index.ts +++ b/src/api/lightning/index.ts @@ -46,7 +46,9 @@ export type GetLightningListRequest = paths['/meeting/v2']['get']['parameters'][ export type GetLightningListResponse = paths['/meeting/v2']['get']['responses']['200']['content']['application/json;charset=UTF-8']; export const getLightningList = async ({ page, take }: Partial<GetLightningListRequest>) => { - return api.get<GetLightningListResponse>( - `/meeting/v2?&page=${page}&take=${take}&part=["PM", "DESIGN","IOS","ANDROID","SERVER","WEB"]&category="번쩍"&isOnlyActiveGeneration=false` - ); + return ( + await api.get<GetLightningListResponse>( + `/meeting/v2?&page=${page}&take=${take}&part=["PM", "DESIGN","IOS","ANDROID","SERVER","WEB"]&category="번쩍"&isOnlyActiveGeneration=false` + ) + ).data; }; diff --git a/src/components/page/detail/Information/constant.tsx b/src/components/page/detail/Information/constant.tsx index e7756589..bbcd0adf 100644 --- a/src/components/page/detail/Information/constant.tsx +++ b/src/components/page/detail/Information/constant.tsx @@ -73,7 +73,7 @@ export const LightningDetailList = (detailData: GetLightningByIdResponse) => [ return ( <STarget> {detailData?.welcomeMessageTypes.map(tag => ( - <Chip key={tag} style={{ width: '80px', boxShadow: 'none' }} active> + <Chip key={tag} style={{ boxShadow: 'none' }} active> {tag} </Chip> ))} From 4036c5f102a9c9a2300de9365a2c568cf332d36e Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Fri, 24 Jan 2025 08:38:59 +0900 Subject: [PATCH 13/16] =?UTF-8?q?fix:=20Card=20=EC=82=AC=EC=A7=84=20?= =?UTF-8?q?=EB=B9=84=EC=9C=A8=20=EA=B3=A0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/page/home/HomeCardList/DesktopCard.tsx | 4 +--- src/components/page/home/HomeCardList/MobileCard.tsx | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/components/page/home/HomeCardList/DesktopCard.tsx b/src/components/page/home/HomeCardList/DesktopCard.tsx index e8d818fa..209be8d0 100644 --- a/src/components/page/home/HomeCardList/DesktopCard.tsx +++ b/src/components/page/home/HomeCardList/DesktopCard.tsx @@ -70,9 +70,7 @@ const SThumbnailImage = styled('img', { borderRadius: '$12', backgroundColor: '$gray800', - backgroundSize: 'cover', - backgroundPosition: 'center center', - backgroundRepeat: 'no-repeat', + objectFit: 'cover', }); const STitleStyle = styled('h3', { diff --git a/src/components/page/home/HomeCardList/MobileCard.tsx b/src/components/page/home/HomeCardList/MobileCard.tsx index f83f16ef..3d032550 100644 --- a/src/components/page/home/HomeCardList/MobileCard.tsx +++ b/src/components/page/home/HomeCardList/MobileCard.tsx @@ -77,9 +77,7 @@ const SThumbnailImage = styled('img', { borderRadius: '$12', backgroundColor: '$gray800', - backgroundSize: 'cover', - backgroundPosition: 'center center', - backgroundRepeat: 'no-repeat', + objectFit: 'cover', }); const STitleStyle = styled('h3', { From 9b18eb0b2b13783b1750d8d616681249234bf51a Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Fri, 24 Jan 2025 10:02:54 +0900 Subject: [PATCH 14/16] =?UTF-8?q?feat:=20=EC=A0=84=EC=B2=B4=20=EB=AA=A8?= =?UTF-8?q?=EC=9E=84=20=EB=B2=88=EC=A9=8D,=20=EB=AA=A8=EC=9E=84=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/API_LEGACY/meeting/index.ts | 2 +- src/api/meeting/index.ts | 6 +- .../page/home/HomeCardList/index.tsx | 3 +- .../list/Card/DesktopSizeCard/constant.ts | 51 +++++++++++++++++ .../page/list/Card/DesktopSizeCard/index.tsx | 57 +++++++------------ .../page/list/Card/MobileSize/CardType.tsx | 1 - .../page/list/Card/MobileSize/ListType.tsx | 26 +-------- .../page/list/Card/MobileSize/index.tsx | 7 +-- src/components/page/list/Card/index.tsx | 9 +-- 9 files changed, 84 insertions(+), 78 deletions(-) create mode 100644 src/components/page/list/Card/DesktopSizeCard/constant.ts diff --git a/src/api/API_LEGACY/meeting/index.ts b/src/api/API_LEGACY/meeting/index.ts index 80d68494..c6950ba5 100644 --- a/src/api/API_LEGACY/meeting/index.ts +++ b/src/api/API_LEGACY/meeting/index.ts @@ -60,7 +60,7 @@ export interface MeetingResponse { targetActiveGeneration: number | null; joinableParts: string[]; } -type MeetingListOfFilterResponse = +export type MeetingListOfFilterResponse = paths['/meeting/v2']['get']['responses']['200']['content']['application/json;charset=UTF-8']; /** diff --git a/src/api/meeting/index.ts b/src/api/meeting/index.ts index dc2c8d68..08266864 100644 --- a/src/api/meeting/index.ts +++ b/src/api/meeting/index.ts @@ -59,14 +59,14 @@ export const getRecommendMeetingList = async ({ meetingIds = [] }: { meetingIds: return (await api.get<RecommendMeetingListResponse>(`/meeting/v2/recommend${meetingIdsParams}`, {})).data.meetings; }; -export const createBungae = async (formData: LightningFormType) => { +export const createLightning = async (formData: LightningFormType) => { const { data: { lightningId }, - } = await api.post<{ lightningId: number }>('/lightning/v2', filterBungaeFormData(formData)); + } = await api.post<{ lightningId: number }>('/lightning/v2', filterLightningFormData(formData)); return lightningId; }; -const filterBungaeFormData = (formData: LightningFormType) => { +const filterLightningFormData = (formData: LightningFormType) => { const convertedTags = formData.welcomeTags?.map(tag => { return tag?.value; }); diff --git a/src/components/page/home/HomeCardList/index.tsx b/src/components/page/home/HomeCardList/index.tsx index 904c02df..7a3bd90c 100644 --- a/src/components/page/home/HomeCardList/index.tsx +++ b/src/components/page/home/HomeCardList/index.tsx @@ -4,12 +4,13 @@ import { styled } from 'stitches.config'; const HomeCardList = ({ groupBrowsingCardData }: { groupBrowsingCardData: RecommendMeetingListQueryResponse }) => { const { data: recommendMeetings } = useGetRecommendMeetingListQuery({ meetingIds: [359, 360, 361] }); + const { data: nowRecruitingMeetings } = useGetRecommendMeetingListQuery({ meetingIds: [] }); return ( <SWrapper> <SGradationRight /> {recommendMeetings && <CardList label="🔹 우리... 같이 솝커톤 할래?" data={recommendMeetings.slice(0, 3)} />} - <CardList label="🔥 지금 모집중인 모임" data={groupBrowsingCardData.slice(0, 3)} /> + {nowRecruitingMeetings && <CardList label="🔥 지금 모집중인 모임" data={nowRecruitingMeetings.slice(0, 3)} />} {recommendMeetings && ( <CardList label="🍀 1차 행사 신청이 얼마 남지 않았어요!" data={recommendMeetings.slice(0, 3)} /> )} diff --git a/src/components/page/list/Card/DesktopSizeCard/constant.ts b/src/components/page/list/Card/DesktopSizeCard/constant.ts new file mode 100644 index 00000000..7d02581b --- /dev/null +++ b/src/components/page/list/Card/DesktopSizeCard/constant.ts @@ -0,0 +1,51 @@ +import { GetMeetingResponse, MeetingListOfFilterResponse, parsePartValueToLabel } from '@api/API_LEGACY/meeting'; +import { GetLightningByIdResponse } from '@api/lightning'; +import dayjs from 'dayjs'; + +export const MeetingInformation = (meetingData: MeetingListOfFilterResponse['meetings'][0]) => [ + { + label: '활동 기간', + value: () => + `${dayjs(meetingData.mStartDate).format('YY.MM.DD')} - ${dayjs(meetingData.mEndDate).format('YY.MM.DD')}`, + }, + { + label: '모집 대상', + value: () => { + const isAllParts = meetingData.joinableParts?.length === 6 || meetingData.joinableParts === null; + const part = isAllParts + ? '전체 파트' + : meetingData.joinableParts + .map(part => parsePartValueToLabel(part)) + .filter(item => item !== null) + .join(','); + return `${meetingData.targetActiveGeneration ? `${meetingData.targetActiveGeneration}기` : '전체 기수' + } / ${part}`; + }, + }, + { + label: '모집 현황', + value: () => `${meetingData.approvedCount}/${meetingData.capacity}명`, + }, +]; + +export const LightningInformation = (lightningData: GetLightningByIdResponse) => [ + { + label: '진행 일자', + value: () => { + const startDate = dayjs(lightningData.activityStartDate).format('YY.MM.dd'); + const endDate = dayjs(lightningData.activityEndDate).format('YY.MM.dd'); + + if (lightningData.timingType === '당일') return startDate; + + return `${startDate} - ${endDate} / 협의 후 결정`; + }, + }, + { + label: '활동 장소', + value: () => lightningData.place, + }, + { + label: '모집 현황', + value: () => `${lightningData.minimumCapacity} ~ ${lightningData.maximumCapacity} / `, + }, +]; diff --git a/src/components/page/list/Card/DesktopSizeCard/index.tsx b/src/components/page/list/Card/DesktopSizeCard/index.tsx index 33afac5d..7cf34e19 100644 --- a/src/components/page/list/Card/DesktopSizeCard/index.tsx +++ b/src/components/page/list/Card/DesktopSizeCard/index.tsx @@ -1,38 +1,39 @@ import { Flex } from '@components/util/layout/Flex'; import { CategoryKoType } from '@constants/option'; -import dayjs from 'dayjs'; -import { parsePartValueToLabel } from '@api/API_LEGACY/meeting'; +import { MeetingListOfFilterResponse } from '@api/API_LEGACY/meeting'; import { styled } from 'stitches.config'; import ProfileDefaultIcon from '@assets/svg/profile_default.svg?rect'; import { getResizedImage } from '@utils/image'; -import { paths } from '@/__generated__/schema2'; import { CategoryChip } from '@components/page/list/Card/DesktopSizeCard/CategoryChip'; import RecruitmentStatusTag from '@components/Tag/RecruitmentStatusTag'; +import { useLightningByIdQuery } from '@api/lightning/hook'; +import { LightningInformation, MeetingInformation } from '@components/page/list/Card/DesktopSizeCard/constant'; interface CardProps { - meetingData: Omit< - paths['/user/v2/meeting']['get']['responses']['200']['content']['application/json;charset=UTF-8']['meetings'][number], - 'isCoLeader' - > & { isCoLeader?: boolean }; - isAllParts: boolean; + meetingData: MeetingListOfFilterResponse['meetings'][0]; } -function DesktopSizeCard({ meetingData, isAllParts }: CardProps) { +function DesktopSizeCard({ meetingData }: CardProps) { + const { data: lightningData } = useLightningByIdQuery({ meetingId: +meetingData.id }); + + const detailData = lightningData ? lightningData : meetingData; + const detailInfo = lightningData ? LightningInformation(lightningData) : MeetingInformation(meetingData); + return ( <div> <ImageWrapper> - <RecruitmentStatusTag status={meetingData.status} style={{ position: 'absolute', top: '16px', left: '16px' }} /> + <RecruitmentStatusTag status={detailData.status} style={{ position: 'absolute', top: '16px', left: '16px' }} /> <SThumbnailImage css={{ - backgroundImage: `url(${meetingData.imageURL[0]?.url})`, + backgroundImage: `url(${detailData.imageURL[0]?.url})`, }} /> </ImageWrapper> <STitleSection> <CategoryChip - category={meetingData.category as CategoryKoType} - welcomeMessage={['YB 환영', 'OB 환영', '입문자 환영']} + category={detailData.category as CategoryKoType} + welcomeMessage={lightningData ? lightningData.welcomeMessageTypes : []} /> <STitle>{meetingData.title}</STitle> </STitleSection> @@ -47,30 +48,12 @@ function DesktopSizeCard({ meetingData, isAllParts }: CardProps) { </SProfileWrapper> <SName>{meetingData.user.name}</SName> </Flex> - <SInfoRow> - <SKey>활동 기간</SKey> - <SValue> - {dayjs(meetingData.mStartDate).format('YY.MM.DD')} - {dayjs(meetingData.mEndDate).format('YY.MM.DD')} - </SValue> - </SInfoRow> - <SInfoRow> - <SKey>모집 대상</SKey> - <SValue> - {meetingData.targetActiveGeneration ? `${meetingData.targetActiveGeneration}기` : '전체 기수'} /{' '} - {isAllParts - ? '전체 파트' - : meetingData.joinableParts - .map(part => parsePartValueToLabel(part)) - .filter(item => item !== null) - .join(',')} - </SValue> - </SInfoRow> - <SInfoRow> - <SKey>모집 현황</SKey> - <SValue> - {meetingData.approvedCount}/{meetingData.capacity}명 - </SValue> - </SInfoRow> + {detailInfo.map(({ label, value }) => ( + <SInfoRow key={label}> + <SKey>{label}</SKey> + <SValue>{value()}</SValue> + </SInfoRow> + ))} </div> ); } diff --git a/src/components/page/list/Card/MobileSize/CardType.tsx b/src/components/page/list/Card/MobileSize/CardType.tsx index a769b9e9..f2403f50 100644 --- a/src/components/page/list/Card/MobileSize/CardType.tsx +++ b/src/components/page/list/Card/MobileSize/CardType.tsx @@ -1,6 +1,5 @@ import { RECRUITMENT_STATUS } from '@constants/option'; import { styled } from 'stitches.config'; -import { getResizedImage } from '@utils/image'; import { MobileSizeCardProps } from '.'; function CardType({ meetingData }: Pick<MobileSizeCardProps, 'meetingData'>) { diff --git a/src/components/page/list/Card/MobileSize/ListType.tsx b/src/components/page/list/Card/MobileSize/ListType.tsx index d39aad59..9c87c524 100644 --- a/src/components/page/list/Card/MobileSize/ListType.tsx +++ b/src/components/page/list/Card/MobileSize/ListType.tsx @@ -1,18 +1,18 @@ import { Flex } from '@components/util/layout/Flex'; -import { RECRUITMENT_STATUS } from '@constants/option'; import { parsePartValueToLabel } from '@api/API_LEGACY/meeting'; import { styled } from 'stitches.config'; import ProfileDefaultIcon from '@assets/svg/profile_default.svg?rect'; import { getResizedImage } from '@utils/image'; import { Divider } from '@components/util/Divider'; import { MobileSizeCardProps } from '.'; +import RecruitmentStatusTag from '@components/Tag/RecruitmentStatusTag'; function ListType({ meetingData, isAllParts }: Omit<MobileSizeCardProps, 'mobileType'>) { return ( <Container> <Flex align="center" css={{ mb: '$16' }}> <ImageWrapper> - <SStatus recruitingStatus={meetingData.status}>{RECRUITMENT_STATUS[meetingData.status]}</SStatus> + <RecruitmentStatusTag status={meetingData.status} style={{ position: 'absolute', top: '8px', left: '8px' }} /> <SThumbnailImage css={{ backgroundImage: `url(${meetingData.imageURL[0]?.url})`, @@ -73,28 +73,6 @@ const SThumbnailImage = styled('div', { backgroundRepeat: 'no-repeat', }); -const SStatus = styled('div', { - position: 'absolute', - fontStyle: 'B4', - top: '8px', - left: '8px', - borderRadius: '5px', - padding: '$2 $6', - variants: { - recruitingStatus: { - 0: { - backgroundColor: '$gray500', - }, - 1: { - backgroundColor: '$secondary', - color: '$gray950', - }, - 2: { - backgroundColor: '$gray700', - }, - }, - }, -}); const InfoGroup = styled('div', { ml: '$12', }); diff --git a/src/components/page/list/Card/MobileSize/index.tsx b/src/components/page/list/Card/MobileSize/index.tsx index a6b1eb0c..a72fabcd 100644 --- a/src/components/page/list/Card/MobileSize/index.tsx +++ b/src/components/page/list/Card/MobileSize/index.tsx @@ -1,12 +1,9 @@ +import { GetMeetingResponse, MeetingListOfFilterResponse } from '@api/API_LEGACY/meeting'; import CardType from './CardType'; import ListType from './ListType'; -import { paths } from '@/__generated__/schema2'; export interface MobileSizeCardProps { - meetingData: Omit< - paths['/user/v2/meeting']['get']['responses']['200']['content']['application/json;charset=UTF-8']['meetings'][number], - 'isCoLeader' - > & { isCoLeader?: boolean }; + meetingData: MeetingListOfFilterResponse['meetings'][0]; isAllParts: boolean; mobileType: 'list' | 'card'; } diff --git a/src/components/page/list/Card/index.tsx b/src/components/page/list/Card/index.tsx index f1a17012..88a116ba 100644 --- a/src/components/page/list/Card/index.tsx +++ b/src/components/page/list/Card/index.tsx @@ -6,14 +6,11 @@ import MobileSizeCard from './MobileSize'; import { styled } from 'stitches.config'; import { PART_OPTIONS, PART_VALUES, RECRUITMENT_STATUS } from '@constants/option'; import { ampli } from '@/ampli'; -import { paths } from '@/__generated__/schema2'; +import { MeetingListOfFilterResponse } from '@api/API_LEGACY/meeting'; interface CardProps { bottom?: ReactNode; - meetingData: Omit< - paths['/user/v2/meeting']['get']['responses']['200']['content']['application/json;charset=UTF-8']['meetings'][number], - 'isCoLeader' - > & { isCoLeader?: boolean }; + meetingData: MeetingListOfFilterResponse['meetings'][0]; mobileType: 'list' | 'card'; } @@ -37,7 +34,7 @@ function Card({ bottom, meetingData, mobileType }: CardProps) { > <Link href={`/detail?id=${meetingData.id}`}> <DesktopOnly> - <DesktopSizeCard meetingData={meetingData} isAllParts={isAllParts} /> + <DesktopSizeCard meetingData={meetingData} /> </DesktopOnly> <MobileOnly> <MobileSizeCard meetingData={meetingData} isAllParts={isAllParts} mobileType={mobileType} /> From bcc2780f8a98938108daa75edd329f75d67acc41 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Fri, 24 Jan 2025 10:15:35 +0900 Subject: [PATCH 15/16] =?UTF-8?q?fix:=20=EB=B9=8C=EB=93=9C=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/mine/management/index.tsx | 2 -- pages/post/index.tsx | 2 +- src/api/API_LEGACY/meeting/hooks.ts | 1 - src/api/lightning/hook.ts | 12 ++++------ .../FeedCommentContainer.tsx | 3 --- .../FeedCommentViewer/FeedCommentViewer.tsx | 1 - src/components/feed/Mention/index.tsx | 3 +-- src/components/form/SearchMention/index.tsx | 17 -------------- .../MeetingController/LightningAbout.tsx | 1 - .../page/home/HomeCardList/MobileCard.tsx | 1 - .../page/home/HomeCardList/index.tsx | 3 +-- .../list/Advertisement/PostDeleteModal.tsx | 2 +- .../list/Card/DesktopSizeCard/constant.ts | 11 +++++---- .../page/list/Card/MobileSize/index.tsx | 2 +- .../mine/management/ManagementListItem.tsx | 23 ------------------- src/hooks/useOverlay/OverlayProvider.tsx | 2 +- 16 files changed, 16 insertions(+), 70 deletions(-) diff --git a/pages/mine/management/index.tsx b/pages/mine/management/index.tsx index 7a900337..2e5f6f75 100644 --- a/pages/mine/management/index.tsx +++ b/pages/mine/management/index.tsx @@ -1,7 +1,5 @@ import { useRouter } from 'next/router'; -import Link from 'next/link'; import { styled } from 'stitches.config'; -import { TabList } from '@components/@common/tabList/TabList'; import ManagementListSkeleton from '@components/page/mine/management/Skeleton/ManagementListSkeleton'; import MeetingInformationSkeleton from '@components/page/mine/management/Skeleton/MeetingInformationSkeleton'; import ManagementListItem from '@components/page/mine/management/ManagementListItem'; diff --git a/pages/post/index.tsx b/pages/post/index.tsx index 76a4ec5d..457e306c 100644 --- a/pages/post/index.tsx +++ b/pages/post/index.tsx @@ -58,7 +58,7 @@ export default function PostPage() { mutationFn: (comment: string) => POST('/comment/v2', { body: { - postId: post!.id, + postId: post?.id, contents: comment, isParent: parentComment.parentComment, parentCommentId: parentComment.parentComment ? null : parentComment.parentCommentId, diff --git a/src/api/API_LEGACY/meeting/hooks.ts b/src/api/API_LEGACY/meeting/hooks.ts index af6e2de9..70645115 100644 --- a/src/api/API_LEGACY/meeting/hooks.ts +++ b/src/api/API_LEGACY/meeting/hooks.ts @@ -28,7 +28,6 @@ import { UpdateApplicationRequest, downloadMeetingMemberCSV, getGroupBrowsingCard, - GroupBrowsingCardDetail, GetMeetingResponse, postEventApplication, GroupBrowsingCardResponse, diff --git a/src/api/lightning/hook.ts b/src/api/lightning/hook.ts index 14410555..0c0c2b29 100644 --- a/src/api/lightning/hook.ts +++ b/src/api/lightning/hook.ts @@ -22,16 +22,12 @@ export const useLightningByIdQuery = ({ }; export const useLightningListQuery = ({ page, take }: { page: number; take: number }) => { - return useQuery( - ['lightningList', page], - () => + return useQuery({ + queryKey: ['lightningList', page], + queryFn: () => getLightningList({ page, take, }), - { - select: response => response.data, - suspense: true, - } - ); + }); }; diff --git a/src/components/feed/FeedCommentContainer/FeedCommentContainer.tsx b/src/components/feed/FeedCommentContainer/FeedCommentContainer.tsx index 13ad9993..c9d41fa9 100644 --- a/src/components/feed/FeedCommentContainer/FeedCommentContainer.tsx +++ b/src/components/feed/FeedCommentContainer/FeedCommentContainer.tsx @@ -25,8 +25,6 @@ import FeedReCommentInput from '../FeedReCommentInput/FeedReCommentInput'; import { MentionContext } from '../Mention/MentionContext'; import { useToast } from '@sopt-makers/ui'; import CommentBlocker from '@components/blocker/CommentBlocker'; -import { IconTrash, IconWrite } from '@sopt-makers/icons'; -import { IconAlertCircle } from '@sopt-makers/icons'; import ReWriteIcon from '@assets/svg/comment-write.svg'; import TrashIcon from '@assets/svg/trash.svg'; import AlertIcon from '@assets/svg/alert-triangle.svg'; @@ -59,7 +57,6 @@ export default function FeedCommentContainer({ const initialReplyLength = useRef<number>(comment?.replies?.length); const { parentComment } = useContext(MentionContext); - const recommentRef = useRef<HTMLTextAreaElement | null>(null); const { mutate: mutateDeleteComment } = useDeleteComment(query.id as string); diff --git a/src/components/feed/FeedCommentViewer/FeedCommentViewer.tsx b/src/components/feed/FeedCommentViewer/FeedCommentViewer.tsx index 00ca8348..2a55c640 100644 --- a/src/components/feed/FeedCommentViewer/FeedCommentViewer.tsx +++ b/src/components/feed/FeedCommentViewer/FeedCommentViewer.tsx @@ -32,7 +32,6 @@ interface FeedCommentViewerProps { export default function FeedCommentViewer({ comment, commentParentId, - isMine, isPosterComment, Content, Actions, diff --git a/src/components/feed/Mention/index.tsx b/src/components/feed/Mention/index.tsx index a6862d25..f95b9209 100644 --- a/src/components/feed/Mention/index.tsx +++ b/src/components/feed/Mention/index.tsx @@ -43,8 +43,7 @@ const CommonMention = ({ }: CommonMentionProps) => { const { data: mentionUserList } = useQueryGetMentionUsers(); - const { parentComment, user, isReCommentClicked, setIsReCommentClicked, setParentComment } = - useContext(MentionContext); + const { parentComment, user, isReCommentClicked } = useContext(MentionContext); useEffect(() => { //컨테이너의 ID일 경우 (즉, 답글 달기에 매칭되는 댓글 or 대댓글인 경우) diff --git a/src/components/form/SearchMention/index.tsx b/src/components/form/SearchMention/index.tsx index 5e855462..3cbbb487 100644 --- a/src/components/form/SearchMention/index.tsx +++ b/src/components/form/SearchMention/index.tsx @@ -53,29 +53,12 @@ const SearchMention = ({ }: SearchMentionProps) => { //전역 상태 - 혹시 필요하면 context 적절히 조작하여 사용 const { user, setUser } = useContext(SearchMentionContext); - const [selectedSuggestionIndex, setSelectedSuggestionIndex] = useState(-1); const handleUserClick = (user: mentionableDataType) => { onUserSelect(user); setValue(''); }; - //엔터만 눌러도 추가가 되도록 함수 구현 -> 지금은 사용하고 있지 않음. - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Enter') { - const suggestions = getFilteredAndRandomUsers( - value, - mentionUserList.map(v => ({ ...v, id: v.orgId, display: v.userName })) - ); - - if (suggestions.length > 0) { - // select the first suggestion - handleUserClick(suggestions[0] ?? defaultMentionableDataType); - e.preventDefault(); - } - } - }; - const filterUsersBySearchTerm = (searchTerm: string, users: mentionableDataType[]) => { return users?.filter((v: mentionableDataType) => v.userName.includes(searchTerm)); }; diff --git a/src/components/page/detail/MeetingController/LightningAbout.tsx b/src/components/page/detail/MeetingController/LightningAbout.tsx index 5727cd6f..2ff02c8d 100644 --- a/src/components/page/detail/MeetingController/LightningAbout.tsx +++ b/src/components/page/detail/MeetingController/LightningAbout.tsx @@ -1,4 +1,3 @@ -import { RECRUITMENT_STATUS } from '@constants/option'; import React from 'react'; import { styled } from 'stitches.config'; import dayjs from 'dayjs'; diff --git a/src/components/page/home/HomeCardList/MobileCard.tsx b/src/components/page/home/HomeCardList/MobileCard.tsx index 3d032550..767d36ee 100644 --- a/src/components/page/home/HomeCardList/MobileCard.tsx +++ b/src/components/page/home/HomeCardList/MobileCard.tsx @@ -4,7 +4,6 @@ import UserIcon from '@assets/svg/user.svg?rect'; import { CATEGORY_NAME, CategoryType, PART_NAME } from '@constants/option'; import Link from 'next/link'; import { Flex } from '@components/util/layout/Flex'; -import { fontsObject } from '@sopt-makers/fonts'; type MobileCardProps = { id: number; diff --git a/src/components/page/home/HomeCardList/index.tsx b/src/components/page/home/HomeCardList/index.tsx index 7a3bd90c..904c02df 100644 --- a/src/components/page/home/HomeCardList/index.tsx +++ b/src/components/page/home/HomeCardList/index.tsx @@ -4,13 +4,12 @@ import { styled } from 'stitches.config'; const HomeCardList = ({ groupBrowsingCardData }: { groupBrowsingCardData: RecommendMeetingListQueryResponse }) => { const { data: recommendMeetings } = useGetRecommendMeetingListQuery({ meetingIds: [359, 360, 361] }); - const { data: nowRecruitingMeetings } = useGetRecommendMeetingListQuery({ meetingIds: [] }); return ( <SWrapper> <SGradationRight /> {recommendMeetings && <CardList label="🔹 우리... 같이 솝커톤 할래?" data={recommendMeetings.slice(0, 3)} />} - {nowRecruitingMeetings && <CardList label="🔥 지금 모집중인 모임" data={nowRecruitingMeetings.slice(0, 3)} />} + <CardList label="🔥 지금 모집중인 모임" data={groupBrowsingCardData.slice(0, 3)} /> {recommendMeetings && ( <CardList label="🍀 1차 행사 신청이 얼마 남지 않았어요!" data={recommendMeetings.slice(0, 3)} /> )} diff --git a/src/components/page/list/Advertisement/PostDeleteModal.tsx b/src/components/page/list/Advertisement/PostDeleteModal.tsx index 150b2982..838ab593 100644 --- a/src/components/page/list/Advertisement/PostDeleteModal.tsx +++ b/src/components/page/list/Advertisement/PostDeleteModal.tsx @@ -13,7 +13,7 @@ interface PostDeleteModalProps { } //todo: meetingId 없애기 -const PostDeleteModal = ({ isOpen, close, postId, meetingId }: PostDeleteModalProps) => { +const PostDeleteModal = ({ isOpen, close, postId }: PostDeleteModalProps) => { const queryClient = useQueryClient(); const { DELETE } = apiV2.get(); diff --git a/src/components/page/list/Card/DesktopSizeCard/constant.ts b/src/components/page/list/Card/DesktopSizeCard/constant.ts index 7d02581b..74813c2d 100644 --- a/src/components/page/list/Card/DesktopSizeCard/constant.ts +++ b/src/components/page/list/Card/DesktopSizeCard/constant.ts @@ -15,11 +15,12 @@ export const MeetingInformation = (meetingData: MeetingListOfFilterResponse['mee const part = isAllParts ? '전체 파트' : meetingData.joinableParts - .map(part => parsePartValueToLabel(part)) - .filter(item => item !== null) - .join(','); - return `${meetingData.targetActiveGeneration ? `${meetingData.targetActiveGeneration}기` : '전체 기수' - } / ${part}`; + .map(part => parsePartValueToLabel(part)) + .filter(item => item !== null) + .join(','); + return `${ + meetingData.targetActiveGeneration ? `${meetingData.targetActiveGeneration}기` : '전체 기수' + } / ${part}`; }, }, { diff --git a/src/components/page/list/Card/MobileSize/index.tsx b/src/components/page/list/Card/MobileSize/index.tsx index a72fabcd..bc1ef776 100644 --- a/src/components/page/list/Card/MobileSize/index.tsx +++ b/src/components/page/list/Card/MobileSize/index.tsx @@ -1,4 +1,4 @@ -import { GetMeetingResponse, MeetingListOfFilterResponse } from '@api/API_LEGACY/meeting'; +import { MeetingListOfFilterResponse } from '@api/API_LEGACY/meeting'; import CardType from './CardType'; import ListType from './ListType'; diff --git a/src/components/page/mine/management/ManagementListItem.tsx b/src/components/page/mine/management/ManagementListItem.tsx index 35b242e2..bd8168c7 100644 --- a/src/components/page/mine/management/ManagementListItem.tsx +++ b/src/components/page/mine/management/ManagementListItem.tsx @@ -6,7 +6,6 @@ import DefaultModal from '@components/modal/DefaultModal'; import { useMutationUpdateApplication } from '@api/API_LEGACY/meeting/hooks'; import { APPLICATION_TYPE, EApprovalStatus, APPROVAL_STATUS_ENGLISH_TO_KOREAN } from '@constants/option'; import { playgroundLink } from '@sopt-makers/playground-common'; -import ArrowMiniIcon from '@assets/svg/arrow_mini.svg'; import ProfileDefaultIcon from '@assets/svg/profile_default.svg?rect'; import dayjs from 'dayjs'; import { AxiosError } from 'axios'; @@ -431,28 +430,6 @@ const SPhone = styled('div', { textAlign: 'center', }); -const SDetailButton = styled('button', { - color: '$gray10', - textDecoration: 'underline', - textUnderlinePosition: 'under', - fontAg: '18_semibold_100', - width: '$216', - textAlign: 'center', -}); - -const SCardDetailButton = styled('button', { - display: 'flex', - justifyContent: 'flex-start', - mt: '$3', - height: 'fit-content', - fontAg: '12_medium_100', - color: '$gray400', - - '& > span': { - mr: '$2', - }, -}); - const SButtonContainer = styled('div', { mr: '$8', minWidth: 'fit-content', diff --git a/src/hooks/useOverlay/OverlayProvider.tsx b/src/hooks/useOverlay/OverlayProvider.tsx index 29f40519..2a83a02f 100644 --- a/src/hooks/useOverlay/OverlayProvider.tsx +++ b/src/hooks/useOverlay/OverlayProvider.tsx @@ -1,4 +1,4 @@ -import React, { createContext, PropsWithChildren, ReactNode, useCallback, useEffect, useMemo, useState } from 'react'; +import React, { createContext, PropsWithChildren, ReactNode, useCallback, useMemo, useState } from 'react'; export const OverlayContext = createContext<{ mount(id: string, element: ReactNode): void; From f62e0f31c20e413fd4df2feeaf57ebf4cf1dde8a Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Fri, 24 Jan 2025 10:43:58 +0900 Subject: [PATCH 16/16] =?UTF-8?q?feat:=20=ED=99=88=20=EB=B2=88=EC=A9=8D=20?= =?UTF-8?q?API=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/index.tsx | 12 ++++---- src/api/lightning/hook.ts | 10 ++----- src/api/lightning/index.ts | 17 ++++++----- .../GroupBrowsingCard/GroupBrowsingCard.tsx | 30 ++++++++++--------- .../page/home/HomeCardList/index.tsx | 4 +-- 5 files changed, 37 insertions(+), 36 deletions(-) diff --git a/pages/index.tsx b/pages/index.tsx index 3daf76f2..18e81d52 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,4 +1,3 @@ -import { useQueryGetGroupBrowsingCard } from '@api/API_LEGACY/meeting/hooks'; import { useInfinitePosts } from '@api/post/hooks'; import Carousel from '@components/groupBrowsing/Carousel/Carousel'; import GroupBrowsingSlider from '@components/groupBrowsingSlider/groupBrowsingSlider'; @@ -16,6 +15,7 @@ import { styled } from 'stitches.config'; import CrewTab from '@components/CrewTab'; import HomeCardList from '@components/page/home/HomeCardList'; import { useGetRecommendMeetingListQuery } from '@api/meeting/hook'; +import { useLightningListQuery } from '@api/lightning/hook'; const Home: NextPage = () => { const { isLaptop, isTablet } = useDisplay(); @@ -24,7 +24,7 @@ const Home: NextPage = () => { const { fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } = useInfinitePosts(TAKE_COUNT); - const { data: groupBrowsingCardData } = useQueryGetGroupBrowsingCard(); + const lightningList = useLightningListQuery().data?.meetings; const { data: inProgressMeetings } = useGetRecommendMeetingListQuery({ meetingIds: [] }); useEffect(() => { @@ -40,7 +40,7 @@ const Home: NextPage = () => { {isTablet ? ( <> <SContentTitle style={{ marginTop: '16px' }}>⚡ ️솝트만의 일회성 모임, 번쩍</SContentTitle> - {groupBrowsingCardData && <GroupBrowsingSlider cardList={groupBrowsingCardData}></GroupBrowsingSlider>} + {lightningList && <GroupBrowsingSlider cardList={lightningList}></GroupBrowsingSlider>} </> ) : ( <> @@ -48,7 +48,7 @@ const Home: NextPage = () => { <SContentTitle style={{ marginTop: '54px' }}>⚡ ️솝트만의 일회성 모임, 번쩍</SContentTitle> </Flex> <GroupBrowsingCarouselContainer> - {groupBrowsingCardData && <Carousel cardList={groupBrowsingCardData} />} + {lightningList && <Carousel cardList={lightningList} />} </GroupBrowsingCarouselContainer> </> )} @@ -57,12 +57,12 @@ const Home: NextPage = () => { <QuickMenuWrapper> <QuickMenu /> </QuickMenuWrapper> - {inProgressMeetings && <HomeCardList groupBrowsingCardData={inProgressMeetings} />} + {inProgressMeetings && <HomeCardList inProgressMeetingData={inProgressMeetings} />} </Flex> ) : ( <> <Flex justify="center" style={{ marginTop: '72px' }}> - {inProgressMeetings && <HomeCardList groupBrowsingCardData={inProgressMeetings} />} + {inProgressMeetings && <HomeCardList inProgressMeetingData={inProgressMeetings} />} <div style={{ paddingLeft: '106px' }}> <QuickMenu /> </div> diff --git a/src/api/lightning/hook.ts b/src/api/lightning/hook.ts index 0c0c2b29..9497a33d 100644 --- a/src/api/lightning/hook.ts +++ b/src/api/lightning/hook.ts @@ -21,13 +21,9 @@ export const useLightningByIdQuery = ({ }); }; -export const useLightningListQuery = ({ page, take }: { page: number; take: number }) => { +export const useLightningListQuery = () => { return useQuery({ - queryKey: ['lightningList', page], - queryFn: () => - getLightningList({ - page, - take, - }), + queryKey: ['lightningList'], + queryFn: () => getLightningList(), }); }; diff --git a/src/api/lightning/index.ts b/src/api/lightning/index.ts index d14e4ac9..71b8514b 100644 --- a/src/api/lightning/index.ts +++ b/src/api/lightning/index.ts @@ -43,12 +43,15 @@ export const getLightningById = async (meetingId: number): Promise<GetLightningB }; export type GetLightningListRequest = paths['/meeting/v2']['get']['parameters']['query']; -export type GetLightningListResponse = +export type GetMeetingListResponse = paths['/meeting/v2']['get']['responses']['200']['content']['application/json;charset=UTF-8']; -export const getLightningList = async ({ page, take }: Partial<GetLightningListRequest>) => { - return ( - await api.get<GetLightningListResponse>( - `/meeting/v2?&page=${page}&take=${take}&part=["PM", "DESIGN","IOS","ANDROID","SERVER","WEB"]&category="번쩍"&isOnlyActiveGeneration=false` - ) - ).data; +export const getLightningList = async () => { + const params = { + page: 1, + take: 12, + category: '번쩍', + joinableParts: ['PM', 'DESIGN', 'IOS', 'ANDROID', 'SERVER', 'WEB'].join(','), + isOnlyActiveGeneration: false, + }; + return (await api.get<GetMeetingListResponse>('/meeting/v2', { params })).data; }; diff --git a/src/components/groupBrowsing/GroupBrowsingCard/GroupBrowsingCard.tsx b/src/components/groupBrowsing/GroupBrowsingCard/GroupBrowsingCard.tsx index b6891056..e91b2a9a 100644 --- a/src/components/groupBrowsing/GroupBrowsingCard/GroupBrowsingCard.tsx +++ b/src/components/groupBrowsing/GroupBrowsingCard/GroupBrowsingCard.tsx @@ -12,8 +12,11 @@ import { getResizedImage } from '@utils/image'; import { fontsObject } from '@sopt-makers/fonts'; import { Tag } from '@sopt-makers/ui'; import { IconLocation } from '@sopt-makers/icons'; +import { useLightningByIdQuery } from '@api/lightning/hook'; + +const GroupBrowsingCard: FC<GroupBrowsingCardItem> = ({ id, title, user, imageURL }) => { + const { data: lightningData } = useLightningByIdQuery({ meetingId: +id }); -const GroupBrowsingCard: FC<GroupBrowsingCardItem> = ({ id, title, mStartDate, mEndDate, user, imageURL }) => { const imgSrc = imageURL[0]?.url && getResizedImage(imageURL[0].url, 285); return ( <Link href={`/detail?id=${id}`} style={{ display: 'flex', justifyContent: 'start', width: '305px' }}> @@ -31,31 +34,30 @@ const GroupBrowsingCard: FC<GroupBrowsingCardItem> = ({ id, title, mStartDate, m </SUser> <STitle>{title}</STitle> <SBottom> - <SDesc css={{ color: '$green400' }}>김솝트님 외 8명 신청중이에요</SDesc> - <SDesc>모집 2일 남음</SDesc> + <SDesc + css={{ color: '$green400' }} + >{`김솝트님 외 ${lightningData?.appliedInfo.length}명 신청중이에요`}</SDesc> + <SDesc>{`모집 ${dayjs(lightningData?.endDate).diff(dayjs(), 'day')}일 남음`}</SDesc> </SBottom> <SOverlayContent> <Flex align="center"> <CalendarIcon style={{ marginRight: '6px' }} /> <SDesc> - {dayjs(mStartDate).format('YYYY.MM.DD')} - {dayjs(mEndDate).format('YYYY.MM.DD')} + {dayjs(lightningData?.activityStartDate).format('YYYY.MM.DD')} -{' '} + {dayjs(lightningData?.activityEndDate).format('YYYY.MM.DD')} </SDesc> </Flex> <Flex align="center"> <IconLocation style={{ width: '12px', height: '12px', marginRight: '6px', color: '#9D9DA4' }} /> - <SDesc>건대입구역</SDesc> + <SDesc>{lightningData?.place}</SDesc> </Flex> <SChipWrapper> - <Tag size="sm" shape="pill" variant="secondary" type="solid"> - YB 환영 - </Tag> - <Tag size="sm" shape="pill" variant="secondary" type="solid"> - 입문자 환영 - </Tag> - <Tag size="sm" shape="pill" variant="secondary" type="solid"> - 초면 환영 - </Tag> + {lightningData?.welcomeMessageTypes.map(welcome => ( + <Tag size="sm" shape="pill" variant="secondary" type="solid"> + {welcome} + </Tag> + ))} </SChipWrapper> </SOverlayContent> </SGroupBrowsingCard> diff --git a/src/components/page/home/HomeCardList/index.tsx b/src/components/page/home/HomeCardList/index.tsx index 904c02df..c1b6958f 100644 --- a/src/components/page/home/HomeCardList/index.tsx +++ b/src/components/page/home/HomeCardList/index.tsx @@ -2,14 +2,14 @@ import { RecommendMeetingListQueryResponse, useGetRecommendMeetingListQuery } fr import CardList from '@components/page/home/HomeCardList/CardList'; import { styled } from 'stitches.config'; -const HomeCardList = ({ groupBrowsingCardData }: { groupBrowsingCardData: RecommendMeetingListQueryResponse }) => { +const HomeCardList = ({ inProgressMeetingData }: { inProgressMeetingData: RecommendMeetingListQueryResponse }) => { const { data: recommendMeetings } = useGetRecommendMeetingListQuery({ meetingIds: [359, 360, 361] }); return ( <SWrapper> <SGradationRight /> {recommendMeetings && <CardList label="🔹 우리... 같이 솝커톤 할래?" data={recommendMeetings.slice(0, 3)} />} - <CardList label="🔥 지금 모집중인 모임" data={groupBrowsingCardData.slice(0, 3)} /> + <CardList label="🔥 지금 모집중인 모임" data={inProgressMeetingData.slice(0, 3)} /> {recommendMeetings && ( <CardList label="🍀 1차 행사 신청이 얼마 남지 않았어요!" data={recommendMeetings.slice(0, 3)} /> )}