@@ -238,4 +131,5 @@ const LotteryCanvas = ({
>
);
};
+
export default LotteryCanvas;
diff --git a/service/src/components/MainPage/DrawSection/DrawSection.tsx b/service/src/components/MainPage/DrawSection/DrawSection.tsx
index e69f232..13534a7 100644
--- a/service/src/components/MainPage/DrawSection/DrawSection.tsx
+++ b/service/src/components/MainPage/DrawSection/DrawSection.tsx
@@ -6,9 +6,13 @@ import Button from '@/components/common/Button/Button';
import { motion } from 'framer-motion';
import { SCROLL_MOTION } from '@/constants/animation';
import { EventInfo } from '@/types/main/type';
-import { memo, useCallback, useEffect, useState } from 'react';
-import { useEventDateContext } from '@/store/context/useEventDateContext';
+import { memo, useCallback, useEffect, useMemo, useState } from 'react';
+import {
+ useEventDateContext,
+ useEventDateSetterContext,
+} from '@/store/context/useEventDateContext';
import NotEventPeriodPage from '@/components/ErrorPage/NotEventPeriodPage';
+import { checkDrawPeriod } from '@/utils/checkDrawPeriod';
const backgroundImage =
'https://d1wv99asbppzjv.cloudfront.net/main-page/event_bg_3.webp';
@@ -30,40 +34,28 @@ const DrawSection = ({
drawEndTime,
}: EventProps) => {
const { startDate, endDate } = useEventDateContext();
- const today = new Date();
+ const { setStartTime, setEndTime } = useEventDateSetterContext();
+ const today = useMemo(() => new Date(), []);
const navigator = useNavigate();
const { isLogined } = useLoginContext();
- const checkDrawPeriod = useCallback(() => {
- const startPeriod = new Date(startDate);
- const endPeriod = new Date(endDate);
- const drawStartDateTime = new Date(today);
- const [startHour, startMinute] = drawStartTime.split(':').map(Number);
- drawStartDateTime.setHours(startHour, startMinute, 0, 0);
-
- const drawEndDateTime = new Date(today);
- const [endHour, endMinute] = drawEndTime.split(':').map(Number);
- drawEndDateTime.setHours(endHour, endMinute, 0, 0);
-
- // ์กฐ๊ฑด์ ์ฒดํฌํ์ฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํ
- if (
- today >= startPeriod &&
- today <= endPeriod &&
- today >= drawStartDateTime &&
- today <= drawEndDateTime
- ) {
- return true;
- } else {
- return false;
- }
- }, [startDate, endDate, drawStartTime, drawEndTime, today]);
-
const [isDrawPeriod, setIsDrawPeriod] = useState(false);
useEffect(() => {
- const isPeriod = checkDrawPeriod();
+ setStartTime(drawStartTime);
+ setEndTime(drawEndTime);
+ }, [drawEndTime, drawStartTime]);
+
+ useEffect(() => {
+ const isPeriod = checkDrawPeriod(
+ startDate,
+ endDate,
+ drawStartTime,
+ drawEndTime,
+ today
+ );
setIsDrawPeriod(isPeriod);
- }, [checkDrawPeriod]);
+ }, [today]);
const goLotteryLounge = useCallback(() => {
if (isDrawPeriod) {
diff --git a/service/src/components/MainPage/EventIntroductionSection/EventIntroductionSection.tsx b/service/src/components/MainPage/EventIntroductionSection/EventIntroductionSection.tsx
index 6406829..211263f 100644
--- a/service/src/components/MainPage/EventIntroductionSection/EventIntroductionSection.tsx
+++ b/service/src/components/MainPage/EventIntroductionSection/EventIntroductionSection.tsx
@@ -34,7 +34,7 @@ const EventIntroductionSection = () => {
>
{isOpen && (
-
+
)}
diff --git a/service/src/components/MainPage/EventSection/LoginModal/LoginModal.tsx b/service/src/components/MainPage/EventSection/LoginModal/LoginModal.tsx
index bd9d7b7..f726c41 100644
--- a/service/src/components/MainPage/EventSection/LoginModal/LoginModal.tsx
+++ b/service/src/components/MainPage/EventSection/LoginModal/LoginModal.tsx
@@ -1,236 +1,43 @@
import './LoginModal.css';
import Button from '@/components/common/Button/Button';
import Input from '@/components/common/Input/Input';
-import { ChangeEvent, useEffect, useState, memo, useCallback } from 'react';
-import { useLoginContext } from '@/store/context/useLoginContext';
+import { memo } from 'react';
import Modal from '@/components/common/Modal/Modal';
-
-import {
- useMutationCode,
- useMutationCodeVerification,
- useMutationLogin,
- useMutationTestCode,
-} from '@/apis/login/query';
-import {
- ConfirmVerificationRequestBody,
- LoginRequestBody,
-} from '@/types/Authorization/type';
-import { ErrorCode, ERROR_CODES, ERROR_MESSAGES } from '@/constants/error';
-import { parseISO, differenceInSeconds } from 'date-fns';
-import { validatePhoneNumber } from '@/utils/checkPhoneNumber';
-import { checkAuthCode } from '@/utils/checkAuthCode';
-import { useQueryClient } from '@tanstack/react-query';
-import { setCookie } from '@/utils/cookie';
-import { useUrl } from '@/store/context/useUrl';
-import { getSharedUrl } from '@/apis/shareurl/api';
-
-interface CloseProps {
- onClose: () => void;
-}
-
+import { useLoginLogic } from '@/hooks/MainPage/useLoginLogic';
+import { useModalContext } from '@/store/context/useModalContext';
const checkbox = 'svg/check-off.svg';
const checked = 'svg/check-on.svg';
-const LoginModal = ({ onClose }: CloseProps) => {
- const codeMutation = useMutationCode();
- //const codeMutation = useMutationTestCode();
- const codeVerificationMutation = useMutationCodeVerification();
- const loginMutation = useMutationLogin();
-
- const [time, setTime] = useState({ timer: 0, minutes: 0, seconds: 0 });
- const [name, setName] = useState('');
- const [code, setCode] = useState('');
- const [phoneNumber, setPhoneNumber] = useState('');
- const [validPhoneNumber, setValidPhoneNumber] = useState(false);
- const [validCode, setValidCode] = useState(false);
- const [canSendCode, setCanSendCode] = useState(true);
- const [codeVerified, setCodeVerified] = useState(false);
- const [privacyConsent, setPrivacyConsent] = useState(false);
- const [marketingConsent, setMarketingConsent] = useState(false);
-
- const [allValid, setAllValid] = useState(false);
-
- const [codeErrorMsg, setCodeErrorMsg] = useState
('');
- const [validateErrorMsg, setValidateErrorMsg] = useState('');
-
- const { setUrl } = useUrl();
-
- const queryClient = useQueryClient();
-
- const handleNameInputChange = (e: ChangeEvent) => {
- e.preventDefault();
- const newValue = e.target.value.replace(/\s+/g, ''); // ๊ณต๋ฐฑ ์ ๊ฑฐ
- setName(newValue);
- };
+const LoginModal = () => {
+ const { isOpen, setIsOpen } = useModalContext();
- const handlePhoneNumberChange = (e: ChangeEvent) => {
- const rawValue = e.target.value.replace(/\D/g, '').slice(0, 11); // ์ซ์๋ง ์ถ์ถ
- setPhoneNumber(rawValue);
- setValidPhoneNumber(validatePhoneNumber(rawValue));
- };
- const handlePrivacyConsentChange = () => {
- setPrivacyConsent((prevConsent) => !prevConsent);
+ const onClose = () => {
+ setIsOpen(!isOpen);
};
- const handleMarketingConsentChange = () => {
- setMarketingConsent((prevConsent) => !prevConsent);
- };
-
- const { setIsLogined } = useLoginContext();
-
- const handleCodeInputChange = (e: ChangeEvent) => {
- const filteredValue = e.target.value.slice(0, 6); // 6์๋ฆฌ๊น์ง ์ ํ
- setValidCode(checkAuthCode(filteredValue));
- setCode(filteredValue);
- };
-
- const handleSendAuthCode = useCallback(
- async (phoneNumber: string) => {
- if (!codeVerified) {
- codeMutation.mutate(phoneNumber, {
- onSuccess: (response) => {
- console.log('์ธ์ฆ๋ฒํธ ์ ์ก ์ฑ๊ณต:', response);
- if (response.isSuccess && response.result) {
- setTime({
- timer: response.result.timeLimit,
- minutes: Math.floor(response.result.timeLimit / 60),
- seconds: response.result.timeLimit % 60,
- });
- setValidateErrorMsg('');
- const interval = setInterval(() => {
- setTime((prevTime) => {
- const newTimer = prevTime.timer - 1;
- if (newTimer <= 0) {
- clearInterval(interval);
- return { ...prevTime, timer: 0, minutes: 0, seconds: 0 };
- }
- return {
- ...prevTime,
- timer: newTimer,
- minutes: Math.floor(newTimer / 60),
- seconds: newTimer % 60,
- };
- });
- }, 1000);
- } else if (!response.isSuccess && response.code in ERROR_MESSAGES) {
- setCodeErrorMsg('');
- setTime({ timer: 0, minutes: 0, seconds: 0 });
- setCanSendCode(true);
- alert(ERROR_MESSAGES[response.code as ErrorCode]);
- }
- },
- onError: () => {
- setCanSendCode(true);
- },
- });
- }
- },
- [codeVerified, codeMutation]
- );
-
- const handleVerification = useCallback(
- async (body: ConfirmVerificationRequestBody) => {
- codeVerificationMutation.mutate(body, {
- onSuccess: async (response) => {
- if (response.isSuccess) {
- setCodeErrorMsg('');
- setValidateErrorMsg('');
- setCodeVerified(true);
- setTime({ timer: 0, minutes: 0, seconds: 0 });
- } else {
- console.log(response);
-
- if (response.code in ERROR_MESSAGES) {
- const errorMessage = ERROR_MESSAGES[response.code as ErrorCode];
-
- if (response.code === ERROR_CODES.TIMEOUT) {
- setCodeErrorMsg(errorMessage);
- } else if (response.code !== ERROR_CODES.RESEND_REQUIRED) {
- setValidateErrorMsg(errorMessage);
- } else if (response.code === ERROR_CODES.RESEND_REQUIRED) {
- setValidateErrorMsg(errorMessage);
- setTime({ timer: 0, minutes: 0, seconds: 0 });
- setCodeErrorMsg('');
- }
- }
- }
- },
- });
- },
- [codeVerificationMutation]
- );
-
- const handleLogin = useCallback(
- async (body: LoginRequestBody) => {
- loginMutation.mutate(body, {
- onSuccess: async (response) => {
- console.log(response);
-
- if (response.isSuccess && response.result) {
- setCodeErrorMsg('');
- setValidateErrorMsg('');
-
- const expiresAt = parseISO(response.result.expiredTime);
- const maxAge = differenceInSeconds(expiresAt, new Date());
-
- setCookie('accessToken', response.result.accessToken, {
- path: '/',
- maxAge: maxAge,
- secure: true,
- sameSite: 'strict',
- });
-
- setCookie('refreshToken', response.result.refreshToken, {
- path: '/',
- maxAge: 604800,
- secure: true,
- sameSite: 'strict',
- });
-
- // ๋ฌดํจํ ํ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๊ฐ์ ธ์ด
- await queryClient.invalidateQueries({
- queryKey: ['sharedUrl', response.result.accessToken],
- });
-
- const sharedUrlData = await queryClient.fetchQuery({
- queryKey: ['sharedUrl', response.result.accessToken],
- queryFn: () => getSharedUrl(response.result?.accessToken),
- });
-
- if (sharedUrlData.isSuccess) {
- setUrl(sharedUrlData.result.shareUrl);
- }
- setIsLogined(true);
- onClose();
- } else if (!response.isSuccess && response.code in ERROR_MESSAGES) {
- alert(ERROR_MESSAGES[response.code as ErrorCode]);
- resetForm();
- }
- },
- });
- },
- [loginMutation, queryClient, setIsLogined, onClose]
- );
-
- const resetForm = () => {
- setName('');
- setPhoneNumber('');
- setCode('');
- setValidCode(false);
- setCodeVerified(false);
- setAllValid(false);
- setMarketingConsent(false);
- setPrivacyConsent(false);
- setCanSendCode(true);
- };
-
- useEffect(() => {
- if (privacyConsent && codeVerified) {
- setAllValid(true);
- } else {
- setAllValid(false);
- }
- }, [privacyConsent, codeVerified]);
+ const {
+ time,
+ name,
+ code,
+ phoneNumber,
+ validPhoneNumber,
+ validCode,
+ canSendCode,
+ codeVerified,
+ privacyConsent,
+ marketingConsent,
+ allValid,
+ codeErrorMsg,
+ validateErrorMsg,
+ handleNameInputChange,
+ handlePhoneNumberChange,
+ handlePrivacyConsentChange,
+ handleMarketingConsentChange,
+ handleCodeInputChange,
+ handleSendAuthCode,
+ handleVerification,
+ handleLogin,
+ } = useLoginLogic(onClose);
return (
@@ -348,4 +155,5 @@ const LoginModal = ({ onClose }: CloseProps) => {
);
};
+
export default memo(LoginModal);
diff --git a/service/src/components/QuizLounge/QuizContainer.tsx b/service/src/components/QuizLounge/QuizContainer.tsx
index ffa11ae..020d9fc 100644
--- a/service/src/components/QuizLounge/QuizContainer.tsx
+++ b/service/src/components/QuizLounge/QuizContainer.tsx
@@ -11,7 +11,7 @@ import { useMutationPostAnswer } from '@/apis/quizLounge/query';
import TutorialResultModal from './TutorialResultModal';
import { ModalData, QuizContainerProps } from '@/types/quizLounge/type';
import { useModalContext } from '@/store/context/useModalContext';
-//import { ERROR_MESSAGES, ErrorCode } from '@/constants/error';
+
const QuizContainer = ({
answer,
mode,
diff --git a/service/src/hooks/LotteryLounge/useCanvasDrawing.tsx b/service/src/hooks/LotteryLounge/useCanvasDrawing.tsx
new file mode 100644
index 0000000..d2866d0
--- /dev/null
+++ b/service/src/hooks/LotteryLounge/useCanvasDrawing.tsx
@@ -0,0 +1,137 @@
+import { useRef, useEffect, useState, useCallback } from 'react';
+import { craftFireworks } from '@/utils/confettiCrafter';
+import { useModalContext } from '@/store/context/useModalContext';
+import { useNavigate } from 'react-router-dom';
+import { ROUTER_PATH } from '@/constants/lib/constants';
+
+export const useCanvasDrawing = () => {
+ const { isOpen, setIsOpen } = useModalContext();
+
+ const [isGameEnded, setIsGameEnded] = useState(false);
+
+ const isCompeletRef = useRef(false);
+
+ const navigation = useNavigate();
+
+ const closeModal = () => {
+ setIsOpen(false);
+ setIsGameEnded(true);
+ };
+
+ const canvasRef = useRef(null);
+ const drawing = useRef(false);
+
+ useEffect(() => {
+ const canvas = canvasRef.current;
+ const ctx = canvas?.getContext('2d');
+ if (!canvas || !ctx) return;
+
+ canvas.width = 784;
+ canvas.height = 400;
+
+ ctx.fillStyle = '#FFFFFF';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+ // ์ง์ฐ๊ธฐ ์ค์
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.lineWidth = 50;
+ ctx.lineJoin = 'round';
+ ctx.lineCap = 'round';
+ }, []);
+
+ const endDrawing = () => {
+ drawing.current = false;
+ if (canvasRef.current) {
+ canvasRef.current.getContext('2d')?.beginPath();
+ }
+ };
+
+ const draw = (
+ e: React.MouseEvent | React.TouchEvent
+ ) => {
+ if (!drawing.current) return;
+
+ const canvas = canvasRef.current;
+ const ctx = canvas?.getContext('2d');
+ if (!canvas || !ctx) return;
+
+ const rect = canvas.getBoundingClientRect();
+ const scaleX = canvas.width / rect.width;
+ const scaleY = canvas.height / rect.height;
+
+ let x, y;
+ if ('clientX' in e) {
+ x = (e.clientX - rect.left) * scaleX;
+ y = (e.clientY - rect.top) * scaleY;
+ } else {
+ x = (e.touches[0].clientX - rect.left) * scaleX;
+ y = (e.touches[0].clientY - rect.top) * scaleY;
+ }
+
+ ctx.lineTo(x, y);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.moveTo(x, y);
+
+ checkErasePercentage();
+ };
+
+ const checkErasePercentage = () => {
+ const canvas = canvasRef.current;
+ const ctx = canvas?.getContext('2d');
+ if (!canvas || !ctx) return;
+
+ const startX = 100;
+ const startY = 100;
+ const width = canvas.width - startY * 2;
+ const height = canvas.height - startX * 2;
+ const imageData = ctx.getImageData(startX, startY, width, height);
+ const data = imageData.data;
+ let erasedPixels = 0;
+
+ for (let i = 3; i < data.length; i += 4) {
+ if (data[i] === 0) {
+ erasedPixels++;
+ }
+ }
+
+ const erasePercentage = (erasedPixels / (width * height)) * 100;
+
+ if (erasePercentage >= 75 && !isCompeletRef.current) {
+ fadeOutCanvas();
+ craftFireworks(1);
+ isCompeletRef.current = true;
+ setTimeout(() => {
+ setIsOpen(true);
+ }, 1500);
+ }
+ };
+
+ const fadeOutCanvas = () => {
+ if (canvasRef.current) {
+ canvasRef.current.style.transition = 'opacity 1s';
+ canvasRef.current.style.opacity = '0';
+ canvasRef.current.style.pointerEvents = 'none';
+ }
+ };
+
+ const handleRetryButton = () => {
+ window.location.reload();
+ };
+
+ const handleBackToMain = useCallback(() => {
+ navigation(ROUTER_PATH.MAIN);
+ }, [navigation]);
+
+ return {
+ canvasRef,
+ draw,
+ endDrawing,
+ isOpen,
+ closeModal,
+ isGameEnded,
+ drawing,
+ handleRetryButton,
+ handleBackToMain,
+ };
+};
diff --git a/service/src/hooks/MainPage/useLoginLogic.tsx b/service/src/hooks/MainPage/useLoginLogic.tsx
new file mode 100644
index 0000000..0569b31
--- /dev/null
+++ b/service/src/hooks/MainPage/useLoginLogic.tsx
@@ -0,0 +1,250 @@
+// src/hooks/useLoginTest.ts
+
+import { useState, useCallback, ChangeEvent, useEffect } from 'react';
+import { useLoginContext } from '@/store/context/useLoginContext';
+import {
+ useMutationCode,
+ useMutationCodeVerification,
+ useMutationLogin,
+ useMutationTestCode,
+} from '@/apis/login/query';
+import {
+ ConfirmVerificationRequestBody,
+ LoginRequestBody,
+} from '@/types/Authorization/type';
+import { ErrorCode, ERROR_CODES, ERROR_MESSAGES } from '@/constants/error';
+import { parseISO, differenceInSeconds } from 'date-fns';
+import { validatePhoneNumber } from '@/utils/checkPhoneNumber';
+import { checkAuthCode } from '@/utils/checkAuthCode';
+import { useQueryClient } from '@tanstack/react-query';
+import { setCookie } from '@/utils/cookie';
+import { useUrl } from '@/store/context/useUrl';
+import { getSharedUrl } from '@/apis/shareurl/api';
+
+export const useLoginLogic = (onClose: () => void) => {
+ const codeMutation = useMutationCode();
+ const codeVerificationMutation = useMutationCodeVerification();
+ const loginMutation = useMutationLogin();
+
+ const [time, setTime] = useState({ timer: 0, minutes: 0, seconds: 0 });
+ const [name, setName] = useState('');
+ const [code, setCode] = useState('');
+ const [phoneNumber, setPhoneNumber] = useState('');
+ const [validPhoneNumber, setValidPhoneNumber] = useState(false);
+ const [validCode, setValidCode] = useState(false);
+ const [canSendCode, setCanSendCode] = useState(true);
+ const [codeVerified, setCodeVerified] = useState(false);
+ const [privacyConsent, setPrivacyConsent] = useState(false);
+ const [marketingConsent, setMarketingConsent] = useState(false);
+ const [allValid, setAllValid] = useState(false);
+ const [codeErrorMsg, setCodeErrorMsg] = useState('');
+ const [validateErrorMsg, setValidateErrorMsg] = useState('');
+
+ const { setUrl } = useUrl();
+ const queryClient = useQueryClient();
+ const { setIsLogined } = useLoginContext();
+
+ const handleNameInputChange = useCallback(
+ (e: ChangeEvent) => {
+ const newValue = e.target.value.replace(/\s+/g, ''); // ๊ณต๋ฐฑ ์ ๊ฑฐ
+ setName(newValue);
+ },
+ []
+ );
+
+ const handlePhoneNumberChange = useCallback(
+ (e: ChangeEvent) => {
+ const rawValue = e.target.value.replace(/\D/g, '').slice(0, 11); // ์ซ์๋ง ์ถ์ถ
+ setPhoneNumber(rawValue);
+ setValidPhoneNumber(validatePhoneNumber(rawValue));
+ },
+ []
+ );
+
+ const handlePrivacyConsentChange = useCallback(() => {
+ setPrivacyConsent((prev) => !prev);
+ }, []);
+
+ const handleMarketingConsentChange = useCallback(() => {
+ setMarketingConsent((prev) => !prev);
+ }, []);
+
+ const handleCodeInputChange = useCallback(
+ (e: ChangeEvent) => {
+ const filteredValue = e.target.value.slice(0, 6); // 6์๋ฆฌ๊น์ง ์ ํ
+ setValidCode(checkAuthCode(filteredValue));
+ setCode(filteredValue);
+ },
+ []
+ );
+
+ const handleSendAuthCode = useCallback(
+ async (phoneNumber: string) => {
+ if (!codeVerified) {
+ codeMutation.mutate(phoneNumber, {
+ onSuccess: (response) => {
+ console.log('์ธ์ฆ๋ฒํธ ์ ์ก ์ฑ๊ณต:', response);
+
+ if (response.isSuccess && response.result) {
+ setTime({
+ timer: response.result.timeLimit,
+ minutes: Math.floor(response.result.timeLimit / 60),
+ seconds: response.result.timeLimit % 60,
+ });
+ setValidateErrorMsg('');
+ const interval = setInterval(() => {
+ setTime((prevTime) => {
+ const newTimer = prevTime.timer - 1;
+ if (newTimer <= 0) {
+ clearInterval(interval);
+ return { ...prevTime, timer: 0, minutes: 0, seconds: 0 };
+ }
+ return {
+ ...prevTime,
+ timer: newTimer,
+ minutes: Math.floor(newTimer / 60),
+ seconds: newTimer % 60,
+ };
+ });
+ }, 1000);
+ } else if (!response.isSuccess && response.code in ERROR_MESSAGES) {
+ setCodeErrorMsg('');
+ setTime({ timer: 0, minutes: 0, seconds: 0 });
+ setCanSendCode(true);
+ alert(ERROR_MESSAGES[response.code as ErrorCode]);
+ }
+ },
+ onError: () => {
+ setCanSendCode(true);
+ },
+ });
+ }
+ },
+ [codeMutation, codeVerified]
+ );
+
+ const handleVerification = useCallback(
+ async (body: ConfirmVerificationRequestBody) => {
+ codeVerificationMutation.mutate(body, {
+ onSuccess: async (response) => {
+ if (response.isSuccess) {
+ setCodeErrorMsg('');
+ setValidateErrorMsg('');
+ setCodeVerified(true);
+ setTime({ timer: 0, minutes: 0, seconds: 0 });
+ } else {
+ if (response.code in ERROR_MESSAGES) {
+ const errorMessage = ERROR_MESSAGES[response.code as ErrorCode];
+
+ if (response.code === ERROR_CODES.TIMEOUT) {
+ setCodeErrorMsg(errorMessage);
+ } else if (response.code !== ERROR_CODES.RESEND_REQUIRED) {
+ setValidateErrorMsg(errorMessage);
+ } else if (response.code === ERROR_CODES.RESEND_REQUIRED) {
+ setValidateErrorMsg(errorMessage);
+ setTime({ timer: 0, minutes: 0, seconds: 0 });
+ setCodeErrorMsg('');
+ }
+ }
+ }
+ },
+ });
+ },
+ [codeVerificationMutation]
+ );
+
+ const handleLogin = useCallback(
+ async (body: LoginRequestBody) => {
+ loginMutation.mutate(body, {
+ onSuccess: async (response) => {
+ if (response.isSuccess && response.result) {
+ setCodeErrorMsg('');
+ setValidateErrorMsg('');
+
+ const expiresAt = parseISO(response.result.expiredTime);
+ const maxAge = differenceInSeconds(expiresAt, new Date());
+
+ setCookie('accessToken', response.result.accessToken, {
+ path: '/',
+ maxAge: maxAge,
+ secure: true,
+ sameSite: 'strict',
+ });
+
+ setCookie('refreshToken', response.result.refreshToken, {
+ path: '/',
+ maxAge: 604800,
+ secure: true,
+ sameSite: 'strict',
+ });
+
+ // ๋ฌดํจํ ํ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๊ฐ์ ธ์ด
+ await queryClient.invalidateQueries({
+ queryKey: ['sharedUrl', response.result.accessToken],
+ });
+
+ const sharedUrlData = await queryClient.fetchQuery({
+ queryKey: ['sharedUrl', response.result.accessToken],
+ queryFn: () => getSharedUrl(response.result?.accessToken),
+ });
+
+ if (sharedUrlData.isSuccess) {
+ setUrl(sharedUrlData.result.shareUrl);
+ }
+ setIsLogined(true);
+ onClose();
+ } else if (!response.isSuccess && response.code in ERROR_MESSAGES) {
+ alert(ERROR_MESSAGES[response.code as ErrorCode]);
+ resetForm();
+ }
+ },
+ });
+ },
+ [loginMutation, queryClient, onClose, setUrl, setIsLogined]
+ );
+
+ const resetForm = useCallback(() => {
+ setName('');
+ setPhoneNumber('');
+ setCode('');
+ setValidCode(false);
+ setCodeVerified(false);
+ setValidPhoneNumber(false);
+ setAllValid(false);
+ setMarketingConsent(false);
+ setPrivacyConsent(false);
+ setCanSendCode(true);
+ }, []);
+
+ useEffect(() => {
+ if (privacyConsent && codeVerified) {
+ setAllValid(true);
+ } else {
+ setAllValid(false);
+ }
+ }, [privacyConsent, codeVerified]);
+
+ return {
+ time,
+ name,
+ code,
+ phoneNumber,
+ validPhoneNumber,
+ validCode,
+ canSendCode,
+ codeVerified,
+ privacyConsent,
+ marketingConsent,
+ allValid,
+ codeErrorMsg,
+ validateErrorMsg,
+ handleNameInputChange,
+ handlePhoneNumberChange,
+ handlePrivacyConsentChange,
+ handleMarketingConsentChange,
+ handleCodeInputChange,
+ handleSendAuthCode,
+ handleVerification,
+ handleLogin,
+ };
+};
diff --git a/service/src/hooks/common/useApiError.tsx b/service/src/hooks/common/useApiError.tsx
index 1ef137a..198bd61 100644
--- a/service/src/hooks/common/useApiError.tsx
+++ b/service/src/hooks/common/useApiError.tsx
@@ -2,7 +2,7 @@ import { useCallback } from 'react';
interface ErrorResponse {
status: number; // HTTP ์ํ ์ฝ๋
- errorCode: string; // ์๋ฒ ์ ์ ์๋ฌ ์ฝ๋
+ code: string; // ์๋ฒ ์ ์ ์๋ฌ ์ฝ๋
message: string; // ์๋ฌ ๋ฉ์์ง
}
@@ -24,7 +24,7 @@ export const useApiError = () => {
}
const httpMessage = errorResponse?.message || '์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.';
- const httpErrorCode = errorResponse?.errorCode || null;
+ const httpErrorCode = errorResponse?.code || null;
const handle = httpStatus
? statusHandlers[httpStatus]
@@ -36,10 +36,8 @@ export const useApiError = () => {
alert('์์ฒญ์ด ์ฑ๊ณต์ ์ผ๋ก ์ฒ๋ฆฌ๋์์ต๋๋ค.');
}
} else if (error instanceof Error) {
- //JavaScript ์๋ฌ ๊ฐ์ฒด๊ฐ ์ ๋ฌ๋ ๊ฒฝ์ฐ
alert(`์๋ฌ๊ฐ ๋ฐ์ํ์ต๋๋ค: ${error.message}`);
} else {
- // ์ ์ ์๋ ์๋ฌ
alert('์ ์ ์๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.');
}
}, []);
diff --git a/service/src/hooks/common/useFetch.ts b/service/src/hooks/common/useFetch.ts
deleted file mode 100644
index f72aacb..0000000
--- a/service/src/hooks/common/useFetch.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import { useState, useEffect } from 'react';
-import { UseFetchOptions } from '@/types/apiType';
-
-interface Response {
- data: T | null;
- error: Error | null;
- loading: boolean;
-}
-
-function useFetch(
- url: string,
- { method, params, queryParams, body, headers }: UseFetchOptions
-): Response {
- //const [data, setData] = useState(null);
- const [error, setError] = useState(null);
- const [loading, setLoading] = useState(true);
-
- useEffect(() => {
- const fetchData = async () => {
- setLoading(true);
- setError(null);
-
- let fullUrl = url;
- if (params) {
- Object.keys(params).forEach((key) => {
- fullUrl = fullUrl.replace(`/${key}`, String(params[key]));
- });
- }
-
- if (queryParams) {
- const queryString = new URLSearchParams(
- queryParams as Record
- ).toString();
- fullUrl += `?${queryString}`;
- }
-
- const response = await fetch(fullUrl, {
- method,
- headers,
- body: method !== 'GET' && body ? JSON.stringify(body) : null,
- });
-
- return response.json();
- };
-
- fetchData();
- }, [url, method, params, queryParams, body, headers]);
-
- return { data, error, loading };
-}
-
-export default useFetch;
diff --git a/service/src/hooks/useLoginModalDispatchContext.ts b/service/src/hooks/useLoginModalDispatchContext.ts
deleted file mode 100644
index f21ce3d..0000000
--- a/service/src/hooks/useLoginModalDispatchContext.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Dispatch, useContext } from 'react';
-
-import { LoginModalDispatchContext } from '@/store/context/loginModalContext';
-import { LoginAction } from '@/store/types/loginModalTypes';
-
-export default function useLoginModalDispatchContext(): Dispatch {
- const context = useContext(LoginModalDispatchContext);
- if (context === null) {
- throw new Error(
- 'LoginModalDispatchContext must be used within a useLoginModalDispatchContext'
- );
- }
- return context;
-}
diff --git a/service/src/hooks/useLoginModalStateContext.ts b/service/src/hooks/useLoginModalStateContext.ts
deleted file mode 100644
index 0e631c5..0000000
--- a/service/src/hooks/useLoginModalStateContext.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { useContext } from 'react';
-import { loginModalState } from '@/store/types/loginModalTypes';
-import { LoginModalStateContext } from '@/store/context/loginModalContext';
-
-export default function useLoginModalStateContext(): loginModalState {
- const context = useContext(LoginModalStateContext);
- if (context === null) {
- throw new Error(
- 'LoginModalStateContext must be used within a useLoginModalStateContext'
- );
- }
- return context;
-}
diff --git a/service/src/pages/LotteryLounge.tsx b/service/src/pages/LotteryLounge.tsx
index 1d46939..e0b5985 100644
--- a/service/src/pages/LotteryLounge.tsx
+++ b/service/src/pages/LotteryLounge.tsx
@@ -11,6 +11,7 @@ import { useTabContext } from '@/store/context/useTabContext';
import LoadingPage from '@/components/Loading/Loading';
import { useEventDateContext } from '@/store/context/useEventDateContext';
import NotEventPeriodPage from '@/components/ErrorPage/NotEventPeriodPage';
+import { checkDrawPeriod } from '@/utils/checkDrawPeriod';
const backgroundImage =
'https://d1wv99asbppzjv.cloudfront.net/main-page/draw_bg.webp';
@@ -19,7 +20,7 @@ const sample = () => {};
const LotteryLoungePage = () => {
const token = getCookie('accessToken');
- const { startDate, endDate } = useEventDateContext();
+ const { startDate, endDate, startTime, endTime } = useEventDateContext();
const { data, isLoading } = useQueryGetDrawAttendance(token);
const [drawResult, setDrawResult] = useState(null);
const [isScratched, setIsScratched] = useState(false);
@@ -52,16 +53,13 @@ const LotteryLoungePage = () => {
setDrawResult(result);
setIsScratched(true);
};
-
- const startPeriod = new Date(startDate);
- const endPeriod = new Date(endDate);
const today = new Date();
if (isLoading) {
return ;
}
- if (today < startPeriod || today > endPeriod) {
+ if (!checkDrawPeriod(startDate, endDate, startTime, endTime, today)) {
return ;
}
@@ -101,21 +99,14 @@ const LotteryLoungePage = () => {
))
) : (
<>
-
-
-
+ {[...Array(3)].map((_, index) => (
+
+ ))}
>
)}
diff --git a/service/src/pages/ShareHandler.tsx b/service/src/pages/ShareHandlerPage.tsx
similarity index 86%
rename from service/src/pages/ShareHandler.tsx
rename to service/src/pages/ShareHandlerPage.tsx
index d55341b..1d55077 100644
--- a/service/src/pages/ShareHandler.tsx
+++ b/service/src/pages/ShareHandlerPage.tsx
@@ -2,7 +2,7 @@ import { useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import LoadingPage from '../components/Loading/Loading';
-const ShareHandler = () => {
+const ShareHandlerPage = () => {
const { code } = useParams<{ code: string }>();
const navigate = useNavigate();
@@ -18,4 +18,4 @@ const ShareHandler = () => {
return
;
};
-export default ShareHandler;
+export default ShareHandlerPage;
diff --git a/service/src/router.tsx b/service/src/router.tsx
index 91514e0..35fc30a 100644
--- a/service/src/router.tsx
+++ b/service/src/router.tsx
@@ -8,7 +8,7 @@ import CommentsLoungePage from './pages/CommentsLoungePage';
import ErrorBoundaryPage from './components/ErrorPage/ErrorBoundaryPage';
import NotFoundPage from './components/ErrorPage/NotFoundPage';
import ProtectedRoute from '@/components/common/ProtectedRoute/ProtectedRoute';
-import ShareHandler from './pages/ShareHandler';
+import ShareHandlerPage from './pages/ShareHandlerPage';
import WinningResultPage from './pages/WinningResultPage';
export const router = createBrowserRouter([
@@ -43,7 +43,7 @@ export const router = createBrowserRouter([
path: ROUTER_PATH.COMMENTS_LOUNGE,
},
{
- element:
,
+ element:
,
path: ROUTER_PATH.SHARE,
},
{
diff --git a/service/src/store/context/loginModalContext.tsx b/service/src/store/context/loginModalContext.tsx
deleted file mode 100644
index 4699993..0000000
--- a/service/src/store/context/loginModalContext.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import { createContext, Dispatch, ReactNode, useReducer } from 'react';
-import { LoginAction, LoginModalState } from '../types/loginModalTypes';
-import { loginModalReducer } from '../reducer/loginModalReducer';
-
-const initialState: LoginModalState = {
- name: '',
- phoneNumber: '',
- verificationCode: '',
- isVerified: false,
- isChecked: false,
- marketingChecked: false,
- allValid: false,
-};
-
-export const LoginModalStateContext = createContext
(
- null
-);
-export const LoginModalDispatchContext =
- createContext | null>(null);
-
-export const LoginModalProvider = ({ children }: { children: ReactNode }) => {
- const [state, dispatch] = useReducer(loginModalReducer, initialState);
-
- return (
-
-
- {children}
-
-
- );
-};
diff --git a/service/src/store/context/useEventDateContext.tsx b/service/src/store/context/useEventDateContext.tsx
index 3b3c87d..9c6a7b2 100644
--- a/service/src/store/context/useEventDateContext.tsx
+++ b/service/src/store/context/useEventDateContext.tsx
@@ -3,11 +3,15 @@ import { createContext, useContext } from 'react';
interface EventDateContextType {
startDate: string;
endDate: string;
+ startTime: string;
+ endTime: string;
}
interface EventDateSetterContextType {
setStartDate: (date: string) => void;
setEndDate: (date: string) => void;
+ setStartTime: (date: string) => void;
+ setEndTime: (date: string) => void;
}
export const EventDateContext = createContext(
diff --git a/service/src/store/provider/EventDateProvider.tsx b/service/src/store/provider/EventDateProvider.tsx
index b3bb83c..23a99e2 100644
--- a/service/src/store/provider/EventDateProvider.tsx
+++ b/service/src/store/provider/EventDateProvider.tsx
@@ -15,18 +15,28 @@ export const EventDateProvider = ({ children }: EventDateProviderProps) => {
const [endDate, setEndDate] = useState(() => {
return localStorage.getItem('endDate') || '';
});
+ const [startTime, setStartTime] = useState(() => {
+ return localStorage.getItem('startTime') || '';
+ });
+ const [endTime, setEndTime] = useState(() => {
+ return localStorage.getItem('endTime') || '';
+ });
useEffect(() => {
- localStorage.setItem('startDate', startDate);
- }, [startDate]);
+ localStorage.setItem('startTime', startTime);
+ }, [startTime]);
useEffect(() => {
- localStorage.setItem('endDate', endDate);
- }, [endDate]);
+ localStorage.setItem('endTime', endTime);
+ }, [endTime]);
return (
-
-
+
+
{children}
diff --git a/service/src/store/reducer/loginModalReducer.tsx b/service/src/store/reducer/loginModalReducer.tsx
deleted file mode 100644
index aa12925..0000000
--- a/service/src/store/reducer/loginModalReducer.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import {
- LoginModalState,
- LoginAction,
- LOGIN_ACTION,
-} from '../types/loginModalTypes';
-
-export const loginModalReducer = (
- state: LoginModalState,
- action: LoginAction
-): LoginModalState => {
- switch (action.type) {
- case LOGIN_ACTION.SET_VERIFICATION_CODE:
- return { ...state, verificationCode: action.payload };
- case LOGIN_ACTION.SET_VERIFIED:
- return {
- ...state,
- isVerified: action.payload,
- allValid: action.payload && state.isChecked,
- };
- case LOGIN_ACTION.SET_CHECKED:
- return {
- ...state,
- isChecked: action.payload,
- allValid: action.payload && state.isVerified,
- };
- case LOGIN_ACTION.SET_MARKETING_CHECKED:
- return { ...state, marketingChecked: action.payload };
- case LOGIN_ACTION.SET_NAME:
- return { ...state, name: action.payload };
- case LOGIN_ACTION.SET_PHONE_NUMBER:
- return { ...state, phoneNumber: action.payload };
- default:
- return state;
- }
-};
diff --git a/service/src/store/reducer/modalReducer.tsx b/service/src/store/reducer/modalReducer.tsx
deleted file mode 100644
index e69de29..0000000
diff --git a/service/src/store/types/loginModalTypes.ts b/service/src/store/types/loginModalTypes.ts
deleted file mode 100644
index e007c6f..0000000
--- a/service/src/store/types/loginModalTypes.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-export interface LoginModalState {
- name: string;
- phoneNumber: string;
- verificationCode: string;
- isVerified: boolean;
- isChecked: boolean;
- marketingChecked: boolean;
- allValid: boolean;
-}
-
-export type LoginAction =
- | { type: LOGIN_ACTION.SET_VERIFIED; payload: boolean }
- | { type: LOGIN_ACTION.SET_CHECKED; payload: boolean }
- | { type: LOGIN_ACTION.SET_MARKETING_CHECKED; payload: boolean }
- | { type: LOGIN_ACTION.SET_NAME; payload: string }
- | { type: LOGIN_ACTION.SET_PHONE_NUMBER; payload: string }
- | { type: LOGIN_ACTION.SET_VERIFICATION_CODE; payload: string };
-
-export enum LOGIN_ACTION {
- SET_VERIFIED = 'SET_VERIFIED',
- SET_CHECKED = 'SET_CHECKED',
- SET_MARKETING_CHECKED = 'SET_MARKETING_CHECKED',
- SET_NAME = 'SET_NAME',
- SET_PHONE_NUMBER = 'SET_PHONE_NUMBER',
- SET_VERIFICATION_CODE = 'SET_VERIFICATION_CODE',
-}
diff --git a/service/src/types/Authorization/type.ts b/service/src/types/Authorization/type.ts
index 5f91204..6a90dd1 100644
--- a/service/src/types/Authorization/type.ts
+++ b/service/src/types/Authorization/type.ts
@@ -1,3 +1,5 @@
+import { ResponseType } from '../apiType';
+
export interface SendCodeRequestBody {
phoneNumber: string;
}
@@ -14,25 +16,15 @@ export interface LoginRequestBody {
marketingConsent: boolean;
}
-export type CodeResponse = {
- isSuccess: boolean;
- code: string;
- message: string;
+export interface CodeResponse extends ResponseType {
result?: {
timeLimit: number;
};
-};
-
-export interface ConfirmResponse {
- isSuccess: boolean;
- code: string;
- message: string;
}
-export interface LoginResponse {
- isSuccess: boolean;
- code: string;
- message: string;
+export interface ConfirmResponse extends ResponseType {}
+
+export interface LoginResponse extends ResponseType {
result?: {
accessToken: string;
refreshToken: string;
diff --git a/service/src/types/apiType.ts b/service/src/types/apiType.ts
index d065b72..94d596d 100644
--- a/service/src/types/apiType.ts
+++ b/service/src/types/apiType.ts
@@ -1,22 +1,3 @@
-import { HEADERS } from '@/constants/lib/constants';
-
-export interface RequestType {
- url: string;
- method: methodType;
- params?: string;
- queryParams?: Record;
- body?: T;
-}
-export interface UseFetchOptions {
- method: methodType;
- params?: Record;
- queryParams?: Record;
- body?: BodyInit | null;
- headers?: typeof HEADERS;
-}
-
-export type methodType = 'GET' | 'POST' | 'PUT' | 'DELETE';
-
export interface ResponseType {
isSuccess: boolean;
code: string;
diff --git a/service/src/types/comment/type.ts b/service/src/types/comment/type.ts
index f911d2f..2433177 100644
--- a/service/src/types/comment/type.ts
+++ b/service/src/types/comment/type.ts
@@ -1,13 +1,12 @@
+import { ResponseType } from '../apiType';
+
export interface CommentsType {
commentType: number;
isMine: boolean;
nickName: string;
}
-export interface CommentsResponseType {
- isSuccess: boolean;
- code: string;
- message: string;
+export interface CommentsResponseType extends ResponseType {
result: {
nextCursor: number;
totalComments: number;
diff --git a/service/src/utils/checkDrawPeriod.ts b/service/src/utils/checkDrawPeriod.ts
new file mode 100644
index 0000000..ee3d3fd
--- /dev/null
+++ b/service/src/utils/checkDrawPeriod.ts
@@ -0,0 +1,25 @@
+export const checkDrawPeriod = (
+ startDate: string,
+ endDate: string,
+ drawStartTime: string,
+ drawEndTime: string,
+ today: Date
+): boolean => {
+ const startPeriod = new Date(startDate);
+ const endPeriod = new Date(endDate);
+ const drawStartDateTime = new Date(today);
+
+ const [startHour, startMinute] = drawStartTime.split(':').map(Number);
+ drawStartDateTime.setHours(startHour, startMinute, 0, 0);
+
+ const drawEndDateTime = new Date(today);
+ const [endHour, endMinute] = drawEndTime.split(':').map(Number);
+ drawEndDateTime.setHours(endHour, endMinute, 0, 0);
+
+ return (
+ today >= startPeriod &&
+ today <= endPeriod &&
+ today >= drawStartDateTime &&
+ today <= drawEndDateTime
+ );
+};