From 4217052c19456c635e184d43d48cc6f57198d587 Mon Sep 17 00:00:00 2001 From: Daehee Date: Tue, 17 Dec 2024 00:41:36 +0900 Subject: [PATCH 1/4] refactor: Party Page --- src/components/PartyPlan/index.tsx | 2 +- src/components/PlaceMap/index.tsx | 2 +- .../ProfileModal/ProfileInfo/index.tsx | 2 +- src/components/ProfileModal/index.tsx | 2 +- .../AdminPage/Management/Region/index.tsx | 8 +- .../UserInfo/UserTable/RegionModal/index.tsx | 9 +- .../DriverApplyPage/RegionList/index.tsx | 10 +- .../PartyFilter/RegionModal/index.tsx | 10 +- .../BasicInfo/RegionModal/index.tsx | 9 +- src/pages/NewPartyPage/Region/index.tsx | 9 +- .../CancelModal/{index.jsx => index.tsx} | 66 +++++--- .../{index.jsx => index.tsx} | 10 +- .../CourseDnD/{index.jsx => index.tsx} | 76 ++++++--- src/pages/PartyPage/CreditInfo/index.tsx | 3 +- .../DriverReview/{index.jsx => index.tsx} | 10 +- .../AcceptModal/{index.jsx => index.tsx} | 57 +++++-- .../CancelModal/{index.jsx => index.tsx} | 61 +++++-- .../EditAgreement/{index.jsx => index.tsx} | 44 +++-- .../PartyPage/EditMap/ToggleButton/index.jsx | 47 ------ .../PartyPage/EditMap/ToggleButton/index.tsx | 59 +++++++ .../EditMap/{index.jsx => index.tsx} | 44 +++-- .../EditModal/{index.jsx => index.tsx} | 118 +++++++++---- .../AgreeItem/{index.jsx => index.tsx} | 19 ++- src/pages/PartyPage/JoinAgreement/index.jsx | 86 ---------- src/pages/PartyPage/JoinAgreement/index.tsx | 122 ++++++++++++++ .../JoinButton/{index.jsx => index.tsx} | 28 +++- .../JoinGreeting/{index.jsx => index.tsx} | 13 +- .../JoinMember/MemberButton/index.jsx | 32 ---- .../JoinMember/MemberButton/index.tsx | 49 ++++++ .../JoinMember/{index.jsx => index.tsx} | 28 +++- .../JoinMemberInfo/FriendInfo/index.jsx | 43 ----- .../JoinMemberInfo/FriendInfo/index.tsx | 64 +++++++ .../MyInfo/{index.jsx => index.tsx} | 6 +- .../JoinMemberInfo/{index.jsx => index.tsx} | 33 +++- .../JoinModal/{index.jsx => index.tsx} | 106 +++++++----- .../WhatReady/{index.jsx => index.tsx} | 15 +- .../MallangReady/{index.jsx => index.tsx} | 39 ++++- .../AcceptModal/{index.jsx => index.tsx} | 56 +++++-- .../{index.jsx => index.tsx} | 12 +- .../NotFoundParty/{index.jsx => index.tsx} | 3 +- .../PartyDate/{index.jsx => index.tsx} | 9 +- .../PartyIntro/{index.jsx => index.tsx} | 10 +- .../Status/{index.jsx => index.tsx} | 11 +- .../MemberProfile/{index.jsx => index.tsx} | 34 +++- .../PartyMember/{index.jsx => index.tsx} | 34 ++-- .../QuitModal/{index.jsx => index.tsx} | 105 ++++++++---- .../QuitButton/{index.jsx => index.tsx} | 15 +- .../ToTalPrice/{index.jsx => index.tsx} | 11 +- src/pages/PartyPage/{index.jsx => index.tsx} | 157 +++++++++++------- src/types/index.ts | 7 + 50 files changed, 1175 insertions(+), 630 deletions(-) rename src/pages/PartyPage/CancelNewPartyButton/CancelModal/{index.jsx => index.tsx} (64%) rename src/pages/PartyPage/CancelNewPartyButton/{index.jsx => index.tsx} (80%) rename src/pages/PartyPage/CourseDnD/{index.jsx => index.tsx} (69%) rename src/pages/PartyPage/DriverReview/{index.jsx => index.tsx} (78%) rename src/pages/PartyPage/EditAgreement/AcceptModal/{index.jsx => index.tsx} (69%) rename src/pages/PartyPage/EditAgreement/CancelModal/{index.jsx => index.tsx} (65%) rename src/pages/PartyPage/EditAgreement/{index.jsx => index.tsx} (75%) delete mode 100644 src/pages/PartyPage/EditMap/ToggleButton/index.jsx create mode 100644 src/pages/PartyPage/EditMap/ToggleButton/index.tsx rename src/pages/PartyPage/EditMap/{index.jsx => index.tsx} (57%) rename src/pages/PartyPage/EditModal/{index.jsx => index.tsx} (76%) rename src/pages/PartyPage/JoinAgreement/AgreeItem/{index.jsx => index.tsx} (65%) delete mode 100644 src/pages/PartyPage/JoinAgreement/index.jsx create mode 100644 src/pages/PartyPage/JoinAgreement/index.tsx rename src/pages/PartyPage/JoinButton/{index.jsx => index.tsx} (63%) rename src/pages/PartyPage/JoinGreeting/{index.jsx => index.tsx} (55%) delete mode 100644 src/pages/PartyPage/JoinMember/MemberButton/index.jsx create mode 100644 src/pages/PartyPage/JoinMember/MemberButton/index.tsx rename src/pages/PartyPage/JoinMember/{index.jsx => index.tsx} (61%) delete mode 100644 src/pages/PartyPage/JoinMemberInfo/FriendInfo/index.jsx create mode 100644 src/pages/PartyPage/JoinMemberInfo/FriendInfo/index.tsx rename src/pages/PartyPage/JoinMemberInfo/MyInfo/{index.jsx => index.tsx} (68%) rename src/pages/PartyPage/JoinMemberInfo/{index.jsx => index.tsx} (59%) rename src/pages/PartyPage/JoinModal/{index.jsx => index.tsx} (74%) rename src/pages/PartyPage/MallangReady/WhatReady/{index.jsx => index.tsx} (82%) rename src/pages/PartyPage/MallangReady/{index.jsx => index.tsx} (76%) rename src/pages/PartyPage/NewPartyAgreement/AcceptModal/{index.jsx => index.tsx} (69%) rename src/pages/PartyPage/NewPartyAgreement/{index.jsx => index.tsx} (79%) rename src/pages/PartyPage/NotFoundParty/{index.jsx => index.tsx} (92%) rename src/pages/PartyPage/PartyDate/{index.jsx => index.tsx} (66%) rename src/pages/PartyPage/PartyIntro/{index.jsx => index.tsx} (60%) rename src/pages/PartyPage/PartyMember/MemberProfile/Status/{index.jsx => index.tsx} (89%) rename src/pages/PartyPage/PartyMember/MemberProfile/{index.jsx => index.tsx} (65%) rename src/pages/PartyPage/PartyMember/{index.jsx => index.tsx} (89%) rename src/pages/PartyPage/QuitButton/QuitModal/{index.jsx => index.tsx} (71%) rename src/pages/PartyPage/QuitButton/{index.jsx => index.tsx} (85%) rename src/pages/PartyPage/ToTalPrice/{index.jsx => index.tsx} (71%) rename src/pages/PartyPage/{index.jsx => index.tsx} (86%) diff --git a/src/components/PartyPlan/index.tsx b/src/components/PartyPlan/index.tsx index 6c390a03..8f279cd0 100644 --- a/src/components/PartyPlan/index.tsx +++ b/src/components/PartyPlan/index.tsx @@ -6,7 +6,7 @@ interface Props { edit: boolean; startDate: string; editHandler?: () => void; - comment?: boolean; + comment?: string; course: Course; } diff --git a/src/components/PlaceMap/index.tsx b/src/components/PlaceMap/index.tsx index 80ff0be6..ee65dd03 100644 --- a/src/components/PlaceMap/index.tsx +++ b/src/components/PlaceMap/index.tsx @@ -18,7 +18,7 @@ import NewPlaceModal from "./NewPlaceModal"; interface Props { search: boolean; - keyword: string; + keyword?: string; searchPage: boolean; onlyAllPlace: boolean; courseData?: Destination[]; diff --git a/src/components/ProfileModal/ProfileInfo/index.tsx b/src/components/ProfileModal/ProfileInfo/index.tsx index dd1ae462..f93c0d12 100644 --- a/src/components/ProfileModal/ProfileInfo/index.tsx +++ b/src/components/ProfileModal/ProfileInfo/index.tsx @@ -8,7 +8,7 @@ interface Props { nickname: string | undefined; introduction: string | undefined; createdAt: string | undefined; - driverName: boolean; + driverName: string | boolean; } function ProfileInfo({ diff --git a/src/components/ProfileModal/index.tsx b/src/components/ProfileModal/index.tsx index 6a5b93e5..76637852 100644 --- a/src/components/ProfileModal/index.tsx +++ b/src/components/ProfileModal/index.tsx @@ -23,7 +23,7 @@ interface Props { userId?: number; chatRoomId?: number; reportId?: number; - driverName: boolean; + driverName: string | boolean; } function ProfileModal({ diff --git a/src/pages/AdminPage/Management/Region/index.tsx b/src/pages/AdminPage/Management/Region/index.tsx index ab39d8df..07277f47 100644 --- a/src/pages/AdminPage/Management/Region/index.tsx +++ b/src/pages/AdminPage/Management/Region/index.tsx @@ -1,19 +1,13 @@ import { memo, useCallback, useEffect, useState } from "react"; import { useSearchParams } from "react-router-dom"; import { getPartyRegionList } from "../../../../api/region"; +import { RegionData } from "../../../../types"; import Title from "../../../../components/Title"; import ConfirmModal from "../../../../components/ConfirmModal"; import SearchBar from "./SearchBar"; import RegionList from "./RegionList"; import RegionDriver from "./RegionDriver"; -export interface RegionData { - image: string; - name: string; - province: string; - regionId: number; -} - function Region() { const [searchParams] = useSearchParams(); const [searchKeyword, setSearchKeyword] = useState(""); diff --git a/src/pages/AdminPage/User/UserInfo/UserTable/RegionModal/index.tsx b/src/pages/AdminPage/User/UserInfo/UserTable/RegionModal/index.tsx index 08895f6f..5c9812ff 100644 --- a/src/pages/AdminPage/User/UserInfo/UserTable/RegionModal/index.tsx +++ b/src/pages/AdminPage/User/UserInfo/UserTable/RegionModal/index.tsx @@ -12,6 +12,7 @@ import { createPortal } from "react-dom"; import { useNavigate } from "react-router-dom"; import { postApplyDriver } from "../../../../../../api/admin"; import { getPartyRegionList } from "../../../../../../api/region"; +import { RegionData } from "../../../../../../types"; import RegionButton from "./RegionButton"; import clsx from "clsx"; @@ -25,13 +26,7 @@ function RegionModal({ showModal, setShowModal, userId }: Props) { const navigation = useNavigate(); const modalRef = useRef(null); const [selectedRegion, setSelectedRegion] = useState([]); - const [regionData, setRegionData] = useState< - { - image: string; - name: string; - regionId: number; - }[] - >([]); + const [regionData, setRegionData] = useState([]); const regionClickHandler = useCallback(async () => { try { diff --git a/src/pages/DriverApplyPage/RegionList/index.tsx b/src/pages/DriverApplyPage/RegionList/index.tsx index 56719796..43e07cfb 100644 --- a/src/pages/DriverApplyPage/RegionList/index.tsx +++ b/src/pages/DriverApplyPage/RegionList/index.tsx @@ -7,15 +7,9 @@ import { useState, } from "react"; import { getPartyRegionList } from "../../../api/region"; +import { RegionData } from "../../../types"; import Region from "./Region"; -interface RegionType { - image: string; - name: string; - province: string; - regionId: number; -} - interface Props { setActiveNext: Dispatch>; region: string[]; @@ -23,7 +17,7 @@ interface Props { } function RegionList({ setActiveNext, region, setRegion }: Props) { - const [regionData, setRegionData] = useState([]); + const [regionData, setRegionData] = useState([]); const getPartyRegionListFunc = useCallback(async () => { try { diff --git a/src/pages/LandingPage/PartyFilter/RegionModal/index.tsx b/src/pages/LandingPage/PartyFilter/RegionModal/index.tsx index dff88952..612ab2f8 100644 --- a/src/pages/LandingPage/PartyFilter/RegionModal/index.tsx +++ b/src/pages/LandingPage/PartyFilter/RegionModal/index.tsx @@ -13,16 +13,10 @@ import { useDispatch, useSelector } from "react-redux"; import { setRegion } from "../../../../redux/modules/partyFilterSlice"; import { getPartyRegionList } from "../../../../api/region"; import { RootState } from "../../../../redux/store"; +import { RegionData } from "../../../../types"; import RegionButton from "./RegionButton"; import clsx from "clsx"; -interface RegionType { - image: string; - name: string; - province: string | null; - regionId: number; -} - interface Props { showModal: boolean; setShowModal: Dispatch>; @@ -38,7 +32,7 @@ function RegionModal({ const modalRef = useRef(null); const region = useSelector((state: RootState) => state.partyFilter.region); const [selectedRegion, setSelectedRegion] = useState(""); - const [regionData, setRegionData] = useState([]); + const [regionData, setRegionData] = useState([]); const regionClickHandler = useCallback((target: string) => { setShowModal(false); diff --git a/src/pages/MyProfilePage/DriverProfile/BasicInfo/RegionModal/index.tsx b/src/pages/MyProfilePage/DriverProfile/BasicInfo/RegionModal/index.tsx index a4926779..025b9eac 100644 --- a/src/pages/MyProfilePage/DriverProfile/BasicInfo/RegionModal/index.tsx +++ b/src/pages/MyProfilePage/DriverProfile/BasicInfo/RegionModal/index.tsx @@ -9,17 +9,10 @@ import { useState, } from "react"; import { getPartyRegionList } from "../../../../../api/region"; -import { DriverInfo } from "../../../../../types"; +import { DriverInfo, RegionData } from "../../../../../types"; import Region from "./Region"; import clsx from "clsx"; -interface RegionData { - image: string; - name: string; - province: string | null; - regionId: number; -} - interface Props { showModal: boolean; setShowModal: Dispatch>; diff --git a/src/pages/NewPartyPage/Region/index.tsx b/src/pages/NewPartyPage/Region/index.tsx index da663169..56cd7671 100644 --- a/src/pages/NewPartyPage/Region/index.tsx +++ b/src/pages/NewPartyPage/Region/index.tsx @@ -7,7 +7,7 @@ import { useState, } from "react"; import { getPartyRegionList } from "../../../api/region"; -import { DriverInfo, Review } from "../../../types"; +import { DriverInfo, RegionData, Review } from "../../../types"; import CheckModal from "../../../components/CheckModal"; import Title from "../../../components/Title"; import RegionButton from "./RegionButton"; @@ -19,13 +19,6 @@ interface DriverInfoType extends DriverInfo { reviews: Review[]; } -interface RegionData { - image: string; - name: string; - province: string | null; - regionId: number; -} - interface Props { setRegion: Dispatch>; member: number; diff --git a/src/pages/PartyPage/CancelNewPartyButton/CancelModal/index.jsx b/src/pages/PartyPage/CancelNewPartyButton/CancelModal/index.tsx similarity index 64% rename from src/pages/PartyPage/CancelNewPartyButton/CancelModal/index.jsx rename to src/pages/PartyPage/CancelNewPartyButton/CancelModal/index.tsx index dd858252..f685cef8 100644 --- a/src/pages/PartyPage/CancelNewPartyButton/CancelModal/index.jsx +++ b/src/pages/PartyPage/CancelNewPartyButton/CancelModal/index.tsx @@ -1,16 +1,31 @@ -import { useEffect, useRef, useState } from "react"; +import { + Dispatch, + memo, + MouseEvent, + SetStateAction, + useCallback, + useEffect, + useRef, + useState, +} from "react"; import { useNavigate, useParams } from "react-router-dom"; import { deleteNewParty } from "../../../../api/party"; +import clsx from "clsx"; -function CancelModal({ showModal, setShowModal }) { +interface Props { + showModal: boolean; + setShowModal: Dispatch>; +} + +function CancelModal({ showModal, setShowModal }: Props) { const navigation = useNavigate(); - const modalRef = useRef(); + const modalRef = useRef(null); const { partyId } = useParams(); const [message, setMessage] = useState(""); const [complete, setComplete] = useState(false); const [loading, setLoading] = useState(false); - const CancelHandler = async () => { + const CancelHandler = useCallback(async () => { if (loading) return; try { @@ -25,24 +40,30 @@ function CancelModal({ showModal, setShowModal }) { } finally { setLoading(false); } - }; + }, [loading, partyId]); - const closeModal = () => { + const closeModal = useCallback(() => { if (complete) navigation("/", { replace: true }); setShowModal(false); - }; + }, [complete]); - const modalOutSideClick = (e) => { - if (modalRef.current === e.target) closeModal(); - }; + const modalOutSideClick = useCallback( + (event: MouseEvent) => { + if (modalRef.current === event.target) closeModal(); + }, + [modalRef] + ); - const handleKeyPress = (event) => { - if (event.key === "Escape") closeModal(); - else if (event.key === "Enter") { - if (!complete) return CancelHandler(); - else navigation("/", { replace: true }); - } - }; + const handleKeyPress = useCallback( + (event: KeyboardEvent) => { + if (event.key === "Escape") closeModal(); + else if (event.key === "Enter") { + if (!complete) return CancelHandler(); + else navigation("/", { replace: true }); + } + }, + [complete] + ); useEffect(() => { if (!showModal) return document.body.classList.remove("overflow-hidden"); @@ -59,11 +80,12 @@ function CancelModal({ showModal, setShowModal }) { return (
modalOutSideClick(e)} + onClick={modalOutSideClick} >
@@ -97,4 +119,4 @@ function CancelModal({ showModal, setShowModal }) { ); } -export default CancelModal; +export default memo(CancelModal); diff --git a/src/pages/PartyPage/CancelNewPartyButton/index.jsx b/src/pages/PartyPage/CancelNewPartyButton/index.tsx similarity index 80% rename from src/pages/PartyPage/CancelNewPartyButton/index.jsx rename to src/pages/PartyPage/CancelNewPartyButton/index.tsx index 6825a11a..a3df10a6 100644 --- a/src/pages/PartyPage/CancelNewPartyButton/index.jsx +++ b/src/pages/PartyPage/CancelNewPartyButton/index.tsx @@ -1,8 +1,12 @@ -import { useState } from "react"; +import { memo, useState } from "react"; import CancelModal from "./CancelModal"; import BottomButton from "../../../components/BottomButton"; -function CancelNewPartyButton({ isDriver }) { +interface Props { + isDriver: boolean; +} + +function CancelNewPartyButton({ isDriver }: Props) { const [showModal, setShowModal] = useState(false); if (isDriver) return null; @@ -25,4 +29,4 @@ function CancelNewPartyButton({ isDriver }) { ); } -export default CancelNewPartyButton; +export default memo(CancelNewPartyButton); diff --git a/src/pages/PartyPage/CourseDnD/index.jsx b/src/pages/PartyPage/CourseDnD/index.tsx similarity index 69% rename from src/pages/PartyPage/CourseDnD/index.jsx rename to src/pages/PartyPage/CourseDnD/index.tsx index f7a92b65..2dd7c185 100644 --- a/src/pages/PartyPage/CourseDnD/index.jsx +++ b/src/pages/PartyPage/CourseDnD/index.tsx @@ -1,10 +1,35 @@ -import { useEffect, useState } from "react"; +import { + Dispatch, + ForwardedRef, + memo, + SetStateAction, + useCallback, + useEffect, + useState, +} from "react"; import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd"; import { dateToStringHan } from "../../../utils"; +import { Course, Destination } from "../../../types"; import dragIcon from "../../../assets/svg/dragIcon.svg"; import deleteIcon from "../../../assets/svg/x-modal-icon.svg"; import ConfirmModal from "../../../components/ConfirmModal"; import TimeModal from "../../DriverCoursePage/CourseDnD/TimeModal"; +import clsx from "clsx"; + +interface Props { + name: string; + setName: Dispatch>; + course: Course; + startDate: string; + hours: number; + courseData: Destination[]; + setCourseData: Dispatch>; + startTime: string; + endTime: string; + setStartTime: Dispatch>; + shakeCourse?: boolean; + courseRef?: ForwardedRef; +} function CourseDnD({ name, @@ -19,24 +44,29 @@ function CourseDnD({ setStartTime, shakeCourse, courseRef, -}) { +}: Props) { const [showText, setShowText] = useState(false); const [showModal, setShowModal] = useState(false); const [showTimeModal, setShowTimeModal] = useState(false); - const handleChange = (result) => { - if (!result.destination) return; - const items = [...courseData]; - const [reorderedItem] = items.splice(result.source.index, 1); - items.splice(result.destination.index, 0, reorderedItem); - setCourseData(items); - }; - - const deleteHandler = (targetIndex) => { - if (courseData.length === 2) return setShowModal(true); + const handleChange = useCallback( + (result: any) => { + if (!result.destination) return; + const items = [...courseData]; + const [reorderedItem] = items.splice(result.source.index, 1); + items.splice(result.destination.index, 0, reorderedItem); + setCourseData(items); + }, + [courseData] + ); - setCourseData(courseData.filter((_, index) => index !== targetIndex)); - }; + const deleteHandler = useCallback( + (targetIndex: number) => { + if (courseData.length === 2) return setShowModal(true); + setCourseData(courseData.filter((_, index) => index !== targetIndex)); + }, + [courseData] + ); useEffect(() => { if (courseData.length === 0) { @@ -49,7 +79,7 @@ function CourseDnD({ }, [shakeCourse]); return ( -
+
setName(e.target.value)} /> -
+
{courseData.length === 0 && "여행지를 추가해주세요!"}
@@ -85,9 +117,10 @@ function CourseDnD({ {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef} - className={`bg-white w-4/5 md:w-[700px] h-[83px] mx-auto text-boldblue text-lg md:text-xl text-bold flex items-center ${ + className={clsx( + "bg-white w-4/5 md:w-[700px] h-[83px] mx-auto text-boldblue text-lg md:text-xl text-bold flex items-center", snapshot.isDragging && "border-2 border-primary" - }`} + )} >
{item.name}
index === 0 && setShowTimeModal(true)} > {index === 0 @@ -137,4 +173,4 @@ function CourseDnD({ ); } -export default CourseDnD; +export default memo(CourseDnD); diff --git a/src/pages/PartyPage/CreditInfo/index.tsx b/src/pages/PartyPage/CreditInfo/index.tsx index cba48212..78ac750a 100644 --- a/src/pages/PartyPage/CreditInfo/index.tsx +++ b/src/pages/PartyPage/CreditInfo/index.tsx @@ -1,14 +1,13 @@ import { memo, useCallback, useEffect, useState } from "react"; import { priceToString, dateToKoreanDataTime } from "../../../utils"; import { postPaymentAgain } from "../../../api/card"; -import { partyStatusObj } from "../../../utils/data"; import CheckModal from "../../../components/CheckModal"; import ConfirmModal from "../../../components/ConfirmModal"; interface Props { totalPrice: number; capacity: number; - partyStatus?: keyof typeof partyStatusObj; + partyStatus?: string; paymentAmount?: number; createdAt?: string; receiptUrl?: string | null; diff --git a/src/pages/PartyPage/DriverReview/index.jsx b/src/pages/PartyPage/DriverReview/index.tsx similarity index 78% rename from src/pages/PartyPage/DriverReview/index.jsx rename to src/pages/PartyPage/DriverReview/index.tsx index 6c2a3547..da851ac7 100644 --- a/src/pages/PartyPage/DriverReview/index.jsx +++ b/src/pages/PartyPage/DriverReview/index.tsx @@ -1,7 +1,13 @@ +import { memo } from "react"; import { useNavigate } from "react-router-dom"; import { computeGapDay } from "../../../utils"; -function DriverReview({ driverId, startDate }) { +interface Props { + driverId: number; + startDate: string; +} + +function DriverReview({ driverId, startDate }: Props) { const navigation = useNavigate(); if (computeGapDay(startDate) > 0) return null; @@ -20,4 +26,4 @@ function DriverReview({ driverId, startDate }) { ); } -export default DriverReview; +export default memo(DriverReview); diff --git a/src/pages/PartyPage/EditAgreement/AcceptModal/index.jsx b/src/pages/PartyPage/EditAgreement/AcceptModal/index.tsx similarity index 69% rename from src/pages/PartyPage/EditAgreement/AcceptModal/index.jsx rename to src/pages/PartyPage/EditAgreement/AcceptModal/index.tsx index 85635f15..a51b587d 100644 --- a/src/pages/PartyPage/EditAgreement/AcceptModal/index.jsx +++ b/src/pages/PartyPage/EditAgreement/AcceptModal/index.tsx @@ -1,5 +1,23 @@ -import { useEffect, useRef, useState } from "react"; +import { + Dispatch, + memo, + MouseEvent, + SetStateAction, + useCallback, + useEffect, + useRef, + useState, +} from "react"; import { putProposalAccept } from "../../../../api/party"; +import clsx from "clsx"; + +interface Props { + showModal: boolean; + setShowModal: Dispatch>; + getPartyData: (toScrollTop?: boolean) => void; + accept: boolean; + proposalId: number; +} function AcceptModal({ showModal, @@ -7,13 +25,13 @@ function AcceptModal({ getPartyData, accept, proposalId, -}) { - const modalRef = useRef(); +}: Props) { + const modalRef = useRef(null); const [message, setMessage] = useState(""); const [loading, setLoading] = useState(false); const [complete, setComplete] = useState(false); - const acceptHandler = async () => { + const acceptHandler = useCallback(async () => { if (loading) return; try { @@ -29,21 +47,23 @@ function AcceptModal({ } finally { setLoading(false); } - }; + }, [loading, proposalId, accept]); - const closeModal = () => { + const closeModal = useCallback(() => { if (complete) getPartyData(); - setShowModal(false); - }; + }, [complete]); - const modalOutSideClick = (e) => { - if (modalRef.current === e.target) closeModal(); - }; + const modalOutSideClick = useCallback( + (event: MouseEvent) => { + if (modalRef.current === event.target) closeModal(); + }, + [modalRef] + ); - const handleKeyPress = (event) => { + const handleKeyPress = useCallback((event: KeyboardEvent) => { if (event.key === "Escape") closeModal(); - }; + }, []); useEffect(() => { if (!showModal) return document.body.classList.remove("overflow-hidden"); @@ -65,11 +85,12 @@ function AcceptModal({ return (
modalOutSideClick(e)} + onClick={modalOutSideClick} >
@@ -103,4 +124,4 @@ function AcceptModal({ ); } -export default AcceptModal; +export default memo(AcceptModal); diff --git a/src/pages/PartyPage/EditAgreement/CancelModal/index.jsx b/src/pages/PartyPage/EditAgreement/CancelModal/index.tsx similarity index 65% rename from src/pages/PartyPage/EditAgreement/CancelModal/index.jsx rename to src/pages/PartyPage/EditAgreement/CancelModal/index.tsx index b697e592..9533279b 100644 --- a/src/pages/PartyPage/EditAgreement/CancelModal/index.jsx +++ b/src/pages/PartyPage/EditAgreement/CancelModal/index.tsx @@ -1,13 +1,35 @@ -import { useEffect, useRef, useState } from "react"; +import { + Dispatch, + memo, + MouseEvent, + SetStateAction, + useCallback, + useEffect, + useRef, + useState, +} from "react"; import { deleteProposalCancel } from "../../../../api/party"; +import clsx from "clsx"; -function CancelModal({ showModal, setShowModal, getPartyData, proposalId }) { - const modalRef = useRef(); +interface Props { + showModal: boolean; + setShowModal: Dispatch>; + getPartyData: (toScrollTop?: boolean) => void; + proposalId: number; +} + +function CancelModal({ + showModal, + setShowModal, + getPartyData, + proposalId, +}: Props) { + const modalRef = useRef(null); const [message, setMessage] = useState(""); const [loading, setLoading] = useState(false); const [complete, setComplete] = useState(false); - const cancelHandler = async () => { + const cancelHandler = useCallback(async () => { if (loading) return; try { @@ -22,21 +44,23 @@ function CancelModal({ showModal, setShowModal, getPartyData, proposalId }) { } finally { setLoading(false); } - }; + }, [loading, proposalId]); - const closeModal = () => { + const closeModal = useCallback(() => { if (complete) getPartyData(true); - setShowModal(false); - }; + }, [complete]); - const modalOutSideClick = (e) => { - if (modalRef.current === e.target) closeModal(); - }; + const modalOutSideClick = useCallback( + (event: MouseEvent) => { + if (modalRef.current === event.target) closeModal(); + }, + [modalRef] + ); - const handleKeyPress = (event) => { + const handleKeyPress = useCallback((event: KeyboardEvent) => { if (event.key === "Escape") closeModal(); - }; + }, []); useEffect(() => { if (!showModal) return document.body.classList.remove("overflow-hidden"); @@ -53,11 +77,12 @@ function CancelModal({ showModal, setShowModal, getPartyData, proposalId }) { return (
modalOutSideClick(e)} + onClick={modalOutSideClick} >
@@ -91,4 +116,4 @@ function CancelModal({ showModal, setShowModal, getPartyData, proposalId }) { ); } -export default CancelModal; +export default memo(CancelModal); diff --git a/src/pages/PartyPage/EditAgreement/index.jsx b/src/pages/PartyPage/EditAgreement/index.tsx similarity index 75% rename from src/pages/PartyPage/EditAgreement/index.jsx rename to src/pages/PartyPage/EditAgreement/index.tsx index a9b43b8f..1ec019d9 100644 --- a/src/pages/PartyPage/EditAgreement/index.jsx +++ b/src/pages/PartyPage/EditAgreement/index.tsx @@ -1,8 +1,22 @@ -import { useEffect, useState } from "react"; +import { memo, useCallback, useEffect, useMemo, useState } from "react"; import { useSelector } from "react-redux"; +import { RootState } from "../../../redux/store"; import { numberTo00 } from "../../../utils"; import AcceptModal from "./AcceptModal"; import CancelModal from "./CancelModal"; +import clsx from "clsx"; + +interface Props { + myParty: boolean; + partyStatus: string; + createdAt: string; + getPartyData: (toScrollTop?: boolean) => void; + proposalId: number; + agreement: { + userId: number; + status: string; + }[]; +} function EditAgreement({ myParty, @@ -11,24 +25,29 @@ function EditAgreement({ getPartyData, proposalId, agreement, -}) { - const user = useSelector((state) => state.user); +}: Props) { + const user = useSelector((state: RootState) => state.user); const [diffMinute, setDiffMinute] = useState(0); const [accept, setAccept] = useState(true); const [showModal, setShowModal] = useState(false); - const showButton = - agreement.find((item) => item.userId === user.userId)?.status === "WAITING" - ? "block" - : "hidden"; - const diffMinuteHandler = () => setDiffMinute((diffMinute) => diffMinute - 1); + const showButton = useMemo(() => { + const myAgreement = agreement.find((item) => item.userId === user.userId); + if (myAgreement && myAgreement.status === "WAITING") return "block"; + return "hidden"; + }, [agreement, user]); + + const diffMinuteHandler = useCallback( + () => setDiffMinute((diffMinute) => diffMinute - 1), + [diffMinute] + ); useEffect(() => { const now = new Date(); const end = new Date(createdAt); end.setDate(end.getDate() + 1); - setDiffMinute(Math.floor((end - now) / (1000 * 60))); + setDiffMinute(Math.floor((Number(end) - Number(now)) / (1000 * 60))); setInterval(() => diffMinuteHandler(), 60000); }, []); @@ -36,9 +55,10 @@ function EditAgreement({ <>
{myParty && ( - -
-
- - - ); -} - -export default ToggleButton; diff --git a/src/pages/PartyPage/EditMap/ToggleButton/index.tsx b/src/pages/PartyPage/EditMap/ToggleButton/index.tsx new file mode 100644 index 00000000..b0241831 --- /dev/null +++ b/src/pages/PartyPage/EditMap/ToggleButton/index.tsx @@ -0,0 +1,59 @@ +import { Dispatch, memo, SetStateAction, useCallback, useState } from "react"; +import ConfirmModal from "../../../../components/ConfirmModal"; +import clsx from "clsx"; + +interface Props { + showSearchMap: boolean; + setShowSearchMap: Dispatch>; + canToggle: boolean; +} + +function ToggleButton({ showSearchMap, setShowSearchMap, canToggle }: Props) { + const [showModal, setShowModal] = useState(false); + + const toggleHandler = useCallback( + (isSearchMode: boolean) => { + if (!canToggle) return setShowModal(true); + setShowSearchMap(isSearchMode); + }, + [canToggle] + ); + + return ( + <> +
+ + +
+
+ + + ); +} + +export default memo(ToggleButton); diff --git a/src/pages/PartyPage/EditMap/index.jsx b/src/pages/PartyPage/EditMap/index.tsx similarity index 57% rename from src/pages/PartyPage/EditMap/index.jsx rename to src/pages/PartyPage/EditMap/index.tsx index dede9fd8..6a596d09 100644 --- a/src/pages/PartyPage/EditMap/index.jsx +++ b/src/pages/PartyPage/EditMap/index.tsx @@ -1,31 +1,48 @@ -import { useEffect, useState } from "react"; +import { + Dispatch, + memo, + SetStateAction, + useCallback, + useEffect, + useState, +} from "react"; import { getPartyRegionList } from "../../../api/region"; +import { Destination, RegionData } from "../../../types"; import CourseMap from "../../../components/CourseMap"; import PlaceMap from "../../../components/PlaceMap"; import ToggleButton from "./ToggleButton"; -function EditMap({ courseData, setCourseData, setRegion }) { +interface Props { + courseData: Destination[]; + setCourseData: Dispatch>; + setRegion: Dispatch>; +} + +function EditMap({ courseData, setCourseData, setRegion }: Props) { const [showSearchMap, setShowSearchMap] = useState(true); - const [regionData, setRegionData] = useState([]); + const [regionData, setRegionData] = useState([]); - const findRegion = (index) => { - if (courseData.length === index) return null; + const findRegion = useCallback( + (index: number): string => { + if (courseData.length === index) return ""; - const targetRegion = regionData.filter((region) => - courseData[index]?.address?.includes(region.name) - )[0]; + const targetRegion = regionData.filter((region) => + courseData[index]?.address?.includes(region.name) + )[0]; - return targetRegion?.name || findRegion(index + 1); - }; + return targetRegion?.name || findRegion(index + 1); + }, + [regionData, courseData] + ); - const getPartyRegionListFunc = async () => { + const getPartyRegionListFunc = useCallback(async () => { try { const result = await getPartyRegionList(); setRegionData(result.payload); } catch (e) { console.log(e); } - }; + }, []); useEffect(() => { getPartyRegionListFunc(); @@ -51,7 +68,6 @@ function EditMap({ courseData, setCourseData, setRegion }) { {showSearchMap ? ( >; + content: string; + memberCount: number; + companions: { + name: string; + phoneNumber: string; + }[]; + getPartyData: (toScrollTop?: boolean) => void; + capacity: number; + headcount: number; + totalPrice: number; + partyName: string; + course: Course; + myParty: boolean; + courseData: Destination[]; + region: string; + name: string; + startTime: string; + endTime: string; + promotionId: number; +} function EditModal({ showModal, @@ -23,16 +61,20 @@ function EditModal({ startTime, endTime, promotionId, -}) { +}: Props) { const navigation = useNavigate(); - const modalRef = useRef(); + const modalRef = useRef(null); const { partyId } = useParams(); - const [message, setMessage] = useState(""); + const [message, setMessage] = useState(""); const [loading, setLoading] = useState(false); const [complete, setComplete] = useState(false); - const isMemberFull = headcount + memberCount === capacity; - const editPartyHandler = async () => { + const isMemberFull = useMemo( + () => headcount + memberCount === capacity, + [headcount, memberCount, capacity] + ); + + const editPartyHandler = useCallback(async () => { if (loading) return; try { @@ -111,14 +153,7 @@ function EditModal({ ); setComplete(true); - const GA_TRACKING_ID = import.meta.env.VITE_GA_TRACKING_ID; - const META_PIXEL_TRACKING_ID = import.meta.env - .VITE_META_PIXEL_TRACKING_ID; - if ( - GA_TRACKING_ID && - META_PIXEL_TRACKING_ID && - !window.location.href.includes("localhost") - ) { + if (isGAlive()) { ReactGA.event({ category: "기존 파티 참여", action: "15_exsting_suggestionsent_voting", @@ -129,24 +164,40 @@ function EditModal({ } finally { setLoading(false); } - }; + }, [ + loading, + partyId, + myParty, + course, + courseData, + startTime, + endTime, + region, + name, + partyName, + content, + memberCount, + companions, + ]); - const closeModal = () => { + const closeModal = useCallback(() => { if (complete) { - navigation(-1, { replace: true }); + navigation(-1); getPartyData(true); } - setShowModal(false); - }; + }, [complete]); - const modalOutSideClick = (e) => { - if (modalRef.current === e.target) closeModal(); - }; + const modalOutSideClick = useCallback( + (event: MouseEvent) => { + if (modalRef.current === event.target) closeModal(); + }, + [modalRef] + ); - const handleKeyPress = (event) => { + const handleKeyPress = useCallback((event: KeyboardEvent) => { if (event.key === "Escape") closeModal(); - }; + }, []); useEffect(() => { if (!showModal) return document.body.classList.remove("overflow-hidden"); @@ -200,13 +251,7 @@ function EditModal({
); - const GA_TRACKING_ID = import.meta.env.VITE_GA_TRACKING_ID; - const META_PIXEL_TRACKING_ID = import.meta.env.VITE_META_PIXEL_TRACKING_ID; - if ( - GA_TRACKING_ID && - META_PIXEL_TRACKING_ID && - !window.location.href.includes("localhost") - ) { + if (isGAlive()) { ReactGA.event({ category: "기존 파티 참여", action: "14_exsting_joinsuggestion_voting", @@ -221,11 +266,12 @@ function EditModal({ return (
modalOutSideClick(e)} + onClick={modalOutSideClick} >
@@ -259,4 +305,4 @@ function EditModal({ ); } -export default EditModal; +export default memo(EditModal); diff --git a/src/pages/PartyPage/JoinAgreement/AgreeItem/index.jsx b/src/pages/PartyPage/JoinAgreement/AgreeItem/index.tsx similarity index 65% rename from src/pages/PartyPage/JoinAgreement/AgreeItem/index.jsx rename to src/pages/PartyPage/JoinAgreement/AgreeItem/index.tsx index df46830a..e4451b61 100644 --- a/src/pages/PartyPage/JoinAgreement/AgreeItem/index.jsx +++ b/src/pages/PartyPage/JoinAgreement/AgreeItem/index.tsx @@ -1,7 +1,16 @@ +import { ChangeEvent, memo } from "react"; import { useNavigate } from "react-router-dom"; -import { ReactComponent as Check } from "../../../../assets/svg/agree-check.svg"; +import Check from "../../../../assets/svg/agree-check.svg"; -function AgreeItem({ checkedHandler, checked, id, title, url }) { +interface Props { + checkedHandler: (e: ChangeEvent, id: number) => void; + checked: boolean; + id: number; + title: string; + url: string; +} + +function AgreeItem({ checkedHandler, checked, id, title, url }: Props) { const navigation = useNavigate(); return ( @@ -19,7 +28,9 @@ function AgreeItem({ checkedHandler, checked, id, title, url }) { className="flex items-center cursor-pointer" >
- {checked && } + {checked && ( + + )}
{title} @@ -34,4 +45,4 @@ function AgreeItem({ checkedHandler, checked, id, title, url }) { ); } -export default AgreeItem; +export default memo(AgreeItem); diff --git a/src/pages/PartyPage/JoinAgreement/index.jsx b/src/pages/PartyPage/JoinAgreement/index.jsx deleted file mode 100644 index f385d0c8..00000000 --- a/src/pages/PartyPage/JoinAgreement/index.jsx +++ /dev/null @@ -1,86 +0,0 @@ -import { useEffect, useState } from "react"; -import { ReactComponent as Check } from "../../../assets/svg/agree-check.svg"; -import AgreeItem from "./AgreeItem"; - -const agreementData = [ - { - id: 0, - title: "말랑트립 서비스 이용약관(일반회원용) (필수)", - url: "/policy/user/service", - }, - { - id: 1, - title: "말랑트립 개인정보 처리방침(일반회원용) (필수)", - url: "/policy/user/privacy", - }, -]; - -function JoinAgreement({ checked, setChecked, shakeAgree, agreementRef }) { - const [allChecked, setAllChecked] = useState(false); - const [showText, setShowText] = useState(false); - - const allCheckedHandler = (e) => { - setAllChecked(e.target.checked); - setChecked(checked.map((_) => e.target.checked)); - }; - - const checkedHandler = (e, num) => { - const newChecked = [...checked]; - newChecked[num] = e.target.checked; - setChecked(newChecked); - }; - - useEffect(() => { - if (checked.indexOf(false) < 0) setAllChecked(true); - else setAllChecked(false); - }, [checked]); - - useEffect(() => { - if (shakeAgree) setShowText(true); - }, [shakeAgree]); - - useEffect(() => { - if (allChecked) setShowText(false); - }, [allChecked]); - - return ( -
-
-
필수 약관
-
- 필수 약관에 동의해 주세요! -
-
-
-
- - -
- {agreementData.map((item, index) => ( - - ))} -
-
- ); -} - -export default JoinAgreement; diff --git a/src/pages/PartyPage/JoinAgreement/index.tsx b/src/pages/PartyPage/JoinAgreement/index.tsx new file mode 100644 index 00000000..1cf3cd3c --- /dev/null +++ b/src/pages/PartyPage/JoinAgreement/index.tsx @@ -0,0 +1,122 @@ +import { + ChangeEvent, + Dispatch, + ForwardedRef, + memo, + SetStateAction, + useCallback, + useEffect, + useMemo, + useState, +} from "react"; +import Check from "../../../assets/svg/agree-check.svg"; +import AgreeItem from "./AgreeItem"; +import clsx from "clsx"; + +interface Props { + checked: boolean[]; + setChecked: Dispatch>; + shakeAgree: boolean; + agreementRef: ForwardedRef; +} + +function JoinAgreement({ + checked, + setChecked, + shakeAgree, + agreementRef, +}: Props) { + const [allChecked, setAllChecked] = useState(false); + const [showText, setShowText] = useState(false); + + const agreementData = useMemo( + () => [ + { + id: 0, + title: "말랑트립 서비스 이용약관(일반회원용) (필수)", + url: "/policy/user/service", + }, + { + id: 1, + title: "말랑트립 개인정보 처리방침(일반회원용) (필수)", + url: "/policy/user/privacy", + }, + ], + [] + ); + + const allCheckedHandler = useCallback( + (e: ChangeEvent) => { + setAllChecked(e.target.checked); + setChecked(checked.map((_) => e.target.checked)); + }, + [checked] + ); + + const checkedHandler = useCallback( + (e: ChangeEvent, id: number) => { + const newChecked = [...checked]; + newChecked[id] = e.target.checked; + setChecked(newChecked); + }, + [checked] + ); + + useEffect(() => { + if (checked.indexOf(false) < 0) setAllChecked(true); + else setAllChecked(false); + }, [checked]); + + useEffect(() => { + if (shakeAgree) setShowText(true); + }, [shakeAgree]); + + useEffect(() => { + if (allChecked) setShowText(false); + }, [allChecked]); + + return ( +
+
+
필수 약관
+
+ 필수 약관에 동의해 주세요! +
+
+
+
+ + +
+ {agreementData.map((item, index) => ( + + ))} +
+
+ ); +} + +export default memo(JoinAgreement); diff --git a/src/pages/PartyPage/JoinButton/index.jsx b/src/pages/PartyPage/JoinButton/index.tsx similarity index 63% rename from src/pages/PartyPage/JoinButton/index.jsx rename to src/pages/PartyPage/JoinButton/index.tsx index d89ab4c3..f6eda030 100644 --- a/src/pages/PartyPage/JoinButton/index.jsx +++ b/src/pages/PartyPage/JoinButton/index.tsx @@ -1,18 +1,27 @@ -import { useEffect, useState } from "react"; +import { memo, useEffect, useMemo, useState } from "react"; import { useParams } from "react-router-dom"; import { useSelector } from "react-redux"; +import { RootState } from "../../../redux/store"; import BottomButton from "../../../components/BottomButton"; -function JoinButton({ joinHandler, partyStatus }) { - const user = useSelector((state) => state.user); +interface Props { + joinHandler: () => void; + partyStatus: string; +} + +function JoinButton({ joinHandler, partyStatus }: Props) { + const user = useSelector((state: RootState) => state.user); const { type } = useParams(); const [scrollPosition, setScrollPosition] = useState(0); - const buttonText = { - detail: "파티 가입하기", - join: "가입 완료하기", - edit: "제안 보내기", - }; + const buttonText = useMemo( + () => ({ + detail: "파티 가입하기", + join: "가입 완료하기", + edit: "제안 보내기", + }), + [] + ); useEffect(() => { const handleScroll = () => { @@ -26,6 +35,7 @@ function JoinButton({ joinHandler, partyStatus }) { }, []); if (user.role === "ROLE_DRIVER" || partyStatus !== "RECRUITING") return null; + if (type !== "detail" && type !== "join" && type !== "edit") return null; return ( <>
@@ -43,4 +53,4 @@ function JoinButton({ joinHandler, partyStatus }) { ); } -export default JoinButton; +export default memo(JoinButton); diff --git a/src/pages/PartyPage/JoinGreeting/index.jsx b/src/pages/PartyPage/JoinGreeting/index.tsx similarity index 55% rename from src/pages/PartyPage/JoinGreeting/index.jsx rename to src/pages/PartyPage/JoinGreeting/index.tsx index 0cd86bb1..a228d875 100644 --- a/src/pages/PartyPage/JoinGreeting/index.jsx +++ b/src/pages/PartyPage/JoinGreeting/index.tsx @@ -1,6 +1,13 @@ -function JoinGreeting({ content, setContent }) { +import { Dispatch, memo, SetStateAction } from "react"; + +interface Props { + content: string; + setContent: Dispatch>; +} + +function JoinGreeting({ content, setContent }: Props) { return ( -
+