From 190d410a41a851f8815cc5a189af8b00664069f0 Mon Sep 17 00:00:00 2001 From: leejiho9898 Date: Sun, 1 Oct 2023 03:34:27 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=ED=99=94=EB=A9=B4=EC=97=90=20?= =?UTF-8?q?=EC=A7=80=EB=8F=84=20=EB=9D=84=EC=9A=B0=EA=B3=A0=20=EB=A7=88?= =?UTF-8?q?=EC=BB=A4=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pnp.cjs | 46 ++++++ apps/jurumarble/package.json | 1 + apps/jurumarble/src/app/layout.tsx | 13 ++ .../src/app/map/components/MapContainer.tsx | 133 ++++++++++++++++++ apps/jurumarble/src/app/map/page.tsx | 16 +++ .../app/map/services/useDrinksMapService.ts | 34 +++++ apps/jurumarble/src/app/vote/page.tsx | 15 +- apps/jurumarble/src/lib/apis/drink.ts | 20 ++- apps/jurumarble/src/lib/constants.ts | 2 + apps/jurumarble/src/lib/queryKeys.ts | 9 ++ apps/jurumarble/src/types/drink.ts | 20 +++ yarn.lock | 30 ++++ 12 files changed, 336 insertions(+), 3 deletions(-) create mode 100644 apps/jurumarble/src/app/map/components/MapContainer.tsx create mode 100644 apps/jurumarble/src/app/map/page.tsx create mode 100644 apps/jurumarble/src/app/map/services/useDrinksMapService.ts diff --git a/.pnp.cjs b/.pnp.cjs index 477a9161..d37c8212 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -3928,6 +3928,14 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["regenerator-runtime", "npm:0.14.0"]\ ],\ "linkType": "HARD"\ + }],\ + ["npm:7.23.1", {\ + "packageLocation": "./.yarn/cache/@babel-runtime-npm-7.23.1-9b8781107c-0cd0d43e6e.zip/node_modules/@babel/runtime/",\ + "packageDependencies": [\ + ["@babel/runtime", "npm:7.23.1"],\ + ["regenerator-runtime", "npm:0.14.0"]\ + ],\ + "linkType": "HARD"\ }]\ ]],\ ["@babel/template", [\ @@ -4143,6 +4151,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["react", "npm:18.2.0"],\ ["react-dom", "virtual:040d27df013de8c2443d97cd34785bd6338b0d64a4a625937768b1a38353fca7b8404d53a1248b5c67570c2a031bc43d100149c15012f4485eb41e15d763240d#npm:18.2.0"],\ ["react-is", "npm:18.2.0"],\ + ["react-kakao-maps-sdk", "virtual:8ec4233c825b2c709f2ff549456a05fb98060d621f5df14748cda36fdb51190eccc2e438466f7464c85557cdad97eb73aa6580e78bca957a1149bfc58c6ddf34#npm:1.1.21"],\ ["react-toastify", "virtual:8ec4233c825b2c709f2ff549456a05fb98060d621f5df14748cda36fdb51190eccc2e438466f7464c85557cdad97eb73aa6580e78bca957a1149bfc58c6ddf34#npm:9.1.3"],\ ["styled-components", "virtual:8ec4233c825b2c709f2ff549456a05fb98060d621f5df14748cda36fdb51190eccc2e438466f7464c85557cdad97eb73aa6580e78bca957a1149bfc58c6ddf34#npm:5.3.11"],\ ["styled-reset", "virtual:8ec4233c825b2c709f2ff549456a05fb98060d621f5df14748cda36fdb51190eccc2e438466f7464c85557cdad97eb73aa6580e78bca957a1149bfc58c6ddf34#npm:4.5.1"],\ @@ -6525,6 +6534,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["kakao.maps.d.ts", [\ + ["npm:0.1.39", {\ + "packageLocation": "./.yarn/cache/kakao.maps.d.ts-npm-0.1.39-333b43bbc2-b89c3ee7d6.zip/node_modules/kakao.maps.d.ts/",\ + "packageDependencies": [\ + ["kakao.maps.d.ts", "npm:0.1.39"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["lines-and-columns", [\ ["npm:1.2.4", {\ "packageLocation": "./.yarn/cache/lines-and-columns-npm-1.2.4-d6c7cc5799-0c37f9f7fa.zip/node_modules/lines-and-columns/",\ @@ -7104,6 +7122,34 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["react-kakao-maps-sdk", [\ + ["npm:1.1.21", {\ + "packageLocation": "./.yarn/cache/react-kakao-maps-sdk-npm-1.1.21-dbdf5986a0-c30bd27f79.zip/node_modules/react-kakao-maps-sdk/",\ + "packageDependencies": [\ + ["react-kakao-maps-sdk", "npm:1.1.21"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:8ec4233c825b2c709f2ff549456a05fb98060d621f5df14748cda36fdb51190eccc2e438466f7464c85557cdad97eb73aa6580e78bca957a1149bfc58c6ddf34#npm:1.1.21", {\ + "packageLocation": "./.yarn/__virtual__/react-kakao-maps-sdk-virtual-afec6341b7/0/cache/react-kakao-maps-sdk-npm-1.1.21-dbdf5986a0-c30bd27f79.zip/node_modules/react-kakao-maps-sdk/",\ + "packageDependencies": [\ + ["react-kakao-maps-sdk", "virtual:8ec4233c825b2c709f2ff549456a05fb98060d621f5df14748cda36fdb51190eccc2e438466f7464c85557cdad97eb73aa6580e78bca957a1149bfc58c6ddf34#npm:1.1.21"],\ + ["@babel/runtime", "npm:7.23.1"],\ + ["@types/react", "npm:18.2.21"],\ + ["@types/react-dom", "npm:18.2.7"],\ + ["kakao.maps.d.ts", "npm:0.1.39"],\ + ["react", "npm:18.2.0"],\ + ["react-dom", "virtual:040d27df013de8c2443d97cd34785bd6338b0d64a4a625937768b1a38353fca7b8404d53a1248b5c67570c2a031bc43d100149c15012f4485eb41e15d763240d#npm:18.2.0"]\ + ],\ + "packagePeers": [\ + "@types/react-dom",\ + "@types/react",\ + "react-dom",\ + "react"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["react-slick", [\ ["npm:0.29.0", {\ "packageLocation": "./.yarn/cache/react-slick-npm-0.29.0-28db290b04-67ce498191.zip/node_modules/react-slick/",\ diff --git a/apps/jurumarble/package.json b/apps/jurumarble/package.json index c5c17e93..757ca862 100644 --- a/apps/jurumarble/package.json +++ b/apps/jurumarble/package.json @@ -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" diff --git a/apps/jurumarble/src/app/layout.tsx b/apps/jurumarble/src/app/layout.tsx index 3fe7bf4c..0aa644b1 100644 --- a/apps/jurumarble/src/app/layout.tsx +++ b/apps/jurumarble/src/app/layout.tsx @@ -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: "주루마블", @@ -16,6 +18,12 @@ if (typeof window !== "undefined") { injectStyle(); } +declare global { + interface Window { + kakao: any; + } +} + export default function RootLayout({ children }: { children: React.ReactNode }) { return ( @@ -33,6 +41,11 @@ export default function RootLayout({ children }: { children: React.ReactNode }) + ); } diff --git a/apps/jurumarble/src/app/map/components/MapContainer.tsx b/apps/jurumarble/src/app/map/components/MapContainer.tsx new file mode 100644 index 00000000..bdb23e9e --- /dev/null +++ b/apps/jurumarble/src/app/map/components/MapContainer.tsx @@ -0,0 +1,133 @@ +"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"; +const MapContainer = () => { + const mapRef = useRef(null); + const [mapXY, setMapXY] = useState({ + startX: 0, + startY: 0, + endX: 0, + endY: 0, + }); + + const { drinksList, subscribe } = useDrinksMapService({ + startX: mapXY.startX, + startY: mapXY.startY, + endX: mapXY.endX, + endY: mapXY.endY, + page: 0, + size: 100, + }); + const [state, setState] = useState({ + // 지도의 초기 위치 서울 시청 + center: { lat: 37.5662952, lng: 126.9779 }, + // 지도 위치 변경시 panto를 이용할지에 대해서 정의 + isPanto: false, + }); + + useEffect(() => { + // 윈도우 리사이즈 실행 + setTimeout(() => { + window.dispatchEvent(new Event("resize")); + }, 1000); + }, []); + + return ( + + + +

+ 여행지 근처의 +
우리술을 찾아드려요 +

+ + +
+
+ 여행지를 설정하면
+ 여행지의 우리술을 확인할 수 있어요 +
+
+ + { + 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(), + }); + } + }} + > + + {drinksList.map(({ latitude, longitude }) => ( + + ))} + + +
+ ); +}; + +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 MapBox = styled.div``; + +export default MapContainer; diff --git a/apps/jurumarble/src/app/map/page.tsx b/apps/jurumarble/src/app/map/page.tsx new file mode 100644 index 00000000..336b6ddf --- /dev/null +++ b/apps/jurumarble/src/app/map/page.tsx @@ -0,0 +1,16 @@ +import BottomBar from "components/BottomBar"; +import Header from "components/Header"; +import React from "react"; +import MapContainer from "./components/MapContainer"; + +const MapPage = () => { + return ( + <> +
+ + + + ); +}; + +export default MapPage; diff --git a/apps/jurumarble/src/app/map/services/useDrinksMapService.ts b/apps/jurumarble/src/app/map/services/useDrinksMapService.ts new file mode 100644 index 00000000..abe6e84e --- /dev/null +++ b/apps/jurumarble/src/app/map/services/useDrinksMapService.ts @@ -0,0 +1,34 @@ +import { useInfiniteQuery } from "@tanstack/react-query"; +import { useInfiniteScroll } from "@monorepo/hooks"; +import { reactQueryKeys } from "lib/queryKeys"; +import { getDrinksMap } from "lib/apis/drink"; + +export default function useDrinksMapService(params: { + startX: number; + startY: number; + endX: number; + endY: number; + page: number; + size: number; +}) { + const { startX, startY, endX, endY, page, size } = params; + const { data, fetchNextPage } = useInfiniteQuery( + reactQueryKeys.drinksMap(startX, startY, endX, endY, page, size), + ({ pageParam }) => getDrinksMap({ ...params, page: pageParam?.page || 0 }), + { + getNextPageParam: ({ last, number }) => { + if (last) return undefined; + return { + page: number + 1, + }; + }, + keepPreviousData: true, + }, + ); + + const [subscribe] = useInfiniteScroll(fetchNextPage); + + const drinksList = data?.pages.flatMap((page) => page.content) ?? []; + + return { drinksList, subscribe }; +} diff --git a/apps/jurumarble/src/app/vote/page.tsx b/apps/jurumarble/src/app/vote/page.tsx index 99564b86..e18307d8 100644 --- a/apps/jurumarble/src/app/vote/page.tsx +++ b/apps/jurumarble/src/app/vote/page.tsx @@ -15,6 +15,7 @@ import VoteDescription from "./[id]/components/VoteDescription"; import Path from "lib/Path"; import useExecuteVoteService from "./[id]/services/useExecuteVoteService"; import useInfiniteMainListService from "./services/useGetVoteListService"; +import { useMemo } from "react"; export type Drag = "up" | "down" | null; @@ -35,6 +36,16 @@ function VoteHomePage() { const { title, imageA, imageB, titleA, titleB, detail, voteId, region } = mainVoteList[nowShowing] || {}; + const safeImageA = useMemo(() => { + console.log(imageA); + if (!imageA || imageA === "string") return EmptyAImg; + return imageA; + }, [imageA]); + const safeImageB = useMemo(() => { + if (!imageB || imageB === "string") return EmptyAImg; + return imageB; + }, [imageB]); + const { mutateBookMark, bookMarkCheckQuery } = usePostBookmarkService(voteId); const { mutate, select } = useExecuteVoteService(voteId); @@ -85,8 +96,8 @@ function VoteHomePage() { isBookmark={isBookmark} /> { }); return response.data; }; + +export interface GetDrinksMapRequest { + startX: number; + startY: number; + endX: number; + endY: number; + page: number; + size: number; +} + +export const getDrinksMap = async (params: GetDrinksMapRequest) => { + const response = await baseApi.get("api/drinks/map", { + params: { + ...params, + }, + }); + return response.data; +}; diff --git a/apps/jurumarble/src/lib/constants.ts b/apps/jurumarble/src/lib/constants.ts index d0bca41c..b04a2dae 100644 --- a/apps/jurumarble/src/lib/constants.ts +++ b/apps/jurumarble/src/lib/constants.ts @@ -5,6 +5,8 @@ export const SERVER_URL = process.env.NEXT_PUBLIC_SERVER_URL || ""; export const KAKAO_CLIENT_ID = process.env.NEXT_PUBLIC_KAKAO_CLIENT_ID || ""; export const NAVER_CLIENT_ID = process.env.NEXT_PUBLIC_NAVER_CLIENT_ID || ""; + +export const KAKAO_MAP_API_KEY = process.env.NEXT_PUBLIC_KAKAO_MAP_API_KEY || ""; export const KAKAO_LOGIN_REDIRECT_URL = process.env.NODE_ENV === "development" ? `http://localhost:3000/${Path.KAKAO_LOGIN_PROCESS}` diff --git a/apps/jurumarble/src/lib/queryKeys.ts b/apps/jurumarble/src/lib/queryKeys.ts index 62372af1..8a2b118c 100644 --- a/apps/jurumarble/src/lib/queryKeys.ts +++ b/apps/jurumarble/src/lib/queryKeys.ts @@ -18,6 +18,7 @@ export const queryKeys = { MY_PARTICIPATED_VOTE: "myParticipatedVote" as const, MY_CREATED_VOTE: "myCreatedVote" as const, MY_BOOKMARKED_VOTE: "myBookmarkedVote" as const, + DRINKS_MAP: "drinksMap" as const, }; export const reactQueryKeys = { @@ -38,4 +39,12 @@ export const reactQueryKeys = { detailVoteCount: (id: number) => [queryKeys.DETAIL_VOTE_COUNT, id] as const, detailFilterdAnalysis: (id: number, mbti?: string, gender?: string, age?: string) => [queryKeys.DETAIL_FILTERED_ANALYSIS, id, mbti, gender, age] as const, + drinksMap: ( + startX: number, + startY: number, + endX: number, + endY: number, + page: number, + size: number, + ) => [queryKeys.DRINKS_MAP, startX, startY, endX, endY, page, size] as const, }; diff --git a/apps/jurumarble/src/types/drink.ts b/apps/jurumarble/src/types/drink.ts index 317a7a77..e969420b 100644 --- a/apps/jurumarble/src/types/drink.ts +++ b/apps/jurumarble/src/types/drink.ts @@ -30,6 +30,14 @@ export interface DrinkInfo { enjoyCount: number | null; } +export interface DrinkMapInfo { + drinkId: number; + name: string; + region: string; + latitude: number; + longitude: number; +} + export interface DrinkListResponse { content: DrinkInfo[]; pageable: Pageable; @@ -41,3 +49,15 @@ export interface DrinkListResponse { size: number; empty: boolean; } + +export interface DrinkMapResponse { + content: DrinkMapInfo[]; + pageable: Pageable; + sort: Sort; + first: boolean; + last: boolean; + number: number; + numberOfElements: number; + size: number; + empty: boolean; +} diff --git a/yarn.lock b/yarn.lock index c3df845a..da22bd32 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1427,6 +1427,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.22.15": + version: 7.23.1 + resolution: "@babel/runtime@npm:7.23.1" + dependencies: + regenerator-runtime: ^0.14.0 + checksum: 0cd0d43e6e7dc7f9152fda8c8312b08321cda2f56ef53d6c22ebdd773abdc6f5d0a69008de90aa41908d00e2c1facb24715ff121274e689305c858355ff02c70 + languageName: node + linkType: hard + "@babel/runtime@npm:^7.8.4": version: 7.22.11 resolution: "@babel/runtime@npm:7.22.11" @@ -1613,6 +1622,7 @@ __metadata: 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 @@ -3226,6 +3236,13 @@ __metadata: languageName: node linkType: hard +"kakao.maps.d.ts@npm:^0.1.39": + version: 0.1.39 + resolution: "kakao.maps.d.ts@npm:0.1.39" + checksum: b89c3ee7d6e2e86b1f53fa43060097f7cfc11b5a1e6c3862ab0e4126aa17483c6642d24c2a67deaa1c36de3fb604ec3fc7c049e6e4ddb0689782b040fa341526 + languageName: node + linkType: hard + "lines-and-columns@npm:^1.1.6": version: 1.2.4 resolution: "lines-and-columns@npm:1.2.4" @@ -3643,6 +3660,19 @@ __metadata: languageName: node linkType: hard +"react-kakao-maps-sdk@npm:^1.1.21": + version: 1.1.21 + resolution: "react-kakao-maps-sdk@npm:1.1.21" + dependencies: + "@babel/runtime": ^7.22.15 + kakao.maps.d.ts: ^0.1.39 + peerDependencies: + react: ^16.8 || ^17 || ^18 + react-dom: ^16.8 || ^17 || ^18 + checksum: c30bd27f79c18da81a7083ba9b6e39ec1ee0bdfb8d1ae3890b96bf44bf77279a36e44f29ec652de89f97a6e4c1f32bfa3660d738f1b6f3eaf26e14d7c3241d20 + languageName: node + linkType: hard + "react-slick@npm:^0.29.0": version: 0.29.0 resolution: "react-slick@npm:0.29.0" From 17bc5177300e9f793d2893c96b685d657bf72910 Mon Sep 17 00:00:00 2001 From: leejiho9898 Date: Mon, 2 Oct 2023 00:41:17 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=EC=A7=80=EC=97=AD=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EB=B0=94=ED=85=80=EC=8B=9C=ED=8A=B8=20=ED=8D=BC?= =?UTF-8?q?=EB=B8=94=EB=A6=AC=EC=8B=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/app/map/components/MapContainer.tsx | 6 +- .../app/map/components/RegionBottomsheet.tsx | 154 ++++++++++++++++++ 2 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 apps/jurumarble/src/app/map/components/RegionBottomsheet.tsx diff --git a/apps/jurumarble/src/app/map/components/MapContainer.tsx b/apps/jurumarble/src/app/map/components/MapContainer.tsx index bdb23e9e..1cf895b1 100644 --- a/apps/jurumarble/src/app/map/components/MapContainer.tsx +++ b/apps/jurumarble/src/app/map/components/MapContainer.tsx @@ -5,7 +5,10 @@ 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"; const MapContainer = () => { + const [on, toggle] = useToggle(); const mapRef = useRef(null); const [mapXY, setMapXY] = useState({ startX: 0, @@ -45,7 +48,7 @@ const MapContainer = () => {
우리술을 찾아드려요 - @@ -98,6 +101,7 @@ const MapContainer = () => { ))} + ); }; diff --git a/apps/jurumarble/src/app/map/components/RegionBottomsheet.tsx b/apps/jurumarble/src/app/map/components/RegionBottomsheet.tsx new file mode 100644 index 00000000..f31772b2 --- /dev/null +++ b/apps/jurumarble/src/app/map/components/RegionBottomsheet.tsx @@ -0,0 +1,154 @@ +import { Button, Portal } from "components/index"; +import { REGION_LIST } from "lib/constants"; +import { transitions } from "lib/styles"; +import Image from "next/image"; +import React from "react"; +import { SvgIcPrev, SvgIcX } from "src/assets/icons/components"; +import styled, { css } from "styled-components"; + +interface Props { + on: boolean; + onToggleDrinkSearchModal: () => void; +} + +const RegionBottomSheet = ({ on, onToggleDrinkSearchModal }: Props) => { + if (!on) return null; + + return ( + + + + 지역 설정 + + + + + + 지역을 선택해주세요{" "} + {" "} + + + {REGION_LIST.map(({ label, value }) => ( + {label} + ))} + {REGION_LIST.map(({ label, value }) => ( + {label} + ))} + + + + + + + + + ); +}; + +const BottomSheet = styled.div` + position: fixed; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 9999; +`; + +const Inner = styled.div` + position: absolute; + z-index: 9999; + background-color: white; + bottom: 0; + right: 0; + left: 0; + margin: auto; + width: 100%; + max-width: 720px; + height: 90%; + animation: ${transitions.popInFromBottom} 0.4s ease-in-out; + border-radius: 16px 16px 0px 0px; + padding: 26px 20px 20px 20px; +`; + +const Background = styled.div` + display: block; + width: 100%; + height: 100%; + background-color: black; + position: absolute; + left: 0; + top: 0; + opacity: 0.4; +`; + +const Title = styled.div` + display: flex; + ${({ theme }) => css` + ${theme.typography.headline03} + `} + justify-content: center; + padding-bottom: 36px; +`; + +const Exit = styled.div` + position: absolute; + top: 26px; + right: 20px; + width: 24px; + height: 24px; + cursor: pointer; +`; + +const SelectBox = styled.div` + border-radius: 8px; + border: 1px solid ${({ theme }) => theme.colors.black_05}; + width: 100%; + display: flex; + justify-content: center; + align-items: center; + padding: 10px 16px; + color: ${({ theme }) => theme.colors.black_03}; + ${({ theme }) => theme.typography.button01} + gap: 4px; + margin-bottom: 8px; +`; + +const List = styled.div` + width: 100%; + border-radius: 8px; + border: 1px solid ${({ theme }) => theme.colors.line_01}; + overflow-y: scroll; + height: calc(90% - 134px); + -ms-overflow-style: none; + scrollbar-width: none; + ::-webkit-scrollbar { + display: none; + } +`; + +const RegionItem = styled.div` + padding: 16px; + text-align: center; + color: ${({ theme }) => theme.colors.black_02}; + ${({ theme }) => theme.typography.button02} + border-bottom: 1px solid ${({ theme }) => theme.colors.line_01}; + :active { + background-color: ${({ theme }) => theme.colors.bg_01}; + } +`; + +const ButtonWrapper = styled.div` + width: 100%; + position: absolute; + bottom: 0; + left: 0; + padding: 20px; +`; + +export default RegionBottomSheet; From 5fc3a9eb57a2ab4f0f48f92b0feebc2f6017f0ff Mon Sep 17 00:00:00 2001 From: leejiho9898 Date: Mon, 2 Oct 2023 01:58:13 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=EC=A7=80=EB=8F=84=20=ED=81=B4?= =?UTF-8?q?=EB=A6=AD=20=EC=9D=B4=EB=B2=A4=ED=8A=B8,=20=EC=A7=80=EC=97=AD?= =?UTF-8?q?=EC=84=A4=EC=A0=95,=20=EC=88=A0=EC=A0=9C=ED=92=88=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EB=9E=9C=EB=8D=94=EB=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/app/map/components/MapContainer.tsx | 154 ++++++++++++------ .../app/map/components/RegionBottomsheet.tsx | 19 ++- .../app/map/components/RegionSmallSelect.tsx | 59 +++++++ .../src/app/stemp/components/DrinkItem.tsx | 10 +- apps/jurumarble/src/components/BottomBar.tsx | 2 +- apps/jurumarble/src/lib/constants.ts | 20 +++ 6 files changed, 209 insertions(+), 55 deletions(-) create mode 100644 apps/jurumarble/src/app/map/components/RegionSmallSelect.tsx diff --git a/apps/jurumarble/src/app/map/components/MapContainer.tsx b/apps/jurumarble/src/app/map/components/MapContainer.tsx index 1cf895b1..216b7cf6 100644 --- a/apps/jurumarble/src/app/map/components/MapContainer.tsx +++ b/apps/jurumarble/src/app/map/components/MapContainer.tsx @@ -7,17 +7,22 @@ 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(null); const [mapXY, setMapXY] = useState({ - startX: 0, - startY: 0, - endX: 0, - endY: 0, + startX: 33.64225953272826, + startY: 119.06076029979886, + endX: 40.856225138838, + endY: 132.02500466772065, }); - const { drinksList, subscribe } = useDrinksMapService({ + const { drinksList } = useDrinksMapService({ startX: mapXY.startX, startY: mapXY.startY, endX: mapXY.endX, @@ -25,11 +30,18 @@ const MapContainer = () => { page: 0, size: 100, }); + const [sortBy, setSortBy] = useState("ByPopularity"); + + const onChangeDrinkInfoSortOption = (sortOption: string) => { + setSortBy(sortOption); + }; + const [state, setState] = useState({ // 지도의 초기 위치 서울 시청 - center: { lat: 37.5662952, lng: 126.9779 }, + center: { lat: 36.75158343784174, lng: 127.90408370095267 }, // 지도 위치 변경시 panto를 이용할지에 대해서 정의 isPanto: false, + level: 13, }); useEffect(() => { @@ -39,6 +51,32 @@ const MapContainer = () => { }, 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 ( @@ -57,51 +95,64 @@ const MapContainer = () => { 여행지의 우리술을 확인할 수 있어요 - - { - 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(), - }); - } + + onIdleMap()} + > + + /> + {drinksList.map(({ latitude, longitude }) => ( - {drinksList.map(({ latitude, longitude }) => ( - - ))} - - - + ))} + + + + + + {drinksList.map(({ drinkId, name, region, latitude, longitude }) => ( + { + setChangeMapCenter(latitude, longitude); + }} + selectedDrinkList={[]} + /> + ))} + + ); }; @@ -132,6 +183,17 @@ const SettingWrapper = styled.div` justify-content: space-between; `; -const MapBox = styled.div``; +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; diff --git a/apps/jurumarble/src/app/map/components/RegionBottomsheet.tsx b/apps/jurumarble/src/app/map/components/RegionBottomsheet.tsx index f31772b2..5acf08a6 100644 --- a/apps/jurumarble/src/app/map/components/RegionBottomsheet.tsx +++ b/apps/jurumarble/src/app/map/components/RegionBottomsheet.tsx @@ -1,5 +1,5 @@ import { Button, Portal } from "components/index"; -import { REGION_LIST } from "lib/constants"; +import { REGION_LIST_BOUNDS } from "lib/constants"; import { transitions } from "lib/styles"; import Image from "next/image"; import React from "react"; @@ -9,9 +9,10 @@ import styled, { css } from "styled-components"; interface Props { on: boolean; onToggleDrinkSearchModal: () => void; + setChangeMapCenter: (lat: number, lng: number) => void; } -const RegionBottomSheet = ({ on, onToggleDrinkSearchModal }: Props) => { +const RegionBottomSheet = ({ on, onToggleDrinkSearchModal, setChangeMapCenter }: Props) => { if (!on) return null; return ( @@ -32,11 +33,15 @@ const RegionBottomSheet = ({ on, onToggleDrinkSearchModal }: Props) => { />{" "} - {REGION_LIST.map(({ label, value }) => ( - {label} - ))} - {REGION_LIST.map(({ label, value }) => ( - {label} + {REGION_LIST_BOUNDS.map(({ label, value, lat, long }) => ( + { + setChangeMapCenter(lat, long); + onToggleDrinkSearchModal(); + }} + > + {label} + ))} diff --git a/apps/jurumarble/src/app/map/components/RegionSmallSelect.tsx b/apps/jurumarble/src/app/map/components/RegionSmallSelect.tsx new file mode 100644 index 00000000..b8472e94 --- /dev/null +++ b/apps/jurumarble/src/app/map/components/RegionSmallSelect.tsx @@ -0,0 +1,59 @@ +import styled, { css } from "styled-components"; +import { useToggle } from "@monorepo/hooks"; +import SvgIcExpandMore from "src/assets/icons/components/IcExpandMore"; +import { Select } from "components/selectBox"; + +interface Props { + defaultOption: string; + onChangeSortOption: (id: string) => void; + options: { value: string; label: string }[]; +} + +function RegionSmallSelect({ defaultOption, onChangeSortOption, options }: Props) { + const [isOpen, onToggleOpen] = useToggle(); + + return ( + + + + ); +} + +const SelectStyled = styled.span<{ isOpen: boolean }>` + ${({ theme, isOpen }) => css` + ${theme.typography.button01}; + color: ${theme.colors.black_03}; + width: 85px; + /* height: 40px; */ + .selected-label { + border: 1px solid ${theme.colors.line_01}; + border-radius: 8px; + padding: 10px 12px; + } + svg { + ${isOpen && "transform: rotateX( 180deg )"} + } + #select-list { + width: 100px; + height: 78px; + display: flex; + flex-direction: column; + align-items: center; + padding: 8px 0; + gap: 20px; + } + #indicator { + display: flex; + } + `} +`; + +export default RegionSmallSelect; diff --git a/apps/jurumarble/src/app/stemp/components/DrinkItem.tsx b/apps/jurumarble/src/app/stemp/components/DrinkItem.tsx index f24ed668..df45ac1e 100644 --- a/apps/jurumarble/src/app/stemp/components/DrinkItem.tsx +++ b/apps/jurumarble/src/app/stemp/components/DrinkItem.tsx @@ -6,7 +6,14 @@ import styled, { css, useTheme } from "styled-components"; import useDrinkStempService from "../service/useDrinkStempService"; interface Props { - drinkInfo: DrinkInfo; + drinkInfo: + | DrinkInfo + | { + id: number; + name: string; + productName: string; + image: string; + }; onClickReplaceDrinkInfo: (e: React.MouseEvent) => void; selectedDrinkList?: string[]; } @@ -54,6 +61,7 @@ const Container = styled.button<{ selected: boolean | undefined }>` padding: 16px; border-radius: 16px; cursor: pointer; + width: 100%; ${({ theme, selected }) => selected && css` diff --git a/apps/jurumarble/src/components/BottomBar.tsx b/apps/jurumarble/src/components/BottomBar.tsx index 28b5961d..26e3a1d3 100644 --- a/apps/jurumarble/src/components/BottomBar.tsx +++ b/apps/jurumarble/src/components/BottomBar.tsx @@ -16,7 +16,7 @@ const NAVIGATION_LIST = [ }, { name: "술도장", - path: "/stamp", + path: "/stemp", icon: , }, { diff --git a/apps/jurumarble/src/lib/constants.ts b/apps/jurumarble/src/lib/constants.ts index b04a2dae..6705792e 100644 --- a/apps/jurumarble/src/lib/constants.ts +++ b/apps/jurumarble/src/lib/constants.ts @@ -36,6 +36,26 @@ export const REGION_LIST = [ { value: "JEJU", label: "제주" }, ]; +export const REGION_LIST_BOUNDS = [ + { value: "SEOUL", label: "서울", lat: 37.53391, long: 126.9775 }, + { value: "INCHEON", label: "인천", lat: 37.45323333333334, long: 126.70735277777779 }, + { value: "DAEJEON", label: "대전", lat: 36.347119444444445, long: 127.38656666666667 }, + { value: "DAEGU", label: "대구", lat: 35.868541666666665, long: 128.60355277777776 }, + { value: "GWANGJU", label: "광주", lat: 35.156974999999996, long: 126.85336388888888 }, + { value: "BUSAN", label: "부산", lat: 35.17701944444444, long: 129.07695277777776 }, + { value: "ULSAN", label: "울산", lat: 35.53540833333333, long: 129.3136888888889 }, + { value: "SEJONG", label: "세종", lat: 36.4800121, long: 127.289069 }, + { value: "GYEONGGI", label: "경기도", lat: 37.39067, long: 126.7888 }, + { value: "GANGWON", label: "강원도", lat: 38.642618, long: 127.170231 }, + { value: "CHUNGBUK", label: "충청북도", lat: 36.6325, long: 127.49358611111111 }, + { value: "CHUNGNAM", label: "충청남도", lat: 36.32387222222223, long: 127.42295555555556 }, + { value: "GYEONGBUK", label: "경상북도", lat: 36.491286, long: 128.889433 }, + { value: "GYEONGNAM", label: "경상남도", lat: 35.459369, long: 128.214826 }, + { value: "JEONBUK", label: "전라북도", lat: 35.81727, long: 127.11105277777777 }, + { value: "JEONNAM", label: "전라남도", lat: 34.813044444444444, long: 126.465 }, + { value: "JEJU", label: "제주", lat: 33.48569444444445, long: 126.50033333333333 }, +]; + export const DRINK_VOTE_SORT_LIST = [ { value: "ByPopularity", label: "인기순" }, { value: "ByTime", label: "최신순" }, From a80680f1d4adef84861f14fd7b84ae7b0dd9c60d Mon Sep 17 00:00:00 2001 From: leejiho9898 Date: Mon, 2 Oct 2023 02:06:35 +0900 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=ED=8C=8C=EB=B9=84=EC=BD=98=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jurumarble/public/favicon.ico | Bin 0 -> 5012 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 apps/jurumarble/public/favicon.ico diff --git a/apps/jurumarble/public/favicon.ico b/apps/jurumarble/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..44e9b6f705fa68e8091ad69aee2db50451ac09a3 GIT binary patch literal 5012 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