From 46bdd9563541cf83fe65bf4f3e86a17e0e8d266d Mon Sep 17 00:00:00 2001 From: Hyeonsu Kim <86764406+borimong@users.noreply.github.com> Date: Mon, 3 Feb 2025 20:48:36 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20any=20=EC=A7=80=EC=9A=B0=EA=B8=B0=20(#1001)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/gtm.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/gtm.ts b/src/utils/gtm.ts index 75a5e036e..613e568cd 100644 --- a/src/utils/gtm.ts +++ b/src/utils/gtm.ts @@ -2,7 +2,7 @@ const dataLayer = typeof window === 'undefined' ? undefined : // eslint-disable-next-line @typescript-eslint/no-explicit-any - (window as any).dataLayer; + (window as typeof window & { dataLayer?: { event: string; page: string }[] }).dataLayer; export const GTM_ID = process.env.NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID; From b7cdb0c8ce9bb78b68ece51d5cff8cf80488c4e7 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Tue, 4 Feb 2025 07:09:49 +0900 Subject: [PATCH 2/6] =?UTF-8?q?=EB=B2=88=EC=A9=8D=20beta=20QA=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20(#1002)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * style: 모임 홈 번쩍 카드 어둡게 * fix: 번쩍 신청 시 refetch refactor: GuestConfirmModal 삭제 (미사용 코드) * refactor: useQuery hook 으로 교체 * feat: 번쩍 수정 API 연결 (api 나올 때까지 잠시 중단) * chore: type에러만 잡아놓기 (수정 잠시 중단) * style: 모임 상세 Tab Navigation 디자인 * style: 번개가 어쩌고 ? * style: 디바이더 align --- pages/edit/flash/index.tsx | 160 ++++++++++++++++++ pages/edit/index.tsx | 12 +- pages/index.tsx | 4 +- .../GroupBrowsingCard/GroupBrowsingCard.tsx | 2 +- .../detail/Information/InformationPanel.tsx | 4 +- .../page/detail/Information/constant.tsx | 3 +- .../detail/MeetingController/FlashAbout.tsx | 3 + .../Modal/Confirm/GuestConfirmModal.tsx | 37 ---- .../page/detail/MeetingController/index.tsx | 44 ++--- 9 files changed, 189 insertions(+), 80 deletions(-) create mode 100644 pages/edit/flash/index.tsx delete mode 100644 src/components/page/detail/MeetingController/Modal/Confirm/GuestConfirmModal.tsx diff --git a/pages/edit/flash/index.tsx b/pages/edit/flash/index.tsx new file mode 100644 index 000000000..9ad4e0480 --- /dev/null +++ b/pages/edit/flash/index.tsx @@ -0,0 +1,160 @@ +import TableOfContents from '@components/form/TableOfContents'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useRouter } from 'next/router'; +import { useEffect } from 'react'; +import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'; +import { updateMeeting } from '@api/API_LEGACY/meeting'; +import { styled } from 'stitches.config'; +import dayjs from 'dayjs'; +import Loader from '@components/@common/loader/Loader'; +import dynamic from 'next/dynamic'; +import { useFlashByIdQuery } from '@api/flash/hook'; +import { FlashFormType, flashSchema } from '@type/form'; +import Presentation from '@components/form/Bungae'; +import BungaeIcon from '@assets/svg/bungae.svg'; + +const DevTool = dynamic(() => import('@hookform/devtools').then(module => module.DevTool), { + ssr: false, +}); + +const EditPage = () => { + const queryClient = useQueryClient(); + const router = useRouter(); + const id = +(router.query.id || 0); + + const { data: formData } = useFlashByIdQuery({ meetingId: id }); + const { mutateAsync, isLoading: isSubmitting } = useMutation({ + // mutationFn: ({ id, formData }: { id: number; formData: FlashFormType }) => updateMeeting(id + '', formData), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['getFlash', id] }); + }, + }); + + const formMethods = useForm({ + mode: 'onChange', + resolver: zodResolver(flashSchema), + }); + + const { isValid, errors } = formMethods.formState; + + const onSubmit: SubmitHandler = async formData => { + try { + // await mutateAsync({ id, formData }); + // TODO: handle success + alert('모임을 수정했습니다.'); + router.push(`/detail?id=${id}`); + } catch (error) { + // TODO: handle error + alert('모임을 수정하지 못했습니다.'); + } + }; + + const handleChangeImage = (index: number, url: string) => { + const files = formMethods.getValues().files.slice(); + files.splice(index, 1, url); + formMethods.setValue('files', files); + }; + + const handleDeleteImage = (index: number) => { + const files = formMethods.getValues().files.slice(); + files.splice(index, 1); + formMethods.setValue('files', files); + }; + + useEffect(() => { + if (!formData) { + return; + } + async function fillForm() { + formMethods.reset({ + ...formData, + title: formData?.title, + desc: formData?.desc, + minCapacity: formData?.minimumCapacity, + maxCapacity: formData?.maximumCapacity, + // flashTimingType: formData?.flashTimingType, + // activityStartDate: dayjs(formData?.activityStartDate).format('YYYY.MM.DD'), + // activityEndDate: dayjs(formData?.activityEndDate).format('YYYY.MM.DD'), + // flashPlaceType: formData?.flashPlaceType, + // flashPlace: formData?.flashPlace, + // welcomeMessageTypes: formData?.welcomeMessageTypes, + // imageURL: formData?.imageURL, + }); + } + + fillForm(); + }, [formMethods, formData]); + + if (!formData) { + return ; + } + + return ( + + + + 번쩍 수정하기 + + + + 번쩍 수정하기 + + } + cancelButtonLabel="수정 취소하기" + handleChangeImage={handleChangeImage} + handleDeleteImage={handleDeleteImage} + onSubmit={formMethods.handleSubmit(onSubmit)} + disabled={isSubmitting || !isValid} + /> + + + + + {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} + {/* @ts-ignore */} + + + ); +}; + +export default EditPage; + +const SContainer = styled('div', { + margin: '80px 0', + display: 'flex', + gap: '30px', + + '@tablet': { + margin: 0, + }, +}); +const SFormContainer = styled('div', { + width: '100%', + padding: '44px 40px 56px', + borderRadius: '15px', + + '@tablet': { + padding: '40px 0 0 0', + background: '$gray950', + }, +}); +const SFormName = styled('h1', { + fontAg: '24_bold_100', + color: '$gray10', + marginBottom: '90px', + + '@tablet': { + margin: 0, + paddingBottom: '40px', + borderBottom: '1px solid $gray700', + }, +}); +const SFormWrapper = styled('div', { + '@tablet': { + paddingTop: '40px', + }, +}); diff --git a/pages/edit/index.tsx b/pages/edit/index.tsx index b545a5c89..66cf76d8b 100644 --- a/pages/edit/index.tsx +++ b/pages/edit/index.tsx @@ -1,11 +1,11 @@ import Presentation from '@components/form/Presentation'; import TableOfContents from '@components/form/TableOfContents'; import { zodResolver } from '@hookform/resolvers/zod'; -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useRouter } from 'next/router'; import { useEffect } from 'react'; import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'; -import { getMeeting, updateMeeting } from '@api/API_LEGACY/meeting'; +import { updateMeeting } from '@api/API_LEGACY/meeting'; import { FormType, schema } from '@type/form'; import { styled } from 'stitches.config'; import dayjs from 'dayjs'; @@ -13,6 +13,7 @@ import Loader from '@components/@common/loader/Loader'; import CheckIcon from '@assets/svg/check.svg'; import dynamic from 'next/dynamic'; import { parts } from '@data/options'; +import { useQueryGetMeeting } from '@api/API_LEGACY/meeting/hooks'; const DevTool = dynamic(() => import('@hookform/devtools').then(module => module.DevTool), { ssr: false, }); @@ -22,12 +23,7 @@ const EditPage = () => { const router = useRouter(); const id = router.query.id as string; - const query = useQuery({ - queryKey: ['meeting', id], - queryFn: () => getMeeting(id), - enabled: !!id, - }); - const { data: formData } = query; + const { data: formData } = useQueryGetMeeting({ params: { id } }); const { mutateAsync, isLoading: isSubmitting } = useMutation({ mutationFn: ({ id, formData }: { id: string; formData: FormType }) => updateMeeting(id, formData), onSuccess: () => { diff --git a/pages/index.tsx b/pages/index.tsx index e9fbe5b9b..e471114a8 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -39,13 +39,13 @@ const Home: NextPage = () => { {isLoading && (isTablet ? : )} {isTablet ? ( <> - ⚡ ️솝트만의 일회성 모임, 번쩍 + ⚡ 솝트만의 일회성 모임, 번쩍 {flashList && } ) : ( <> - ⚡ ️솝트만의 일회성 모임, 번쩍 + ⚡ 솝트만의 일회성 모임, 번쩍 {flashList && } diff --git a/src/components/groupBrowsing/GroupBrowsingCard/GroupBrowsingCard.tsx b/src/components/groupBrowsing/GroupBrowsingCard/GroupBrowsingCard.tsx index 4ca79da1f..28542df70 100644 --- a/src/components/groupBrowsing/GroupBrowsingCard/GroupBrowsingCard.tsx +++ b/src/components/groupBrowsing/GroupBrowsingCard/GroupBrowsingCard.tsx @@ -34,7 +34,7 @@ const GroupBrowsingCard: FC = ({ id, title, user, imageUR { {isMobile && ( {detailList.map( - ({ key, Title, isValid }) => + ({ key, isValid }) => isValid && ( - + {key} </TabList.Item> ) )} diff --git a/src/components/page/detail/Information/constant.tsx b/src/components/page/detail/Information/constant.tsx index 544b1984f..19061f994 100644 --- a/src/components/page/detail/Information/constant.tsx +++ b/src/components/page/detail/Information/constant.tsx @@ -66,10 +66,9 @@ export const MeetingDetailList = (detailData: GetMeetingResponse) => [ export const FlashDetailList = (detailData: GetFlashByIdResponse) => [ { - key: '#환영 태그', + key: '환영 태그', Title: () => <STitle>#환영 태그</STitle>, Content: () => { - console.log(detailData.welcomeMessageTypes); return ( <STarget> {detailData?.welcomeMessageTypes.map(tag => ( diff --git a/src/components/page/detail/MeetingController/FlashAbout.tsx b/src/components/page/detail/MeetingController/FlashAbout.tsx index 03f893388..223484881 100644 --- a/src/components/page/detail/MeetingController/FlashAbout.tsx +++ b/src/components/page/detail/MeetingController/FlashAbout.tsx @@ -90,6 +90,9 @@ const Divider = styled('hr', { }); const SPeriod = styled('div', { + display: 'flex', + alignItems: 'center', + fontAg: '20_bold_100', color: '$gray300', diff --git a/src/components/page/detail/MeetingController/Modal/Confirm/GuestConfirmModal.tsx b/src/components/page/detail/MeetingController/Modal/Confirm/GuestConfirmModal.tsx deleted file mode 100644 index 421e64143..000000000 --- a/src/components/page/detail/MeetingController/Modal/Confirm/GuestConfirmModal.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import ConfirmModal from '@components/modal/ConfirmModal'; - -interface GuestConfirmModalProps { - isModalOpened: boolean; - message: string; - handleModalClose: () => void; - handleConfirm: () => void; - cancelButtonDisabled?: boolean; - confirmButtonDisabled?: boolean; - isSubmitting?: boolean; -} - -const GuestConfirmModal = ({ - isModalOpened, - message, - handleModalClose, - handleConfirm, - cancelButtonDisabled, - confirmButtonDisabled, - isSubmitting, -}: GuestConfirmModalProps) => { - return ( - <ConfirmModal - isModalOpened={isModalOpened} - message={message} - cancelButton="돌아가기" - confirmButton="취소하기" - handleModalClose={handleModalClose} - handleConfirm={handleConfirm} - cancelButtonDisabled={cancelButtonDisabled} - confirmButtonDisabled={confirmButtonDisabled} - isSubmitting={isSubmitting} - /> - ); -}; - -export default GuestConfirmModal; diff --git a/src/components/page/detail/MeetingController/index.tsx b/src/components/page/detail/MeetingController/index.tsx index 2d4a731e3..45d10789d 100644 --- a/src/components/page/detail/MeetingController/index.tsx +++ b/src/components/page/detail/MeetingController/index.tsx @@ -8,7 +8,6 @@ import { playgroundLink } from '@sopt-makers/playground-common'; import useModal from '@hooks/useModal'; import DefaultModal from '@components/modal/DefaultModal'; import ProfileConfirmModal from './Modal/Confirm/ProfileConfirmModal'; -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'; @@ -70,20 +69,14 @@ const MeetingController = ({ mutateApplicationDeletion, }: DetailHeaderProps) => { const isFlash = detailData.category === '번쩍'; - const { status, category, appliedInfo, approved, host: isHost, apply: isApplied } = detailData; + const { status, category, appliedInfo, approved, host: isHost, apply: isApplied, id: meetingId } = detailData; const { open: dialogOpen, close: dialogClose } = useDialog(); const { data: me } = useQueryMyProfile(); const queryClient = useQueryClient(); const router = useRouter(); - const meetingId = router.query.id; const isRecruiting = status === ERecruitmentStatus.RECRUITING; const { mutate: mutateEventApplication } = useMutationPostEventApplication({}); - const { - isModalOpened: isGuestModalOpened, - handleModalOpen: handleGuestModalOpen, - handleModalClose: handleGuestModalClose, - } = useModal(); const { isModalOpened: isProfileModalOpened, handleModalOpen: handleProfileModalOpen, @@ -130,10 +123,7 @@ const MeetingController = ({ const handleApplicationModal = () => { if (!isApplied) { ampli.clickRegisterGroup({ user_id: Number(me?.orgId) }); - //handleDefaultModalOpen(); - //setModalTitle('모임 신청하기'); handleApplicationButton('No resolution'); - return; } @@ -163,7 +153,7 @@ const MeetingController = ({ { onSuccess: async () => { await queryClient.refetchQueries({ - queryKey: ['getMeeting', meetingId as string], + queryKey: ['getMeeting', meetingId + ''], }); dialogOpen({ title: '신청 완료 되었습니다', @@ -177,7 +167,7 @@ const MeetingController = ({ }, onError: async (error: AxiosError) => { await queryClient.refetchQueries({ - queryKey: ['getMeeting', meetingId as string], + queryKey: ['getMeeting', meetingId + ''], }); const errorResponse = error.response as AxiosResponse; dialogOpen({ @@ -197,7 +187,10 @@ const MeetingController = ({ { onSuccess: async () => { await queryClient.refetchQueries({ - queryKey: ['getMeeting', meetingId as string], + queryKey: ['getMeeting', meetingId + ''], + }); + await queryClient.refetchQueries({ + queryKey: ['getFlash', meetingId], }); dialogOpen({ title: '신청 완료 되었습니다', @@ -211,7 +204,10 @@ const MeetingController = ({ }, onError: async (error: AxiosError) => { await queryClient.refetchQueries({ - queryKey: ['getMeeting', meetingId as string], + queryKey: ['getMeeting', meetingId + ''], + }); + await queryClient.refetchQueries({ + queryKey: ['getFlash', meetingId], }); const errorResponse = error.response as AxiosResponse; dialogOpen({ @@ -233,20 +229,21 @@ const MeetingController = ({ mutateApplicationDeletion(Number(meetingId), { onSuccess: async () => { await queryClient.refetchQueries({ - queryKey: ['getMeeting', meetingId as string], + queryKey: ['getMeeting', meetingId + ''], + }); + await queryClient.refetchQueries({ + queryKey: ['getFlash', meetingId], }); alert('신청이 취소됐습니다!'); setIsSubmitting(false); - handleGuestModalClose(); }, onError: async (error: AxiosError) => { await queryClient.refetchQueries({ - queryKey: ['getMeeting', meetingId as string], + queryKey: ['getMeeting', meetingId + ''], }); const errorResponse = error.response as AxiosResponse; alert(errorResponse.data.errorCode); setIsSubmitting(false); - handleGuestModalClose(); }, }); }; @@ -289,15 +286,6 @@ const MeetingController = ({ handleModalClose={handleProfileModalClose} handleConfirm={() => (window.location.href = `${playgroundLink.memberUpload()}`)} /> - <GuestConfirmModal - isModalOpened={isGuestModalOpened} - message="신청을 취소하시겠습니까?" - handleModalClose={handleGuestModalClose} - handleConfirm={handleCancelApplication} - cancelButtonDisabled={isSubmitting} - confirmButtonDisabled={isSubmitting} - isSubmitting={isSubmitting} - /> <DefaultModal isModalOpened={isDefaultModalOpened} title={modalTitle} From 1121615ec1649a65fdc6c06a20158fc81c6282eb Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Tue, 4 Feb 2025 14:58:27 +0900 Subject: [PATCH 3/6] =?UTF-8?q?=EB=B2=88=EC=A9=8D=20=EC=88=98=EC=A0=95=20A?= =?UTF-8?q?PI=20=EC=97=B0=EA=B2=B0=20(#1006)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * style: 모임 홈 번쩍 카드 어둡게 * fix: 번쩍 신청 시 refetch refactor: GuestConfirmModal 삭제 (미사용 코드) * refactor: useQuery hook 으로 교체 * feat: 번쩍 수정 API 연결 (api 나올 때까지 잠시 중단) * chore: type에러만 잡아놓기 (수정 잠시 중단) * style: 모임 상세 Tab Navigation 디자인 * style: 번개가 어쩌고 ? * style: 디바이더 align * feat: 번쩍 수정 API 연결 * feat: place, time state 연결 --- pages/edit/flash/index.tsx | 42 ++++++++++++------- pages/make/flash/index.tsx | 2 +- src/api/flash/index.ts | 7 ++++ .../form/{Bungae => Flash}/index.tsx | 9 +++- .../page/detail/MeetingController/index.tsx | 2 +- 5 files changed, 42 insertions(+), 20 deletions(-) rename src/components/form/{Bungae => Flash}/index.tsx (98%) diff --git a/pages/edit/flash/index.tsx b/pages/edit/flash/index.tsx index 9ad4e0480..8d967a04c 100644 --- a/pages/edit/flash/index.tsx +++ b/pages/edit/flash/index.tsx @@ -1,31 +1,30 @@ -import TableOfContents from '@components/form/TableOfContents'; import { zodResolver } from '@hookform/resolvers/zod'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useRouter } from 'next/router'; import { useEffect } from 'react'; import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'; -import { updateMeeting } from '@api/API_LEGACY/meeting'; import { styled } from 'stitches.config'; -import dayjs from 'dayjs'; import Loader from '@components/@common/loader/Loader'; import dynamic from 'next/dynamic'; import { useFlashByIdQuery } from '@api/flash/hook'; import { FlashFormType, flashSchema } from '@type/form'; -import Presentation from '@components/form/Bungae'; +import Presentation from '@components/form/Flash'; import BungaeIcon from '@assets/svg/bungae.svg'; +import { updateFlashById } from '@api/flash'; +import dayjs from 'dayjs'; const DevTool = dynamic(() => import('@hookform/devtools').then(module => module.DevTool), { ssr: false, }); -const EditPage = () => { +const FlashEditPage = () => { const queryClient = useQueryClient(); const router = useRouter(); const id = +(router.query.id || 0); const { data: formData } = useFlashByIdQuery({ meetingId: id }); const { mutateAsync, isLoading: isSubmitting } = useMutation({ - // mutationFn: ({ id, formData }: { id: number; formData: FlashFormType }) => updateMeeting(id + '', formData), + mutationFn: ({ id, formData }: { id: number; formData: FlashFormType }) => updateFlashById({ id, formData }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['getFlash', id] }); }, @@ -40,7 +39,7 @@ const EditPage = () => { const onSubmit: SubmitHandler<FlashFormType> = async formData => { try { - // await mutateAsync({ id, formData }); + await mutateAsync({ id, formData }); // TODO: handle success alert('모임을 수정했습니다.'); router.push(`/detail?id=${id}`); @@ -71,15 +70,25 @@ const EditPage = () => { ...formData, title: formData?.title, desc: formData?.desc, + timeInfo: { + time: { + label: formData?.flashTimingType, + value: formData?.flashTimingType, + }, + startDate: dayjs(formData?.activityStartDate).format('YYYY.MM.DD'), + endDate: dayjs(formData?.activityEndDate).format('YYYY.MM.DD'), + }, + placeInfo: { + place: { + label: formData?.flashPlaceType, + value: formData?.flashPlaceType, + }, + placeDetail: formData?.flashPlace, + }, minCapacity: formData?.minimumCapacity, maxCapacity: formData?.maximumCapacity, - // flashTimingType: formData?.flashTimingType, - // activityStartDate: dayjs(formData?.activityStartDate).format('YYYY.MM.DD'), - // activityEndDate: dayjs(formData?.activityEndDate).format('YYYY.MM.DD'), - // flashPlaceType: formData?.flashPlaceType, - // flashPlace: formData?.flashPlace, - // welcomeMessageTypes: formData?.welcomeMessageTypes, - // imageURL: formData?.imageURL, + files: formData?.imageURL.map(({ url }) => url), + welcomeTags: formData?.welcomeMessageTypes.map(type => ({ label: type, value: type })), }); } @@ -109,10 +118,11 @@ const EditPage = () => { handleDeleteImage={handleDeleteImage} onSubmit={formMethods.handleSubmit(onSubmit)} disabled={isSubmitting || !isValid} + placeType={formData.flashPlaceType as '협의 후 결정' | '오프라인' | '온라인'} + timeType={formData.flashTimingType as '당일' | '예정 기간 (협의 후 결정)'} /> </SFormWrapper> </SFormContainer> - <TableOfContents label="모임 수정" /> </SContainer> {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} {/* @ts-ignore */} @@ -121,7 +131,7 @@ const EditPage = () => { ); }; -export default EditPage; +export default FlashEditPage; const SContainer = styled('div', { margin: '80px 0', diff --git a/pages/make/flash/index.tsx b/pages/make/flash/index.tsx index 77047b719..7c86812f0 100644 --- a/pages/make/flash/index.tsx +++ b/pages/make/flash/index.tsx @@ -9,7 +9,7 @@ import dynamic from 'next/dynamic'; import { ampli } from '@/ampli'; import { fontsObject } from '@sopt-makers/fonts'; import { colors } from '@sopt-makers/colors'; -import Presentation from '@components/form/Bungae'; +import Presentation from '@components/form/Flash'; import { createFlash } from '@api/flash'; const DevTool = dynamic(() => import('@hookform/devtools').then(module => module.DevTool), { diff --git a/src/api/flash/index.ts b/src/api/flash/index.ts index 4ed4a8004..945c251b6 100644 --- a/src/api/flash/index.ts +++ b/src/api/flash/index.ts @@ -54,3 +54,10 @@ export const getFlashList = async () => { }; return (await api.get<GetMeetingListResponse>('/meeting/v2', { params })).data; }; + +export const updateFlashById = async ({ id, formData }: { id: number; formData: FlashFormType }) => { + const { + data: { meetingId }, + } = await api.put(`/flash/v2/${id}`, filterFlashFormData(formData)); + return meetingId; +}; diff --git a/src/components/form/Bungae/index.tsx b/src/components/form/Flash/index.tsx similarity index 98% rename from src/components/form/Bungae/index.tsx rename to src/components/form/Flash/index.tsx index bf768bf4a..0adfdc9c1 100644 --- a/src/components/form/Bungae/index.tsx +++ b/src/components/form/Flash/index.tsx @@ -26,7 +26,10 @@ interface PresentationProps { onSubmit: React.FormEventHandler<HTMLFormElement>; disabled?: boolean; errors: FieldErrors<FlashFormType>; + placeType?: '오프라인' | '온라인' | '협의 후 결정' | null; + timeType?: '당일' | '예정 기간 (협의 후 결정)' | null; } + interface FileChangeHandler { imageUrls: string[]; onChange: (urls: string[]) => void; @@ -51,11 +54,13 @@ function Presentation({ onSubmit, disabled = true, errors, + placeType = null, + timeType = null, }: PresentationProps) { const router = useRouter(); const { open } = useDialog(); - const [placeState, setPlaceState] = useState<'오프라인' | '온라인' | '협의 후 결정' | null>(null); - const [timeState, setTimeState] = useState<'당일' | '예정 기간 (협의 후 결정)' | null>(null); + const [placeState, setPlaceState] = useState<'오프라인' | '온라인' | '협의 후 결정' | null>(placeType); + const [timeState, setTimeState] = useState<'당일' | '예정 기간 (협의 후 결정)' | null>(timeType); const isEdit = router.asPath.includes('/edit'); const formRef = useRef<HTMLFormElement>(null); diff --git a/src/components/page/detail/MeetingController/index.tsx b/src/components/page/detail/MeetingController/index.tsx index 45d10789d..f51a90e79 100644 --- a/src/components/page/detail/MeetingController/index.tsx +++ b/src/components/page/detail/MeetingController/index.tsx @@ -276,7 +276,7 @@ const MeetingController = ({ {isHost && ( <SHostButtonContainer> <button onClick={handleHostModalOpen}>삭제</button> - <Link href={`/edit?id=${meetingId}`}>수정</Link> + <Link href={isFlash ? `/edit/flash?id=${meetingId}` : `/edit?id=${meetingId}`}>수정</Link> </SHostButtonContainer> )} </div> From 0b3635769e96891e2781ac413286b0bcbf7d27bd Mon Sep 17 00:00:00 2001 From: Hyeonsu Kim <86764406+borimong@users.noreply.github.com> Date: Wed, 5 Feb 2025 16:00:50 +0900 Subject: [PATCH 4/6] =?UTF-8?q?[=20Feature=20]=20=EB=B2=88=EC=A9=8D=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A4=20=EC=8B=9C=20=EC=B5=9C=EB=8C=80=20=EC=9D=B8?= =?UTF-8?q?=EC=9B=90=EC=9D=B4=20=EC=B5=9C=EC=86=8C=20=EC=9D=B8=EC=9B=90?= =?UTF-8?q?=EB=B3=B4=EB=8B=A4=20=EC=9E=91=EC=9D=84=20=EB=95=8C=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=B6=9C=EB=A0=A5?= =?UTF-8?q?=EC=8B=9C=ED=82=A4=EA=B8=B0=20(#1004)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 필요없는 any 지우기 * feat: 최대 인원이 최소 인원보다 작을 때 validation 검사 후 에러 메시지 출력 * chore: 맞춤법 수정, 오타 수정 * fix: 임시로 빌드 에러 수정 * fix: 빌드 에러 임시 수정 * refactor: 리뷰 반영 (공통 스키마 작성) * fix: edit 페이지 스키마 구조 수정 * fix: 수정 시 기본값이 undefined 값일 수 있으므로 .nullable() 로 변경 * chore: 후에 undefined 값으로 바뀔 수도 있음을 대비하여 optional() 추가 --- pages/edit/flash/index.tsx | 6 ++-- src/api/flash/index.ts | 4 +-- src/api/meeting/index.ts | 4 +-- src/components/form/Flash/index.tsx | 10 ++++--- src/types/form.ts | 44 ++++++++++++++++------------- 5 files changed, 38 insertions(+), 30 deletions(-) diff --git a/pages/edit/flash/index.tsx b/pages/edit/flash/index.tsx index 8d967a04c..d072efcc0 100644 --- a/pages/edit/flash/index.tsx +++ b/pages/edit/flash/index.tsx @@ -85,8 +85,10 @@ const FlashEditPage = () => { }, placeDetail: formData?.flashPlace, }, - minCapacity: formData?.minimumCapacity, - maxCapacity: formData?.maximumCapacity, + capacityInfo: { + minCapacity: formData?.minimumCapacity, + maxCapacity: formData?.maximumCapacity, + }, files: formData?.imageURL.map(({ url }) => url), welcomeTags: formData?.welcomeMessageTypes.map(type => ({ label: type, value: type })), }); diff --git a/src/api/flash/index.ts b/src/api/flash/index.ts index 945c251b6..19eb89f5a 100644 --- a/src/api/flash/index.ts +++ b/src/api/flash/index.ts @@ -25,8 +25,8 @@ const filterFlashFormData = (formData: FlashFormType) => { activityEndDate: convertedEndDate, flashPlaceType: formData.placeInfo.place.value, flashPlace: convertedFlashPlace, - minimumCapacity: formData.minCapacity, - maximumCapacity: formData.maxCapacity, + minimumCapacity: formData.capacityInfo.minCapacity, + maximumCapacity: formData.capacityInfo.maxCapacity, files: formData.files, }, welcomeMessageTypes: convertedTags?.length === 0 ? null : convertedTags, diff --git a/src/api/meeting/index.ts b/src/api/meeting/index.ts index e6a35c703..8ae50aa4c 100644 --- a/src/api/meeting/index.ts +++ b/src/api/meeting/index.ts @@ -82,8 +82,8 @@ const filterFlashFormData = (formData: FlashFormType) => { activityEndDate: convertedEndDate, flashPlaceType: formData.placeInfo.place.value, flashPlace: convertedFlashPlace, - minimumCapacity: formData.minCapacity, - maximumCapacity: formData.maxCapacity, + minimumCapacity: formData.capacityInfo.minCapacity, + maximumCapacity: formData.capacityInfo.maxCapacity, files: formData.files, }, welcomeMessageTypes: convertedTags?.length === 0 ? null : convertedTags, diff --git a/src/components/form/Flash/index.tsx b/src/components/form/Flash/index.tsx index 0adfdc9c1..403b6572d 100644 --- a/src/components/form/Flash/index.tsx +++ b/src/components/form/Flash/index.tsx @@ -303,7 +303,7 @@ function Presentation({ <HelpMessage>번쩍이 진행될 수 있는 최소 인원~최대 인원을 입력해주세요 (개설자 제외)</HelpMessage> <SPeopleWrapper> <FormController - name="minCapacity" + name="capacityInfo.minCapacity" render={({ field, fieldState: { error } }) => ( <TextInput type="number" @@ -323,7 +323,7 @@ function Presentation({ )} ></FormController> <FormController - name="maxCapacity" + name="capacityInfo.maxCapacity" render={({ field, fieldState: { error } }) => ( <TextInput type="number" @@ -343,8 +343,10 @@ function Presentation({ )} ></FormController> </SPeopleWrapper> - {(errors.minCapacity || errors.maxCapacity) && ( - <SErrorMessage>{errors.minCapacity?.message || errors.maxCapacity?.message}</SErrorMessage> + {(errors.capacityInfo?.minCapacity || errors.capacityInfo?.maxCapacity) && ( + <SErrorMessage> + {errors.capacityInfo?.minCapacity?.message || errors.capacityInfo?.maxCapacity?.message} + </SErrorMessage> )} </div> diff --git a/src/types/form.ts b/src/types/form.ts index ce6bab017..d038bc1e8 100644 --- a/src/types/form.ts +++ b/src/types/form.ts @@ -3,6 +3,11 @@ import { z } from 'zod'; import customParseFormat from 'dayjs/plugin/customParseFormat'; dayjs.extend(customParseFormat); +const capacitySchema = z.number({ + required_error: '모집 인원을 입력해주세요.', + invalid_type_error: '모집 인원을 입력해주세요.', +}); + export const schema = z.object({ title: z .string() @@ -32,12 +37,7 @@ export const schema = z.object({ .refine(datetime => dayjs(datetime, 'YYYY.MM.DD').isValid(), { message: 'YYYY.MM.DD 형식으로 입력해주세요.', }), - capacity: z - .number({ - required_error: '모집 인원을 입력해주세요.', - invalid_type_error: '모집 인원을 입력해주세요.', - }) - .gt(0, { message: '0보다 큰 값을 입력해주세요.' }), + capacity: capacitySchema.gt(0, { message: '0보다 큰 값을 입력해주세요.' }), detail: z.object({ desc: z .string() @@ -129,7 +129,7 @@ export const flashSchema = z.object({ label: z.string(), value: z.string(), }), - placeDetail: z.string().optional(), + placeDetail: z.string().nullable().optional(), }) .refine(data => { if (data.place.label === '오프라인' || data.place.label === '온라인') { @@ -139,20 +139,24 @@ export const flashSchema = z.object({ } return false; }), - minCapacity: z - .number({ - required_error: '모집 인원을 입력해주세요.', - invalid_type_error: '모집 인원을 입력해주세요.', - }) - .gt(0, { message: '0보다 큰 값을 입력해주세요.' }) - .lte(999, { message: '모집 인원을 다시 입력해주세요.' }), - maxCapacity: z - .number({ - required_error: '모집 인원을 입력해주세요.', - invalid_type_error: '모집 인원을 입력해주세요.', + capacityInfo: z + .object({ + minCapacity: capacitySchema + .gt(0, { message: '0보다 큰 값을 입력해주세요.' }) + .lte(999, { message: '모집 인원을 다시 입력해주세요.' }), + maxCapacity: capacitySchema + .gt(0, { message: '0보다 큰 값을 입력해주세요.' }) + .lte(999, { message: '모집 인원을 다시 입력해주세요.' }), }) - .gt(0, { message: '0보다 큰 값을 입력해주세요.' }) - .lte(999, { message: '모집 인원을 다시 입력해주세요.' }), + .superRefine(({ minCapacity, maxCapacity }, ctx) => { + if (minCapacity > maxCapacity) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: '최소 인원수가 최대 인원수보다 큽니다.', + path: ['maxCapacity'], + }); + } + }), files: z.array(z.string()), welcomeTags: z .array( From 9ce8f387b76f5d0090a9fa459187e2382b90e3de Mon Sep 17 00:00:00 2001 From: Kong <155794105+ocahs9@users.noreply.github.com> Date: Wed, 5 Feb 2025 16:24:18 +0900 Subject: [PATCH 5/6] =?UTF-8?q?=EC=A0=84=EC=B2=B4=20=EB=AA=A8=EC=9E=84=20?= =?UTF-8?q?=ED=95=84=ED=84=B0=20=EC=9E=91=EC=97=85=20=EB=B0=8F=20UI=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=9E=91=EC=97=85=20(=ED=95=84=ED=84=B0?= =?UTF-8?q?=20=EB=AA=A8=EB=8B=AC=20=EC=82=AD=EC=A0=9C)=20(#997)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 공지 box 제거 * design: arrow 버튼 모양 변경 * feat: 전체 칩 추가 * feat: useMediaQuery 훅 추가, 카테고리 바깥으로 이동 * feat: 필요없는 ui 제거 * feat: 파트 드롭다운 필터 구현 * chore: 주석 제거 * fix: useMediaQuery 훅 삭제, useDisplay로 변경 * style: CATEGORY_OPTIONS 변경으로 인한 스타일 변경 처리 --- package.json | 2 +- pages/list/index.tsx | 27 +++++++---- public/assets/svg/arrow_right_circle.svg | 4 ++ .../page/list/Filter/DropDown/index.tsx | 48 +++++++++++++++++++ .../page/list/Filter/Modal/Chip/ChipItem.tsx | 15 ++++-- .../page/list/Filter/Modal/Chip/index.tsx | 16 ++++--- .../page/list/Filter/Modal/Toggle/index.tsx | 2 +- .../page/list/Filter/Modal/index.tsx | 11 +++-- .../page/list/Filter/Result/index.tsx | 1 + .../page/list/Filter/Search/index.tsx | 2 +- src/components/page/list/Filter/index.tsx | 19 ++++---- src/constants/option.ts | 5 +- src/hooks/queryString/index.ts | 13 +++++ src/hooks/useDisplay.ts | 1 + 14 files changed, 128 insertions(+), 38 deletions(-) create mode 100644 public/assets/svg/arrow_right_circle.svg create mode 100644 src/components/page/list/Filter/DropDown/index.tsx diff --git a/package.json b/package.json index 73b1d9b41..14141630a 100644 --- a/package.json +++ b/package.json @@ -93,4 +93,4 @@ "workspaces": [ "packages/*" ] -} \ No newline at end of file +} diff --git a/pages/list/index.tsx b/pages/list/index.tsx index 6a22959f2..a4f50257d 100644 --- a/pages/list/index.tsx +++ b/pages/list/index.tsx @@ -9,7 +9,6 @@ import Filter from '@components/page/list/Filter'; import Search from '@components/page/list/Filter/Search'; import GridLayout from '@components/page/list/Grid/Layout'; import { MeetingListOfAll } from '@components/page/list/Grid/List'; -import NoticeSlider from '@components/page/list/Slider/NoticeSlider/NoticeSlider'; import { SSRSafeSuspense } from '@components/util/SSRSafeSuspense'; import useModal from '@hooks/useModal'; import { playgroundLink } from '@sopt-makers/playground-common'; @@ -18,12 +17,15 @@ import { useRouter } from 'next/router'; import { useEffect } from 'react'; import { styled } from 'stitches.config'; import CrewTab from '@components/CrewTab'; +import Chips from '@components/page/list/Filter/Modal/Chip'; +import { CATEGORY_FILTER } from '@constants/option'; const Home: NextPage = () => { const router = useRouter(); const { data: me } = useQueryMyProfile(); const { isModalOpened, handleModalOpen, handleModalClose } = useModal(); - const { data: notices } = useNotices(); + + const categoryFilterStyle = { display: 'flex', alignItems: 'flex-start', gap: '$12', alignSelf: 'stretch' }; const handleMakeMeeting = () => { if (!me?.hasActivities) { @@ -61,12 +63,12 @@ const Home: NextPage = () => { </SMakeMeetingButton> </CrewTab> - {/*Notice 슬라이더*/} - <SNoticeWrapper> - <NoticeSlider notices={notices} /> - </SNoticeWrapper> + {/*카테고리 필터 칩*/} + <SChipWrapper> + <Chips css={categoryFilterStyle} filter={CATEGORY_FILTER} /> + </SChipWrapper> - {/*필터 - 필터, 모임 검색, 모임 신청 가이드, 필터 적용 후 생기는 FLEX 박스(chip 모임)*/} + {/*필터 - 모임 검색, 드롭다운, 토글, 모임 신청 가이드*/} <SFilterWrapper> <Filter /> </SFilterWrapper> @@ -136,10 +138,10 @@ const SMobileButtonContainer = styled('div', { }); const SFilterWrapper = styled('div', { - mt: '$40', + mt: '$20', mb: '$64', '@tablet': { - mt: '$32', + mt: '$16', mb: '$24', }, }); @@ -150,3 +152,10 @@ const SNoticeWrapper = styled('div', { mt: '$28', }, }); + +const SChipWrapper = styled('div', { + mt: '$45', + '@tablet': { + mt: '$32', + }, +}); diff --git a/public/assets/svg/arrow_right_circle.svg b/public/assets/svg/arrow_right_circle.svg new file mode 100644 index 000000000..faf0b90c2 --- /dev/null +++ b/public/assets/svg/arrow_right_circle.svg @@ -0,0 +1,4 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28" fill="none"> + <circle cx="14" cy="14" r="14" fill="#3F3F47"/> + <path fill-rule="evenodd" clip-rule="evenodd" d="M11.4921 19.3371C11.1016 18.9466 11.1016 18.3134 11.4921 17.9229L15.4517 13.9633L11.4921 10.0038C11.1016 9.61325 11.1016 8.98009 11.4921 8.58957C11.8826 8.19904 12.5158 8.19904 12.9063 8.58957L16.8659 12.5491C17.6469 13.3302 17.6469 14.5965 16.8659 15.3776L12.9063 19.3371C12.5158 19.7276 11.8826 19.7276 11.4921 19.3371Z" fill="#FCFCFC"/> +</svg> \ No newline at end of file diff --git a/src/components/page/list/Filter/DropDown/index.tsx b/src/components/page/list/Filter/DropDown/index.tsx new file mode 100644 index 000000000..2fa214923 --- /dev/null +++ b/src/components/page/list/Filter/DropDown/index.tsx @@ -0,0 +1,48 @@ +import { ampli } from '@/ampli'; +import { FilterType } from '@constants/option'; +import { useQueryString } from '@hooks/queryString'; +import { SelectV2 } from '@sopt-makers/ui'; +import { useRouter } from 'next/router'; +import React from 'react'; +import { styled } from 'stitches.config'; + +interface DropDownFilterProps { + filter: FilterType; +} + +function DropDownFilter({ filter }: DropDownFilterProps) { + const router = useRouter(); + const selectedPartQuery = router.query.part as string; + const defaultValue = selectedPartQuery ? { label: selectedPartQuery, value: selectedPartQuery } : undefined; + + const { subject, options } = filter; + const { value: selectedValue, setValue, deleteKey } = useQueryString(subject); + + const setPartQuery = (value: string) => { + ampli.clickFilterPart({ group_part: value }); + + if (selectedValue === value) return deleteKey(); + return setValue(value); + }; + + return ( + <SDropDownContainer> + <SelectV2.Root type="text" visibleOptions={6} defaultValue={defaultValue} onChange={setPartQuery}> + <SelectV2.Trigger> + <SelectV2.TriggerContent placeholder={'대상 파트'} /> + </SelectV2.Trigger> + <SelectV2.Menu> + {options.map(option => ( + <SelectV2.MenuItem key={option} option={{ label: option, value: option }} /> + ))} + </SelectV2.Menu> + </SelectV2.Root> + </SDropDownContainer> + ); +} + +export default DropDownFilter; + +const SDropDownContainer = styled('div', { + ml: '$16', +}); diff --git a/src/components/page/list/Filter/Modal/Chip/ChipItem.tsx b/src/components/page/list/Filter/Modal/Chip/ChipItem.tsx index 4e69d6b66..653f07943 100644 --- a/src/components/page/list/Filter/Modal/Chip/ChipItem.tsx +++ b/src/components/page/list/Filter/Modal/Chip/ChipItem.tsx @@ -1,5 +1,7 @@ import { ampli } from '@/ampli'; import { CATEGORY_FILTER, PART_FILTER, STATUS_FILTER } from '@constants/option'; +import { useDisplay } from '@hooks/useDisplay'; +import { Chip } from '@sopt-makers/ui'; import { styled } from 'stitches.config'; interface ChipItemProps { @@ -8,10 +10,17 @@ interface ChipItemProps { isSelected: boolean; addValue: (val: string) => void; deleteValue: (val: string) => void; + resetQuery: () => void; } -function ChipItem({ label, value, isSelected, addValue, deleteValue }: ChipItemProps) { +function ChipItem({ label, value, isSelected, addValue, deleteValue, resetQuery }: ChipItemProps) { + const { isTablet } = useDisplay(); const toggle = () => { + if (value === '전체') { + resetQuery(); + return; + } + switch (label) { case CATEGORY_FILTER.label: ampli.clickFilterCategory({ group_category: value }); @@ -28,9 +37,9 @@ function ChipItem({ label, value, isSelected, addValue, deleteValue }: ChipItemP return addValue(value); }; return ( - <SOption isSelected={isSelected} onClick={toggle}> + <Chip size={isTablet ? 'sm' : 'md'} active={isSelected} onClick={toggle}> {value} - </SOption> + </Chip> ); } diff --git a/src/components/page/list/Filter/Modal/Chip/index.tsx b/src/components/page/list/Filter/Modal/Chip/index.tsx index cca2cef8c..56eab01d5 100644 --- a/src/components/page/list/Filter/Modal/Chip/index.tsx +++ b/src/components/page/list/Filter/Modal/Chip/index.tsx @@ -3,33 +3,37 @@ import { useMultiQueryString } from '@hooks/queryString'; import { CSSType, styled } from 'stitches.config'; import ChipItem from './ChipItem'; -interface ChipProps { +interface ChipsProps { + isLabel?: boolean; css?: CSSType; filter: FilterType; } -function Chip({ css, filter }: ChipProps) { +function Chips({ isLabel, css, filter }: ChipsProps) { //해당 Chip 선택시 Chip의 filter로 전달된 subject를 이용하여 쿼리 세팅 const { label, subject, options } = filter; - const { value: selectedValues, addValue, deleteValue } = useMultiQueryString(subject, true); + const { value: selectedValues, addValue, deleteValue, resetQuery } = useMultiQueryString(subject, true); + + const isEntire = !selectedValues.length; return ( <SChipWrapper css={{ ...css }}> - {label && <SLabel>{label}</SLabel>} + {isLabel && <SLabel>{label}</SLabel>} {options.map(option => ( <ChipItem key={option} - isSelected={selectedValues.includes(option)} + isSelected={selectedValues.includes(option) || (isEntire && option === '전체')} label={label} value={option} addValue={addValue} deleteValue={deleteValue} + resetQuery={resetQuery} /> ))} </SChipWrapper> ); } -export default Chip; +export default Chips; const SChipWrapper = styled('div', {}); const SLabel = styled('p', { diff --git a/src/components/page/list/Filter/Modal/Toggle/index.tsx b/src/components/page/list/Filter/Modal/Toggle/index.tsx index 00763786b..f8eaef607 100644 --- a/src/components/page/list/Filter/Modal/Toggle/index.tsx +++ b/src/components/page/list/Filter/Modal/Toggle/index.tsx @@ -7,7 +7,7 @@ import { Switch as HeadlessSwitch } from '@headlessui/react'; import { ampli } from '@/ampli'; interface ToggleProps { css?: CSSType; - label: string; + label?: string; } function Toggle({ css, label }: ToggleProps) { diff --git a/src/components/page/list/Filter/Modal/index.tsx b/src/components/page/list/Filter/Modal/index.tsx index bbc1e4e69..97af36235 100644 --- a/src/components/page/list/Filter/Modal/index.tsx +++ b/src/components/page/list/Filter/Modal/index.tsx @@ -2,13 +2,14 @@ import DefaultModal from '@components/modal/DefaultModal'; import { CATEGORY_FILTER, PART_FILTER, STATUS_FILTER } from '@constants/option'; import { styled } from 'stitches.config'; import InitializationButton from '../Result/InitializationButton'; -import Chip from './Chip'; +import Chips from './Chip'; import Toggle from './Toggle'; - interface FilterSelectModalProps { isModalOpened: boolean; handleModalClose: () => void; } + +//Notice: 현재 사용 중이지 않습니다. function FilterSelectModal({ isModalOpened, handleModalClose }: FilterSelectModalProps) { const filterSectionStyle = { mb: '$48', '@tablet': { mb: '$40' } }; return ( @@ -19,10 +20,10 @@ function FilterSelectModal({ isModalOpened, handleModalClose }: FilterSelectModa titleLeft={<InitializationButton withText={false} size={24} />} > <SSelectWrapper> - <Chip css={filterSectionStyle} filter={CATEGORY_FILTER} /> - <Chip css={filterSectionStyle} filter={STATUS_FILTER} /> + <Chips css={filterSectionStyle} filter={CATEGORY_FILTER} isLabel={true} /> + <Chips css={filterSectionStyle} filter={STATUS_FILTER} isLabel={true} /> <Toggle css={filterSectionStyle} label="대상 기수" /> - <Chip css={filterSectionStyle} filter={PART_FILTER} /> + <Chips css={filterSectionStyle} filter={PART_FILTER} isLabel={true} /> </SSelectWrapper> </DefaultModal> ); diff --git a/src/components/page/list/Filter/Result/index.tsx b/src/components/page/list/Filter/Result/index.tsx index 74e9ba7b5..ece483dcc 100644 --- a/src/components/page/list/Filter/Result/index.tsx +++ b/src/components/page/list/Filter/Result/index.tsx @@ -10,6 +10,7 @@ import InitializationButton from './InitializationButton'; import { styled } from 'stitches.config'; import { parseBool } from '@utils/parseBool'; +//Notice: 현재 사용 중이지 않습니다. function Result() { //설정된 쿼리 파라미터의 값을 가져와서 OR 연산으로 필터링한 모임 렌더링 const { value: category, deleteValue: deleteCategoryValue } = useCategoryParams(); diff --git a/src/components/page/list/Filter/Search/index.tsx b/src/components/page/list/Filter/Search/index.tsx index f507c373e..9b9382f0b 100644 --- a/src/components/page/list/Filter/Search/index.tsx +++ b/src/components/page/list/Filter/Search/index.tsx @@ -29,11 +29,11 @@ function Search() { export default Search; const SSearchWrapper = styled(Flex, { + height: '$48', py: '$15', px: '$20', border: '1px solid $gray600', borderRadius: '14px', - ml: '$12', '@tablet': { display: 'none', }, diff --git a/src/components/page/list/Filter/index.tsx b/src/components/page/list/Filter/index.tsx index cec0e987b..1d32cedb6 100644 --- a/src/components/page/list/Filter/index.tsx +++ b/src/components/page/list/Filter/index.tsx @@ -2,9 +2,10 @@ import { styled } from 'stitches.config'; import { Flex } from '@components/util/layout/Flex'; import { useSearchParams } from '@hooks/queryString/custom'; import Search from './Search'; -import Result from './Result'; -import ArrowSmallRightIcon from '@assets/svg/arrow_small_right.svg'; -import FilterModalOpenButton from './Modal/OpenButton'; +import ArrowRightCircleIcon from '@assets/svg/arrow_right_circle.svg'; +import Toggle from './Modal/Toggle'; +import { PART_FILTER } from '@constants/option'; +import DropDownFilter from './DropDown'; function Filter() { const { value: search } = useSearchParams(); @@ -13,8 +14,9 @@ function Filter() { <> <Flex align="center" justify="between"> <Flex> - <FilterModalOpenButton /> <Search /> + <DropDownFilter filter={PART_FILTER} /> + <Toggle /> </Flex> <SGuideButton @@ -23,13 +25,10 @@ function Filter() { rel="noreferrer noopener" > 모임 신청 가이드 - <ArrowSmallRightIcon /> + <ArrowRightCircleIcon /> </SGuideButton> </Flex> - {/*필터 적용 결과 박스 (chip 모임)*/} - <Result /> - {!!search && <SearchResultMessage>"{search}"에 대한 검색결과입니다.</SearchResultMessage>} </> ); @@ -38,13 +37,13 @@ function Filter() { export default Filter; const SGuideButton = styled('a', { + height: '$48', flexType: 'verticalCenter', gap: '$8', color: '$gray10', padding: '$18 $20', - border: '1px solid $gray10', borderRadius: '14px', - fontAg: '18_medium_100', + fontAg: '18_semibold_100', boxSizing: 'border-box', '@tablet': { padding: '$14 $12 $14 $16', diff --git a/src/constants/option.ts b/src/constants/option.ts index 6d2b19bb2..e7faff3c8 100644 --- a/src/constants/option.ts +++ b/src/constants/option.ts @@ -21,7 +21,8 @@ export const APPROVAL_STATUS_KOREAN_TO_ENGLISH: StringKeyObject = { 거절: 'REJECT', }; export const APPLICATION_TYPE = ['신청', '초대']; -export const CATEGORY_OPTIONS = ['번쩍', '스터디', '세미나', '행사']; +export const CATEGORY_OPTIONS = ['스터디', '세미나', '행사', '번쩍']; +export const CATEGORY_FILTER_OPTIONS = ['전체', ...CATEGORY_OPTIONS]; export const PART_OPTIONS = ['기획', '디자인', 'Android', 'iOS', '웹', '서버']; export const PART_VALUES = ['PM', 'DESIGN', 'ANDROID', 'IOS', 'WEB', 'SERVER']; export const ACTION_STATUS = ['모집 전', '모집 중', '모집 마감', '활동 중', '활동 종료']; @@ -53,7 +54,7 @@ export interface FilterType { export const CATEGORY_FILTER = { label: '카테고리', subject: 'category', - options: CATEGORY_OPTIONS, + options: CATEGORY_FILTER_OPTIONS, }; export const STATUS_FILTER = { label: '모집 상태', diff --git a/src/hooks/queryString/index.ts b/src/hooks/queryString/index.ts index ffa578d62..62013bcb4 100644 --- a/src/hooks/queryString/index.ts +++ b/src/hooks/queryString/index.ts @@ -82,11 +82,24 @@ export function useMultiQueryString(key: string, withPage?: boolean) { delete query[key]; pushQuery(query); }; + + const resetQuery = () => { + router.replace( + { + pathname: router.pathname, + query: {}, + }, + undefined, + { shallow: true } + ); + }; + return { value: splitQueryOfKey, // , 로 구분된 해당 key의 value들을 배열 형태로 반환 setValue, addValue, deleteValue, deleteKey, + resetQuery, }; } diff --git a/src/hooks/useDisplay.ts b/src/hooks/useDisplay.ts index 5ae4db8aa..97c8729d6 100644 --- a/src/hooks/useDisplay.ts +++ b/src/hooks/useDisplay.ts @@ -1,6 +1,7 @@ import { useState } from 'react'; import { useMediaQuery } from 'react-responsive'; import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect'; + export function useDisplay() { const [isMobile, setIsMobile] = useState(false); const [isTablet, setIsTable] = useState(false); From 9e4c819480c9531106f487b00aab80d864d5c6e2 Mon Sep 17 00:00:00 2001 From: Hyeonsu Kim <86764406+borimong@users.noreply.github.com> Date: Fri, 7 Feb 2025 14:58:47 +0900 Subject: [PATCH 6/6] =?UTF-8?q?fix:=20=EB=B2=88=EC=A9=8D=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A4=20=EC=8B=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20width=20?= =?UTF-8?q?=EB=A5=BC=20=EA=B3=A0=EC=A0=95=EA=B0=92=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=B8=A1?= =?UTF-8?q?=20=EC=9A=94=EC=B2=AD=EC=82=AC=ED=95=AD)=20(#1008)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/form/Flash/index.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/components/form/Flash/index.tsx b/src/components/form/Flash/index.tsx index 403b6572d..1874e37e5 100644 --- a/src/components/form/Flash/index.tsx +++ b/src/components/form/Flash/index.tsx @@ -475,12 +475,10 @@ const STitleField = styled('div', { width: '100%', }); const SFileInputWrapper = styled('div', { - display: 'grid', - gridTemplateColumns: 'repeat(3, 1fr)', - gap: '16px', + width: '260px', - '@tablet': { - gridTemplateColumns: 'repeat(2, 1fr)', + '@mobile': { + width: '256px', }, }); const SApplicationFieldWrapper = styled('div', {