Skip to content

Commit

Permalink
Merge pull request #95 from BCSDLab/feature/#94
Browse files Browse the repository at this point in the history
[DETAIL] 메인페이지 주소 변경 UI 구현
  • Loading branch information
hyejun0228 authored Sep 4, 2023
2 parents 8d4a851 + 555be6d commit ddc767b
Show file tree
Hide file tree
Showing 16 changed files with 16,357 additions and 6 deletions.
15,732 changes: 15,732 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"axios": "^0.27.2",
"http-proxy-middleware": "1",
"jotai": "^1.11.0",
"qs": "^6.11.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.36.1",
"react-naver-maps": "^0.1.2",
"react-query": "^3.39.1",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
Expand Down Expand Up @@ -71,5 +73,5 @@
"stylelint-config-standard": "^26.0.0",
"stylelint-config-standard-scss": "^4.0.0"
},
"proxy": "https://nid.naver.com"
"proxy": "https://naveropenapi.apigw.ntruss.com"
}
25 changes: 25 additions & 0 deletions src/api/location/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import axios from 'axios';
import { NAVER_MAPS_CLIENT_ID, NAVER_MAP_CLOUD_SECRET_ID } from 'config/constants';

const getAddress = async (latitude: number, longitude: number): Promise<string> => {
const response = await axios.get('/map-reversegeocode/v2/gc', {
params: {
coords: `${longitude},${latitude}`,
sourcecrs: 'epsg:4326',
orders: 'roadaddr',
output: 'json',
},
headers: {
'X-NCP-APIGW-API-KEY-ID': NAVER_MAPS_CLIENT_ID,
'X-NCP-APIGW-API-KEY': NAVER_MAP_CLOUD_SECRET_ID,
},
});

const { data } = response;
if (data?.results?.length > 0) {
return `${data.results[0].region.area1.name} ${data.results[0].region.area2.name} ${data.results[0].region.area3.name} ${data.results[0].land.name} ${data.results[0].land.number1}`;
}
return '주소 정보 없음';
};

export default getAddress;
3 changes: 3 additions & 0 deletions src/assets/svg/home/arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/assets/svg/home/point.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 src/assets/svg/home/search.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const SERVER_LOGIN_REDIRECT_URL = checkEnvVar('REACT_APP_SERVER_LOGIN_RED

// 네이버 지도
export const NAVER_MAPS_CLIENT_ID = checkEnvVar('REACT_APP_NAVER_MAPS_CLIENT_ID');
export const NAVER_MAP_CLOUD_SECRET_ID = checkEnvVar('REACT_APP_NAVER_CLOUD_MAPS_CLIENT_SECRET_ID');

// 구글 OAuth
export const GOOGLE_CLIENT_ID = checkEnvVar('REACT_APP_GOOGLE_CLIENT_ID');
Expand Down
16 changes: 16 additions & 0 deletions src/pages/Home/Home.module.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
.home {
width: 100%;
}

.map {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}

.locationBox {
position: absolute;
top: 0;
left: 0;
z-index: 3;
}
37 changes: 37 additions & 0 deletions src/pages/Home/components/LocationInfo/LocationInfo.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@use "src/utils/styles/mediaQuery.scss" as media;

.map-container {
display: none;

@include media.media-breakpoint-up(mobile) {
position: absolute;
right: 24px;
top: 24px;
height: 57px;
border-radius: 100px;
border: none;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 20px;
font-weight: 500;
line-height: 24.96px;
color: #222222;
z-index: 2;
backdrop-filter: blur(2px);
background-color: #ffffff99;
box-shadow: 1px 1px 10px 1px #0000001a;
cursor: pointer;
}

&__text {
display: flex;
margin-left: 24px;
}

&__image {
display: flex;
margin-right: 24px;
margin-left: 10px;
}
}
24 changes: 24 additions & 0 deletions src/pages/Home/components/LocationInfo/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ReactComponent as Arrow } from 'assets/svg/home/arrow.svg';
import styles from './LocationInfo.module.scss';

interface LocationInfoProps {
address: string | null;
onClick: () => void;
}

export default function LocationInfo({ address, onClick }: LocationInfoProps): JSX.Element {
return (
<button
type="button"
className={styles['map-container']}
onClick={onClick}
>
{address ? (
<div className={styles['map-container__text']}>{address}</div>
) : (
<div className={styles['map-container__text']}>위치 정보를 가져오는 중...</div>
)}
<Arrow className={styles['map-container__image']} />
</button>
);
}
111 changes: 111 additions & 0 deletions src/pages/Home/components/Map/components/Location/Location.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
.container {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
width: 617px;
height: 749px;
flex-direction: column;
border-radius: 15px;
backdrop-filter: blur(2px);
background-color: #ffffffcc;
z-index: 3;
}

.box {
display: flex;
flex-direction: column;
align-items: center;

&__title {
display: flex;
color: #ff7f23;
font-size: 28px;
font-weight: 700;
margin: 68px auto 12px;
}

&__subTitle {
display: flex;
font-size: 16px;
font-weight: 400;
}
}

.search {
display: flex;
align-items: baseline;
margin-left: 40px;

&__box {
display: flex;
width: 426px;
height: 46px;
border-radius: 20px;
background-color: white;
box-shadow: 5px 5px 15px 1px #0000001a;
border: none;
margin-top: 48px;
}

&__image {
display: flex;
margin: auto 20px;
}

&__text {
display: flex;
border: none;
width: 350px;
font-size: 18px;
font-weight: 500;
}

&__button {
display: flex;
border: none;
background-color: #ff7f23;
color: white;
border-radius: 100px;
width: 72px;
height: 46px;
justify-content: center;
align-items: center;
font-weight: 500;
font-size: 18px;
margin-left: 39px;
box-shadow: 5px 5px 15px 1px #0000001a;
}
}

.location {
display: flex;
border: none;
margin-top: 8px;
font-size: 16px;
font-weight: 400;
margin-left: 32px;
border-bottom: 1px solid #c4c4c4;
width: 510px;
padding: 10px;
cursor: pointer;

&__active {
display: flex;
align-items: center;
border-radius: 50px;
padding: 3px 25px 3px 10px;

&:hover {
display: flex;
background-color: #eeeeee;
}
}

&__image {
display: flex;
margin: 8px;
margin-left: 17px;
}
}
28 changes: 28 additions & 0 deletions src/pages/Home/components/Map/components/Location/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { ReactComponent as Search } from 'assets/svg/home/search.svg';
import { ReactComponent as Point } from 'assets/svg/home/point.svg';
import styles from './Location.module.scss';

export default function Location(): JSX.Element {
return (
<div className={styles.container}>
<div className={styles.box}>
<div className={styles.box__title}>현재 위치가 올바르지 않은가요?</div>
<div className={styles.box__subTitle}>현재 계신 곳의 위치를 아래 검색창을 통해</div>
<div className={styles.box__subTitle}>알려주시면 반영하겠습니다.</div>
</div>
<div className={styles.search}>
<div className={styles.search__box}>
<Search className={styles.search__image} />
<input type="text" className={styles.search__text} placeholder="지번, 도로명, 건물명으로 검색해주세요." />
</div>
<button className={styles.search__button} type="button">등록</button>
</div>
<div className={styles.location}>
<div className={styles.location__active}>
<Point className={styles.location__image} />
현재 위치로 설정
</div>
</div>
</div>
);
}
56 changes: 56 additions & 0 deletions src/pages/Home/components/Map/hooks/useHome.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useEffect, useRef, useState } from 'react';
import useBooleanState from 'utils/hooks/useBooleanState';
import getAddress from 'api/location';
import makeToast from 'utils/ts/makeToast';

export default function useHome() {
const [isClickLocation, active, unactive] = useBooleanState(false);
const locationRef = useRef<HTMLDivElement | null>(null);
const [userLocation, setUserLocation] = useState({
latitude: null as number | null,
longitude: null as number | null,
address: null as string | null,
});

useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (locationRef.current && !locationRef.current.contains(event.target as Node)) {
unactive();
}
}
document.addEventListener('mouseup', handleClickOutside);
return () => {
document.removeEventListener('mouseup', handleClickOutside);
};
}, [unactive]);

const updateUserLocation = () => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(async (position) => {
const addressData = await getAddress(
position.coords.latitude,
position.coords.longitude,
);
setUserLocation((prevUserLocation) => ({
...prevUserLocation,
latitude: position.coords.latitude,
longitude: position.coords.longitude,
address: addressData,
}));
}, (error) => {
makeToast('error', error.message);
});
}
};

useEffect(() => {
updateUserLocation();
}, []);

return {
isClickLocation,
active,
locationRef,
userLocation,
};
}
2 changes: 1 addition & 1 deletion src/pages/Home/components/Map/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import useMarker from './hooks/useMarker';
import useFilterShops from './hooks/useFilterShops';
import Pin from '../Pin';

export default function Map(): JSX.Element {
export default function NaverMap(): JSX.Element {
const { isMobile } = useMediaQuery();
const { location } = useLocation();
const map = useNaverMap(location?.latitude, location?.longitude);
Expand Down
24 changes: 22 additions & 2 deletions src/pages/Home/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
import Map from './components/Map';
import { Container as MapDiv } from 'react-naver-maps';
import useHome from './components/Map/hooks/useHome';
import Location from './components/Map/components/Location/index';
import NaverMap from './components/Map';
import styles from './Home.module.scss';
import LocationInfo from './components/LocationInfo';

export default function Home(): JSX.Element {
const {
isClickLocation, active, userLocation, locationRef,
} = useHome();
return (
<div className={styles.home}>
<Map />
<LocationInfo address={userLocation.address} onClick={active} />
<div className={styles.map}>
<MapDiv>
<NaverMap />
</MapDiv>
</div>
{isClickLocation && (
<div
className={styles.locationBox}
ref={locationRef}
>
<Location />
</div>
)}
</div>
);
}
Loading

0 comments on commit ddc767b

Please sign in to comment.