Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
chaeseungyun committed Aug 30, 2024
2 parents 64d6512 + 1467b2d commit e87caa5
Show file tree
Hide file tree
Showing 11 changed files with 460 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import OwnerList from 'pages/UserManage/Owner/OwnerList';
import OwnerRequestList from 'pages/UserManage/OwnerRequest/OwnerRequestList';
import OwnerRequestDetail from 'pages/UserManage/OwnerRequest/OwnerRequestDetail';
import OwnerDetail from 'pages/UserManage/Owner/OwnerDetail';
import ReviewList from 'pages/Services/Review/ReviewList';

function RequireAuth() {
const location = useLocation();
Expand Down Expand Up @@ -56,6 +57,7 @@ function App() {
<Route path="/owner-request/:id" element={<OwnerRequestDetail />} />
<Route path="/member" element={<MemberList />} />
<Route path="/member/:id" element={<MemberDetail />} />
<Route path="/review" element={<ReviewList />} />
<Route path="*" element={<h1>404</h1>} />
</Route>
</Routes>
Expand Down
22 changes: 22 additions & 0 deletions src/components/common/ScrollUpButton/ScrollUpButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { UpCircleOutlined } from '@ant-design/icons';
import styled from 'styled-components';

const RightDownButton = styled.div`
position: fixed;
bottom: 100px;
right: 100px;
font-size: 40px;
cursor: pointer;
`;

export default function ScrollUpButton() {
const scrollUp = () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
};

return (
<RightDownButton onClick={scrollUp}>
<UpCircleOutlined />
</RightDownButton>
);
}
3 changes: 2 additions & 1 deletion src/components/common/SideNav/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
AppstoreOutlined, UserOutlined, CarOutlined, ShopOutlined,
HomeOutlined, UserSwitchOutlined,
UsergroupDeleteOutlined, FolderOpenOutlined, ControlOutlined,
UserAddOutlined, BoldOutlined,
UserAddOutlined, BoldOutlined, SnippetsOutlined,
} from '@ant-design/icons';
import { Menu, MenuProps } from 'antd';
import { Link, useLocation, useNavigate } from 'react-router-dom';
Expand Down Expand Up @@ -31,6 +31,7 @@ const items: MenuProps['items'] = [
getItem('주변상점', 'service-store', <ShopOutlined />, [
getItem('상점 관리', '/store', <ControlOutlined />),
getItem('카테고리', '/category', <FolderOpenOutlined />),
getItem('리뷰 관리', '/review', <SnippetsOutlined />),
]),
getItem('버스 정보', '/bus', <CarOutlined />),
getItem('복덕방', '/room', <HomeOutlined />),
Expand Down
2 changes: 2 additions & 0 deletions src/constant/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ export const API_PATH = process.env.REACT_APP_API_PATH;

export const SECOND_PASSWORD = process.env.REACT_APP_SECOND_PASSWORD;

export const KOIN_URL = process.env.REACT_APP_API_PATH?.includes('stage') ? 'https://stage.koreatech.in' : 'https://koreatech.in';

// 테이블 헤더 Title 매핑
export const TITLE_MAPPER: Record<string, string> = {
id: 'ID',
Expand Down
45 changes: 45 additions & 0 deletions src/model/review.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
export interface ReviewListResponse {
total_count: number,
current_count: number,
current_page: number,
reviews: ReviewContent[]
}

export interface ReviewContent {
reviewId: number,
rating: number,
nickName: string,
content: string,
imageUrls: string[],
menuNames: string[],
isModified: boolean,
isHaveUnhandledReport: boolean,
createdAt: string,
reports: ReportedReviewContent[]
shop: {
shopId: number,
shopName: string,
}
}

export interface ReportedReviewContent {
reportId: number,
title: string,
content: string,
nickName: string,
status: string,
}

export interface GetReviewListParam {
page: number;
limit: number;
isReported: boolean;
}

export interface SetReviewParam {
id: number;
page: number;
body: {
report_status: string
}
}
57 changes: 57 additions & 0 deletions src/pages/Services/Review/ReviewCard.style.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import styled from 'styled-components';

export const Shortcut = styled.a`
color: '#cacaca';
text-decoration: none;
`;

export const Container = styled.div<{ isHandle: boolean }>`
display: flex;
flex-direction: column;
padding: 10px 15px;
background: ${(props) => (props.isHandle ? '#ff000050' : '#00BFFF10')};
border-radius: 10px;
transition: scale 0.3s, height 0.3s;
width: 100%;
gap: 10px;
`;

export const Row = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
`;

export const RowItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
`;

export const Item = styled.div`
width: 150px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
`;

export const ToggleButton = styled.button`
background: transparent;
border: none;
cursor: pointer;
height: 30px;
`;

export const MenuImage = styled.img`
width: 250px;
height: 250px;
object-fit: cover;
border-radius: 10px;
`;

export const AroundRow = styled.div`
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
`;
193 changes: 193 additions & 0 deletions src/pages/Services/Review/ReviewCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import { ReviewContent } from 'model/review.model';
import { Button, message, Modal } from 'antd';
import { useState } from 'react';
import { CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons';
import { useDeleteReviewMutation, useSetReviewDismissedMutation } from 'store/api/review';
import { KOIN_URL } from 'constant';
import * as S from './ReviewCard.style';

interface Props {
review: ReviewContent;
currentPage: number;
}

export default function ReviewCard({ review, currentPage }: Props) {
const [isOpen, setIsOpen] = useState<boolean>(false);
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const [isReportOpen, setIsReportOpen] = useState<boolean>(false);
const toggle = () => {
setIsOpen((prev) => !prev);
};

const [deleteReview, {
isLoading: isDeleteLoading,
isError: isDeleteError,
}] = useDeleteReviewMutation();
const [dismissReview, {
isLoading: isDismissLoading,
isError: isDismissError,
}] = useSetReviewDismissedMutation();

if (isDeleteError) {
message.error('리뷰 삭제에 실패했습니다');
}
if (isDismissError) {
message.error('리뷰 상태 변경에 실패했습니다.');
}

const deleteSpecificReview = () => {
deleteReview({
id: review.reviewId,
page: currentPage,
});
};

const dismissSpecificReview = () => {
dismissReview({
id: review.reviewId,
page: currentPage,
body: {
report_status: 'DISMISSED',
},
});
};

return (
<S.Container isHandle={review.isHaveUnhandledReport}>
<S.Row>
<S.RowItem>
<S.Item>
{review.shop.shopName}
</S.Item>
<S.Item>
{review.createdAt}
</S.Item>
</S.RowItem>
<S.RowItem>
<S.Item>
<S.Shortcut href={`${KOIN_URL}/store/${review.shop.shopId}?state=리뷰`} target="_blank" rel="noreferrer">식당 페이지 바로가기</S.Shortcut>
</S.Item>
<S.Item>
<Button
danger
disabled={isDeleteLoading}
onClick={() => setIsModalOpen(true)}
>
삭제하기
</Button>
</S.Item>
</S.RowItem>
</S.Row>
<S.Row>
<S.RowItem>
<S.Item>
별점:
{' '}
{review.rating}
</S.Item>
{!isOpen && (
<S.Item>
{review.content}
</S.Item>
)}
</S.RowItem>
</S.Row>
{isOpen && (
<>
<S.Row>
<div>
리뷰 내용:
{' '}
{review.content}
</div>
</S.Row>
<S.Row>
<div>
사진:
{' '}
{review.imageUrls.length > 0 ? review.imageUrls.map((image) => (
<S.MenuImage
src={image}
key={image}
loading="lazy"
alt="리뷰 이미지"
/>
)) : '없음'}
</div>
</S.Row>
<S.Row>
<div>
이용 메뉴:
{' '}
{review.menuNames.length > 0 ? review.menuNames.map((menu) => <div key={menu}>{menu}</div>) : '미기재'}
</div>
</S.Row>
<S.Row>
<div>
수정이력:
{' '}
{review.isModified ? 'O' : 'X'}
</div>
</S.Row>
<S.AroundRow>
<S.Item>
{review.isHaveUnhandledReport ? '신고정보' : '신고이력'}
</S.Item>
</S.AroundRow>
{review.reports.length > 0
? (
<S.Row>
<S.Item>
<Button onClick={() => setIsReportOpen(true)}>확인하기</Button>
</S.Item>
{review.isHaveUnhandledReport
&& (
<S.Item>
<Button
onClick={dismissSpecificReview}
disabled={isDismissLoading}
>
유지하기
</Button>
</S.Item>
)}
</S.Row>

) : '없음'}
</>
)}
<S.ToggleButton type="button" onClick={toggle}>
{isOpen ? <CaretUpOutlined /> : <CaretDownOutlined />}
</S.ToggleButton>
<Modal open={isModalOpen} footer={null} onCancel={() => setIsModalOpen(false)}>
<S.AroundRow>
정말로 삭제하시겠습니까?
<S.Item>
<Button
danger
onClick={() => {
deleteSpecificReview();
setIsModalOpen(false);
}}
>
삭제
</Button>
<Button onClick={() => setIsModalOpen(false)}>취소</Button>
</S.Item>
</S.AroundRow>
</Modal>
<Modal open={isReportOpen} onCancel={() => setIsReportOpen(false)} footer={null}>
{review.reports.map((report, idx) => (
<S.Row
key={report.reportId}
>
{idx + 1}
.
{' '}
{report.content}
</S.Row>
))}
</Modal>
</S.Container>
);
}
26 changes: 26 additions & 0 deletions src/pages/Services/Review/ReviewList.style.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import styled from 'styled-components';

export const Container = styled.div`
height: 100vh;
min-width: 1000px;
display: flex;
flex-direction: column;
box-sizing: border-box;
gap: 30px;
position: relative;
`;

export const Filter = styled.div`
display: flex;
align-items: center;
gap: 15px;
`;

export const DataContainer = styled.div`
display: flex;
align-items: center;
flex-direction: column;
gap: 15px;
width: 100%;
margin-bottom: 30px;
`;
Loading

0 comments on commit e87caa5

Please sign in to comment.