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: 리뷰 좋아요, 신고하기 기능 추가 #537

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
51031d1
feat: 리뷰 api 수정으로 인한 타입 수정 및 api 요청 코드 추가 (#528)
turtle601 Sep 28, 2023
e0fdb5f
feat: 명세 수정으로 인한 목데이터 수정 (#528)
turtle601 Sep 28, 2023
8e37964
feat: 리뷰 좋아요 및 신고하기 관련 기능 훅 구현(#528)
turtle601 Sep 28, 2023
0947225
refactor: 리뷰 레이아웃에 사용될 아이콘 추가 (#528)
turtle601 Sep 28, 2023
e607063
feat: RestaurantItem 컴포넌트 기능 추가 및 구현 (#528)
turtle601 Sep 28, 2023
14be6f2
refactor: RestaurantReviewList 컴포넌트 스토리북 오류 및 레이아웃 수정 (#528)
turtle601 Sep 28, 2023
1f3ea41
feat: 리뷰 좋아요 시 좋아요 숫자도 동시에 올라가는 기능 구현(#528)
turtle601 Sep 28, 2023
d0d2077
feat: 신고하기 관련 모달 폼 구현 및 기능 구현(#528)
turtle601 Sep 28, 2023
46047b4
fix: 명세와 다른 키 값 수정 (#528)
turtle601 Sep 28, 2023
3c9efef
feat: ReviewModalProvider에 있는 update, create, delete 함수 추상화 (#528)
turtle601 Sep 28, 2023
e410b2e
refactor: ReviewForm index 파일 추가 (#528)
turtle601 Sep 28, 2023
fe69fc7
refactor: ReviewFormType 생성 (#528)
turtle601 Sep 28, 2023
6a210dd
refactor: 중복되는 네이밍(onSuccess)으로 인한 코드 수정 (#528)
turtle601 Sep 28, 2023
1028c19
refactor: REVIEW_FORM_TITLE 변수 생성 및 코드 수정 (#528)
turtle601 Sep 28, 2023
6ee2702
refactor: ReviewSubmitButtonType 타입 추가 (#528)
turtle601 Sep 28, 2023
a76c983
style: import type 위치 제일 하단에 위치하기(#528)
turtle601 Sep 28, 2023
a29e72a
refactor: create, update 함수를 submit관련 함수로 통합(#528)
turtle601 Sep 28, 2023
16b22f8
refactor: 별표 관련 아이콘 저장 (#539)
turtle601 Sep 30, 2023
7dc3bca
feat: Star 컴포넌트 구현(#539)
turtle601 Sep 30, 2023
4340b18
feat: StarRating 컴포넌트 구현 (#539)
turtle601 Sep 30, 2023
9527fc7
refactor: svg 파일 사이즈 수정 (#539)
turtle601 Sep 30, 2023
7fb11c4
feat: 별점 기능 구현 (#539)
turtle601 Sep 30, 2023
ee9fde6
feat: ImageForm 컴포넌트 구현 (#539)
turtle601 Sep 30, 2023
204188c
feat: ReviewImageForm 컴포넌트 구현 (#539)
turtle601 Sep 30, 2023
9e68a48
feat: 업로드한 이미지 취소 기능 추가 (#539)
turtle601 Sep 30, 2023
932d027
fix: 잘못된 height 설정으로 인한 콘솔 오류 수정 (#539)
turtle601 Oct 1, 2023
f512aef
fix: 잘못된 쿼리 스트링을 가져오는 오류 해결 (#539)
turtle601 Oct 1, 2023
d6a3315
feat: 리뷰 등록, 수정 시 이미지를 추가로 넣는 api 인스턴스 생성 및 기능 구현 (#539)
turtle601 Oct 1, 2023
df78df7
feat: 리뷰 post, patch 관련 msw 구현 (#539)
turtle601 Oct 1, 2023
53c0572
feat: 리뷰 form 스타일 개선 (#539)
turtle601 Oct 1, 2023
d624f5e
feat: 이미지 업로드 개수 제한 기능 추가 (#539)
turtle601 Oct 1, 2023
194dfaa
fix: 로그인 상태에서 처음 모달을 열었을 경우 로그인이 되지 않은 상태이던 오류 해결 (#539)
turtle601 Oct 1, 2023
76280a9
feat: 리뷰 create, update, delete 시 성공, 실패 시 Toast UI를 띄우는 기능 구현 (#539)
turtle601 Oct 1, 2023
c1bd8a6
fix: 모바일 hover 관련 에러 해결 (#539)
turtle601 Oct 1, 2023
e0c979d
refactor: ReviewModal, ReviewDeleteForm 파일 분리 (#539)
turtle601 Oct 1, 2023
197bef5
feat: 별점, 리뷰 관련 disabled 기능 추가 (#539)
turtle601 Oct 1, 2023
2c3d765
fix: 리뷰 신고하기 모달 폼에 별점, 사진 등록 기능이 있어 제거 (#539)
turtle601 Oct 1, 2023
7bcfc66
refactor: Star, StarRating 컴포넌트 props size 추가 및 Star 컴포넌트 네이밍 수정 (#539)
turtle601 Oct 1, 2023
fa2886b
refactor: 동작하지 않은 RestaurantReviewItem 스토리북 제거 (#539)
turtle601 Oct 1, 2023
2b58364
fix: 리뷰 아이템에 별점 기능 추가로 인한 목 데이터 수정 (#539)
turtle601 Oct 1, 2023
f42d801
feat: RestaurantReviewItem에 별점 ui 추가 (#539)
turtle601 Oct 1, 2023
368cc48
feat: Review이미지 타입 데이터 변경으로 인한 코드 수정 (#543)
turtle601 Oct 3, 2023
9cb0890
feat: 이미지 업로드 시 파일 압축 (#543)
turtle601 Oct 3, 2023
01a59cc
refactor: ReviewModalProvier의 useMemo 디펜던시 수정 (#543)
turtle601 Oct 3, 2023
535bc5b
refactor: getImgUrl import 방식 수정 (#543)
turtle601 Oct 3, 2023
0bf334c
feat: 이미지 확장자 변환 함수 구현 (#543)
turtle601 Oct 3, 2023
e355cbb
refactor: review 관련 msw 코드 수정 (#543)
turtle601 Oct 3, 2023
a7448f1
feat: 리뷰 관련 명세 수정으로 인한 코드 수정 (#543)
turtle601 Oct 4, 2023
99a2d37
feat: review update 명세 수정으로 인한 코드 수정 + 수정 시 사진 수정 기능 제거 (#543)
turtle601 Oct 5, 2023
5462a03
refactor: 불필요한 svg 파일 삭제 (#543)
turtle601 Oct 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@tanstack/react-query": "^4.32.0",
"@tanstack/react-query-devtools": "^4.32.0",
"axios": "^1.4.0",
"browser-image-compression": "^2.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.14.2",
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/@types/api.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,24 @@ export interface VideoList {
currentElementsCount: number;
}

export type StarRate = 0 | 0.5 | 1 | 1.5 | 2 | 2.5 | 3 | 3.5 | 4 | 4.5 | 5;

export type ReviewUploadImageType = {
imgUrl: string;
imgFile: Blob;
};

export interface RestaurantReview {
id: number;
nickname: string;
memberId: number;
profileImageUrl: string;
content: string;
createdDate: HyphenatedDate;
isLiked: boolean;
likeCount: number;
rating: StarRate;
reviewImageUrls: ReviewUploadImageType[];
}

export interface RestaurantReviewData {
Expand All @@ -71,6 +82,7 @@ export interface RestaurantReviewData {

export interface RestaurantReviewPatchBody {
content: string;
rating: number;
}

export interface RestaurantReviewPostBody {
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/@types/review.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type ReviewFormType = 'create' | 'update' | 'delete' | 'report' | 'all' | null;

export type ReviewSubmitButtonType = Exclude<ReviewFormType, 'all' | 'delete' | 'report' | null>;
8 changes: 8 additions & 0 deletions frontend/src/api/apiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,11 @@ export const apiClient = axios.create({
'Content-type': 'application/json',
},
});

export const apiUserFilesClient = axios.create({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네이밍에 관한 부분들은 나중에 함께 얘기나눠보면 좋을 것 같네요.

baseURL: `${process.env.BASE_URL}`,
headers: {
'Content-Type': 'multipart/form-data',
},
withCredentials: true,
});
31 changes: 21 additions & 10 deletions frontend/src/api/restaurantReview.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import type { RestaurantReviewPatchBody, RestaurantReviewPostBody } from '~/@types/api.types';
import { apiClient, apiUserClient } from './apiClient';
import { apiUserClient, apiUserFilesClient } from './apiClient';

import type { RestaurantReviewPatchBody } from '~/@types/api.types';

export const getRestaurantReview = async (id: string) => {
const response = await apiClient.get(`/reviews?restaurantId=${id}`);
const response = await apiUserClient.get(`/reviews?restaurantId=${id}`);
return response.data;
};

export const postRestaurantReview = async (body: RestaurantReviewPostBody) => {
const response = await apiUserClient.post(`/reviews`, body);
return response;
};

export const deleteRestaurantReview = async (reviewId: number) => {
const response = await apiUserClient.delete(`/reviews/${reviewId}`);
export const postRestaurantReview = async (body: FormData) => {
const response = await apiUserFilesClient.post(`/reviews`, body);
return response;
};

Expand All @@ -26,3 +22,18 @@ export const patchRestaurantReview = async ({
const response = await apiUserClient.patch(`/reviews/${reviewId}`, body);
return response;
};

export const deleteRestaurantReview = async (reviewId: number) => {
const response = await apiUserFilesClient.delete(`/reviews/${reviewId}`);
return response;
};

export const postRestaurantReviewLike = async (reviewId: number) => {
const response = await apiUserClient.post(`/reviews/${reviewId}/like`);
return response;
};

export const postRestaurantReviewReport = async ({ reviewId, content }: { reviewId: number; content: string }) => {
const response = await apiUserClient.post(`/reviews/${reviewId}/report`, { content });
return response;
};
46 changes: 46 additions & 0 deletions frontend/src/assets/icons/etc/groups.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/icons/etc/speakerphone.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/icons/etc/thumb-up.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/icons/star/empty-left-star-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/icons/star/empty-right-star-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions frontend/src/components/@common/ImageForm/ImageForm.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { Meta, StoryObj } from '@storybook/react';
import ImageForm from './ImageForm';

const meta: Meta<typeof ImageForm> = {
title: 'ImageForm',
component: ImageForm,
};

export default meta;

type Story = StoryObj<typeof ImageForm>;

export const Default: Story = {
args: {},
};
37 changes: 37 additions & 0 deletions frontend/src/components/@common/ImageForm/ImageForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { styled } from 'styled-components';
import { FONT_SIZE } from '~/styles/common';

interface ImageFormProps extends React.ComponentPropsWithoutRef<'input'> {
onChange: React.ChangeEventHandler<HTMLInputElement>;
}

function ImageForm({ onChange, ...restProps }: ImageFormProps) {
return (
<StyledButton>
+
<input type="file" onChange={onChange} {...restProps} />
</StyledButton>
);
}

export default ImageForm;

const StyledButton = styled.label`
padding: 4.8rem;

border: 4px solid #ddd;
border-radius: 20px;
background-color: transparent;

font-size: ${FONT_SIZE.lg};

cursor: pointer;

&:hover {
background-color: #ddd;
}

input[type='file'] {
display: none;
}
`;
3 changes: 3 additions & 0 deletions frontend/src/components/@common/ImageForm/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ImageForm from '~/components/@common/ImageForm/ImageForm';

export default ImageForm;
18 changes: 18 additions & 0 deletions frontend/src/components/@common/Star/HalfStar.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { Meta, StoryObj } from '@storybook/react';
import Star from './HalfStar';

const meta: Meta<typeof Star> = {
title: 'Star',
component: Star,
};

export default meta;

type Story = StoryObj<typeof Star>;

export const Default: Story = {
args: {
isLeft: true,
isFilled: false,
},
};
47 changes: 47 additions & 0 deletions frontend/src/components/@common/Star/HalfStar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { styled } from 'styled-components';

import EmptyLeftStarIcon from '~/assets/icons/star/empty-left-star-icon.svg';
import EmptyRightStarIcon from '~/assets/icons/star/empty-right-star-icon.svg';

import type { StarRate } from '~/@types/api.types';

interface HalfStarProps {
isLeft: boolean;
isFilled: boolean;
rateNumber: StarRate;
size: `${number}px`;
onRateClick: React.MouseEventHandler<HTMLButtonElement>;
}

function HalfStar({ isLeft, isFilled, rateNumber, size, onRateClick }: HalfStarProps) {
const halfStarColor = isFilled ? '#FFD601' : '#e8e8e8';

if (isLeft) {
return (
<StyleHalfStarButton type="button" data-rate={rateNumber} size={size} onClick={onRateClick}>
<EmptyLeftStarIcon fill={halfStarColor} />
</StyleHalfStarButton>
);
}

return (
<StyleHalfStarButton type="button" onClick={onRateClick} size={size} data-rate={rateNumber}>
<EmptyRightStarIcon fill={halfStarColor} />
</StyleHalfStarButton>
);
}

export default HalfStar;

const StyleHalfStarButton = styled.button<{ size: `${number}px` }>`
padding: 0;
margin: 0;

border: none;
background: none;
outline: none;

& > svg {
width: ${({ size }) => size};
}
`;
3 changes: 3 additions & 0 deletions frontend/src/components/@common/Star/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import HalfStar from '~/components/@common/Star/HalfStar';

export default HalfStar;
17 changes: 17 additions & 0 deletions frontend/src/components/@common/StarRating/StarRating.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { Meta, StoryObj } from '@storybook/react';
import StarRating from './StarRating';

const meta: Meta<typeof StarRating> = {
title: 'StarRating',
component: StarRating,
};

export default meta;

type Story = StoryObj<typeof StarRating>;

export const Default: Story = {
args: {
rate: 3.5,
},
};
40 changes: 40 additions & 0 deletions frontend/src/components/@common/StarRating/StarRating.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ComponentPropsWithoutRef } from 'react';
import { styled } from 'styled-components';
import HalfStar from '~/components/@common/Star/HalfStar';

import type { StarRate } from '~/@types/api.types';

const starRateList: StarRate[] = [0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5];

interface StarRatingProps extends ComponentPropsWithoutRef<'div'> {
rate: StarRate;
size?: `${number}px`;
onRateClick?: React.MouseEventHandler<HTMLButtonElement>;
}

function StarRating({ rate, onRateClick, size = '20px', ...restProps }: StarRatingProps) {
return (
<StyledStarRatingWrapper {...restProps}>
{starRateList.map((starRate, idx) => {
const isLeft = idx % 2 === 0;
const isFilled = starRate <= rate;

return (
<HalfStar size={size} isLeft={isLeft} isFilled={isFilled} rateNumber={starRate} onRateClick={onRateClick} />
);
})}
</StyledStarRatingWrapper>
);
}

export default StarRating;

const StyledStarRatingWrapper = styled.div`
display: flex;

width: max-content;

button:nth-child(2n) {
margin-right: 1.2rem;
}
`;
3 changes: 3 additions & 0 deletions frontend/src/components/@common/StarRating/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import StarRating from '~/components/@common/StarRating/StarRating';

export default StarRating;
2 changes: 1 addition & 1 deletion frontend/src/components/@common/Toast/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import useToastState from '~/hooks/store/useToastState';
import useMediaQuery from '~/hooks/useMediaQuery';
import { BORDER_RADIUS, FONT_SIZE } from '~/styles/common';
import Portal from '../Portal';
import getImgUrl from '~/utils/image';
import { getImgUrl } from '~/utils/image';

interface StyledToastProps {
isSuccess?: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { styled, css } from 'styled-components';
import { BORDER_RADIUS, FONT_SIZE, paintSkeleton } from '~/styles/common';
import getImgUrl from '~/utils/image';
import { getImgUrl } from '~/utils/image';

interface WaterMarkImageProps {
waterMark: string;
Expand Down
Loading
Loading