diff --git a/apps/web/src/app/(home)/_ui/NewsSection/_hooks/useSectionHadnler.ts b/apps/web/src/app/(home)/_ui/NewsSection/_hooks/useSectionHadnler.ts deleted file mode 100644 index 23cfcadb..00000000 --- a/apps/web/src/app/(home)/_ui/NewsSection/_hooks/useSectionHadnler.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { type RefObject, useEffect, useRef, useState } from "react"; - -interface UseSectionHandlerReturn { - sectionRef: RefObject; - visible: boolean; -} - -const useSectionHandler = (): UseSectionHandlerReturn => { - const [visible, setVisible] = useState(false); - - const sectionRef = useRef(null!); - useEffect(() => { - if (!sectionRef.current) return; - - const observer = new window.IntersectionObserver( - ([entry]) => { - if (entry.isIntersecting) { - setVisible(true); - observer.disconnect(); - } - }, - { - rootMargin: "0px", - threshold: 0, - }, - ); - - observer.observe(sectionRef.current); - - return () => observer.disconnect(); - }, []); - return { - sectionRef, - visible, - }; -}; - -export default useSectionHandler; diff --git a/apps/web/src/app/(home)/_ui/NewsSection/index.tsx b/apps/web/src/app/(home)/_ui/NewsSection/index.tsx index e0c4e3b8..8a7c0215 100644 --- a/apps/web/src/app/(home)/_ui/NewsSection/index.tsx +++ b/apps/web/src/app/(home)/_ui/NewsSection/index.tsx @@ -1,17 +1,38 @@ "use client"; import Link from "next/link"; +import { useEffect, useRef, useState } from "react"; import Image from "@/components/ui/FallbackImage"; import { IconLoveLetter } from "@/public/svgs/home"; import type { News } from "@/types/news"; -import useSectionHandler from "./_hooks/useSectionHadnler"; export type NewsSectionProps = { newsList: News[]; }; const NewsSection = ({ newsList }: NewsSectionProps) => { - const { sectionRef, visible } = useSectionHandler(); + const [visible, setVisible] = useState(false); + const sectionRef = useRef(null); + + useEffect(() => { + if (!sectionRef.current) return; + + const observer = new window.IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + setVisible(true); + observer.disconnect(); + } + }, + { + rootMargin: "0px", + threshold: 0, + }, + ); + + observer.observe(sectionRef.current); + return () => observer.disconnect(); + }, []); return (
diff --git a/apps/web/src/app/(home)/_ui/UniversityList/_hooks/useRegionHandler.ts b/apps/web/src/app/(home)/_ui/UniversityList/_hooks/useRegionHandler.ts deleted file mode 100644 index 12b467cf..00000000 --- a/apps/web/src/app/(home)/_ui/UniversityList/_hooks/useRegionHandler.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { useEffect, useState } from "react"; - -import { RegionEnumExtend } from "@/types/university"; - -const useRegionHandler = () => { - const [region, setRegion] = useState(RegionEnumExtend.ALL); - - const handleRegionChange = (newRegion: RegionEnumExtend | null) => { - setRegion(newRegion); - }; - useEffect(() => { - if (region === null) { - setRegion(RegionEnumExtend.ALL); - } - }, [region]); - - return { - region, - handleRegionChange, - }; -}; - -export default useRegionHandler; diff --git a/apps/web/src/app/(home)/_ui/UniversityList/index.tsx b/apps/web/src/app/(home)/_ui/UniversityList/index.tsx index 5722b8ab..478e63c4 100644 --- a/apps/web/src/app/(home)/_ui/UniversityList/index.tsx +++ b/apps/web/src/app/(home)/_ui/UniversityList/index.tsx @@ -1,22 +1,30 @@ "use client"; import Link from "next/link"; -import { useMemo } from "react"; +import { useEffect, useMemo, useState } from "react"; import ButtonTab from "@/components/ui/ButtonTab"; import UniversityCards from "@/components/university/UniversityCards"; import { IconDirectionRight } from "@/public/svgs/mentor"; import { type AllRegionsUniversityList, type ListUniversity, RegionEnumExtend } from "@/types/university"; -import useRegionHandler from "./_hooks/useRegionHandler"; interface UniversityListProps { allRegionsUniversityList: AllRegionsUniversityList; } const UniversityList = ({ allRegionsUniversityList }: UniversityListProps) => { - const { region, handleRegionChange } = useRegionHandler(); + const [region, setRegion] = useState(RegionEnumExtend.ALL); const choices = Object.values(RegionEnumExtend); + const handleRegionChange = (newRegion: RegionEnumExtend | null) => { + setRegion(newRegion); + }; + + useEffect(() => { + if (region === null) { + setRegion(RegionEnumExtend.ALL); + } + }, [region]); const universities: ListUniversity[] = useMemo( () => allRegionsUniversityList[region || RegionEnumExtend.ALL] ?? [], diff --git a/apps/web/src/app/login/LoginContent.tsx b/apps/web/src/app/login/LoginContent.tsx index 06d49c93..0a5a898d 100644 --- a/apps/web/src/app/login/LoginContent.tsx +++ b/apps/web/src/app/login/LoginContent.tsx @@ -3,7 +3,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import Link from "next/link"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { usePostEmailAuth } from "@/apis/Auth"; @@ -11,7 +11,6 @@ import { toast } from "@/lib/zustand/useToastStore"; import { IconSolidConnectionFullBlackLogo } from "@/public/svgs"; import { IconAppleLogo, IconEmailIcon, IconKakaoLogo } from "@/public/svgs/auth"; import { appleLogin, kakaoLogin } from "@/utils/authUtils"; -import useInputHandler from "./_hooks/useInputHandler"; // Zod 스키마 정의 const loginSchema = z.object({ @@ -28,9 +27,14 @@ const LoginContent = () => { const pathname = usePathname(); const searchParams = useSearchParams(); const hasShownCommunityOnlyToast = useRef(false); + const [showPasswordField, setShowPasswordField] = useState(false); const { mutate: postEmailAuth, isPending } = usePostEmailAuth(); - const { showPasswordField, handleEmailChange } = useInputHandler(); + + const handleEmailChange = (e: React.ChangeEvent) => { + const value = e.target.value.trim(); + setShowPasswordField(value.length > 0); + }; const { register, diff --git a/apps/web/src/app/login/_hooks/useInputHandler.ts b/apps/web/src/app/login/_hooks/useInputHandler.ts deleted file mode 100644 index db1de905..00000000 --- a/apps/web/src/app/login/_hooks/useInputHandler.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useState } from "react"; - -interface UseInputHandlerReturn { - showPasswordField: boolean; - handleEmailChange: (e: React.ChangeEvent) => void; -} - -const useInputHandler = (): UseInputHandlerReturn => { - // 비밀번호 필드 표시 상태를 위한 state - const [showPasswordField, setShowPasswordField] = useState(false); - - // 이메일 입력 시 비밀번호 필드 표시 제어 - const handleEmailChange = (e: React.ChangeEvent) => { - const value = e.target.value.trim(); - setShowPasswordField(value.length > 0); - }; - return { - showPasswordField, - handleEmailChange, - }; -}; -export default useInputHandler; diff --git a/apps/web/src/app/mentor/[id]/_ui/MentorDetialContent/_ui/MentorArticle/_hooks/useLikeToggle.ts b/apps/web/src/app/mentor/[id]/_ui/MentorDetialContent/_ui/MentorArticle/_hooks/useLikeToggle.ts deleted file mode 100644 index 12d67b6f..00000000 --- a/apps/web/src/app/mentor/[id]/_ui/MentorDetialContent/_ui/MentorArticle/_hooks/useLikeToggle.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { useEffect, useState } from "react"; - -import { useDeleteArticleLike, usePostArticleLike } from "@/apis/news"; - -const useLikeToggle = (articleId: number, mentorId: number, articleIsLiked?: boolean) => { - const { mutate: postArticleLike } = usePostArticleLike(mentorId); - const { mutate: deleteArticleLike } = useDeleteArticleLike(mentorId); - const [isLiked, setIsLiked] = useState(articleIsLiked ?? false); - - const handleToggleLike = () => { - if (!isLiked) { - postArticleLike(articleId); - } else { - deleteArticleLike(articleId); - } - }; - useEffect(() => { - if (articleIsLiked !== undefined) { - setIsLiked(articleIsLiked); - } - }, [articleIsLiked]); - - return { - isLiked, - handleToggleLike, - }; -}; -export default useLikeToggle; diff --git a/apps/web/src/app/mentor/[id]/_ui/MentorDetialContent/_ui/MentorArticle/index.tsx b/apps/web/src/app/mentor/[id]/_ui/MentorDetialContent/_ui/MentorArticle/index.tsx index 938c0382..8dfbd10f 100644 --- a/apps/web/src/app/mentor/[id]/_ui/MentorDetialContent/_ui/MentorArticle/index.tsx +++ b/apps/web/src/app/mentor/[id]/_ui/MentorDetialContent/_ui/MentorArticle/index.tsx @@ -1,10 +1,11 @@ "use client"; +import { useEffect, useState } from "react"; +import { useDeleteArticleLike, usePostArticleLike } from "@/apis/news"; import Image from "@/components/ui/FallbackImage"; import { IconLikeFill, IconLikeNotFill } from "@/public/svgs/mentor"; import type { Article } from "@/types/news"; import { convertUploadedImageUrl } from "@/utils/fileUtils"; -import useLikeToggle from "./_hooks/useLikeToggle"; interface MentorArticleProps { article: Article; @@ -12,7 +13,24 @@ interface MentorArticleProps { } const MentorArticle = ({ article, mentorId }: MentorArticleProps) => { - const { isLiked, handleToggleLike } = useLikeToggle(article.id, mentorId, article.isLiked); + const { mutate: postArticleLike } = usePostArticleLike(mentorId); + const { mutate: deleteArticleLike } = useDeleteArticleLike(mentorId); + const [isLiked, setIsLiked] = useState(article.isLiked ?? false); + + const handleToggleLike = () => { + if (!isLiked) { + postArticleLike(article.id); + } else { + deleteArticleLike(article.id); + } + }; + + useEffect(() => { + if (article.isLiked !== undefined) { + setIsLiked(article.isLiked); + } + }, [article.isLiked]); + const thumbnailUrl = convertUploadedImageUrl(article.thumbnailUrl); return (
diff --git a/apps/web/src/app/mentor/_ui/MentorClient/_ui/MentorFindSection/_hooks/usePrefetchMentorFindTab.ts b/apps/web/src/app/mentor/_ui/MentorClient/_ui/MentorFindSection/_hooks/usePrefetchMentorFindTab.ts deleted file mode 100644 index d664ae49..00000000 --- a/apps/web/src/app/mentor/_ui/MentorClient/_ui/MentorFindSection/_hooks/usePrefetchMentorFindTab.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { useEffect } from "react"; -import { usePrefetchMentorList } from "@/apis/mentor"; -import { FilterTab } from "@/types/mentor"; - -const usePrefetchMentorFindTab = () => { - const { prefetchMentorList } = usePrefetchMentorList(); - - // 컴포넌트 마운트 시 모든 탭 데이터 프리페치 - useEffect(() => { - const prefetchAllTabs = async () => { - // 현재 선택된 탭안 전체탭 제외한 나머지 탭들 프리페치 - const tabsToPrefetch = Object.values(FilterTab).filter((tab) => tab !== FilterTab.ALL); - tabsToPrefetch.forEach((tab) => { - prefetchMentorList(tab); - }); - }; - - // 1초 후에 프리페치 실행 (초기 로딩 후) - const timer = setTimeout(prefetchAllTabs, 1000); - return () => clearTimeout(timer); - }, [prefetchMentorList]); -}; -export default usePrefetchMentorFindTab; diff --git a/apps/web/src/app/mentor/_ui/MentorClient/_ui/MentorFindSection/_hooks/useSelectedTab.ts b/apps/web/src/app/mentor/_ui/MentorClient/_ui/MentorFindSection/_hooks/useSelectedTab.ts deleted file mode 100644 index 6aff076b..00000000 --- a/apps/web/src/app/mentor/_ui/MentorClient/_ui/MentorFindSection/_hooks/useSelectedTab.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { type RefObject, useRef, useState } from "react"; - -import { FilterTab } from "@/types/mentor"; - -interface UseSelectedTabReturn { - listRef: RefObject; - selectedTab: FilterTab; - handleSelectTab: (tab: FilterTab) => void; -} - -const useSelectedTab = (): UseSelectedTabReturn => { - const [selectedTab, setSelectedTab] = useState(FilterTab.ALL); - const listRef = useRef(null!); - - const handleSelectTab = (tab: FilterTab) => { - setSelectedTab(tab); - if (listRef.current) { - listRef.current.scrollTo({ top: 0, behavior: "smooth" }); - } - }; - - return { listRef, selectedTab, handleSelectTab }; -}; -export default useSelectedTab; diff --git a/apps/web/src/app/mentor/_ui/MentorClient/_ui/MentorFindSection/index.tsx b/apps/web/src/app/mentor/_ui/MentorClient/_ui/MentorFindSection/index.tsx index 1121423d..6e65fcc3 100644 --- a/apps/web/src/app/mentor/_ui/MentorClient/_ui/MentorFindSection/index.tsx +++ b/apps/web/src/app/mentor/_ui/MentorClient/_ui/MentorFindSection/index.tsx @@ -1,17 +1,37 @@ "use client"; -import { useGetMentorList } from "@/apis/mentor"; +import { useEffect, useRef, useState } from "react"; +import { useGetMentorList, usePrefetchMentorList } from "@/apis/mentor"; import MentorCard from "@/components/mentor/MentorCard"; import EmptySdwBCards from "@/components/ui/EmptySdwBCards"; import FloatingUpBtn from "@/components/ui/FloatingUpBtn"; import { FilterTab } from "@/types/mentor"; import useInfinityScroll from "@/utils/useInfinityScroll"; -import usePrefetchMentorFindTab from "./_hooks/usePrefetchMentorFindTab"; -import useSelectedTab from "./_hooks/useSelectedTab"; const MentorFindSection = () => { - const { listRef, selectedTab, handleSelectTab } = useSelectedTab(); + const [selectedTab, setSelectedTab] = useState(FilterTab.ALL); + const listRef = useRef(null); + const { prefetchMentorList } = usePrefetchMentorList(); + + const handleSelectTab = (tab: FilterTab) => { + setSelectedTab(tab); + if (listRef.current) { + listRef.current.scrollTo({ top: 0, behavior: "smooth" }); + } + }; + + useEffect(() => { + const prefetchAllTabs = () => { + const tabsToPrefetch = Object.values(FilterTab).filter((tab) => tab !== FilterTab.ALL); + tabsToPrefetch.forEach((tab) => { + prefetchMentorList(tab); + }); + }; + + const timer = setTimeout(prefetchAllTabs, 1000); + return () => clearTimeout(timer); + }, [prefetchMentorList]); const { data: mentorList = [], @@ -21,7 +41,6 @@ const MentorFindSection = () => { region: selectedTab !== FilterTab.ALL ? selectedTab : "", }); const { lastElementRef } = useInfinityScroll({ fetchNextPage, hasNextPage }); - usePrefetchMentorFindTab(); return (
diff --git a/apps/web/src/app/mentor/chat/[chatId]/_ui/ChatContent/_hooks/usePutChatReadHandler.ts b/apps/web/src/app/mentor/chat/[chatId]/_ui/ChatContent/_hooks/usePutChatReadHandler.ts deleted file mode 100644 index f40124cc..00000000 --- a/apps/web/src/app/mentor/chat/[chatId]/_ui/ChatContent/_hooks/usePutChatReadHandler.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { useEffect } from "react"; - -import { usePutChatRead } from "@/apis/chat"; - -const usePutChatReadHandler = (chatId: number) => { - const { mutate: putChatRead } = usePutChatRead(); - - useEffect(() => { - if (chatId) { - putChatRead(chatId); - } - }, [chatId, putChatRead]); -}; -export default usePutChatReadHandler; diff --git a/apps/web/src/app/mentor/chat/[chatId]/_ui/ChatContent/index.tsx b/apps/web/src/app/mentor/chat/[chatId]/_ui/ChatContent/index.tsx index 8fb606ad..1cf3ae6d 100644 --- a/apps/web/src/app/mentor/chat/[chatId]/_ui/ChatContent/index.tsx +++ b/apps/web/src/app/mentor/chat/[chatId]/_ui/ChatContent/index.tsx @@ -2,7 +2,8 @@ import clsx from "clsx"; import Link from "next/link"; -import { useGetPartnerInfo } from "@/apis/chat"; +import { useEffect } from "react"; +import { useGetPartnerInfo, usePutChatRead } from "@/apis/chat"; import { useUploadProfileImage } from "@/apis/image-upload"; import ProfileWithBadge from "@/components/ui/ProfileWithBadge"; @@ -12,7 +13,6 @@ import { ConnectionStatus } from "@/types/chat"; import { UserRole } from "@/types/mentor"; import { tokenParse } from "@/utils/jwtUtils"; import useChatListHandler from "./_hooks/useChatListHandler"; -import usePutChatReadHandler from "./_hooks/usePutChatReadHandler"; import { formatDateSeparator, isSameDay } from "./_lib/dateUtils"; import ChatInputBar from "./_ui/ChatInputBar"; import ChatMessageBox from "./_ui/ChatMessageBox"; @@ -27,9 +27,13 @@ const ChatContent = ({ chatId }: ChatContentProps) => { const userId = parsedData?.sub ?? 0; const isMentor = parsedData?.role === UserRole.MENTOR || parsedData?.role === UserRole.ADMIN; + const { mutate: putChatRead } = usePutChatRead(); - // 채팅 읽음 상태 업데이트 훅 진입시 자동으로 - usePutChatReadHandler(chatId); + useEffect(() => { + if (chatId) { + putChatRead(chatId); + } + }, [chatId, putChatRead]); // 채팅 리스트 관리 훅 const { diff --git a/apps/web/src/components/mentor/MentorCard/hooks/usePostApplyMentorHandler.ts b/apps/web/src/components/mentor/MentorCard/hooks/usePostApplyMentorHandler.ts deleted file mode 100644 index bc0723ae..00000000 --- a/apps/web/src/components/mentor/MentorCard/hooks/usePostApplyMentorHandler.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { useRouter } from "next/navigation"; - -import { usePostApplyMentoring } from "@/apis/mentor"; -import { customConfirm } from "@/lib/zustand/useConfirmModalStore"; -import { IconCheck } from "@/public/svgs/mentor"; - -interface UsePostApplyMentorHandlerReturn { - handlePostApplyMentor: (mentorId: number) => Promise; -} -const usePostApplyMentorHandler = (): UsePostApplyMentorHandlerReturn => { - const router = useRouter(); - - const { mutate: postApplyMentoring } = usePostApplyMentoring(); - - const handlePostApplyMentor = async (mentorId: number) => { - postApplyMentoring( - { mentorId }, - { - onSuccess: async () => { - const ok = await customConfirm({ - title: "멘티 신청이 완료되었어요!", - content: "멘토가 신청을 수락하면 대화를 시작할 수 있어요.\n조금만 기다려주세요.", - icon: IconCheck, - approveMessage: "다른 멘토 찾기", - rejectMessage: "홈으로", - }); - if (!ok) { - router.push("/"); - } - }, - }, - ); - }; - return { handlePostApplyMentor }; -}; -export default usePostApplyMentorHandler; diff --git a/apps/web/src/components/mentor/MentorCard/index.tsx b/apps/web/src/components/mentor/MentorCard/index.tsx index f78151d7..0a6c3cca 100644 --- a/apps/web/src/components/mentor/MentorCard/index.tsx +++ b/apps/web/src/components/mentor/MentorCard/index.tsx @@ -2,13 +2,15 @@ import clsx from "clsx"; import Link from "next/link"; +import { useRouter } from "next/navigation"; import { useState } from "react"; -import { IconDirectionDown, IconDirectionUp } from "@/public/svgs/mentor"; +import { usePostApplyMentoring } from "@/apis/mentor"; +import { customConfirm } from "@/lib/zustand/useConfirmModalStore"; +import { IconCheck, IconDirectionDown, IconDirectionUp } from "@/public/svgs/mentor"; import type { MentorCardDetail, MentorCardPreview } from "@/types/mentor"; import ChannelBadge from "../../ui/ChannelBadge"; import ProfileWithBadge from "../../ui/ProfileWithBadge"; import StudyDate from "../StudyDate"; -import usePostApplyMentorHandler from "./hooks/usePostApplyMentorHandler"; interface MentorCardProps { mentor: MentorCardDetail | MentorCardPreview; @@ -17,8 +19,28 @@ interface MentorCardProps { } const MentorCard = ({ mentor, observeRef, isMine = false }: MentorCardProps) => { + const router = useRouter(); + const { mutate: postApplyMentoring } = usePostApplyMentoring(); const [isExpanded, setIsExpanded] = useState(false); - const { handlePostApplyMentor } = usePostApplyMentorHandler(); + const handlePostApplyMentor = async (mentorId: number) => { + postApplyMentoring( + { mentorId }, + { + onSuccess: async () => { + const ok = await customConfirm({ + title: "멘티 신청이 완료되었어요!", + content: "멘토가 신청을 수락하면 대화를 시작할 수 있어요.\n조금만 기다려주세요.", + icon: IconCheck, + approveMessage: "다른 멘토 찾기", + rejectMessage: "홈으로", + }); + if (!ok) { + router.push("/"); + } + }, + }, + ); + }; const { profileImageUrl, diff --git a/apps/web/src/components/mentor/MentorExpandChatCard/hooks/usePatchApprovalStatusHandler.ts b/apps/web/src/components/mentor/MentorExpandChatCard/hooks/usePatchApprovalStatusHandler.ts deleted file mode 100644 index 1ad3e734..00000000 --- a/apps/web/src/components/mentor/MentorExpandChatCard/hooks/usePatchApprovalStatusHandler.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { usePatchApprovalStatus } from "@/apis/mentor"; -import { customConfirm } from "@/lib/zustand/useConfirmModalStore"; -import { IconUnSmile } from "@/public/svgs/mentor"; -import { MentoringApprovalStatus } from "@/types/mentor"; - -const usePatchApprovalStatusHandler = () => { - const { mutate: patchApprovalStatus } = usePatchApprovalStatus(); - - const handleAccept = (mentoringId: number) => { - patchApprovalStatus({ status: MentoringApprovalStatus.APPROVED, mentoringId }); - }; - - const handleReject = async (mentoringId: number) => { - const ok = await customConfirm({ - title: "멘티 신청을 정말 거절하시겠어요?", - content: "누적 5회 거절 시 멘토 활동에 제약이 있을 수 있어요. 다시 한 번 고민해보세요.", - icon: IconUnSmile, - approveMessage: "다시 생각해볼게요", - rejectMessage: "그래도 거절할래요", - }); - if (ok) return; - patchApprovalStatus({ status: MentoringApprovalStatus.REJECTED, mentoringId }); - }; - - return { handleAccept, handleReject }; -}; - -export default usePatchApprovalStatusHandler; diff --git a/apps/web/src/components/mentor/MentorExpandChatCard/index.tsx b/apps/web/src/components/mentor/MentorExpandChatCard/index.tsx index 3324d034..f402942a 100644 --- a/apps/web/src/components/mentor/MentorExpandChatCard/index.tsx +++ b/apps/web/src/components/mentor/MentorExpandChatCard/index.tsx @@ -1,10 +1,11 @@ "use client"; import Link from "next/link"; -import usePatchApprovalStatusHandler from "@/components/mentor/MentorExpandChatCard/hooks/usePatchApprovalStatusHandler"; +import { usePatchApprovalStatus } from "@/apis/mentor"; import ProfileWithBadge from "@/components/ui/ProfileWithBadge"; -import { IconDirectionDown, IconDirectionUp } from "@/public/svgs/mentor"; -import { MentoringApplyStatus } from "@/types/mentor"; +import { customConfirm } from "@/lib/zustand/useConfirmModalStore"; +import { IconDirectionDown, IconDirectionUp, IconUnSmile } from "@/public/svgs/mentor"; +import { MentoringApplyStatus, MentoringApprovalStatus } from "@/types/mentor"; import { convertISODateToKoreanTime } from "@/utils/datetimeUtils"; import useExpandCardClickHandler from "./hooks/useExpandCardClickHandler"; @@ -33,7 +34,23 @@ const MentorExpandChatCard = ({ mentoringId, initChecked: isChecked, }); - const { handleAccept, handleReject } = usePatchApprovalStatusHandler(); + const { mutate: patchApprovalStatus } = usePatchApprovalStatus(); + + const handleAccept = (targetMentoringId: number) => { + patchApprovalStatus({ status: MentoringApprovalStatus.APPROVED, mentoringId: targetMentoringId }); + }; + + const handleReject = async (targetMentoringId: number) => { + const ok = await customConfirm({ + title: "멘티 신청을 정말 거절하시겠어요?", + content: "누적 5회 거절 시 멘토 활동에 제약이 있을 수 있어요. 다시 한 번 고민해보세요.", + icon: IconUnSmile, + approveMessage: "다시 생각해볼게요", + rejectMessage: "그래도 거절할래요", + }); + if (ok) return; + patchApprovalStatus({ status: MentoringApprovalStatus.REJECTED, mentoringId: targetMentoringId }); + }; return (
diff --git a/apps/web/src/components/ui/FloatingUpBtn/hooks/useFloatingUpHandler.ts b/apps/web/src/components/ui/FloatingUpBtn/hooks/useFloatingUpHandler.ts deleted file mode 100644 index e2a28e92..00000000 --- a/apps/web/src/components/ui/FloatingUpBtn/hooks/useFloatingUpHandler.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { useCallback, useEffect, useState } from "react"; - -const useFloatingUpHandler = (scrollYThreshold: number = 400) => { - const [isVisible, setIsVisible] = useState(false); - - const handleScroll = useCallback(() => { - setIsVisible(window.scrollY > scrollYThreshold); - }, [scrollYThreshold]); - - const handleClick = () => { - window.scrollTo({ - top: 0, - behavior: "smooth", - }); - }; - - useEffect(() => { - window.addEventListener("scroll", handleScroll); - return () => { - window.removeEventListener("scroll", handleScroll); - }; - }, [handleScroll]); - - return { isVisible, handleClick }; -}; - -export default useFloatingUpHandler; diff --git a/apps/web/src/components/ui/FloatingUpBtn/index.tsx b/apps/web/src/components/ui/FloatingUpBtn/index.tsx index 9e062be4..a353ccd8 100644 --- a/apps/web/src/components/ui/FloatingUpBtn/index.tsx +++ b/apps/web/src/components/ui/FloatingUpBtn/index.tsx @@ -1,11 +1,31 @@ -import useFloatingUpHandler from "./hooks/useFloatingUpHandler"; +import { useCallback, useEffect, useState } from "react"; interface FloatingUpBtnProps { scrollYThreshold?: number; } -const FloatingUpBtn = ({ scrollYThreshold }: FloatingUpBtnProps) => { - const { isVisible, handleClick } = useFloatingUpHandler(scrollYThreshold); +const FloatingUpBtn = ({ scrollYThreshold = 400 }: FloatingUpBtnProps) => { + const [isVisible, setIsVisible] = useState(false); + + const handleScroll = useCallback(() => { + setIsVisible(window.scrollY > scrollYThreshold); + }, [scrollYThreshold]); + + const handleClick = () => { + window.scrollTo({ + top: 0, + behavior: "smooth", + }); + }; + + useEffect(() => { + window.addEventListener("scroll", handleScroll); + handleScroll(); + return () => { + window.removeEventListener("scroll", handleScroll); + }; + }, [handleScroll]); + if (!isVisible) return null; return (