From acf3d03d962d4f84b6540e32c9198824d8e56dec Mon Sep 17 00:00:00 2001 From: choihooo Date: Wed, 13 Nov 2024 15:12:54 +0900 Subject: [PATCH 01/13] =?UTF-8?q?[feature]=20=EB=A7=88=EC=BB=A4=20?= =?UTF-8?q?=ED=81=B4=EB=A6=AD=EC=8B=9C=20=EC=BB=A4=EC=A7=80=EB=8A=94=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[id]/components/MapComponent.tsx | 114 +++++++++++------- 1 file changed, 68 insertions(+), 46 deletions(-) diff --git a/fe/src/app/event-maps/[id]/components/MapComponent.tsx b/fe/src/app/event-maps/[id]/components/MapComponent.tsx index 60c7e09..810e470 100644 --- a/fe/src/app/event-maps/[id]/components/MapComponent.tsx +++ b/fe/src/app/event-maps/[id]/components/MapComponent.tsx @@ -14,10 +14,12 @@ export default function MapComponent({ px, py }: MapComponentProps) { const { center } = useLocationStore(); const [selectedMarker, setSelectedMarker] = useState(null); const markersRef = useRef([]); + const infoWindowRef = useRef(null); + const previousMarkerIndexRef = useRef(null); // 이전 선택된 마커 인덱스 저장 // 레벨에 따라 커스텀 마커 아이콘 설정 const getIconByLevel = (level: number, isSelected: boolean = false) => { - const size = isSelected ? 44 : 36; // 선택된 마커는 44px, 기본 마커는 24px + const size = isSelected ? 44 : 36; // 선택된 마커는 44px, 기본 마커는 36px return { url: `/pin/level${level}.svg`, // 레벨에 따라 다른 아이콘 파일 사용 size: new window.naver.maps.Size(size, size), @@ -53,7 +55,7 @@ export default function MapComponent({ px, py }: MapComponentProps) { script.onload = () => initializeMap(); document.head.appendChild(script); } - }, [px, py]); + }, []); // customMarkers가 변경될 때마다 마커를 업데이트 useEffect(() => { @@ -65,74 +67,94 @@ export default function MapComponent({ px, py }: MapComponentProps) { // 새로운 마커 추가 customMarkers.forEach((ping, index) => { - const marker = new window.naver.maps.Marker({ + const markerOptions: naver.maps.MarkerOptions = { position: new window.naver.maps.LatLng(ping.py, ping.px), map: mapInstanceRef.current!, icon: getIconByLevel(ping.iconLevel), - }); - markersRef.current.push(marker); + clickable: true, + }; - // `nonMembers` 이름을 콤마로 연결된 문자열로 변환 - const nonMemberNames = (ping.nonMembers || []) - .map((member) => member.name) - .join(", "); - - // Custom tooltip style with triangle arrow for infoWindow - const infoWindowContent = ` -
- ${nonMemberNames}
- - 가게 정보 바로 보기 › - -
-
- `; - - const infoWindow = new window.naver.maps.InfoWindow({ - content: infoWindowContent, - borderWidth: 0, - disableAnchor: true, - backgroundColor: "transparent", - }); + const marker = new window.naver.maps.Marker(markerOptions); + markersRef.current.push(marker); - // 마커 클릭 이벤트 + // 마커 클릭 이벤트 추가 window.naver.maps.Event.addListener(marker, "click", () => { - // 이전 선택된 마커의 크기를 원래대로 초기화 - if (selectedMarker !== null && selectedMarker !== index) { - markersRef.current[selectedMarker].setIcon( - getIconByLevel(customMarkers[selectedMarker].iconLevel) + // 이전에 선택된 마커가 존재하면 크기 복원 + if ( + previousMarkerIndexRef.current !== null && + previousMarkerIndexRef.current !== index + ) { + markersRef.current[previousMarkerIndexRef.current]?.setIcon( + getIconByLevel( + customMarkers[previousMarkerIndexRef.current].iconLevel, + false + ) ); } - // 현재 클릭된 마커를 선택하고 크기 변경 - setSelectedMarker(index); + // 현재 클릭된 마커를 확대하고, 이전 마커 인덱스를 업데이트 marker.setIcon(getIconByLevel(ping.iconLevel, true)); - infoWindow.open(mapInstanceRef.current!, marker); + previousMarkerIndexRef.current = index; + setSelectedMarker(index); + + // InfoWindow 생성 및 설정 + if (infoWindowRef.current) { + infoWindowRef.current.close(); + } + const infoWindow = new window.naver.maps.InfoWindow({ + content: `
+ ${ping.placeName}
+
`, + borderWidth: 0, + backgroundColor: "transparent", + disableAnchor: true, + }); + if (mapInstanceRef.current) { + infoWindow.open(mapInstanceRef.current, marker); + } + + infoWindowRef.current = infoWindow; }); + }); + }, [customMarkers]); - // 지도 클릭 시 모든 마커 초기화 + // 맵 클릭 시 모든 마커 초기화 및 InfoWindow 닫기 + useEffect(() => { + if (mapInstanceRef.current) { window.naver.maps.Event.addListener( - mapInstanceRef.current!, + mapInstanceRef.current, "click", () => { - if (selectedMarker !== null) { - markersRef.current[selectedMarker].setIcon( - getIconByLevel(customMarkers[selectedMarker].iconLevel) + if (previousMarkerIndexRef.current !== null) { + markersRef.current[previousMarkerIndexRef.current].setIcon( + getIconByLevel( + customMarkers[previousMarkerIndexRef.current].iconLevel + ) ); - setSelectedMarker(null); // 선택된 마커 상태 초기화 - infoWindow.close(); + previousMarkerIndexRef.current = null; + } + setSelectedMarker(null); + + if (infoWindowRef.current) { + infoWindowRef.current.close(); } } ); - }); - }, [customMarkers, selectedMarker]); + } + }, [customMarkers]); // `center` 위치가 변경될 때마다 지도의 중심 업데이트 useEffect(() => { if (mapInstanceRef.current) { - mapInstanceRef.current.setCenter( - new window.naver.maps.LatLng(center.latitude, center.longitude) + const currentCenter = mapInstanceRef.current.getCenter(); + const targetCenter = new window.naver.maps.LatLng( + center.latitude, + center.longitude ); + + if (!currentCenter.equals(targetCenter)) { + mapInstanceRef.current.setCenter(targetCenter); + } } }, [center]); From 712d32bf93789b831c3e8696483f65c7f155c50a Mon Sep 17 00:00:00 2001 From: choihooo Date: Wed, 13 Nov 2024 18:38:57 +0900 Subject: [PATCH 02/13] =?UTF-8?q?[feature]=20=EB=A7=88=EC=BB=A4=20?= =?UTF-8?q?=EB=B0=91=EC=97=90=20=EA=B0=80=EA=B2=8C=EB=AA=85=20=EB=85=B8?= =?UTF-8?q?=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[id]/components/MapComponent.tsx | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/fe/src/app/event-maps/[id]/components/MapComponent.tsx b/fe/src/app/event-maps/[id]/components/MapComponent.tsx index 810e470..6f07818 100644 --- a/fe/src/app/event-maps/[id]/components/MapComponent.tsx +++ b/fe/src/app/event-maps/[id]/components/MapComponent.tsx @@ -10,22 +10,28 @@ interface MapComponentProps { export default function MapComponent({ px, py }: MapComponentProps) { const mapRef = useRef(null); const mapInstanceRef = useRef(null); - const { customMarkers } = useMarkerStore(); // MarkerStore에서 customMarkers 가져오기 + const { customMarkers } = useMarkerStore(); const { center } = useLocationStore(); const [selectedMarker, setSelectedMarker] = useState(null); const markersRef = useRef([]); const infoWindowRef = useRef(null); - const previousMarkerIndexRef = useRef(null); // 이전 선택된 마커 인덱스 저장 - - // 레벨에 따라 커스텀 마커 아이콘 설정 - const getIconByLevel = (level: number, isSelected: boolean = false) => { - const size = isSelected ? 44 : 36; // 선택된 마커는 44px, 기본 마커는 36px + const previousMarkerIndexRef = useRef(null); + + // HTML 아이콘으로 커스텀 마커 설정 + const getHtmlIconByLevel = ( + level: number, + placeName: string, + isSelected: boolean = false + ) => { + const iconSize = isSelected ? 44 : 36; return { - url: `/pin/level${level}.svg`, // 레벨에 따라 다른 아이콘 파일 사용 - size: new window.naver.maps.Size(size, size), - scaledSize: new window.naver.maps.Size(size, size), - origin: new window.naver.maps.Point(0, 0), - anchor: new window.naver.maps.Point(size / 2, size), + content: `
+
+ ${placeName} +
+
`, + size: new window.naver.maps.Size(iconSize, iconSize), + anchor: new window.naver.maps.Point(iconSize / 2, iconSize + 15), // 라벨이 포함되므로 앵커 포지션 조정 }; }; @@ -57,47 +63,43 @@ export default function MapComponent({ px, py }: MapComponentProps) { } }, []); - // customMarkers가 변경될 때마다 마커를 업데이트 useEffect(() => { if (!mapInstanceRef.current) return; - // 이전 마커 제거 markersRef.current.forEach((marker) => marker.setMap(null)); markersRef.current = []; - // 새로운 마커 추가 customMarkers.forEach((ping, index) => { const markerOptions: naver.maps.MarkerOptions = { position: new window.naver.maps.LatLng(ping.py, ping.px), - map: mapInstanceRef.current!, - icon: getIconByLevel(ping.iconLevel), + map: mapInstanceRef.current as naver.maps.Map, + icon: getHtmlIconByLevel(ping.iconLevel, ping.placeName, false), clickable: true, }; const marker = new window.naver.maps.Marker(markerOptions); markersRef.current.push(marker); - // 마커 클릭 이벤트 추가 window.naver.maps.Event.addListener(marker, "click", () => { - // 이전에 선택된 마커가 존재하면 크기 복원 if ( previousMarkerIndexRef.current !== null && previousMarkerIndexRef.current !== index ) { - markersRef.current[previousMarkerIndexRef.current]?.setIcon( - getIconByLevel( + markersRef.current[previousMarkerIndexRef.current].setIcon( + getHtmlIconByLevel( customMarkers[previousMarkerIndexRef.current].iconLevel, + customMarkers[previousMarkerIndexRef.current].placeName, false ) ); } - // 현재 클릭된 마커를 확대하고, 이전 마커 인덱스를 업데이트 - marker.setIcon(getIconByLevel(ping.iconLevel, true)); + marker.setIcon( + getHtmlIconByLevel(ping.iconLevel, ping.placeName, true) + ); previousMarkerIndexRef.current = index; setSelectedMarker(index); - // InfoWindow 생성 및 설정 if (infoWindowRef.current) { infoWindowRef.current.close(); } @@ -118,7 +120,6 @@ export default function MapComponent({ px, py }: MapComponentProps) { }); }, [customMarkers]); - // 맵 클릭 시 모든 마커 초기화 및 InfoWindow 닫기 useEffect(() => { if (mapInstanceRef.current) { window.naver.maps.Event.addListener( @@ -127,8 +128,10 @@ export default function MapComponent({ px, py }: MapComponentProps) { () => { if (previousMarkerIndexRef.current !== null) { markersRef.current[previousMarkerIndexRef.current].setIcon( - getIconByLevel( - customMarkers[previousMarkerIndexRef.current].iconLevel + getHtmlIconByLevel( + customMarkers[previousMarkerIndexRef.current].iconLevel, + customMarkers[previousMarkerIndexRef.current].placeName, + false ) ); previousMarkerIndexRef.current = null; @@ -143,7 +146,6 @@ export default function MapComponent({ px, py }: MapComponentProps) { } }, [customMarkers]); - // `center` 위치가 변경될 때마다 지도의 중심 업데이트 useEffect(() => { if (mapInstanceRef.current) { const currentCenter = mapInstanceRef.current.getCenter(); From 1c6257e9a790a4db8749b3559d896f89bca3d5d0 Mon Sep 17 00:00:00 2001 From: choihooo Date: Wed, 13 Nov 2024 18:57:04 +0900 Subject: [PATCH 03/13] =?UTF-8?q?[feature]=20=EC=95=84=EC=9D=B4=EC=BD=98?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EA=B0=80=EA=B2=8C=EB=AA=85?= =?UTF-8?q?=20=ED=8F=B0=ED=8A=B8=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fe/public/pin/level1.svg | 25 ++++++++++--------- fe/public/pin/level2.svg | 25 ++++++++++--------- fe/public/pin/level3.svg | 25 ++++++++++--------- fe/public/pin/level4.svg | 25 ++++++++++--------- .../[id]/components/MapComponent.tsx | 20 ++++++++++++--- 5 files changed, 68 insertions(+), 52 deletions(-) diff --git a/fe/public/pin/level1.svg b/fe/public/pin/level1.svg index f29d2a3..aed8716 100644 --- a/fe/public/pin/level1.svg +++ b/fe/public/pin/level1.svg @@ -1,22 +1,23 @@ - - - - - - - - + + + + + + + + + - + - + - - + + diff --git a/fe/public/pin/level2.svg b/fe/public/pin/level2.svg index 8dbdf2e..9e85cf1 100644 --- a/fe/public/pin/level2.svg +++ b/fe/public/pin/level2.svg @@ -1,22 +1,23 @@ - - - - - - - - + + + + + + + + + - + - + - - + + diff --git a/fe/public/pin/level3.svg b/fe/public/pin/level3.svg index c52938a..6a5385c 100644 --- a/fe/public/pin/level3.svg +++ b/fe/public/pin/level3.svg @@ -1,22 +1,23 @@ - - - - - - - - + + + + + + + + + - + - + - - + + diff --git a/fe/public/pin/level4.svg b/fe/public/pin/level4.svg index a41a7c2..c64063f 100644 --- a/fe/public/pin/level4.svg +++ b/fe/public/pin/level4.svg @@ -1,22 +1,23 @@ - - - - - - - - + + + + + + + + + - + - + - - + + diff --git a/fe/src/app/event-maps/[id]/components/MapComponent.tsx b/fe/src/app/event-maps/[id]/components/MapComponent.tsx index 6f07818..cf19da0 100644 --- a/fe/src/app/event-maps/[id]/components/MapComponent.tsx +++ b/fe/src/app/event-maps/[id]/components/MapComponent.tsx @@ -24,12 +24,24 @@ export default function MapComponent({ px, py }: MapComponentProps) { isSelected: boolean = false ) => { const iconSize = isSelected ? 44 : 36; + let textColor, textShadow; + + if (level === 1) { + textColor = "#000000"; // 검정색 글씨 + textShadow = + "-1px 0px #FFFFFF, 0px 1px #FFFFFF, 1px 0px #FFFFFF, 0px -1px #FFFFFF"; // 흰색 텍스트 쉐도우 테두리 + } else { + textColor = "#FA8980"; // 살구색 글씨 + textShadow = + "-1px 0px #FFFFFF, 0px 1px #FFFFFF, 1px 0px #FFFFFF, 0px -1px #FFFFFF"; // 흰색 텍스트 쉐도우 테두리 + } + return { content: `
-
- ${placeName} -
-
`, +
+ ${placeName} +
+ `, size: new window.naver.maps.Size(iconSize, iconSize), anchor: new window.naver.maps.Point(iconSize / 2, iconSize + 15), // 라벨이 포함되므로 앵커 포지션 조정 }; From b01fdc2b2e55fb623d65d58df0f67c108c8a5e22 Mon Sep 17 00:00:00 2001 From: choihooo Date: Thu, 14 Nov 2024 02:40:34 +0900 Subject: [PATCH 04/13] =?UTF-8?q?[feature]=20=ED=88=B4=ED=8C=81=20api=20?= =?UTF-8?q?=EC=9E=84=EC=8B=9C=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dh.html | 194 ++++++++++++++++++ fe/public/svg/dropdown.svg | 3 + fe/public/svg/people.svg | 4 + fe/public/svg/seeMore.svg | 3 + .../[id]/components/MapComponent.tsx | 194 +++++++++++++++++- fe/src/app/event-maps/[id]/types/types.ts | 6 + 6 files changed, 395 insertions(+), 9 deletions(-) create mode 100644 dh.html create mode 100644 fe/public/svg/dropdown.svg create mode 100644 fe/public/svg/people.svg create mode 100644 fe/public/svg/seeMore.svg diff --git a/dh.html b/dh.html new file mode 100644 index 0000000..df3268f --- /dev/null +++ b/dh.html @@ -0,0 +1,194 @@ + + + + + + Dropdown Interaction + + +
+
+
+
베이커리
+
더보기
+
+
+ 맛있는 빵집 +
+
+ +
+
+ + 3 +
+
+ 이어령이어령, 이어령이어령, 김규리, 김규리, 김규리 +
+ +
+ + +
+ + + + diff --git a/fe/public/svg/dropdown.svg b/fe/public/svg/dropdown.svg new file mode 100644 index 0000000..732ef4b --- /dev/null +++ b/fe/public/svg/dropdown.svg @@ -0,0 +1,3 @@ + + + diff --git a/fe/public/svg/people.svg b/fe/public/svg/people.svg new file mode 100644 index 0000000..c2e58f7 --- /dev/null +++ b/fe/public/svg/people.svg @@ -0,0 +1,4 @@ + + + + diff --git a/fe/public/svg/seeMore.svg b/fe/public/svg/seeMore.svg new file mode 100644 index 0000000..c325e79 --- /dev/null +++ b/fe/public/svg/seeMore.svg @@ -0,0 +1,3 @@ + + + diff --git a/fe/src/app/event-maps/[id]/components/MapComponent.tsx b/fe/src/app/event-maps/[id]/components/MapComponent.tsx index cf19da0..bfacadc 100644 --- a/fe/src/app/event-maps/[id]/components/MapComponent.tsx +++ b/fe/src/app/event-maps/[id]/components/MapComponent.tsx @@ -7,6 +7,53 @@ interface MapComponentProps { py: number; } +interface NonMember { + nonMemberId: number; + name: string; + profileSvg: string; +} + +interface PingData { + placeName: string; + url: string; + nonMembers: NonMember[]; +} + +const transformPingData = (ping: any): PingData => ({ + placeName: ping.placeName, + url: ping.url, + nonMembers: ping.nonMembers.map( + (member: { profileSvg: string; nonMemberId: number; name: string }) => ({ + ...member, + profileSvg: member.profileSvg || "https://default-image.svg", // 기본 이미지 URL 추가 + }) + ), +}); + +const setupToggleDropdown = (): void => { + window.toggleDropdown = (): void => { + const dropdown: HTMLElement | null = + document.getElementById("dropdownExtended"); + const dropdownIcon: HTMLImageElement | null = document.querySelector( + 'img[src="/svg/dropdown.svg"]' + ); + const namesShort: HTMLElement | null = + document.querySelector(".names-short"); + + if (!dropdown || !dropdownIcon || !namesShort) return; // 요소가 없으면 함수를 실행하지 않습니다. + + if (dropdown.style.display === "none") { + dropdown.style.display = "block"; + dropdownIcon.style.transform = "rotate(0deg)"; + namesShort.style.opacity = "0"; // CSS 속성은 문자열로 처리해야 합니다. + } else { + dropdown.style.display = "none"; + dropdownIcon.style.transform = "rotate(180deg)"; + namesShort.style.opacity = "1"; // CSS 속성은 문자열로 처리해야 합니다. + } + }; +}; + export default function MapComponent({ px, py }: MapComponentProps) { const mapRef = useRef(null); const mapInstanceRef = useRef(null); @@ -23,7 +70,8 @@ export default function MapComponent({ px, py }: MapComponentProps) { placeName: string, isSelected: boolean = false ) => { - const iconSize = isSelected ? 44 : 36; + const iconSize_w = isSelected ? 35 : 28; + const iconSize_h = isSelected ? 40 : 32; let textColor, textShadow; if (level === 1) { @@ -37,16 +85,21 @@ export default function MapComponent({ px, py }: MapComponentProps) { } return { - content: `
-
+ content: `
+
${placeName}
`, - size: new window.naver.maps.Size(iconSize, iconSize), - anchor: new window.naver.maps.Point(iconSize / 2, iconSize + 15), // 라벨이 포함되므로 앵커 포지션 조정 + size: new window.naver.maps.Size(iconSize_w, iconSize_h), + anchor: new window.naver.maps.Point(iconSize_w / 2, iconSize_h + 15), // 라벨이 포함되므로 앵커 포지션 조정 }; }; + useEffect(() => { + setupToggleDropdown(); + // 마커와 이벤트 리스너 생성 코드 이하 생략 + }, []); + useEffect(() => { const initializeMap = () => { if (!mapInstanceRef.current && window.naver && mapRef.current) { @@ -82,7 +135,8 @@ export default function MapComponent({ px, py }: MapComponentProps) { markersRef.current = []; customMarkers.forEach((ping, index) => { - const markerOptions: naver.maps.MarkerOptions = { + const transformedPing = transformPingData(ping); + const markerOptions = { position: new window.naver.maps.LatLng(ping.py, ping.px), map: mapInstanceRef.current as naver.maps.Map, icon: getHtmlIconByLevel(ping.iconLevel, ping.placeName, false), @@ -115,14 +169,136 @@ export default function MapComponent({ px, py }: MapComponentProps) { if (infoWindowRef.current) { infoWindowRef.current.close(); } + const infoWindowContent = (data: PingData) => ` +
+
+
+
${data.placeName}
+ +
+
+ ${data.nonMembers.map((member) => member.name).join(", ")} +
+
+ +
+
+ + ${data.nonMembers.length} +
+
+ ${data.nonMembers.map((member) => member.name).join(", ")} +
+ +
+ + +
+`; + const infoWindow = new window.naver.maps.InfoWindow({ - content: `
- ${ping.placeName}
-
`, + content: infoWindowContent(transformedPing), // API에서 받은 데이터를 infoWindowContent 함수에 전달 borderWidth: 0, backgroundColor: "transparent", disableAnchor: true, }); + if (mapInstanceRef.current) { infoWindow.open(mapInstanceRef.current, marker); } diff --git a/fe/src/app/event-maps/[id]/types/types.ts b/fe/src/app/event-maps/[id]/types/types.ts index a239088..ca4c3c9 100644 --- a/fe/src/app/event-maps/[id]/types/types.ts +++ b/fe/src/app/event-maps/[id]/types/types.ts @@ -1,3 +1,9 @@ +declare global { + interface Window { + toggleDropdown: () => void; + } +} + export interface Location { latitude: number; longitude: number; From 696a23d28fc13aa3dcd7a3939c42405684ad3094 Mon Sep 17 00:00:00 2001 From: choihooo Date: Thu, 14 Nov 2024 23:58:51 +0900 Subject: [PATCH 05/13] =?UTF-8?q?[feature]=20=EC=95=84=EC=9D=B4=EC=BD=98?= =?UTF-8?q?=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F?= =?UTF-8?q?=20=ED=94=84=EC=82=AC=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fe/public/pin/recommend.svg | 7 +++ fe/public/svg/bulb.svg | 4 ++ fe/public/svg/edit.svg | 6 +- fe/public/svg/my-location.svg | 12 ++-- fe/public/svg/refresh.svg | 4 +- fe/public/svg/share.svg | 12 ++-- .../[id]/components/BottomDrawer.tsx | 62 ++++++++----------- .../[id]/components/MapComponent.tsx | 4 +- fe/src/app/event-maps/[id]/page.tsx | 14 +++-- 9 files changed, 65 insertions(+), 60 deletions(-) create mode 100644 fe/public/pin/recommend.svg create mode 100644 fe/public/svg/bulb.svg diff --git a/fe/public/pin/recommend.svg b/fe/public/pin/recommend.svg new file mode 100644 index 0000000..4b1356a --- /dev/null +++ b/fe/public/pin/recommend.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/fe/public/svg/bulb.svg b/fe/public/svg/bulb.svg new file mode 100644 index 0000000..1d2d153 --- /dev/null +++ b/fe/public/svg/bulb.svg @@ -0,0 +1,4 @@ + + + + diff --git a/fe/public/svg/edit.svg b/fe/public/svg/edit.svg index 8e1f26b..9a6c01c 100644 --- a/fe/public/svg/edit.svg +++ b/fe/public/svg/edit.svg @@ -1,5 +1,5 @@ - - - + + + diff --git a/fe/public/svg/my-location.svg b/fe/public/svg/my-location.svg index acbbe81..4805a59 100644 --- a/fe/public/svg/my-location.svg +++ b/fe/public/svg/my-location.svg @@ -1,9 +1,9 @@ - - - - - - + + + + + + diff --git a/fe/public/svg/refresh.svg b/fe/public/svg/refresh.svg index 30a2667..9342b4a 100644 --- a/fe/public/svg/refresh.svg +++ b/fe/public/svg/refresh.svg @@ -1,5 +1,5 @@ - - + + diff --git a/fe/public/svg/share.svg b/fe/public/svg/share.svg index c9fafe9..9c69c9f 100644 --- a/fe/public/svg/share.svg +++ b/fe/public/svg/share.svg @@ -1,7 +1,7 @@ - - - - - - + + + + + + diff --git a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx index ccc1ebe..492194f 100644 --- a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx +++ b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx @@ -7,6 +7,7 @@ import { useMarkerStore } from "../load-mappin/stores/useMarkerStore"; interface NonMember { nonMemberId: number; name: string; + profileSvg: string; } interface Ping { @@ -31,45 +32,23 @@ export default function BottomDrawer({ const [eventName, setEventName] = useState(initialEventName); const [selectedButton, setSelectedButton] = useState(null); const [nonMembers, setNonMembers] = useState(initialNonMembers); - const [memberProfiles, setMemberProfiles] = useState<{ - [key: number]: string; - }>({}); const [allPings, setAllPings] = useState([]); const { setCustomMarkers } = useMarkerStore(); const moveToLocation = useLocationStore((state) => state.moveToLocation); const router = useRouter(); - const profileImagesRef = useRef([ - "/profile/profil1.svg", - "/profile/profil2.svg", - "/profile/profil3.svg", - "/profile/profil4.svg", - ]); const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL; - useEffect(() => { - const profiles = nonMembers.reduce( - (acc, member) => { - const randomImage = - profileImagesRef.current[ - Math.floor(Math.random() * profileImagesRef.current.length) - ]; - acc[member.nonMemberId] = randomImage; - return acc; - }, - {} as { [key: number]: string } - ); - setMemberProfiles(profiles); - }, [nonMembers]); - useEffect(() => { const fetchAllPings = async () => { try { const response = await fetch(`${apiUrl}/nonmembers/pings?uuid=${id}`); if (response.ok) { const data = await response.json(); - setAllPings(data.pings || []); - setCustomMarkers(data.pings || []); + setEventName(data.eventName || ""); // eventName이 없는 경우 빈 문자열 설정 + setNonMembers(data.nonMembers || []); // nonMembers가 없는 경우 빈 배열 설정 + setAllPings(data.pings || []); // pings가 없는 경우 빈 배열 설정 + setCustomMarkers(data.pings || []); // customMarkers가 없는 경우 빈 배열 설정 } } catch (error) { console.log("Error:", error); @@ -149,7 +128,6 @@ export default function BottomDrawer({ if (response.ok) { const data = await response.json(); - console.log(data); setEventName(data.eventName); setNonMembers(data.nonMembers); setAllPings(data.pings || []); @@ -195,14 +173,17 @@ export default function BottomDrawer({ } }} > -
+
+
+
-
{eventName}
+
{eventName}
+ {selectedButton !== null ? (
{data && ( @@ -142,7 +143,10 @@ export default function Page() { className="w-full h-[218px] fixed bottom-0 z-10" > ({ + ...member, + profileSvg: member.profileSvg || "https://default-image.svg", // 기본값 추가 + }))} eventName={data.eventName} id={parsedId} /> From d74a2d0dea67db2168bfc71c8305ace1a851e18b Mon Sep 17 00:00:00 2001 From: choihooo Date: Sun, 17 Nov 2024 13:51:09 +0900 Subject: [PATCH 06/13] =?UTF-8?q?[feature]=20=EA=B0=80=EA=B2=8C=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fe/src/app/event-maps/[id]/components/MapComponent.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fe/src/app/event-maps/[id]/components/MapComponent.tsx b/fe/src/app/event-maps/[id]/components/MapComponent.tsx index 9000ff8..2d8727e 100644 --- a/fe/src/app/event-maps/[id]/components/MapComponent.tsx +++ b/fe/src/app/event-maps/[id]/components/MapComponent.tsx @@ -17,6 +17,7 @@ interface PingData { placeName: string; url: string; nonMembers: NonMember[]; + type: string; } const transformPingData = (ping: any): PingData => ({ @@ -28,6 +29,7 @@ const transformPingData = (ping: any): PingData => ({ profileSvg: member.profileSvg || "https://default-image.svg", // 기본 이미지 URL 추가 }) ), + type: ping.type, }); const setupToggleDropdown = (): void => { @@ -187,7 +189,7 @@ export default function MapComponent({ px, py }: MapComponentProps) { justify-content: space-between; " > -
${data.placeName}
+
${data.type}
From 6218909b6560974c845fd7f1f92a7fb32dbb11b7 Mon Sep 17 00:00:00 2001 From: choihooo Date: Sun, 17 Nov 2024 14:17:13 +0900 Subject: [PATCH 07/13] =?UTF-8?q?[feature]=20=EB=B0=94=ED=85=80=20?= =?UTF-8?q?=EB=93=9C=EB=A1=9C=EC=9B=8C=203=EB=8B=A8=EA=B3=84=EB=A1=9C=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[id]/components/BottomDrawer.tsx | 2 +- fe/src/app/event-maps/[id]/hooks/useDrawer.ts | 11 ++++--- fe/src/app/event-maps/[id]/page.tsx | 31 ++++++++++--------- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx index 492194f..2a6538b 100644 --- a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx +++ b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx @@ -165,7 +165,7 @@ export default function BottomDrawer({
{ if (e.key === "Enter" || e.key === " ") { diff --git a/fe/src/app/event-maps/[id]/hooks/useDrawer.ts b/fe/src/app/event-maps/[id]/hooks/useDrawer.ts index b78f155..71484f8 100644 --- a/fe/src/app/event-maps/[id]/hooks/useDrawer.ts +++ b/fe/src/app/event-maps/[id]/hooks/useDrawer.ts @@ -2,21 +2,22 @@ import { useState } from "react"; import { useSpring } from "@react-spring/web"; const useDrawer = () => { + const stopPoints = [-540, 0, 130]; // Top, middle, and bottom positions const [isOpen, setIsOpen] = useState(false); - const [{ y }, api] = useSpring(() => ({ y: 0 })); + const [{ y }, api] = useSpring(() => ({ y: stopPoints[2] })); // Initialize at closed position const openDrawer = () => { setIsOpen(true); - api.start({ y: 0 }); + api.start({ y: stopPoints[0] }); // Move to the top }; const closeDrawer = () => { setIsOpen(false); - api.start({ y: 130 }); + api.start({ y: stopPoints[2] }); // Move to the bottom }; const setPosition = (newY: number) => { - const limitedY = Math.min(Math.max(newY, 0), 130); + const limitedY = Math.max(Math.min(newY, stopPoints[2]), stopPoints[0]); // Clamp y value api.start({ y: limitedY }); }; @@ -26,7 +27,7 @@ const useDrawer = () => { openDrawer, closeDrawer, setPosition, - api, // 필요한 경우 외부에서 제어할 수 있도록 spring API를 반환 + stopPoints, }; }; diff --git a/fe/src/app/event-maps/[id]/page.tsx b/fe/src/app/event-maps/[id]/page.tsx index ff626cc..aa0088a 100644 --- a/fe/src/app/event-maps/[id]/page.tsx +++ b/fe/src/app/event-maps/[id]/page.tsx @@ -2,7 +2,6 @@ import React, { useEffect, useState } from "react"; import { useParams, useRouter } from "next/navigation"; -import Image from "next/image"; import { a } from "@react-spring/web"; import { useDrag } from "@use-gesture/react"; import MapComponent from "./components/MapComponent"; @@ -36,7 +35,7 @@ interface Data { } export default function Page() { - const { y, openDrawer, closeDrawer, setPosition } = useDrawer(); + const { y, openDrawer, closeDrawer, setPosition, stopPoints } = useDrawer(); const { id } = useParams(); const parsedId = Array.isArray(id) ? id[0] : id; const [data, setData] = useState(null); @@ -46,12 +45,8 @@ export default function Page() { const router = useRouter(); useEffect(() => { - console.log("useEffect triggered with id:", id); - const fetchData = async () => { try { - console.log("Fetching data for id:", id); - const response = await fetch( `${process.env.NEXT_PUBLIC_API_BASE_URL}/nonmembers/pings?uuid=${id}`, { @@ -65,14 +60,11 @@ export default function Page() { if (response.ok) { const result = await response.json(); setData(result); - console.log("API Response Data:", JSON.stringify(result, null, 2)); if (result.px && result.py) { - console.log("Moving to location:", result.py, result.px); moveToLocation(result.py, result.px); } if (result.pings) { - console.log("Setting custom markers:", result.pings); setCustomMarkers(result.pings); } } else { @@ -107,10 +99,16 @@ export default function Page() { const bind = useDrag( ({ last, movement: [, my], memo = y.get() }) => { if (last) { - if (my + memo > 100) { - closeDrawer(); - } else { + const newY = my + memo; + const closestStopPoint = stopPoints.reduce((prev, curr) => + Math.abs(curr - newY) < Math.abs(prev - newY) ? curr : prev + ); + setPosition(closestStopPoint); + + if (closestStopPoint === stopPoints[0]) { openDrawer(); + } else if (closestStopPoint === stopPoints[2]) { + closeDrawer(); } } else { setPosition(my + memo); @@ -139,13 +137,16 @@ export default function Page() { `translateY(${val}px)`), // Use translateY from y value + touchAction: "none", + }} + className="w-full h-[218px] fixed bottom-0 z-10 bg-white shadow-lg rounded-t-lg" > ({ ...member, - profileSvg: member.profileSvg || "https://default-image.svg", // 기본값 추가 + profileSvg: member.profileSvg || "/profile/default.svg", }))} eventName={data.eventName} id={parsedId} From 1a3ab52cd861c9262d232de7f67cfdb9651cf3be Mon Sep 17 00:00:00 2001 From: choihooo Date: Thu, 21 Nov 2024 17:25:58 +0900 Subject: [PATCH 08/13] =?UTF-8?q?[feature]=20=EB=B0=94=ED=85=80=20?= =?UTF-8?q?=EC=8B=9C=ED=8A=B8=20=EB=86=92=EC=9D=B4=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EB=B0=8F=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[id]/components/BottomDrawer.tsx | 199 +++++------------- .../[id]/components/ButtonComponents.tsx | 57 +++++ .../[id]/components/LocationButton.tsx | 18 ++ .../[id]/components/RecommendButton.tsx | 19 ++ .../event-maps/[id]/components/StoreItem.tsx | 25 +++ fe/src/app/event-maps/[id]/hooks/useDrawer.ts | 2 +- 6 files changed, 169 insertions(+), 151 deletions(-) create mode 100644 fe/src/app/event-maps/[id]/components/ButtonComponents.tsx create mode 100644 fe/src/app/event-maps/[id]/components/LocationButton.tsx create mode 100644 fe/src/app/event-maps/[id]/components/RecommendButton.tsx create mode 100644 fe/src/app/event-maps/[id]/components/StoreItem.tsx diff --git a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx index 2a6538b..08b4754 100644 --- a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx +++ b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx @@ -1,8 +1,16 @@ -import React, { useState, useEffect, useRef } from "react"; -import Image from "next/image"; +import React, { useState, useEffect } from "react"; import { useRouter } from "next/navigation"; +import Image from "next/image"; import { useLocationStore } from "../stores/useLocationStore"; import { useMarkerStore } from "../load-mappin/stores/useMarkerStore"; +import RecommendButton from "./RecommendButton"; +import { + ShareButton, + RefreshButton, + EditButton, + MemberButton, +} from "./ButtonComponents"; +import StoreItem from "./StoreItem"; interface NonMember { nonMemberId: number; @@ -24,12 +32,12 @@ interface BottomDrawerProps { id: string; } -export default function BottomDrawer({ +const BottomDrawer: React.FC = ({ nonMembers: initialNonMembers, eventName: initialEventName, id, -}: BottomDrawerProps) { - const [eventName, setEventName] = useState(initialEventName); +}) => { + const [eventName, setEventName] = useState(initialEventName); const [selectedButton, setSelectedButton] = useState(null); const [nonMembers, setNonMembers] = useState(initialNonMembers); const [allPings, setAllPings] = useState([]); @@ -45,10 +53,10 @@ export default function BottomDrawer({ const response = await fetch(`${apiUrl}/nonmembers/pings?uuid=${id}`); if (response.ok) { const data = await response.json(); - setEventName(data.eventName || ""); // eventName이 없는 경우 빈 문자열 설정 - setNonMembers(data.nonMembers || []); // nonMembers가 없는 경우 빈 배열 설정 - setAllPings(data.pings || []); // pings가 없는 경우 빈 배열 설정 - setCustomMarkers(data.pings || []); // customMarkers가 없는 경우 빈 배열 설정 + setEventName(data.eventName || ""); + setNonMembers(data.nonMembers || []); + setAllPings(data.pings || []); + setCustomMarkers(data.pings || []); } } catch (error) { console.log("Error:", error); @@ -69,63 +77,22 @@ export default function BottomDrawer({ } }; - const handleButtonClick = async (nonMemberId: number) => { - const isDeselect = selectedButton === nonMemberId; - setSelectedButton(isDeselect ? null : nonMemberId); - - if (isDeselect) { - setCustomMarkers(allPings); - } else { - try { - const response = await fetch( - `${apiUrl}/nonmembers/pings/${nonMemberId}`, - { method: "GET", headers: { "Content-Type": "application/json" } } - ); - - if (response.ok) { - const data = await response.json(); - const filteredPings = data.pings.map((ping: Ping) => ({ - ...ping, - iconLevel: 1, - })); - setCustomMarkers(filteredPings); - } else { - console.log("Failed to fetch data:", response.status); - } - } catch (error) { - console.log("Error:", error); - } - } + const handleButtonClick = (nonMemberId: number) => { + const isSelected = selectedButton === nonMemberId; + setSelectedButton(isSelected ? null : nonMemberId); + const pingsToShow = isSelected + ? allPings + : allPings.filter((ping) => ping.iconLevel === 1); + setCustomMarkers(pingsToShow); }; - const handleAddButtonClick = () => { router.push(`/event-maps/${id}/load-mappin`); }; - - const handleEditBtn = () => { - if (selectedButton !== null) { - router.push(`/event-maps/${id}/${selectedButton}`); - } - }; - - const handleShare = () => { - if (navigator.share) { - navigator.share({ url: window.location.href }).then().catch(); - } else { - alert("이 브라우저에서는 공유 기능을 지원하지 않습니다."); - } - }; - const handleRefresh = async () => { try { const response = await fetch( - `${apiUrl}/nonmembers/pings/refresh-all?uuid=${id}`, - { - method: "GET", - headers: { "Content-Type": "application/json" }, - } + `${apiUrl}/nonmembers/pings/refresh?uuid=${id}` ); - if (response.ok) { const data = await response.json(); setEventName(data.eventName); @@ -140,62 +107,16 @@ export default function BottomDrawer({ } }; - const handleDrawerClick = ( - event: - | React.MouseEvent - | React.KeyboardEvent - ) => { - if (event.type === "keydown") { - const keyboardEvent = event as React.KeyboardEvent; - if (keyboardEvent.key === "Enter" || keyboardEvent.key === " ") { - setSelectedButton(null); - setCustomMarkers(allPings); - } - } else if (event.type === "click") { - const mouseEvent = event as React.MouseEvent; - const target = mouseEvent.target as HTMLElement; - if (!target.closest("button")) { - setSelectedButton(null); - setCustomMarkers(allPings); - } - } - }; - return (
{ - if (e.key === "Enter" || e.key === " ") { - handleDrawerClick(e); - } - }} + onClick={(event: React.MouseEvent) => {}} + onKeyDown={(event: React.KeyboardEvent) => {}} >
- -
-
- +
{eventName}
- + navigator.share({ url: window.location.href })} + /> {selectedButton !== null ? ( - + router.push(`/edit-event/${id}/${selectedButton}`)} + /> ) : ( - + )}
@@ -255,26 +157,23 @@ export default function BottomDrawer({ key={member.nonMemberId} className="w-[68px] h-[90px] flex flex-col justify-between shrink-0" > - +
{member.name}
))}
+ {/* Infinite scroll area */} +
+ {allPings.map((ping, index) => ( + + ))} +
); -} +}; + +export default BottomDrawer; diff --git a/fe/src/app/event-maps/[id]/components/ButtonComponents.tsx b/fe/src/app/event-maps/[id]/components/ButtonComponents.tsx new file mode 100644 index 0000000..0fa6002 --- /dev/null +++ b/fe/src/app/event-maps/[id]/components/ButtonComponents.tsx @@ -0,0 +1,57 @@ +import React from "react"; +import Image from "next/image"; + +interface ButtonProps { + onClick: () => void; +} + +export const ShareButton: React.FC = ({ onClick }) => ( + +); + +export const RefreshButton: React.FC = ({ onClick }) => ( + +); + +export const EditButton: React.FC = ({ onClick }) => ( + +); + +interface MemberButtonProps { + member: { + nonMemberId: number; + name: string; + profileSvg: string; + }; + isSelected: boolean; + onClick: (id: number) => void; +} + +export const MemberButton: React.FC = ({ + member, + isSelected, + onClick, +}) => ( + +); diff --git a/fe/src/app/event-maps/[id]/components/LocationButton.tsx b/fe/src/app/event-maps/[id]/components/LocationButton.tsx new file mode 100644 index 0000000..9a74246 --- /dev/null +++ b/fe/src/app/event-maps/[id]/components/LocationButton.tsx @@ -0,0 +1,18 @@ +import React from "react"; +import Image from "next/image"; + +interface ButtonProps { + onClick: () => void; +} + +const LocationButton: React.FC = ({ onClick }) => ( + +); + +export default LocationButton; diff --git a/fe/src/app/event-maps/[id]/components/RecommendButton.tsx b/fe/src/app/event-maps/[id]/components/RecommendButton.tsx new file mode 100644 index 0000000..f995c12 --- /dev/null +++ b/fe/src/app/event-maps/[id]/components/RecommendButton.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import Image from "next/image"; + +interface RecommendButtonProps { + onClick: () => void; +} + +const RecommendButton: React.FC = ({ onClick }) => ( + +); + +export default RecommendButton; diff --git a/fe/src/app/event-maps/[id]/components/StoreItem.tsx b/fe/src/app/event-maps/[id]/components/StoreItem.tsx new file mode 100644 index 0000000..97d0faa --- /dev/null +++ b/fe/src/app/event-maps/[id]/components/StoreItem.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import Image from "next/image"; + +interface StoreItemProps { + name: string; + type: string; +} + +const StoreItem: React.FC = ({ name, type }) => ( +
+
+ edit +
+
{name}
+
{type}
+
+
+
+ 상세정보 + 화살표 +
+
+); + +export default StoreItem; diff --git a/fe/src/app/event-maps/[id]/hooks/useDrawer.ts b/fe/src/app/event-maps/[id]/hooks/useDrawer.ts index 71484f8..50d7fa8 100644 --- a/fe/src/app/event-maps/[id]/hooks/useDrawer.ts +++ b/fe/src/app/event-maps/[id]/hooks/useDrawer.ts @@ -2,7 +2,7 @@ import { useState } from "react"; import { useSpring } from "@react-spring/web"; const useDrawer = () => { - const stopPoints = [-540, 0, 130]; // Top, middle, and bottom positions + const stopPoints = [-500, 20, 130]; // Top, middle, and bottom positions const [isOpen, setIsOpen] = useState(false); const [{ y }, api] = useSpring(() => ({ y: stopPoints[2] })); // Initialize at closed position From 7881e7cab0b21f83040bf788c1379843d2718960 Mon Sep 17 00:00:00 2001 From: choihooo Date: Thu, 21 Nov 2024 17:34:26 +0900 Subject: [PATCH 09/13] =?UTF-8?q?[feature]=20=ED=98=84=EC=9E=AC=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=EB=B2=84=ED=8A=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[id]/components/BottomDrawer.tsx | 18 +++++++++++++++++- .../[id]/components/LocationButton.tsx | 4 ++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx index 08b4754..9ede44d 100644 --- a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx +++ b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx @@ -11,6 +11,7 @@ import { MemberButton, } from "./ButtonComponents"; import StoreItem from "./StoreItem"; +import LocationButton from "./LocationButton"; interface NonMember { nonMemberId: number; @@ -77,6 +78,18 @@ const BottomDrawer: React.FC = ({ } }; + const handleRecommendClick = () => { + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition( + (position) => { + const { latitude, longitude } = position.coords; + moveToLocation(latitude, longitude); + }, + () => alert("현재 위치 정보를 가져올 수 없습니다.") + ); + } + }; + const handleButtonClick = (nonMemberId: number) => { const isSelected = selectedButton === nonMemberId; setSelectedButton(isSelected ? null : nonMemberId); @@ -116,7 +129,10 @@ const BottomDrawer: React.FC = ({ onKeyDown={(event: React.KeyboardEvent) => {}} >
- + +
+
+
= ({ onClick }) => ( ); From 992d3cd98f179f40a9bcf2c680bbcd7a45783867 Mon Sep 17 00:00:00 2001 From: choihooo Date: Fri, 22 Nov 2024 13:45:39 +0900 Subject: [PATCH 10/13] =?UTF-8?q?[feature]=20=EB=B0=94=ED=85=80=20?= =?UTF-8?q?=EC=8B=9C=ED=8A=B8=20=EA=B8=B0=EC=A2=85=20=EB=B3=84=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fe/public/profile/level1.svg | 23 +++ fe/public/profile/level2.svg | 23 +++ fe/public/profile/level3.svg | 23 +++ fe/public/profile/level4.svg | 23 +++ fe/public/profile/profil1.svg | 14 -- fe/public/profile/profil2.svg | 18 -- fe/public/profile/profil3.svg | 13 -- fe/public/profile/profil4.svg | 23 --- fe/public/svg/recommendCancle.svg | 4 + .../[id]/components/BottomDrawer.tsx | 160 +++++++++++------- .../[id]/components/RecommendActive.tsx | 43 +++++ .../[id]/components/RecommendInActive.tsx | 106 ++++++++++++ .../event-maps/[id]/components/StoreItem.tsx | 46 +++-- fe/src/app/event-maps/[id]/hooks/useDrawer.ts | 38 ++++- fe/tailwind.config.ts | 9 + 15 files changed, 413 insertions(+), 153 deletions(-) create mode 100644 fe/public/profile/level1.svg create mode 100644 fe/public/profile/level2.svg create mode 100644 fe/public/profile/level3.svg create mode 100644 fe/public/profile/level4.svg delete mode 100644 fe/public/profile/profil1.svg delete mode 100644 fe/public/profile/profil2.svg delete mode 100644 fe/public/profile/profil3.svg delete mode 100644 fe/public/profile/profil4.svg create mode 100644 fe/public/svg/recommendCancle.svg create mode 100644 fe/src/app/event-maps/[id]/components/RecommendActive.tsx create mode 100644 fe/src/app/event-maps/[id]/components/RecommendInActive.tsx diff --git a/fe/public/profile/level1.svg b/fe/public/profile/level1.svg new file mode 100644 index 0000000..4c0408c --- /dev/null +++ b/fe/public/profile/level1.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fe/public/profile/level2.svg b/fe/public/profile/level2.svg new file mode 100644 index 0000000..db49621 --- /dev/null +++ b/fe/public/profile/level2.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fe/public/profile/level3.svg b/fe/public/profile/level3.svg new file mode 100644 index 0000000..6159274 --- /dev/null +++ b/fe/public/profile/level3.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fe/public/profile/level4.svg b/fe/public/profile/level4.svg new file mode 100644 index 0000000..9fb321f --- /dev/null +++ b/fe/public/profile/level4.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fe/public/profile/profil1.svg b/fe/public/profile/profil1.svg deleted file mode 100644 index d36dd23..0000000 --- a/fe/public/profile/profil1.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/fe/public/profile/profil2.svg b/fe/public/profile/profil2.svg deleted file mode 100644 index 1d63af7..0000000 --- a/fe/public/profile/profil2.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/fe/public/profile/profil3.svg b/fe/public/profile/profil3.svg deleted file mode 100644 index db47008..0000000 --- a/fe/public/profile/profil3.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/fe/public/profile/profil4.svg b/fe/public/profile/profil4.svg deleted file mode 100644 index 207a4da..0000000 --- a/fe/public/profile/profil4.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fe/public/svg/recommendCancle.svg b/fe/public/svg/recommendCancle.svg new file mode 100644 index 0000000..b986695 --- /dev/null +++ b/fe/public/svg/recommendCancle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx index 9ede44d..f10abe6 100644 --- a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx +++ b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx @@ -1,17 +1,12 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useRef } from "react"; import { useRouter } from "next/navigation"; import Image from "next/image"; import { useLocationStore } from "../stores/useLocationStore"; import { useMarkerStore } from "../load-mappin/stores/useMarkerStore"; import RecommendButton from "./RecommendButton"; -import { - ShareButton, - RefreshButton, - EditButton, - MemberButton, -} from "./ButtonComponents"; -import StoreItem from "./StoreItem"; +import RecommendInActive from "./RecommendInActive"; import LocationButton from "./LocationButton"; +import RecommendActive from "./RecommendActive"; interface NonMember { nonMemberId: number; @@ -25,6 +20,7 @@ interface Ping { px: number; py: number; url: string; + type: string; } interface BottomDrawerProps { @@ -42,9 +38,16 @@ const BottomDrawer: React.FC = ({ const [selectedButton, setSelectedButton] = useState(null); const [nonMembers, setNonMembers] = useState(initialNonMembers); const [allPings, setAllPings] = useState([]); + const [isRecommend, setIsRecommend] = useState(false); + const [neighborhood, setNeighborhood] = useState(""); + const [recommendPings, setRecommendPings] = useState([]); + const { setCustomMarkers } = useMarkerStore(); const moveToLocation = useLocationStore((state) => state.moveToLocation); + const router = useRouter(); + const observer = useRef(); + const lastPingElementRef = useRef(null); const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL; @@ -54,10 +57,12 @@ const BottomDrawer: React.FC = ({ const response = await fetch(`${apiUrl}/nonmembers/pings?uuid=${id}`); if (response.ok) { const data = await response.json(); + console.log(data); setEventName(data.eventName || ""); setNonMembers(data.nonMembers || []); setAllPings(data.pings || []); setCustomMarkers(data.pings || []); + setNeighborhood(data.neighborhood); } } catch (error) { console.log("Error:", error); @@ -78,18 +83,64 @@ const BottomDrawer: React.FC = ({ } }; - const handleRecommendClick = () => { - if (navigator.geolocation) { - navigator.geolocation.getCurrentPosition( - (position) => { - const { latitude, longitude } = position.coords; - moveToLocation(latitude, longitude); - }, - () => alert("현재 위치 정보를 가져올 수 없습니다.") - ); + const handleRecommendClick = async () => { + let Km = 1.0; + let found = false; + + while (Km <= 5.0) { + try { + const response = await fetch( + `${apiUrl}/nonmembers/pings/recommend?uuid=${id}&radiusInKm=${Km}`, + { method: "GET" } + ); + if (response.ok) { + const data = await response.json(); + console.log( + `Recommended data fetched successfully for ${Km} km:`, + data + ); + + if (data.recommendPings && data.recommendPings.length >= 5) { + console.log( + "Found more than or equal to 5 pings at radius:", + Km, + "km" + ); + setRecommendPings(data.recommendPings); // 데이터를 상태에 저장 + found = true; + break; + } + } else { + console.error( + "Failed to fetch recommended data, status:", + response.status + ); + } + } catch (error) { + console.error("Error fetching recommended data:", error); + } + + Km += 1.0; } + + setIsRecommend(found); // 상태 업데이트는 검색 결과에 따라 결정 }; + const handleAddToMorphing = () => { + console.log("앙"); + if (recommendPings.length > 0) { + console.log("Adding selected pings to morphing...", recommendPings); + console.log("버튼눌림"); + setCustomMarkers(recommendPings); + setIsRecommend(false); + } else { + console.log("No pings available to add to morphing."); + } + }; + + const handleRecommendCancle = () => { + setIsRecommend(false); + }; const handleButtonClick = (nonMemberId: number) => { const isSelected = selectedButton === nonMemberId; setSelectedButton(isSelected ? null : nonMemberId); @@ -101,6 +152,7 @@ const BottomDrawer: React.FC = ({ const handleAddButtonClick = () => { router.push(`/event-maps/${id}/load-mappin`); }; + const handleRefresh = async () => { try { const response = await fetch( @@ -108,6 +160,7 @@ const BottomDrawer: React.FC = ({ ); if (response.ok) { const data = await response.json(); + console.log(data); setEventName(data.eventName); setNonMembers(data.nonMembers); setAllPings(data.pings || []); @@ -128,9 +181,11 @@ const BottomDrawer: React.FC = ({ onClick={(event: React.MouseEvent) => {}} onKeyDown={(event: React.KeyboardEvent) => {}} > -
- -
+ {!isRecommend && ( +
+ +
+ )}
@@ -143,51 +198,26 @@ const BottomDrawer: React.FC = ({ className="mt-[12px]" />
-
-
{eventName}
-
- navigator.share({ url: window.location.href })} - /> - {selectedButton !== null ? ( - router.push(`/edit-event/${id}/${selectedButton}`)} - /> - ) : ( - - )} -
-
-
-
- -
- {nonMembers.map((member) => ( -
- -
{member.name}
-
- ))} -
- {/* Infinite scroll area */} -
- {allPings.map((ping, index) => ( - - ))} -
+ {isRecommend ? ( + + ) : ( + + )}
); }; diff --git a/fe/src/app/event-maps/[id]/components/RecommendActive.tsx b/fe/src/app/event-maps/[id]/components/RecommendActive.tsx new file mode 100644 index 0000000..3cd081a --- /dev/null +++ b/fe/src/app/event-maps/[id]/components/RecommendActive.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import Image from "next/image"; + +interface RecommendActiveProps { + neighborhood: string; + handleRecommendCancle: () => void; + handleAddToMorphing: () => void; +} + +const RecommendActive: React.FC = ({ + neighborhood, + handleRecommendCancle, + handleAddToMorphing, +}) => { + return ( +
+
+
+
우리끼리만 보는
+
{neighborhood} 인기 공간
+
+ +
+
+ +
+
+ ); +}; + +export default RecommendActive; diff --git a/fe/src/app/event-maps/[id]/components/RecommendInActive.tsx b/fe/src/app/event-maps/[id]/components/RecommendInActive.tsx new file mode 100644 index 0000000..6eb2207 --- /dev/null +++ b/fe/src/app/event-maps/[id]/components/RecommendInActive.tsx @@ -0,0 +1,106 @@ +import React from "react"; +import Image from "next/image"; +import { + ShareButton, + RefreshButton, + EditButton, + MemberButton, +} from "./ButtonComponents"; +import StoreItem from "./StoreItem"; + +interface NonMember { + nonMemberId: number; + name: string; + profileSvg: string; +} + +interface Ping { + iconLevel: number; + placeName: string; + px: number; + py: number; + url: string; + type: string; +} + +interface RecommendInActiveProps { + nonMembers: NonMember[]; + handleButtonClick: (nonMemberId: number) => void; + handleAddButtonClick: () => void; + allPings: Ping[]; + lastPingElementRef: React.RefObject; + selectedButton: number | null; + handleRefresh: () => void; + eventName: string; + id: string; + router: any; // Type based on your routing library +} + +const RecommendInActive: React.FC = ({ + nonMembers, + handleButtonClick, + handleAddButtonClick, + allPings, + lastPingElementRef, + selectedButton, + handleRefresh, + eventName, + id, + router, +}) => ( + <> +
+
{eventName}
+
+ navigator.share({ url: window.location.href })} + /> + {selectedButton !== null ? ( + router.push(`/event-maps/${id}/${selectedButton}`)} + /> + ) : ( + + )} +
+
+
+
+ +
+ {nonMembers.map((member) => ( +
+ handleButtonClick(member.nonMemberId)} + /> +
{member.name}
+
+ ))} +
+
+ {allPings.map((ping, index) => ( + + ))} +
+ +); + +export default RecommendInActive; diff --git a/fe/src/app/event-maps/[id]/components/StoreItem.tsx b/fe/src/app/event-maps/[id]/components/StoreItem.tsx index 97d0faa..e487e35 100644 --- a/fe/src/app/event-maps/[id]/components/StoreItem.tsx +++ b/fe/src/app/event-maps/[id]/components/StoreItem.tsx @@ -1,25 +1,45 @@ -import React from "react"; +import React, { forwardRef } from "react"; import Image from "next/image"; interface StoreItemProps { name: string; type: string; + url: string; + iconLevel: number; } -const StoreItem: React.FC = ({ name, type }) => ( -
-
- edit -
-
{name}
-
{type}
+const StoreItem = forwardRef( + ({ name, type, url, iconLevel }, ref) => ( +
+
+ { + e.currentTarget.src = "/profile/default.svg"; // Fallback to a default image + }} + alt="edit" + width={40} + height={40} + /> +
+
{name}
+
{type}
+
+
-
- 상세정보 - 화살표 -
-
+ ) ); export default StoreItem; diff --git a/fe/src/app/event-maps/[id]/hooks/useDrawer.ts b/fe/src/app/event-maps/[id]/hooks/useDrawer.ts index 50d7fa8..f6cfe15 100644 --- a/fe/src/app/event-maps/[id]/hooks/useDrawer.ts +++ b/fe/src/app/event-maps/[id]/hooks/useDrawer.ts @@ -1,23 +1,47 @@ -import { useState } from "react"; +import { useState, useEffect } from "react"; import { useSpring } from "@react-spring/web"; const useDrawer = () => { - const stopPoints = [-500, 20, 130]; // Top, middle, and bottom positions + const [stopPoints, setStopPoints] = useState([]); + + // 뷰포트 크기에 따른 스톱 포인트 계산 + const updateStopPoints = () => { + let stopPointsPercent; + if (window.matchMedia("(max-height: 668px)").matches) { + // 작은 기종 + stopPointsPercent = [55, 30, 0, -20]; + } else if (window.matchMedia("(max-height: 850px)").matches) { + // 중간 기종 + stopPointsPercent = [60, 24, 0, -15.5]; + } else { + // 큰 기종 + stopPointsPercent = [57.5, 22.5, 0, -14]; + } + const vh = window.innerHeight; + setStopPoints(stopPointsPercent.map((p) => vh * (p / 100) * -1)); + }; + + useEffect(() => { + updateStopPoints(); // 초기 설정 + window.addEventListener("resize", updateStopPoints); // 창 크기 변경에 따라 업데이트 + return () => window.removeEventListener("resize", updateStopPoints); + }, []); + const [isOpen, setIsOpen] = useState(false); - const [{ y }, api] = useSpring(() => ({ y: stopPoints[2] })); // Initialize at closed position + const [{ y }, api] = useSpring(() => ({ y: stopPoints[3] || 0 })); const openDrawer = () => { setIsOpen(true); - api.start({ y: stopPoints[0] }); // Move to the top + api.start({ y: stopPoints[0] }); }; const closeDrawer = () => { setIsOpen(false); - api.start({ y: stopPoints[2] }); // Move to the bottom + api.start({ y: stopPoints[2] }); }; const setPosition = (newY: number) => { - const limitedY = Math.max(Math.min(newY, stopPoints[2]), stopPoints[0]); // Clamp y value + const limitedY = Math.max(Math.min(newY, stopPoints[3]), stopPoints[0]); // y 값 제한 api.start({ y: limitedY }); }; @@ -26,7 +50,7 @@ const useDrawer = () => { isOpen, openDrawer, closeDrawer, - setPosition, + setPosition, // 함수를 반환 객체에 추가 stopPoints, }; }; diff --git a/fe/tailwind.config.ts b/fe/tailwind.config.ts index 9bed041..1446c31 100644 --- a/fe/tailwind.config.ts +++ b/fe/tailwind.config.ts @@ -171,6 +171,15 @@ const config: Config = { animation: { fadein: "fadein 2s ease-in-out", }, + scrollbarHide: { + "&::-webkit-scrollbar": { + display: "none", + }, + "&": { + "-ms-overflow-style": "none", + "scrollbar-width": "none", + }, + }, }, plugins: [require("tailwind-scrollbar-hide")], }; From 12585c16778e31a876b33ced7699ed6b3cfc5f38 Mon Sep 17 00:00:00 2001 From: choihooo Date: Sun, 24 Nov 2024 01:10:48 +0900 Subject: [PATCH 11/13] =?UTF-8?q?[feature]=20=EB=AA=A8=ED=95=91=20?= =?UTF-8?q?=EB=AA=B0=EB=9E=98=EB=B3=B4=EA=B8=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fe/public/pin/{recommend.svg => .svg} | 2 +- fe/public/pin/recommendPing.svg | 7 + fe/public/profile/recommendProfile.svg | 15 ++ .../[id]/components/BottomDrawer.tsx | 208 +++++++++++++----- .../[id]/components/MapComponent.tsx | 17 +- .../[id]/components/RecommendActive.tsx | 90 ++++++-- fe/src/app/event-maps/[id]/hooks/useDrawer.ts | 6 +- .../[id]/load-mappin/stores/useMarkerStore.ts | 19 +- 8 files changed, 276 insertions(+), 88 deletions(-) rename fe/public/pin/{recommend.svg => .svg} (87%) create mode 100644 fe/public/pin/recommendPing.svg create mode 100644 fe/public/profile/recommendProfile.svg diff --git a/fe/public/pin/recommend.svg b/fe/public/pin/.svg similarity index 87% rename from fe/public/pin/recommend.svg rename to fe/public/pin/.svg index 4b1356a..4243960 100644 --- a/fe/public/pin/recommend.svg +++ b/fe/public/pin/.svg @@ -3,5 +3,5 @@ - + diff --git a/fe/public/pin/recommendPing.svg b/fe/public/pin/recommendPing.svg new file mode 100644 index 0000000..4243960 --- /dev/null +++ b/fe/public/pin/recommendPing.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/fe/public/profile/recommendProfile.svg b/fe/public/profile/recommendProfile.svg new file mode 100644 index 0000000..86e2e1c --- /dev/null +++ b/fe/public/profile/recommendProfile.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx index f10abe6..d7fc0fe 100644 --- a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx +++ b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx @@ -21,6 +21,7 @@ interface Ping { py: number; url: string; type: string; + nonMembers: NonMember[]; } interface BottomDrawerProps { @@ -29,6 +30,16 @@ interface BottomDrawerProps { id: string; } +interface RecommendPing { + iconLevel: number; + placeName: string; + sid: string; + px: number; + py: number; + url: string; + type: string; +} + const BottomDrawer: React.FC = ({ nonMembers: initialNonMembers, eventName: initialEventName, @@ -39,8 +50,9 @@ const BottomDrawer: React.FC = ({ const [nonMembers, setNonMembers] = useState(initialNonMembers); const [allPings, setAllPings] = useState([]); const [isRecommend, setIsRecommend] = useState(false); + const [isRecommended, setIsRecommended] = useState(false); const [neighborhood, setNeighborhood] = useState(""); - const [recommendPings, setRecommendPings] = useState([]); + const [nonRecommend, setNonRecommend] = useState(false); const { setCustomMarkers } = useMarkerStore(); const moveToLocation = useLocationStore((state) => state.moveToLocation); @@ -57,11 +69,47 @@ const BottomDrawer: React.FC = ({ const response = await fetch(`${apiUrl}/nonmembers/pings?uuid=${id}`); if (response.ok) { const data = await response.json(); - console.log(data); + let recommendProfile; + if (data.recommendPings && data.recommendPings.length > 0) { + setIsRecommended(true); + console.log(isRecommended); + recommendProfile = (data.recommendPings || []).map( + (ping: RecommendPing) => ({ + iconLevel: 10, // Fixed icon level + nonMembers: [ + { + nonMemberId: -1, + name: "추천 모핑", // Fixed name + profileSvg: "/profile/recommendProfile.svg", // Fixed profileSvg + }, + ], + url: ping.url, + placeName: ping.placeName, + px: ping.px, + py: ping.py, + type: ping.type, + }) + ); + console.log("리코멘트 프로필" + JSON.stringify(recommendProfile)); + } else { + recommendProfile = []; // Default value when no recommendPings + } setEventName(data.eventName || ""); - setNonMembers(data.nonMembers || []); - setAllPings(data.pings || []); - setCustomMarkers(data.pings || []); + setNonMembers([ + ...(recommendProfile[0].nonMembers || []), + ...(data.nonMembers || []), + ]); + console.log("논멤버스" + JSON.stringify(nonMembers)); + setAllPings([ + ...(data.pings || []), // 기존 pings + ...(recommendProfile || []), // recommendProfile 추가 + ]); + + setCustomMarkers([ + ...(data.pings || []), // 기존 pings + ...(recommendProfile || []), // recommendProfile 추가 + ]); + setNeighborhood(data.neighborhood); } } catch (error) { @@ -83,72 +131,124 @@ const BottomDrawer: React.FC = ({ } }; - const handleRecommendClick = async () => { + const handleRecommendAllowClick = () => { + setIsRecommend(true); + }; + + const handleAddToMorphing = async () => { let Km = 1.0; let found = false; - while (Km <= 5.0) { - try { - const response = await fetch( - `${apiUrl}/nonmembers/pings/recommend?uuid=${id}&radiusInKm=${Km}`, - { method: "GET" } + try { + const response = await fetch( + `${apiUrl}/nonmembers/pings/recommend?uuid=${id}&radiusInKm=${Km}`, + { method: "GET" } + ); + if (response.ok) { + const data = await response.json(); + console.log( + `Recommended data fetched successfully for ${Km} km:`, + data ); - if (response.ok) { - const data = await response.json(); + if (data.recommendPings.length == 0) { + setNonRecommend(true); + console.log("없음"); + } + if (data.recommendPings && data.recommendPings.length >= 5) { console.log( - `Recommended data fetched successfully for ${Km} km:`, - data - ); - - if (data.recommendPings && data.recommendPings.length >= 5) { - console.log( - "Found more than or equal to 5 pings at radius:", - Km, - "km" - ); - setRecommendPings(data.recommendPings); // 데이터를 상태에 저장 - found = true; - break; - } - } else { - console.error( - "Failed to fetch recommended data, status:", - response.status + "Found more than or equal to 5 pings at radius:", + Km, + "km" ); + setCustomMarkers(data.recommendPings); + found = true; + // break; + setIsRecommend(found); } - } catch (error) { - console.error("Error fetching recommended data:", error); + } else { + console.error( + "Failed to fetch recommended data, status:", + response.status + ); } - - Km += 1.0; + } catch (error) { + console.error("Error fetching recommended data:", error); } - setIsRecommend(found); // 상태 업데이트는 검색 결과에 따라 결정 + // 상태 업데이트는 검색 결과에 따라 결정 }; - const handleAddToMorphing = () => { - console.log("앙"); - if (recommendPings.length > 0) { - console.log("Adding selected pings to morphing...", recommendPings); - console.log("버튼눌림"); - setCustomMarkers(recommendPings); - setIsRecommend(false); - } else { - console.log("No pings available to add to morphing."); - } - }; + // const handleAddToMorphing = async () => { + // if (recommendPings.length > 0) { + // console.log("Adding selected pings to morphing...", recommendPings); + + // // Prepare payload + // const payload = { + // uuid: id, // UUID는 필요에 따라 동적으로 설정하세요 + // sids: recommendPings.map((ping) => ping.sid), // recommendPings에서 sid 추출 + // }; + + // console.log(payload); + + // try { + // // Send POST request + // const response = await fetch(`${apiUrl}/nonmembers/pings/recommend`, { + // method: "POST", + // headers: { + // "Content-Type": "application/json;charset=UTF-8", + // }, + // body: JSON.stringify(payload), // Convert payload to JSON + // }); + // // Handle response + // if (response.ok) { + // const result = await response.json(); + // console.log("추가:" + JSON.stringify(result)); + + // setIsRecommend(false); + // } else { + // const errorText = await response.text(); // 서버에서 제공하는 오류 메시지 확인 + // console.error( + // "Failed to add to morphing:", + // response.status, + // response.statusText, + // errorText + // ); + // console.error("Failed to add to morphing:", response.statusText); + // } + // } catch (error) { + // console.error("Error adding to morphing:", error); + // } + // } else { + // console.log("No pings available to add to morphing."); + // } + // }; const handleRecommendCancle = () => { setIsRecommend(false); }; + const handleButtonClick = (nonMemberId: number) => { + // 1. 현재 선택 상태를 확인합니다. const isSelected = selectedButton === nonMemberId; + + // 2. 선택 상태를 토글합니다. setSelectedButton(isSelected ? null : nonMemberId); + + // 3. 선택 상태에 따라 pingsToShow를 설정합니다. const pingsToShow = isSelected - ? allPings - : allPings.filter((ping) => ping.iconLevel === 1); + ? [...allPings] // 선택 해제 시 모든 핑을 표시 + : allPings.filter((ping) => + ping.nonMembers.some((member) => member.nonMemberId === nonMemberId) + ); // nonMemberId와 일치하는 항목만 필터링 + + // 4. Zustand의 customMarkers를 업데이트합니다. setCustomMarkers(pingsToShow); + + // 5. 현재 상태를 로그로 확인합니다. + console.log("선택된 버튼:", isSelected ? null : nonMemberId); + console.log("업데이트된 마커:", pingsToShow); }; + const handleAddButtonClick = () => { router.push(`/event-maps/${id}/load-mappin`); }; @@ -181,11 +281,11 @@ const BottomDrawer: React.FC = ({ onClick={(event: React.MouseEvent) => {}} onKeyDown={(event: React.KeyboardEvent) => {}} > - {!isRecommend && ( + {isRecommend === false && isRecommended === false ? (
- +
- )} + ) : null}
@@ -198,11 +298,15 @@ const BottomDrawer: React.FC = ({ className="mt-[12px]" />
+ {isRecommend ? ( ) : ( -
- ${placeName} -
-
`, + content: `
+
+ ${placeName} +
+
`, size: new window.naver.maps.Size(iconSize_w, iconSize_h), anchor: new window.naver.maps.Point(iconSize_w / 2, iconSize_h + 15), // 라벨이 포함되므로 앵커 포지션 조정 }; @@ -304,7 +306,7 @@ export default function MapComponent({ px, py }: MapComponentProps) { if (mapInstanceRef.current) { infoWindow.open(mapInstanceRef.current, marker); } - + console.log(customMarkers); infoWindowRef.current = infoWindow; }); }); @@ -334,6 +336,7 @@ export default function MapComponent({ px, py }: MapComponentProps) { } ); } + console.log(customMarkers); }, [customMarkers]); useEffect(() => { diff --git a/fe/src/app/event-maps/[id]/components/RecommendActive.tsx b/fe/src/app/event-maps/[id]/components/RecommendActive.tsx index 3cd081a..c6490e2 100644 --- a/fe/src/app/event-maps/[id]/components/RecommendActive.tsx +++ b/fe/src/app/event-maps/[id]/components/RecommendActive.tsx @@ -1,41 +1,87 @@ import React from "react"; import Image from "next/image"; +import { Dispatch, SetStateAction } from "react"; interface RecommendActiveProps { neighborhood: string; handleRecommendCancle: () => void; handleAddToMorphing: () => void; + setIsRecommend: Dispatch>; + setNonRecommend: Dispatch>; + nonRecommend: boolean; } const RecommendActive: React.FC = ({ neighborhood, handleRecommendCancle, handleAddToMorphing, + setIsRecommend, + setNonRecommend, + nonRecommend, }) => { return (
-
-
-
우리끼리만 보는
-
{neighborhood} 인기 공간
-
- -
-
- -
+ {/* nonRecommend이 true일 때 다른 UI를 렌더링 */} + {nonRecommend ? ( + <> + {/* 추천 데이터가 있을 때 기본 UI */} +
+
+
근처에 추천할만한 공간이 없어요
+
+ +
+
+ +
+ + ) : ( + <> + {/* 추천 데이터가 있을 때 기본 UI */} +
+
+
우리끼리만 보는
+
+
+ {neighborhood} +
+
인기 공간
+
+
+ +
+
+ +
+ + )}
); }; diff --git a/fe/src/app/event-maps/[id]/hooks/useDrawer.ts b/fe/src/app/event-maps/[id]/hooks/useDrawer.ts index f6cfe15..7b296c4 100644 --- a/fe/src/app/event-maps/[id]/hooks/useDrawer.ts +++ b/fe/src/app/event-maps/[id]/hooks/useDrawer.ts @@ -9,13 +9,13 @@ const useDrawer = () => { let stopPointsPercent; if (window.matchMedia("(max-height: 668px)").matches) { // 작은 기종 - stopPointsPercent = [55, 30, 0, -20]; + stopPointsPercent = [54, 30, 0, -20]; } else if (window.matchMedia("(max-height: 850px)").matches) { // 중간 기종 - stopPointsPercent = [60, 24, 0, -15.5]; + stopPointsPercent = [59, 24, 0, -15.5]; } else { // 큰 기종 - stopPointsPercent = [57.5, 22.5, 0, -14]; + stopPointsPercent = [57, 22.5, 0, -14]; } const vh = window.innerHeight; setStopPoints(stopPointsPercent.map((p) => vh * (p / 100) * -1)); diff --git a/fe/src/app/event-maps/[id]/load-mappin/stores/useMarkerStore.ts b/fe/src/app/event-maps/[id]/load-mappin/stores/useMarkerStore.ts index 760c1db..4878887 100644 --- a/fe/src/app/event-maps/[id]/load-mappin/stores/useMarkerStore.ts +++ b/fe/src/app/event-maps/[id]/load-mappin/stores/useMarkerStore.ts @@ -1,24 +1,37 @@ import { create } from "zustand"; +interface NonMember { + nonMemberId: number; + name: string; +} + interface Ping { iconLevel: number; placeName: string; px: number; py: number; url: string; - nonMembers: { nonMemberId: number; name: string }[]; + nonMembers: NonMember[]; } export type PingWithoutNonMembers = Omit; interface MarkerStoreState { customMarkers: Ping[]; - setCustomMarkers: (pings: Ping[] | PingWithoutNonMembers[]) => void; + setCustomMarkers: ( + pings: Ping[] | PingWithoutNonMembers[] | ((prev: Ping[]) => Ping[]) + ) => void; resetMarkers: () => void; } export const useMarkerStore = create((set) => ({ customMarkers: [], - setCustomMarkers: (pings) => set({ customMarkers: pings as Ping[] }), + setCustomMarkers: (pings) => { + if (typeof pings === "function") { + set((state) => ({ customMarkers: pings(state.customMarkers) })); + } else { + set({ customMarkers: pings as Ping[] }); + } + }, resetMarkers: () => set({ customMarkers: [] }), })); From f4e2e992c25a555836ac06c33da7c65c179884bc Mon Sep 17 00:00:00 2001 From: choihooo Date: Sun, 24 Nov 2024 01:56:37 +0900 Subject: [PATCH 12/13] =?UTF-8?q?[feature]=20=EB=B9=8C=EB=93=9C=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[id]/components/BottomDrawer.tsx | 121 +----- .../[id]/components/ButtonComponents.tsx | 78 ++-- .../[id]/components/LocationButton.tsx | 20 +- .../[id]/components/MapComponent.tsx | 381 +++++++++--------- .../[id]/components/RecommendActive.tsx | 25 +- .../[id]/components/RecommendButton.tsx | 26 +- .../[id]/components/RecommendInActive.tsx | 111 ++--- .../event-maps/[id]/components/StoreItem.tsx | 3 +- 8 files changed, 352 insertions(+), 413 deletions(-) diff --git a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx index d7fc0fe..d3e2b20 100644 --- a/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx +++ b/fe/src/app/event-maps/[id]/components/BottomDrawer.tsx @@ -1,12 +1,13 @@ import React, { useState, useEffect, useRef } from "react"; import { useRouter } from "next/navigation"; + import Image from "next/image"; import { useLocationStore } from "../stores/useLocationStore"; import { useMarkerStore } from "../load-mappin/stores/useMarkerStore"; -import RecommendButton from "./RecommendButton"; -import RecommendInActive from "./RecommendInActive"; +import { RecommendButton } from "./RecommendButton"; +import { RecommendInActive } from "./RecommendInActive"; import LocationButton from "./LocationButton"; -import RecommendActive from "./RecommendActive"; +import { RecommendActive } from "./RecommendActive"; interface NonMember { nonMemberId: number; @@ -40,11 +41,11 @@ interface RecommendPing { type: string; } -const BottomDrawer: React.FC = ({ +export function BottomDrawer({ nonMembers: initialNonMembers, eventName: initialEventName, id, -}) => { +}: BottomDrawerProps): JSX.Element { const [eventName, setEventName] = useState(initialEventName); const [selectedButton, setSelectedButton] = useState(null); const [nonMembers, setNonMembers] = useState(initialNonMembers); @@ -58,7 +59,6 @@ const BottomDrawer: React.FC = ({ const moveToLocation = useLocationStore((state) => state.moveToLocation); const router = useRouter(); - const observer = useRef(); const lastPingElementRef = useRef(null); const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL; @@ -69,11 +69,10 @@ const BottomDrawer: React.FC = ({ const response = await fetch(`${apiUrl}/nonmembers/pings?uuid=${id}`); if (response.ok) { const data = await response.json(); - let recommendProfile; + let recommendProfile = []; if (data.recommendPings && data.recommendPings.length > 0) { setIsRecommended(true); - console.log(isRecommended); - recommendProfile = (data.recommendPings || []).map( + recommendProfile = data.recommendPings.map( (ping: RecommendPing) => ({ iconLevel: 10, // Fixed icon level nonMembers: [ @@ -90,30 +89,24 @@ const BottomDrawer: React.FC = ({ type: ping.type, }) ); - console.log("리코멘트 프로필" + JSON.stringify(recommendProfile)); - } else { - recommendProfile = []; // Default value when no recommendPings } setEventName(data.eventName || ""); setNonMembers([ - ...(recommendProfile[0].nonMembers || []), + ...(recommendProfile[0]?.nonMembers || []), ...(data.nonMembers || []), ]); - console.log("논멤버스" + JSON.stringify(nonMembers)); setAllPings([ ...(data.pings || []), // 기존 pings ...(recommendProfile || []), // recommendProfile 추가 ]); - setCustomMarkers([ ...(data.pings || []), // 기존 pings ...(recommendProfile || []), // recommendProfile 추가 ]); - setNeighborhood(data.neighborhood); } } catch (error) { - console.log("Error:", error); + console.error("Error:", error); } }; fetchAllPings(); @@ -136,7 +129,7 @@ const BottomDrawer: React.FC = ({ }; const handleAddToMorphing = async () => { - let Km = 1.0; + const Km = 1.0; let found = false; try { @@ -146,23 +139,11 @@ const BottomDrawer: React.FC = ({ ); if (response.ok) { const data = await response.json(); - console.log( - `Recommended data fetched successfully for ${Km} km:`, - data - ); - if (data.recommendPings.length == 0) { + if (data.recommendPings.length === 0) { setNonRecommend(true); - console.log("없음"); - } - if (data.recommendPings && data.recommendPings.length >= 5) { - console.log( - "Found more than or equal to 5 pings at radius:", - Km, - "km" - ); + } else if (data.recommendPings.length >= 5) { setCustomMarkers(data.recommendPings); found = true; - // break; setIsRecommend(found); } } else { @@ -174,79 +155,23 @@ const BottomDrawer: React.FC = ({ } catch (error) { console.error("Error fetching recommended data:", error); } - - // 상태 업데이트는 검색 결과에 따라 결정 }; - // const handleAddToMorphing = async () => { - // if (recommendPings.length > 0) { - // console.log("Adding selected pings to morphing...", recommendPings); - - // // Prepare payload - // const payload = { - // uuid: id, // UUID는 필요에 따라 동적으로 설정하세요 - // sids: recommendPings.map((ping) => ping.sid), // recommendPings에서 sid 추출 - // }; - - // console.log(payload); - - // try { - // // Send POST request - // const response = await fetch(`${apiUrl}/nonmembers/pings/recommend`, { - // method: "POST", - // headers: { - // "Content-Type": "application/json;charset=UTF-8", - // }, - // body: JSON.stringify(payload), // Convert payload to JSON - // }); - // // Handle response - // if (response.ok) { - // const result = await response.json(); - // console.log("추가:" + JSON.stringify(result)); - - // setIsRecommend(false); - // } else { - // const errorText = await response.text(); // 서버에서 제공하는 오류 메시지 확인 - // console.error( - // "Failed to add to morphing:", - // response.status, - // response.statusText, - // errorText - // ); - // console.error("Failed to add to morphing:", response.statusText); - // } - // } catch (error) { - // console.error("Error adding to morphing:", error); - // } - // } else { - // console.log("No pings available to add to morphing."); - // } - // }; - const handleRecommendCancle = () => { setIsRecommend(false); }; const handleButtonClick = (nonMemberId: number) => { - // 1. 현재 선택 상태를 확인합니다. const isSelected = selectedButton === nonMemberId; - - // 2. 선택 상태를 토글합니다. setSelectedButton(isSelected ? null : nonMemberId); - // 3. 선택 상태에 따라 pingsToShow를 설정합니다. const pingsToShow = isSelected - ? [...allPings] // 선택 해제 시 모든 핑을 표시 + ? [...allPings] : allPings.filter((ping) => ping.nonMembers.some((member) => member.nonMemberId === nonMemberId) - ); // nonMemberId와 일치하는 항목만 필터링 + ); - // 4. Zustand의 customMarkers를 업데이트합니다. setCustomMarkers(pingsToShow); - - // 5. 현재 상태를 로그로 확인합니다. - console.log("선택된 버튼:", isSelected ? null : nonMemberId); - console.log("업데이트된 마커:", pingsToShow); }; const handleAddButtonClick = () => { @@ -260,16 +185,15 @@ const BottomDrawer: React.FC = ({ ); if (response.ok) { const data = await response.json(); - console.log(data); setEventName(data.eventName); setNonMembers(data.nonMembers); setAllPings(data.pings || []); setCustomMarkers(data.pings || []); } else { - console.log("Failed to fetch refreshed data:", response.status); + console.error("Failed to fetch refreshed data:", response.status); } } catch (error) { - console.log("Error refreshing data:", error); + console.error("Error refreshing data:", error); } }; @@ -278,14 +202,12 @@ const BottomDrawer: React.FC = ({ role="button" tabIndex={0} className="bottom-drawer w-full h-[760px] bg-grayscale-90 z-10 rounded-t-xlarge" - onClick={(event: React.MouseEvent) => {}} - onKeyDown={(event: React.KeyboardEvent) => {}} > - {isRecommend === false && isRecommended === false ? ( + {!isRecommend && !isRecommended && (
- ) : null} + )}
@@ -305,8 +227,8 @@ const BottomDrawer: React.FC = ({ handleRecommendCancle={handleRecommendCancle} handleAddToMorphing={handleAddToMorphing} setIsRecommend={setIsRecommend} - setNonRecommend={setNonRecommend} nonRecommend={nonRecommend} + setNonRecommend={setNonRecommend} /> ) : ( = ({ handleRefresh={handleRefresh} eventName={eventName} id={id} - router={router} /> )}
); -}; +} export default BottomDrawer; diff --git a/fe/src/app/event-maps/[id]/components/ButtonComponents.tsx b/fe/src/app/event-maps/[id]/components/ButtonComponents.tsx index 0fa6002..aa18dc3 100644 --- a/fe/src/app/event-maps/[id]/components/ButtonComponents.tsx +++ b/fe/src/app/event-maps/[id]/components/ButtonComponents.tsx @@ -5,27 +5,33 @@ interface ButtonProps { onClick: () => void; } -export const ShareButton: React.FC = ({ onClick }) => ( - -); +export function ShareButton({ onClick }: ButtonProps) { + return ( + + ); +} -export const RefreshButton: React.FC = ({ onClick }) => ( - -); +export function RefreshButton({ onClick }: ButtonProps) { + return ( + + ); +} -export const EditButton: React.FC = ({ onClick }) => ( - -); +export function EditButton({ onClick }: ButtonProps) { + return ( + + ); +} interface MemberButtonProps { member: { @@ -37,21 +43,25 @@ interface MemberButtonProps { onClick: (id: number) => void; } -export const MemberButton: React.FC = ({ +export function MemberButton({ member, isSelected, onClick, -}) => ( - -); +}: MemberButtonProps) { + return ( + + ); +} diff --git a/fe/src/app/event-maps/[id]/components/LocationButton.tsx b/fe/src/app/event-maps/[id]/components/LocationButton.tsx index 5d49551..eb268ed 100644 --- a/fe/src/app/event-maps/[id]/components/LocationButton.tsx +++ b/fe/src/app/event-maps/[id]/components/LocationButton.tsx @@ -5,14 +5,16 @@ interface ButtonProps { onClick: () => void; } -const LocationButton: React.FC = ({ onClick }) => ( - -); +function LocationButton({ onClick }: ButtonProps) { + return ( + + ); +} export default LocationButton; diff --git a/fe/src/app/event-maps/[id]/components/MapComponent.tsx b/fe/src/app/event-maps/[id]/components/MapComponent.tsx index c093520..9dde6de 100644 --- a/fe/src/app/event-maps/[id]/components/MapComponent.tsx +++ b/fe/src/app/event-maps/[id]/components/MapComponent.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState } from "react"; +import React, { useEffect, useRef } from "react"; import { useLocationStore } from "../stores/useLocationStore"; import { useMarkerStore } from "../load-mappin/stores/useMarkerStore"; @@ -13,97 +13,81 @@ interface NonMember { profileSvg: string; } -interface PingData { +interface Ping { placeName: string; url: string; nonMembers: NonMember[]; type: string; + px: number; + py: number; + iconLevel: number; } -const transformPingData = (ping: any): PingData => ({ - placeName: ping.placeName, - url: ping.url, - nonMembers: (ping.nonMembers || []).map( - (member: { profileSvg?: string; nonMemberId: number; name: string }) => ({ - ...member, - profileSvg: member.profileSvg || "https://default-image.svg", // 기본 이미지 URL 추가 - }) - ), - type: ping.type, -}); - -const setupToggleDropdown = (): void => { - window.toggleDropdown = (): void => { - const dropdown: HTMLElement | null = - document.getElementById("dropdownExtended"); - const dropdownIcon: HTMLImageElement | null = document.querySelector( - 'img[src="/svg/dropdown.svg"]' - ); - const namesShort: HTMLElement | null = - document.querySelector(".names-short"); +const transformPingData = (ping: unknown): Ping => { + if (typeof ping !== "object" || ping === null) { + throw new Error("Invalid ping data"); + } - if (!dropdown || !dropdownIcon || !namesShort) return; // 요소가 없으면 함수를 실행하지 않습니다. + const { + placeName = "Unknown Place", + url = "#", + nonMembers = [], + type = "Unknown", + px = 0, + py = 0, + iconLevel = 1, + } = ping as Partial; - if (dropdown.style.display === "none") { - dropdown.style.display = "block"; - dropdownIcon.style.transform = "rotate(0deg)"; - namesShort.style.opacity = "0"; // CSS 속성은 문자열로 처리해야 합니다. - } else { - dropdown.style.display = "none"; - dropdownIcon.style.transform = "rotate(180deg)"; - namesShort.style.opacity = "1"; // CSS 속성은 문자열로 처리해야 합니다. - } + return { + placeName, + url, + nonMembers: (nonMembers as NonMember[]).map((member) => ({ + ...member, + profileSvg: member.profileSvg || "https://default-image.svg", + })), + type, + px, + py, + iconLevel, }; }; -export default function MapComponent({ px, py }: MapComponentProps) { +export default function MapComponent({ + px, + py, +}: MapComponentProps): JSX.Element { const mapRef = useRef(null); const mapInstanceRef = useRef(null); const { customMarkers } = useMarkerStore(); const { center } = useLocationStore(); - const [selectedMarker, setSelectedMarker] = useState(null); const markersRef = useRef([]); const infoWindowRef = useRef(null); const previousMarkerIndexRef = useRef(null); - // HTML 아이콘으로 커스텀 마커 설정 const getHtmlIconByLevel = ( level: number, placeName: string, - isSelected: boolean = false + isSelected = false ) => { - const iconSize_w = isSelected ? 35 : 28; - const iconSize_h = isSelected ? 40 : 32; - let textColor, textShadow; - - if (level === 1 || level === 10 || level === 10) { - textColor = "#000000"; // 검정색 글씨 - textShadow = - "-1px 0px #FFFFFF, 0px 1px #FFFFFF, 1px 0px #FFFFFF, 0px -1px #FFFFFF"; // 흰색 텍스트 쉐도우 테두리 - } else { - textColor = "#FA8980"; // 살구색 글씨 - textShadow = - "-1px 0px #FFFFFF, 0px 1px #FFFFFF, 1px 0px #FFFFFF, 0px -1px #FFFFFF"; // 흰색 텍스트 쉐도우 테두리 - } + const iconWidth = isSelected ? 35 : 28; + const iconHeight = isSelected ? 40 : 32; + const textColor = level === 1 || level === 10 ? "#000000" : "#FA8980"; + const textShadow = + "-1px 0px #FFFFFF, 0px 1px #FFFFFF, 1px 0px #FFFFFF, 0px -1px #FFFFFF"; return { - content: `
-
+
${placeName}
`, - size: new window.naver.maps.Size(iconSize_w, iconSize_h), - anchor: new window.naver.maps.Point(iconSize_w / 2, iconSize_h + 15), // 라벨이 포함되므로 앵커 포지션 조정 + size: new window.naver.maps.Size(iconWidth, iconHeight), + anchor: new window.naver.maps.Point(iconWidth / 2, iconHeight + 15), }; }; - useEffect(() => { - setupToggleDropdown(); - // 마커와 이벤트 리스너 생성 코드 이하 생략 - }, []); - useEffect(() => { const initializeMap = () => { if (!mapInstanceRef.current && window.naver && mapRef.current) { @@ -130,7 +114,7 @@ export default function MapComponent({ px, py }: MapComponentProps) { script.onload = () => initializeMap(); document.head.appendChild(script); } - }, []); + }, [px, py]); useEffect(() => { if (!mapInstanceRef.current) return; @@ -140,10 +124,18 @@ export default function MapComponent({ px, py }: MapComponentProps) { customMarkers.forEach((ping, index) => { const transformedPing = transformPingData(ping); - const markerOptions = { - position: new window.naver.maps.LatLng(ping.py, ping.px), - map: mapInstanceRef.current as naver.maps.Map, - icon: getHtmlIconByLevel(ping.iconLevel, ping.placeName, false), + + const markerOptions: naver.maps.MarkerOptions = { + position: new window.naver.maps.LatLng( + transformedPing.py, + transformedPing.px + ), + map: mapInstanceRef.current || undefined, + icon: getHtmlIconByLevel( + transformedPing.iconLevel, + transformedPing.placeName, + false + ), clickable: true, }; @@ -165,148 +157,149 @@ export default function MapComponent({ px, py }: MapComponentProps) { } marker.setIcon( - getHtmlIconByLevel(ping.iconLevel, ping.placeName, true) + getHtmlIconByLevel( + transformedPing.iconLevel, + transformedPing.placeName, + true + ) ); previousMarkerIndexRef.current = index; - setSelectedMarker(index); if (infoWindowRef.current) { infoWindowRef.current.close(); } - const infoWindowContent = (data: PingData) => ` -
-
- -
- ${data.placeName} -
-
- -
-
- - ${data.nonMembers.length} -
-
- ${data.nonMembers.map((member) => member.name).join(", ")} -
- -
- - -
-`; const infoWindow = new window.naver.maps.InfoWindow({ - content: infoWindowContent(transformedPing), // API에서 받은 데이터를 infoWindowContent 함수에 전달 - borderWidth: 0, - backgroundColor: "transparent", - disableAnchor: true, + content: ` +
+
+
+
${transformedPing.type}
+ +
+
+ ${transformedPing.placeName} +
+
+ +
+
+ + ${transformedPing.nonMembers.length} +
+
+ ${transformedPing.nonMembers.map((member) => member.name).join(", ")} +
+ +
+ + +
+ `, + borderWidth: 0, // 보더 제거 + backgroundColor: "transparent", // 백그라운드 설정 + disableAnchor: true, // 앵커 비활성화 }); if (mapInstanceRef.current) { infoWindow.open(mapInstanceRef.current, marker); } - console.log(customMarkers); infoWindowRef.current = infoWindow; }); }); @@ -328,7 +321,6 @@ export default function MapComponent({ px, py }: MapComponentProps) { ); previousMarkerIndexRef.current = null; } - setSelectedMarker(null); if (infoWindowRef.current) { infoWindowRef.current.close(); @@ -336,7 +328,6 @@ export default function MapComponent({ px, py }: MapComponentProps) { } ); } - console.log(customMarkers); }, [customMarkers]); useEffect(() => { diff --git a/fe/src/app/event-maps/[id]/components/RecommendActive.tsx b/fe/src/app/event-maps/[id]/components/RecommendActive.tsx index c6490e2..77d1648 100644 --- a/fe/src/app/event-maps/[id]/components/RecommendActive.tsx +++ b/fe/src/app/event-maps/[id]/components/RecommendActive.tsx @@ -1,6 +1,5 @@ -import React from "react"; +import React, { Dispatch, SetStateAction } from "react"; import Image from "next/image"; -import { Dispatch, SetStateAction } from "react"; interface RecommendActiveProps { neighborhood: string; @@ -11,25 +10,29 @@ interface RecommendActiveProps { nonRecommend: boolean; } -const RecommendActive: React.FC = ({ +export function RecommendActive({ neighborhood, handleRecommendCancle, handleAddToMorphing, setIsRecommend, setNonRecommend, nonRecommend, -}) => { +}: RecommendActiveProps): JSX.Element { return (
{/* nonRecommend이 true일 때 다른 UI를 렌더링 */} {nonRecommend ? ( <> - {/* 추천 데이터가 있을 때 기본 UI */} + {/* 추천 데이터가 없을 때 UI */}
근처에 추천할만한 공간이 없어요
-
-
); -}; +} export default RecommendActive; diff --git a/fe/src/app/event-maps/[id]/components/RecommendButton.tsx b/fe/src/app/event-maps/[id]/components/RecommendButton.tsx index f995c12..a82beb7 100644 --- a/fe/src/app/event-maps/[id]/components/RecommendButton.tsx +++ b/fe/src/app/event-maps/[id]/components/RecommendButton.tsx @@ -5,15 +5,17 @@ interface RecommendButtonProps { onClick: () => void; } -const RecommendButton: React.FC = ({ onClick }) => ( - -); - -export default RecommendButton; +export function RecommendButton({ + onClick, +}: RecommendButtonProps): JSX.Element { + return ( + + ); +} diff --git a/fe/src/app/event-maps/[id]/components/RecommendInActive.tsx b/fe/src/app/event-maps/[id]/components/RecommendInActive.tsx index 6eb2207..efa69ed 100644 --- a/fe/src/app/event-maps/[id]/components/RecommendInActive.tsx +++ b/fe/src/app/event-maps/[id]/components/RecommendInActive.tsx @@ -1,5 +1,6 @@ import React from "react"; import Image from "next/image"; +import { useRouter } from "next/navigation"; import { ShareButton, RefreshButton, @@ -33,10 +34,9 @@ interface RecommendInActiveProps { handleRefresh: () => void; eventName: string; id: string; - router: any; // Type based on your routing library } -const RecommendInActive: React.FC = ({ +export function RecommendInActive({ nonMembers, handleButtonClick, handleAddButtonClick, @@ -46,61 +46,64 @@ const RecommendInActive: React.FC = ({ handleRefresh, eventName, id, - router, -}) => ( - <> -
-
{eventName}
-
- navigator.share({ url: window.location.href })} - /> - {selectedButton !== null ? ( - router.push(`/event-maps/${id}/${selectedButton}`)} +}: RecommendInActiveProps): JSX.Element { + const router = useRouter(); + + return ( + <> +
+
{eventName}
+
+ navigator.share({ url: window.location.href })} /> - ) : ( - - )} + {selectedButton !== null ? ( + router.push(`/event-maps/${id}/${selectedButton}`)} // router 사용 + /> + ) : ( + + )} +
-
-
-
- +
+
+ +
+ {nonMembers.map((member) => ( +
+ handleButtonClick(member.nonMemberId)} + /> +
{member.name}
+
+ ))}
- {nonMembers.map((member) => ( -
- handleButtonClick(member.nonMemberId)} +
+ {allPings.map((ping, index) => ( + -
{member.name}
-
- ))} -
-
- {allPings.map((ping, index) => ( - - ))} -
- -); + ))} +
+ + ); +} export default RecommendInActive; diff --git a/fe/src/app/event-maps/[id]/components/StoreItem.tsx b/fe/src/app/event-maps/[id]/components/StoreItem.tsx index e487e35..742c296 100644 --- a/fe/src/app/event-maps/[id]/components/StoreItem.tsx +++ b/fe/src/app/event-maps/[id]/components/StoreItem.tsx @@ -30,8 +30,9 @@ const StoreItem = forwardRef(