diff --git a/public/icons/heart.svg b/public/icons/heart.svg index 40bec30..c686a17 100644 --- a/public/icons/heart.svg +++ b/public/icons/heart.svg @@ -1,9 +1 @@ - - - - - - - - - + \ No newline at end of file diff --git a/public/icons/red_haert.svg b/public/icons/red_haert.svg deleted file mode 100644 index c22752a..0000000 --- a/public/icons/red_haert.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/red_heart.svg b/public/icons/red_heart.svg new file mode 100644 index 0000000..96b98c5 --- /dev/null +++ b/public/icons/red_heart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/common/LikesPost/LikesPost.module.scss b/src/components/common/LikesPost/LikesPost.module.scss new file mode 100644 index 0000000..1cd2229 --- /dev/null +++ b/src/components/common/LikesPost/LikesPost.module.scss @@ -0,0 +1,42 @@ +.Box { + width: 100%; + padding: 2rem; + @include flexSort(null, space-between, center, null); + border: 1px solid #9d9d9d; + border-radius: 2rem; + box-shadow: 0 0.4rem 0.4rem 0 rgba(0, 0, 0, 0.25); + + &:hover { + background-color: $gray-3; + } +} + +.leftBox { + @include flexSort(column, null, null, 1rem); +} + +.titleBox { + @include flexSort(null, null, center, 2rem); +} + +.title { + @include font(2rem, 600, normal); +} + +.dateBox { + @include flexSort(null, null, center, 1rem); + @include font(1.3rem, 300, normal); +} + +.rightBox { + @include flexSort(column, null, flex-end, 1rem); +} + +.detailBtn { + padding: 0.7rem 1rem; + border-radius: 1rem; + background: #8b8282; + color: white; + @include font(1.3rem, 600, normal); + @include flexSort(null, center, center, null); +} diff --git a/src/components/common/LikesPost/LikesPost.tsx b/src/components/common/LikesPost/LikesPost.tsx new file mode 100644 index 0000000..b7e09ef --- /dev/null +++ b/src/components/common/LikesPost/LikesPost.tsx @@ -0,0 +1,42 @@ +import classNames from "classnames/bind"; + +import Link from "next/link"; + +import { ROUTE } from "@/constants/route"; +import RedHeart from "@/icons/red_heart.svg"; +import { formatDateString } from "@/utils"; + +import styles from "./LikesPost.module.scss"; +import PostStatusLabel from "../PostStatusLabel/PostStatusLabel"; + +const cn = classNames.bind(styles); + +interface LikesPostProps { + id: number; + title: string; + postStatus: "RECRUITING" | "FINISHED"; + modifiedAt: string; + endTime: string; + postType: "TAKER" | "GIVER"; +} + +export default function LikesPost({ endTime, id, modifiedAt, postStatus, title, postType }: LikesPostProps) { + return ( + +
+
+

{`#${id} ${title}`}

+ +
+
+

일시 |

+

{`${formatDateString(modifiedAt)} ~ ${formatDateString(endTime)}`}

+
+
+
+ + +
+ + ); +} diff --git a/src/components/common/Pagenation/apis/getHelpMeList.ts b/src/components/common/Pagenation/apis/getHelpMeList.ts index a7f8f8f..c6f082c 100644 --- a/src/components/common/Pagenation/apis/getHelpMeList.ts +++ b/src/components/common/Pagenation/apis/getHelpMeList.ts @@ -3,6 +3,7 @@ import axiosInstance from "@/apis/axiosInstance"; export default async function getPagenationItems(postType: string, page: number, limit: number) { const { data } = await axiosInstance.get( `posts?post-type=${postType}&page=${page}&size=${limit}&sorted=modifiedAt,DESC&post-status=RECRUITING`, + { withCredentials: true }, ); return data; } diff --git a/src/components/common/Post/Post.tsx b/src/components/common/Post/Post.tsx index 06295ef..c04a081 100644 --- a/src/components/common/Post/Post.tsx +++ b/src/components/common/Post/Post.tsx @@ -1,5 +1,6 @@ -import { useState } from "react"; +import { MouseEvent, useState } from "react"; +import { useMutation } from "@tanstack/react-query"; import classNames from "classnames/bind"; import Link from "next/link"; @@ -8,8 +9,11 @@ import styles from "@/components/common/Post/Post.module.scss"; import PostData from "@/components/page-layout/HomeLayout/types"; import { ROUTE } from "@/constants/route"; import Heart from "@/icons/heart.svg"; +import RedHeart from "@/icons/red_heart.svg"; import { formatDateString } from "@/utils"; +import postLikes from "./apis/postLikes"; + const cn = classNames.bind(styles); interface PostProps { @@ -17,11 +21,30 @@ interface PostProps { } export default function Post({ data }: PostProps) { - const [isHeartClick, setIsHeartClick] = useState(false); - const { postType, title, author, assistanceType, district, startTime, endTime, scheduleType, id, postStatus } = data; + const { + postType, + title, + author, + assistanceType, + district, + startTime, + endTime, + scheduleType, + id, + postStatus, + isLiked, + } = data; + + const { mutate } = useMutation({ + mutationFn: () => postLikes(id), + }); + + const [isHeartClick, setIsHeartClick] = useState(isLiked); - const handleHeartClick = () => { + const handleHeartClick = (event: MouseEvent) => { + event.preventDefault(); setIsHeartClick((prev) => !prev); + mutate(); }; return ( @@ -42,7 +65,11 @@ export default function Post({ data }: PostProps) { > {postStatus === "RECRUITING" ? "매칭중" : "매칭완료"}

- + {isHeartClick ? ( + + ) : ( + + )}

{title}

diff --git a/src/components/common/Post/apis/postLikes.ts b/src/components/common/Post/apis/postLikes.ts new file mode 100644 index 0000000..dcc2569 --- /dev/null +++ b/src/components/common/Post/apis/postLikes.ts @@ -0,0 +1,12 @@ +import axiosInstance from "@/apis/axiosInstance"; + +export default async function postLikes(postId: number) { + const { data } = await axiosInstance.post( + `posts/likes/${postId}`, + {}, + { + withCredentials: true, + }, + ); + return data; +} diff --git a/src/components/common/PostStatusLabel/PostStatusLabel.module.scss b/src/components/common/PostStatusLabel/PostStatusLabel.module.scss new file mode 100644 index 0000000..1157c25 --- /dev/null +++ b/src/components/common/PostStatusLabel/PostStatusLabel.module.scss @@ -0,0 +1,23 @@ +.box { + @include flexSort(null, center, center, null); + border-radius: 1rem; + padding: 0.5rem 1rem; + border: 0.1rem solid #ff6767; + @include font(1.2rem, 500, normal); + + &.RECRUITING { + background-color: white; + + .statusText { + color: #ff6767; + } + } + + &.FINISHED { + background-color: #ff6767; + + .statusText { + color: white; + } + } +} diff --git a/src/components/common/PostStatusLabel/PostStatusLabel.tsx b/src/components/common/PostStatusLabel/PostStatusLabel.tsx new file mode 100644 index 0000000..9890780 --- /dev/null +++ b/src/components/common/PostStatusLabel/PostStatusLabel.tsx @@ -0,0 +1,19 @@ +import classNames from "classnames/bind"; + +import styles from "./PostStatusLabel.module.scss"; + +const cn = classNames.bind(styles); + +interface PostStatusLabelProps { + postStatus: "RECRUITING" | "FINISHED"; +} + +export default function PostStatusLabel({ postStatus }: PostStatusLabelProps) { + const statusText = postStatus === "RECRUITING" ? "매칭중" : "매칭완료"; + + return ( +
+

{statusText}

+
+ ); +} diff --git a/src/components/page-layout/HomeLayout/apis/getGiverPost.ts b/src/components/page-layout/HomeLayout/apis/getGiverPost.ts index f329384..8b99e4f 100644 --- a/src/components/page-layout/HomeLayout/apis/getGiverPost.ts +++ b/src/components/page-layout/HomeLayout/apis/getGiverPost.ts @@ -3,6 +3,9 @@ import axiosInstance from "@/apis/axiosInstance"; export default async function getGiverPost() { const { data } = await axiosInstance.get( "posts?post-type=GIVER&page=1&size=4&sorted=modifiedAt,DESC&post-status=RECRUITING", + { + withCredentials: true, + }, ); return data.data.content; } diff --git a/src/components/page-layout/HomeLayout/apis/getTakerPost.ts b/src/components/page-layout/HomeLayout/apis/getTakerPost.ts index d934b11..9578b8d 100644 --- a/src/components/page-layout/HomeLayout/apis/getTakerPost.ts +++ b/src/components/page-layout/HomeLayout/apis/getTakerPost.ts @@ -3,6 +3,9 @@ import axiosInstance from "@/apis/axiosInstance"; export default async function getTakerPost() { const { data } = await axiosInstance.get( "posts?post-type=TAKER&page=1&size=4&sorted=modifiedAt,DESC&post-status=RECRUITING", + { + withCredentials: true, + }, ); return data.data.content; } diff --git a/src/components/page-layout/HomeLayout/types/index.ts b/src/components/page-layout/HomeLayout/types/index.ts index 67b9ee2..b0d2fc0 100644 --- a/src/components/page-layout/HomeLayout/types/index.ts +++ b/src/components/page-layout/HomeLayout/types/index.ts @@ -12,6 +12,7 @@ export default interface PostData { scheduleType: string; startTime: string; title: string; + isLiked: boolean; author: { age: number; disabilityType: string; diff --git a/src/components/page-layout/myPageLayout/constants/index.ts b/src/components/page-layout/myPageLayout/constants/index.ts index 8cadec6..8ec73ab 100644 --- a/src/components/page-layout/myPageLayout/constants/index.ts +++ b/src/components/page-layout/myPageLayout/constants/index.ts @@ -9,8 +9,8 @@ export const MY_PAGE_NAV = [ href: ROUTE.MY_PAGE, }, { - name: "내정보 수정", - href: ROUTE.MY_PAGE_EDIT, + name: "찜한 목록", + href: ROUTE.MY_PAGE_Likes, }, ], }, diff --git a/src/components/page-layout/myPageLikesLayout/apis/getMyLikes.ts b/src/components/page-layout/myPageLikesLayout/apis/getMyLikes.ts new file mode 100644 index 0000000..8fdc238 --- /dev/null +++ b/src/components/page-layout/myPageLikesLayout/apis/getMyLikes.ts @@ -0,0 +1,39 @@ +import axiosInstance from "@/apis/axiosInstance"; + +export interface MyLikesRes { + content: { + assistanceType: "생활" | "교육"; + author: { + age: number; + disabilityType: string; + email: string; + gender: string; + memberId: number; + name: string; + nickname: string; + profileImageUrl: string; + }; + content: string; + createdAt: string; + disabilityType: string | null; + district: string; + endTime: string; + id: number; + modifiedAt: string; + postStatus: "RECRUITING" | "FINISHED"; + postType: "TAKER" | "GIVER"; + scheduleDetails: string; + scheduleType: string; + startTime: string; + title: string; + }[]; + last: boolean; + totalElements: number; +} + +export default async function getMyLikes(pageId: string, postType: string): Promise { + const { data } = await axiosInstance.get(`posts/likes/my-page?post-type=${postType}&page=${pageId}&size=4`, { + withCredentials: true, + }); + return data.data; +} diff --git a/src/components/page-layout/myPageLikesLayout/components/MyLikesList/MyLikesList.module.scss b/src/components/page-layout/myPageLikesLayout/components/MyLikesList/MyLikesList.module.scss new file mode 100644 index 0000000..81732ac --- /dev/null +++ b/src/components/page-layout/myPageLikesLayout/components/MyLikesList/MyLikesList.module.scss @@ -0,0 +1,4 @@ +.container { + padding: 3rem; + @include flexSort(column, null, null, 1rem); +} diff --git a/src/components/page-layout/myPageLikesLayout/components/MyLikesList/MyLikesList.tsx b/src/components/page-layout/myPageLikesLayout/components/MyLikesList/MyLikesList.tsx new file mode 100644 index 0000000..65f741c --- /dev/null +++ b/src/components/page-layout/myPageLikesLayout/components/MyLikesList/MyLikesList.tsx @@ -0,0 +1,30 @@ +import classNames from "classnames/bind"; + +import LikesPost from "@/components/common/LikesPost/LikesPost"; + +import styles from "./MyLikesList.module.scss"; +import { MyLikesRes } from "../../apis/getMyLikes"; + +const cn = classNames.bind(styles); + +interface MyLikesListProps { + likesList: MyLikesRes["content"] | undefined; +} + +export default function MyLikesList({ likesList }: MyLikesListProps) { + return ( +
+ {likesList?.map((post) => ( + + ))} +
+ ); +} diff --git a/src/components/page-layout/myPageLikesLayout/components/MyLikesListBox/MyLikesListBox.module.scss b/src/components/page-layout/myPageLikesLayout/components/MyLikesListBox/MyLikesListBox.module.scss new file mode 100644 index 0000000..efe7010 --- /dev/null +++ b/src/components/page-layout/myPageLikesLayout/components/MyLikesListBox/MyLikesListBox.module.scss @@ -0,0 +1,8 @@ +.myLikesListBox { + @include flexSort(column, null, null, null); +} + +.paginationBox { + @include flexSort(null, center, center, null); + margin-bottom: 3rem; +} diff --git a/src/components/page-layout/myPageLikesLayout/components/MyLikesListBox/MyLikesListBox.tsx b/src/components/page-layout/myPageLikesLayout/components/MyLikesListBox/MyLikesListBox.tsx new file mode 100644 index 0000000..72195a8 --- /dev/null +++ b/src/components/page-layout/myPageLikesLayout/components/MyLikesListBox/MyLikesListBox.tsx @@ -0,0 +1,53 @@ +import { useQuery } from "@tanstack/react-query"; +import classNames from "classnames/bind"; + +import { useRouter } from "next/router"; + +import Pagination from "@/components/common/Pagenation/Pagenation"; + +import styles from "./MyLikesListBox.module.scss"; +import getMyLikes from "../../apis/getMyLikes"; +import MyLikesList from "../MyLikesList/MyLikesList"; +import PostTypeFilter, { PostTypeFilterProps } from "../PostTypeFilter/PostTypeFilter"; + +const cn = classNames.bind(styles); + +export default function MyLikesListBox() { + const router = useRouter(); + const postType = (router.query.postType as PostTypeFilterProps["postType"]) || "TAKER"; + const pageId = router.query.pageId || "1"; + const params = new URLSearchParams(router.query as any); + + const { data: likesList } = useQuery({ + queryKey: ["LikesList", pageId, postType], + queryFn: () => getMyLikes(`${pageId}`, postType), + enabled: !!postType, + }); + + const setPage = (newPage: number) => { + const pathName = router.pathname; + params.set("pageId", newPage.toString()); + router.replace({ + pathname: pathName, + query: { ...Object.fromEntries(params.entries()) }, + }); + }; + + return ( + <> +
+ + +
+
+ +
+ + ); +} diff --git a/src/components/page-layout/myPageLikesLayout/components/MyPageLikesList/MyPageLikesList.module.scss b/src/components/page-layout/myPageLikesLayout/components/MyPageLikesList/MyPageLikesList.module.scss new file mode 100644 index 0000000..98a7b88 --- /dev/null +++ b/src/components/page-layout/myPageLikesLayout/components/MyPageLikesList/MyPageLikesList.module.scss @@ -0,0 +1,15 @@ +.container { + @include flexSort(column, null, null, 2rem); + flex: 1; +} + +.title { + @include gmarketFont(3rem, 500, normal); +} + +.myInfoContainer { + width: 100%; + background-color: white; + height: 100%; + @include flexSort(column, null, null, null); +} diff --git a/src/components/page-layout/myPageLikesLayout/components/MyPageLikesList/MyPageLikesList.tsx b/src/components/page-layout/myPageLikesLayout/components/MyPageLikesList/MyPageLikesList.tsx new file mode 100644 index 0000000..8bfb14c --- /dev/null +++ b/src/components/page-layout/myPageLikesLayout/components/MyPageLikesList/MyPageLikesList.tsx @@ -0,0 +1,20 @@ +import classNames from "classnames/bind"; + +import MyPageMyInfo from "@/components/common/MyPageMyInfo/MyPageMyInfo"; + +import styles from "./MyPageLikesList.module.scss"; +import MyLikesListBox from "../MyLikesListBox/MyLikesListBox"; + +const cn = classNames.bind(styles); + +export default function MyPageLikesList() { + return ( +
+

찜한 목록

+
+ + +
+
+ ); +} diff --git a/src/components/page-layout/myPageLikesLayout/components/PostTypeFilter/PostTypeFilter.module.scss b/src/components/page-layout/myPageLikesLayout/components/PostTypeFilter/PostTypeFilter.module.scss new file mode 100644 index 0000000..1620c36 --- /dev/null +++ b/src/components/page-layout/myPageLikesLayout/components/PostTypeFilter/PostTypeFilter.module.scss @@ -0,0 +1,25 @@ +.postTypeFilterBox { + @include flexSort(null, center, center, null); +} + +.taker, +.giver { + @include flexSort(null, center, center, null); + border-radius: 1rem; + padding: 1rem 3rem; + @include gmarketFont(2rem, 500, normal); + color: black; + background-color: #efefef; +} + +.taker { + &.picked { + background-color: $taker; + } +} + +.giver { + &.picked { + background-color: $giver; + } +} diff --git a/src/components/page-layout/myPageLikesLayout/components/PostTypeFilter/PostTypeFilter.tsx b/src/components/page-layout/myPageLikesLayout/components/PostTypeFilter/PostTypeFilter.tsx new file mode 100644 index 0000000..133fa96 --- /dev/null +++ b/src/components/page-layout/myPageLikesLayout/components/PostTypeFilter/PostTypeFilter.tsx @@ -0,0 +1,42 @@ +import { useQueryClient } from "@tanstack/react-query"; +import classNames from "classnames/bind"; + +import Link from "next/link"; + +import { ROUTE } from "@/constants/route"; + +import styles from "./PostTypeFilter.module.scss"; + +const cn = classNames.bind(styles); + +export interface PostTypeFilterProps { + postType: "TAKER" | "GIVER"; + pageId: string; +} + +export default function PostTypeFilter({ postType, pageId }: PostTypeFilterProps) { + const queryClient = useQueryClient(); + + const handleFilterClick = () => { + queryClient.invalidateQueries({ queryKey: ["LikesList", pageId, postType] }); + }; + + return ( +
+ + 도와줄래요? + + + 도와줄게요! + +
+ ); +} diff --git a/src/components/page-layout/myPageLikesLayout/components/myPageLikesLayout.module.scss b/src/components/page-layout/myPageLikesLayout/components/myPageLikesLayout.module.scss new file mode 100644 index 0000000..ea73df5 --- /dev/null +++ b/src/components/page-layout/myPageLikesLayout/components/myPageLikesLayout.module.scss @@ -0,0 +1,6 @@ +.container { + height: 100%; + background: #f9fafb; + padding: 11.1rem 20rem; + @include flexSort(null, null, null, 9rem); +} diff --git a/src/components/page-layout/myPageLikesLayout/components/myPageLikesLayout.tsx b/src/components/page-layout/myPageLikesLayout/components/myPageLikesLayout.tsx new file mode 100644 index 0000000..d020650 --- /dev/null +++ b/src/components/page-layout/myPageLikesLayout/components/myPageLikesLayout.tsx @@ -0,0 +1,17 @@ +import classNames from "classnames/bind"; + +import MyPageNav from "@/components/page-layout/myPageLayout/components/MyPageNav/MyPageNav"; +import styles from "@/components/page-layout/myPageLikesLayout/components/myPageLikesLayout.module.scss"; + +import MyPageLikesList from "./MyPageLikesList/MyPageLikesList"; + +const cn = classNames.bind(styles); + +export default function MyPageLikesLayout() { + return ( +
+ + +
+ ); +} diff --git a/src/constants/route.ts b/src/constants/route.ts index c2e98ed..897d104 100644 --- a/src/constants/route.ts +++ b/src/constants/route.ts @@ -12,4 +12,5 @@ export const ROUTE = { MY_PAGE_EDIT: "/my-page/edit", MY_PAGE_HELP_ME: "/my-page/help-me", MY_PAGE_HELP_YOU: "/my-page/help-you", + MY_PAGE_Likes: "/my-page/likes", } as const; diff --git a/src/pages/my-page/likes/index.tsx b/src/pages/my-page/likes/index.tsx new file mode 100644 index 0000000..b0848fa --- /dev/null +++ b/src/pages/my-page/likes/index.tsx @@ -0,0 +1,12 @@ +import { ReactElement } from "react"; + +import RootLayout from "@/components/common/RootLayout/RootLayout"; +import MyPageLikesLayout from "@/components/page-layout/myPageLikesLayout/components/myPageLikesLayout"; + +export default function MyPageLikes() { + return ; +} + +MyPageLikes.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; diff --git a/src/styles/globals.scss b/src/styles/globals.scss index 646ec84..2abc0ef 100644 --- a/src/styles/globals.scss +++ b/src/styles/globals.scss @@ -1,5 +1,5 @@ @import "./_colors.scss"; @import "./_mixin.scss"; -@import "./_zindex.scss"; +@import "./_zIndex.scss"; @import "./_fonts.scss"; @import "./_flex.scss"; diff --git a/src/styles/reset.scss b/src/styles/reset.scss index e796a43..8e8f050 100644 --- a/src/styles/reset.scss +++ b/src/styles/reset.scss @@ -157,6 +157,7 @@ button { a { text-decoration: none; + color: black; } html,