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

Refactor/detail/add review #14

Merged
merged 13 commits into from
Dec 23, 2022
60 changes: 60 additions & 0 deletions components/detail/ReviewContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import styled from '@emotion/styled'
import { useState } from 'react'
import { reviewAverageCount } from '../../data'
import { ReviewSortType } from '../../types/state'
import Text from '../common/Text'
import ReviewInput from './ReviewInput'
import ReviewList from './ReviewList'

// TODO: set location id
const ReviewContainer = () => {
const [sort, setSort] = useState<ReviewSortType>('recommended')

return (
<>
<ReviewContainerHeader>
<Text size={1.2} bold>
{reviewAverageCount.count} 개의 리뷰
</Text>
<ReviewSortContainer>
<SelectSort
sort={sort}
name={'recommended'}
onClick={() => setSort('recommended')}
>
추천순
</SelectSort>
<SelectSort
sort={sort}
name={'createdAt'}
onClick={() => setSort('createdAt')}
>
최신순
</SelectSort>
</ReviewSortContainer>
</ReviewContainerHeader>
<ReviewInput />
<ReviewList />
</>
)
}

const ReviewContainerHeader = styled.header`
display: flex;
justify-content: space-around;
align-items: flex-end;
flex-direction: row;
`
const SelectSort = styled.div<{ sort: string; name: string }>`
font-weight: ${({ sort, name }) => (sort === name ? 'bold' : 'normal')};
padding: 0 0.5rem;
cursor: pointer;
`
const ReviewSortContainer = styled.div`
display: flex;
${SelectSort}:first-of-type {
border-right: 1px solid black;
}
`

export default ReviewContainer
113 changes: 113 additions & 0 deletions components/detail/ReviewInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import styled from '@emotion/styled'
import React, { useState, useEffect } from 'react'
import Text, { StyledText } from '../common/Text'

import Radio from '@mui/material/Radio'
import RadioGroup from '@mui/material/RadioGroup'
import FormControlLabel from '@mui/material/FormControlLabel'
import FormControl from '@mui/material/FormControl'
import Rating from '@mui/material/Rating'
import { ReviewUserType } from '../../types/state'

const ReviewInput = () => {
const [userType, setUserType] = useState<ReviewUserType>('anonymous')
const [inputValue, setInputValue] = useState<string>('')
const [rate, setRate] = useState<number | null>(0)

return (
<ReviewInputContainer>
<ReviewInputHeader>
<UserInfo>
<Text>평점</Text>
<Rating
value={rate}
precision={0.5}
onChange={(_, newRate) => {
setRate(newRate)
}}
/>
</UserInfo>
<FormControl>
<RadioGroup
row
value={userType}
onChange={(e) => setUserType(e.target.value as ReviewUserType)}
>
<FormControlLabel
value="disabled"
control={<Radio size="small" />}
label="장애인"
/>
<FormControlLabel
value="able"
control={<Radio size="small" />}
label="비장애인"
/>
<FormControlLabel
value="anonymous"
control={<Radio size="small" />}
label="익명"
/>
</RadioGroup>
</FormControl>
</ReviewInputHeader>

<ReviewInputArea
onChange={(e) => setInputValue(e.target.value)}
placeholder="리뷰는 익명으로 등록됩니다."
value={inputValue}
/>
<ReviewInputButton
onClick={() => {
// postReview(userType, locationId, inputValue, rating)
setInputValue('')
setRate(0)
}}
>
등록
</ReviewInputButton>
</ReviewInputContainer>
)
}

const ReviewInputContainer = styled.section`
display: flex;
flex-direction: column;
margin-top: 1rem;
`
const ReviewInputHeader = styled.div`
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
`
const UserInfo = styled.div`
display: flex;
align-items: center;
justify-content: center;
${StyledText} {
margin-right: 0.2rem;
}
`
const ReviewInputArea = styled.textarea`
border: 2px solid #9d9d9d;
border-radius: 10px;
font-size: 1rem;
min-height: 5rem;
padding: 0.3rem;
margin-top: 0.4rem;
resize: none;
`
const ReviewInputButton = styled.button`
display: flex;
background-color: #e599b3;
color: white;
font-size: 0.9rem;
margin-top: 0.6rem;
padding: 0.4rem;
justify-content: center;
align-items: center;
border-radius: 5px;
`

export default ReviewInput
112 changes: 112 additions & 0 deletions components/detail/ReviewList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import styled from '@emotion/styled'
import Image from 'next/image'
import { reviews } from '../../data'
import Text, { StyledText } from '../common/Text'

import Rating from '@mui/material/Rating'

const ReviewList = () => {
return (
<ReviewListContainer>
{reviews?.data?.map((review) => (
<ReviewContainer key={review._id}>
<ReviewInfoWrapper>
<ReviewStarUserTypeWrapper>
<Rating
value={review.star}
precision={0.5}
size="small"
readOnly
/>
<Text color="#f0a044" size={0.9}>
{review.userType}
</Text>
</ReviewStarUserTypeWrapper>
<Text color="#9d9d9d" size={0.7}>
{review.createdAt.substring(0, 10)}
</Text>
</ReviewInfoWrapper>
<ReviewText>{review.detail}</ReviewText>
<HelpButtonWrapper>
<HelpButton
// onClick={() => {
// postReviewRecommend(review._id)
// }}
>
<Image
src={'/images/thumbs_up.svg'}
width={15}
height={15}
></Image>
<ButtonDescriptionText>도움이 돼요</ButtonDescriptionText>
<Text color="#cb3267" size={1} bold>
{review.good}
</Text>
</HelpButton>
<HelpButton
// onClick={() => postReviewDiscourage(review._id)}
>
<Image
src={'/images/thumbs_down.svg'}
width={15}
height={15}
></Image>
<ButtonDescriptionText>도움이 안 돼요</ButtonDescriptionText>
<Text color="#cb3267" size={1} bold>
{review.bad}
</Text>
</HelpButton>
</HelpButtonWrapper>
</ReviewContainer>
))}
</ReviewListContainer>
)
}

const ReviewListContainer = styled.section``
const ReviewContainer = styled.div`
margin-top: 1rem;
padding-bottom: 1rem;
border-bottom: #9d9d9d;
border-bottom-width: 0.08rem;
border-bottom-style: solid;
`
const ReviewInfoWrapper = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
`
const ReviewStarUserTypeWrapper = styled.div`
display: flex;
align-items: center;
${StyledText} {
margin-left: 0.4rem;
}
`
const ReviewText = styled.p`
font-size: 1rem;
align-items: center;
margin-left: 0.5rem;
margin-top: 0.5rem;
`
const HelpButtonWrapper = styled.div`
display: flex;
justify-content: space-between;
`
const HelpButton = styled.button`
display: flex;
font-weight: bold;
font-size: 0.8rem;
height: 1.7rem;
align-items: center;
border: 1px solid #f0a044;
border-radius: 10px;
background-color: #ffffff;
padding-left: 1rem;
padding-right: 1rem;
`
const ButtonDescriptionText = styled.p`
margin: 0 0.5rem 0 0.2rem;
`

export default ReviewList
26 changes: 20 additions & 6 deletions components/detail/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import styled from '@emotion/styled'
import { Dispatch, SetStateAction } from 'react'
import { useRouter } from 'next/router'
import { placeDetail, reviewAverageCount } from '../../data'
import { PlaceInfoType } from '../../types'
import FacilitiesIcons from '../common/FacilitiesIcons'
import { NameTypeSection } from '../common/PlaceInfo'
import Text, { StyledText } from '../common/Text'
import ChargerInfoToggle from './ChargerInfoToggle'
import { Dispatch, SetStateAction } from 'react'
import { useRouter } from 'next/router'
import ReviewContainer from './ReviewContainer'

import Rating from '@mui/material/Rating'

type DetailPropsType = {
setIsDetailOpen?: Dispatch<SetStateAction<boolean>>
}

const Detail = ({ setIsDetailOpen }: DetailPropsType) => {
const place: PlaceInfoType = placeDetail.response
// TODO: 동적 id와 실데이터 연결
const router = useRouter()

return (
<DetailContainer>
<button onClick={() => (setIsDetailOpen ? setIsDetailOpen(false) : router.push('/'))}>
Expand All @@ -35,17 +40,22 @@ const Detail = ({ setIsDetailOpen }: DetailPropsType) => {
</AddressText>
{reviewAverageCount && (
<ReviewAverageCountSection>
{reviewAverageCount.average}
<Rating
value={reviewAverageCount.average}
precision={0.1}
size="small"
readOnly
/>
<Text size={0.8}>({reviewAverageCount.average})</Text>
<Text>
(리뷰 <b>{reviewAverageCount.count}</b>
개)
리뷰 <b>{reviewAverageCount.count}</b>
</Text>
</ReviewAverageCountSection>
)}
<FacilitiesIcons place={place} size={50} hasDescription />
</DetailInfoSection>
<ChargerInfoToggle />
{/* <ReviewPage locationId={id} /> */}
<ReviewContainer />
</DetailContainer>
)
}
Expand All @@ -65,6 +75,10 @@ const AddressText = styled(StyledText)`
const ReviewAverageCountSection = styled.section`
display: flex;
margin: 1rem 0;
align-items: center;
${StyledText}:last-of-type {
margin-left: 0.6rem;
}
`

export default Detail
31 changes: 31 additions & 0 deletions data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,34 @@ export const reviewAverageCount = {
count: 5,
average: 4.5,
}

// 리뷰 리스트
export const reviews = {
message: 'List Review of location number 1 Success',
data: [
{
_id: '62343510c3dd5a345c6431d4',
locationId: 1,
userId: null,
userType: 'anonymous',
good: 1,
bad: 2,
detail:
'매장이 넓고 경사로가 있어서 이용하기 편리했어요 카페 내부도 예뻐요! 입구는 하나라 경사로 찾기 쉬울 것 같아요',
star: 4.5,
createdAt: '2022-03-18T09:40:53.077Z',
},
{
_id: '62343510c3dd5a345c6431d5',
locationId: 2,
userId: null,
userType: 'anonymous',
good: 1,
bad: 3,
detail:
'매장이 넓고 경사로가 있어서 이용하기 편리했어요 카페 내부도 예뻐요!',
star: 4.2,
createdAt: '2022-03-19T09:40:53.077Z',
},
],
}
3 changes: 3 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
compiler: {
emotion: true,
},
}

module.exports = nextConfig
Loading