From afe891831a84d2dbf6ad77cd1f7538828b027953 Mon Sep 17 00:00:00 2001 From: iOdiO89 <117376841+iOdiO89@users.noreply.github.com> Date: Tue, 25 Jun 2024 00:52:58 +0900 Subject: [PATCH 1/8] =?UTF-8?q?fix:=20=EB=82=B4=EA=B0=80=20=EC=B0=B8?= =?UTF-8?q?=EC=97=AC=ED=95=9C=20=EC=B1=8C=EB=A6=B0=EC=A7=80=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/home/challenge.tsx | 21 +++++++++++++++------ components/home/challengeBox.tsx | 8 ++++++-- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/components/home/challenge.tsx b/components/home/challenge.tsx index 3015706..29f068d 100644 --- a/components/home/challenge.tsx +++ b/components/home/challenge.tsx @@ -3,12 +3,17 @@ import FlexBox from "../Flexbox"; import { useRouter } from "next/router"; import { useAtom } from "jotai"; import { isAdminAtom } from "@/utils/atom"; +import { Challenge as ChallengeType } from "@/apis/challenge"; interface ChallengeProps { setIsModalVisible: React.Dispatch>; + challengeInfo: ChallengeType; } -const Challenge: NextPage = ({ setIsModalVisible }) => { +const Challenge: NextPage = ({ + setIsModalVisible, + challengeInfo, +}) => { const router = useRouter(); const [isAdmin] = useAtom(isAdminAtom); @@ -20,10 +25,14 @@ const Challenge: NextPage = ({ setIsModalVisible }) => { onClick={isAdmin ? null : () => router.push("/challenge")} > -
풍물패 두드림
-
12명 참여 중
+
{challengeInfo.name}
+
+ {challengeInfo.participantsNum}명 참여 중 +
-
서울시립도서관 4층 | 월 16시
+
+ {challengeInfo.location} | {challengeInfo.schedule} +
= ({ setIsModalVisible }) => { {!isAdmin && (
개인달성률
-
100%
+
{challengeInfo.attendanceRate}%
)}
전체달성률
-
100%
+
{challengeInfo.totalAttendanceRate}%
void; @@ -19,6 +20,8 @@ const HomeChallenge: NextPage = ({ onNotify }) => { const [isAdmin] = useAtom(isAdminAtom); const [isOpen, setIsOpen] = useState(false); + const { data: challengeInfo } = useGetMyChallengeList(); + return (
@@ -31,8 +34,9 @@ const HomeChallenge: NextPage = ({ onNotify }) => { > {isAdmin ? "새 프로그램 등록" : "참여 프로그램 추가"}
- - + {challengeInfo.result.map((info) => ( + + ))} Date: Tue, 25 Jun 2024 12:59:09 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20/challenge/ads=20get=20API=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apis/challenge.ts | 15 ++++++++++++++- apis/client.ts | 6 +++++- apis/hooks/challenge.ts | 13 +++++++++++-- components/home/carousel.tsx | 23 ++++++++++++++++++----- components/home/popular.tsx | 34 ++++++++++++++++++++++++++++++---- 5 files changed, 78 insertions(+), 13 deletions(-) diff --git a/apis/challenge.ts b/apis/challenge.ts index 4eea8ac..48c7a21 100644 --- a/apis/challenge.ts +++ b/apis/challenge.ts @@ -13,9 +13,22 @@ export interface Challenge { attendanceRate: number; totalAttendanceRate: number; } +interface GetChallengeAdsResponse extends ResponseBody2 { + result: { + mostParticipatedChallenge: Challenge; + mostAttendancedChallenge: Challenge; + mostRecentlyStartedChallenge: Challenge; + }; +} + async function getMyChallengeList(): Promise { const { data } = await client.get(`/challenges`); return data; } -export { getMyChallengeList }; +async function getChallengeAds(): Promise { + const { data } = await client.get(`/challenges/ads`); + return data; +} + +export { getMyChallengeList, getChallengeAds }; diff --git a/apis/client.ts b/apis/client.ts index c3dbd5a..04a1d94 100644 --- a/apis/client.ts +++ b/apis/client.ts @@ -5,6 +5,10 @@ interface ResponseBody { code: number; message: string; } +interface ResponseBody2 { + isSuccess: boolean; + message: string; +} export const setTokenFromLocalStorage = (access_token: string) => { localStorage.setItem("access_token", access_token); @@ -49,4 +53,4 @@ client.interceptors.request.use( ); export default client; -export type { ResponseBody }; +export type { ResponseBody, ResponseBody2 }; diff --git a/apis/hooks/challenge.ts b/apis/hooks/challenge.ts index 0ab0369..4ac797d 100644 --- a/apis/hooks/challenge.ts +++ b/apis/hooks/challenge.ts @@ -1,4 +1,4 @@ -import { getMyChallengeList } from "../challenge"; +import { getChallengeAds, getMyChallengeList } from "../challenge"; import { useQuery } from "@tanstack/react-query"; function useGetMyChallengeList() { @@ -10,4 +10,13 @@ function useGetMyChallengeList() { return { data }; } -export { useGetMyChallengeList }; +function useGetChallengeAds() { + const { data } = useQuery({ + queryKey: ["getChallengeAds"], + queryFn: getChallengeAds, + }); + + return { data }; +} + +export { useGetMyChallengeList, useGetChallengeAds }; diff --git a/components/home/carousel.tsx b/components/home/carousel.tsx index 53e2f8e..f59c99d 100644 --- a/components/home/carousel.tsx +++ b/components/home/carousel.tsx @@ -1,19 +1,32 @@ import { NextPage } from "next"; import FlexBox from "../Flexbox"; -import { PopularChallenge } from "./popular"; +import PopularChallenge from "./popular"; import useEmblaCarousel from "embla-carousel-react"; import Autoplay from "embla-carousel-autoplay"; +import { useGetChallengeAds } from "@/apis/hooks/challenge"; -export const HomeCarousel: NextPage = () => { +const HomeCarousel: NextPage = () => { const [emblaRef] = useEmblaCarousel({ loop: true }, [Autoplay()]); + const { data } = useGetChallengeAds(); return (
- - - + + +
); }; + +export default HomeCarousel; diff --git a/components/home/popular.tsx b/components/home/popular.tsx index 280a64d..354c03b 100644 --- a/components/home/popular.tsx +++ b/components/home/popular.tsx @@ -2,15 +2,41 @@ import { NextPage } from "next"; import FlexBox from "../Flexbox"; import React from "react"; import useEmblaCarousel from "embla-carousel-react"; +import { Challenge } from "@/apis/challenge"; + +interface PopularChallengeProps { + challengeInfo: Challenge; + type: + | "mostParticipatedChallenge" + | "mostAttendancedChallenge" + | "mostRecentlyStartedChallenge"; +} + +export default function PopularChallenge({ + challengeInfo, + type, +}: PopularChallengeProps) { + const returnTitle = () => { + switch (type) { + case "mostAttendancedChallenge": + return "출석률이 가장 높은 챌린지 🔥"; + break; + case "mostParticipatedChallenge": + return "참여자가 가장 많은 챌린지 🔥"; + break; + case "mostRecentlyStartedChallenge": + return "가장 최근에 개설된 챌린지 🔥"; + break; + } + }; -export const PopularChallenge: NextPage = () => { return ( -
참여자가 가장 많은 챌린지 🔥
-
레전드 영화보기 챌린지
+
{returnTitle()}
+
{challengeInfo?.name}
); -}; +} From 49d76a4b5a711e5e0469ee453efe44d9f827267f Mon Sep 17 00:00:00 2001 From: iOdiO89 <117376841+iOdiO89@users.noreply.github.com> Date: Tue, 25 Jun 2024 12:59:28 +0900 Subject: [PATCH 3/8] =?UTF-8?q?fix:=20=EC=B1=8C=EB=A6=B0=EC=A7=80=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EB=AA=A8=EB=8B=AC=20title=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/home/certifyModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/home/certifyModal.tsx b/components/home/certifyModal.tsx index 0e8bbba..22c3532 100644 --- a/components/home/certifyModal.tsx +++ b/components/home/certifyModal.tsx @@ -27,7 +27,7 @@ export default function CertifyModal({ return ( <> -
풍물패 두드림 챌린지 인증하기
+
챌린지 인증하기
setIsModalVisible(false)}>
From 6bb9879a96bd2d860da159f452e980ded7006edc Mon Sep 17 00:00:00 2001 From: iOdiO89 <117376841+iOdiO89@users.noreply.github.com> Date: Tue, 25 Jun 2024 12:59:48 +0900 Subject: [PATCH 4/8] =?UTF-8?q?fix:=20=EC=B0=B8=EC=97=AC=20=EC=A4=91?= =?UTF-8?q?=EC=9D=B8=20=EC=B1=8C=EB=A6=B0=EC=A7=80=20=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?Key=20props=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/home/challengeBox.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/components/home/challengeBox.tsx b/components/home/challengeBox.tsx index 04493a1..aae9041 100644 --- a/components/home/challengeBox.tsx +++ b/components/home/challengeBox.tsx @@ -1,7 +1,7 @@ import { NextPage } from "next"; import FlexBox from "../Flexbox"; import Challenge from "./challenge"; -import { HomeCarousel } from "./carousel"; +import HomeCarousel from "./carousel"; import { useRouter } from "next/router"; import { isAdminAtom } from "@/utils/atom"; import { useAtom } from "jotai"; @@ -34,8 +34,12 @@ const HomeChallenge: NextPage = ({ onNotify }) => { > {isAdmin ? "새 프로그램 등록" : "참여 프로그램 추가"}
- {challengeInfo.result.map((info) => ( - + {challengeInfo?.result.map((info) => ( + ))} Date: Tue, 25 Jun 2024 17:44:33 +0900 Subject: [PATCH 5/8] =?UTF-8?q?feat:=20/challenges/participation=20&=20/ch?= =?UTF-8?q?allenges/particiation=20API=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apis/challenge.ts | 27 +++++++++++++++++++-- apis/hooks/challenge.ts | 40 +++++++++++++++++++++++++++++--- components/home/searchResult.tsx | 24 +++++++++++++++---- pages/challenge/join.tsx | 12 ++++++---- 4 files changed, 90 insertions(+), 13 deletions(-) diff --git a/apis/challenge.ts b/apis/challenge.ts index 48c7a21..3ea3c3b 100644 --- a/apis/challenge.ts +++ b/apis/challenge.ts @@ -1,4 +1,4 @@ -import client, { ResponseBody } from "./client"; +import client, { ResponseBody, ResponseBody2 } from "./client"; interface GetMyChallengeListResponse extends ResponseBody { result: Challenge[]; @@ -21,6 +21,10 @@ interface GetChallengeAdsResponse extends ResponseBody2 { }; } +interface getChallengeSearchResponse extends ResponseBody2 { + result: Challenge[]; +} + async function getMyChallengeList(): Promise { const { data } = await client.get(`/challenges`); return data; @@ -31,4 +35,23 @@ async function getChallengeAds(): Promise { return data; } -export { getMyChallengeList, getChallengeAds }; +async function getChallengeSearch( + keyword: string, +): Promise { + const { data } = await client.get(`/challenges/search?searchWord=${keyword}`); + return data; +} + +async function postNewChallenge(challengeIdx: number): Promise { + const { data } = await client.post(`/challenges/participation`, { + challengeIdx, + }); + return data; +} + +export { + getMyChallengeList, + getChallengeAds, + getChallengeSearch, + postNewChallenge, +}; diff --git a/apis/hooks/challenge.ts b/apis/hooks/challenge.ts index 4ac797d..0a48418 100644 --- a/apis/hooks/challenge.ts +++ b/apis/hooks/challenge.ts @@ -1,5 +1,10 @@ -import { getChallengeAds, getMyChallengeList } from "../challenge"; -import { useQuery } from "@tanstack/react-query"; +import { + getChallengeAds, + getChallengeSearch, + getMyChallengeList, + postNewChallenge, +} from "../challenge"; +import { useMutation, useQuery } from "@tanstack/react-query"; function useGetMyChallengeList() { const { data } = useQuery({ @@ -19,4 +24,33 @@ function useGetChallengeAds() { return { data }; } -export { useGetMyChallengeList, useGetChallengeAds }; +function useGetChallengeSearch(keyword: string) { + const { data } = useQuery({ + queryKey: ["getChallengeSearch", keyword], + queryFn: () => getChallengeSearch(keyword), + enabled: keyword.trim().length !== 0, + }); + + return { data }; +} + +function usePostNewChallenge( + challengeIdx: number, + challengeName: string, + notify: (title: string) => void, +) { + const { mutate } = useMutation({ + mutationKey: ["postNewChallenge", challengeIdx], + mutationFn: () => postNewChallenge(challengeIdx), + onSuccess: () => notify(challengeName), + }); + + return { mutate }; +} + +export { + useGetMyChallengeList, + useGetChallengeAds, + useGetChallengeSearch, + usePostNewChallenge, +}; diff --git a/components/home/searchResult.tsx b/components/home/searchResult.tsx index e17273d..eafaf19 100644 --- a/components/home/searchResult.tsx +++ b/components/home/searchResult.tsx @@ -1,15 +1,31 @@ +import { Challenge } from "@/apis/challenge"; import FlexBox from "../Flexbox"; +import { usePostNewChallenge } from "@/apis/hooks/challenge"; interface SearchResultProps { - onClick?: () => void; + notify: (title: string) => void; + challengeInfo: Challenge; } -export default function SearchResult({ onClick }: SearchResultProps) { +export default function SearchResult({ + notify, + challengeInfo, +}: SearchResultProps) { + const { mutate } = usePostNewChallenge( + challengeInfo.challengeIdx, + challengeInfo.name, + notify, + ); + + const onClick = () => mutate(); + return ( -
풍물패 두드림
-
서울시립도서관 4층 | 월 16시
+
{challengeInfo.name}
+
+ {challengeInfo.location} | {challengeInfo.schedule} +
참여 diff --git a/pages/challenge/join.tsx b/pages/challenge/join.tsx index 25f56db..79e869b 100644 --- a/pages/challenge/join.tsx +++ b/pages/challenge/join.tsx @@ -1,3 +1,4 @@ +import { useGetChallengeSearch } from "@/apis/hooks/challenge"; import Divider from "@/components/Divider"; import HeadFunction from "@/components/HeadFunction"; import NavBar from "@/components/NavBar"; @@ -12,8 +13,10 @@ import "react-toastify/dist/ReactToastify.css"; const JoinChallenge: NextPage = () => { const [keyword, setKeyword] = useState(""); - const notify = () => { - toast.success("(챌린지이름)에 성공적으로 참여하셨습니다.", { + const { data } = useGetChallengeSearch(keyword); + + const notify = (title: string) => { + toast.success(`${title}에 성공적으로 참여하셨습니다.`, { position: "bottom-center", icon: ({ theme, type }) => ( @@ -27,8 +30,9 @@ const JoinChallenge: NextPage = () => {
- - + {data?.result.map((info) => ( + + ))}
Date: Tue, 25 Jun 2024 17:46:50 +0900 Subject: [PATCH 6/8] =?UTF-8?q?fix:=20Next/Image=20=EB=8C=80=EC=8B=A0=20?= =?UTF-8?q?=EC=A7=81=EC=A0=91=20import=20=ED=95=B4=EC=84=9C=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/home/carousel.tsx | 2 +- components/mypage/profile.tsx | 3 ++- pages/challenge/join.tsx | 5 ++--- pages/index.tsx | 7 +++---- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/components/home/carousel.tsx b/components/home/carousel.tsx index f59c99d..422c7ef 100644 --- a/components/home/carousel.tsx +++ b/components/home/carousel.tsx @@ -10,7 +10,7 @@ const HomeCarousel: NextPage = () => { const { data } = useGetChallengeAds(); return ( -
+
서울복지관 관리자
- + ); } diff --git a/pages/challenge/join.tsx b/pages/challenge/join.tsx index 79e869b..8df79f1 100644 --- a/pages/challenge/join.tsx +++ b/pages/challenge/join.tsx @@ -9,6 +9,7 @@ import Image from "next/image"; import { useState } from "react"; import { toast, ToastContainer, Zoom } from "react-toastify"; import "react-toastify/dist/ReactToastify.css"; +import CheckIcon from "@/public/svgs/Check.svg"; const JoinChallenge: NextPage = () => { const [keyword, setKeyword] = useState(""); @@ -18,9 +19,7 @@ const JoinChallenge: NextPage = () => { const notify = (title: string) => { toast.success(`${title}에 성공적으로 참여하셨습니다.`, { position: "bottom-center", - icon: ({ theme, type }) => ( - - ), + icon: ({ theme, type }) => , }); }; diff --git a/pages/index.tsx b/pages/index.tsx index 881a1fe..d5b3001 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -2,21 +2,20 @@ import HeadFunction from "@/components/HeadFunction"; import { NextPage } from "next"; import NavBar from "@/components/NavBar"; import Challenge from "@/components/home/challenge"; -import { HomeCarousel } from "@/components/home/carousel"; +import HomeCarousel from "@/components/home/carousel"; import { HomeHeader } from "@/components/home/header"; import HomeChallenge from "@/components/home/challengeBox"; import FlexBox from "@/components/Flexbox"; import { ToastContainer, Zoom, toast } from "react-toastify"; import Image from "next/image"; import "react-toastify/dist/ReactToastify.css"; +import CheckIcon from "@/public/svgs/Check.svg"; const Home: NextPage = () => { const notify = (msg: string) => { toast.success(msg, { position: "bottom-center", - icon: ({ theme, type }) => ( - - ), + icon: ({ theme, type }) => , }); }; From aa09ec085cff81f511427fa94de1bfe28b7ac0a8 Mon Sep 17 00:00:00 2001 From: iOdiO89 <117376841+iOdiO89@users.noreply.github.com> Date: Fri, 28 Jun 2024 18:13:51 +0900 Subject: [PATCH 7/8] =?UTF-8?q?fix:=20=EC=B1=8C=EB=A6=B0=EC=A7=80=20?= =?UTF-8?q?=EA=B4=91=EA=B3=A0=20Undefined=20=EC=9D=BC=20=EB=95=8C=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=EB=82=98=EC=A7=80=20=EC=95=8A=EA=B2=8C=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/home/carousel.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/home/carousel.tsx b/components/home/carousel.tsx index 422c7ef..dcd6c07 100644 --- a/components/home/carousel.tsx +++ b/components/home/carousel.tsx @@ -13,15 +13,15 @@ const HomeCarousel: NextPage = () => {
From b98ed30a2226baabe132c6625f469c072f4f40b0 Mon Sep 17 00:00:00 2001 From: iOdiO89 <117376841+iOdiO89@users.noreply.github.com> Date: Fri, 28 Jun 2024 18:14:15 +0900 Subject: [PATCH 8/8] =?UTF-8?q?fix:=20=EC=B1=8C=EB=A6=B0=EC=A7=80=20?= =?UTF-8?q?=EC=B0=B8=EC=97=AC=20=ED=9B=84=20=EB=AA=A9=EB=A1=9D=20=EC=9E=AC?= =?UTF-8?q?=EA=B2=80=EC=83=89=EB=90=98=EA=B2=8C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apis/hooks/challenge.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apis/hooks/challenge.ts b/apis/hooks/challenge.ts index 0a48418..3055a43 100644 --- a/apis/hooks/challenge.ts +++ b/apis/hooks/challenge.ts @@ -4,7 +4,7 @@ import { getMyChallengeList, postNewChallenge, } from "../challenge"; -import { useMutation, useQuery } from "@tanstack/react-query"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; function useGetMyChallengeList() { const { data } = useQuery({ @@ -39,10 +39,16 @@ function usePostNewChallenge( challengeName: string, notify: (title: string) => void, ) { + const queryClient = useQueryClient(); const { mutate } = useMutation({ mutationKey: ["postNewChallenge", challengeIdx], mutationFn: () => postNewChallenge(challengeIdx), - onSuccess: () => notify(challengeName), + onSuccess: () => { + notify(challengeName); + queryClient.invalidateQueries({ + queryKey: ["getChallengeSearch"], + }); + }, }); return { mutate };