8ueET0_bAZWT8ACynKGCh%*6hnmP
z%8fwQGCHtE%N#pl0fc5XwY`ir7?}4#oOw13-)eis(@OY>-^{vWS8bF2{?>cwEy;0f
zVXS6t-%fQ$QS+PcyH+;VX={=hLEI%DtZ&?3=j3%;6}ir!T&>?(ZxdHi?+6qIvY|<>N
zA16e;KOi!kC^xprPVg)5$|8AidCSPk!=}vC#n|lMN0MFjtYw#54zzKaGX<)3VB(f-UlBI0N0%?+i-qm3%B
zEo$WL2)`MO`f+Hf-T1S$t)c%VYh5qYvCo>u`ev_ABU$j6^ZWgv&%DEC%dC(9Q$v^m
JN$ {
+ const fetchTravelPlans = async () => {
+ try {
+ const response = await fetch('https://your-backend-server-address/travel-plans/myRooms', {
+ method: 'GET',
+ headers: {
+ 'Authorization': 'Bearer '
+ }
+ });
+
+ if (response.ok) {
+ const data = await response.json();
+ setTravelPlans(data);
+ } else {
+ console.error('Failed to fetch data');
+ }
+ } catch (error) {
+ console.error('Error:', error);
+ }
+ };
+
+ fetchTravelPlans();
+ }, []);
+
+ // 계획 만들기 버튼 클릭 핸들러
const handleCreatePlan = () => {
navigate('/MakePlanRoom1');
};
return (
-
+
@@ -29,15 +63,24 @@ function Home() {
- {Array(9).fill(0).map((_, index) => (
-
+ {travelPlans.map((plan, index) => (
+
-
강원도
-
여행 제목
-
2024. 07. 13 - 2024. 8. 27
+
+ {plan.region}
+
+
{plan.title}
+
+ {new Date(plan.start_date).toLocaleDateString()} -{' '}
+ {new Date(plan.end_date).toLocaleDateString()}
+
diff --git a/src/pages/MakePlanRoom1.jsx b/src/pages/MakePlanRoom1.jsx
index c5f79ba..7256075 100644
--- a/src/pages/MakePlanRoom1.jsx
+++ b/src/pages/MakePlanRoom1.jsx
@@ -8,6 +8,7 @@ import 'react-calendar/dist/Calendar.css';
import 'react-time-picker/dist/TimePicker.css';
import { format } from 'date-fns';
import moment from 'moment';
+import axios from 'axios';
/*컨테이너 및 구역 나눔*/
const FullContainner = styled.div`
@@ -181,16 +182,22 @@ const Time = styled.div``;
function MakePlanRoom1() {
const [image, setImage] = useState(ImgUpload);
+ const [title, setTitle] = useState(''); // 여행 제목 상태 관리
+ const [explain, setExplain] = useState(''); // 여행 설명 상태 관리
const [dateRange, setDateRange] = useState([new Date(), new Date()]);
const [startDate, endDate] = dateRange;
const [startTime, setStartTime] = useState('00:00');
const [endTime, setEndTime] = useState('00:00');
const [showTime, setShowTime] = useState(false);
+ const [imageFile, setImageFile] = useState(null);
const fileInputRef = useRef(null);
+ const region='지역'; // MakePlanRoom2 이전에 미리 초기값 설정 (formData 넘기려고..)
+ const status=1;
const handleImageUpload = (event) => {
const file = event.target.files[0];
if (file) {
+ setImageFile(file);
setImage(URL.createObjectURL(file));
}
};
@@ -205,9 +212,48 @@ function MakePlanRoom1() {
const navigate = useNavigate();
- const handleLink = () => {
- navigate('/MakePlanroom2');
+ const handleLink = async (travelId) => {
+ const [startDate, endDate] = dateRange;
+ const token = localStorage.getItem('token');
+ console.log('JWT Token:', JSON.stringify(token));
+ // FormData 생성
+ const formData = new FormData();
+ formData.append('title', title);
+ formData.append('start_date', format(startDate, 'yyyy-MM-dd'));
+ formData.append('end_date', format(endDate, 'yyyy-MM-dd'));
+ formData.append('explain', explain);
+ formData.append('start_time', showTime ? startTime : '00:00');
+ formData.append('end_time', showTime ? endTime : '00:00');
+ formData.append('region', region); // 추가된 지역
+ formData.append('status', status);
+
+ if (image) {
+ formData.append('travel_image', imageFile); // 선택된 파일 추가
+
+ try {
+ const response = await axios.post('http://43.200.238.249:5000/travel-plans/makeRoom', formData, {
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ 'Content-Type': 'multipart/form-data',
+ },
+ });
+
+ const travelId = response.data.travel_id;
+ console.log('응답 데이터(FormData):', response.data);
+ console.log('여행 계획방 ID:', travelId);
+
+ navigate('/MakePlanroom2', { state: { travelId } });
+
+ } catch (error) {
+ console.error('에러 발생:', error.response ? error.response.data : error.message);
+ if (error.response) {
+ console.error('서버 응답 오류:', error.response.data);
+ }
+ }
+ }
};
+
+
return (
여행 계획방 만들기 (1/2)
@@ -241,12 +287,13 @@ function MakePlanRoom1() {
여행 제목
-
+ {setTitle(e.target.value)}}/>
여행 설명
diff --git a/src/pages/MakePlanRoom2.jsx b/src/pages/MakePlanRoom2.jsx
index 7af1f3c..e9b444b 100644
--- a/src/pages/MakePlanRoom2.jsx
+++ b/src/pages/MakePlanRoom2.jsx
@@ -1,6 +1,6 @@
import React, { useState } from 'react';
import styled from 'styled-components';
-import { useNavigate } from 'react-router-dom';
+import { useNavigate, useLocation } from 'react-router-dom';
import {
MapContainer as LeafletMapContainer,
TileLayer,
@@ -181,7 +181,8 @@ const route = [[37.5665, 126.978]];
function MakePlanRoom2() {
const [accommodation, setAccommodation] = useState('');
const [registeredAccommodations, setRegisteredAccommodations] = useState([]);
-
+ const location = useLocation();
+
const handleAddAccommodation = () => {
if (accommodation.trim() === '') return;
setRegisteredAccommodations([
@@ -209,7 +210,9 @@ function MakePlanRoom2() {
const navigate = useNavigate();
const handleLink = () => {
- navigate('/StartPlanRoom');
+ const travelId = location.state?.travelId; //MakePlanRoom1에서 받은 travelId
+
+ navigate('/StartPlanRoom', { state: { travelId }});
};
return (
diff --git a/src/pages/PlanRoom1.jsx b/src/pages/PlanRoom1.jsx
index 8b8a506..a635b36 100644
--- a/src/pages/PlanRoom1.jsx
+++ b/src/pages/PlanRoom1.jsx
@@ -6,6 +6,7 @@ import ToggleListPlace from "../toggleLists/TogglesListPlace";
import ToggleListVote from "../toggleLists/ToggleListVote";
import MyPlaceModal from "../modals/MyPlaceModal";
import KakaoMap from "../components/KakaoMap";
+import axios from 'axios';
/*구역 나눔*/
const Container=styled.div`
@@ -264,9 +265,11 @@ function PlanRoom1(){
const [isModalOpen, setIsModalOpen] = useState(false); // 채팅 모달 상태
const [cursorPosition, setCursorPosition] = useState({ x: 0, y: 0 })
const [chatValue,setChatValue]=useState('');
+ const [lists,setLists]=useState([]);//나의 장소 불러오기
const inputRef = useRef(null);
const spanRef = useRef(null);
const navigate=useNavigate();
+ const token = localStorage.getItem('token');
const shareVoteDetails = (details) => {
setVoteDetails(details);
@@ -283,7 +286,6 @@ function PlanRoom1(){
const closeModal2 = () =>{
setModal2(false);
};
-
const handleListClick = (list) => {
if (selectedLists.includes(list)) {
setSelectedLists(selectedLists.filter((item) => item !== list));
@@ -291,11 +293,9 @@ function PlanRoom1(){
setSelectedLists([...selectedLists, list]);
}
};
-
const handleCandidate=()=>{
setCandidateList([...candidateList, `나의 후보 리스트 ${candidateList.length+1}`]);
};
-
const handlePlace=()=>{
setPlaceLists([...placeLists, inputValue]);
};
@@ -366,14 +366,28 @@ function PlanRoom1(){
const handleMouseMove = (e) => {
setCursorPosition({ x: e.clientX, y: e.clientY });
};
+ // const lists=[
+ // '나의 장소 리스트1','나의 장소 리스트2','나의 장소 리스트3','나의 장소 리스트4','나의 장소 리스트5','나의 장소 리스트6',];
- useEffect(() => {
- console.log("modal1 상태 변경:", modal1);
- }, [modal1]);
-
- const lists=[
- '나의 장소 리스트1','나의 장소 리스트2','나의 장소 리스트3','나의 장소 리스트4','나의 장소 리스트5','나의 장소 리스트6',];
-
+ /*백 연결*/
+ useEffect(()=>{
+ const fetchLists=async()=>{
+ try{
+ const response=await axios.get(
+ 'http://43.200.238.249:5000/users/lists',
+ {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ }
+ );
+ setLists(response.data);
+ } catch (error) {
+ console.error('Error fetching lists:', error);
+ }
+ };
+ fetchLists();
+ }, [token]);
return(
@@ -415,7 +429,7 @@ function PlanRoom1(){
>
}
- {/*모달들*/}
+ {/*채팅, 모달들*/}
{isModalOpen && (
diff --git a/src/pages/PlanRoom2.jsx b/src/pages/PlanRoom2.jsx
index 44e1ad5..cdf6002 100644
--- a/src/pages/PlanRoom2.jsx
+++ b/src/pages/PlanRoom2.jsx
@@ -1,22 +1,27 @@
import React, { useState } from 'react';
+import { useNavigate, useLocation } from 'react-router-dom'; // useLocation 임포트
+import KakaoMap from '../components/KakaoMap'; // KakaoMap 컴포넌트 임포트
import { planroomStyle } from '../styles/PlanRoom2style';
/** @jsxImportSource @emotion/react */
-const PlanRoom2 = ({ selectedDates }) => {
- const [zoomLevel, setZoomLevel] = useState(1);
+const PlanRoom2 = () => {
+ const navigate = useNavigate(); // useNavigate 훅 사용
+ const location = useLocation(); // useLocation 훅 사용
+ const selectedDates = location.state?.selectedDates; // 전달된 날짜 정보 받기
+ const [zoomLevel, setZoomLevel] = useState(3); // 기본 줌 레벨 설정
const [expandedAccommodation, setExpandedAccommodation] = useState(false);
const [expandedCandidates, setExpandedCandidates] = useState(null);
const handleZoomIn = () => {
- setZoomLevel(prev => prev + 0.1);
+ setZoomLevel(prev => Math.min(prev + 1, 10)); // 최대 줌 레벨 제한
};
const handleZoomOut = () => {
- setZoomLevel(prev => Math.max(prev - 0.1, 1));
+ setZoomLevel(prev => Math.max(prev - 1, 1)); // 최소 줌 레벨 제한
};
const handleCompletePlan = () => {
- alert('여행 계획이 완료되었습니다!');
+ navigate('/planroomresult'); // PlanRoomResult 페이지로 이동
};
const handleAccommodationClick = () => {
@@ -32,13 +37,13 @@ const PlanRoom2 = ({ selectedDates }) => {
return (
여행 기간: {selectedDates}
-
-
-
-
-
-
+
{/* KakaoMap 컴포넌트 추가 */}
+
+
+
+
+
장소 정보
@@ -93,4 +98,4 @@ const PlanRoom2 = ({ selectedDates }) => {
);
};
-export default PlanRoom2;
+export default PlanRoom2;
\ No newline at end of file
diff --git a/src/pages/PlanRoomResult.jsx b/src/pages/PlanRoomResult.jsx
index 54d0fa1..781647e 100644
--- a/src/pages/PlanRoomResult.jsx
+++ b/src/pages/PlanRoomResult.jsx
@@ -13,6 +13,7 @@ import L from 'leaflet';
import OutImage from '../buttonimage/outbutton.png';
import prevImage from '../buttonimage/prebutton.png';
import DownImage from '../buttonimage/down.png';
+import ExdownImage from '../buttonimage/exdown.png';
import SaveImage from '../buttonimage/save.png';
import TravelImage from '../buttonimage/travel.png';
import RouteImage from '../buttonimage/route.png';
@@ -236,8 +237,8 @@ const Save = styled.button`
const Modal = styled.div`
display: ${(props) => (props.open ? 'block' : 'none')};
position: fixed;
- top: 70%;
- left: 80%;
+ top: 80%;
+ left: 70%;
transform: translate(-50%, -50%);
width: 400px;
padding: 20px;
@@ -274,6 +275,19 @@ const DownImageContainer = styled.div`
z-index: 1000;
`;
+const ExdownImageContainer = styled.div`
+ display: ${(props) => (props.show ? 'flex' : 'none')};
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 300px;
+ height: 300px;
+ background: url(${ExdownImage}) no-repeat center center;
+ background-size: contain;
+ z-index: 1000;
+`;
+
// 기본 마커 아이콘 설정
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
@@ -323,18 +337,8 @@ function PlanRoomResult() {
const [activeRoute, setActiveRoute] = useState(1);
const [showTime, setShowTime] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);
- const [isSaved, setIsSaved] = useState(false);
- const [showDownImage, setShowDownImage] = useState(false);
-
- useEffect(() => {
- let timer;
- if (showDownImage) {
- timer = setTimeout(() => {
- setShowDownImage(false);
- }, 1000);
- }
- return () => clearTimeout(timer); // Cleanup the timer on component unmount
- }, [showDownImage]);
+ const [isExcelDownload, setIsExcelDownload] = useState(false);
+ const [isImageDownload, setIsImageDownload] = useState(false);
const handleRouteClick = (routeId) => {
setActiveRoute(routeId);
@@ -344,6 +348,26 @@ function PlanRoomResult() {
setShowTime(!showTime);
};
+ useEffect(() => {
+ let timer;
+ if (isImageDownload) {
+ timer = setTimeout(() => {
+ setIsImageDownload(false);
+ }, 1000);
+ }
+ return () => clearTimeout(timer);
+ }, [isImageDownload]);
+
+ useEffect(() => {
+ let timer;
+ if (isExcelDownload) {
+ timer = setTimeout(() => {
+ setIsExcelDownload(false);
+ }, 1000);
+ }
+ return () => clearTimeout(timer);
+ }, [isExcelDownload]);
+
// 동선에 포함된 여행지만 필터링
const activeRouteLocations = activeRoute
? routes[activeRoute].map((point) =>
@@ -364,7 +388,7 @@ function PlanRoomResult() {
};
const handleSaveClick = () => {
- setIsModalOpen(true);
+ setIsModalOpen((prevState) => !prevState); // 현재 상태의 반대로 설정
};
const handleSaveAsImage = () => {
@@ -376,9 +400,8 @@ function PlanRoomResult() {
link.download = 'travel-plan.png';
link.click();
});
- setIsSaved(true);
setIsModalOpen(false);
- setShowDownImage(true);
+ setIsImageDownload(true);
};
const handleSaveAsExcel = () => {
@@ -407,9 +430,8 @@ function PlanRoomResult() {
// 엑셀 파일 생성 및 다운로드
XLSX.writeFile(wb, 'travel-plan.xlsx');
- setIsSaved(true);
setIsModalOpen(false);
- setShowDownImage(true);
+ setIsExcelDownload(true);
};
//장소위치조정
@@ -534,7 +556,8 @@ function PlanRoomResult() {
사진으로 저장
엑셀로 저장
-
+
+
);
}
diff --git a/src/pages/SignUp.jsx b/src/pages/SignUp.jsx
index e77ea2d..c259a79 100644
--- a/src/pages/SignUp.jsx
+++ b/src/pages/SignUp.jsx
@@ -5,7 +5,7 @@ import InputSignup from '../styles/input-signup'; // 사용자 정의 입력 컴
import SearchIcon from "../assets/images/searchIcon.png";
import { useNavigate } from 'react-router-dom';
-const API_URL = 'https://jsonplaceholder.typicode.com/users';
+const API_URL = 'http://43.200.238.249:5000/users/user'; // 서버 URL
const LogoImage = styled.img`
width: 280px;
@@ -80,7 +80,18 @@ const SignUp = () => {
const handlePassword = (event) => {
const value = event.target.value;
setPassword(value);
- setPasswordError(value.length < 4 ? "비밀번호는 4자리 이상이어야 합니다." : '');
+ const passwordPattern = /^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*?_])/;
+ if (!value) {
+ setPasswordError("비밀번호를 입력해주세요.");
+ } else if (value.length < 4) {
+ setPasswordError("비밀번호는 4자리 이상이어야 합니다.");
+ } else if (value.length > 12) {
+ setPasswordError("비밀번호는 최대 12자리까지 입력 가능합니다.");
+ } else if (!passwordPattern.test(value)) {
+ setPasswordError("비밀번호는 영어, 숫자, 특수문자를 포함해야 합니다.");
+ } else {
+ setPasswordError('');
+ }
};
const handleConfirmPassword = (event) => {
@@ -92,7 +103,7 @@ const SignUp = () => {
const handleSignup = async () => {
if (!emailError && !passwordError && !confirmPasswordError && isAgreed) {
try {
- const userData = { email, password };
+ const userData = { email, password, confirmPassword };
const response = await axios.post(API_URL, userData);
if (response.status === 201) {
console.log(response);
diff --git a/src/pages/home2.jsx b/src/pages/home2.jsx
index 6d52cbb..6c7574f 100644
--- a/src/pages/home2.jsx
+++ b/src/pages/home2.jsx
@@ -12,7 +12,11 @@ const Home2 = () => {
useEffect(() => {
const fetchData = async () => {
try {
- const response = await axios.get('');
+ const response = await axios.get('/travel-plans/myRooms', {
+ headers: {
+ Authorization: `Bearer
`,
+ },
+ });
setTravel(response.data);
} catch (error) {
console.error('Error:', error);
@@ -28,9 +32,9 @@ const Home2 = () => {
const sortedTravel = [...travel].sort((a, b) => {
if (sortOption === 'latest') {
- return new Date(b.date) - new Date(a.date);
+ return new Date(b.start_date) - new Date(a.start_date);
} else if (sortOption === 'name') {
- return a.name.localeCompare(b.name);
+ return a.title.localeCompare(b.title);
}
return 0;
});
diff --git a/src/redux/appWrapper.js b/src/redux/appWrapper.js
new file mode 100644
index 0000000..872eccb
--- /dev/null
+++ b/src/redux/appWrapper.js
@@ -0,0 +1,13 @@
+import React from 'react';
+import { BrowserRouter as Router } from 'react-router-dom';
+import App from '../App';
+
+const AppWrapper = () => {
+ return (
+
+
+
+ );
+};
+
+export default AppWrapper;
\ No newline at end of file
diff --git a/src/redux/dataSlice.js b/src/redux/dataSlice.js
new file mode 100644
index 0000000..1b0e72f
--- /dev/null
+++ b/src/redux/dataSlice.js
@@ -0,0 +1,34 @@
+import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
+import axios from 'axios';
+
+// 비동기 데이터 로딩을 위한 Thunk 생성
+export const fetchData = createAsyncThunk('data/fetchData', async () => {
+ const response = await axios.get('http://43.200.238.249:5000/your-endpoint');
+ return response.data;
+});
+
+export const dataSlice = createSlice({
+ name: 'data',
+ initialState: {
+ data: null,
+ status: 'idle',
+ error: null,
+ },
+ reducers: {},
+ extraReducers: (builder) => {
+ builder
+ .addCase(fetchData.pending, (state) => {
+ state.status = 'loading';
+ })
+ .addCase(fetchData.fulfilled, (state, action) => {
+ state.status = 'succeeded';
+ state.data = action.payload;
+ })
+ .addCase(fetchData.rejected, (state, action) => {
+ state.status = 'failed';
+ state.error = action.error.message;
+ });
+ },
+});
+
+export default dataSlice.reducer;
\ No newline at end of file
diff --git a/src/redux/store.js b/src/redux/store.js
new file mode 100644
index 0000000..953a3f6
--- /dev/null
+++ b/src/redux/store.js
@@ -0,0 +1,10 @@
+import { configureStore } from '@reduxjs/toolkit';
+import dataReducer from './dataSlice';
+
+export const store = configureStore({
+ reducer: {
+ data: dataReducer,
+ },
+});
+
+export default store;
\ No newline at end of file
diff --git a/src/styles/PlanRoom2style.jsx b/src/styles/PlanRoom2style.jsx
index 5b1d15d..d03b373 100644
--- a/src/styles/PlanRoom2style.jsx
+++ b/src/styles/PlanRoom2style.jsx
@@ -17,10 +17,10 @@ export const planroomStyle = css`
.map-container {
width: 50%;
- height: 450px;
+ height: 400px;
background-color: lightgray;
position: relative;
- overflow: hidden;
+
}
.map {
diff --git a/src/styles/homestyle.jsx b/src/styles/homestyle.jsx
index bede26f..42532a2 100644
--- a/src/styles/homestyle.jsx
+++ b/src/styles/homestyle.jsx
@@ -5,7 +5,7 @@ const Home = () => {
const containerStyle = {
textAlign: 'center',
padding: '50px',
- backgroundColor: '#f8f9fa',
+ backgroundColor: '#ffffff',
};
const headingStyle = {