-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
게시글, 댓글, 닉네임 신고 API fetch 함수 구현 HTML5 해결 #296
Changes from 22 commits
58a1e31
8b3a912
bf6bf6a
32cfca9
4d40a19
eb73008
617cb22
099f073
3b1ed66
a487115
c379888
a5fae3f
28ec3b2
3d2da36
f1145fe
e05e810
1ee2b3e
25ef888
e29520a
09a8d60
a984e28
b4ec217
2b66396
a962913
c63314b
c62d12e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
<!DOCTYPE html> | ||
<html lang="ko"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ import { | |
} from '@utils/fetch'; | ||
|
||
const BASE_URL = process.env.VOTOGETHER_BASE_URL; | ||
const MOCK_URL = process.env.VOTOGETHER_MOCKING_URL; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍👍 |
||
|
||
export const transformPostResponse = (post: PostInfoResponse): PostInfo => { | ||
return { | ||
|
@@ -68,7 +69,7 @@ export const editPost = async (postId: number, updatedPost: FormData) => { | |
}; | ||
|
||
export const removePost = async (postId: number) => { | ||
return await deleteFetch(`${BASE_URL}/posts/${postId}`); | ||
return await deleteFetch(`${MOCK_URL}/posts/${postId}`); | ||
}; | ||
|
||
export const setEarlyClosePost = async (postId: number) => { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { ReportRequest } from '@type/report'; | ||
|
||
import { postFetch } from '@utils/fetch'; | ||
|
||
const BASE_URL = process.env.VOTOGETHER_MOCKING_URL; | ||
|
||
export const reportContent = async (reportData: ReportRequest) => { | ||
return await postFetch(`${BASE_URL}/report`, reportData); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
import ReportModal from '.'; | ||
|
||
const meta: Meta<typeof ReportModal> = { | ||
component: ReportModal, | ||
}; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof ReportModal>; | ||
|
||
export const Nickname: Story = { | ||
render: () => ( | ||
<ReportModal reportType="NICKNAME" handleCancelClick={() => {}} handleReportClick={() => {}} /> | ||
), | ||
}; | ||
|
||
export const Comment: Story = { | ||
render: () => ( | ||
<ReportModal reportType="COMMENT" handleCancelClick={() => {}} handleReportClick={() => {}} /> | ||
), | ||
}; | ||
|
||
export const Post: Story = { | ||
render: () => ( | ||
<ReportModal reportType="POST" handleCancelClick={() => {}} handleReportClick={() => {}} /> | ||
), | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { ReportInfo, ReportType } from '@type/report'; | ||
|
||
export const REPORT_MESSAGE = { | ||
BEHAVIOR: '부적절한 언행/혐오/차별적 표현이 포함되어있습니다.', | ||
SPAMMING: '도배성 내용이 포함되어있습니다.', | ||
ADVERTISING: '광고성 내용이 포함되어있습니다.', | ||
SENSUALITY: '음란성 내용이 포함되어 있습니다.', | ||
SPECIFIC_PERSON: '특정인이 거론되어있습니다.', | ||
PRIVACY: '개인정보가 포함되어있습니다.', | ||
}; | ||
|
||
export const REPORT_TYPE: Record<ReportType, ReportInfo> = { | ||
POST: { name: '게시글 신고', reportMessageList: REPORT_MESSAGE }, | ||
COMMENT: { name: '댓글 신고', reportMessageList: REPORT_MESSAGE }, | ||
NICKNAME: { name: '닉네임 신고', reportMessageList: REPORT_MESSAGE }, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { ReportType } from '@type/report'; | ||
|
||
import { useSelect } from '@hooks/useSelect'; | ||
|
||
import Select from '@components/common/Select'; | ||
import TwoButtonModal from '@components/common/TwoButtonModal'; | ||
|
||
import { REPORT_TYPE } from './constants'; | ||
|
||
interface UserReportModalProps { | ||
reportType: ReportType; | ||
handleCancelClick: () => void; | ||
handleReportClick: (reason: string) => void; | ||
} | ||
|
||
export default function ReportModal({ | ||
reportType, | ||
handleCancelClick, | ||
handleReportClick, | ||
}: UserReportModalProps) { | ||
const { name, reportMessageList } = REPORT_TYPE[reportType]; | ||
const defaultReportMessage = Object.keys(reportMessageList)[0]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. reportMessageList.BEHAVIOR 대신 keys 함수를 사용하시는 이유가 궁금합니다. '첫번째 인덱스의 값을 가져오겠다' 라는 의미를 위해서인지..? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네 맞습니다. 현재 해당 모달은 key에 string으로 필수적으로 들어가는 key값이 없습니다. |
||
const { selectedOption, handleOptionChange } = useSelect(defaultReportMessage); | ||
|
||
const handlePrimaryButtonClick = () => { | ||
handleReportClick(selectedOption); | ||
handleCancelClick(); | ||
}; | ||
|
||
return ( | ||
<TwoButtonModal | ||
title={name} | ||
primaryButton={{ | ||
text: '신고', | ||
handleClick: handlePrimaryButtonClick, | ||
}} | ||
secondaryButton={{ | ||
text: '취소', | ||
handleClick: handleCancelClick, | ||
}} | ||
> | ||
<Select | ||
aria-label={`${name} 방법 선택`} | ||
optionList={reportMessageList} | ||
handleOptionChange={handleOptionChange} | ||
selectedOption={reportMessageList[selectedOption]} | ||
/> | ||
</TwoButtonModal> | ||
); | ||
} |
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,14 +2,16 @@ import { useState } from 'react'; | |
import { useParams } from 'react-router-dom'; | ||
|
||
import { Comment } from '@type/comment'; | ||
import { ReportRequest } from '@type/report'; | ||
|
||
import { useDeleteComment } from '@hooks/query/comment/useDeleteComment'; | ||
import { useToggle } from '@hooks/useToggle'; | ||
|
||
import CommentDeleteModal from '@components/comment/CommentDeleteModal'; | ||
import { reportContent } from '@api/report'; | ||
|
||
import CommentTextForm from '@components/comment/CommentList/CommentTextForm'; | ||
import CommentReportModal from '@components/report/CommentReportModal'; | ||
import UserReportModal from '@components/report/UserReportModal'; | ||
import DeleteModal from '@components/common/DeleteModal'; | ||
import ReportModal from '@components/ReportModal'; | ||
|
||
import ellipsis from '@assets/ellipsis-horizontal.svg'; | ||
|
||
|
@@ -39,6 +41,22 @@ export default function CommentItem({ comment, userType }: CommentItemProps) { | |
setAction(menu); | ||
}; | ||
|
||
const handleCommentReportClick = async (reason: string) => { | ||
const reportData = { type: 'POST', id: postId, reason } as ReportRequest; | ||
|
||
await reportContent(reportData) | ||
.then(res => alert('댓글을 신고했습니다.')) | ||
.catch(error => alert('댓글 신고가 실패했습니다.')); | ||
}; | ||
|
||
const handleNicknameReportClick = async (reason: string) => { | ||
const reportData = { type: 'NICKNAME', id: member.id, reason } as ReportRequest; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 단언 대신 선언 어떠신가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아 선언을 하지 못한다고 생각했는데 생각해보니 가능하네요! 감사합니다! |
||
|
||
await reportContent(reportData) | ||
.then(res => alert('작성자 닉네임을 신고했습니다.')) | ||
.catch(error => alert('작성자 닉네임 신고가 실패했습니다.')); | ||
}; | ||
|
||
const handleCancelClick = () => { | ||
setAction(null); | ||
}; | ||
|
@@ -84,16 +102,25 @@ export default function CommentItem({ comment, userType }: CommentItemProps) { | |
<S.Description>{content}</S.Description> | ||
)} | ||
{action === COMMENT_ACTION.DELETE && ( | ||
<CommentDeleteModal | ||
<DeleteModal | ||
target="댓글" | ||
handleCancelClick={handleCancelClick} | ||
handleDeleteClick={handleDeleteClick} | ||
/> | ||
)} | ||
{action === COMMENT_ACTION.USER_REPORT && ( | ||
<UserReportModal handleCancelClick={handleCancelClick} /> | ||
<ReportModal | ||
reportType="NICKNAME" | ||
handleReportClick={handleNicknameReportClick} | ||
handleCancelClick={handleCancelClick} | ||
/> | ||
)} | ||
{action === COMMENT_ACTION.COMMENT_REPORT && ( | ||
<CommentReportModal handleCancelClick={handleCancelClick} /> | ||
<ReportModal | ||
reportType="COMMENT" | ||
handleReportClick={handleCommentReportClick} | ||
handleCancelClick={handleCancelClick} | ||
/> | ||
)} | ||
</S.Container> | ||
); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
import DeleteModal from '.'; | ||
|
||
const meta: Meta<typeof DeleteModal> = { | ||
component: DeleteModal, | ||
}; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof DeleteModal>; | ||
|
||
export const Post: Story = { | ||
render: () => ( | ||
<DeleteModal target="게시물" handleCancelClick={() => {}} handleDeleteClick={() => {}} /> | ||
), | ||
}; | ||
|
||
export const User: Story = { | ||
render: () => ( | ||
<DeleteModal target="계정" handleCancelClick={() => {}} handleDeleteClick={() => {}} /> | ||
), | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import TwoButtonModal from '../../common/TwoButtonModal'; | ||
|
||
import * as S from './style'; | ||
|
||
interface DeleteModalProps { | ||
target: string; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. string 대신 타입의 유니온으로 구체해봐도 좋겠네요! |
||
handleCancelClick: () => void; | ||
handleDeleteClick: () => void; | ||
} | ||
|
||
export default function DeleteModal({ | ||
target, | ||
handleCancelClick, | ||
handleDeleteClick, | ||
}: DeleteModalProps) { | ||
const handlePrimaryButtonClick = () => { | ||
handleDeleteClick(); | ||
handleCancelClick(); | ||
}; | ||
|
||
return ( | ||
<TwoButtonModal | ||
title={`${target} 삭제하기`} | ||
primaryButton={{ | ||
text: '삭제', | ||
handleClick: handlePrimaryButtonClick, | ||
}} | ||
secondaryButton={{ | ||
text: '취소', | ||
handleClick: handleCancelClick, | ||
}} | ||
> | ||
<S.Description>{`${target}을(를) 삭제하시겠습니까?\n${target}은(는) 삭제되고 취소할 수 없습니다.`}</S.Description> | ||
</TwoButtonModal> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
import { PostMenuItem } from '@type/menu'; | ||
|
||
import PostMenu from '.'; | ||
|
||
const meta: Meta<typeof PostMenu> = { | ||
component: PostMenu, | ||
}; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof PostMenu>; | ||
|
||
const menuList: PostMenuItem[] = [ | ||
{ color: 'black', content: '닉네임 신고', action: 'NICKNAME_REPORT' }, | ||
{ color: 'black', content: '게시글 신고', action: 'POST_REPORT' }, | ||
]; | ||
|
||
export const Default: Story = { | ||
render: () => <PostMenu menuList={menuList} handleMenuClick={() => {}} />, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { MouseEvent } from 'react'; | ||
|
||
import { PostAction, PostMenuItem } from '@type/menu'; | ||
|
||
import * as S from './style'; | ||
|
||
interface PostMenuProps { | ||
menuList: PostMenuItem[]; | ||
handleMenuClick: (menu: PostAction) => void; | ||
} | ||
|
||
export default function PostMenu({ menuList, handleMenuClick }: PostMenuProps) { | ||
return ( | ||
<S.Container> | ||
{menuList.map(({ content, color, action }) => ( | ||
<S.Menu | ||
key={content} | ||
type="button" | ||
$color={color} | ||
onClick={(event: MouseEvent) => { | ||
event.stopPropagation(); | ||
handleMenuClick(action); | ||
}} | ||
> | ||
{content} | ||
</S.Menu> | ||
))} | ||
</S.Container> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍👍👍