Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,5 @@ export const overlayStyle = style({

padding: '0.2rem 0rem 0.5rem 0rem',

backgroundColor: vars.colors.black70,
backgroundColor: vars.colors.black50,
});
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ const StudentCard = ({ studentData, index, lessonId, selectedTab }: StudentCardP
{STATUS_KOREAN_MAP[studentData.reservationStatus]}
</ApplyTag>
</div>
<Text tag="b3_r" color="gray7">
<Text tag="b3_r" color="gray7" className={isWithdrawStudent ? styles.hiddenStyle : undefined}>
{formatPhoneNumber(studentData.phoneNumber)}
</Text>
</div>
Expand All @@ -131,7 +131,11 @@ const StudentCard = ({ studentData, index, lessonId, selectedTab }: StudentCardP
<Text tag="c1_r" color="gray9">
{formatDateTime(studentData.reservationDateTime)}
</Text>
<BoxButton variant={buttonVariant} onClick={handleStatusChangeClick} disabled={successPending || cancelPending}>
<BoxButton
variant={buttonVariant}
onClick={handleStatusChangeClick}
disabled={successPending || cancelPending}
className={isWithdrawStudent ? styles.hiddenStyle : undefined}>
{buttonText}
</BoxButton>
</section>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,8 @@ export const rightWrapper = style({

minWidth: '13.2rem',
});

export const hiddenStyle = style({
visibility: 'hidden',
pointerEvents: 'none',
});
30 changes: 27 additions & 3 deletions src/pages/instructor/classRegister/ClassRegister.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { useQueryClient } from '@tanstack/react-query';
import type { FormEvent } from 'react';
import { useEffect, type FormEvent } from 'react';
import { FormProvider, useController, useForm } from 'react-hook-form';
import { useNavigate, useParams } from 'react-router-dom';
import { useGetLessonDetail } from '@/pages/class/apis/queries';
Expand Down Expand Up @@ -28,6 +28,8 @@ import { classRegisterSchema } from '@/pages/instructor/classRegister/schema/cla
import type { ClassRegisterInfoTypes } from '@/pages/instructor/classRegister/types/api';
import type { LocationTypes } from '@/pages/instructor/classRegister/types/index';
import { ROUTES_CONFIG } from '@/routes/routesConfig';
import Modal from '@/common/components/Modal/Modal';
import { useModalStore } from '@/common/stores/modal';
import BoxButton from '@/shared/components/BoxButton/BoxButton';
import { notify } from '@/shared/components/Toast/Toast';
import { genreEngMapping, levelEngMapping } from '@/shared/constants';
Expand All @@ -41,6 +43,7 @@ import { CLASS_REGISTER_EDIT_MESSAGE } from './constants/notifyMessage';
const ClassRegister = () => {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const { openModal } = useModalStore();

const lessonId = id ? Number(id) : null;
const isValidId = lessonId !== null && !isNaN(lessonId) && lessonId > 0;
Expand Down Expand Up @@ -154,17 +157,18 @@ const ClassRegister = () => {

const handleLocationCheckboxClick = () => {
handleNoneLocationCheck();
setValue('isUndecidedLocation', !isUndecidedLocation, { shouldValidate: true, shouldDirty: true });
clearErrors('selectedLocation');
};

const handleAddTime = () => {
const newTimes = originalHandleAddTime();
setValue('times', newTimes, { shouldValidate: true });
setValue('times', newTimes, { shouldValidate: true, shouldDirty: true });
};

const handleRemoveTime = (idx: number) => {
const newTimes = originalHandleRemoveTime(idx);
setValue('times', newTimes, { shouldValidate: true });
setValue('times', newTimes, { shouldValidate: true, shouldDirty: true });
};

const initTimeAndOpenBottomSheet = () => {
Expand Down Expand Up @@ -290,6 +294,26 @@ const ClassRegister = () => {
};
useBlockBackWithUnsavedChanges({ methods });

useEffect(() => {
if (isEditMode && lessonData?.lessonRound?.lessonRounds?.length) {
const firstRound = lessonData.lessonRound.lessonRounds[0];
const startDateTime = new Date(firstRound.startDateTime);
const now = new Date();

if (now >= startDateTime) {
openModal(() => (
<Modal
type="single"
content={'비정상적인 접근입니다.'}
onClose={() => navigate(-1)}
rightButtonText="뒤로가기"
onClickHandler={() => navigate(-1)}
/>
));
}
}
}, [isEditMode, lessonData, navigate, openModal]);

return (
<>
<FormProvider {...methods}>
Expand Down
10 changes: 9 additions & 1 deletion src/pages/instructor/classRegister/hooks/useClassEditMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ export const useClassEditMode = ({
});

// 외부 state 업데이트
updateExternalStates(lessonData, formattedTimes, locationData, setImageUrls, setTimes, setSelectedLocation, setIsUndecidedLocation);
updateExternalStates(
lessonData,
formattedTimes,
locationData,
setImageUrls,
setTimes,
setSelectedLocation,
setIsUndecidedLocation
);
}, [isEditMode, lessonData, reset, setImageUrls, setTimes, setSelectedLocation, setIsUndecidedLocation]);
};
15 changes: 12 additions & 3 deletions src/pages/reservation/Reservation.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Navigate, useNavigate, useParams } from 'react-router-dom';
import ConfirmationStep from '@/pages/reservation/components/ConfirmationStep/ConfirmationStep';
import ReservationHeader from '@/pages/reservation/components/ReservationHeader/ReservationHeader';
import ReservationStep from '@/pages/reservation/components/ReservationStep/ReservationStep';
Expand All @@ -10,6 +10,7 @@ import { useFunnel } from '@/shared/hooks/useFunnel';

const Reservation = () => {
const navigate = useNavigate();
const { id } = useParams<{ id: string }>();
const { Funnel, Step, setStep } = useFunnel(3, ROUTES_CONFIG.home.path);
const [reservationDetail, setReservationDetail] = useState<ClassReservationResponseTypes | null>(null);

Expand All @@ -27,12 +28,20 @@ const Reservation = () => {

<Step name="2">
<ReservationHeader step={2} />
{reservationDetail && <ConfirmationStep onNext={() => setStep(1)} {...reservationDetail} />}
{!reservationDetail ? (
<Navigate to={ROUTES_CONFIG.reservation.path(id!)} replace />
) : (
<ConfirmationStep onNext={() => setStep(1)} {...reservationDetail} />
)}
</Step>

<Step name="3">
<ReservationHeader step={3} />
<SuccessStep onGoHome={() => navigate(ROUTES_CONFIG.mypageReservation.path)} />
{!reservationDetail ? (
<Navigate to={ROUTES_CONFIG.reservation.path(id!)} replace />
) : (
<SuccessStep onGoHome={() => navigate(ROUTES_CONFIG.mypageReservation.path)} />
)}
</Step>
</Funnel>
);
Expand Down
57 changes: 31 additions & 26 deletions src/routes/guards/reservationGuard.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,41 @@
// import { Navigate, Outlet, useParams } from 'react-router-dom';
// import { useGetLessonDetail } from '@/pages/class/apis/queries';
// import { ROUTES_CONFIG } from '@/routes/routesConfig';
// import { WITHDRAW_USER_NAME } from '@/shared/constants/withdrawUser';
import { Navigate, Outlet, useParams, useSearchParams } from 'react-router-dom';
import { useGetLessonDetail } from '@/pages/class/apis/queries';
import { ROUTES_CONFIG } from '@/routes/routesConfig';
import { WITHDRAW_USER_NAME } from '@/shared/constants/withdrawUser';

// export const ReservationGuard = () => {
// const { id } = useParams<{ id: string }>();
// const lessonId = Number(id);
export const ReservationGuard = () => {
const { id } = useParams<{ id: string }>();
const [searchParams] = useSearchParams();

// const isValidLessonId = Number.isInteger(lessonId) && lessonId > 0;
const lessonId = Number(id);
const step = searchParams.get('step') ?? '1';

// const { data, isPending, isError } = useGetLessonDetail(lessonId, {
// enabled: isValidLessonId,
// });
const isValidLessonId = Number.isInteger(lessonId) && lessonId > 0;
const shouldCheckGuard = step === '1';

// if (!isValidLessonId) {
// return <Navigate to={ROUTES_CONFIG.error.path} replace />;
// }
const { data, isPending, isError } = useGetLessonDetail(lessonId, {
enabled: isValidLessonId && shouldCheckGuard,
});

// if (isPending) return null;
if (!isValidLessonId) {
return <Navigate to={ROUTES_CONFIG.error.path} replace />;
}

// if (isError || !data) {
// return <Navigate to={ROUTES_CONFIG.error.path} replace />;
// }
if (shouldCheckGuard) {
if (isPending) return null;

// const { status, bookStatus, teacherNickname, isMyLesson } = data;
if (isError || !data) {
return <Navigate to={ROUTES_CONFIG.error.path} replace />;
}

// const isButtonEnabled =
// status === 'OPEN' && isMyLesson === false && bookStatus === false && teacherNickname !== WITHDRAW_USER_NAME;
const { status, bookStatus, teacherNickname, isMyLesson } = data;

// if (!isButtonEnabled) {
// return <Navigate to={ROUTES_CONFIG.class.path(lessonId.toString())} replace />;
// }
const isButtonEnabled = status === 'OPEN' && !isMyLesson && !bookStatus && teacherNickname !== WITHDRAW_USER_NAME;

// return <Outlet />;
// };
if (!isButtonEnabled) {
return <Navigate to={ROUTES_CONFIG.class.path(lessonId.toString())} replace />;
}
}

return <Outlet />;
};
12 changes: 7 additions & 5 deletions src/routes/router.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { lazy } from 'react';
import { createBrowserRouter } from 'react-router-dom';
import Withdraw from '@/pages/mypage/components/Withdraw/Withdraw';
import Reservation from '@/pages/reservation/Reservation';
import AuthGuard from '@/routes/guards/authGuard';
import GuestGuard from '@/routes/guards/guestGuard';
import OnboardingGuard from '@/routes/guards/onboardingGuard';
import { ReservationGuard } from '@/routes/guards/reservationGuard';
import WithdrawGuard from '@/routes/guards/withdrawGuard';
import { guestRoutes } from '@/routes/modules/guestRoutes';
import { protectedRoutes } from '@/routes/modules/protectedRoutes';
Expand Down Expand Up @@ -38,11 +40,11 @@ export const router = createBrowserRouter([
element: <WithdrawGuard />,
children: [{ index: true, element: <Withdraw /> }],
},
// {
// path: ROUTES_CONFIG.reservation.path(':id'),
// element: <ReservationGuard />,
// children: [{ index: true, element: <Reservation /> }],
// },
{
path: ROUTES_CONFIG.reservation.path(':id'),
element: <ReservationGuard />,
children: [{ index: true, element: <Reservation /> }],
},
{ path: '*', element: <Error /> },
],
},
Expand Down
74 changes: 43 additions & 31 deletions src/shared/hooks/useBlockBackWithUnsavedChanges.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,18 @@ export default function useBlockBackWithUnsavedChanges<TFieldValues extends Fiel
const handlePopState = () => {
if (!shouldBlockRef.current) return;

// 모달이 열려있으면 모달을 닫고 현재 위치 유지
// 모달이 열려있으면 먼저 히스토리를 복원한 후 모달을 닫고 현재 위치 유지
if (closeModalRef.current) {
closeModalRef.current();
closeModalRef.current = null;
// Chrome에서 popstate 발생 시 이미 페이지가 이동한 상태일 수 있으므로
// 즉시 히스토리를 복원해야 함
history.pushState(null, '', location.href);
// 다음 이벤트 루프에서 모달을 닫아서 히스토리 복원이 완료된 후 처리
setTimeout(() => {
if (closeModalRef.current) {
closeModalRef.current();
closeModalRef.current = null;
}
}, 0);
return;
}

Expand All @@ -84,36 +91,41 @@ export default function useBlockBackWithUnsavedChanges<TFieldValues extends Fiel

if (!armedRef.current) return;

// Chrome에서 popstate 발생 시 이미 페이지가 이동한 상태일 수 있으므로
// 즉시 히스토리를 복원한 후 모달을 열어야 함
history.pushState(null, '', location.href);

openModal(({ close }) => {
closeModalRef.current = close;
return (
<Modal
content={content}
description={description}
type="default"
onClose={() => {
closeModalRef.current = null;
close();
}}
leftButtonText={leftButtonText}
rightButtonText={rightButtonText}
onLeftClickHandler={() => {
shouldBlockRef.current = false;
closeModalRef.current = null;
close();
const steps = armedRef.current ? -2 : -1;
armedRef.current = false;
navigate(steps);
}}
onRightClickHandler={() => {
closeModalRef.current = null;
close();
}}
/>
);
});
// 다음 이벤트 루프에서 모달을 열어서 히스토리 복원이 완료된 후 처리
setTimeout(() => {
openModal(({ close }) => {
closeModalRef.current = close;
return (
<Modal
content={content}
description={description}
type="default"
onClose={() => {
closeModalRef.current = null;
close();
}}
leftButtonText={leftButtonText}
rightButtonText={rightButtonText}
onLeftClickHandler={() => {
shouldBlockRef.current = false;
closeModalRef.current = null;
close();
const steps = armedRef.current ? -2 : -1;
armedRef.current = false;
navigate(steps);
}}
onRightClickHandler={() => {
closeModalRef.current = null;
close();
}}
/>
);
});
}, 0);
};

window.addEventListener('popstate', handlePopState);
Expand Down