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

[JR-787] 술지도 페이지 구현 #155

Merged
merged 4 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
46 changes: 46 additions & 0 deletions .pnp.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apps/jurumarble/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"react-is": "^18.2.0",
"react-kakao-maps-sdk": "^1.1.21",
"react-toastify": "^9.1.3",
"styled-components": "^5.3.6",
"styled-reset": "^4.5.1"
Expand Down
Binary file added apps/jurumarble/public/favicon.ico
Binary file not shown.
13 changes: 13 additions & 0 deletions apps/jurumarble/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import type { Metadata } from "next";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import { injectStyle } from "react-toastify/dist/inject-style";
import { KAKAO_MAP_API_KEY } from "lib/constants";
import Script from "next/script";

export const metadata: Metadata = {
title: "주루마블",
Expand All @@ -16,6 +18,12 @@ if (typeof window !== "undefined") {
injectStyle();
}

declare global {
interface Window {
kakao: any;
}
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="kr">
Expand All @@ -33,6 +41,11 @@ export default function RootLayout({ children }: { children: React.ReactNode })
</StyledComponents>
</ReactQueryProvider>
</body>
<Script src="https://developers.kakao.com/sdk/js/kakao.js" async />
<Script
type="text/javascript"
src={`//dapi.kakao.com/v2/maps/sdk.js?appkey=${KAKAO_MAP_API_KEY}&libraries=services&autoload=false`}
></Script>
</html>
);
}
199 changes: 199 additions & 0 deletions apps/jurumarble/src/app/map/components/MapContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
"use client";

import { Button } from "components/button";
import React, { useEffect, useRef, useState } from "react";
import styled from "styled-components";
import useDrinksMapService from "../services/useDrinksMapService";
import { Map, MapMarker } from "react-kakao-maps-sdk";
import { useToggle } from "@react-hookz/web";
import RegionBottomSheet from "./RegionBottomsheet";
import DrinkItem from "app/stemp/components/DrinkItem";
import { ExImg1 } from "public/images";
import RegionSmallSelect from "./RegionSmallSelect";
import { DRINK_INFO_SORT_LIST } from "lib/constants";

const MapContainer = () => {
const [on, toggle] = useToggle();
const mapRef = useRef<kakao.maps.Map>(null);
const [mapXY, setMapXY] = useState({
startX: 33.64225953272826,
startY: 119.06076029979886,
endX: 40.856225138838,
endY: 132.02500466772065,
});

const { drinksList } = useDrinksMapService({
startX: mapXY.startX,
startY: mapXY.startY,
endX: mapXY.endX,
endY: mapXY.endY,
page: 0,
size: 100,
});
const [sortBy, setSortBy] = useState("ByPopularity");

const onChangeDrinkInfoSortOption = (sortOption: string) => {
setSortBy(sortOption);
};

const [state, setState] = useState({
// 지도의 초기 위치 서울 시청
center: { lat: 36.75158343784174, lng: 127.90408370095267 },
// 지도 위치 변경시 panto를 이용할지에 대해서 정의
isPanto: false,
level: 13,
});

useEffect(() => {
// 윈도우 리사이즈 실행
setTimeout(() => {
window.dispatchEvent(new Event("resize"));
}, 1000);
}, []);

const onIdleMap = () => {
const map = mapRef.current;
if (map) {
const bounds = map.getBounds();
const ne = bounds.getNorthEast();
const sw = bounds.getSouthWest();
setMapXY({
startX: sw.getLat(),
startY: sw.getLng(),
endX: ne.getLat(),
endY: ne.getLng(),
});
}
};

const setChangeMapCenter = (lat: number, lng: number) => {
setState({
center: { lat, lng },
isPanto: true,
level: 11,
});
setTimeout(() => {
onIdleMap();
}, 100);
};

return (
<Container>
<TopBox>
<SettingWrapper>
<h1 className="title">
여행지 근처의
<br /> 우리술을 찾아드려요
</h1>

<Button variant="primary" height="40px" width="82px" onClick={toggle}>
직접 설정
</Button>
</SettingWrapper>
<div className="description">
여행지를 설정하면 <br />
여행지의 우리술을 확인할 수 있어요
</div>
</TopBox>

<Map // 지도를 표시할 Container
center={state.center}
isPanto={state.isPanto}
style={{
// 지도의 크기
width: "100%",
height: "375px",
}}
ref={mapRef}
level={state.level} // 지도의 확대 레벨
onIdle={() => onIdleMap()}
>
<MapMarker // 마커를 생성합니다
position={{
// 마커가 표시될 위치입니다
lat: 37.5662952,
lng: 126.9779,
}}
/>
{drinksList.map(({ latitude, longitude }) => (
<MapMarker // 마커를 생성합니다
position={{
// 마커가 표시될 위치입니다
lat: latitude,
lng: longitude,
}}
/>
))}
</Map>
<FilterBox>
<RegionSmallSelect
defaultOption={sortBy}
onChangeSortOption={onChangeDrinkInfoSortOption}
options={DRINK_INFO_SORT_LIST}
/>
</FilterBox>
<DrinkBox>
{drinksList.map(({ drinkId, name, region, latitude, longitude }) => (
<DrinkItem
drinkInfo={{
id: drinkId,
name: name,
productName: region,
image: ExImg1 as unknown as string,
}}
onClickReplaceDrinkInfo={() => {
setChangeMapCenter(latitude, longitude);
}}
selectedDrinkList={[]}
/>
))}
</DrinkBox>
<RegionBottomSheet
setChangeMapCenter={setChangeMapCenter}
onToggleDrinkSearchModal={toggle}
on={on}
/>
</Container>
);
};

const Container = styled.div`
width: 100%;
height: 100%;
`;

const TopBox = styled.section`
padding: 20px;
.title {
color: ${({ theme }) => theme.colors.black_01};
text-align: left;
${({ theme }) => theme.typography.headline02};
padding-bottom: 8px;
}

.description {
color: ${({ theme }) => theme.colors.black_02};
${({ theme }) => theme.typography.body_long03};
}
`;

const SettingWrapper = styled.div`
width: 100%;
display: flex;
justify-content: space-between;
`;

const FilterBox = styled.div`
display: flex;
justify-content: flex-end;
padding: 16px 20px 32px 20px;
`;

const DrinkBox = styled.div`
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 20px;
`;

export default MapContainer;
Loading