Skip to content

Commit 704a772

Browse files
committedNov 14, 2023
[ADD] 신고현황 페이지 추가
1 parent 30c7be1 commit 704a772

11 files changed

+482
-13
lines changed
 

‎.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ yarn-debug.log*
2323
yarn-error.log*
2424

2525
package-lock.json
26+
.env

‎package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"react-icons": "^4.11.0",
1515
"react-router-dom": "^6.17.0",
1616
"react-scripts": "5.0.1",
17+
"react-select": "^5.8.0",
1718
"react-spinners": "^0.13.8",
1819
"recoil": "^0.7.7",
1920
"stream": "^0.0.2",
@@ -47,8 +48,8 @@
4748
]
4849
},
4950
"devDependencies": {
50-
"tailwindcss": "^3.3.3",
5151
"http-proxy-middleware": "^2.0.6",
52-
"kakao.maps.d.ts": "^0.1.39"
52+
"kakao.maps.d.ts": "^0.1.39",
53+
"tailwindcss": "^3.3.3"
5354
}
5455
}

‎src/App.js

+10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import LoginPage from './pages/LoginAndJoin/LoginPage';
1111
import JoinPage from './pages/LoginAndJoin/JoinPage';
1212
import DictionaryComponent from './components/Dictionary/DictionaryComponent';
1313
import CropListComponent from './components/Dictionary/CropListComponent';
14+
import MapComponent from './components/Map/MapComponent';
1415
import TestPage from './pages/testPage';
1516

1617
function App() {
@@ -52,6 +53,15 @@ function App() {
5253
<Footer />
5354
</>
5455
}
56+
/>
57+
<Route
58+
path="/map"
59+
element={
60+
<>
61+
<MapComponent />
62+
<Footer />
63+
</>
64+
}
5565
/>
5666
<Route path="/croplist/:cropName" element={<DictionaryComponent />} />
5767
<Route path = "/" Component = {FirstPage} />

‎src/components/Dictionary/CropListComponent.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
import { Link, useNavigate } from 'react-router-dom';
33
import React, { useState } from 'react';
4-
import './dictionary.css'
4+
import '../common/common.css'
55
import MoveBackComponent from '../common/MoveBackComponent';
66

77
const CropListComponent = () => {

‎src/components/Dictionary/DictionaryComponent.js

-4
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,6 @@ const DictionaryComponent = () => {
146146
}
147147
}, [cropName]);
148148

149-
const goBack = () => {
150-
window.history.back();
151-
};
152-
153149
const openModal = (disease) => {
154150
setSelectedDisease(disease);
155151
setIsModalOpen(true);

‎src/components/Map/MapComponent.js

+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import React, { useEffect, useState } from 'react';
2+
import { useNavigate, useLocation } from 'react-router-dom';
3+
import mapData from '../../config/mapData.json'
4+
5+
const MapComponent = () => {
6+
const navigate = useNavigate();
7+
const location = useLocation();
8+
const [map, setMap] = useState(null);
9+
const [modalVisible, setModalVisible] = useState(false);
10+
const [modalMessage, setModalMessage] = useState('');
11+
const [data, setData] = useState();
12+
13+
useEffect(()=>{
14+
15+
}, [])
16+
const Modal = ({ message, onClose }) => {
17+
// 모달 바깥 부분을 클릭했을 때 닫기
18+
const handleOutsideClick = (e) => {
19+
if (e.target.className === 'modal') {
20+
onClose();
21+
}
22+
};
23+
console.log(message);
24+
return (
25+
<div className="modal" onClick={handleOutsideClick}>
26+
<div className="w-72 h-32 p-4 bg-white rounded-2xl">
27+
<div>작물 : {data.crop}</div>
28+
<div>질병 : {data.disease}</div>
29+
<div>주소 : {data.address}</div>
30+
<div>신고날짜 : {data.date}</div>
31+
</div>
32+
</div>
33+
);
34+
};
35+
36+
// 작물별 마커 이미지 경로
37+
const cropMarkerImages = {
38+
'tomato': 'https://cdn1.iconfinder.com/data/icons/color-bold-style/21/14_2-1024.png',
39+
'cucumber': 'https://cdn3.iconfinder.com/data/icons/flat-pro-basic-set-1-1/32/location-green-1024.png',
40+
'pepper': 'https://cdn1.iconfinder.com/data/icons/color-bold-style/21/14-1024.png',
41+
'strawberry': 'https://cdn1.iconfinder.com/data/icons/black-bold-style-1/3/14-1024.png',
42+
};
43+
44+
const loadMapScript = () => {
45+
return new Promise((resolve) => {
46+
const script = document.createElement('script');
47+
script.type = 'text/javascript';
48+
script.src = `//dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.REACT_APP_KAKAO_API_KEY}&autoload=false`;
49+
document.head.appendChild(script);
50+
51+
script.onload = () => resolve(window.kakao.maps.load(resolve));
52+
});
53+
};
54+
55+
const initializeMap = (latitude, longitude) => {
56+
const container = document.getElementById('map');
57+
const options = {
58+
center: new window.kakao.maps.LatLng(latitude, longitude),
59+
level: 3, // 적절한 확대 레벨 설정
60+
};
61+
const createdMap = new window.kakao.maps.Map(container, options);
62+
setMap(createdMap);
63+
64+
const locPosition = new window.kakao.maps.LatLng(latitude, longitude);
65+
const currentMarker = new window.kakao.maps.Marker({
66+
position: locPosition,
67+
map: createdMap,
68+
title: '현재 위치',
69+
});
70+
71+
const infowindow = new window.kakao.maps.InfoWindow({
72+
content: '<div style="padding:5px;">현재 위치</div>',
73+
});
74+
infowindow.open(createdMap, currentMarker);
75+
76+
// 저장된 마커 로드 (예시)
77+
78+
const storedMarkers = JSON.parse(localStorage.getItem('reports')) || [];
79+
80+
mapData.forEach((m) => {
81+
const position = new window.kakao.maps.LatLng(m.lat, m.lng);
82+
const imageSrc = cropMarkerImages[m.crop] || 'default_marker_image.png';
83+
const markerImage = new window.kakao.maps.MarkerImage(imageSrc, new window.kakao.maps.Size(40, 40));
84+
const marker = new window.kakao.maps.Marker({
85+
position: position,
86+
map: createdMap,
87+
title: '신고된 위치',
88+
image: markerImage,
89+
});
90+
91+
window.kakao.maps.event.addListener(marker, 'click', () => {
92+
const message = `Marker Details:\nLatitude: ${m.lat}\nLongitude: ${m.lng}\nDate: ${m.date}`;
93+
setData(m);
94+
setModalMessage(message);
95+
setModalVisible(true);
96+
});
97+
});
98+
};
99+
100+
useEffect(() => {
101+
let isCancelled = false;
102+
103+
loadMapScript().then(() => {
104+
if (isCancelled) return;
105+
106+
navigator.geolocation.getCurrentPosition((position) => {
107+
const { latitude, longitude } = position.coords;
108+
if (!isCancelled) initializeMap(latitude, longitude);
109+
});
110+
});
111+
112+
return () => {
113+
isCancelled = true;
114+
};
115+
}, [location.state]);
116+
117+
const goBack = () => {
118+
navigate(-1);
119+
};
120+
121+
return (
122+
<div style={{ position: 'relative', width: '100%', height: '100vh' }}>
123+
{modalVisible && <Modal message={modalMessage} onClose={() => setModalVisible(false)} />}
124+
<div style={{
125+
position: 'absolute',
126+
top: '20px',
127+
right: '20px',
128+
zIndex: 5,
129+
backgroundColor: 'white',
130+
padding: '10px',
131+
borderRadius: '8px',
132+
boxShadow: '0 2px 4px rgba(0,0,0,0.2)'
133+
}}>
134+
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '5px' }}>
135+
<div style={{ width: '20px', height: '20px', backgroundColor: 'red', marginRight: '10px' }}></div>
136+
<div>토마토</div>
137+
</div>
138+
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '5px' }}>
139+
<div style={{ width: '20px', height: '20px', backgroundColor: 'green', marginRight: '10px' }}></div>
140+
<div>오이</div>
141+
</div>
142+
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '5px' }}>
143+
<div style={{ width: '20px', height: '20px', backgroundColor: 'skyblue', marginRight: '10px' }}></div>
144+
<div>고추</div>
145+
</div>
146+
<div style={{ display: 'flex', alignItems: 'center' }}>
147+
<div style={{ width: '20px', height: '20px', backgroundColor: 'black', marginRight: '10px' }}></div>
148+
<div>딸기</div>
149+
</div>
150+
</div>
151+
<button onClick={goBack} style={{
152+
position: 'absolute',
153+
top: '20px',
154+
left: '20px',
155+
zIndex: 5
156+
}}>
157+
</button>
158+
<div id="map" style={{ width: '100%', height: '100%' }}></div>
159+
</div>
160+
);
161+
};
162+
163+
export default MapComponent;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import React, { useEffect, useState } from 'react';
2+
import '../common/common.css'
3+
import { FaArrowLeft } from 'react-icons/fa';
4+
import { useNavigate } from 'react-router-dom';
5+
6+
7+
//주소 reportpage로 보내줌
8+
const ReportAddressComponent = () => {
9+
const navigate = useNavigate();
10+
const [map, setMap] = useState(null);
11+
const [marker, setMarker] = useState(null);
12+
const [infowindow, setInfowindow] = useState(null); // 인포윈도우 상태 추가
13+
const [address, setAddress] = useState('위치를 불러오는 중...');
14+
15+
useEffect(() => {
16+
const script = document.createElement('script');
17+
script.type = 'text/javascript';
18+
script.src = `//dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.REACT_APP_KAKAO_API_KEY}&libraries=services&autoload=false`;
19+
document.head.appendChild(script);
20+
21+
script.onload = () => {
22+
window.kakao.maps.load(() => {
23+
navigator.geolocation.getCurrentPosition((position) => {
24+
const userLat = position.coords.latitude;
25+
const userLng = position.coords.longitude;
26+
27+
const container = document.getElementById('map__kakao');
28+
const options = {
29+
center: new window.kakao.maps.LatLng(userLat, userLng),
30+
level: 3,
31+
};
32+
const createdMap = new window.kakao.maps.Map(container, options);
33+
34+
const markerPosition = new window.kakao.maps.LatLng(userLat, userLng);
35+
const createdMarker = new window.kakao.maps.Marker({
36+
position: markerPosition,
37+
draggable: true,
38+
});
39+
40+
createdMarker.setMap(createdMap);
41+
setMap(createdMap);
42+
setMarker(createdMarker);
43+
updateAddress(userLat, userLng);
44+
45+
// 인포윈도우 생성 및 설정
46+
const createdInfowindow = new window.kakao.maps.InfoWindow({
47+
content: '마커를 움직이세요', // 인포윈도우에 표시할 내용
48+
removable: true
49+
});
50+
createdInfowindow.open(createdMap, createdMarker); // 마커에 인포윈도우를 표시
51+
setInfowindow(createdInfowindow); // 인포윈도우 상태 설정
52+
});
53+
});
54+
};
55+
}, []);
56+
57+
const updateAddress = (lat, lng) => {
58+
const geocoder = new window.kakao.maps.services.Geocoder();
59+
geocoder.coord2Address(lng, lat, (result, status) => {
60+
if (status === window.kakao.maps.services.Status.OK) {
61+
setAddress(result[0].address.address_name);
62+
} else {
63+
setAddress('주소를 찾을 수 없습니다');
64+
}
65+
});
66+
};
67+
68+
useEffect(() => {
69+
if (marker && infowindow) {
70+
window.kakao.maps.event.addListener(marker, 'dragstart', function() {
71+
// 드래그 시작 시 인포윈도우 닫기
72+
infowindow.close();
73+
});
74+
75+
window.kakao.maps.event.addListener(marker, 'dragend', function() {
76+
const position = marker.getPosition();
77+
updateAddress(position.getLat(), position.getLng());
78+
});
79+
}
80+
}, [marker, infowindow]);
81+
82+
const handleConfirm = () => {
83+
const position = marker.getPosition();
84+
const latlng = {
85+
lat: position.getLat(),
86+
lng: position.getLng(),
87+
address: address
88+
};
89+
90+
navigate('/report-page', { state: latlng });
91+
};
92+
93+
return (
94+
<div style={{ position: 'relative', width: '100%', height: '100vh' }}>
95+
<div style={{ padding: "40px" }}>
96+
<button onClick={() => navigate(-1)} className="goBackButton">
97+
<FaArrowLeft size={20}/>
98+
</button>
99+
<div id="map__kakao" style={{ width: '100%', height: '90vh' }}></div>
100+
<div
101+
style={{
102+
position: 'absolute',
103+
bottom: '50px',
104+
width: '100%',
105+
backgroundColor: 'rgba(255, 255, 255, 0.7)',
106+
padding: '25px',
107+
textAlign: 'center',
108+
borderTop: '1px solid #ccc',
109+
zIndex: 2
110+
}}>
111+
<button onClick={handleConfirm} className="confirmButton">
112+
이 위치로 신고하기
113+
</button>
114+
<p>{address}</p>
115+
</div>
116+
</div>
117+
</div>
118+
);
119+
};
120+
121+
export default ReportAddressComponent;

0 commit comments

Comments
 (0)
Please sign in to comment.