diff --git a/client/src/atoms/buttons/ListToggle.js b/client/src/atoms/buttons/ListToggle.js index a8a5a0e1..4402b782 100644 --- a/client/src/atoms/buttons/ListToggle.js +++ b/client/src/atoms/buttons/ListToggle.js @@ -3,11 +3,16 @@ import { useSelector } from "react-redux"; import { styled } from "styled-components"; import tokens from "../../styles/tokens.json"; import axios from "axios"; +import { AlertModal } from "../modal/Modal"; const ListToggle = ({ OnOff, videoId }) => { const isDark = useSelector((state) => state.uiSetting.isDark); const token = useSelector((state) => state.loginInfo.accessToken); const [isOnOff, setOnOff] = useState(OnOff); + const [isModalOpen, setIsModalOpen] = useState({ + isModalOpen: false, + content: "", + }); const patchVideoStatus = () => { return axios @@ -17,27 +22,53 @@ const ListToggle = ({ OnOff, videoId }) => { .then((res) => { setOnOff(!res.data.data); if (res.data.data) { - alert("강의를 활성화 했습니다."); + setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: "강의를 활성화 했습니다.", + }); } else { - alert("강의를 비활성화 했습니다."); + setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: "강의를 비활성화 했습니다.", + }); } }) .catch((err) => { - console.log(err); - alert("강의 비활성화를 실패했습니다."); + setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: "강의 비활성화를 실패했습니다.", + }); }); }; return ( - { - patchVideoStatus(); - }} - > - - - - + <> + { + patchVideoStatus(); + }} + > + + + + + + setIsModalOpen({ + ...isModalOpen, + isModalOpen: false, + }) + } + /> + ); }; @@ -56,10 +87,13 @@ export const ToggleContainer = styled.div` top: 0; left: 0; // "rgba(24,35,51,0.7)" background-color: ${(props) => - props.isDark && !props.isOnOff ? globalTokens.LightNavy.value - : !props.isDark && !props.isOnOff ? globalTokens.Negative.value - : props.isDark && props.isOnOff ? 'rgba(255,255,255,0.1)' - : 'rgba(0,0,0,0.15)'}; + props.isDark && !props.isOnOff + ? globalTokens.LightNavy.value + : !props.isDark && !props.isOnOff + ? globalTokens.Negative.value + : props.isDark && props.isOnOff + ? "rgba(255,255,255,0.1)" + : "rgba(0,0,0,0.15)"}; border-radius: ${globalTokens.BigRadius.value}px; width: 48px; height: 24px; diff --git a/client/src/atoms/modal/Modal.js b/client/src/atoms/modal/Modal.js index 66a59cb1..198ef0e5 100644 --- a/client/src/atoms/modal/Modal.js +++ b/client/src/atoms/modal/Modal.js @@ -1,124 +1,153 @@ -import React from 'react'; -import { styled } from 'styled-components'; -import tokens from '../../styles/tokens.json'; -import { useDispatch, useSelector } from 'react-redux'; -import { BodyTextTypo } from '../typographys/Typographys' -import { NegativeTextButton, PositiveTextButton } from '../buttons/Buttons'; +import { useSelector } from "react-redux"; +import { styled } from "styled-components"; +import tokens from "../../styles/tokens.json"; +import { BodyTextTypo } from "../typographys/Typographys"; +import { NegativeTextButton, PositiveTextButton } from "../buttons/Buttons"; -const globalTokens = tokens.global; - -export const ModalBackdrop = styled.div` - width: 100vw; - height: 100vh; - position: fixed; - z-index: 1001; - top: 0; - left: 0; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - background-color: ${(props)=>props.isDark?'rgba(255, 255, 255, 0.25)':'rgba(0, 0, 0, 0.25)'}; - opacity: ${(props) => (props.isModalOpen ? `1` : `0`)}; - visibility: ${(props) => (props.isModalOpen ? "visible" : "hidden")}; -` -export const ModalContainer = styled.div` - width: 320px; - height: 150px; - padding: ${globalTokens.Spacing20.value}px ${globalTokens.Spacing8.value}px; - background-color: ${(props)=>props.isDark?globalTokens.Black.value:globalTokens.White.value}; - border-radius: ${globalTokens.BigRadius.value}px; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -` -export const ModalContent = styled(BodyTextTypo)` - flex-grow: 1; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -` -export const ModalButtonContainer = styled.div` - display: flex; - flex-direction: row; - justify-content: space-around; - align-items: start; -` -export const ModalPositiveButton = styled(PositiveTextButton)` - margin: ${globalTokens.Spacing4.value}px; - padding: 0 ${globalTokens.Spacing24.value}px; -` -export const ModalNegativeButton = styled(NegativeTextButton)` - margin: ${globalTokens.Spacing4.value}px; - padding: 0 ${globalTokens.Spacing24.value}px; -` //예, 아니오를 선택하는 모달 export const ConfirmModal = ({ - isModalOpen, - setIsModalOpen, - isBackdropClickClose, - content, - negativeButtonTitle, - positiveButtonTitle, - handleNegativeButtonClick, - handlePositiveButtonClick, + isModalOpen, + setIsModalOpen, + isBackdropClickClose, + content, + negativeButtonTitle, + positiveButtonTitle, + handleNegativeButtonClick, + handlePositiveButtonClick, }) => { - const isDark = useSelector(state=>state.uiSetting.isDark); + const isDark = useSelector((state) => state.uiSetting.isDark); - return ( - { + isBackdropClickClose && setIsModalOpen(false); + }} + > + { + e.stopPropagation(); + }} + > + {content} + + { + handlePositiveButtonClick(); + }} + > + {positiveButtonTitle} + + { isBackdropClickClose && setIsModalOpen(false) }}> - {e.stopPropagation();}}> - {content} - - { - handleNegativeButtonClick(); - }}> - {negativeButtonTitle} - - { - handlePositiveButtonClick(); - }}> - {positiveButtonTitle} - - - - - ); + onClick={(e) => { + handleNegativeButtonClick(); + }} + > + {negativeButtonTitle} + + + + + ); }; + //확인 버튼만 있는 모달 export const AlertModal = ({ - isModalOpen, - setIsModalOpen, - isBackdropClickClose, - content, - buttonTitle, - handleButtonClick, + isModalOpen, + setIsModalOpen, + isBackdropClickClose, + content, + buttonTitle, + handleButtonClick, }) => { - const isDark = useSelector(state=>state.uiSetting.isDark); + const isDark = useSelector((state) => state.uiSetting.isDark); - return ( - { + isBackdropClickClose && setIsModalOpen(false); + }} + > + { + e.stopPropagation(); + }} + > + {content} + + { isBackdropClickClose && setIsModalOpen(false) }}> - {e.stopPropagation();}}> - {content} - - - {buttonTitle} - - - - - ); -} + onClick={handleButtonClick} + > + {buttonTitle} + + + + + ); +}; + +const globalTokens = tokens.global; + +export const ModalBackdrop = styled.div` + width: 100vw; + height: 100vh; + position: fixed; + z-index: 1001; + top: 0; + left: 0; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background-color: ${(props) => + props.isDark ? "rgba(255, 255, 255, 0.25)" : "rgba(0, 0, 0, 0.25)"}; + opacity: ${(props) => (props.isModalOpen ? `1` : `0`)}; + visibility: ${(props) => (props.isModalOpen ? "visible" : "hidden")}; +`; + +export const ModalContainer = styled.div` + width: 320px; + height: 150px; + padding: ${globalTokens.Spacing20.value}px ${globalTokens.Spacing8.value}px; + background-color: ${(props) => + props.isDark ? globalTokens.Black.value : globalTokens.White.value}; + border-radius: ${globalTokens.BigRadius.value}px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +`; + +export const ModalContent = styled(BodyTextTypo)` + flex-grow: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +`; + +export const ModalButtonContainer = styled.div` + display: flex; + flex-direction: row; + justify-content: space-around; + align-items: start; +`; + +export const ModalPositiveButton = styled(PositiveTextButton)` + margin: ${globalTokens.Spacing4.value}px; + padding: 0 ${globalTokens.Spacing24.value}px; +`; + +export const ModalNegativeButton = styled(NegativeTextButton)` + margin: ${globalTokens.Spacing4.value}px; + padding: 0 ${globalTokens.Spacing24.value}px; +`; diff --git a/client/src/components/CartPage/CartPayInfo.js b/client/src/components/CartPage/CartPayInfo.js index 7a54b5e3..7fbd0082 100644 --- a/client/src/components/CartPage/CartPayInfo.js +++ b/client/src/components/CartPage/CartPayInfo.js @@ -9,6 +9,7 @@ import { import { RegularInput } from "../../atoms/inputs/Inputs"; import { RegularButton } from "../../atoms/buttons/Buttons"; import PaymentBtn from "../../pages/contents/CartPage/TossPayment"; +import { AlertModal } from "../../atoms/modal/Modal"; const globalTokens = tokens.global; @@ -23,6 +24,9 @@ const CartPayInfo = () => { const myCartInfo = useSelector((state) => state.cartSlice.myCartInfo); const checkedItems = useSelector((state) => state.cartSlice.checkedItem); const [isDiscount, setDiscount] = useState(0); + const [countModal, setCountModal] = useState(false); + const [overPointModal, setOverPointModal] = useState(false); + const [spendPointModal, setSpendPointModal] = useState(false); const getTotal = () => { const cartsArr = cartsData.map((el) => el.videoId); @@ -52,17 +56,17 @@ const CartPayInfo = () => { const regExp = /[a-z|ㄱ-ㅎ|ㅏ-ㅣ|가-힣]/g; if (regExp.test(reward)) { setDiscount(0); - return alert("숫자만 입력할 수 있습니다."); + return setCountModal(true) } if (totalPrice < parseInt(reward)) { setDiscount(totalPrice); - return alert("선택하신 상품 금액을 넘어서 포인트를 사용할 수 없습니다."); + return setOverPointModal(true) } if (reward > myCartInfo.reward) { - alert(`현재 사용할 수 있는 포인트는 ${myCartInfo.reward}포인트 입니다.`); setDiscount(myCartInfo.reward); + return setSpendPointModal(true) } }; @@ -73,48 +77,76 @@ const CartPayInfo = () => { }, [totalPrice]); return ( - - - 포인트 - 보유 : {priceToString(myCartInfo.reward)} - - - handleChangeDiscount(e.target.value)} - onBlur={(e) => handleBlurDiscount(e.target.value)} - /> - { - e.preventDefault(); - handleAllDiscount(myCartInfo.reward); - }} - > - 전액 사용 - - - - - 선택 상품 금액 - {priceToString(totalPrice)}원 - - - 할인 금액 - {priceToString(isDiscount)}원 - - - 총 결제금액 - - {priceToString(totalPrice - isDiscount)}원 - - - - - + <> + + + 포인트 + + 보유 : {priceToString(myCartInfo.reward)} + + + + handleChangeDiscount(e.target.value)} + onBlur={(e) => handleBlurDiscount(e.target.value)} + /> + { + e.preventDefault(); + handleAllDiscount(myCartInfo.reward); + }} + > + 전액 사용 + + + + + 선택 상품 금액 + {priceToString(totalPrice)}원 + + + 할인 금액 + {priceToString(isDiscount)}원 + + + 총 결제금액 + + {priceToString(totalPrice - isDiscount)}원 + + + + + + setCountModal(false)} + /> + setOverPointModal(false)} + /> + setSpendPointModal(false)} + /> + ); }; diff --git a/client/src/components/DetailPage/AddCart.js b/client/src/components/DetailPage/AddCart.js index aa048dd2..c7789a1c 100644 --- a/client/src/components/DetailPage/AddCart.js +++ b/client/src/components/DetailPage/AddCart.js @@ -6,12 +6,14 @@ import { useToken } from "../../hooks/useToken"; import tokens from "../../styles/tokens.json"; import { ReactComponent as Cart } from "../../assets/images/icons/listItem/Cart.svg"; import { RoundButton } from "../../atoms/buttons/Buttons"; +import { AlertModal } from "../../atoms/modal/Modal"; const AddCart = ({ videoId, isInCart, content = "", border = false }) => { const refreshToken = useToken(); const isDark = useSelector((state) => state.uiSetting.isDark); const token = useSelector((state) => state.loginInfo.accessToken); const [isCart, setCart] = useState(isInCart); + const [isModalOpen, setIsModalOpen] = useState(false); const handlePahctCart = () => { return axios @@ -19,14 +21,13 @@ const AddCart = ({ videoId, isInCart, content = "", border = false }) => { headers: { Authorization: token.authorization }, }) .then((res) => { - // console.log(res.data.data); setCart(res.data.data); }) .catch((err) => { if (err.response.data.message === "만료된 토큰입니다.") { refreshToken(); } else if (err.response.data.code === 403) { - alert("로그인 시 이용 가능합니다."); + setIsModalOpen(true) } else { console.log(err); } @@ -42,6 +43,14 @@ const AddCart = ({ videoId, isInCart, content = "", border = false }) => { ) : ( handlePahctCart()} /> )} + setIsModalOpen(false)} + /> ); }; diff --git a/client/src/components/DetailPage/ReviewList.js b/client/src/components/DetailPage/ReviewList.js index ac6231e1..e7d430ac 100644 --- a/client/src/components/DetailPage/ReviewList.js +++ b/client/src/components/DetailPage/ReviewList.js @@ -3,15 +3,22 @@ import { useState } from "react"; import { useSelector } from "react-redux"; import { styled } from "styled-components"; import Stars from "../contentListItems/Stars"; -import tokens from '../../styles/tokens.json'; -import { NegativeTextButton, PositiveTextButton } from "../../atoms/buttons/Buttons"; -import { BodyTextTypo, SmallTextTypo } from "../../atoms/typographys/Typographys"; +import tokens from "../../styles/tokens.json"; +import { + NegativeTextButton, + PositiveTextButton, +} from "../../atoms/buttons/Buttons"; +import { + BodyTextTypo, + SmallTextTypo, +} from "../../atoms/typographys/Typographys"; import { RegularInput } from "../../atoms/inputs/Inputs"; +import { ConfirmModal } from "../../atoms/modal/Modal"; const globalTokens = tokens.global; const ReviewList = ({ el, getReview }) => { - const isDark = useSelector(state=>state.uiSetting.isDark); + const isDark = useSelector((state) => state.uiSetting.isDark); const myId = useSelector((state) => state.loginInfo.myid); const token = useSelector((state) => state.loginInfo.accessToken); const [isEditMode, setEditMode] = useState(false); @@ -19,6 +26,8 @@ const ReviewList = ({ el, getReview }) => { content: el.content, star: 0, }); + const [saveReview, setSaveReview] = useState(false); + const [delReview, setDelReview] = useState(false); const patchReview = (replyId) => { return axios @@ -28,7 +37,6 @@ const ReviewList = ({ el, getReview }) => { .then((res) => { console.log(res); if (res.status === 204) { - // window.location.reload(); getReview(); } }) @@ -43,7 +51,6 @@ const ReviewList = ({ el, getReview }) => { .then((res) => { console.log(res); if (res.status === 204) { - // window.location.reload(); getReview(); } }) @@ -51,65 +58,102 @@ const ReviewList = ({ el, getReview }) => { }; return ( - - {myId === el.member.memberId && ( - <> - {isEditMode ? ( - + + {myId === el.member.memberId && ( + <> + {isEditMode ? ( + { + setSaveReview(true); + // if (window.confirm("댓글을 저장 하시겠습니까?")) { + // setEditMode(false); + // patchReview(el.replyId); + // } + }} + > + 저장 + + ) : ( + { + setEditMode(true); + setEditReply({ ...editReply, star: el.star }); + }} + > + 수정 + + )} + { - if (window.confirm("댓글을 저장 하시겠습니까?")) { - setEditMode(false); - patchReview(el.replyId); - } + setDelReview(true); + // if (window.confirm("댓글을 삭제 하시겠습니까?")) { + // deleteReview(el.replyId); + // } }} > - 저장 - - ) : ( - { - setEditMode(true); - setEditReply({ ...editReply, star: el.star }); - }} - > - 수정 - - )} - + + )} + + + + + {isEditMode ? ( + { - if (window.confirm("댓글을 삭제 하시겠습니까?")) { - deleteReview(el.replyId); - } - }} - > - 삭제 - - - )} - - - - - {isEditMode ? ( - - setEditReply({ ...editReply, content: e.target.value }) - } - /> - ) : ( - {el.content} - )} - - - {el.member.nickname} - {el.createdDate.split("T")[0]} - - + value={editReply.content} + onChange={(e) => + setEditReply({ ...editReply, content: e.target.value }) + } + /> + ) : ( + {el.content} + )} + + + {el.member.nickname} + + {el.createdDate.split("T")[0]} + + + + { + setSaveReview(false); + }} + handlePositiveButtonClick={() => { + setEditMode(false); + patchReview(el.replyId); + setSaveReview(false); + }} + /> + { + setDelReview(false); + }} + handlePositiveButtonClick={() => { + deleteReview(el.replyId); + setDelReview(false); + }} + /> + ); }; @@ -131,7 +175,9 @@ export const ReList = styled.li` flex-direction: column; justify-content: space-around; align-items: start; - border: 1px solid ${props=>props.isDark?globalTokens.Gray.value:globalTokens.LightGray.value}; + border: 1px solid + ${(props) => + props.isDark ? globalTokens.Gray.value : globalTokens.LightGray.value}; border-radius: ${globalTokens.RegularRadius.value}px; padding: ${globalTokens.Spacing16.value}px ${globalTokens.Spacing20.value}px; margin-top: 15px; @@ -159,7 +205,8 @@ export const ReviewEdit = styled(RegularInput)` margin: 5px 0px; padding: 5px 10px; border: none; - background-color: ${props=>props.isDark?'rgba(255,255,255,0.15)':globalTokens.White.value}; + background-color: ${(props) => + props.isDark ? "rgba(255,255,255,0.15)" : globalTokens.White.value}; &:focus { outline: none; } @@ -168,7 +215,7 @@ export const ReviewContent = styled(BodyTextTypo)` width: 100%; flex-wrap: wrap; margin: 5px 0px; - padding: 5px 0px + padding: 5px 0px; `; export const ReviewInfo = styled(SmallTextTypo)` @@ -176,20 +223,17 @@ export const ReviewInfo = styled(SmallTextTypo)` flex-direction: row; justify-content: start; align-items: center; - color: ${ props=>props.isDark ? - globalTokens.LightGray.value - : globalTokens.Gray.value }; + color: ${(props) => + props.isDark ? globalTokens.LightGray.value : globalTokens.Gray.value}; `; export const ReviewName = styled(SmallTextTypo)` margin-right: 10px; - color: ${ props=>props.isDark ? - globalTokens.LightGray.value - : globalTokens.Gray.value }; + color: ${(props) => + props.isDark ? globalTokens.LightGray.value : globalTokens.Gray.value}; `; export const ReviewDate = styled(SmallTextTypo)` - color: ${ props=>props.isDark ? - globalTokens.LightGray.value - : globalTokens.Gray.value }; + color: ${(props) => + props.isDark ? globalTokens.LightGray.value : globalTokens.Gray.value}; `; diff --git a/client/src/components/DetailPage/VideoPlayer.js b/client/src/components/DetailPage/VideoPlayer.js index 0a40db78..85655711 100644 --- a/client/src/components/DetailPage/VideoPlayer.js +++ b/client/src/components/DetailPage/VideoPlayer.js @@ -1,12 +1,12 @@ import axios from "axios"; import { useEffect, useMemo, useRef, useState } from "react"; +import screenfull from "screenfull"; import ReactPlayer from "react-player"; import styled from "styled-components"; import { ReactComponent as Play } from "../../assets/images/icons/Play.svg"; import { ReactComponent as Pause } from "../../assets/images/icons/Pause.svg"; import { ReactComponent as Volume } from "../../assets/images/icons/Volume.svg"; import { ReactComponent as FullScreen } from "../../assets/images/icons/FullScreen.svg"; -import screenfull from "screenfull"; const VideoPlayer = ({ videoId, diff --git a/client/src/components/UploadPage/CourseUpload.js b/client/src/components/UploadPage/CourseUpload.js index 4b5fab0c..20ea8236 100644 --- a/client/src/components/UploadPage/CourseUpload.js +++ b/client/src/components/UploadPage/CourseUpload.js @@ -17,6 +17,7 @@ import { useToken } from "../../hooks/useToken"; import Loading from "../../atoms/loading/Loading"; import tokens from "../../styles/tokens.json"; import { BigButton } from "../../atoms/buttons/Buttons"; +import { AlertModal, ConfirmModal } from "../../atoms/modal/Modal"; const globalToken = tokens.global; @@ -49,6 +50,12 @@ const CourseUpload = ({ isTags }) => { const tagsData = isTags.map((el) => el.categoryName); // 실제 tag data 리스트 const tagsDataLower = tagsData.map((el) => el.toLowerCase()); // tagsData 대소문자 판별 const [tagOpen, setTagOpen] = useState(false); // tag 드롭다운 열고 닫기 + const [isModalOpen, setIsModalOpen] = useState({ + isModalOpen: false, + content: "", + }); + const [alertUpload, setAlertUpload] = useState(false); + const [confirmUpload, setConfirmUpload] = useState(false); const handleSaveFile = (e) => { const file = e.target.files[0]; @@ -74,27 +81,51 @@ const CourseUpload = ({ isTags }) => { const regExp = /[a-z|ㄱ-ㅎ|ㅏ-ㅣ|가-힣]/g; if (regExp.test(price)) { setUploadDetail({ ...uploadDetail, price: 0 }); - alert("숫자만 입력해주세요."); + setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: "숫자만 입력해주세요.", + }); } }; const handleVideoPost = () => { if (!uploadDetail.price) { - return alert("가격을 설정해 주세요."); + return setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: "가격을 설정해 주세요.", + }); } if (!uploadDetail.categories.length) { - return alert("1개 이상의 카테고리를 설정해 주세요."); + return setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: "1개 이상의 카테고리를 설정해 주세요.", + }); } if (!imgFile) { - return alert("썸네일 이미지 파일을 올려주세요"); + return setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: "썸네일 이미지 파일을 올려주세요", + }); } if (!videoFile) { - return alert("동영상 파일을 올려주세요"); + return setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: "동영상 파일을 올려주세요", + }); } if (imgRef.current.files[0].name.split(".")[0] !== uploadDetail.videoName) { - return alert("동일한 이름의 이미지와 동영상 파일을 업로드 해주세요."); + return setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: "동일한 이름의 이미지와 동영상 파일을 업로드 해주세요.", + }); } if (uploadVideo.fileName && uploadVideo.imageType) { @@ -113,7 +144,11 @@ const CourseUpload = ({ isTags }) => { .catch((err) => { console.log(err); if (err.response.data.code === 409) { - alert(`${err.response.data.message}`); + return setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: `${err.response.data.message}`, + }); } else if (err.response.data.code === 401) { refreshToken(() => handleVideoPost()); } else { @@ -142,7 +177,6 @@ const CourseUpload = ({ isTags }) => { .catch((err) => { if (err.response.status === 503) { console.log(err); - // handleImgUpload(); } }); } @@ -164,12 +198,13 @@ const CourseUpload = ({ isTags }) => { .catch((err) => { if (err.response.status === 503) { console.log(err); - // handleVideoUpload(); } }); } }; + const [isLocation, setLocation] = useState(""); + const handleDetailPost = () => { if (!isComplete) { return axios @@ -179,12 +214,8 @@ const CourseUpload = ({ isTags }) => { .then((res) => { setLoading(false); setComplete(true); - alert("성공적으로 강의가 등록 되었습니다."); - if (window.confirm("강의 문제를 업로드 하시겠습니까?")) { - navigate(`${res.headers.location}/problems/upload`); - } else { - navigate("/lecture"); - } + setAlertUpload(true); + setLocation(`${res.headers.location}/problems/upload`); }) .catch((err) => { console.log(err); @@ -203,15 +234,21 @@ const CourseUpload = ({ isTags }) => { uploadDetail.categories.includes(e.target.value) || tagListLower.includes(e.target.value.toLowerCase()) ) { - alert("이미 존재하는 카테고리입니다."); - return; + return setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: "이미 존재하는 카테고리입니다.", + }); } if ( !tagsData.includes(e.target.value) && !tagsDataLower.includes(e.target.value.toLowerCase()) ) { - alert("존재하지 않는 카테고리 입니다."); - return; + return setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: "존재하지 않는 카테고리 입니다.", + }); } if (e.target.value && !uploadDetail.categories.includes(e.target.value)) { setUploadDetail({ @@ -223,11 +260,12 @@ const CourseUpload = ({ isTags }) => { } } else if (e.key === "Backspace" && !e.target.value) { if (uploadDetail.categories.length) { - setUploadDetail( - uploadDetail.categories.filter( + setUploadDetail({ + ...uploadDetail, + categories: uploadDetail.categories.filter( (el, idx) => idx !== uploadDetail.categories.length - 1 - ) - ); + ), + }); return; } } @@ -238,8 +276,11 @@ const CourseUpload = ({ isTags }) => { uploadDetail.categories.includes(el) || tagListLower.includes(el.toLowerCase()) ) { - alert("이미 존재하는 카테고리입니다."); - return; + return setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: "이미 존재하는 카테고리입니다.", + }); } if (!uploadDetail.categories.includes(el)) { setUploadDetail({ @@ -257,147 +298,192 @@ const CourseUpload = ({ isTags }) => { }; return ( - - 강의 등록하기 - 강의 정보를 입력합니다. - - - 강의명 - - - - 강의 소개 - { - setUploadDetail({ ...uploadDetail, description: e.target.value }); - }} - /> - - - 가격 - { - setUploadDetail({ ...uploadDetail, price: e.target.value }); - }} - onBlur={(e) => handleBlurPrice(e.target.value)} - /> - - - 카테고리 - - - {uploadDetail.categories.map((el, idx) => ( - - {el} - removeTagList(el)} - > - × - - - ))} - - + + 강의 등록하기 + 강의 정보를 입력합니다. + + + 강의명 + + + + 강의 소개 + { + setUploadDetail({ + ...uploadDetail, + description: e.target.value, + }); + }} + /> + + + 가격 + addTagList(e)} - onFocus={() => setTagOpen(true)} - onBlur={() => { - setTimeout(() => { - setTagOpen(false); - }, 100); + placeholder="가격을 설정해 주세요." + value={uploadDetail.price} + onChange={(e) => { + setUploadDetail({ ...uploadDetail, price: e.target.value }); }} + onBlur={(e) => handleBlurPrice(e.target.value)} /> - {tagOpen && ( - - {tagsData.map((el, idx) => ( - { - handleAddTags(el); - setTagOpen(false); - }} - > + + + 카테고리 + + + {uploadDetail.categories.map((el, idx) => ( + {el} - + removeTagList(el)} + > + × + + ))} - - )} - - - - - 썸네일 이미지 - - - - {imgFile ? ( - - ) : ( - 썸네일을 등록해 주세요. + + addTagList(e)} + onFocus={() => setTagOpen(true)} + onBlur={() => { + setTimeout(() => { + setTagOpen(false); + }, 100); + }} + /> + {tagOpen && ( + + {tagsData.map((el, idx) => ( + { + handleAddTags(el); + setTagOpen(false); + }} + > + {el} + + ))} + )} - - - 썸네일 이미지는 png, jpg, jpeg 확장자만 등록이 가능합니다. - - - 권장 이미지 크기 : 291px × 212px - - - - - - 강의 영상 - - - - 강의 영상은 mp4만 등록이 가능합니다. - - - 권장 화면 비율 : 1920 × 1080 - - 최대 영상 크기 : 1GB - - - - 강의 등록 완료 - - - - + + + + + 썸네일 이미지 + + + + {imgFile ? ( + + ) : ( + + 썸네일을 등록해 주세요. + + )} + + + 썸네일 이미지는 png, jpg, jpeg 확장자만 등록이 가능합니다. + + + 권장 이미지 크기 : 291px × 212px + + + + + + 강의 영상 + + + + 강의 영상은 mp4만 등록이 가능합니다. + + + 권장 화면 비율 : 1920 × 1080 + + 최대 영상 크기 : 1GB + + + + 강의 등록 완료 + + + + + + setIsModalOpen({ + ...isModalOpen, + isModalOpen: false, + }) + } + /> + { + setAlertUpload(false); + setConfirmUpload(true); + }} + /> + { + navigate("/lecture"); + }} + handlePositiveButtonClick={() => { + navigate(isLocation); + }} + /> + ); }; diff --git a/client/src/components/UploadPage/Modal/UploadModal.js b/client/src/components/UploadPage/Modal/UploadModal.js index 131cbf55..cd38d152 100644 --- a/client/src/components/UploadPage/Modal/UploadModal.js +++ b/client/src/components/UploadPage/Modal/UploadModal.js @@ -1,11 +1,16 @@ import { styled } from "styled-components"; import SelectToggle from "./SelectToggle"; -import { useSelector } from 'react-redux'; -import tokens from '../../../styles/tokens.json'; -import { BodyTextTypo, Heading5Typo } from '../../../atoms/typographys/Typographys'; -import { RegularInput } from '../../../atoms/inputs/Inputs'; -import { RegularTextArea } from '../../../atoms/inputs/TextAreas'; +import { useSelector } from "react-redux"; +import tokens from "../../../styles/tokens.json"; +import { + BodyTextTypo, + Heading5Typo, +} from "../../../atoms/typographys/Typographys"; +import { RegularInput } from "../../../atoms/inputs/Inputs"; +import { RegularTextArea } from "../../../atoms/inputs/TextAreas"; import { RegularButton } from "../../../atoms/buttons/Buttons"; +import { useState } from "react"; +import { AlertModal } from "../../../atoms/modal/Modal"; const globalTokens = tokens.global; @@ -18,114 +23,147 @@ const UploadModal = ({ selectMode, setSelectMode, }) => { - const isDark = useSelector(state=>state.uiSetting.isDark); + const isDark = useSelector((state) => state.uiSetting.isDark); + const [contentModal, setContentModal] = useState(false); + const [answerModal, setAnswerModal] = useState(false); + const [checkedModal, setCheckedModal] = useState(false); + + const handleSubmit = () => { + const find = isProblem.selections.find((el) => el === ""); + + if (!isProblem.content) { + return setContentModal(true); + } + if (!isProblem.questionAnswer) { + return setAnswerModal(true); + } + if (find === "" && selectMode) { + return setCheckedModal(true); + } + handleCreateProblem(); + setModal(false); + initProblem(); + document.body.style.overflow = "unset"; + }; return ( - - { - e.stopPropagation(); - }}> - - + + { - setModal(false); - initProblem(); - document.body.style.overflow = "unset"; + onClick={(e) => { + e.stopPropagation(); }} > - × - - - 문제 등록하기 - handleChangeContent(e)} - value={isProblem.content} + setSelectMode={setSelectMode} + initProblem={initProblem} /> - - - - {selectMode ? ( - [1, 2, 3, 4].map((el) => ( + { + setModal(false); + initProblem(); + document.body.style.overflow = "unset"; + }} + > + × + + + 문제 등록하기 + handleChangeContent(e)} + value={isProblem.content} + /> + + + + {selectMode ? ( + [1, 2, 3, 4].map((el) => ( + + { + handleChangeContent(e, el); + }} + checked={isProblem.questionAnswer === el} + /> + {el}번 문항 + handleChangeContent(e, el)} + value={isProblem.selections[el - 1]} + /> + + )) + ) : ( - 정답 + { - handleChangeContent(e, el); + handleChangeContent(e, e.target.value); }} - checked={isProblem.questionAnswer === el} - /> - {el}번 문항 - handleChangeContent(e, el)} - value={isProblem.selections[el - 1]} /> - )) - ) : ( + )} - 정답 - 해설 + { - handleChangeContent(e, e.target.value); - }}/> + selectMode={selectMode} + id="ProblemDiscribe" + type="text" + placeholder="해설을 입력해 주세요." + onChange={(e) => handleChangeContent(e)} + value={isProblem.description} + /> - )} - - 해설 - handleChangeContent(e)} - value={isProblem.description} - /> - - - - { - if (!isProblem.content) { - return alert("지문을 입력해 주세요."); - } - if (!isProblem.questionAnswer) { - return alert("정답을 체크해 주세요."); - } - if (!isProblem.selections.length && selectMode) { - return alert("선택지들을 입력해 주세요."); - } - handleCreateProblem(); - setModal(false); - initProblem(); - }} - > - 문제 추가 - - - + + + + 문제 추가 + + + + setContentModal(false)} + /> + setAnswerModal(false)} + /> + setCheckedModal(false)} + /> + ); }; @@ -139,7 +177,8 @@ export const ModalBackground = styled.div` display: flex; justify-content: center; align-items: center; - background-color: ${props=>props.isDark? 'rgba(255,255,255,0.15)' : 'rgba(0,0,0,0.15)'}; + background-color: ${(props) => + props.isDark ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.15)"}; width: 100vw; height: 100vh; `; @@ -155,13 +194,17 @@ export const Close = styled.button` right: 3%; font-size: 18px; font-weight: bold; - color: ${props=>props.isDark?globalTokens.White.value:globalTokens.Black.value} + color: ${(props) => + props.isDark ? globalTokens.White.value : globalTokens.Black.value}; `; export const ProblemModal = styled.div` position: relative; - background-color: ${props=>props.isDark?globalTokens.Black.value:globalTokens.White.value}; - border: 1px solid ${props=>props.isDark?globalTokens.Gray.value:globalTokens.LightGray.value}; + background-color: ${(props) => + props.isDark ? globalTokens.Black.value : globalTokens.White.value}; + border: 1px solid + ${(props) => + props.isDark ? globalTokens.Gray.value : globalTokens.LightGray.value}; border-radius: 8px; width: 100%; max-width: 600px; @@ -226,7 +269,7 @@ export const CheckNumber = styled.input` export const TitleInput = styled(RegularTextArea)` width: 100%; - height: ${props=>props.selectMode?'80px':'120px'}; + height: ${(props) => (props.selectMode ? "80px" : "120px")}; margin-top: 20px; padding: 10px 0px 0px 10px; resize: none; @@ -249,7 +292,7 @@ export const CommentInput = styled(RegularTextArea)` export const DiscribeInput = styled(RegularTextArea)` width: 100%; - height: ${props=>props.selectMode? '100px' : '150px'}; + height: ${(props) => (props.selectMode ? "100px" : "150px")}; margin-left: 15px; padding: 10px 0px 0px 10px; resize: none; diff --git a/client/src/components/UploadPage/ProblemUpload.js b/client/src/components/UploadPage/ProblemUpload.js index 7df04136..9987f3a1 100644 --- a/client/src/components/UploadPage/ProblemUpload.js +++ b/client/src/components/UploadPage/ProblemUpload.js @@ -12,6 +12,7 @@ import plus_circle from "../../assets/images/icons/plus_circle.svg"; import { useToken } from "../../hooks/useToken"; import tokens from "../../styles/tokens.json"; import { RoundButton } from "../../atoms/buttons/Buttons"; +import { AlertModal } from "../../atoms/modal/Modal"; const globalTokens = tokens.global; @@ -31,6 +32,7 @@ const ProblemUpload = () => { const [isProblemList, setProblemList] = useState([]); const [isProblem, setProblem] = useState(initialState); const [selectMode, setSelectMode] = useState(true); // ture(객관식), false(주관식) + const [isModalOpen, setIsModalOpen] = useState(false); const handleChangeContent = (e, answer) => { switch (e.target.id) { @@ -98,9 +100,7 @@ const ProblemUpload = () => { .then((res) => { console.log(res.data); if (res.data.code === 201) { - alert("성공적으로 강의 문제가 등록되었습니다."); - navigate(`/videos/${videoId}/problems`); - setProblemList([]); + setIsModalOpen(true); } }) .catch((err) => { @@ -112,53 +112,70 @@ const ProblemUpload = () => { }; return ( - - 문제 등록하기 - - 수강 후 성취도를 검사할 문제를 등록합니다. - - - - {isProblemList.map((el, idx) => ( - - {/* {idx + 1}번 문제 */} - {el.content} - handleDeleteList(idx + 1)}> - × - - - ))} - - - + + 문제 등록하기 + + 수강 후 성취도를 검사할 문제를 등록합니다. + + + + {isProblemList.map((el, idx) => ( + + {/* {idx + 1}번 문제 */} + {el.content} + handleDeleteList(idx + 1)}> + × + + + ))} + + + { + setModal(!isModal); + document.body.style.overflow = "hidden"; + }} + > + + 문제를 등록해 주세요. + + handleSubmitProblem()} + > + 강의 등록 완료 + + + + {isModal && ( + { - setModal(!isModal); - document.body.style.overflow = "hidden"; - }} - > - - 문제를 등록해 주세요. - - handleSubmitProblem()}> - 강의 등록 완료 - - - - {isModal && ( - - )} - + setModal={setModal} + isProblem={isProblem} + setProblem={setProblem} + handleChangeContent={handleChangeContent} + handleCreateProblem={handleCreateProblem} + initProblem={initProblem} + selectMode={selectMode} + setSelectMode={setSelectMode} + /> + )} + + { + setIsModalOpen(false); + navigate(`/videos/${videoId}/problems`); + setProblemList([]); + }} + /> + ); }; diff --git a/client/src/pages/contents/CartPage/CartPage.js b/client/src/pages/contents/CartPage/CartPage.js index 0bbf9187..87375953 100644 --- a/client/src/pages/contents/CartPage/CartPage.js +++ b/client/src/pages/contents/CartPage/CartPage.js @@ -1,5 +1,5 @@ import axios from "axios"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { styled } from "styled-components"; import { useDispatch, useSelector } from "react-redux"; import CartLeft from "./CartLeft"; @@ -9,8 +9,8 @@ import { useToken } from "../../../hooks/useToken"; import { setCarts } from "../../../redux/createSlice/CartsSlice"; import { PageContainer } from "../../../atoms/layouts/PageContainer"; import { HomeTitle } from "../../../components/contentListItems/ChannelHome"; -import { setIsLoading } from "../../../redux/createSlice/UISettingSlice"; import { useNavigate } from "react-router-dom"; +import { AlertModal, ConfirmModal } from "../../../atoms/modal/Modal"; const globalTokens = tokens.global; @@ -21,6 +21,8 @@ const CartPage = () => { const isDark = useSelector((state) => state.uiSetting.isDark); const token = useSelector((state) => state.loginInfo.accessToken); const checkedItems = useSelector((state) => state.cartSlice.checkedItem); + const [isModalOpen, setIsModalOpen] = useState(false); + const [isConfirmOpen, setIsConfirmOpen] = useState(false); const getCartsData = () => { return axios @@ -29,7 +31,6 @@ const CartPage = () => { }) .then((res) => { dispatch(setCarts(res.data.data)); - console.log(res.data.data); }) .catch((err) => { if (err.response.data?.code === 401) { @@ -49,13 +50,7 @@ const CartPage = () => { } ) .then((res) => { - console.log(res.data); - alert("성공적으로 결제가 완료되었습니다."); - if (window.confirm("구매한 목록 페이지로 가시겠습니까?")) { - navigate(`/purchased`); - } else { - navigate(`/lecture`); - } + setIsModalOpen(true); }) .catch((err) => { console.log(err); @@ -75,22 +70,47 @@ const CartPage = () => { }, [checkedItems]); return ( - - - 수강 바구니 - - - - - - + <> + + + 수강 바구니 + + + + + + + { + setIsModalOpen(false); + setIsConfirmOpen(true); + }} + /> + { + navigate(`/lecture`); + }} + handlePositiveButtonClick={() => { + navigate(`/purchased`); + }} + /> + ); }; export default CartPage; -// const globalTokens = tokens.global; - export const CartContainer = styled.div` width: 100%; max-width: 1170px; diff --git a/client/src/pages/contents/CartPage/TossPayment.js b/client/src/pages/contents/CartPage/TossPayment.js index 2004febc..6e0844c7 100644 --- a/client/src/pages/contents/CartPage/TossPayment.js +++ b/client/src/pages/contents/CartPage/TossPayment.js @@ -1,14 +1,13 @@ import axios from "axios"; import { useState } from "react"; import { styled } from "styled-components"; -import { useDispatch, useSelector } from "react-redux"; +import { useSelector } from "react-redux"; import { loadTossPayments } from "@tosspayments/payment-sdk"; import { BigButton } from "../../../atoms/buttons/Buttons"; -import { setIsLoading } from "../../../redux/createSlice/UISettingSlice"; import Loading from "../../../atoms/loading/Loading"; +import { AlertModal } from "../../../atoms/modal/Modal"; const PaymentBtn = ({ isDiscount }) => { - const dispatch = useDispatch(); const [isLoading, setLoading] = useState(false); const isDark = useSelector((state) => state.uiSetting.isDark); const cartsItems = useSelector((state) => state.cartSlice.data); @@ -19,10 +18,11 @@ const PaymentBtn = ({ isDiscount }) => { const orderName = orderList && orderList.videoName; const orderDetail = checkedItems.length > 1 ? ` 외 ${checkedItems.length - 1}건` : ""; + const [isModalOpen, setIsModalOpen] = useState(false); const handlePostPayment = () => { if (!checkedItems.length) { - return alert("선택된 강의가 없습니다."); + return setIsModalOpen(true); } return axios .post( @@ -79,6 +79,14 @@ const PaymentBtn = ({ isDiscount }) => { 결제하기 + setIsModalOpen(false)} + /> ); }; diff --git a/client/src/pages/contents/DetailPage/DetailReview.js b/client/src/pages/contents/DetailPage/DetailReview.js index 4a1f6674..d5175c5e 100644 --- a/client/src/pages/contents/DetailPage/DetailReview.js +++ b/client/src/pages/contents/DetailPage/DetailReview.js @@ -14,6 +14,7 @@ import { Heading5Typo, } from "../../../atoms/typographys/Typographys"; import { RegularButton, TextButton } from "../../../atoms/buttons/Buttons"; +import { AlertModal } from "../../../atoms/modal/Modal"; const globalTokens = tokens.global; @@ -36,6 +37,10 @@ const DetailReview = () => { const myId = useSelector((state) => state.loginInfo.myid); const videoDatas = useSelector((state) => state.videoInfo.data); const token = useSelector((state) => state.loginInfo.accessToken); + const [isModalOpen, setIsModalOpen] = useState({ + isModalOpen: false, + content: "", + }); const getReview = () => { const queryString = new URLSearchParams(isParams).toString(); @@ -55,13 +60,25 @@ const DetailReview = () => { const postReview = () => { if (myId === videoDatas.channel.memberId) { - return alert("내 강의에는 리뷰를 쓸 수 없습니다."); + return setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: "내 강의에는 리뷰를 쓸 수 없습니다.", + }); } if (!isReply.content) { - return alert("감상평을 입력해주세요."); + return setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: "감상평을 입력해주세요.", + }); } if (!isReply.star) { - return alert("별점을 선택해주세요."); + return setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: "별점을 선택해주세요.", + }); } return axios .post(`https://api.itprometheus.net/videos/${videoId}/replies`, isReply, { @@ -69,18 +86,29 @@ const DetailReview = () => { }) .then((res) => { if (res.status === 201) { - alert("성공적으로 댓글이 등록되었습니다."); + setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: "성공적으로 댓글이 등록되었습니다.", + }); } setReply({ content: "", star: 0 }); - // window.location.reload(); getReview(); }) .catch((err) => { if (err.response.status === 403) { - alert("구매한 강의만 리뷰를 남길 수 있습니다."); + return setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: "구매한 강의만 리뷰를 남길 수 있습니다.", + }); } if (err.response.status === 404) { - alert("수강평은 한 번만 작성할 수 있습니다."); + return setIsModalOpen({ + ...isModalOpen, + isModalOpen: true, + content: "수강평은 한 번만 작성할 수 있습니다.", + }); } }); }; @@ -91,104 +119,123 @@ const DetailReview = () => { useEffect(() => { getReview(); - }, []); // isParams.page, isParams.sort, isParams.star + }, []); useMemo(() => { getReview(); }, [isParams.page, isParams.sort, isParams.star]); return ( - - 수강평 {isReviews.length} - { videoDatas.isPurchased && - - 리뷰 - 별점을 선택해주세요. - - - + + 수강평 {isReviews.length} + {videoDatas.isPurchased && ( + + 리뷰 + 별점을 선택해주세요. + + + handleChangeReply(e)} + /> + { + e.preventDefault(); + postReview(); + }} + > + 등록 + + + + )} + + + handleChangeReply(e)} - /> - { + setActive(1); + setParams({ ...isParams, sort: "created-date" }); + }} + > + 최신순 + + { - e.preventDefault(); - postReview(); + isActive={isActive === 2} + onClick={() => { + setActive(2); + setParams({ ...isParams, sort: "star" }); }} > - 등록 - - - - } - - - { - setActive(1); - setParams({ ...isParams, sort: "created-date" }); - }} - > - 최신순 - - { - setActive(2); - setParams({ ...isParams, sort: "star" }); - }} - > - 별점순 - - { - setActive(3); - }} - > - 별점별 - {isActive === 3 && ( - <> - - {isParams.star} - { - setParams({ ...isParams, sort: "", star: e.target.value }); - }} - /> - - )} - - - {!isReviews.length ? ( - 현재 리뷰가 없습니다. - ) : ( - - {isReviews.map((el, idx) => ( - - ))} - - )} - - + { + setActive(3); + }} + > + 별점별 + {isActive === 3 && ( + <> + + {isParams.star} + { + setParams({ + ...isParams, + sort: "", + star: e.target.value, + }); + }} + /> + + )} + + + {!isReviews.length ? ( + 현재 리뷰가 없습니다. + ) : ( + + {isReviews.map((el, idx) => ( + + ))} + + )} + + + + + setIsModalOpen({ + ...isModalOpen, + isModalOpen: false, + }) + } /> - + ); }; diff --git a/client/src/pages/contents/DetailPage/DetailVideo.js b/client/src/pages/contents/DetailPage/DetailVideo.js index 4b07ac95..6b50d9a1 100644 --- a/client/src/pages/contents/DetailPage/DetailVideo.js +++ b/client/src/pages/contents/DetailPage/DetailVideo.js @@ -21,6 +21,7 @@ import { import profileGray from "../../../assets/images/icons/profile/profileGray.svg"; import AddCart from "../../../components/DetailPage/AddCart"; import VideoPlayer from "../../../components/DetailPage/VideoPlayer"; +import { AlertModal } from "../../../atoms/modal/Modal"; const globalTokens = tokens.global; @@ -36,6 +37,8 @@ const DetailVideo = () => { const [isSub, setSub] = useState(""); const [channelInfo, setChannelInfo] = useState({}); const [isPrevMode, setPrevMode] = useState(false); + const [purchaseModal, setPurchaseModal] = useState(false); + const [alertModal, setAlertModal] = useState(false); useEffect(() => { getChannelInfo(); @@ -69,8 +72,7 @@ const DetailVideo = () => { } ) .then((res) => { - alert("성공적으로 강의가 구매 되었습니다."); - window.location.reload(); + setPurchaseModal(true); }) .catch((err) => { if (err.response.data.message === "만료된 토큰입니다.") { @@ -104,99 +106,43 @@ const DetailVideo = () => { const handleNavProblem = () => { if (!videoDatas.isPurchased && myId !== videoDatas.channel.memberId) { - alert("강의를 먼저 구매해주세요."); + setAlertModal(true); } else { navigate(`/videos/${videoId}/problems`); } }; return ( - - - 강의를 다 들었다면? - - 문제 풀러가기 → - - + <> + + + 강의를 다 들었다면? + + 문제 풀러가기 → + + - {videoDatas.isPurchased || myId === videoDatas.channel.memberId ? ( - - ) : ( - - { - setPrevMode(true); - dispatch(setPrev(true)); - setTimeout(() => { - dispatch(setPrev(false)); - }, 61000); - }} - > - 1분 미리보기 - - { - if (videoDatas.price > 0) { - handleCartNav(); - } else { - handlePurchase(); - } - }} - > - 강의 구매하기 - - - )} - - - {videoDatas.videoName} - {!videoDatas.isPurchased && myId !== videoDatas.channel.memberId && ( - {videoDatas.price ? `${videoDatas.price}원` : "무료"} - )} - - - - - - - - {videoDatas.channel.channelName} - - - 구독자 {channelInfo.subscribers}명 - - - - - {myId === videoDatas.channel.memberId || ( - - )} - - {!videoDatas.isPurchased && myId !== videoDatas.channel.memberId && ( - - {videoDatas.price > 0 && ( - - )} - + { + setPrevMode(true); + dispatch(setPrev(true)); + setTimeout(() => { + dispatch(setPrev(false)); + }, 61000); + }} + > + 1분 미리보기 + + { if (videoDatas.price > 0) { handleCartNav(); @@ -206,11 +152,88 @@ const DetailVideo = () => { }} > 강의 구매하기 - - + + )} - - + + + {videoDatas.videoName} + {!videoDatas.isPurchased && myId !== videoDatas.channel.memberId && ( + {videoDatas.price ? `${videoDatas.price}원` : "무료"} + )} + + + + + + + + {videoDatas.channel.channelName} + + + 구독자 {channelInfo.subscribers}명 + + + + + {myId === videoDatas.channel.memberId || ( + + )} + + {!videoDatas.isPurchased && myId !== videoDatas.channel.memberId && ( + + {videoDatas.price > 0 && ( + + )} + { + if (videoDatas.price > 0) { + handleCartNav(); + } else { + handlePurchase(); + } + }} + > + 강의 구매하기 + + + )} + + + { + setPurchaseModal(false); + window.location.reload(); + }} + /> + setAlertModal(false)} + /> + ); };