diff --git a/components/common/FacilitiesIcons.tsx b/components/common/FacilitiesIcons.tsx new file mode 100644 index 0000000..30027f6 --- /dev/null +++ b/components/common/FacilitiesIcons.tsx @@ -0,0 +1,63 @@ +import Image from 'next/image' +import styled from '@emotion/styled' +import { PlaceInfoType } from '../../types' + +type FacilitiesIconsType = { + place: PlaceInfoType + size: number + hasDescription?: boolean +} + +type IconImageType = { + src: string + description: string +} + +// 시설 정보 아이콘 뷰 +const FacilitiesIcons = ({ + place, + size, + hasDescription = false, +}: FacilitiesIconsType) => { + const IconImage = ({ src, description }: IconImageType) => ( + + {description} + {hasDescription && {description}} + + ) + + return ( + + {place.isToiletExists && ( + + )} + {place.isChargerExists && ( + + )} + {place.isElevatorExists && ( + + )} + {place.isSlopeExists && ( + + )} + + ) +} + +const IconsSection = styled.section` + display: flex; + margin-top: 0.5rem; + justify-content: flex-end; +` +const IconWrapper = styled.div<{ hasDescription: boolean }>` + margin: 0 ${({ hasDescription }) => (hasDescription ? 0.4 : 0.1)}rem; + display: flex; + flex-direction: column; + align-items: center; +` +const DescriptionText = styled.div` + margin-top: 0.4rem; + font-size: 0.7rem; +` + +export default FacilitiesIcons diff --git a/components/common/PlaceInfo.tsx b/components/common/PlaceInfo.tsx index 964f3d1..bb48a4a 100644 --- a/components/common/PlaceInfo.tsx +++ b/components/common/PlaceInfo.tsx @@ -1,17 +1,11 @@ -import Image from 'next/image' import Link from 'next/link' import styled from '@emotion/styled' import { PlaceInfoType } from '../../types' import { conceptColors } from './BaseLayout' import Text from './Text' +import FacilitiesIcons from './FacilitiesIcons' const PlaceInfo = ({ place }: { place: PlaceInfoType }) => { - const IconImage = ({ src }: { src: string }) => ( - - - - ) - return ( @@ -29,12 +23,7 @@ const PlaceInfo = ({ place }: { place: PlaceInfoType }) => { {place.address} - - {place.isToiletExists && } - {place.isChargerExists && } - {place.isElevatorExists && } - {place.isSlopeExists && } - + ) @@ -42,7 +31,8 @@ const PlaceInfo = ({ place }: { place: PlaceInfoType }) => { const PlaceInfoContainer = styled.div` margin: 0.2rem; - div { + div, + p { margin-bottom: 0.2rem; } ` @@ -55,17 +45,11 @@ const NameTypeSection = styled.section` display: flex; flex-wrap: wrap; align-items: center; - div:last-child { - margin-left: 0.4rem; + p:first-of-type { + margin-right: 0.4rem; } ` -const IconsSection = styled.section` - display: flex; - margin-top: 0.5rem; - justify-content: flex-end; -` -const IconWrapper = styled.div` - margin: 0 0.1rem; -` + +export { NameTypeSection } export default PlaceInfo diff --git a/components/common/Text.tsx b/components/common/Text.tsx index 4c0b95c..bd69844 100644 --- a/components/common/Text.tsx +++ b/components/common/Text.tsx @@ -16,10 +16,12 @@ const Text = ({ children, color, size = 1, bold }: TextType) => { ) } -const StyledText = styled.div` +export const StyledText = styled.p` color: ${({ color }) => color}; font-size: ${({ size }) => size}rem; font-weight: ${({ bold }) => bold && 'bold'}; + margin-block-start: 0; + margin-block-end: 0; ` export default Text diff --git a/components/common/ToggleView.tsx b/components/common/ToggleView.tsx new file mode 100644 index 0000000..244dcac --- /dev/null +++ b/components/common/ToggleView.tsx @@ -0,0 +1,54 @@ +import React, { useState } from 'react' +import { styled } from '@mui/material/styles' +import MuiAccordion, { AccordionProps } from '@mui/material/Accordion' +import AccordionDetails from '@mui/material/AccordionDetails' +import MuiAccordionSummary, { + AccordionSummaryProps, +} from '@mui/material/AccordionSummary' +import ExpandMoreIcon from '@mui/icons-material/ExpandMore' + +type ToggleViewType = { + children: React.ReactNode + title: string +} + +const ToggleView = ({ children, title }: ToggleViewType) => { + const [isExpanded, setIsExpanded] = useState(false) + + return ( + setIsExpanded(!isExpanded)} + > + }> + {title} + + {children} + + ) +} + +const Accordion = styled((props: AccordionProps) => ( + +))(() => ({ + border: 'none', +})) + +const AccordionSummary = styled((props: AccordionSummaryProps) => ( + +))(() => ({ + flexDirection: 'row-reverse', + minHeight: '0px', + '& .MuiAccordionSummary-expandIconWrapper': { + transform: 'rotate(-90deg)', + }, + '& .MuiAccordionSummary-expandIconWrapper.Mui-expanded': { + transform: 'rotate(0deg)', + }, + '& .MuiAccordionSummary-content': { + margin: '0.4rem', + flexGrow: 'inherit', + }, +})) + +export default ToggleView diff --git a/components/detail/ChargerInfoToggle.tsx b/components/detail/ChargerInfoToggle.tsx new file mode 100644 index 0000000..4d01490 --- /dev/null +++ b/components/detail/ChargerInfoToggle.tsx @@ -0,0 +1,74 @@ +import styled from '@emotion/styled' +import { chargerDetail } from '../../data' +import Text from '../common/Text' +import ToggleView from '../common/ToggleView' + +const ChargerInfoToggle = () => { + const charger = chargerDetail.data + + type ChargerInfoType = { + title: string + data: string + } + + const ChargerInfoList: ChargerInfoType[] = [ + { + title: '설치 장소', + data: charger.instllcdesc, + }, + { + title: '공기 주입 가능', + data: charger.airinjectoryn, + }, + { + title: '휴대전화 충전', + data: charger.moblphonchrstnyn, + }, + { title: '동시 사용 가능 대수', data: charger.smtmuseco }, + { + title: '평일 운영 시간', + data: `${charger.weekdayoperopenhhmm} ~ ${charger.weekdayopercolsehhmm}`, + }, + { + title: '토요일 운영 시간', + data: `${charger.satoperoperopenhhmm} ~ ${charger.satoperclosehhmm}`, + }, + { + title: '공휴일 운영 시간', + data: `${charger.holidayoperopenhhmm} ~ ${charger.holidaycloseopenhhmm}`, + }, + { title: '시설명', data: charger.fcltynm }, + { title: '관리기관명', data: charger.institutionnm }, + { title: '관리기관 번호', data: charger.institutionphonenumber }, + { title: '데이터 기준 일자', data: charger.referencedate }, + ] + + return ( + + + {ChargerInfoList.map((chargerInfo: ChargerInfoType, index: number) => ( + + {chargerInfo.title} + {chargerInfo.data} + + ))} + + + ) +} + +const ChargerInfoToggleSection = styled.section` + display: flex; + justify-content: center; + margin: 1rem 0; +` +const ChargerInfoWrapper = styled.div` + display: flex; + justify-content: space-between; + margin: 0.1rem; +` +const ChargerDataText = styled.div` + margin-left: 3rem; +` + +export default ChargerInfoToggle diff --git a/components/detail/index.tsx b/components/detail/index.tsx new file mode 100644 index 0000000..4f8bf35 --- /dev/null +++ b/components/detail/index.tsx @@ -0,0 +1,64 @@ +import Link from 'next/link' +import styled from '@emotion/styled' +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' + +const Detail = () => { + const place: PlaceInfoType = placeDetail.response + // TODO: 동적 id와 실데이터 연결 + + return ( + + ◀ 뒤로 가기 + {/* TODO: 모바일 환경에서는 /list (리스트로 가기) */} + + + + {place.name} + + + {place.locationType} + + + + {place.address} + + {reviewAverageCount && ( + + {reviewAverageCount.average} + + (리뷰 {reviewAverageCount.count} + 개) + + + )} + + + + {/* */} + + ) +} + +const DetailContainer = styled.article` + margin-top: 0.4rem; +` +const DetailInfoSection = styled.section` + display: flex; + flex-direction: column; + align-items: center; + margin: 0.4rem 0; +` +const AddressText = styled(StyledText)` + margin-top: 0.4rem; +` +const ReviewAverageCountSection = styled.section` + display: flex; + margin: 1rem 0; +` + +export default Detail diff --git a/data.ts b/data.ts index f7efed8..8a3906b 100644 --- a/data.ts +++ b/data.ts @@ -5,7 +5,7 @@ export const places = { nextUrl: '?page=2', results: [ { - _id: '123', + _id: 123, locationType: '음식점', name: '몬플몬플 숙대점', address: '서울특별시 용산구 청파동 청파로', @@ -19,7 +19,7 @@ export const places = { googlePlaceId: 'ChIJN2x0fu2ifDUR51BupseGYmE', }, { - _id: '124', + _id: 124, locationType: '음식점', name: '육쌈냉면 숙대점', address: '서울특별시 용산구 청파동 청파로', @@ -33,7 +33,7 @@ export const places = { googlePlaceId: 'ChIJN2x0fu2ifDUR51BupseGYmE', }, { - _id: '125', + _id: 125, locationType: '음식점', name: '코피티암 숙대점', address: '서울특별시 용산구 청파동 청파로', @@ -48,3 +48,51 @@ export const places = { }, ], } + +// 장소 디테일 +export const placeDetail = { + message: 'Retrieve Location By Id Success', + response: { + _id: 1, + locationType: 'subway', + name: '국립중앙박물관', + address: '137 Seobinggo-ro', + latitude: 37.5238506, + longitude: 126.9804702, + distance: 1.5, + isToiletExists: true, + isChargerExists: true, + isElevatorExists: true, + isSlopeExists: true, + googlePlaceId: 'ChIJN2x0fu2ifDUR51BupseGYmE', + }, +} + +// 충전기 디테일 +export const chargerDetail = { + message: 'get charger info success', + data: { + locationId: 12, + airinjectoryn: 'Y', + moblphonchrstnyn: 'Y', + smtmuseco: '2', + weekdayoperopenhhmm: '09:00', + weekdayopercolsehhmm: '18:00', + satoperoperopenhhmm: '00:00', + satoperclosehhmm: '00:00', + holidayoperopenhhmm: '00:00', + holidaycloseopenhhmm: '00:00', + fcltynm: '용산행복장애인자립생활센터', + institutionnm: '용산행복장애인자립생활센터', + institutionphonenumber: '02-704-0420', + instllcdesc: '센터 1층', + work_dttm: '1646631453000', + referencedate: '2021-09-15', + }, +} + +// 리뷰 평균 및 개수 +export const reviewAverageCount = { + count: 5, + average: 4.5, +} diff --git a/package.json b/package.json index 4fffd0e..0c85500 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "@emotion/react": "^11.9.3", "@emotion/styled": "^11.9.3", + "@mui/icons-material": "^5.10.9", "@mui/material": "^5.9.2", "@react-google-maps/api": "^2.12.1", "axios": "^0.27.2", diff --git a/pages/detail/[id].tsx b/pages/detail/[id].tsx new file mode 100644 index 0000000..984fd83 --- /dev/null +++ b/pages/detail/[id].tsx @@ -0,0 +1,13 @@ +import type { NextPage } from 'next' +import BaseLayout from '../../components/common/BaseLayout' +import Detail from '../../components/detail' + +const DetailPage: NextPage = () => { + return ( + + + + ) +} + +export default DetailPage diff --git a/pages/list.tsx b/pages/list.tsx index 55bdd1b..0efdcb3 100644 --- a/pages/list.tsx +++ b/pages/list.tsx @@ -12,8 +12,8 @@ const ListPage: NextPage = () => {
View Map
{places?.results?.map((place) => ( - - + + ))} diff --git a/types/index.ts b/types/index.ts index 3652704..ac02fe6 100644 --- a/types/index.ts +++ b/types/index.ts @@ -1,6 +1,6 @@ // 장소 리스트 export interface PlaceInfoType { - _id: string + _id: number locationType: string name: string address: string @@ -20,3 +20,40 @@ export interface PlaceListType { nextUrl: string results: PlaceInfoType[] } + +// 장소 디테일 +export interface PlaceDetailType { + message: string + response: PlaceInfoType +} + +// 충전기 디테일 +export interface chargerDataType { + locationId: number + airinjectoryn: string + moblphonchrstnyn: string + smtmuseco: string + weekdayoperopenhhmm: string + weekdayopercolsehhmm: string + satoperoperopenhhmm: string + satoperclosehhmm: string + holidayoperopenhhmm: string + holidaycloseopenhhmm: string + fcltynm: string + institutionnm: string + institutionphonenumber: string + instllcdesc: string + work_dttm: string + referencedate: string +} + +export interface chargerDetailType { + message: string + data: chargerDataType +} + +// 리뷰 평균 및 개수 +export interface ReviewAverageCountType { + count: number + average: number +} diff --git a/yarn.lock b/yarn.lock index d6905ed..2a1f23a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -57,6 +57,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.19.0": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9" + integrity sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg== + dependencies: + regenerator-runtime "^0.13.10" + "@babel/types@^7.18.6": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.9.tgz#7148d64ba133d8d73a41b3172ac4b83a1452205f" @@ -224,6 +231,13 @@ prop-types "^15.8.1" react-is "^18.2.0" +"@mui/icons-material@^5.10.9": + version "5.10.9" + resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.10.9.tgz#f9522c49797caf30146acc576e37ecb4f95bbc38" + integrity sha512-sqClXdEM39WKQJOQ0ZCPTptaZgqwibhj2EFV9N0v7BU1PO8y4OcX/a2wIQHn4fNuDjIZktJIBrmU23h7aqlGgg== + dependencies: + "@babel/runtime" "^7.19.0" + "@mui/material@^5.9.2": version "5.9.2" resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.9.2.tgz#2526c33568b4c785b0784b0714a8f613cd9dcd46" @@ -1962,6 +1976,11 @@ react@17.0.0: loose-envify "^1.1.0" object-assign "^4.1.1" +regenerator-runtime@^0.13.10: + version "0.13.10" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz#ed07b19616bcbec5da6274ebc75ae95634bfc2ee" + integrity sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw== + regenerator-runtime@^0.13.4: version "0.13.9" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"