Skip to content

Commit

Permalink
feat: 찜하기 관련 기능 추가 (#91)
Browse files Browse the repository at this point in the history
  • Loading branch information
smb0123 authored Oct 1, 2024
2 parents 74cb92a + 0b004cf commit fb8cf55
Show file tree
Hide file tree
Showing 29 changed files with 456 additions and 18 deletions.
10 changes: 1 addition & 9 deletions public/icons/heart.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion public/icons/red_haert.svg

This file was deleted.

1 change: 1 addition & 0 deletions public/icons/red_heart.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions src/components/common/LikesPost/LikesPost.module.scss
Original file line number Diff line number Diff line change
@@ -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);
}
42 changes: 42 additions & 0 deletions src/components/common/LikesPost/LikesPost.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Link href={postType === "TAKER" ? `${ROUTE.HELP_ME}/${id}` : `${ROUTE.HELP_YOU}/${id}`} className={cn("Box")}>
<div className={cn("leftBox")}>
<div className={cn("titleBox")}>
<p className={cn("title")}>{`#${id} ${title}`}</p>
<PostStatusLabel postStatus={postStatus} />
</div>
<div className={cn("dateBox")}>
<p>일시 | </p>
<p>{`${formatDateString(modifiedAt)} ~ ${formatDateString(endTime)}`}</p>
</div>
</div>
<div className={cn("rightBox")}>
<RedHeart width={32} height={32} />
<button className={cn("detailBtn")}>자세히 보기</button>
</div>
</Link>
);
}
1 change: 1 addition & 0 deletions src/components/common/Pagenation/apis/getHelpMeList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
37 changes: 32 additions & 5 deletions src/components/common/Post/Post.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -8,20 +9,42 @@ 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 {
data: PostData;
}

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<SVGSVGElement>) => {
event.preventDefault();
setIsHeartClick((prev) => !prev);
mutate();
};

return (
Expand All @@ -42,7 +65,11 @@ export default function Post({ data }: PostProps) {
>
{postStatus === "RECRUITING" ? "매칭중" : "매칭완료"}
</p>
<Heart width={32} height={32} className={cn("heart")} />
{isHeartClick ? (
<RedHeart onClick={handleHeartClick} width={32} height={32} className={cn("heart")} />
) : (
<Heart onClick={handleHeartClick} width={32} height={32} className={cn("heart")} />
)}
<div className={cn("contentBox")}>
<div className={cn("Box")}>
<p className={cn("title")}>{title}</p>
Expand Down
12 changes: 12 additions & 0 deletions src/components/common/Post/apis/postLikes.ts
Original file line number Diff line number Diff line change
@@ -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;
}
23 changes: 23 additions & 0 deletions src/components/common/PostStatusLabel/PostStatusLabel.module.scss
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
19 changes: 19 additions & 0 deletions src/components/common/PostStatusLabel/PostStatusLabel.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className={cn("box", postStatus)}>
<p className={cn("statusText")}>{statusText}</p>
</div>
);
}
3 changes: 3 additions & 0 deletions src/components/page-layout/HomeLayout/apis/getGiverPost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
3 changes: 3 additions & 0 deletions src/components/page-layout/HomeLayout/apis/getTakerPost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
1 change: 1 addition & 0 deletions src/components/page-layout/HomeLayout/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export default interface PostData {
scheduleType: string;
startTime: string;
title: string;
isLiked: boolean;
author: {
age: number;
disabilityType: string;
Expand Down
4 changes: 2 additions & 2 deletions src/components/page-layout/myPageLayout/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
],
},
Expand Down
39 changes: 39 additions & 0 deletions src/components/page-layout/myPageLikesLayout/apis/getMyLikes.ts
Original file line number Diff line number Diff line change
@@ -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<MyLikesRes> {
const { data } = await axiosInstance.get(`posts/likes/my-page?post-type=${postType}&page=${pageId}&size=4`, {
withCredentials: true,
});
return data.data;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.container {
padding: 3rem;
@include flexSort(column, null, null, 1rem);
}
Original file line number Diff line number Diff line change
@@ -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 (
<div className={cn("container")}>
{likesList?.map((post) => (
<LikesPost
key={post.id}
endTime={post.endTime}
id={post.id}
modifiedAt={post.modifiedAt}
postStatus={post.postStatus}
title={post.title}
postType={post.postType}
/>
))}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.myLikesListBox {
@include flexSort(column, null, null, null);
}

.paginationBox {
@include flexSort(null, center, center, null);
margin-bottom: 3rem;
}
Loading

0 comments on commit fb8cf55

Please sign in to comment.