Skip to content
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

feat: 입덕 포인트 추가, 조회 구현 #311

Merged
merged 10 commits into from
Nov 23, 2023
32 changes: 32 additions & 0 deletions src/features/reviews/api/review.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ export interface UserEvaluation {
score: number;
}

export type AttractionType =
| "STORY"
| "CHARACTER"
| "DRAWING"
| "VOICE_ACTOR"
| "MUSIC";

export default class ReviewApi {
/** @description 리뷰 작성 요청 */
async addReview(review: AddReviewDto): Promise<void> {
Expand Down Expand Up @@ -106,4 +113,29 @@ export default class ReviewApi {
async getEvaluation(animeId: number) {
return get<UserEvaluation>(`/ratings/${animeId}`);
}

// 입덕 포인트

/** @description 입덕 포인트 남기기 */
async addAttractionPoint(
animeId: number,
attractionElements: AttractionType[],
) {
return post(`/attraction-points`, {
animeId,
attractionElements,
});
}

/** @description 입덕 포인트 존재 여부 조회 */
async getUserAttractionPointStatus(animeId: number) {
return get<{ isAttractionPoint: boolean }>(`/attraction-points/${animeId}`);
}

/** @description 리뷰 수정 시 입덕 포인트 조회 */
async getUserAttractionPoint(animeId: number, name: string) {
return get<AttractionPoint>(`/short-reviews/attraction-points`, {
params: { animeId, name },
});
}
}
30 changes: 29 additions & 1 deletion src/features/reviews/api/reviewDev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ import recentReviewMock1 from "./mock/recentReview1.json";
import recentReviewMock2 from "./mock/recentReview2.json";
import recentReviewMock3 from "./mock/recentReview3.json";
import recentReviewOnlyOneMock from "./mock/recentReviewOnlyOne.json";
import { AddReviewDto, ReviewInfo, UserEvaluation } from "./review";
import {
AddReviewDto,
AttractionType,
ReviewInfo,
UserEvaluation,
} from "./review";

export default class ReviewDevApi {
/** @description 리뷰 작성 요청*/
Expand Down Expand Up @@ -71,4 +76,27 @@ export default class ReviewDevApi {
async getEvaluation(animeId: number) {
return get<UserEvaluation>(`/ratings/${animeId}`);
}

/** @description 입덕 포인트 남기기 */
async addAttractionPoint(
animeId: number,
attractionElements: AttractionType[],
) {
return post(`/attraction-points`, {
animeId,
attractionElements,
});
}

/** @description 입덕 포인트 존재 여부 조회 */
async getUserAttractionPointStatus(animeId: number) {
return get<{ isAttractionPoint: boolean }>(`/attraction-points/${animeId}`);
}

/** @description 리뷰 수정 시 입덕 포인트 조회 */
async getUserAttractionPoint(animeId: number, name: string) {
return get<AttractionPoint>(`/short-reviews/attraction-points`, {
params: { animeId, name },
});
}
}
18 changes: 5 additions & 13 deletions src/features/reviews/components/ReviewCard/ReviewMoreButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import useSnackBar from "@/components/SnackBar/useSnackBar";
import useToast from "@/components/Toast/useToast";
import DropDownModal from "@/features/users/components/DropDownModal";
import useDropDownModal from "@/features/users/components/DropDownModal/useDropDownModal";
import useDebounce from "@/hooks/useDebounce";

import useEvaluation from "../../hook/useEvaluation";
import ShortReviewModal from "../ReviewRating/ShortReviewModal";
Expand All @@ -19,15 +20,6 @@ import {
RatingContainer,
} from "./ReviewMoreButton.style";

// TODO: 서버에서 가져오기
const USER_MOCK_ATTRACTION = {
character: true,
art: true,
story: false,
voiceActing: false,
sound: true,
};

interface ReviewMoreButtonProps {
isMine: boolean;
reviewId: number;
Expand All @@ -37,6 +29,8 @@ interface ReviewMoreButtonProps {
score: number;
}

const DEBOUNCE_DELAY = 200;

export default function ReviewMoreButton({
isMine,
reviewId,
Expand All @@ -62,7 +56,7 @@ export default function ReviewMoreButton({
handleReviewModalToggle();
};

const handleRate = (value: number) => {
const handleRate = useDebounce((value: number) => {
evaluationMutation.mutate(
{ score: value },
{
Expand All @@ -71,8 +65,7 @@ export default function ReviewMoreButton({
},
},
);
console.log(value);
};
}, DEBOUNCE_DELAY);

const handleReviewDeleteClick = () => console.log("리뷰삭제");

Expand Down Expand Up @@ -150,7 +143,6 @@ export default function ReviewMoreButton({
animeId,
content,
isSpoiler,
...USER_MOCK_ATTRACTION,
}}
>
<MyRating>내 별점</MyRating>
Expand Down
154 changes: 78 additions & 76 deletions src/features/reviews/components/ReviewRating/ShortReviewModal.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { PropsWithChildren } from "react";

import DeferredComponent from "@/components/DeferredComponent";
import Modal from "@/components/Modal";
import Textarea from "@/components/TextArea";

import { ReviewInfo } from "../../api/review";
import useReviewForm from "../../hook/useReviewForm";
import AttractionPoint from "../AttractionPoint";

Expand All @@ -15,23 +17,16 @@ import {
} from "./ShortReviewModal.style";
import SpoilerCheckBox from "./SpoilerCheckBox";

export interface MOCK_USER_REVIEW_DATA {
reviewId: number;
animeId: number;
content: string;
isSpoiler: boolean;
character: boolean;
art: boolean;
story: boolean;
voiceActing: boolean;
sound: boolean;
}
export type UserReview = Pick<
ReviewInfo,
"reviewId" | "animeId" | "content" | "isSpoiler"
>;

interface ShortReviewModalProps {
onClose: () => void;
onReview: () => void;
showBackdrop?: boolean;
userReviewData?: MOCK_USER_REVIEW_DATA;
userReviewData?: UserReview;
}

export default function ShortReviewModal({
Expand Down Expand Up @@ -60,14 +55,14 @@ export default function ShortReviewModal({
isChecked: form.character,
},
{
name: "art",
name: "drawing",
content: (
<>
현실 찢고 들어간듯한/이쁜
<strong style={{ marginLeft: 4 }}>그림체</strong>
</>
),
isChecked: form.art,
isChecked: form.drawing,
},
{
name: "story",
Expand All @@ -80,84 +75,91 @@ export default function ShortReviewModal({
isChecked: form.story,
},
{
name: "voiceActing",
name: "voiceActor",
content: (
<>
<strong>성우</strong>들의 미친 연기력
</>
),
isChecked: form.voiceActing,
isChecked: form.voiceActor,
},
{
name: "sound",
name: "music",
content: (
<>
가슴이 옹졸해지는 <strong style={{ marginLeft: 4 }}>음악</strong>
</>
),
isChecked: form.sound,
isChecked: form.music,
},
];

return (
<Modal onClose={onClose} showBackdrop={showBackdrop}>
<Modal.Content>
<Title>한 줄 리뷰 모달</Title>
{children}
<ReviewContentSection>
<label htmlFor="content">작품에 대한 의견을 남겨주세요</label>
<Textarea
name="content"
placeholder="최대 100자 까지 입력 가능해요"
onChange={handleTextInputChange}
value={form.content}
warn={error}
message="최소 10자 이상 입력해 주세요."
/>
<SpoilerCheckBox
name="isSpoiler"
checked={form.isSpoiler}
onChange={handleCheckboxChange}
/>
</ReviewContentSection>
<AttractionPointSection>
<label htmlFor="attraction-point">
이 애니의 입덕포인트는 무엇인가요?
</label>
<p>
입덕 포인트 선택 시 <span>포인트</span>가 쌓여요!
</p>
<AttractionPointList>
{attractionPoints.map((point) => (
<li key={point.name}>
<AttractionPoint
name={point.name}
isChecked={point.isChecked}
onChange={handleCheckboxChange}
>
{point.content}
</AttractionPoint>
</li>
))}
</AttractionPointList>
</AttractionPointSection>
</Modal.Content>
<DeferredComponent>
<Modal onClose={onClose} showBackdrop={showBackdrop}>
<Modal.Content>
<Title>한 줄 리뷰 모달</Title>
{children}
<ReviewContentSection>
<label htmlFor="content">작품에 대한 의견을 남겨주세요</label>
<Textarea
name="content"
placeholder="최대 100자 까지 입력 가능해요"
onChange={handleTextInputChange}
value={form.content}
warn={error}
message="최소 10자 이상 입력해 주세요."
/>
<SpoilerCheckBox
name="isSpoiler"
checked={form.isSpoiler}
onChange={handleCheckboxChange}
/>
</ReviewContentSection>
<AttractionPointSection>
<label htmlFor="attraction-point">
이 애니의 입덕포인트는 무엇인가요?
</label>
<p>
입덕 포인트 선택 시 <span>포인트</span>가 쌓여요!
</p>
<AttractionPointList>
{attractionPoints.map((point) => (
<li key={point.name}>
<AttractionPoint
name={point.name}
isChecked={point.isChecked}
onChange={handleCheckboxChange}
>
{point.content}
</AttractionPoint>
</li>
))}
</AttractionPointList>
</AttractionPointSection>
</Modal.Content>

<Modal.Actions direction="row">
<Button
name="닫기"
variant="solid"
color="neutral"
size="lg"
isBlock
onClick={onClose}
>
취소
</Button>
<Button name="평가 완료" isBlock size="lg" onClick={handleReviewSubmit}>
완료
</Button>
</Modal.Actions>
</Modal>
<Modal.Actions direction="row">
<Button
name="닫기"
variant="solid"
color="neutral"
size="lg"
isBlock
onClick={onClose}
>
취소
</Button>
<Button
name="평가 완료"
isBlock
size="lg"
onClick={handleReviewSubmit}
>
완료
</Button>
</Modal.Actions>
</Modal>
</DeferredComponent>
);
}
Loading