diff --git a/src/app/mobile/main/page.tsx b/src/app/mobile/main/page.tsx index 8a4db2f..ede2c88 100644 --- a/src/app/mobile/main/page.tsx +++ b/src/app/mobile/main/page.tsx @@ -11,6 +11,7 @@ import { WelfareItemData, Item } from '@/types/welfareItemType'; import IconSearch from 'public/assets/icons/icon-search.svg'; import { useRouter } from 'next/navigation'; import { requestNotificationPermission } from '@/utils/pushNotification'; +import PopUp from '@/components/mobile/PopUp'; export default function MobileMain() { const [isBottomSheetOpen, setIsBottomSheetOpen] = useState(false); @@ -19,6 +20,7 @@ export default function MobileMain() { items: [], }); const [searchQuery, setSearchQuery] = useState(''); + const [showPopUp, setShowPopUp] = useState(false); const router = useRouter(); const fetchWelfareItems = async () => { @@ -47,6 +49,11 @@ export default function MobileMain() { fetchWelfareItems(); requestNotificationPermission(); + + // "다시 보지 않기" 플래그가 없으면 팝업 표시 + if (!localStorage.getItem('popUpDismissed')) { + setShowPopUp(true); + } }, []); const handleSearchChange = (e: React.ChangeEvent) => { @@ -103,6 +110,19 @@ export default function MobileMain() { + {showPopUp && ( + setShowPopUp(false)} + onClickOther={() => { + localStorage.setItem('popUpDismissed', 'true'); + setShowPopUp(false); + }} + /> + )} + {/* Bottom Sheet */} ({ ...prevErrors, quantity: errorMsg })); }; - // 시간 입력 시 검증 - const handleHourChange = (e: React.ChangeEvent) => { - const { value } = e.target; - setHour(value); + // 통합 시간 검증 함수 + const validateTime = (hourStr: string, minuteStr: string): string => { + const numHour = parseInt(hourStr, 10); + const numMinute = parseInt(minuteStr, 10); - const numHour = parseInt(value, 10); - let errorMsg = ''; + if (Number.isNaN(numHour) || Number.isNaN(numMinute)) { + return '올바른 시간을 입력해주세요.'; + } - if (Number.isNaN(numHour)) { - errorMsg = '올바른 시간을 입력해주세요.'; - } else if (numHour < 10 || numHour >= 17) { - errorMsg = '대여 가능 시간은 10:00 ~ 17:00입니다.'; // 10시 ~ 17시 사이가 아닐 경우 - } else if (numHour < currentHour) { - errorMsg = '대여는 현재 시간 이후로만 가능합니다.'; // 현재 시간보다 이전이면 무조건 오류 - } else if ( - numHour === currentHour && - parseInt(minute || '0', 10) <= currentMinute - ) { - errorMsg = '대여는 현재 시간 이후로만 가능합니다.'; // 현재 시각과 같다면, 분이 현재 분보다 커야 함 + const now = new Date(); + const inputTime = new Date( + now.getFullYear(), + now.getMonth(), + now.getDate(), + numHour, + numMinute, + ); + const openTime = new Date( + now.getFullYear(), + now.getMonth(), + now.getDate(), + 10, + 0, + ); + const closeTime = new Date( + now.getFullYear(), + now.getMonth(), + now.getDate(), + 17, + 0, + ); + + // 점심 시간 + const lunchOpenTime = new Date( + now.getFullYear(), + now.getMonth(), + now.getDate(), + 12, + 0, + ); + const lunchCloseTime = new Date( + now.getFullYear(), + now.getMonth(), + now.getDate(), + 12, + 59, + ); + + // 운영시간: 10:00 ~ 17:00 + if (inputTime < openTime || inputTime > closeTime) { + return '대여 가능한 시간은 10:00 ~ 17:00입니다.'; + } + + // 점심시간: 12:00 ~ 12:59 + if (inputTime >= lunchOpenTime && inputTime < lunchCloseTime) { + return '12:00 ~ 12:59은 점심시간입니다.'; + } + + // 입력 시간이 현재 시간보다 이후인지 체크 + if (inputTime <= now) { + return '대여는 현재 시간 이후로만 가능합니다.'; + } + + // 현재 시각을 기준으로 5분 후 체크 (현재 시간이 16:55 이전일 때만 적용) + const threshold = new Date( + now.getFullYear(), + now.getMonth(), + now.getDate(), + 16, + 55, + ); + if (now < threshold) { + const fiveMinutesLater = new Date(now.getTime() + 5 * 60 * 1000); + if (inputTime < fiveMinutesLater) { + return '대여는 현재 시간으로부터 5분 후에 가능합니다.'; + } } + return ''; + }; + + // 시간 입력 시 검증 (시) + const handleHourChange = (e: React.ChangeEvent) => { + const { value } = e.target; + setHour(value); + + const errorMsg = validateTime(value, minute); setErrors((prevErrors) => ({ ...prevErrors, time: errorMsg })); }; - // 분 입력 시 검증 + // 시간 입력 시 검증 (분) const handleMinuteChange = (e: React.ChangeEvent) => { const { value } = e.target; setMinute(value); - const numHour = parseInt(hour, 10); - const numMinute = parseInt(value, 10); - let errorMsg = ''; - - if (Number.isNaN(numHour) || Number.isNaN(numMinute)) { - errorMsg = '올바른 시간을 입력해주세요.'; - } else if (numHour < 10 || numHour >= 17) { - errorMsg = '대여 가능 시간은 10:00 ~ 17:00입니다.'; - } else if (numHour < currentHour) { - errorMsg = '대여는 현재 시간 이후로만 가능합니다.'; - } else if (numHour === currentHour && numMinute <= currentMinute) { - errorMsg = '대여는 현재 시간 이후로만 가능합니다.'; - } - + const errorMsg = validateTime(hour, value); setErrors((prevErrors) => ({ ...prevErrors, time: errorMsg })); }; diff --git a/src/components/mobile/PopUp/index.tsx b/src/components/mobile/PopUp/index.tsx new file mode 100644 index 0000000..249c51c --- /dev/null +++ b/src/components/mobile/PopUp/index.tsx @@ -0,0 +1,66 @@ +import { handleTouchStart, handleTouchEnd } from '@/utils/handleTouch'; + +interface PopUpProps { + title: string; // PopUp창 제목 + content: string; // PopUp창 내용 + ctaButtonText?: string; // 오른쪽 버튼에 들어갈 문구 + otherButtonText?: string; // 왼쪽 버튼에 들어갈 문구 + onClickCta?: () => void; // 오른쪽 버튼을 눌렀을 때 실행될 함수 + onClickOther?: () => void; // 왼쪽 버튼을 눌렀을 때 실행될 함수 +} + +export default function PopUp({ + title, + content, + ctaButtonText = '확인', + otherButtonText = '다시 보지 않기', + onClickCta, + onClickOther, +}: PopUpProps) { + const defalutButtonClass = + 'text-body-1-normal_semi w-[108px] rounded-[10px] py-[9px] font-medium outline-none '; + + return ( +
+ {/* 반투명한 검정 배경 */} + {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */} +
+ +
+
+ {/* 제목 */} +
{title}
+ {/* 문구 */} +
+ {content} +
+
+ + {/* 버튼 2개(서브 버튼 / 메인 버튼) */} +
+ + +
+
+
+ ); +}