diff --git a/package.json b/package.json
index 753474f8..2a63f1a5 100644
--- a/package.json
+++ b/package.json
@@ -24,11 +24,13 @@
"@svgr/rollup": "^8.1.0",
"@tanstack/react-query": "^5.14.6",
"@tanstack/react-query-devtools": "^5.14.6",
+ "@types/react-beautiful-dnd": "^13.1.8",
"axios": "^1.6.2",
"date-fns": "^3.1.0",
"msw": "0.36.3",
"path": "^0.12.7",
"react": "^18.2.0",
+ "react-beautiful-dnd": "^13.1.1",
"react-dom": "^18.2.0",
"react-hook-form": "^7.49.2",
"react-infinite-scroller": "^1.2.6",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4d42035a..753ad932 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -47,6 +47,9 @@ dependencies:
'@tanstack/react-query-devtools':
specifier: ^5.14.6
version: 5.15.0(@tanstack/react-query@5.15.0)(react@18.2.0)
+ '@types/react-beautiful-dnd':
+ specifier: ^13.1.8
+ version: 13.1.8
axios:
specifier: ^1.6.2
version: 1.6.3
@@ -62,6 +65,9 @@ dependencies:
react:
specifier: ^18.2.0
version: 18.2.0
+ react-beautiful-dnd:
+ specifier: ^13.1.1
+ version: 13.1.1(react-dom@18.2.0)(react@18.2.0)
react-dom:
specifier: ^18.2.0
version: 18.2.0(react@18.2.0)
@@ -2959,6 +2965,13 @@ packages:
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
dev: false
+ /@types/hoist-non-react-statics@3.3.5:
+ resolution: {integrity: sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==}
+ dependencies:
+ '@types/react': 18.2.45
+ hoist-non-react-statics: 3.3.2
+ dev: false
+
/@types/inquirer@8.2.10:
resolution: {integrity: sha512-IdD5NmHyVjWM8SHWo/kPBgtzXatwPkfwzyP3fN1jF2g9BWt5WO+8hL2F4o2GKIYsU40PpqeevuUWvkS/roXJkA==}
dependencies:
@@ -2983,6 +2996,12 @@ packages:
/@types/prop-types@15.7.11:
resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==}
+ /@types/react-beautiful-dnd@13.1.8:
+ resolution: {integrity: sha512-E3TyFsro9pQuK4r8S/OL6G99eq7p8v29sX0PM7oT8Z+PJfZvSQTx4zTQbUJ+QZXioAF0e7TGBEcA1XhYhCweyQ==}
+ dependencies:
+ '@types/react': 18.2.45
+ dev: false
+
/@types/react-date-range@1.4.9:
resolution: {integrity: sha512-5oVEDW0ElYmY1+YVSzdMUR8stxSI5QrRJCgCFUvuEAV5197t412vimD9aVTW6g4JTaxCnMmB1BdEOT/odpaBxQ==}
dependencies:
@@ -3007,6 +3026,15 @@ packages:
'@types/react': 18.2.45
dev: true
+ /@types/react-redux@7.1.33:
+ resolution: {integrity: sha512-NF8m5AjWCkert+fosDsN3hAlHzpjSiXlVy9EgQEmLoBhaNXbmyeGs/aj5dQzKuF+/q+S7JQagorGDW8pJ28Hmg==}
+ dependencies:
+ '@types/hoist-non-react-statics': 3.3.5
+ '@types/react': 18.2.45
+ hoist-non-react-statics: 3.3.2
+ redux: 4.2.1
+ dev: false
+
/@types/react-scroll@1.8.10:
resolution: {integrity: sha512-RD4Z7grbdNGOKwKnUBKar6zNxqaW3n8m9QSrfvljW+gmkj1GArb8AFBomVr6xMOgHPD3v1uV3BrIf01py57daQ==}
dependencies:
@@ -3606,6 +3634,12 @@ packages:
which: 2.0.2
dev: true
+ /css-box-model@1.2.1:
+ resolution: {integrity: sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==}
+ dependencies:
+ tiny-invariant: 1.3.1
+ dev: false
+
/css-color-keywords@1.0.0:
resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==}
engines: {node: '>=4'}
@@ -4263,6 +4297,12 @@ packages:
resolution: {integrity: sha512-xAxZkM1dRyGV2Ou5bzMxBPNLoRCjcX+ya7KSWybQD2KwLphxsapUVK6x/02o7f4VU6GPSXch9vNY2+gkU8tYWQ==}
dev: false
+ /hoist-non-react-statics@3.3.2:
+ resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
+ dependencies:
+ react-is: 16.13.1
+ dev: false
+
/http-parser-js@0.5.8:
resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==}
dev: false
@@ -4555,6 +4595,10 @@ packages:
resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
dev: false
+ /memoize-one@5.2.1:
+ resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
+ dev: false
+
/merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@@ -5024,6 +5068,29 @@ packages:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
dev: true
+ /raf-schd@4.0.3:
+ resolution: {integrity: sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==}
+ dev: false
+
+ /react-beautiful-dnd@13.1.1(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==}
+ peerDependencies:
+ react: ^16.8.5 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0
+ dependencies:
+ '@babel/runtime': 7.23.6
+ css-box-model: 1.2.1
+ memoize-one: 5.2.1
+ raf-schd: 4.0.3
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ react-redux: 7.2.9(react-dom@18.2.0)(react@18.2.0)
+ redux: 4.2.1
+ use-memo-one: 1.1.3(react@18.2.0)
+ transitivePeerDependencies:
+ - react-native
+ dev: false
+
/react-dom@18.2.0(react@18.2.0):
resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
peerDependencies:
@@ -5056,6 +5123,10 @@ packages:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
dev: false
+ /react-is@17.0.2:
+ resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+ dev: false
+
/react-kakao-maps-sdk@1.1.24(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-leLbFwBj6zbTdDg6A9U7EwYT2oq0+2F+NHZSVTyCmmvyc4yt2zpRvUmcAt8I6h2SDUdgHbpvKAV1sZoRIxD4JQ==}
peerDependencies:
@@ -5087,6 +5158,28 @@ packages:
warning: 4.0.3
dev: false
+ /react-redux@7.2.9(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==}
+ peerDependencies:
+ react: ^16.8.3 || ^17 || ^18
+ react-dom: '*'
+ react-native: '*'
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+ react-native:
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.6
+ '@types/react-redux': 7.1.33
+ hoist-non-react-statics: 3.3.2
+ loose-envify: 1.4.0
+ prop-types: 15.8.1
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ react-is: 17.0.2
+ dev: false
+
/react-refresh@0.14.0:
resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==}
engines: {node: '>=0.10.0'}
@@ -5228,6 +5321,12 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
+ /redux@4.2.1:
+ resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==}
+ dependencies:
+ '@babel/runtime': 7.23.6
+ dev: false
+
/regenerate-unicode-properties@10.1.1:
resolution: {integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==}
engines: {node: '>=4'}
@@ -5658,6 +5757,10 @@ packages:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
dev: false
+ /tiny-invariant@1.3.1:
+ resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==}
+ dev: false
+
/tmp@0.0.33:
resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
engines: {node: '>=0.6.0'}
@@ -5806,6 +5909,14 @@ packages:
tslib: 2.6.2
dev: false
+ /use-memo-one@1.1.3(react@18.2.0):
+ resolution: {integrity: sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ dependencies:
+ react: 18.2.0
+ dev: false
+
/use-sidecar@1.1.2(@types/react@18.2.45)(react@18.2.0):
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
engines: {node: '>=10'}
diff --git a/src/@types/service.ts b/src/@types/service.ts
index 8706a8da..cf8f405b 100644
--- a/src/@types/service.ts
+++ b/src/@types/service.ts
@@ -120,3 +120,11 @@ export type TripItem = {
visitDate: string;
price: number;
};
+
+export interface pubUpdateTripItemReq {
+ visitDate: string;
+ tripItemOrder: {
+ tripItemId: number;
+ seqNum: number;
+ }[];
+}
diff --git a/src/@types/socket.types.ts b/src/@types/socket.types.ts
index be9c82fa..9434e981 100644
--- a/src/@types/socket.types.ts
+++ b/src/@types/socket.types.ts
@@ -121,13 +121,13 @@ interface pubUpdateTransportation {
}
interface pubVisitDate {
- tripId: number;
+ tripId: string;
oldVisitDate: string;
newVisitDate: string;
}
interface pubDeleteItem {
- tripId: number;
+ tripId: string;
visitDate: string;
}
diff --git a/src/api/socket.ts b/src/api/socket.ts
index 94033e68..2be1d259 100644
--- a/src/api/socket.ts
+++ b/src/api/socket.ts
@@ -2,6 +2,8 @@ import * as StompJs from '@stomp/stompjs';
export const socketClient = new StompJs.Client({
brokerURL: import.meta.env.VITE_SOCKET_URL,
+ heartbeatIncoming: 1000,
+ heartbeatOutgoing: 1000,
});
// 소켓 구독
@@ -99,6 +101,9 @@ export const pubUpdateTripItem = (
destination: `/pub/trips/${tripId}/updateTripItemOrder`,
body: JSON.stringify(pubUpdateTripItem),
});
+
+ console.log(pubUpdateTripItem);
+ console.log('펍실행');
};
// 여행 날짜별 교통 수단 변경 이벤트 발생시 (01/16 업데이트)
@@ -132,6 +137,7 @@ export const pubDeleteItem = (
destination: `/pub/tripItems/${tripItemId}/deleteItem`,
body: JSON.stringify(pubDeleteItem),
});
+ console.log(pubDeleteItem);
};
// 멤버 여정 페이지로 입장 이벤트 발생시
@@ -163,7 +169,6 @@ export const pubGetPathAndItems = (
pubGetPathAndItems: pubGetPathAndItems,
tripId: string,
) => {
- console.log('펍내부',pubGetPathAndItems);
socketClient.publish({
destination: `/pub/trips/${tripId}/getPathAndItems`,
body: JSON.stringify(pubGetPathAndItems),
diff --git a/src/assets/images/FifthMarker.png b/src/assets/images/FifthMarker.png
new file mode 100644
index 00000000..617795ce
Binary files /dev/null and b/src/assets/images/FifthMarker.png differ
diff --git a/src/assets/images/FifthSelectedMarker.png b/src/assets/images/FifthSelectedMarker.png
new file mode 100644
index 00000000..b931eb7b
Binary files /dev/null and b/src/assets/images/FifthSelectedMarker.png differ
diff --git a/src/assets/images/FirstMarker.png b/src/assets/images/FirstMarker.png
new file mode 100644
index 00000000..b317d65c
Binary files /dev/null and b/src/assets/images/FirstMarker.png differ
diff --git a/src/assets/images/FirstSelectedMarker.png b/src/assets/images/FirstSelectedMarker.png
new file mode 100644
index 00000000..632130de
Binary files /dev/null and b/src/assets/images/FirstSelectedMarker.png differ
diff --git a/src/assets/images/FourthMarker.png b/src/assets/images/FourthMarker.png
new file mode 100644
index 00000000..f1963795
Binary files /dev/null and b/src/assets/images/FourthMarker.png differ
diff --git a/src/assets/images/FourthSelectedMarker.png b/src/assets/images/FourthSelectedMarker.png
new file mode 100644
index 00000000..16fcbf5b
Binary files /dev/null and b/src/assets/images/FourthSelectedMarker.png differ
diff --git a/src/assets/images/SecondMarker.png b/src/assets/images/SecondMarker.png
new file mode 100644
index 00000000..741e7cdb
Binary files /dev/null and b/src/assets/images/SecondMarker.png differ
diff --git a/src/assets/images/SecondSelectedMarker.png b/src/assets/images/SecondSelectedMarker.png
new file mode 100644
index 00000000..d236dc12
Binary files /dev/null and b/src/assets/images/SecondSelectedMarker.png differ
diff --git a/src/assets/images/ThirdMarker.png b/src/assets/images/ThirdMarker.png
new file mode 100644
index 00000000..a55fa7fc
Binary files /dev/null and b/src/assets/images/ThirdMarker.png differ
diff --git a/src/assets/images/ThirdSelectedMarker.png b/src/assets/images/ThirdSelectedMarker.png
new file mode 100644
index 00000000..bdfe7cb3
Binary files /dev/null and b/src/assets/images/ThirdSelectedMarker.png differ
diff --git a/src/components/DetailSectionTop/DetailAddSchedule.tsx b/src/components/DetailSectionTop/DetailAddSchedule.tsx
index d805968c..d6fac6dd 100644
--- a/src/components/DetailSectionTop/DetailAddSchedule.tsx
+++ b/src/components/DetailSectionTop/DetailAddSchedule.tsx
@@ -38,21 +38,21 @@ const DetailAddSchedule = () => {
className="h-[52px] w-[52px] flex-shrink-0 flex-grow-0 rounded-lg object-cover"
/>
-
+
+
2023.12.20 - 12.22 (3박 4일)
-
+
diff --git a/src/components/Plan/PlanEditItemBox.tsx b/src/components/Plan/PlanEditItemBox.tsx
new file mode 100644
index 00000000..ae5f4873
--- /dev/null
+++ b/src/components/Plan/PlanEditItemBox.tsx
@@ -0,0 +1,190 @@
+import { PenIcon, DragAndDropIcon } from '@components/common/icons/Icons';
+import { TripItem } from '@/@types/service';
+import {
+ DragDropContext,
+ Droppable,
+ Draggable,
+ DropResult,
+} from 'react-beautiful-dnd';
+import { useState, useEffect } from 'react';
+import { pubUpdateTripItem, pubDeleteItem } from '@api/socket';
+import { useContext } from 'react';
+import { socketContext } from '@hooks/useSocket';
+import { pubUpdateTripItemReq } from '@/@types/service';
+import Alert from '@components/common/alert/Alert';
+import ToastPopUp from '@components/common/toastpopup/ToastPopUp';
+import PlanMoveItem from './PlanMoveItem';
+
+type PlanItemBoxProps = {
+ item: TripItem[];
+ day: string;
+ visitDate: string;
+ tripId: string;
+};
+
+const PlanEditItemBox = ({
+ item,
+ day,
+ visitDate,
+ tripId,
+}: PlanItemBoxProps) => {
+ if (!item) {
+ return Missing data
;
+ }
+
+ const { callBackPub } = useContext(socketContext);
+
+ const [items, setItems] = useState(item);
+ const [newData, setNewData] = useState(null);
+ const [selectedItemId, setSelectedItemId] = useState(null);
+ const [toastPopUp, setToastPopUp] = useState({
+ isPopUp: false,
+ noun: '',
+ verb: '',
+ });
+
+ const onDragEnd = (result: DropResult) => {
+ if (!result.destination) return;
+ const reorderedItems = Array.from(items);
+ const [relocatedItem] = reorderedItems.splice(result.source.index, 1);
+ reorderedItems.splice(result.destination.index, 0, relocatedItem);
+ setItems(reorderedItems);
+
+ const tripItemOrder = reorderedItems.map((item, index) => ({
+ tripItemId: item.tripItemId,
+ seqNum: index + 1,
+ }));
+
+ setNewData({
+ visitDate: visitDate,
+ tripItemOrder,
+ });
+
+ console.log(newData);
+ };
+
+ useEffect(() => {
+ if (newData && tripId) {
+ callBackPub(() => pubUpdateTripItem(newData, tripId));
+ }
+ }, [newData]);
+
+ const handleConfirm = () => {
+ if (tripId && visitDate && selectedItemId) {
+ callBackPub(() =>
+ pubDeleteItem({ tripId: tripId, visitDate: visitDate }, selectedItemId),
+ );
+ }
+ setToastPopUp(() => ({
+ isPopUp: true,
+ noun: '여행지',
+ verb: '삭제',
+ }));
+ };
+
+ const handleRadioChange = (id: number | null) => {
+ setSelectedItemId(id);
+ };
+
+ useEffect(() => {
+ if (toastPopUp.isPopUp) {
+ const timer = setTimeout(() => {
+ setToastPopUp(() => ({
+ isPopUp: false,
+ noun: '',
+ verb: '',
+ }));
+ }, 2000);
+ return () => clearTimeout(timer);
+ }
+ }, [toastPopUp]);
+
+ return (
+ <>
+ {toastPopUp.isPopUp && (
+
+ )}
+
+
+ {(provided) => (
+
+
{day}
+ {items.map((item, index) => (
+
+ {(provided) => (
+
+
+ handleRadioChange(item.tripItemId)}
+ checked={selectedItemId === item.tripItemId}>
+
+
+
+
+
+
+
+ {item.category}
+
+
+ {item.price} 원
+
+
+
+
+
+
+
+
+ )}
+
+ ))}
+ {provided.placeholder}
+
+ )}
+
+
+
+
+
선택한 장소를 삭제하시겠습니까?>}
+ onConfirm={handleConfirm}
+ closeOnConfirm={true}
+ isCheck={selectedItemId}>
+
+
+
+
+
+ >
+ );
+};
+
+export default PlanEditItemBox;
diff --git a/src/components/Plan/PlanItem.tsx b/src/components/Plan/PlanItem.tsx
index 89a28752..c27cf354 100644
--- a/src/components/Plan/PlanItem.tsx
+++ b/src/components/Plan/PlanItem.tsx
@@ -3,7 +3,8 @@ import { PlusIcon, CarIcon, BusIcon } from '@components/common/icons/Icons';
import { useNavigate } from 'react-router-dom';
import TripMap from './TripMap';
import PlanItemBox from './PlanItemBox';
-import { useContext, useEffect } from 'react';
+import PlanEditItemBox from './PlanEditItemBox';
+import { useContext, useEffect, useState } from 'react';
import { socketContext } from '@hooks/useSocket';
import { useRecoilState } from 'recoil';
import { visitDateState } from '@recoil/socket';
@@ -11,15 +12,22 @@ import { pubGetPathAndItems, pubUpdateTransportation } from '@api/socket';
import { tripIdState } from '@recoil/socket';
import { useRecoilValue } from 'recoil';
-const PlanItem = (date: any) => {
+type PlanItemProps = {
+ date: string;
+ day: string;
+};
+
+const PlanItem: React.FC = ({ date, day }) => {
const navigate = useNavigate();
+ const [isEdit, SetIsEdit] = useState(false);
+
const tripId = useRecoilValue(tripIdState);
const [visitDate, setVisitDate] = useRecoilState(visitDateState);
const { tripItem, tripPath, callBackPub } = useContext(socketContext);
useEffect(() => {
- setVisitDate({ visitDate: date.date });
- }, [date.date]);
+ setVisitDate({ visitDate: date });
+ }, [date]);
useEffect(() => {
if (visitDate && tripId) {
@@ -27,6 +35,10 @@ const PlanItem = (date: any) => {
}
}, [visitDate]);
+ const handleEdit = () => {
+ SetIsEdit((prev) => !prev);
+ };
+
const handleTranspo = (
transportation: 'CAR' | 'PUBLIC_TRANSPORTATION',
visitDate: string,
@@ -52,55 +64,77 @@ const PlanItem = (date: any) => {
{tripPath && }
-
-
- handleTranspo('CAR', visitDate?.visitDate || '', tripId || '')
- }
- className="flex h-[32px] w-[32px] cursor-pointer items-center justify-center rounded-l-md border border-solid border-gray3">
-
-
-
- handleTranspo(
- 'PUBLIC_TRANSPORTATION',
- visitDate?.visitDate || '',
- tripId || '',
- )
- }
- className="pointer-cursor -ml-[1px] flex h-[32px] w-[32px] cursor-pointer items-center justify-center rounded-r-md border border-solid border-gray3">
-
+ ) : (
+
+
+ handleTranspo('CAR', visitDate?.visitDate || '', tripId || '')
+ }
+ className="flex h-[32px] w-[32px] cursor-pointer items-center justify-center rounded-l-md border border-solid border-gray3">
+
+
+
+ handleTranspo(
+ 'PUBLIC_TRANSPORTATION',
+ visitDate?.visitDate || '',
+ tripId || '',
+ )
}
- />
+ className="pointer-cursor -ml-[1px] flex h-[32px] w-[32px] cursor-pointer items-center justify-center rounded-r-md border border-solid border-gray3">
+
+
-
-
-
+ {isEdit ? (
+
+ ) : (
+
+ )}
-
navigate('./place')}
- className="h-[40px] w-full">
-
-
+ {isEdit ? (
+ ''
+ ) : (
+
navigate('./place')}
+ className="h-[40px] w-full">
+
+
+ )}
>
);
diff --git a/src/components/Plan/PlanItemBox.tsx b/src/components/Plan/PlanItemBox.tsx
index 217e96c2..d54a922c 100644
--- a/src/components/Plan/PlanItemBox.tsx
+++ b/src/components/Plan/PlanItemBox.tsx
@@ -1,4 +1,9 @@
-import { PenIcon, CarIcon, BusIcon } from '@components/common/icons/Icons';
+import {
+ PenIcon,
+ CarIcon,
+ BusIcon,
+ SequenceIcon,
+} from '@components/common/icons/Icons';
import { TripItem, Paths } from '@/@types/service';
import { v4 as uuidv4 } from 'uuid';
@@ -6,9 +11,15 @@ type PlanItemBoxProps = {
item: TripItem[];
paths: Paths[];
transportation: string;
+ day: string;
};
-const PlanItemBox = ({ item, paths, transportation }: PlanItemBoxProps) => {
+const PlanItemBox = ({
+ item,
+ paths,
+ transportation,
+ day,
+}: PlanItemBoxProps) => {
if (!item || !paths) {
return
Missing data
;
}
@@ -18,53 +29,62 @@ const PlanItemBox = ({ item, paths, transportation }: PlanItemBoxProps) => {
return (
<>
+
{day}
{item.map((item, index) => (
- <>
-
-
-
-
-
- {item.category}
-
-
- {item.price} 원
+
+
+ {index !== 0 ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+ {item.category}
+
+
+ {item.price} 원
+
-
- {index < itemLength - 1 &&
- paths
- .filter((path) => path.fromSeqNum === item.seqNum)
- .map((path) => (
-
-
-
- {transportation === 'CAR' ? (
-
- ) : transportation === 'PUBLIC_TRANSPORTATION' ? (
-
- ) : null}
-
-
- {(path.pathInfo.totalDistance / 1000).toFixed(2)}km,{' '}
- {path.pathInfo.totalTime}분,{' '}
- {path.pathInfo.price.toLocaleString()}원
+ {index < itemLength - 1 &&
+ paths
+ .filter((path) => path.fromSeqNum === item.seqNum)
+ .map((path) => (
+
+
+
+ {transportation === 'CAR' ? (
+
+ ) : transportation === 'PUBLIC_TRANSPORTATION' ? (
+
+ ) : null}
+
+
+ {(path.pathInfo.totalDistance / 1000).toFixed(2)}km,{' '}
+ {path.pathInfo.totalTime}분,{' '}
+ {path.pathInfo.price.toLocaleString()}원
+
-
- ))}
- >
+ ))}
+
+
))}
>
diff --git a/src/components/Plan/PlanMoveItem.tsx b/src/components/Plan/PlanMoveItem.tsx
new file mode 100644
index 00000000..9601061a
--- /dev/null
+++ b/src/components/Plan/PlanMoveItem.tsx
@@ -0,0 +1,122 @@
+import * as Dialog from '@radix-ui/react-dialog';
+import { PaperIcon } from '@components/common/icons/Icons';
+import { useRecoilValue } from 'recoil';
+import { dayState, dateState } from '@recoil/plan';
+import { pubUpdateVisitDate } from '@api/socket';
+import { useContext } from 'react';
+import { socketContext } from '@hooks/useSocket';
+import { useState, useEffect } from 'react';
+import ToastPopUp from '@components/common/toastpopup/ToastPopUp';
+
+interface PlanMoveItemProps {
+ isCheck: number | null;
+ tripId: string | null;
+ visitDate: string | null;
+}
+
+const PlanMoveItem: React.FC
= ({
+ isCheck,
+ tripId,
+ visitDate,
+}) => {
+ const { callBackPub } = useContext(socketContext);
+ const day = useRecoilValue(dayState);
+ const date = useRecoilValue(dateState);
+
+ const [toastPopUp, setToastPopUp] = useState({
+ isPopUp: false,
+ noun: '',
+ verb: '',
+ });
+
+ const handleMoveItem = (newVisitDate: string) => {
+ if (visitDate === newVisitDate) {
+ return;
+ }
+ if (tripId && isCheck && visitDate) {
+ callBackPub(() =>
+ pubUpdateVisitDate(
+ {
+ tripId: tripId,
+ oldVisitDate: visitDate,
+ newVisitDate: newVisitDate,
+ },
+ isCheck,
+ ),
+ );
+ }
+ setToastPopUp(() => ({
+ isPopUp: true,
+ noun: '날짜 이동',
+ verb: '완료',
+ }));
+ };
+
+ useEffect(() => {
+ if (toastPopUp.isPopUp) {
+ const timer = setTimeout(() => {
+ setToastPopUp(() => ({
+ isPopUp: false,
+ noun: '',
+ verb: '',
+ }));
+ }, 2000);
+ return () => clearTimeout(timer);
+ }
+ }, [toastPopUp]);
+
+ return (
+ <>
+ {toastPopUp.isPopUp && (
+
+ )}
+
+
+
+ 날짜 이동
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {day.map((day, index) => (
+
+ handleMoveItem(date[index])}
+ className="relative flex flex-shrink-0 flex-grow-0 justify-start gap-2">
+
+
+ {day}
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default PlanMoveItem;
diff --git a/src/components/Plan/PlanSectionTop.tsx b/src/components/Plan/PlanSectionTop.tsx
index 37224884..c8ace510 100644
--- a/src/components/Plan/PlanSectionTop.tsx
+++ b/src/components/Plan/PlanSectionTop.tsx
@@ -8,7 +8,8 @@ import { socketContext } from '@hooks/useSocket';
import { useContext } from 'react';
import { pubEnterMember } from '@api/socket';
import { useEffect } from 'react';
-import { useRecoilValue } from 'recoil';
+import { useRecoilValue, useRecoilState } from 'recoil';
+import { dayState, dateState } from '@recoil/plan';
import { tripIdState, memberIdState } from '@recoil/socket';
import { calculateDayAndDate } from '@utils/utils';
@@ -16,6 +17,8 @@ const PlanSectionTop = () => {
const navigate = useNavigate();
const tripId = useRecoilValue(tripIdState);
const pubMember = useRecoilValue(memberIdState);
+ const [, setDay] = useRecoilState(dayState);
+ const [, setDate] = useRecoilState(dateState);
if (!pubMember || !tripId) {
return 에러
;
@@ -36,7 +39,12 @@ const PlanSectionTop = () => {
if (startDate && endDate) {
({ DayArr, DateArr } = calculateDayAndDate(startDate, endDate));
}
-
+
+ useEffect(() => {
+ setDay(DayArr);
+ setDate(DateArr);
+ }, [startDate, endDate]);
+
return (
{
(
-
+ contents={DateArr.map((date, index) => (
+
))}
/>
diff --git a/src/components/Plan/TripBudget.tsx b/src/components/Plan/TripBudget.tsx
index 8838fb25..bac3e435 100644
--- a/src/components/Plan/TripBudget.tsx
+++ b/src/components/Plan/TripBudget.tsx
@@ -1,3 +1,4 @@
+import Alert from '@components/common/alert/Alert';
import { SettingIcons } from '@components/common/icons/Icons';
import { socketContext } from '@hooks/useSocket';
import * as Progress from '@radix-ui/react-progress';
@@ -13,7 +14,9 @@ const TripBudget = () => {
useEffect(() => {
if (budget) {
setTargetBudget(budget.budget || 0);
- setCurrentSpending(budget.calculatedPrice || 0);
+ setCurrentSpending(
+ budget.calculatedPrice >= 0 ? budget.calculatedPrice : 0,
+ );
}
}, [budget]);
@@ -25,52 +28,76 @@ const TripBudget = () => {
100,
);
- // 목표 경비 설정 함수
- const handleSetTargetBudget = (newTargetBudget: number) => {
- setTargetBudget(newTargetBudget);
- };
+ // // 목표 경비 설정 함수
+ // const handleSetTargetBudget = (newTargetBudget: number) => {
+ // setTargetBudget(newTargetBudget);
+ // };
return (
-
-
사용 경비
-
-
- {budget?.calculatedPrice.toLocaleString()}
-
- 원
-
+ <>
+
+
사용 경비
+
+
+ {currentSpending.toLocaleString()}
+
+ 원
+
-
- = 100 ? 'bg-sub2' : 'bg-main2'
- } transition-transform duration-[660ms]`}
+ = 100 ? 0 : -100 + progress}%)`,
+ transform: 'translateZ(0)',
}}
- />
-
+ value={progress}>
+ = 100 ? 'bg-sub2' : 'bg-main2'
+ } transition-transform duration-[660ms]`}
+ style={{
+ transform: `translateX(${
+ progress >= 100 ? 0 : -100 + progress
+ }%)`,
+ }}
+ />
+
-
-
- 목표 경비
- handleSetTargetBudget(500000)}
- className="text-gray3">
-
-
-
-
-
{budget?.budget.toLocaleString()}
-
원
+
+
+
목표 경비
+
{
+ console.log('확인');
+ }}
+ onCancel={() => {
+ console.log('취소');
+ }}
+ children={
+
+
+
+ }
+ content={
+
+
+ 원
+
+ }
+ />
+
+
+ {targetBudget.toLocaleString()}
+ 원
+
-
+ >
);
};
diff --git a/src/components/Plan/TripMap.tsx b/src/components/Plan/TripMap.tsx
index 7c7cc026..9816403e 100644
--- a/src/components/Plan/TripMap.tsx
+++ b/src/components/Plan/TripMap.tsx
@@ -1,6 +1,16 @@
import { Paths } from '@/@types/service';
-import { useEffect, useRef } from 'react';
+import { useEffect, useRef, useState } from 'react';
import { Map, MapMarker, Polyline, useKakaoLoader } from 'react-kakao-maps-sdk';
+import FirstMarker from '@/assets/images/FirstMarker.png';
+import FirstSelectedMarker from '@/assets/images/FirstSelectedMarker.png';
+import SecondMarker from '@/assets/images/SecondMarker.png';
+import ThirdMarker from '@/assets/images/ThirdMarker.png';
+import FourthMarker from '@/assets/images/FourthMarker.png';
+import FifthMarker from '@/assets/images/FifthMarker.png';
+import SecondSelectedMarker from '@/assets/images/SecondSelectedMarker.png';
+import ThirdSelectedMarker from '@/assets/images/ThirdSelectedMarker.png';
+import FourthSelectedMarker from '@/assets/images/FourthSelectedMarker.png';
+import FifthSelectedMarker from '@/assets/images/FifthSelectedMarker.png';
const VITE_KAKAO_MAP_API_KEY = import.meta.env.VITE_KAKAO_MAP_API_KEY;
@@ -87,6 +97,44 @@ const TripMap = ({ paths }: { paths: Paths[] }) => {
setBounds();
}, [paths]);
+ // 선택된 마커의 인덱스를 추적하기 위한 상태
+ const [selectedMarker, setSelectedMarker] = useState
(null);
+
+ // ...
+
+ // 마커를 클릭할 때 호출되는 함수
+ const handleMarkerClick = (index: number) => {
+ setSelectedMarker(index);
+ };
+
+ // 각 마커에 대한 이미지를 렌더링하는 함수
+ const renderMarkerImage = (index: number, isSelected: boolean) => {
+ let svgComponent;
+ switch (index % 5) {
+ case 0:
+ svgComponent = isSelected ? FirstSelectedMarker : FirstMarker;
+ break;
+ case 1:
+ svgComponent = isSelected ? SecondSelectedMarker : SecondMarker;
+ break;
+ case 2:
+ svgComponent = isSelected ? ThirdSelectedMarker : ThirdMarker;
+ break;
+ case 3:
+ svgComponent = isSelected ? FourthSelectedMarker : FourthMarker;
+ break;
+ case 4:
+ svgComponent = isSelected ? FifthSelectedMarker : FifthMarker;
+ break;
+ default:
+ // 기본 마커가 필요한 경우 기본 마커 이미지 URL을 제공합니다.
+ return 'default_marker_image_url';
+ }
+ return svgComponent;
+ };
+
+ // ... TripMap 컴포넌트 및 나머지 코드
+
return (