diff --git a/package.json b/package.json
index 10b3be84..611f6d99 100644
--- a/package.json
+++ b/package.json
@@ -34,6 +34,7 @@
"react-beautiful-dnd": "^13.1.1",
"react-dom": "^18.2.0",
"react-hook-form": "^7.49.2",
+ "react-icons": "^5.0.1",
"react-infinite-scroller": "^1.2.6",
"react-kakao-maps-sdk": "^1.1.24",
"react-modal": "^3.16.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0720f243..71bfd5fe 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -77,6 +77,9 @@ dependencies:
react-hook-form:
specifier: ^7.49.2
version: 7.49.3(react@18.2.0)
+ react-icons:
+ specifier: ^5.0.1
+ version: 5.0.1(react@18.2.0)
react-infinite-scroller:
specifier: ^1.2.6
version: 1.2.6(react@18.2.0)
@@ -5145,6 +5148,14 @@ packages:
react: 18.2.0
dev: false
+ /react-icons@5.0.1(react@18.2.0):
+ resolution: {integrity: sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==}
+ peerDependencies:
+ react: '*'
+ dependencies:
+ react: 18.2.0
+ dev: false
+
/react-infinite-scroller@1.2.6(react@18.2.0):
resolution: {integrity: sha512-mGdMyOD00YArJ1S1F3TVU9y4fGSfVVl6p5gh/Vt4u99CJOptfVu/q5V/Wlle72TMgYlBwIhbxK5wF0C/R33PXQ==}
peerDependencies:
diff --git a/public/mockServiceWorker.js b/public/mockServiceWorker.js
deleted file mode 100644
index e180e4c6..00000000
--- a/public/mockServiceWorker.js
+++ /dev/null
@@ -1,338 +0,0 @@
-/* eslint-disable */
-/* tslint:disable */
-
-/**
- * Mock Service Worker (0.36.3).
- * @see https://github.com/mswjs/msw
- * - Please do NOT modify this file.
- * - Please do NOT serve this file on production.
- */
-
-const INTEGRITY_CHECKSUM = '02f4ad4a2797f85668baf196e553d929';
-const bypassHeaderName = 'x-msw-bypass';
-const activeClientIds = new Set();
-
-self.addEventListener('install', function () {
- return self.skipWaiting();
-});
-
-self.addEventListener('activate', async function (event) {
- return self.clients.claim();
-});
-
-self.addEventListener('message', async function (event) {
- const clientId = event.source.id;
-
- if (!clientId || !self.clients) {
- return;
- }
-
- const client = await self.clients.get(clientId);
-
- if (!client) {
- return;
- }
-
- const allClients = await self.clients.matchAll();
-
- switch (event.data) {
- case 'KEEPALIVE_REQUEST': {
- sendToClient(client, {
- type: 'KEEPALIVE_RESPONSE',
- });
- break;
- }
-
- case 'INTEGRITY_CHECK_REQUEST': {
- sendToClient(client, {
- type: 'INTEGRITY_CHECK_RESPONSE',
- payload: INTEGRITY_CHECKSUM,
- });
- break;
- }
-
- case 'MOCK_ACTIVATE': {
- activeClientIds.add(clientId);
-
- sendToClient(client, {
- type: 'MOCKING_ENABLED',
- payload: true,
- });
- break;
- }
-
- case 'MOCK_DEACTIVATE': {
- activeClientIds.delete(clientId);
- break;
- }
-
- case 'CLIENT_CLOSED': {
- activeClientIds.delete(clientId);
-
- const remainingClients = allClients.filter((client) => {
- return client.id !== clientId;
- });
-
- // Unregister itself when there are no more clients
- if (remainingClients.length === 0) {
- self.registration.unregister();
- }
-
- break;
- }
- }
-});
-
-// Resolve the "main" client for the given event.
-// Client that issues a request doesn't necessarily equal the client
-// that registered the worker. It's with the latter the worker should
-// communicate with during the response resolving phase.
-async function resolveMainClient(event) {
- const client = await self.clients.get(event.clientId);
-
- if (client.frameType === 'top-level') {
- return client;
- }
-
- const allClients = await self.clients.matchAll();
-
- return allClients
- .filter((client) => {
- // Get only those clients that are currently visible.
- return client.visibilityState === 'visible';
- })
- .find((client) => {
- // Find the client ID that's recorded in the
- // set of clients that have registered the worker.
- return activeClientIds.has(client.id);
- });
-}
-
-async function handleRequest(event, requestId) {
- const client = await resolveMainClient(event);
- const response = await getResponse(event, client, requestId);
-
- // Send back the response clone for the "response:*" life-cycle events.
- // Ensure MSW is active and ready to handle the message, otherwise
- // this message will pend indefinitely.
- if (client && activeClientIds.has(client.id)) {
- (async function () {
- const clonedResponse = response.clone();
- sendToClient(client, {
- type: 'RESPONSE',
- payload: {
- requestId,
- type: clonedResponse.type,
- ok: clonedResponse.ok,
- status: clonedResponse.status,
- statusText: clonedResponse.statusText,
- body:
- clonedResponse.body === null ? null : await clonedResponse.text(),
- headers: serializeHeaders(clonedResponse.headers),
- redirected: clonedResponse.redirected,
- },
- });
- })();
- }
-
- return response;
-}
-
-async function getResponse(event, client, requestId) {
- const { request } = event;
- const requestClone = request.clone();
- const getOriginalResponse = () => fetch(requestClone);
-
- // Bypass mocking when the request client is not active.
- if (!client) {
- return getOriginalResponse();
- }
-
- // Bypass initial page load requests (i.e. static assets).
- // The absence of the immediate/parent client in the map of the active clients
- // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
- // and is not ready to handle requests.
- if (!activeClientIds.has(client.id)) {
- return await getOriginalResponse();
- }
-
- // Bypass requests with the explicit bypass header
- if (requestClone.headers.get(bypassHeaderName) === 'true') {
- const cleanRequestHeaders = serializeHeaders(requestClone.headers);
-
- // Remove the bypass header to comply with the CORS preflight check.
- delete cleanRequestHeaders[bypassHeaderName];
-
- const originalRequest = new Request(requestClone, {
- headers: new Headers(cleanRequestHeaders),
- });
-
- return fetch(originalRequest);
- }
-
- // Send the request to the client-side MSW.
- const reqHeaders = serializeHeaders(request.headers);
- const body = await request.text();
-
- const clientMessage = await sendToClient(client, {
- type: 'REQUEST',
- payload: {
- id: requestId,
- url: request.url,
- method: request.method,
- headers: reqHeaders,
- cache: request.cache,
- mode: request.mode,
- credentials: request.credentials,
- destination: request.destination,
- integrity: request.integrity,
- redirect: request.redirect,
- referrer: request.referrer,
- referrerPolicy: request.referrerPolicy,
- body,
- bodyUsed: request.bodyUsed,
- keepalive: request.keepalive,
- },
- });
-
- switch (clientMessage.type) {
- case 'MOCK_SUCCESS': {
- return delayPromise(
- () => respondWithMock(clientMessage),
- clientMessage.payload.delay,
- );
- }
-
- case 'MOCK_NOT_FOUND': {
- return getOriginalResponse();
- }
-
- case 'NETWORK_ERROR': {
- const { name, message } = clientMessage.payload;
- const networkError = new Error(message);
- networkError.name = name;
-
- // Rejecting a request Promise emulates a network error.
- throw networkError;
- }
-
- case 'INTERNAL_ERROR': {
- const parsedBody = JSON.parse(clientMessage.payload.body);
-
- console.error(
- `\
-[MSW] Uncaught exception in the request handler for "%s %s":
-
-${parsedBody.location}
-
-This exception has been gracefully handled as a 500 response, however, it's strongly recommended to resolve this error, as it indicates a mistake in your code. If you wish to mock an error response, please see this guide: https://mswjs.io/docs/recipes/mocking-error-responses\
-`,
- request.method,
- request.url,
- );
-
- return respondWithMock(clientMessage);
- }
- }
-
- return getOriginalResponse();
-}
-
-self.addEventListener('fetch', function (event) {
- const { request } = event;
- const accept = request.headers.get('accept') || '';
-
- // Bypass server-sent events.
- if (accept.includes('text/event-stream')) {
- return;
- }
-
- // Bypass navigation requests.
- if (request.mode === 'navigate') {
- return;
- }
-
- // Opening the DevTools triggers the "only-if-cached" request
- // that cannot be handled by the worker. Bypass such requests.
- if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {
- return;
- }
-
- // Bypass all requests when there are no active clients.
- // Prevents the self-unregistered worked from handling requests
- // after it's been deleted (still remains active until the next reload).
- if (activeClientIds.size === 0) {
- return;
- }
-
- const requestId = uuidv4();
-
- return event.respondWith(
- handleRequest(event, requestId).catch((error) => {
- if (error.name === 'NetworkError') {
- console.warn(
- '[MSW] Successfully emulated a network error for the "%s %s" request.',
- request.method,
- request.url,
- );
- return;
- }
-
- // At this point, any exception indicates an issue with the original request/response.
- console.error(
- `\
-[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`,
- request.method,
- request.url,
- `${error.name}: ${error.message}`,
- );
- }),
- );
-});
-
-function serializeHeaders(headers) {
- const reqHeaders = {};
- headers.forEach((value, name) => {
- reqHeaders[name] = reqHeaders[name]
- ? [].concat(reqHeaders[name]).concat(value)
- : value;
- });
- return reqHeaders;
-}
-
-function sendToClient(client, message) {
- return new Promise((resolve, reject) => {
- const channel = new MessageChannel();
-
- channel.port1.onmessage = (event) => {
- if (event.data && event.data.error) {
- return reject(event.data.error);
- }
-
- resolve(event.data);
- };
-
- client.postMessage(JSON.stringify(message), [channel.port2]);
- });
-}
-
-function delayPromise(cb, duration) {
- return new Promise((resolve) => {
- setTimeout(() => resolve(cb()), duration);
- });
-}
-
-function respondWithMock(clientMessage) {
- return new Response(clientMessage.payload.body, {
- ...clientMessage.payload,
- headers: clientMessage.payload.headers,
- });
-}
-
-function uuidv4() {
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
- const r = (Math.random() * 16) | 0;
- const v = c == 'x' ? r : (r & 0x3) | 0x8;
- return v.toString(16);
- });
-}
diff --git a/src/@types/member.types.ts b/src/@types/member.types.ts
index 3b8146ac..2a6f4767 100644
--- a/src/@types/member.types.ts
+++ b/src/@types/member.types.ts
@@ -18,7 +18,7 @@ interface MemberInfo {
// | 'TEENAGER'
// | 'TWENTIES'
// | 'THIRTIES'
- // | 'FOURTIES'
+ // | 'FORTIES'
// | 'ABOVE_FIFTIES'
// | 'DEFATULT'
genderType: string | null;
diff --git a/src/@types/service.ts b/src/@types/service.ts
index bae375b2..b0fe82ae 100644
--- a/src/@types/service.ts
+++ b/src/@types/service.ts
@@ -97,12 +97,26 @@ export type subBudgetRes = {
} | null;
};
+export type subCursorRes = {
+ status: number;
+ message: string;
+ data: {
+ tripId: string;
+ visitDate: string;
+ memberId: number;
+ name: string;
+ x: number;
+ y: number;
+ } | null;
+};
+
export type SocketContextType = {
tripInfo: subInfoRes | null;
tripItem: subItemRes | null;
tripPath: subPathRes | null;
tripMember: subMemberRes | null;
tripBudget: subBudgetRes | null;
+ tripCursor: subCursorRes | null;
tripId: string;
callBackPub: (callback: () => void) => void;
};
diff --git a/src/@types/socket.types.ts b/src/@types/socket.types.ts
index 49244096..227a7467 100644
--- a/src/@types/socket.types.ts
+++ b/src/@types/socket.types.ts
@@ -82,6 +82,19 @@ type subBudgetMessage = (response: {
};
}) => void;
+type subCursorMessage = (response: {
+ status: number;
+ message: string;
+ data: {
+ tripId: string;
+ visitDate: string;
+ memberId: number;
+ name: string;
+ x: number;
+ y: number;
+ };
+}) => void;
+
interface pubInfo {
startDate: string;
endDate: string;
@@ -100,7 +113,7 @@ interface pubAddTripItem {
interface pubUpdatePrice {
tripId: string;
visitDate: string;
- price: number;
+ price: string;
}
interface pubUpdateTripItem {
@@ -138,3 +151,10 @@ interface pubGetPathAndItems {
interface pubUpdateBudget {
budget: number;
}
+
+interface pubCursor {
+ token: string;
+ visitDate: string;
+ x: number;
+ y: number;
+}
diff --git a/src/@types/trips.types.ts b/src/@types/trips.types.ts
index 6b6bceec..e2ebbb0c 100644
--- a/src/@types/trips.types.ts
+++ b/src/@types/trips.types.ts
@@ -13,6 +13,7 @@ interface MyTripType {
startDate: string;
endDate: string;
numberOfPeople: number;
+ numberOfTripMember: number;
tripStatus: string;
tripThumbnailUrl: string;
area: string;
diff --git a/src/api/socket.ts b/src/api/socket.ts
index 0ade3027..ff4101db 100644
--- a/src/api/socket.ts
+++ b/src/api/socket.ts
@@ -61,6 +61,18 @@ export const subBudget = (
});
};
+// 커서 공유
+export const subCursor = (
+ tripId: string,
+ visitDate: string,
+ subCursorMessage: subCursorMessage,
+) => {
+ socketClient.subscribe(`/sub/${tripId}/cursor/${visitDate}`, (message) => {
+ const res = JSON.parse(message.body);
+ subCursorMessage(res);
+ });
+};
+
// 소켓 전송
// 여정 기본 정보 변경 이벤트 발생시
export const pubInfo = (pubInfo: pubInfo, tripId: string) => {
@@ -84,7 +96,7 @@ export const pubAddTripItem = (
// 여행 아이템 예상 가격 업데이트 이벤트 발생시
export const pubUpdatePrice = (
pubUpdatePrice: pubUpdatePrice,
- tripItemId: string,
+ tripItemId: number,
) => {
socketClient.publish({
destination: `/pub/tripItems/${tripItemId}/updatePrice`,
@@ -106,10 +118,10 @@ export const pubUpdateTripItem = (
// 여행 날짜별 교통 수단 변경 이벤트 발생시 (01/16 업데이트)
export const pubUpdateTransportation = (
pubUpdateTransportation: pubUpdateTransportation,
- trips: string,
+ tripId: string,
) => {
socketClient.publish({
- destination: `/pub/trips/${trips}/updateTransportation`,
+ destination: `/pub/trips/${tripId}/updateTransportation`,
body: JSON.stringify(pubUpdateTransportation),
});
};
@@ -142,6 +154,7 @@ export const pubConnectMember = (pubMember: pubMember, tripId: string) => {
destination: `/pub/trips/${tripId}/connectMember`,
body: JSON.stringify(pubMember),
});
+ console.log('입장발생');
};
// 멤버 여정 페이지 퇴장 이벤트 발생시
@@ -150,6 +163,7 @@ export const pubDisconnectMember = (pubMember: pubMember, tripId: string) => {
destination: `/pub/trips/${tripId}/disconnectMember`,
body: JSON.stringify(pubMember),
});
+ console.log('퇴장발생');
};
// 여정 편집 페이지 입장 이벤트 발생시(모든 sub 다받음)
@@ -187,3 +201,11 @@ export const pubUpdateBudget = (
body: JSON.stringify(pubUpdateBudget),
});
};
+
+// 커서공유
+export const pubCursor = (pubCursor: pubCursor, tripId: string) => {
+ socketClient.publish({
+ destination: `/pub/trips/${tripId}/cursor`,
+ body: JSON.stringify(pubCursor),
+ });
+};
diff --git a/src/api/trips.ts b/src/api/trips.ts
index 0a89bb54..a33f5e38 100644
--- a/src/api/trips.ts
+++ b/src/api/trips.ts
@@ -21,7 +21,7 @@ export const putTrips = async (
// 여행 상세페이지에서 여정에 여행지 등록
export const postTripsItem = async (
tripId: string,
- tourItemId: number,
+ tourItemId: string,
visitDate: string,
) => {
const requestBody = {
diff --git a/src/components/Auth/AuthSurvey/AuthSurvey.tsx b/src/components/Auth/AuthSurvey/AuthSurvey.tsx
index 7486b522..a4d02834 100644
--- a/src/components/Auth/AuthSurvey/AuthSurvey.tsx
+++ b/src/components/Auth/AuthSurvey/AuthSurvey.tsx
@@ -9,7 +9,7 @@ import { UserInfoState } from '@recoil/Auth.atom';
import { useRecoilState } from 'recoil';
interface Props {
- path: string;
+ path?: string;
}
const AuthSurvey = ({ path }: Props) => {
@@ -42,7 +42,11 @@ const AuthSurvey = ({ path }: Props) => {
// newPrevUserInfo.survey = data;
// return newPrevUserInfo;
// });
- navigate(path);
+ if (path) {
+ navigate(path);
+ } else {
+ navigate(-1);
+ }
}
} catch (err) {
console.error(err);
diff --git a/src/components/DatePicker/Calendar.tsx b/src/components/DatePicker/Calendar.tsx
index 1db551ab..16eb0172 100644
--- a/src/components/DatePicker/Calendar.tsx
+++ b/src/components/DatePicker/Calendar.tsx
@@ -173,7 +173,7 @@ const Calendar: React.FC<{
: '날짜를 선택해주세요.'}
-
+
{visibleMonths.map((month, idx) => (
{renderCalendar(month)}
))}
diff --git a/src/components/DetailSectionBottom/DetailReviewStats.tsx b/src/components/DetailSectionBottom/DetailReviewStats.tsx
index f87f181b..7a2342b4 100644
--- a/src/components/DetailSectionBottom/DetailReviewStats.tsx
+++ b/src/components/DetailSectionBottom/DetailReviewStats.tsx
@@ -9,6 +9,7 @@ const DetailReviewStats = () => {
const { reviewStats } = useGetToursReviews();
const { calculateWidth, getColor } = useReviewStatsCalculator(reviewStats);
const [showAll, setShowAll] = useState(false);
+ console.log(reviewStats);
return (
<>
diff --git a/src/components/DetailSectionBottom/DetailReviews.tsx b/src/components/DetailSectionBottom/DetailReviews.tsx
index fe0e7e83..8d526846 100644
--- a/src/components/DetailSectionBottom/DetailReviews.tsx
+++ b/src/components/DetailSectionBottom/DetailReviews.tsx
@@ -101,7 +101,6 @@ export default function DetailReviews({ reviewData }: reviewProps) {
};
useEffect(() => {
- console.log('toursReviews', toursReviews);
{
toursReviews?.pages.map((group) => {
setReviewDataLength(group?.data.data.reviewTotalCount);
diff --git a/src/components/DetailSectionBottom/ReviewItem.tsx b/src/components/DetailSectionBottom/ReviewItem.tsx
index 4bc2caec..57a89c8c 100644
--- a/src/components/DetailSectionBottom/ReviewItem.tsx
+++ b/src/components/DetailSectionBottom/ReviewItem.tsx
@@ -1,4 +1,9 @@
-import { StarIcon, ChatIcon, MoreIcon } from '@components/common/icons/Icons';
+import {
+ StarIcon,
+ ChatIcon,
+ MoreIcon,
+ UserIcon,
+} from '@components/common/icons/Icons';
import { useSetRecoilState, useRecoilState } from 'recoil';
import {
isModalOpenState,
@@ -16,7 +21,6 @@ import {
import { MouseEvent, useState } from 'react';
import { getEmoji } from '@utils/utils';
import { getStarFill } from '@utils/getStarFill';
-import { ReactComponent as NullUser } from '@assets/images/NullUser.svg';
interface Keyword {
keywordId: number;
@@ -121,16 +125,19 @@ const Item: React.FC
= (props: ItemProps) => {
) : (
-
+
+
+
)}
-
{authorNickname}
+
{authorNickname}
-
+
{Array.from({ length: 5 }, (_, index) => (
= (props: ItemProps) => {
/>
))}
-
+
{formatCreatedTime(createdTime)}
@@ -160,7 +167,7 @@ const Item: React.FC
= (props: ItemProps) => {
{content.length > 55 ? `${content.slice(0, 55)}...` : content}
) : (
-
{content}
+
{content}
)}
@@ -199,7 +206,7 @@ const Item: React.FC
= (props: ItemProps) => {
.map((keyword, idx) => (
+ className="rounded-md bg-gray1 px-2 py-1 text-gray6">
{getEmoji(keyword.content)} {keyword.content}
))}
diff --git a/src/components/DetailSectionTop/DetailAddSchedule.tsx b/src/components/DetailSectionTop/DetailAddSchedule.tsx
index 57880903..fbbc8361 100644
--- a/src/components/DetailSectionTop/DetailAddSchedule.tsx
+++ b/src/components/DetailSectionTop/DetailAddSchedule.tsx
@@ -1,118 +1,255 @@
import * as Dialog from '@radix-ui/react-dialog';
-import { CalendarIcon } from '@components/common/icons/Icons';
+import { CalendarIcon, GrayCalendarIcon } from '@components/common/icons/Icons';
import Alert from '@components/common/alert/Alert';
import { useNavigate } from 'react-router-dom';
import { PlusIcon } from '@components/common/icons/Icons';
import { useGetMyTrips } from '@hooks/useGetMyTrips';
import { calculateTripDuration } from '@utils/calculateTripDuration';
import { calculateDayAndDate } from '@utils/utils';
+import Accordion from '@components/common/accordion/Accordion';
+import { useState, useEffect } from 'react';
+import { postTripsItem } from '@api/trips';
+import { useParams } from 'react-router-dom';
+import { Swiper, SwiperSlide } from 'swiper/react';
const DetailAddSchedule = () => {
- const { myTrips } = useGetMyTrips();
+ const token = localStorage.getItem('accessToken');
- const { SmallDayArr } = calculateDayAndDate(
- myTrips[0]?.startDate,
- myTrips[0]?.endDate,
+ const navigate = useNavigate();
+ const { id: tourItemId } = useParams();
+ const { myTrips } = useGetMyTrips();
+ const [isOpen, setIsOpen] = useState(false);
+ const [openAccordion, setOpenAccordion] = useState('');
+ const [isProcessing, setIsProcessing] = useState(false);
+ const [selectedVisitDate, setSelectedVisitDate] = useState(
+ null,
+ );
+ const [selectedTripId, setSelectedTripId] = useState(null);
+ const [selectedButton, setSelectedButton] = useState(null);
+ const initialValue = myTrips.findIndex(
+ (trip) => trip.tripStatus !== '여행완료',
);
- console.log(SmallDayArr);
+ const handleNavigate = (url: string) => {
+ navigate(url);
+ };
- const navigate = useNavigate();
+ const handleDateButtonClick = (
+ tripId: string | null,
+ visitDate: string | null,
+ index: number | null,
+ ) => {
+ setSelectedTripId(tripId);
+ setSelectedVisitDate(visitDate);
+ setSelectedButton(index);
+ };
- const handleConfirm = () => {
- navigate('/login');
+ const handleAccordion = (newValue: string) => {
+ setOpenAccordion(newValue);
+ setSelectedButton(null);
};
- const handleCreate = () => {
- navigate('/create');
+ const handlePostTripsItem = async (
+ tripId: string,
+ tourItemId: string,
+ visitDate: string,
+ ) => {
+ if (isProcessing) return;
+ setIsProcessing(true);
+ if (tripId && tourItemId && visitDate) {
+ try {
+ await postTripsItem(tripId, tourItemId, visitDate);
+ setIsOpen(false);
+ } catch (error) {
+ console.error('요청 실패:', error);
+ } finally {
+ setIsProcessing(false);
+ }
+ }
};
- return (
-
-
-
-
+ useEffect(() => {
+ setOpenAccordion(`item-${initialValue}`);
+ }, [initialValue]);
-
-
+ useEffect(() => {
+ if (!isOpen) {
+ setOpenAccordion(`item-${initialValue}`);
+ setSelectedButton(null);
+ }
+ }, [isOpen]);
-
-
-
-
-
- {myTrips.map((trip, index) => {
- // 각 여행에 대한 기간을 계산합니다.
- const tripDuration = calculateTripDuration(
- trip.startDate,
- trip.endDate,
- );
-
- return (
-
-
-
-
-
-
- {trip.tripName}
-
-
- {trip.startDate?.replace(/-/g, '.')} -{' '}
- {trip.endDate?.substring(5).replace(/-/g, '.')} (
- {tripDuration})
-
-
-
+
+
+
+ 0 ? 'h-[392px]' : 'h-[276px]'
+ }`}>
+
+
handleNavigate('/create')}
+ className="btn-base mb-[14px] h-[40px] w-[145px] rounded-full bg-main2 text-[14px] font-bold text-white disabled:cursor-not-allowed disabled:bg-gray3">
+
-
- );
- })}
-
-
-
-
- 일정 추가 시 로그인이 필요합니다.
-
- 로그인 하시겠습니까?
- >
- }
- onConfirm={handleConfirm}>
-
-
- 일정 추가하기
-
-
-
-
-
-
+ {myTrips.length > 0 ? (
+
+ {myTrips.map((trip, index) => {
+ if (trip.tripStatus !== '여행완료') {
+ const tripDuration = calculateTripDuration(
+ trip.startDate,
+ trip.endDate,
+ );
+ const { SmallDayArr, DateArr } = calculateDayAndDate(
+ trip.startDate,
+ trip.endDate,
+ );
+ return (
+ <>
+
+
+
+
+
+
+ {trip.tripName}
+
+
+ {trip.startDate?.replace(/-/g, '.')} -{' '}
+ {trip.endDate
+ ?.substring(5)
+ .replace(/-/g, '.')}{' '}
+ {tripDuration === '0박 1일'
+ ? ''
+ : `(${tripDuration})`}
+
+
+
+
+
+ }
+ content={
+
+
+ {SmallDayArr.map((day, index) => (
+
+
+ handleDateButtonClick(
+ trip.tripId,
+ DateArr[index],
+ index,
+ )
+ }>
+
+ {day}
+
+
+
+ ))}
+
+
+ }
+ />
+ >
+ );
+ }
+ })}
+
+ ) : (
+
+
+
+ )}
+ {myTrips.length > 0 ? (
+
+
+ handlePostTripsItem(
+ selectedTripId || '',
+ tourItemId || '',
+ selectedVisitDate || '',
+ )
+ }
+ className="btn-base h-14 bg-main2 text-base font-bold text-white disabled:cursor-not-allowed disabled:bg-gray3">
+ 일정 추가하기
+
+
+ ) : (
+ ''
+ )}
+
+
+
+ ) : (
+
+ 새로운 여행 생성 시 로그인이 필요합니다.
+
+ 로그인 하시겠습니까?
+ >
+ }
+ onConfirm={() => handleNavigate('/login')}>
+
+
+ 일정 추가
+
+
+ )}
+ >
);
};
diff --git a/src/components/DetailSectionTop/DetailSectionTop.tsx b/src/components/DetailSectionTop/DetailSectionTop.tsx
index b6283042..7d4ad2c8 100644
--- a/src/components/DetailSectionTop/DetailSectionTop.tsx
+++ b/src/components/DetailSectionTop/DetailSectionTop.tsx
@@ -1,9 +1,7 @@
// import { useEffect, useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { useParams } from 'react-router-dom';
-
import { getDetailTours, getToursReviews } from '@api/tours';
-
import {
DetailToursButtons,
DetailToursInfo,
diff --git a/src/components/DetailSectionTop/DetailToursMap.tsx b/src/components/DetailSectionTop/DetailToursMap.tsx
index 4413e86f..07871ac0 100644
--- a/src/components/DetailSectionTop/DetailToursMap.tsx
+++ b/src/components/DetailSectionTop/DetailToursMap.tsx
@@ -1,7 +1,6 @@
-import { useState } from 'react';
import { Map, MapMarker } from 'react-kakao-maps-sdk';
import { useKakaoLoader } from 'react-kakao-maps-sdk';
-import { PhoneIcon, MapIcon, DownIcon } from '@components/common/icons/Icons';
+import { PhoneIcon, MapIcon } from '@components/common/icons/Icons';
const VITE_KAKAO_MAP_API_KEY = import.meta.env.VITE_KAKAO_MAP_API_KEY;
@@ -11,8 +10,6 @@ interface DetailToursMapProps {
export default function DetailToursMap({ mapData }: DetailToursMapProps) {
const { fullAddress, longitude, latitude, tel } = mapData;
- const [isMapVisible, setIsMapVisible] = useState
(false);
- const [isAddressVisible, setIsAddressVisible] = useState(false);
const [_] = useKakaoLoader({
appkey: VITE_KAKAO_MAP_API_KEY,
@@ -20,37 +17,13 @@ export default function DetailToursMap({ mapData }: DetailToursMapProps) {
const MapStyle = {
width: '100%',
- height: isMapVisible ? 0 : '180px',
- marginTop: isMapVisible ? '15px' : '15px',
- marginBottom: isMapVisible ? '15px' : '15px',
- transition: 'height 0.3s ease-in-out',
- };
-
- const closeMap = () => {
- setIsMapVisible((prev) => !prev);
+ height: '180px',
+ marginTop: '5px',
+ marginBottom: '15px',
};
return (
-
-
setIsAddressVisible(!isAddressVisible)}>
-
- {!isAddressVisible ? (
-
- {fullAddress}
-
- ) : (
-
- {fullAddress}
-
- )}
-
-
-
-
-
-
-
-
-
- {tel ? tel : '전화번호가 없어요'}
+
+
+
+
+
+ {fullAddress}
-
+
+
+
+
+ {tel ? tel : '전화번호가 없어요'}
+
+
diff --git a/src/components/DetailSectionTop/DetailToursRating.tsx b/src/components/DetailSectionTop/DetailToursRating.tsx
index 2be49e9c..d92303d2 100644
--- a/src/components/DetailSectionTop/DetailToursRating.tsx
+++ b/src/components/DetailSectionTop/DetailToursRating.tsx
@@ -67,7 +67,6 @@ export default function DetailToursRating({
);
})}
-
({reviewTotalCount})
diff --git a/src/components/MyTrip/MyTripIngItem.tsx b/src/components/MyTrip/MyTripIngItem.tsx
index dcac90dd..ca0d08d4 100644
--- a/src/components/MyTrip/MyTripIngItem.tsx
+++ b/src/components/MyTrip/MyTripIngItem.tsx
@@ -37,7 +37,8 @@ const MyTripIngItem: React.FC = ({ myTripList }) => {
{startDate.replace(/-/g, '.')} ~{' '}
- {endDate.replace(/-/g, '.').split('2024.')} ({tripDuration})
+ {endDate.replace(/-/g, '.').split('2024.')}{' '}
+ {tripDuration === '0박 1일' ? null : ` (${tripDuration})`}
diff --git a/src/components/MyTrip/MyTripItem.tsx b/src/components/MyTrip/MyTripItem.tsx
index f327aa9c..72375a66 100644
--- a/src/components/MyTrip/MyTripItem.tsx
+++ b/src/components/MyTrip/MyTripItem.tsx
@@ -21,7 +21,7 @@ const MyTripItem: React.FC
= ({ myTripList }) => {
tripName,
startDate,
endDate,
- numberOfPeople,
+ numberOfTripMember,
tripThumbnailUrl,
} = myTripList;
@@ -44,7 +44,7 @@ const MyTripItem: React.FC = ({ myTripList }) => {
destructive={true}
onClick={() => deleteMyTripMutate(tripId)}>
- 삭제
+ 나가기
@@ -71,13 +71,14 @@ const MyTripItem: React.FC = ({ myTripList }) => {
{startDate.replace(/-/g, '.')} -{' '}
- {endDate.replace(/-/g, '.').split('2024.')} ({tripDuration})
+ {endDate.replace(/-/g, '.').split('2024.')}
+ {tripDuration === '0박 1일' ? null : ` (${tripDuration})`}
- {numberOfPeople}명과 공유중
+ {numberOfTripMember}명과 공유중
diff --git a/src/components/Plan/PlanCursor.tsx b/src/components/Plan/PlanCursor.tsx
new file mode 100644
index 00000000..48d10a01
--- /dev/null
+++ b/src/components/Plan/PlanCursor.tsx
@@ -0,0 +1,94 @@
+import { useEffect, useState, useContext } from 'react';
+import { BsFillCursorFill } from 'react-icons/bs';
+import { pubCursor } from '@api/socket';
+import { socketContext } from '@hooks/useSocket';
+import { useGetTripsAuthority } from '@hooks/useGetTripsAuthority';
+
+type TripCursorData = {
+ memberId: number;
+ x: number;
+ y: number;
+ name: string;
+};
+
+type PlanCursorProps = {
+ date: string;
+};
+
+const PlanCursor = ({ date }: PlanCursorProps) => {
+ const token = localStorage.getItem('accessToken');
+ const { memberId } = useGetTripsAuthority();
+ const { callBackPub, tripId, tripMember } = useContext(socketContext);
+ const [position, setPosition] = useState({ x: 0, y: 0 });
+
+ const myName = tripMember?.data?.tripMembers.find(
+ (member) => member.memberId === memberId,
+ );
+
+ useEffect(() => {
+ const handleMouseMove = (e: MouseEvent) => {
+ setPosition({ x: e.clientX, y: e.clientY });
+ };
+ const cursorStyle = (style: string): void => {
+ document.querySelectorAll('*').forEach((el) => {
+ const element = el as HTMLElement;
+ element.style.cursor = style;
+ });
+ };
+ cursorStyle('none');
+ document.addEventListener('mousemove', handleMouseMove);
+ return () => {
+ cursorStyle('auto');
+ document.removeEventListener('mousemove', handleMouseMove);
+ };
+ }, []);
+
+ // useEffect(() => {
+ // if (token && position && myName && date && tripId) {
+ // const timeoutId = setTimeout(() => {
+ // callBackPub(() =>
+ // pubCursor(
+ // {
+ // token: token,
+ // visitDate: date,
+ // x: position.x,
+ // y: position.y,
+ // },
+ // tripId,
+ // ),
+ // );
+ // }, 1000);
+
+ // return () => clearTimeout(timeoutId);
+ // }
+ // }, [position]);
+
+ useEffect(() => {
+ if (token && position && myName && date && tripId) {
+ callBackPub(() =>
+ pubCursor(
+ {
+ token: token,
+ visitDate: date,
+ x: position.x,
+ y: position.y,
+ },
+ tripId,
+ ),
+ );
+ }
+ }, [position]);
+
+ return (
+
+ );
+};
+
+export default PlanCursor;
diff --git a/src/components/Plan/PlanEditItemBox.tsx b/src/components/Plan/PlanEditItemBox.tsx
index 4c5ce80d..1e1a6fec 100644
--- a/src/components/Plan/PlanEditItemBox.tsx
+++ b/src/components/Plan/PlanEditItemBox.tsx
@@ -1,4 +1,4 @@
-import { PenIcon, DragAndDropIcon } from '@components/common/icons/Icons';
+import { DragAndDropIcon } from '@components/common/icons/Icons';
import { TripItem } from '@/@types/service';
import {
DragDropContext,
@@ -139,9 +139,10 @@ const PlanEditItemBox = ({
alt="img"
/>
-
- {item.name}
-
+
+ {item.name.length > 17
+ ? item.name.slice(0, 17) + '...'
+ : item.name}
{item.category}
@@ -166,7 +167,7 @@ const PlanEditItemBox = ({
)}
-
+
= ({ date, day }) => {
return (
<>
- {tripPath && }
+
+
+ {tripPath && }
{tripAuthority !== 'WRITE' || isEdit ? (
@@ -116,6 +120,7 @@ const PlanItem: React.FC
= ({ date, day }) => {
item={tripItem?.data?.tripItems || []}
paths={tripPath?.data?.paths || []}
transportation={transpo}
+ visitDate={date || ''}
day={day}
/>
)}
@@ -127,7 +132,7 @@ const PlanItem: React.FC = ({ date, day }) => {
navigate('./place')}
- className="h-[40px] w-full">
+ className="h-[56px] w-full">
장소 추가하기
diff --git a/src/components/Plan/PlanItemBox.tsx b/src/components/Plan/PlanItemBox.tsx
index d54a922c..7d269110 100644
--- a/src/components/Plan/PlanItemBox.tsx
+++ b/src/components/Plan/PlanItemBox.tsx
@@ -3,15 +3,22 @@ import {
CarIcon,
BusIcon,
SequenceIcon,
+ CloseIcon,
} from '@components/common/icons/Icons';
import { TripItem, Paths } from '@/@types/service';
import { v4 as uuidv4 } from 'uuid';
+import { useGetTripsAuthority } from '@hooks/useGetTripsAuthority';
+import Alert from '@components/common/alert/Alert';
+import { useContext, useState } from 'react';
+import { socketContext } from '@hooks/useSocket';
+import { pubUpdatePrice } from '@api/socket';
type PlanItemBoxProps = {
item: TripItem[];
paths: Paths[];
transportation: string;
day: string;
+ visitDate: string;
};
const PlanItemBox = ({
@@ -19,12 +26,31 @@ const PlanItemBox = ({
paths,
transportation,
day,
+ visitDate,
}: PlanItemBoxProps) => {
if (!item || !paths) {
return
Missing data
;
}
+ const { tripAuthority } = useGetTripsAuthority();
+ const { tripId } = useContext(socketContext);
const itemLength = item.length;
+ const [inputPrice, setInputPrice] = useState('');
+ const showCloseIcon = inputPrice;
+
+ const handlePrice = (inputBudget: string, tripItemId: number) => {
+ if (inputBudget && tripItemId) {
+ pubUpdatePrice(
+ {
+ tripId: tripId,
+ visitDate: visitDate,
+ price: inputBudget,
+ },
+ tripItemId,
+ );
+ setInputPrice('');
+ }
+ };
return (
<>
@@ -41,22 +67,61 @@ const PlanItemBox = ({
-
+
-
- {item.name}
-
+
+ {item.name.length > 19
+ ? item.name.slice(0, 19) + '...'
+ : item.name}
{item.category}
-
- {item.price} 원
+
+ {item.price.toLocaleString()} 원
+ {tripAuthority == 'WRITE' && (
+
+ handlePrice(inputPrice, item.tripItemId)
+ }
+ closeOnConfirm={true}
+ children={
+
+
+
+ }
+ content={
+
+
+
setInputPrice(e.target.value)}
+ />
+
setInputPrice('')}>
+ {showCloseIcon && (
+
+ )}
+
+
+
+ 원
+
+
+ }
+ />
+ )}
@@ -76,9 +141,15 @@ const PlanItemBox = ({
) : null}
- {(path.pathInfo.totalDistance / 1000).toFixed(2)}km,{' '}
- {path.pathInfo.totalTime}분,{' '}
- {path.pathInfo.price.toLocaleString()}원
+ {path.pathInfo.totalDistance < 0 ||
+ path.pathInfo.totalTime < 0 ||
+ path.pathInfo.price < 0
+ ? '경로 정보가 없습니다.'
+ : `${(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
index 0daa5b5f..fdfa5d84 100644
--- a/src/components/Plan/PlanMoveItem.tsx
+++ b/src/components/Plan/PlanMoveItem.tsx
@@ -85,17 +85,17 @@ const PlanMoveItem: React.FC
= ({
-
+
-
-
+
-
-
+
+
{day.map((day, index) => (
{
+ const { memberId } = useGetTripsAuthority();
+ const { tripCursor } = useContext(socketContext);
+ const [otherCursors, setOtherCursors] = useState([]);
+
+ console.log(otherCursors);
+ useEffect(() => {
+ if (
+ tripCursor &&
+ tripCursor.data &&
+ tripCursor.data.memberId !== memberId
+ ) {
+ setOtherCursors((prevCursors) => {
+ const existingCursorIndex = prevCursors.findIndex(
+ (cursor) => cursor.memberId === tripCursor.data!.memberId,
+ );
+
+ if (existingCursorIndex !== -1) {
+ const updatedCursors = [...prevCursors];
+ updatedCursors[existingCursorIndex] = tripCursor.data!;
+ return updatedCursors;
+ } else {
+ return [...prevCursors, tripCursor.data!];
+ }
+ });
+ }
+ }, [tripCursor, memberId]);
+
+ return (
+ <>
+ {otherCursors.map((cursor, index) => (
+
+ ))}
+ >
+ );
+};
+
+export default PlanOtherCursor;
diff --git a/src/components/Plan/PlanSchedule.tsx b/src/components/Plan/PlanSchedule.tsx
index 4e96dc4f..2571a865 100644
--- a/src/components/Plan/PlanSchedule.tsx
+++ b/src/components/Plan/PlanSchedule.tsx
@@ -20,7 +20,8 @@ const PlanSchedule = () => {
- {trip?.startDate} ~ {trip?.endDate}
+ {trip?.startDate?.substring(2).replace(/-/g, '.') || ''} -{' '}
+ {trip?.endDate?.substring(5).replace(/-/g, '.') || ''}
);
diff --git a/src/components/Plan/PlanSectionTop.tsx b/src/components/Plan/PlanSectionTop.tsx
index 480ec571..fada423c 100644
--- a/src/components/Plan/PlanSectionTop.tsx
+++ b/src/components/Plan/PlanSectionTop.tsx
@@ -1,4 +1,4 @@
-import TripRealtimeEditor from '@components/Trip/TripRealtimeEditor';
+import TripRealtimeMember from '@components/Trip/TripRealtimeMember';
import { BackBox } from '@components/common';
import { useNavigate } from 'react-router-dom';
import TripBudget from './TripBudget';
@@ -92,7 +92,7 @@ const PlanSectionTop = () => {
navigate(`/trip/${tripId}/share`);
}}
/>
-
+
{
const { tripAuthority } = useGetTripsAuthority();
- const { tripBudget, tripId } = useContext(socketContext);
+ const { callBackPub, tripBudget, tripId } = useContext(socketContext);
const budget = tripBudget?.data;
@@ -28,11 +28,13 @@ const TripBudget = () => {
const handleSetTargetBudget = (inputBudget: string) => {
const newTargetBudget = parseInt(inputBudget, 10); // 문자열 숫자로 변환
if (!isNaN(newTargetBudget) && newTargetBudget !== budget?.budget) {
- pubUpdateBudget(
- {
- budget: newTargetBudget,
- },
- tripId || '',
+ callBackPub(() =>
+ pubUpdateBudget(
+ {
+ budget: newTargetBudget,
+ },
+ tripId || '',
+ ),
);
setInputBudget('');
}
@@ -111,11 +113,11 @@ const TripBudget = () => {
}
content={
-
+
setInputBudget(e.target.value)}
diff --git a/src/components/Plan/TripMap.tsx b/src/components/Plan/TripMap.tsx
index 94dac763..60b40607 100644
--- a/src/components/Plan/TripMap.tsx
+++ b/src/components/Plan/TripMap.tsx
@@ -6,12 +6,16 @@ import { getColor } from '@utils/getColor';
const VITE_KAKAO_MAP_API_KEY = import.meta.env.VITE_KAKAO_MAP_API_KEY;
const TripMap = ({ paths }: { paths: Paths[] }) => {
+ const DEFAULT_LATITUDE = 36.34;
+ const DEFAULT_LONGITUDE = 127.77;
+
const firstPath = paths[0];
- const latitude = firstPath?.fromLatitude;
- const longitude = firstPath?.fromLongitude;
+ const latitude = firstPath ? firstPath.fromLatitude : DEFAULT_LATITUDE;
+ const longitude = firstPath ? firstPath.fromLongitude : DEFAULT_LONGITUDE;
const mapRef = useRef
(null);
const [selectedMarker, setSelectedMarker] = useState(null);
+ const [maplevel, setMapLevel] = useState(4);
const defaultPosition = { lat: Number(latitude), lng: Number(longitude) };
@@ -84,7 +88,13 @@ const TripMap = ({ paths }: { paths: Paths[] }) => {
};
useEffect(() => {
- setBounds();
+ if (paths.length > 0) {
+ setTimeout(() => {
+ setBounds();
+ }, 100);
+ } else {
+ setMapLevel(15);
+ }
}, [paths]);
// 마커를 클릭할 때 호출되는 함수
@@ -186,39 +196,40 @@ const TripMap = ({ paths }: { paths: Paths[] }) => {
key={VITE_KAKAO_MAP_API_KEY}
center={centerPosition}
style={MapStyle}
- level={4}
+ level={maplevel}
className="relative object-fill"
ref={mapRef}>
- {paths.map((path, index) => (
-
-
handleMarkerClick(path.toSeqNum)}
- image={getSequenceIconUrl(path.toSeqNum - 1)}
- />
- {/* 마지막 항목인 경우, 목적지 위치에 마커 추가 */}
- {index === paths.length - 1 && (
+ {paths.length !== 0 &&
+ paths.map((path, index) => (
+
handleMarkerClick(path.toSeqNum + 1)}
- image={getSequenceIconUrl(path.toSeqNum)}
+ onClick={() => handleMarkerClick(path.toSeqNum)}
+ image={getSequenceIconUrl(path.toSeqNum - 1)}
/>
- )}
-
-
- ))}
+ {/* 마지막 항목인 경우, 목적지 위치에 마커 추가 */}
+ {index === paths.length - 1 && (
+ handleMarkerClick(path.toSeqNum + 1)}
+ image={getSequenceIconUrl(path.toSeqNum)}
+ />
+ )}
+
+
+ ))}
);
diff --git a/src/components/Review/CommentItem.tsx b/src/components/Review/CommentItem.tsx
index 33712b0c..6c1b907e 100644
--- a/src/components/Review/CommentItem.tsx
+++ b/src/components/Review/CommentItem.tsx
@@ -1,13 +1,11 @@
-import { MoreIcon } from '@components/common/icons/Icons';
+import { MoreIcon, UserIcon } from '@components/common/icons/Icons';
import {
isModalOpenState,
- titleState,
modalChildrenState,
+ titleState,
} from '@recoil/modal';
import { commentState, targetCommentIdState } from '@recoil/review';
import { useRecoilState, useSetRecoilState } from 'recoil';
-import { ReactComponent as NullUser } from '@assets/images/NullUser.svg';
-
interface ItemProps {
commentId: number;
authorNickname: string;
@@ -55,7 +53,7 @@ const CommentItem: React.FC
= (props: ItemProps) => {
};
return (
-
+
{!(
@@ -65,10 +63,13 @@ const CommentItem: React.FC
= (props: ItemProps) => {
) : (
-
+
+
+
)}
@@ -85,7 +86,7 @@ const CommentItem: React.FC = (props: ItemProps) => {
)}
-
{content}
+
{content}
);
};
diff --git a/src/components/Review/ReviewButton.tsx b/src/components/Review/ReviewButton.tsx
index 979354f2..ca1209a5 100644
--- a/src/components/Review/ReviewButton.tsx
+++ b/src/components/Review/ReviewButton.tsx
@@ -1,6 +1,6 @@
import { ButtonPrimary } from '@components/common/button/Button';
import { useState, useEffect } from 'react';
-import { contentState, keywordsState } from '@recoil/review';
+import { contentState, keywordsState, ratingState } from '@recoil/review';
import { useRecoilState, useRecoilValue } from 'recoil';
interface ButtonProps {
@@ -11,8 +11,10 @@ const ReviewButton = (props: ButtonProps) => {
const { onClick } = props;
const [content] = useRecoilState(contentState);
const keywords = useRecoilValue(keywordsState);
+ const rating = useRecoilValue(ratingState);
const [isContentValid, setIsContentValid] = useState(false);
const [isKeywordsValid, setIsKeywordsValid] = useState(false);
+ const [isRatingValid, setIsRatingValid] = useState(false);
useEffect(() => {
if (content.length >= 10) {
@@ -30,18 +32,37 @@ const ReviewButton = (props: ButtonProps) => {
}
}, [keywords]);
+ useEffect(() => {
+ if (rating > 0) {
+ setIsRatingValid(true);
+ } else if (rating <= 0) {
+ setIsRatingValid(false);
+ }
+ }, [rating]);
+
return (
- {isContentValid === false && isKeywordsValid === false && (
+ {isRatingValid === false && (
- 키워드를 선택하거나 텍스트를 10자 이상 입력해주세요
+ 별점을 입력해주세요
)}
+ {isRatingValid === true &&
+ isContentValid === false &&
+ isKeywordsValid === false && (
+
+ 키워드를 선택하거나 텍스트를 10자 이상 입력해주세요
+
+ )}
+
+ className="flex h-[56px] items-center justify-center"
+ disabled={
+ isRatingValid === false ||
+ (isContentValid === false && isKeywordsValid === false)
+ }>
완료
diff --git a/src/components/Review/ReviewComments.tsx b/src/components/Review/ReviewComments.tsx
index 208fbd83..16fc7a00 100644
--- a/src/components/Review/ReviewComments.tsx
+++ b/src/components/Review/ReviewComments.tsx
@@ -10,8 +10,9 @@ import React, { useEffect, useState } from 'react';
import InfiniteScroll from 'react-infinite-scroller';
import EditDelete from '@components/common/modal/children/EditDelete';
import MyAlert from '@components/common/modal/children/MyAlert';
-import { commentState } from '@recoil/review';
+import { commentState, toastPopUpState } from '@recoil/review';
import { alertTypeState } from '@recoil/modal';
+import ToastPopUp from '@components/common/toastpopup/ToastPopUp';
export default function ReviewComments() {
const params = useParams();
@@ -23,6 +24,7 @@ export default function ReviewComments() {
const modalChildren = useRecoilValue(modalChildrenState);
const setComment = useSetRecoilState(commentState);
const alertType = useRecoilValue(alertTypeState);
+ const [toastPopUp, setToastPopUp] = useRecoilState(toastPopUpState);
const {
data: reviewComments,
@@ -61,12 +63,29 @@ export default function ReviewComments() {
console.log('reviewComments', reviewComments);
}, [reviewComments]);
+ useEffect(() => {
+ if (toastPopUp.isPopUp) {
+ const timer = setTimeout(() => {
+ setToastPopUp(() => ({
+ isPopUp: false,
+ noun: '',
+ verb: '',
+ }));
+ }, 2000);
+ return () => clearTimeout(timer);
+ }
+ }, [toastPopUp]);
+
return (
<>
+ {toastPopUp.isPopUp && (
+
+ )}
댓글
{commentDataLength}
+
{commentDataLength == 0 && (
@@ -118,6 +137,9 @@ export default function ReviewComments() {
content="댓글 쓰기 시 로그인이 필요해요. 로그인하시겠어요?"
/>
)}
+ {modalChildren === 'MyAlert' && alertType === 'DeleteComment' && (
+
+ )}
>
);
diff --git a/src/components/Review/ReviewPosting.tsx b/src/components/Review/ReviewPosting.tsx
index c7ea1e8c..e266e39d 100644
--- a/src/components/Review/ReviewPosting.tsx
+++ b/src/components/Review/ReviewPosting.tsx
@@ -15,9 +15,9 @@ export default function ReviewPosting() {
return (
리뷰를 작성해주세요
-
+
-
+
{title}
diff --git a/src/components/Trip/EditCodeModal.tsx b/src/components/Trip/EditCodeModal.tsx
index 7d3ca648..d486c0ce 100644
--- a/src/components/Trip/EditCodeModal.tsx
+++ b/src/components/Trip/EditCodeModal.tsx
@@ -3,11 +3,12 @@ import CodeInput from '@components/Share/CodeInput';
import Alert from '@components/common/alert/Alert';
import { useGetTripsAuthority } from '@hooks/useGetTripsAuthority';
import { useState } from 'react';
-import { useNavigate, useParams } from 'react-router-dom';
+import { useLocation, useNavigate, useParams } from 'react-router-dom';
import * as Dialog from '@radix-ui/react-dialog';
import { DeleteIcon, PenIcon } from '@components/common/icons/Icons';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import ToastPopUp from '@components/common/toastpopup/ToastPopUp';
+import { getItem } from '@utils/localStorageFun';
const EditCodeModal = () => {
const navigate = useNavigate();
@@ -54,62 +55,85 @@ const EditCodeModal = () => {
}
};
+ const isLogin = getItem('accessToken');
+ const { pathname } = useLocation();
+ const handleLoginConfirm = () => {
+ navigate('/login', { state: { prevPath: `${pathname}/code` } });
+ };
+
return (
<>
{isToastVisible &&
}
- {tripAuthority === 'WRITE' ? (
-
-
- setIsEditModal(true)}
- className="body3 rounded-lg border-[1px] border-solid border-gray2 px-[10px] py-[8px] text-gray4">
- 편집
-
-
- {isEditModal && (
-
-
-
-
- 나의 여정
-
+ {isLogin ? (
+ tripAuthority === 'WRITE' ? (
+
+
+ setIsEditModal(true)}
+ className="body3 rounded-lg border-[1px] border-solid border-gray2 px-[10px] py-[8px] text-gray4">
+ 편집
+
+
+ {isEditModal && (
+
+
+
+
+ 나의 여정
+
- <>
- navigate('edit')}
- className="body1 minh-6 mb-2 mr-auto flex w-full items-center rounded-lg py-2 font-medium text-gray7 outline-none">
-
- 수정하기
-
-
-
-
- 삭제하기
+ <>
+ navigate('edit')}
+ className="body1 minh-6 mb-2 mr-auto flex w-full items-center rounded-lg py-2 font-medium text-gray7 outline-none">
+
+ 수정하기
-
- >
-
-
- )}
-
+
+
+
+ 여행 나가기
+
+
+ >
+
+
+ )}
+
+ ) : (
+
+ }
+ isOpen={isModalOpen}
+ setIsOpen={setIsModalOpen}
+ onConfirm={handleConfirm}>
+
+ 편집
+
+
+ )
) : (
+ title={'로그인'}
+ message={
+ <>
+ 편집 참여 코드 입력을 위해 로그인이 필요해요.
+
+ 로그인하시겠어요?
+ >
}
- isOpen={isModalOpen}
- setIsOpen={setIsModalOpen}
- onConfirm={handleConfirm}>
-
+ onConfirm={handleLoginConfirm}>
+
편집
diff --git a/src/components/Trip/TripInfo.tsx b/src/components/Trip/TripInfo.tsx
index 973a38b1..7e753878 100644
--- a/src/components/Trip/TripInfo.tsx
+++ b/src/components/Trip/TripInfo.tsx
@@ -5,7 +5,6 @@ import TripSurveyMember from '@components/common/modal/children/TripSurveyMember
import { Modal } from '@components/common/modal';
import { useQuery } from '@tanstack/react-query';
import { getTripsMembers } from '@api/trips';
-// import { ReactComponent as NullUser } from '@assets/images/NullUser.svg';
import { DownIcon } from '@components/common/icons/Icons';
import { useState } from 'react';
import { UserIcon } from '@components/common/icons/Icons';
@@ -23,7 +22,6 @@ const ShareList = () => {
enabled: !!tripId,
});
const members = tripsMembers?.data?.data?.tripMemberSimpleInfos;
- console.log(tripsMembers);
return (
<>
@@ -47,7 +45,6 @@ const ShareList = () => {
}>
- //
)}
{member.nickname}
@@ -108,7 +105,6 @@ const TripInfo = () => {
}>
- //
)}
))}
diff --git a/src/components/Trip/TripPreference.tsx b/src/components/Trip/TripPreference.tsx
index 432c72f2..3e2e3786 100644
--- a/src/components/Trip/TripPreference.tsx
+++ b/src/components/Trip/TripPreference.tsx
@@ -11,6 +11,7 @@ import { getTripsSurveyMembers } from '@api/trips';
import { useSetRecoilState, useRecoilState } from 'recoil';
import { participantsState } from '@recoil/trip';
import { useGetTripsAuthority } from '@hooks/useGetTripsAuthority';
+import { useNavigate } from 'react-router-dom';
interface RatioBarParams {
value: number;
@@ -27,8 +28,18 @@ interface PercentageParams {
}
const TripPreferenceButton: React.FC = () => {
+ const { tripAuthority } = useGetTripsAuthority();
+ const navigate = useNavigate();
+ const handleTrip = () => {
+ if (tripAuthority === 'WRITE') {
+ navigate('/mypage/survey');
+ }
+ };
+
return (
-
+
@@ -49,11 +60,17 @@ const RatioBar = ({ value, total, color, label1, label2 }: RatioBarParams) => {
value >= total - value
? Math.round((175 * value) / total)
: Math.round((175 * (total - value)) / total);
+ const isZeroColor =
+ total === 0 && value === 0 ? 'text-gray6' : `text-${color}`;
+ const isZeroWeight = total === 0 && value === 0 ? '' : 'font-bold';
+
return (
{value >= total - value ? (
<>
-
{label1}
+
+ {label1}
+
@@ -74,27 +91,33 @@ const RatioBar = ({ value, total, color, label1, label2 }: RatioBarParams) => {
);
};
-const Percentage = ({ value, total, color }: PercentageParams) => (
-
- {value >= total - value ? (
- <>
-
- {calculatePercentage(value, total)}%
-
-
- {calculatePercentageRemain(value, total)}%
-
- >
- ) : (
- <>
-
{calculatePercentage(value, total)}%
-
- {calculatePercentageRemain(value, total)}%
-
- >
- )}
-
-);
+const Percentage = ({ value, total, color }: PercentageParams) => {
+ const isZeroColor =
+ total === 0 && value === 0 ? 'text-gray6' : `text-${color}`;
+ const isZeroWeight = total === 0 && value === 0 ? '' : 'font-bold';
+
+ return (
+
+ {value >= total - value ? (
+ <>
+
+ {calculatePercentage(value, total)}%
+
+
+ {calculatePercentageRemain(value, total)}%
+
+ >
+ ) : (
+ <>
+
{calculatePercentage(value, total)}%
+
+ {calculatePercentageRemain(value, total)}%
+
+ >
+ )}
+
+ );
+};
const TripPreference: React.FC = () => {
const { tripId } = useGetTripsAuthority();
@@ -132,6 +155,7 @@ const TripPreference: React.FC = () => {
useEffect(() => {
if (tripPreference) {
+ console.log('tripPreference', tripPreference);
setA([
tripPreference?.data?.data?.planningCount,
tripPreference?.data?.data?.planningTotalCount,
diff --git a/src/components/Trip/TripRealtimeEditor.tsx b/src/components/Trip/TripRealtimeMember.tsx
similarity index 93%
rename from src/components/Trip/TripRealtimeEditor.tsx
rename to src/components/Trip/TripRealtimeMember.tsx
index 3e47bee2..c57afe5a 100644
--- a/src/components/Trip/TripRealtimeEditor.tsx
+++ b/src/components/Trip/TripRealtimeMember.tsx
@@ -4,7 +4,7 @@ import { UserIcon } from '@components/common/icons/Icons';
import { Swiper, SwiperSlide } from 'swiper/react';
import { Navigation } from 'swiper/modules';
-const TripRealtimeEditor = () => {
+const TripRealtimeMember = () => {
const { tripMember } = useContext(socketContext);
const tripMemberData = tripMember?.data;
@@ -15,7 +15,7 @@ const TripRealtimeEditor = () => {
slidesPerView={5}
navigation={true}
modules={[Navigation]}
- className="flex w-[375px] items-center justify-center">
+ className="w-[100vw]items-center flex max-w-[375px] justify-center">
{tripMemberData?.tripMembers?.map((member) => {
const isConnected = member?.connected;
const thumbnailUrl = member?.thumbnailUrl;
@@ -60,4 +60,4 @@ const TripRealtimeEditor = () => {
);
};
-export default TripRealtimeEditor;
+export default TripRealtimeMember;
diff --git a/src/components/Trip/TripSectionTop.tsx b/src/components/Trip/TripSectionTop.tsx
index d1388108..730f3a65 100644
--- a/src/components/Trip/TripSectionTop.tsx
+++ b/src/components/Trip/TripSectionTop.tsx
@@ -18,6 +18,8 @@ const TripSectionTop = () => {
if (tripAuthority !== null) {
if (tripAuthority !== 'WRITE') {
setIsEditable(true);
+ } else {
+ setIsEditable(false);
}
}
}, [tripAuthority]);
diff --git a/src/components/Wish/WishItem.tsx b/src/components/Wish/WishItem.tsx
index e8c12eb9..8a27f666 100644
--- a/src/components/Wish/WishItem.tsx
+++ b/src/components/Wish/WishItem.tsx
@@ -1,6 +1,7 @@
import { TourType } from '@/@types/tours.types';
import { HeartIcon, StarIcon } from '@components/common/icons/Icons';
import Like from '@components/common/like/Like';
+import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
interface WishItemProps {
@@ -19,8 +20,17 @@ const WishItem: React.FC
= ({ wishList }) => {
tourAddress,
} = wishList;
+ const [isMetroIncluded, setIsMetroIncluded] = useState(false);
+
const navigate = useNavigate();
+ useEffect(() => {
+ if (tourAddress) {
+ const isMetroCityIncluded = /특별시|광역시/.test(tourAddress);
+ setIsMetroIncluded(isMetroCityIncluded);
+ }
+ }, []);
+
return (
= ({ wishList }) => {
{title}
-
{tourAddress}
+
+ {isMetroIncluded && tourAddress
+ ? (tourAddress.match(/(.*?[시군구])/)?.[0] || '') +
+ (tourAddress
+ .replace(/(.*?[시군구])/, '')
+ .match(/(특별시|광역시)?.*?[시군구]/)?.[0] || '')
+ : tourAddress?.match(/(.*?[시군구])/)?.[0]}
+
diff --git a/src/components/common/accordion/Accordion.tsx b/src/components/common/accordion/Accordion.tsx
index 4175b9f7..3d8a8fe9 100644
--- a/src/components/common/accordion/Accordion.tsx
+++ b/src/components/common/accordion/Accordion.tsx
@@ -1,21 +1,31 @@
import * as accordion from '@radix-ui/react-accordion';
-import { DownIcon } from '../icons/Icons';
interface AccordionProps {
title: React.ReactNode;
content: React.ReactNode;
+ value: string;
+ openAccordion: string;
+ setOpenAccordion: (value: string) => void;
}
-const Accordion: React.FC = ({ title, content }) => {
+const Accordion: React.FC = ({
+ title,
+ content,
+ value,
+ openAccordion,
+ setOpenAccordion,
+}) => {
return (
-
-
-
- {title}
-
-
-
-
+
+
+
+ {title}
+
+
{content}
diff --git a/src/components/common/button/Button.tsx b/src/components/common/button/Button.tsx
index a3ca8c3b..211bfa70 100644
--- a/src/components/common/button/Button.tsx
+++ b/src/components/common/button/Button.tsx
@@ -29,7 +29,7 @@ export const ButtonPrimary: React.FC = ({
return (
{children}
diff --git a/src/components/common/header/DetailHeader.tsx b/src/components/common/header/DetailHeader.tsx
index 81f93e03..8af3d6c5 100644
--- a/src/components/common/header/DetailHeader.tsx
+++ b/src/components/common/header/DetailHeader.tsx
@@ -1,7 +1,7 @@
-import { useNavigate } from 'react-router-dom';
-import { BackIcon } from '../icons/Icons';
import { commentState } from '@recoil/review';
+import { useNavigate } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
+import { BackIcon } from '../icons/Icons';
export default function DetailHeader() {
const navigate = useNavigate();
@@ -14,9 +14,7 @@ export default function DetailHeader() {
return (
diff --git a/src/components/createTrip/SelectDate.tsx b/src/components/createTrip/SelectDate.tsx
index aadc9696..b92d92f8 100644
--- a/src/components/createTrip/SelectDate.tsx
+++ b/src/components/createTrip/SelectDate.tsx
@@ -17,7 +17,7 @@ export const SelectDate = ({ onClose }: { onClose: () => void }) => {
};
return (
-
+
@@ -30,7 +30,7 @@ export const SelectDate = ({ onClose }: { onClose: () => void }) => {
setSelectedEndDate(endDate);
}}
/>
-
diff --git a/src/hooks/useGetMyTrips.ts b/src/hooks/useGetMyTrips.ts
index 754188ed..78670740 100644
--- a/src/hooks/useGetMyTrips.ts
+++ b/src/hooks/useGetMyTrips.ts
@@ -2,7 +2,7 @@ import { useQuery } from '@tanstack/react-query';
import { getMyTrips } from '@api/trips';
interface Trip {
- tripId: number;
+ tripId: string;
tripName: string;
startDate: string;
endDate: string;
@@ -15,7 +15,6 @@ export const useGetMyTrips = (): { myTrips: Trip[] } => {
const { data, isLoading, isError } = useQuery({
queryKey: ['getMyTrips'],
queryFn: () => getMyTrips(),
- staleTime: 60000,
});
const myTrips = data?.data.data;
diff --git a/src/hooks/useGetTripsAuthority.ts b/src/hooks/useGetTripsAuthority.ts
index 2db402cc..5eecf742 100644
--- a/src/hooks/useGetTripsAuthority.ts
+++ b/src/hooks/useGetTripsAuthority.ts
@@ -24,7 +24,6 @@ export const useGetTripsAuthority = (): useGetTripsAuthorityReturn => {
queryKey: ['getTripsAuthority', id],
queryFn: () => getTripsAuthority(id),
enabled: !!id,
- staleTime: 60000,
});
const tripAuthority = data?.data.data.tripAuthority;
diff --git a/src/hooks/useSocket.ts b/src/hooks/useSocket.ts
index d0eeef0f..e39c8b10 100644
--- a/src/hooks/useSocket.ts
+++ b/src/hooks/useSocket.ts
@@ -5,6 +5,7 @@ import {
subPath,
subMember,
subBudget,
+ subCursor,
} from '@api/socket';
import {
subInfoRes,
@@ -12,10 +13,11 @@ import {
subPathRes,
subMemberRes,
subBudgetRes,
+ subCursorRes,
SocketContextType,
} from '@/@types/service';
import { createContext } from 'react';
-import { useState, useEffect } from 'react';
+import { useState, useEffect, useRef } from 'react';
import { useParams } from 'react-router-dom';
import { useRecoilValue } from 'recoil';
import { visitDateState } from '@recoil/socket';
@@ -26,6 +28,7 @@ export const socketContext = createContext
({
tripPath: null,
tripMember: null,
tripBudget: null,
+ tripCursor: null,
tripId: '',
callBackPub: () => {},
});
@@ -40,14 +43,17 @@ export const useSocket = () => {
const [tripPath, setTripPath] = useState(null);
const [tripMember, setTripMember] = useState(null);
const [tripBudget, setTripBudget] = useState(null);
- const [socketCallback, setSocketCallback] = useState<(() => void) | null>(
- null,
- );
+ const [tripCursor, setTripCursor] = useState(null);
+
+ const socketCallbackRef = useRef<(() => void) | null>(null);
const callBackPub = (callback: () => void): void => {
- setSocketCallback(() => callback);
+ // socketCallbackRef에 새로운 콜백을 할당
+ socketCallbackRef.current = callback;
};
+ console.log(socketCallbackRef.current);
+
const socketConnect = (tripId: string, visitDate: string) => {
socketClient.onConnect = () => {
subInfo(tripId, (res) => {
@@ -80,8 +86,14 @@ export const useSocket = () => {
}
});
- if (socketCallback) {
- socketCallback();
+ subCursor(tripId, visitDate, (res) => {
+ if (res) {
+ setTripCursor(res);
+ }
+ });
+
+ if (socketCallbackRef.current) {
+ socketCallbackRef.current();
}
};
@@ -92,11 +104,13 @@ export const useSocket = () => {
if (tripId && visitDate) {
socketConnect(tripId, visitDate.visitDate);
}
+ console.log('소켓연결');
return () => {
socketClient.deactivate();
+ console.log('소켓해제');
};
- }, [tripId, visitDate, socketCallback]);
+ }, [tripId, visitDate]);
return {
tripInfo,
@@ -104,7 +118,16 @@ export const useSocket = () => {
tripPath,
tripMember,
tripBudget,
+ tripCursor,
tripId,
callBackPub,
};
};
+
+// const [socketCallback, setSocketCallback] = useState<(() => void) | null>(
+// null,
+// );
+
+// const callBackPub = (callback: () => void): void => {
+// setSocketCallback(() => callback);
+// };
diff --git a/src/index.css b/src/index.css
index ba55d149..00b6f010 100644
--- a/src/index.css
+++ b/src/index.css
@@ -27,7 +27,7 @@
body {
margin: 0;
- background-color: #eee;
+ /* background-color: #eee; */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
diff --git a/src/main.tsx b/src/main.tsx
index 3de05f65..0fe89954 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -2,9 +2,4 @@ import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import './index.css';
-// if (import.meta.env.DEV) {
-// const { worker } = await import('./mocks/browser.ts');
-// await worker.stop();
-// }
-
ReactDOM.createRoot(document.getElementById('root')!).render();
diff --git a/src/mocks/browser.ts b/src/mocks/browser.ts
deleted file mode 100644
index df8e9011..00000000
--- a/src/mocks/browser.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-// import { setupWorker } from 'msw';
-// // import { handlers } from './handlers';
-
-// export const worker = setupWorker(...handlers);
diff --git a/src/mocks/handlers.ts b/src/mocks/handlers.ts
deleted file mode 100644
index 05fe7d83..00000000
--- a/src/mocks/handlers.ts
+++ /dev/null
@@ -1,944 +0,0 @@
-// import { rest } from 'msw';
-
-// const SERVER_URL = import.meta.env.VITE_SERVER_URL;
-
-// export const handlers = [
-// // 댓글 관련 API
-// // 댓글 수정
-// rest.put(`${SERVER_URL}/comments/:commentId`, (req, res, ctx) => {
-// const { commentId } = req.params;
-// const commentData = req.body;
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// commentId: commentId,
-// authorNickname: '은별',
-// authorProfileImageUrl: 'https://~~~.png',
-// content: commentData,
-// createdTime: '2023-12-28T15:12:40.231Z',
-// },
-// };
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// // 댓글 삭제
-// rest.delete(`${SERVER_URL}/comments/:commentId`, (req, res, ctx) => {
-// const { commentId } = req.params;
-// const responseData = {
-// status: 0,
-// message: `댓글 삭제 성공, commentId: ${commentId}`,
-// };
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// // 댓글 작성
-// rest.post(`${SERVER_URL}/comments`, (req, res, ctx) => {
-// const { commentId } = req.params;
-// const commentData = req.body;
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// commentId: commentId,
-// authorNickname: '은별',
-// authorProfileImageUrl: 'https://~~~.png',
-// content: commentData,
-// createdTime: '2023-12-28T15:15:48.041Z',
-// },
-// };
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// // 리뷰 관련 API
-// // 리뷰 수정
-// rest.put(`${SERVER_URL}/reviews/:reviewId`, (req, res, ctx) => {
-// const { reviewId } = req.params;
-// const reviewData = req.body as ReviewRequest;
-// const responseData = reviewData;
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// // 리뷰 삭제
-// rest.delete(`${SERVER_URL}/reviews/:reviewId`, (req, res, ctx) => {
-// const { reviewId } = req.params;
-// return res(
-// ctx.status(200),
-// ctx.json({ message: `리뷰 ${reviewId} 삭제 성공` }),
-// );
-// }),
-// // 리뷰 작성
-// rest.post(`${SERVER_URL}/reviews`, (req, res, ctx) => {
-// const { reviewId } = req.params;
-// const reviewData = req.body as ReviewRequest;
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// reviewId: reviewId,
-// authorNickname: '은별',
-// authorProfileImageUrl: 'https://~~~.png',
-// rating: 4,
-// createdTime: '2023-12-28T16:24:09.639Z',
-// content: '~~~여서 ~~~ 해서 ~~로 좋았습니다.',
-// keywords: [
-// {
-// keywordId: 1,
-// content: '깨끗해요',
-// type: 'ACCOMMODATION_KEYWORD',
-// },
-// ],
-// },
-// };
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// // 리뷰 댓글 조회
-// rest.get(`${SERVER_URL}/reviews/:reviewId/comments`, (req, res, ctx) => {
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// comments: [
-// {
-// commentId: 1,
-// authorNickname: '은별',
-// authorProfileImageUrl: 'https://~~~.png',
-// content: '잘보고 갑니다~',
-// createdTime: '2023-12-28T16:29:34.105Z',
-// },
-// ],
-// },
-// };
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// // 리뷰 키워드 조회
-// rest.get(`${SERVER_URL}/reviews/keywords`, (req, res, ctx) => {
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// keywords: [
-// {
-// keywordId: 1,
-// content: '깨끗해요',
-// type: 'ACCOMMODATION_KEYWORD',
-// },
-// ],
-// },
-// };
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// // 여정 관련 API
-// // 나의 여정 목록 조회
-// rest.get(`${SERVER_URL}/trips`, (req, res, ctx) => {
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// trips: [
-// {
-// tripId: 1,
-// tripName: '나의 ~번째 여정',
-// startDate: '2023-12-28',
-// endDate: '2023-12-28',
-// numberOfTripMembers: 2,
-// tripStatus: '여행 전',
-// tripThumbnailUrl: 'https://~~~~.png',
-// },
-// ],
-// },
-// };
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// // 여정 생성
-// rest.post(`${SERVER_URL}/trips`, (req, res, ctx) => {
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// tripId: 1,
-// tripName: '나의 ~번째 여정',
-// startDate: '2023-12-28',
-// endDate: '2023-12-28',
-// numberOfTripMembers: 2,
-// tripStatus: '여행 전',
-// tripThumbnailUrl: 'https://~~~~.png',
-// },
-// };
-// return res(ctx.status(201), ctx.json(responseData));
-// }),
-// // 여정 탈퇴
-// rest.delete(`${SERVER_URL}/trips/:tripId`, (req, res, ctx) => {
-// const { tripId } = req.params;
-// return res(
-// ctx.status(200),
-// ctx.json({ message: `여행 ${tripId} 탈퇴 성공` }),
-// );
-// }),
-// // 여행지 관련 API
-// // 인기 여행지 조회
-// rest.get(`${SERVER_URL}/tours`, (req, res, ctx) => {
-// const { page, size } = req.params;
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// totalPages: 0,
-// totalElements: 0,
-// pageable: {
-// pageNumber: page,
-// pageSize: size,
-// offset: 0,
-// sort: {
-// sorted: true,
-// empty: true,
-// unsorted: true,
-// },
-// paged: true,
-// unpaged: true,
-// },
-// size: size, // 확인필요
-// content: [
-// {
-// id: 1,
-// title: '여행지 이름',
-// ratingAverage: 4.5,
-// reviewCount: 100,
-// likedCount: 100,
-// liked: false,
-// smallThumbnailUrl: 'http://~~~~~~image.jpg',
-// },
-// ],
-// number: page, // 확인필요
-// sort: {
-// sorted: true,
-// empty: true,
-// unsorted: true,
-// },
-// numberOfElements: 0,
-// first: true,
-// last: true,
-// empty: true,
-// },
-// };
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// // 여행지 상세 조회
-// rest.get(`${SERVER_URL}/tours/:tourItemId`, (req, res, ctx) => {
-// const { tourId } = req.params;
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// id: tourId,
-// title: '여행지 이름',
-// liked: false,
-// fullAddress: 'OO시/도 OO구/군 OO로/길 OOO',
-// zipcode: '00000',
-// longitude: '127.04',
-// latitude: '33.56',
-// tel: '010-0000-0000',
-// originalThumbnailUrl: 'http://~~~~~~image.jpg',
-// },
-// };
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// // 여행지 검색
-// rest.get(`${SERVER_URL}/tours/search`, (req, res, ctx) => {
-// const { page, size } = req.params;
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// totalPages: 0,
-// totalElements: 0,
-// pageable: {
-// pageNumber: page,
-// pageSize: size,
-// offset: 0,
-// sort: {
-// sorted: true,
-// empty: true,
-// unsorted: true,
-// },
-// paged: true,
-// unpaged: true,
-// },
-// size: size, // 확인필요
-// content: [
-// {
-// id: 1,
-// title: '여행지 이름',
-// ratingAverage: 4.5,
-// reviewCount: 100,
-// likedCount: 100,
-// liked: false,
-// smallThumbnailUrl: 'http://~~~~~~image.jpg',
-// },
-// ],
-// number: page, // 확인필요
-// sort: {
-// sorted: true,
-// empty: true,
-// unsorted: true,
-// },
-// numberOfElements: 0,
-// first: true,
-// last: true,
-// empty: true,
-// },
-// };
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// // 유저 관련 API
-// // 회원 정보 조회
-// rest.get(`${SERVER_URL}/member`, (req, res, ctx) => {
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// memberId: 1,
-// nickname: '닉네임',
-// email: 'example@mail.com',
-// profileImageUrl: '프로필 이미지',
-// ageType: '연령대',
-// genderType: '성별',
-// survey: '설문조사 결과',
-// },
-// };
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// // 회원 정보 수정
-// rest.put(`${SERVER_URL}/member`, (req, res, ctx) => {
-// const memberData = req.body as MemberRequest;
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// memberId: 1,
-// nickname: memberData.nickname,
-// email: 'example@mail.com',
-// profileImageUrl: memberData.profileImageUrl,
-// ageType: '연령대',
-// genderType: '성별',
-// survey: memberData.survey,
-// },
-// };
-// return res(
-// ctx.status(200),
-// ctx.json({
-// message: `회원 정보 수정 성공, 비밀번호:${memberData.password}`,
-// responseData,
-// }),
-// );
-// }),
-// // 회원 탈퇴
-// rest.delete(`${SERVER_URL}/member`, (req, res, ctx) => {
-// return res(ctx.status(200), ctx.json({ message: '회원 탈퇴 성공' }));
-// }),
-// // 나의 여정 조회
-// rest.get(`${SERVER_URL}/member/trips`, (req, res, ctx) => {
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// totalPages: 0,
-// totalElements: 0,
-// pageable: {
-// pageNumber: 0,
-// pageSize: 0,
-// offset: 0,
-// sort: {
-// sorted: true,
-// empty: true,
-// unsorted: true,
-// },
-// paged: true,
-// unpaged: true,
-// },
-// size: 0,
-// content: [
-// {
-// tripId: 1,
-// tripName: '나의 ~번째 여정',
-// startDate: '2023-12-28',
-// endDate: '2023-12-28',
-// numberOfTripMembers: 2,
-// tripStatus: '여행 전',
-// tripThumbnailUrl: 'https://~~~~.png',
-// },
-// ],
-// number: 0,
-// sort: {
-// sorted: true,
-// empty: true,
-// unsorted: true,
-// },
-// numberOfElements: 0,
-// first: true,
-// last: true,
-// empty: true,
-// },
-// };
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// // 나의 관심 여행지 조회
-// rest.get(`${SERVER_URL}/member/tours`, (req, res, ctx) => {
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// totalPages: 0,
-// totalElements: 0,
-// pageable: {
-// pageNumber: 0,
-// pageSize: 0,
-// offset: 0,
-// sort: {
-// sorted: true,
-// empty: true,
-// unsorted: true,
-// },
-// paged: true,
-// unpaged: true,
-// },
-// size: 0,
-// content: [
-// {
-// id: 1,
-// title: '여행지 이름',
-// ratingAverage: 4.5,
-// reviewCount: 100,
-// likedCount: 100,
-// liked: false,
-// smallThumbnailUrl: 'http://~~~~~~image.jpg',
-// },
-// ],
-// number: 0,
-// sort: {
-// sorted: true,
-// empty: true,
-// unsorted: true,
-// },
-// numberOfElements: 0,
-// first: true,
-// last: true,
-// empty: true,
-// },
-// };
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// // 나의 리뷰 조회
-// rest.get(`${SERVER_URL}/member/reviews`, (req, res, ctx) => {
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// totalPages: 0,
-// totalElements: 0,
-// pageable: {
-// pageNumber: 0,
-// pageSize: 0,
-// offset: 0,
-// sort: {
-// sorted: true,
-// empty: true,
-// unsorted: true,
-// },
-// paged: true,
-// unpaged: true,
-// },
-// size: 0,
-// content: [
-// {
-// reviewId: 1,
-// authorNickname: '은별',
-// authorProfileImageUrl: 'https://~~~.png',
-// rating: 4,
-// createdTime: '2023-12-28T16:15:43.756Z',
-// content: '~~~여서 ~~~ 해서 ~~로 좋았습니다.',
-// keywords: [
-// {
-// keywordId: 1,
-// content: '깨끗해요',
-// type: 'ACCOMMODATION_KEYWORD',
-// },
-// ],
-// },
-// ],
-// number: 0,
-// sort: {
-// sorted: true,
-// empty: true,
-// unsorted: true,
-// },
-// numberOfElements: 0,
-// first: true,
-// last: true,
-// empty: true,
-// },
-// };
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// // 나의 관심 여행지 삭제
-// rest.delete(`${SERVER_URL}/member/tours/:tourId`, (req, res, ctx) => {
-// const { tourId } = req.params;
-// return res(
-// ctx.status(200),
-// ctx.json({ message: `관심 여행지 ${tourId} 삭제 성공` }),
-// );
-// }),
-// // 인증 관련 API
-// // 회원가입
-// rest.post(`${SERVER_URL}/auth/signup`, (req, res, ctx) => {
-// const authData = req.body as AuthRequest;
-// return res(
-// ctx.status(200),
-// ctx.json({ message: '회원가입 성공', authData }),
-// );
-// }),
-// // 로그아웃
-// rest.post(`${SERVER_URL}/auth/logout`, (req, res, ctx) => {
-// return res(ctx.status(200), ctx.json({ message: '로그아웃 성공' }));
-// }),
-// // 이메일 로그인
-// rest.post(`${SERVER_URL}/auth/login`, (req, res, ctx) => {
-// const loginData = req.body;
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// token: 'jwtToken',
-// },
-// };
-// return res(
-// ctx.status(200),
-// ctx.json({ message: '로그인 성공', loginData, responseData }),
-// );
-// }),
-// // 카카오 로그인
-// rest.post(`${SERVER_URL}/auth/login/kakao`, (req, res, ctx) => {
-// const loginData = req.body;
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// token: 'jwtToken',
-// },
-// };
-// return res(
-// ctx.status(200),
-// ctx.json({ message: '카카오 로그인 성공', loginData, responseData }),
-// );
-// }),
-// // 닉네임 중복조회
-// rest.get(`${SERVER_URL}/auth/nicknames/check/:nickname`, (req, res, ctx) => {
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// exists: true,
-// },
-// };
-// return res(
-// ctx.status(200),
-// ctx.json({ message: '닉네임 중복조회 완료', responseData }),
-// );
-// }),
-// // 이메일 중복조회
-// rest.get(`${SERVER_URL}/auth/emails/check/:email`, (req, res, ctx) => {
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// exists: true,
-// },
-// };
-// return res(
-// ctx.status(200),
-// ctx.json({ message: '이메일 중복조회 완료', responseData }),
-// );
-// }),
-// // 지역 관련 API
-// // 전체 지역 조회
-// rest.get(`${SERVER_URL}/region`, (req, res, ctx) => {
-// const areaCode = req.url.searchParams.get('areaCode');
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// regions: [
-// {
-// areaCode: areaCode, // 확인필요
-// subAreaCode: 0,
-// name: '서울시',
-// },
-// ],
-// },
-// };
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// // 인기 지역 조회
-// rest.get(`${SERVER_URL}/region/popular`, (req, res, ctx) => {
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: {
-// regions: [
-// {
-// areaCode: 1,
-// subAreaCode: 0,
-// name: '서울시',
-// },
-// ],
-// },
-// };
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// // 카테고리 관련 API
-// // 카테고리 조회
-// rest.get(`${SERVER_URL}/category`, (req, res, ctx) => {
-// // 모의 카테고리 데이터
-// const responseData = {
-// status: 0,
-// message: 'string',
-// data: [
-// {
-// code: 39,
-// name: '식당',
-// },
-// ],
-// };
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// // 여행 상품 리뷰 조회
-// rest.get(`${SERVER_URL}/tours/:tourItemId/reviews`, (req, res, ctx) => {
-// const responseData = {
-// status: 200,
-// message: 'SUCCESS',
-// data: {
-// ratingAverage: 4.125,
-// reviewTotalCount: 8,
-// keywordTotalCount: 38,
-// reviewInfos: {
-// content: [
-// {
-// reviewId: 15,
-// authorNickname: '익명 사용자1',
-// authorProfileImageUrl:
-// 'https://common.hanmi.co.kr/upfile/ces/product/p_2011_tenten_p_400.jpg',
-// rating: 4,
-// createdTime: '2023-12-30T20:02:06.03406',
-// content: '좋은 여행지였습니다.',
-// keywords: [
-// {
-// keywordId: 1,
-// content: '깨끗해요',
-// type: 'ACCOMMODATION_KEYWORD',
-// },
-// {
-// keywordId: 2,
-// content: '친절해요',
-// type: 'ACCOMMODATION_KEYWORD',
-// },
-// ],
-// commentCount: 0,
-// },
-// {
-// reviewId: 23,
-// authorNickname: '익명 사용자1',
-// authorProfileImageUrl:
-// 'https://common.hanmi.co.kr/upfile/ces/product/p_2011_tenten_p_400.jpg',
-// rating: 4,
-// createdTime: '2023-12-30T20:03:17.978397',
-// content: '좋은 여행지였습니다.',
-// keywords: [
-// {
-// keywordId: 1,
-// content: '깨끗해요',
-// type: 'ACCOMMODATION_KEYWORD',
-// },
-// {
-// keywordId: 2,
-// content: '친절해요',
-// type: 'ACCOMMODATION_KEYWORD',
-// },
-// ],
-// commentCount: 0,
-// },
-// {
-// reviewId: 24,
-// authorNickname: '익명 사용자1',
-// authorProfileImageUrl:
-// 'https://common.hanmi.co.kr/upfile/ces/product/p_2011_tenten_p_400.jpg',
-// rating: 4,
-// createdTime: '2023-12-30T20:03:18.970952',
-// content: '좋은 여행지였습니다.',
-// keywords: [
-// {
-// keywordId: 1,
-// content: '깨끗해요',
-// type: 'ACCOMMODATION_KEYWORD',
-// },
-// {
-// keywordId: 2,
-// content: '친절해요',
-// type: 'ACCOMMODATION_KEYWORD',
-// },
-// ],
-// commentCount: 0,
-// },
-// {
-// reviewId: 25,
-// authorNickname: '익명 사용자1',
-// authorProfileImageUrl:
-// 'https://common.hanmi.co.kr/upfile/ces/product/p_2011_tenten_p_400.jpg',
-// rating: 4,
-// createdTime: '2023-12-30T20:03:20.335173',
-// content: '좋은 여행지였습니다.',
-// keywords: [
-// {
-// keywordId: 1,
-// content: '깨끗해요',
-// type: 'ACCOMMODATION_KEYWORD',
-// },
-// {
-// keywordId: 2,
-// content: '친절해요',
-// type: 'ACCOMMODATION_KEYWORD',
-// },
-// ],
-// commentCount: 0,
-// },
-// {
-// reviewId: 26,
-// authorNickname: '익명 사용자1',
-// authorProfileImageUrl:
-// 'https://common.hanmi.co.kr/upfile/ces/product/p_2011_tenten_p_400.jpg',
-// rating: 4,
-// createdTime: '2023-12-30T20:03:21.924918',
-// content: '좋은 여행지였습니다.',
-// keywords: [
-// {
-// keywordId: 1,
-// content: '깨끗해요',
-// type: 'ACCOMMODATION_KEYWORD',
-// },
-// {
-// keywordId: 2,
-// content: '친절해요',
-// type: 'ACCOMMODATION_KEYWORD',
-// },
-// ],
-// commentCount: 0,
-// },
-// ],
-// pageable: {
-// pageNumber: 0,
-// pageSize: 20,
-// sort: {
-// sorted: false,
-// empty: true,
-// unsorted: true,
-// },
-// offset: 0,
-// paged: true,
-// unpaged: false,
-// },
-// last: true,
-// totalElements: 5,
-// totalPages: 1,
-// size: 20,
-// number: 0,
-// sort: {
-// sorted: false,
-// empty: true,
-// unsorted: true,
-// },
-// first: true,
-// numberOfElements: 5,
-// empty: false,
-// },
-// tourKeywordInfos: [
-// {
-// keywordId: 1,
-// content: '깨끗해요',
-// type: 'ACCOMMODATION_KEYWORD',
-// keywordCount: 30,
-// },
-// {
-// keywordId: 2,
-// content: '친절해요',
-// type: 'ACCOMMODATION_KEYWORD',
-// keywordCount: 29,
-// },
-// {
-// keywordId: 3,
-// content: '뷰가 좋아요',
-// type: 'ACCOMMODATION_KEYWORD',
-// keywordCount: 28,
-// },
-// {
-// keywordId: 4,
-// content: '침구가 좋아요',
-// type: 'ACCOMMODATION_KEYWORD',
-// keywordCount: 27,
-// },
-// {
-// keywordId: 5,
-// content: '주차하기 편해요',
-// type: 'ACCOMMODATION_KEYWORD',
-// keywordCount: 26,
-// },
-// {
-// keywordId: 6,
-// content: '냉난방이 잘돼요',
-// type: 'ACCOMMODATION_KEYWORD',
-// keywordCount: 25,
-// },
-// {
-// keywordId: 7,
-// content: '대중교통이 편해요',
-// type: 'ACCOMMODATION_KEYWORD',
-// keywordCount: 24,
-// },
-// {
-// keywordId: 8,
-// content: '호캉스하기 좋아요',
-// type: 'ACCOMMODATION_KEYWORD',
-// keywordCount: 23,
-// },
-// {
-// keywordId: 9,
-// content: '조식이 맛있어요',
-// type: 'ACCOMMODATION_KEYWORD',
-// keywordCount: 22,
-// },
-// {
-// keywordId: 10,
-// content: '사진 찍기 좋아요',
-// type: 'ACCOMMODATION_KEYWORD',
-// keywordCount: 21,
-// },
-// {
-// keywordId: 11,
-// content: '음식이 맛있어요',
-// type: 'DINING_KEYWORD',
-// keywordCount: 20,
-// },
-// {
-// keywordId: 12,
-// content: '친절해요',
-// type: 'DINING_KEYWORD',
-// keywordCount: 19,
-// },
-// {
-// keywordId: 13,
-// content: '인테리어가 멋져요',
-// type: 'DINING_KEYWORD',
-// keywordCount: 18,
-// },
-// {
-// keywordId: 14,
-// content: '매장이 청결해요',
-// type: 'DINING_KEYWORD',
-// keywordCount: 17,
-// },
-// {
-// keywordId: 15,
-// content: '특별한 메뉴가 있어요',
-// type: 'DINING_KEYWORD',
-// keywordCount: 16,
-// },
-// {
-// keywordId: 16,
-// content: '가성비가 좋아요',
-// type: 'DINING_KEYWORD',
-// keywordCount: 15,
-// },
-// {
-// keywordId: 17,
-// content: '재료가 신선해요',
-// type: 'DINING_KEYWORD',
-// keywordCount: 14,
-// },
-// {
-// keywordId: 18,
-// content: '사진찍기 좋아요',
-// type: 'DINING_KEYWORD',
-// keywordCount: 13,
-// },
-// {
-// keywordId: 19,
-// content: '주차하기 편해요',
-// type: 'DINING_KEYWORD',
-// keywordCount: 12,
-// },
-// {
-// keywordId: 20,
-// content: '화장실이 깨끗해요',
-// type: 'DINING_KEYWORD',
-// keywordCount: 11,
-// },
-// {
-// keywordId: 21,
-// content: '사진이 잘 나와요',
-// type: 'ATTRACTION_KEYWORD',
-// keywordCount: 10,
-// },
-// {
-// keywordId: 22,
-// content: '뷰가 좋아요',
-// type: 'ATTRACTION_KEYWORD',
-// keywordCount: 9,
-// },
-// {
-// keywordId: 23,
-// content: '관리가 잘 되어있어요',
-// type: 'ATTRACTION_KEYWORD',
-// keywordCount: 8,
-// },
-// {
-// keywordId: 24,
-// content: '볼거리가 많아요',
-// type: 'ATTRACTION_KEYWORD',
-// keywordCount: 7,
-// },
-// {
-// keywordId: 25,
-// content: '편의시설이 잘 되어 있어요',
-// type: 'ATTRACTION_KEYWORD',
-// keywordCount: 6,
-// },
-// {
-// keywordId: 26,
-// content: '대중교통이 편해요',
-// type: 'ATTRACTION_KEYWORD',
-// keywordCount: 5,
-// },
-// {
-// keywordId: 27,
-// content: '주차하기 편해요',
-// type: 'ATTRACTION_KEYWORD',
-// keywordCount: 4,
-// },
-// {
-// keywordId: 28,
-// content: '화장실이 깨끗해요',
-// type: 'ATTRACTION_KEYWORD',
-// keywordCount: 3,
-// },
-// {
-// keywordId: 29,
-// content: '가격이 합리적이에요',
-// type: 'ATTRACTION_KEYWORD',
-// keywordCount: 2,
-// },
-// {
-// keywordId: 30,
-// content: '방문객이 많아요',
-// type: 'ATTRACTION_KEYWORD',
-// keywordCount: 1,
-// },
-// ],
-// },
-// };
-// return res(ctx.status(200), ctx.json(responseData));
-// }),
-// ];
diff --git a/src/pages/create/createTrip.page.tsx b/src/pages/create/createTrip.page.tsx
index ca568ca8..cf52e89a 100644
--- a/src/pages/create/createTrip.page.tsx
+++ b/src/pages/create/createTrip.page.tsx
@@ -88,14 +88,15 @@ export const CreateTrip = () => {
);
}
return (
-
+
여행 생성하기
{
@@ -142,7 +143,7 @@ export const CreateTrip = () => {
{formattedTripDate}
-
diff --git a/src/pages/mypage/editUserSurvey.page.tsx b/src/pages/mypage/editUserSurvey.page.tsx
index 2ec9c382..adf0f324 100644
--- a/src/pages/mypage/editUserSurvey.page.tsx
+++ b/src/pages/mypage/editUserSurvey.page.tsx
@@ -10,12 +10,12 @@ const EditUserSurvey = () => {
{
- navigate('/mypage');
+ navigate(-1);
}}>
나의 여행 취향 설정
>
);
diff --git a/src/pages/plan/addToOurPlace/AddtoListBtn.tsx b/src/pages/plan/addToOurPlace/AddtoListBtn.tsx
index ea7589f6..80b1e760 100644
--- a/src/pages/plan/addToOurPlace/AddtoListBtn.tsx
+++ b/src/pages/plan/addToOurPlace/AddtoListBtn.tsx
@@ -17,7 +17,6 @@ const AddToListButton = ({
const [selectedTourItemIds, setSelectedTourItemIds] =
useRecoilState(selectedItemsState);
const visitDate = useRecoilValue(visitDateState);
- console.log(selectedTourItemIds);
const { callBackPub } = useContext(socketContext);
const navigate = useNavigate();
diff --git a/src/pages/trip/tripEdit.page.tsx b/src/pages/trip/tripEdit.page.tsx
index cd9cb371..d9750bf9 100644
--- a/src/pages/trip/tripEdit.page.tsx
+++ b/src/pages/trip/tripEdit.page.tsx
@@ -32,30 +32,35 @@ const TripEdit = () => {
const [showSelectDate, setShowSelectDate] = useState(false);
const [tripDate, setTripDate] = useRecoilState(tripDateState);
- let start: Date, end: Date;
- if (startDate && endDate) {
- start = new Date(startDate);
- end = new Date(endDate);
- }
+ // 초기 날짜
+ const [initialStartDate, setInitialStartDate] = useState
(null);
+ const [initialEndDate, setInitialEndDate] = useState(null);
useEffect(() => {
if (startDate && endDate) {
+ const start = new Date(startDate);
+ const end = new Date(endDate);
setTripDate({ startDate: start, endDate: end });
- }
- if (tripName) {
- setTitle(tripName);
+ setInitialStartDate(start);
+ setInitialEndDate(end);
+
+ if (tripName) {
+ setTitle(tripName);
+ }
}
}, [tripName, startDate, endDate]);
const handleSubmit = async () => {
try {
- let adjustedStartDate, adjustedEndDate;
+ let adjustedStartDate = startDate ? new Date(startDate) : null;
+ let adjustedEndDate = endDate ? new Date(endDate) : null;
- if (tripDate.startDate) {
+ if (tripDate.startDate && tripDate.startDate !== initialStartDate) {
adjustedStartDate = new Date(tripDate.startDate);
adjustedStartDate.setDate(adjustedStartDate.getDate() + 1);
}
- if (tripDate.endDate) {
+
+ if (tripDate.endDate && tripDate.endDate !== initialEndDate) {
adjustedEndDate = new Date(tripDate.endDate);
adjustedEndDate.setDate(adjustedEndDate.getDate() + 1);
}
@@ -72,9 +77,8 @@ const TripEdit = () => {
};
if (tripId) {
- const response = await putTrips(tripId, tripRequestData);
- console.log('전송 완료: ', response);
- navigate('/trip/' + tripId);
+ await putTrips(tripId, tripRequestData);
+ navigate(`/trip/${tripId}`);
window.location.reload();
}
} catch (error) {
@@ -102,7 +106,7 @@ const TripEdit = () => {
);
}
return (
-
+
{
@@ -115,7 +119,7 @@ const TripEdit = () => {
{
@@ -162,7 +166,7 @@ const TripEdit = () => {
{formattedTripDate}
-
diff --git a/src/router/mainRouter.tsx b/src/router/mainRouter.tsx
index 1cbe518c..dd309613 100644
--- a/src/router/mainRouter.tsx
+++ b/src/router/mainRouter.tsx
@@ -15,12 +15,10 @@ import {
EditUserSurvey,
Mypage,
} from '@pages/mypage';
-import useGetUserInfo from '@hooks/useGetUserInfo';
import MainLayout from './routerLayout';
import { CreateTrip } from '@pages/create/createTrip.page';
const MainRouter = () => {
- useGetUserInfo();
return (
<>
diff --git a/src/router/routerLayout.tsx b/src/router/routerLayout.tsx
index 75d0006f..17c05f51 100644
--- a/src/router/routerLayout.tsx
+++ b/src/router/routerLayout.tsx
@@ -3,6 +3,7 @@ import { Outlet, useLocation } from 'react-router-dom';
import { Nav } from '@components/common/nav';
import { InputComment } from '@components/common/nav';
import '../index.css';
+import useGetUserInfo from '@hooks/useGetUserInfo';
const MainLayout = () => {
const location = useLocation();
@@ -23,10 +24,12 @@ const MainLayout = () => {
location.pathname.includes(path),
);
+ useGetUserInfo();
+
return (
-
+
-
+
{showNav &&
}
diff --git a/src/utils/authSelectOptions.ts b/src/utils/authSelectOptions.ts
index 0e544aa7..d16d0a59 100644
--- a/src/utils/authSelectOptions.ts
+++ b/src/utils/authSelectOptions.ts
@@ -10,6 +10,6 @@ export const ageArr: SelectOption[] = [
{ id: 'TEENAGER', value: '10대' },
{ id: 'TWENTIES', value: '20대' },
{ id: 'THIRTIES', value: '30대' },
- { id: 'FOURTIES', value: '40대' },
+ { id: 'FORTIES', value: '40대' },
{ id: 'ABOVE_FIFTIES', value: '50대 이상' },
];
diff --git a/src/utils/calculatePercentage.ts b/src/utils/calculatePercentage.ts
index 8ca8e8cc..09f866b9 100644
--- a/src/utils/calculatePercentage.ts
+++ b/src/utils/calculatePercentage.ts
@@ -1,6 +1,9 @@
-// utils.ts
export const calculatePercentage = (count: number, totalCount: number) =>
- ((count / totalCount) * 100).toFixed(0);
+ totalCount === 0 && count === 0
+ ? '0'
+ : ((count / totalCount) * 100).toFixed(0);
export const calculatePercentageRemain = (count: number, totalCount: number) =>
- (((totalCount - count) / totalCount) * 100).toFixed(0);
+ totalCount === 0 && count === 0
+ ? '0'
+ : (((totalCount - count) / totalCount) * 100).toFixed(0);