diff --git a/pages/detail/index.tsx b/pages/detail/index.tsx index 0d61bcd0..189c3293 100644 --- a/pages/detail/index.tsx +++ b/pages/detail/index.tsx @@ -14,31 +14,28 @@ 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'; +import { useLightningByIdQuery } from '@api/lightning/hook'; +import { GetMeetingResponse } from '@api/API_LEGACY/meeting'; +import { GetLightningByIdResponse } from '@api/lightning'; dayjs.locale('ko'); const enum SelectedTab { - FEED, INFORMATION, + FEED, } 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({}); 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; @@ -51,7 +48,7 @@ const DetailPage = () => { document.body.removeChild(script); }, []); - if (!detailData) { + if (!meetingData) { return ( <> @@ -68,6 +65,7 @@ const DetailPage = () => { ); } + const detailData: GetMeetingResponse | GetLightningByIdResponse = lightningData || meetingData; return ( <> @@ -82,18 +80,18 @@ const DetailPage = () => { setSelectedIndex(index)}> - 피드 + 모임 안내 - 모임 안내 + 피드 - + - + 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 ? ( <> ⚡ ️솝트만의 일회성 모임, 번쩍 - {groupBrowsingCardData && } + {lightningList && } ) : ( <> @@ -48,7 +48,7 @@ const Home: NextPage = () => { ⚡ ️솝트만의 일회성 모임, 번쩍 - {groupBrowsingCardData && } + {lightningList && } )} @@ -57,12 +57,12 @@ const Home: NextPage = () => { - {inProgressMeetings && } + {inProgressMeetings && } ) : ( <> - {inProgressMeetings && } + {inProgressMeetings && }
diff --git a/pages/make/bungae/index.tsx b/pages/make/lightning/index.tsx similarity index 81% rename from pages/make/bungae/index.tsx rename to pages/make/lightning/index.tsx index 0c2c3c3d..24df37b7 100644 --- a/pages/make/bungae/index.tsx +++ b/pages/make/lightning/index.tsx @@ -1,5 +1,5 @@ import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'; -import { BungaeFormType, bungaeSchema } 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 { createBungae } from '@api/meeting'; +import { createLightning } from '@api/lightning'; const DevTool = dynamic(() => import('@hookform/devtools').then(module => module.DevTool), { ssr: false, }); -const Bungae = () => { +const Lightning = () => { const router = useRouter(); - const formMethods = useForm({ + const formMethods = useForm({ mode: 'onChange', - resolver: zodResolver(bungaeSchema), + resolver: zodResolver(lightningSchema), }); const { isValid, errors } = formMethods.formState; - const { mutateAsync: mutateCreateBungae, isLoading: isSubmitting } = useMutation({ - mutationFn: (formData: BungaeFormType) => createBungae(formData), + const { mutateAsync: mutateCreateLightning, isLoading: isSubmitting } = useMutation({ + mutationFn: (formData: LightningFormType) => createLightning(formData), onError: () => { alert('번쩍을 개설하지 못했습니다.'); }, @@ -42,11 +42,13 @@ const Bungae = () => { formMethods.setValue('files', files); }; - const onSubmit: SubmitHandler = async formData => { - const bungaeId = await mutateCreateBungae(formData); - ampli.completedMakeGroup(); - alert('번쩍을 개설했습니다.'); - router.push(`/detail?id=${bungaeId}`); + const onSubmit: SubmitHandler = async formData => { + mutateCreateLightning(formData, { + onSuccess: data => { + ampli.completedMakeGroup(); + router.push(`/detail?id=${data}`); + }, + }); }; return ( @@ -78,7 +80,7 @@ const Bungae = () => { ); }; -export default Bungae; +export default Lightning; const SContainer = styled('div', { margin: '80px 0', 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/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 @@ + + + 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 @@ + + + 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 @@ + + + + + + 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 @@ + + + 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 @@ - - - diff --git a/src/__generated__/schema2.d.ts b/src/__generated__/schema2.d.ts index e745c8a1..ddbff094 100644 --- a/src/__generated__/schema2.d.ts +++ b/src/__generated__/schema2.d.ts @@ -176,6 +176,10 @@ export interface paths { /** 모임 둘러보기 조회 */ get: operations["getMeetingBanner"]; }; + "/lightning/v2/{meetingId}": { + /** 번쩍 모임 상세 조회 */ + get: operations["getLightningByMeetingId"]; + }; "/internal/meetings": { /** * [Internal] 모임 전체 조회/검색/필터링 @@ -577,10 +581,10 @@ export interface components { LightningV2CreateLightningResponseDto: { /** * Format: int32 - * @description 번쩍 모임 id + * @description 모임 id - 번쩍 카테고리 * @example 1 */ - lightningId: number; + meetingId: number; }; /** @description 댓글 생성 request body dto */ CommentV2CreateCommentBodyDto: { @@ -1899,6 +1903,122 @@ 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 모임 객체 목록 */ @@ -2896,6 +3016,24 @@ 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 모임 전체 조회/검색/필터링 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/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/lightning/hook.ts b/src/api/lightning/hook.ts new file mode 100644 index 00000000..9497a33d --- /dev/null +++ b/src/api/lightning/hook.ts @@ -0,0 +1,29 @@ +import { getLightningById, GetLightningByIdResponse, getLightningList } from '@api/lightning'; +import { useQuery, UseQueryResult } from '@tanstack/react-query'; +import { AxiosError } from 'axios'; + +type UseLightningByIdQueryProps = { + meetingId: number; +}; +export const useLightningByIdQuery = ({ + meetingId, +}: UseLightningByIdQueryProps): UseQueryResult => { + 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; + } + }, + }); +}; + +export const useLightningListQuery = () => { + return useQuery({ + queryKey: ['lightningList'], + queryFn: () => getLightningList(), + }); +}; diff --git a/src/api/lightning/index.ts b/src/api/lightning/index.ts new file mode 100644 index 00000000..71b8514b --- /dev/null +++ b/src/api/lightning/index.ts @@ -0,0 +1,57 @@ +import { paths } from '@/__generated__/schema2'; +import { api } from '@api/index'; +import { LightningFormType } from '@type/form'; + +export const createLightning = async (formData: LightningFormType) => { + const { + data: { meetingId }, + } = await api.post<{ meetingId: number }>('/lightning/v2', filterLightningFormData(formData)); + return meetingId; +}; + +const filterLightningFormData = (formData: LightningFormType) => { + 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; +}; + +export type GetLightningByIdResponse = + paths['/lightning/v2/{meetingId}']['get']['responses']['200']['content']['application/json;charset=UTF-8']; + +export const getLightningById = async (meetingId: number): Promise => { + return (await api.get(`/lightning/v2/${meetingId}`)).data; +}; + +export type GetLightningListRequest = paths['/meeting/v2']['get']['parameters']['query']; +export type GetMeetingListResponse = + paths['/meeting/v2']['get']['responses']['200']['content']['application/json;charset=UTF-8']; +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('/meeting/v2', { params })).data; +}; diff --git a/src/api/meeting/index.ts b/src/api/meeting/index.ts index df04863e..08266864 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 { LightningFormType } from '@type/form'; import { paths } from '@/__generated__/schema2'; interface PaginationType { page: number; @@ -59,14 +59,14 @@ export const getRecommendMeetingList = async ({ meetingIds = [] }: { meetingIds: return (await api.get(`/meeting/v2/recommend${meetingIdsParams}`, {})).data.meetings; }; -export const createBungae = async (formData: BungaeFormType) => { +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: BungaeFormType) => { +const filterLightningFormData = (formData: LightningFormType) => { const convertedTags = formData.welcomeTags?.map(tag => { return tag?.value; }); 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 { + status: 0 | 1 | 2; +} +const RecruitmentStatusTag = ({ status, style }: RecruitmentStatusTagProps) => { + const tagVariant: ('default' | 'primary' | 'secondary')[] = ['secondary', 'primary', 'default']; + + return ( + + {RECRUITMENT_STATUS[status]} + + ); +}; + +export default RecruitmentStatusTag; 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(comment?.replies?.length); const { parentComment } = useContext(MentionContext); - const recommentRef = useRef(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/Bungae/index.tsx b/src/components/form/Bungae/index.tsx index 6839278d..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 { BungaeFormType, 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 { bungaePlace, bungaeTags, bungaeTime } 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; disabled?: boolean; - errors: FieldErrors; + errors: FieldErrors; } interface FileChangeHandler { imageUrls: string[]; @@ -176,7 +176,7 @@ function Presentation({ name="timeInfo.time" render={({ field: { value, onChange } }) => ( <> - {bungaeTime.map(time => ( + {lightningTime.map(time => ( { @@ -249,7 +249,7 @@ function Presentation({ name="placeInfo.place" render={({ field: { value, onChange } }) => ( <> - {bungaePlace.map(place => ( + {lightningPlace.map(place => ( { @@ -362,7 +362,7 @@ function Presentation({ const selectedTags = Array.isArray(value) ? value : []; return ( <> - {bungaeTags.map(tag => { + {lightningTags.map(tag => { const isActive = selectedTags.some( (selectedTag: { label: string; value: string }) => selectedTag.value === tag.value ); 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/groupBrowsing/GroupBrowsingCard/GroupBrowsingCard.tsx b/src/components/groupBrowsing/GroupBrowsingCard/GroupBrowsingCard.tsx index 514aefc0..e91b2a9a 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,8 +11,12 @@ 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'; +import { useLightningByIdQuery } from '@api/lightning/hook'; + +const GroupBrowsingCard: FC = ({ id, title, user, imageURL }) => { + const { data: lightningData } = useLightningByIdQuery({ meetingId: +id }); -const GroupBrowsingCard: FC = ({ id, title, mStartDate, mEndDate, user, imageURL }) => { const imgSrc = imageURL[0]?.url && getResizedImage(imageURL[0].url, 285); return ( @@ -31,31 +34,30 @@ const GroupBrowsingCard: FC = ({ id, title, mStartDate, m {title} - 김솝트님 외 8명 신청중이에요 - 모집 2일 남음 + {`김솝트님 외 ${lightningData?.appliedInfo.length}명 신청중이에요`} + {`모집 ${dayjs(lightningData?.endDate).diff(dayjs(), 'day')}일 남음`} - {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')} - - 건대입구역 + + {lightningData?.place} - - YB 환영 - - - 입문자 환영 - - - 초면 환영 - + {lightningData?.welcomeMessageTypes.map(welcome => ( + + {welcome} + + ))} diff --git a/src/components/modal/FloatingButtonModal.tsx b/src/components/modal/FloatingButtonModal.tsx index 75b6f07f..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/bungae'); + router.push('/make/lightning'); }; const handleFeedCreateButtonClick = () => { diff --git a/src/components/page/detail/Information/InformationPanel.tsx b/src/components/page/detail/Information/InformationPanel.tsx index 2b92508b..32fceebc 100644 --- a/src/components/page/detail/Information/InformationPanel.tsx +++ b/src/components/page/detail/Information/InformationPanel.tsx @@ -1,103 +1,50 @@ 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 { 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([]); - 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 = + detailData.category === '번쩍' + ? LightningDetailList(detailData as GetLightningByIdResponse) + : MeetingDetailList(detailData as GetMeetingResponse); + 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 +69,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..bbcd0adf --- /dev/null +++ b/src/components/page/detail/Information/constant.tsx @@ -0,0 +1,229 @@ +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'); +import CalendarIcon from '@assets/svg/calendar.svg?rect'; +import { GetLightningByIdResponse } from '@api/lightning'; +import { IconLocation } from '@sopt-makers/icons'; + +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 LightningDetailList = (detailData: GetLightningByIdResponse) => [ + { + key: '#환영 태그', + Title: () => <STitle>#환영 태그</STitle>, + Content: () => { + console.log(detailData.welcomeMessageTypes); + return ( + <STarget> + {detailData?.welcomeMessageTypes.map(tag => ( + <Chip key={tag} style={{ boxShadow: 'none' }} active> + {tag} + </Chip> + ))} + </STarget> + ); + }, + isValid: detailData?.welcomeMessageTypes, + }, + { + key: '설명', + Title: () => <STitle>설명</STitle>, + Content: () => { + return <SDescription>{parseTextToLink(detailData?.desc)}</SDescription>; + }, + isValid: detailData?.desc, + }, + { + key: '진행일', + Title: () => ( + <SIconTitleWrapper> + <SIconCalendar /> + <STitle>진행일</STitle> + </SIconTitleWrapper> + ), + 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> + <SIconLocation /> + <STitle>장소</STitle> + </SIconTitleWrapper> + ), + Content: () => ( + <SDescription style={{ color: 'white' }}>{`${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', + + '@tablet': { + fontStyle: 'H4', + mb: '$20', + }, +}); + +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', + gap: '$12', +}); + +const SDescription = styled('p', { + fontAg: '22_regular_170', + whiteSpace: 'pre-line', + color: '$gray200', + + a: { + textDecoration: 'underline', + wordBreak: 'break-all', + }, + + '@tablet': { + fontStyle: 'B3', + }, +}); + +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', + gap: '$10', + color: '$gray10', + flexWrap: 'wrap', + + mb: '$24', + + '@tablet': { + mb: '$20', + }, +}); diff --git a/src/components/page/detail/MeetingController/LightningAbout.tsx b/src/components/page/detail/MeetingController/LightningAbout.tsx new file mode 100644 index 00000000..2ff02c8d --- /dev/null +++ b/src/components/page/detail/MeetingController/LightningAbout.tsx @@ -0,0 +1,107 @@ +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'; +import RecruitmentStatusTag from '@components/Tag/RecruitmentStatusTag'; + +const LightningAbout = ({ detailData }: { detailData: GetLightningByIdResponse }) => { + const { + title, + status, + endDate, + user: { orgId: hostId, name: hostName, profileImage: hostProfileImage }, + category, + } = detailData; + + return ( + <SAbout> + <SStatusWrapper> + <RecruitmentStatusTag status={status} /> + <SPeriod> + {`~${dayjs(endDate).format('YY.MM.DD')}`} + <Divider /> + {dayjs(endDate).format('HH:mm')} + </SPeriod> + </SStatusWrapper> + <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 SStatusWrapper = styled('div', { + display: 'flex', + gap: '$12', + '@tablet': { + gap: '$8', + }, +}); + +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', + + '@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..0d59445c --- /dev/null +++ b/src/components/page/detail/MeetingController/MeetingAbout.tsx @@ -0,0 +1,101 @@ +import { GetMeetingResponse } from '@api/API_LEGACY/meeting'; +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 { + title, + status, + startDate, + endDate, + user: { orgId: hostId, name: hostName, profileImage: hostProfileImage }, + category, + coMeetingLeaders, + isMentorNeeded, + } = detailData; + + return ( + <SAbout> + <SStatusWrapper> + <RecruitmentStatusTag status={status} /> + <SPeriod> + {dayjs(startDate).format('YY.MM.DD')} - {dayjs(endDate).format('YY.MM.DD')} + </SPeriod> + </SStatusWrapper> + <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 SStatusWrapper = styled('div', { + display: 'flex', + gap: '$12', + '@tablet': { + gap: '$8', + }, +}); + +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', 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..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; @@ -77,9 +76,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/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)} /> )} 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.tsx b/src/components/page/list/Card/DesktopSizeCard.tsx deleted file mode 100644 index 39ab1918..00000000 --- a/src/components/page/list/Card/DesktopSizeCard.tsx +++ /dev/null @@ -1,166 +0,0 @@ -import { Flex } from '@components/util/layout/Flex'; -import { 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'; - -interface CardProps { - meetingData: Omit< - paths['/user/v2/meeting']['get']['responses']['200']['content']['application/json;charset=UTF-8']['meetings'][number], - 'isCoLeader' - > & { isCoLeader?: boolean }; - isAllParts: boolean; -} - -function DesktopSizeCard({ meetingData, isAllParts }: CardProps) { - return ( - <div> - <ImageWrapper> - <SStatus recruitingStatus={meetingData.status}>{RECRUITMENT_STATUS[meetingData.status]}</SStatus> - <SThumbnailImage - css={{ - backgroundImage: `url(${meetingData.imageURL[0]?.url})`, - }} - /> - </ImageWrapper> - - <STitleSection> - <SCategory>{meetingData.category}</SCategory> - <STitle>{meetingData.title}</STitle> - </STitleSection> - - <Flex css={{ mb: '$14' }} align="center"> - <SProfileWrapper> - {meetingData.user.profileImage ? ( - <SProfile src={getResizedImage(meetingData.user.profileImage, 120)} alt="" /> - ) : ( - <ProfileDefaultIcon width={24} height={24} /> - )} - </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> - </div> - ); -} - -export default DesktopSizeCard; -const ImageWrapper = styled('div', { - position: 'relative', -}); -const SThumbnailImage = styled('div', { - width: '380px', - height: '260px', - overflow: 'hidden', - borderRadius: '$12', - backgroundColor: '$gray800', - backgroundSize: 'cover', - backgroundPosition: 'center center', - 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': { - my: '$8', - }, -}); - -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', - width: 'fit-content', - mr: '$8', -}); -const SProfile = styled('img', { - width: '$24', - height: '$24', - borderRadius: '50%', - objectFit: 'cover', - background: '$gray700', -}); - -const SName = styled('p', { - fontStyle: 'T5', -}); -const STitle = styled('p', { - maxWidth: '380px', - fontStyle: 'H2', - mt: '$8', -}); -const SInfoRow = styled(Flex, { - '& + &': { - mt: '$4', - }, -}); -const SInfo = styled('p', { - fontStyle: 'B3', -}); -const SKey = styled(SInfo, { - width: '74px', - color: '$gray500', - mr: '$12', - whiteSpace: 'nowrap', -}); -const SValue = styled(SInfo, { - color: '$gray300', -}); 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..42142e75 --- /dev/null +++ b/src/components/page/list/Card/DesktopSizeCard/CategoryChip.tsx @@ -0,0 +1,58 @@ +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" + 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>)} + </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/constant.ts b/src/components/page/list/Card/DesktopSizeCard/constant.ts new file mode 100644 index 00000000..74813c2d --- /dev/null +++ b/src/components/page/list/Card/DesktopSizeCard/constant.ts @@ -0,0 +1,52 @@ +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 new file mode 100644 index 00000000..7cf34e19 --- /dev/null +++ b/src/components/page/list/Card/DesktopSizeCard/index.tsx @@ -0,0 +1,121 @@ +import { Flex } from '@components/util/layout/Flex'; +import { CategoryKoType } from '@constants/option'; +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 { 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: MeetingListOfFilterResponse['meetings'][0]; +} + +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={detailData.status} style={{ position: 'absolute', top: '16px', left: '16px' }} /> + <SThumbnailImage + css={{ + backgroundImage: `url(${detailData.imageURL[0]?.url})`, + }} + /> + </ImageWrapper> + + <STitleSection> + <CategoryChip + category={detailData.category as CategoryKoType} + welcomeMessage={lightningData ? lightningData.welcomeMessageTypes : []} + /> + <STitle>{meetingData.title}</STitle> + </STitleSection> + + <Flex css={{ mb: '$14' }} align="center"> + <SProfileWrapper> + {meetingData.user.profileImage ? ( + <SProfile src={getResizedImage(meetingData.user.profileImage, 120)} alt="" /> + ) : ( + <ProfileDefaultIcon width={24} height={24} /> + )} + </SProfileWrapper> + <SName>{meetingData.user.name}</SName> + </Flex> + {detailInfo.map(({ label, value }) => ( + <SInfoRow key={label}> + <SKey>{label}</SKey> + <SValue>{value()}</SValue> + </SInfoRow> + ))} + </div> + ); +} + +export default DesktopSizeCard; +const ImageWrapper = styled('div', { + position: 'relative', +}); +const SThumbnailImage = styled('div', { + width: '380px', + height: '260px', + overflow: 'hidden', + borderRadius: '$12', + backgroundColor: '$gray800', + backgroundSize: 'cover', + backgroundPosition: 'center center', + backgroundRepeat: 'no-repeat', +}); + +const STitleSection = styled('div', { + my: '$16', + '@tablet': { + my: '$8', + }, +}); + +const SProfileWrapper = styled('div', { + flexType: 'verticalCenter', + color: '$gray10', + width: 'fit-content', + mr: '$8', +}); +const SProfile = styled('img', { + width: '$24', + height: '$24', + borderRadius: '50%', + objectFit: 'cover', + background: '$gray700', +}); + +const SName = styled('p', { + fontStyle: 'T5', +}); +const STitle = styled('p', { + maxWidth: '380px', + fontStyle: 'H2', + mt: '$8', +}); +const SInfoRow = styled(Flex, { + '& + &': { + mt: '$4', + }, +}); +const SInfo = styled('p', { + fontStyle: 'B3', +}); +const SKey = styled(SInfo, { + width: '74px', + color: '$gray500', + mr: '$12', + whiteSpace: 'nowrap', +}); +const SValue = styled(SInfo, { + color: '$gray300', +}); 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..bc1ef776 100644 --- a/src/components/page/list/Card/MobileSize/index.tsx +++ b/src/components/page/list/Card/MobileSize/index.tsx @@ -1,12 +1,9 @@ +import { 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} /> 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/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 '번쩍': diff --git a/src/data/options.ts b/src/data/options.ts index f20283d5..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 bungaeTime = [ +export const lightningTime = [ { label: '당일', value: '당일' }, { label: '예정 기간 (협의 후 결정)', value: '예정 기간 (협의 후 결정)' }, ]; -export const bungaePlace = [ +export const lightningPlace = [ { label: '오프라인', value: '오프라인' }, { label: '온라인', value: '온라인' }, { label: '협의 후 결정', value: '협의 후 결정' }, ]; -export const bungaeTags = [ +export const lightningTags = [ { label: 'YB 환영', value: 'YB 환영' }, { label: 'OB 환영', value: 'OB 환영' }, { label: '초면 환영', value: '초면 환영' }, 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; diff --git a/src/types/form.ts b/src/types/form.ts index b52a68cd..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 bungaeSchema = z.object({ +export const lightningSchema = 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 LightningFormType = z.infer<typeof lightningSchema>;