Skip to content

Commit

Permalink
Merge pull request #18 from TripMingle/feat/SWM-264
Browse files Browse the repository at this point in the history
[feat/SWM-264] 정보글 상세보기 페이지 개발
  • Loading branch information
kxmmxnzx authored Nov 5, 2024
2 parents 6e8389f + f8764b6 commit 1f7e94f
Show file tree
Hide file tree
Showing 19 changed files with 344 additions and 38 deletions.
1 change: 1 addition & 0 deletions public/icons/empty_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: 1 addition & 0 deletions public/icons/full_heart.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions src/api/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,15 @@ export const getPostList = async (
export const postPost = async (data: PostForm) => {
return postFetch(`/post`, data);
};

export const postPostComment = async (
postId: number,
comment: string,
parentCommentId?: number,
) => {
const data = {
parentCommentId: parentCommentId ?? -1,
comment: comment,
};
return postFetch(`/post/comment?postId=${postId}`, data);
};
2 changes: 1 addition & 1 deletion src/app/[country]/board/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ const Page = async ({
</strong>
{' 개'}
</p>
<CommentInput boardId={boardDetail.boardId} />
<CommentInput id={boardDetail.boardId} />
<CommentList comments={boardDetail.boardComments} />
</div>
</div>
Expand Down
88 changes: 88 additions & 0 deletions src/app/[country]/post/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import Image from 'next/image';
import '@/styles/font.css';
import Header from '@/components/header/Header';
import * as styles from '@/styles/country/board/id/page.css';
import TravelerCard from '@/components/country/board/id/TravelerCard';
import CommentInput from '@/components/common/CommentInput';
import CommentList from '@/components/country/post/id/CommentList';
import { headers } from 'next/headers';
import { PostDetailType } from '@/types/country/post';
import Like from '@/components/country/post/id/Like';

const Page = async ({
params,
}: {
params: { country: string; id: string };
}) => {
console.log(params);
const headersList = headers();
const host = headersList.get('host');
const protocol = process.env.NODE_ENV === 'production' ? 'https' : 'http';

const postData = await fetch(
`${protocol}://${host}/api/post?postId=${params.id}`,
).then((res) => res.json());

const postDetail: PostDetailType = postData.data;

return (
<main>
<Header />
<div className={styles.pageContainer}>
<div className={styles.container}>
<div className={styles.contentContainer}>
<p className={styles.title}>{postDetail.title}</p>
<div>
<Like
isLike={postDetail.myLikeState}
likeCount={postDetail.likeCount}
/>
<span className={styles.iconContainer}>
<Image
className={styles.icon}
src="/icons/comment.svg"
width={20}
height={20}
alt="commendIcon"
/>
<span>{postDetail.commentCount}</span>
</span>
</div>
<div className={styles.infoContainer}>
<p className={styles.infoTitle}>내용</p>
<div className={styles.infoContent}>{postDetail.content}</div>
</div>
</div>
<TravelerCard
props={{
userImageUrl: postDetail.userImageUrl,
nickName: postDetail.userNickName,
userId: 0,
ageRange: postDetail.userAgeRange,
gender: postDetail.userGender,
nationality: postDetail.userNationality,
selfIntroduction: postDetail.selfIntroduce ?? '',
}}
title="작성자"
type="post"
/>
</div>
<div className={styles.container}>
<div className={styles.commentContainer}>
<p className={styles.commentTitle}>
{'댓글 '}
<strong className={styles.commentTitleStrong}>
{postDetail.commentCount}
</strong>
{' 개'}
</p>
<CommentInput id={postDetail.postingId} type="post" />
<CommentList comments={postDetail.postingComments} />
</div>
</div>
</div>
</main>
);
};

export default Page;
28 changes: 28 additions & 0 deletions src/app/api/post/comment/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { getAccessToken } from '@/utils/server/token';
import { NextRequest, NextResponse } from 'next/server';

export const POST = async (req: NextRequest) => {
const baseurl = `${process.env.API_URL}`;
const pathname = `/postings`;
const body = req.body;
const postId = req.nextUrl.searchParams.get('postId');

let token = await getAccessToken();
if (!token)
return NextResponse.json(
{ error: 'access token이 없거나 만료되었습니다.' },
{ status: 500 },
);
console.log(`${baseurl}${pathname}/${postId}/comments`);

return await fetch(`${baseurl}${pathname}/${postId}/comments`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token.value}`,
},
body,
// @ts-ignore -- 연결이 단방향임을 나타냄
duplex: 'half',
});
};
16 changes: 16 additions & 0 deletions src/app/api/post/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
import { getAccessToken } from '@/utils/server/token';
import { NextRequest, NextResponse } from 'next/server';

export const GET = async (req: NextRequest) => {
const baseurl = `${process.env.API_URL}`;
const pathname = `/postings`;
const postId = req.nextUrl.searchParams.get('postId');
let accesstoken = await getAccessToken();
let token = accesstoken?.value || process.env.ACCESS_TOKEN;

return await fetch(`${baseurl}${pathname}/${postId}/details`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
});
};

export const POST = async (req: NextRequest) => {
const baseurl = `${process.env.API_URL}`;
const pathname = `/postings`;
Expand Down
15 changes: 13 additions & 2 deletions src/components/common/CommentInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,16 @@ import { useUserStore } from '@/store/userStore';
import useModal from '@/hooks/useModal';
import { useRouter } from 'next/navigation';
import LoginModal from './LoginModal';
import { postPostComment } from '@/api/post';

// TODO :: isOpen, openModal
const CommentInput = ({ boardId }: { boardId: number }) => {
const CommentInput = ({
id,
type = 'board',
}: {
id: number;
type?: string;
}) => {
const [content, setContent] = useState<string>('');

const { isOpen, openModal, closeModal } = useModal();
Expand All @@ -33,7 +40,11 @@ const CommentInput = ({ boardId }: { boardId: number }) => {

const submitComment = async () => {
if (content.trim()) {
await postBoardComment(boardId, content.trim());
if (type == 'board') {
await postBoardComment(id, content.trim());
} else {
await postPostComment(id, content.trim());
}
router.refresh();
}
setContent('');
Expand Down
10 changes: 9 additions & 1 deletion src/components/country/PostPreviewItem.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import Profile from '../common/Profile';
import * as styles from '@/styles/country/post-preview-item.css';
import { PostPreviewProps } from '@/types/country/post';
import { usePathname, useRouter } from 'next/navigation';

const PostPreviewItem = ({ props }: { props: PostPreviewProps }) => {
const router = useRouter();
const pathname = usePathname();

const clickHandler = async () => {
router.push(pathname + `/post/${props.postingId}`);
};

return (
<div className={styles.postCard}>
<div className={styles.postCard} onClick={clickHandler}>
<span className={styles.title}>{props.title}</span>
<span className={styles.content}>{props.content}</span>
<div className={styles.profileContainer}>
Expand Down
18 changes: 15 additions & 3 deletions src/components/country/board/id/TravelerCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,20 @@ type Props = {

// TODO: // OpenModal props

const TravelerCard = ({ props }: { props: Props }) => {
const TravelerCard = ({
props,
title = '여행장',
type = 'board',
}: {
props: Props;
title?: string;
type?: string;
}) => {
return (
<div className={styles.cardContainer}>
<div className={styles.travelerTitle}>
<Image src="/icons/flag.svg" width={24} height={24} alt="flagIcon" />
여행장
{title}
</div>
<div className={styles.profileContainer}>
<Profile
Expand Down Expand Up @@ -53,7 +61,11 @@ const TravelerCard = ({ props }: { props: Props }) => {
<span className={styles.infoTitle}>동행 평점</span>
<span className={styles.infoContent}>별 네개</span>
</div>
<ChatButton nickName={props.nickName} userId={props.userId} />
{type == 'board' ? (
<ChatButton nickName={props.nickName} userId={props.userId} />
) : (
<></>
)}
</div>
);
};
Expand Down
27 changes: 2 additions & 25 deletions src/components/country/post/PostCardList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use client';
import Pagenation from '@/components/common/Pagination';
import Profile from '@/components/common/Profile';
import * as styles from '@/styles/country/post/page.css';
import { useEffect, useState } from 'react';
import SelectCateogry from './SelectCategory';
Expand All @@ -10,6 +9,7 @@ import { usePostStore } from '@/store/postStore';
import EmptyPost from '../EmptyPost';
import { usePathname } from 'next/navigation';
import { getCountryName } from '@/utils/country';
import PostCardListItem from './PostCardListItem';

const PostCardList = () => {
const pathname = usePathname();
Expand Down Expand Up @@ -57,7 +57,7 @@ const PostCardList = () => {
<ul className={styles.postContainer}>
{postList.map((post) => (
<li key={post.postingId} className={styles.postItemContainer}>
<PostPreview props={post} />
<PostCardListItem props={post} />
</li>
))}
</ul>
Expand All @@ -74,27 +74,4 @@ const PostCardList = () => {
);
};

// postcard와 css가 다름
const PostPreview = ({ props }: { props: PostPreviewProps }) => {
return (
<div className={styles.postCard}>
<span className={styles.title}>{props.title}</span>
<span className={styles.content}>{props.content}</span>
<div className={styles.profileContainer}>
<Profile
url={props.userImageUrl}
width={18}
height={18}
changeWidth={24}
changeHeight={24}
/>
<span className={styles.profileInfo}>
{`${props.userNickName} · ${props.userAgeRange.slice(0, 2)}대 ·
${props.userGender === 'male' ? '남' : '여'} · ${props.userNationality}`}
</span>
</div>
</div>
);
};

export default PostCardList;
36 changes: 36 additions & 0 deletions src/components/country/post/PostCardListItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { PostPreviewProps } from '@/types/country/post';
import Profile from '@/components/common/Profile';
import * as styles from '@/styles/country/post/page.css';
import { usePathname, useRouter } from 'next/navigation';

// postcard와 css가 다름
const PostCardListItem = ({ props }: { props: PostPreviewProps }) => {
const router = useRouter();
const pathname = usePathname();

const clickHandler = async () => {
router.push(pathname + `/${props.postingId}`);
};

return (
<div className={styles.postCard} onClick={clickHandler}>
<span className={styles.title}>{props.title}</span>
<span className={styles.content}>{props.content}</span>
<div className={styles.profileContainer}>
<Profile
url={props.userImageUrl}
width={18}
height={18}
changeWidth={24}
changeHeight={24}
/>
<span className={styles.profileInfo}>
{`${props.userNickName} · ${props.userAgeRange.slice(0, 2)}대 ·
${props.userGender === 'male' ? '남' : '여'} · ${props.userNationality}`}
</span>
</div>
</div>
);
};

export default PostCardListItem;
27 changes: 27 additions & 0 deletions src/components/country/post/id/Comment.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as styles from '@/styles/components/common/comment.css';
import Profile from '@/components/common/Profile';
import { formatDate } from '@/utils/date';
import { PostCommentType } from '@/types/country/post';

const Comment = ({ props }: { props: PostCommentType }) => {
return (
<div className={styles.commentContainer}>
<Profile
url={props.userImageUrl}
width={30}
height={30}
changeWidth={36}
changeHeight={36}
/>
<div className={styles.contentContainer}>
<div className={styles.infoContainer}>
<span className={styles.name}>{props.userNickName}</span>
<span className={styles.time}>{formatDate(props.createdAt)}</span>
</div>
<p className={styles.content}>{props.comment}</p>
</div>
</div>
);
};

export default Comment;
17 changes: 17 additions & 0 deletions src/components/country/post/id/CommentList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Comment from './Comment';
import { commentListContainer } from '@/styles/components/common/comment.css';
import { PostCommentType } from '@/types/country/post';

const CommentList = ({ comments }: { comments: PostCommentType[] }) => {
return (
<ul className={commentListContainer}>
{comments.map((comment) => (
<li key={comment.commentId}>
<Comment props={comment} />
</li>
))}
</ul>
);
};

export default CommentList;
Loading

0 comments on commit 1f7e94f

Please sign in to comment.