diff --git a/src/App.jsx b/src/App.jsx index b0b1d87..25995b3 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -6,10 +6,6 @@ import AddExercise from "./pages/AddExercise.jsx"; import { ModalProvider } from "./librarys/context.jsx"; import PlayerPage from "./pages/PlayerPage.jsx"; import store from "./redux/store.js"; - -import { useEffect } from "react"; -import { loadToken } from "./librarys/login-api.js"; -import { login } from "./redux/userSlice.js"; import ProtectedRoute from "./components/ProtectedRoute.jsx"; import "./App.scss"; @@ -21,7 +17,7 @@ const Container = styled.div` const routerList = [ { path: "/", element: }, { path: "/program/:id/play", element: , role: 1 }, - { path: "/register", element: , role: 2 }, + { path: "/register", element: } ]; routerList.forEach((item) => { @@ -35,19 +31,6 @@ routerList.forEach((item) => { }); function App() { - const dispatch = store.dispatch; - - // Login logic - useEffect(() => { - loadToken().then((result) => { - if (!result) { - return; - } else { - dispatch(login(result)); - } - }); - }); - return ( diff --git a/src/App.scss b/src/App.scss index 5dd5204..3743742 100644 --- a/src/App.scss +++ b/src/App.scss @@ -1,6 +1,6 @@ #root { width: 100%; - height: 100vh; + height: 200vh; margin: 0 auto; background-color: #1E1E1E; font-family: "SUIT Variable", sans-serif; diff --git a/src/assets/icons/Page-left.png b/src/assets/icons/Page-left.png new file mode 100644 index 0000000..e8fc80f Binary files /dev/null and b/src/assets/icons/Page-left.png differ diff --git a/src/components/AddHeader.jsx b/src/components/AddHeader.jsx new file mode 100644 index 0000000..ed65811 --- /dev/null +++ b/src/components/AddHeader.jsx @@ -0,0 +1,41 @@ +import styled from "styled-components"; +import LogoImage from "../assets/icons/LOGO.png"; +import BackButton from "./BackButton"; + +const HeaderContainer = styled.div` + display: flex; + align-items: center; + margin-bottom:-10px; +`; + +const Logo = styled.img` + margin-top: 20px; + margin-left: 420px; + margin-right: 10px; +`; + +const Title = styled.h2` + font-size: 36px; + font-weight: bold; + color: #F2F2F2; + margin-left: 10px; + margin-top: 20px; +`; + +const ButtonContainer = styled.div` + margin-left: 1125px; +`; + +const AddHeader = () => { + return ( + + + 영상 등록하기 + + + + + ); +}; + +export default AddHeader; diff --git a/src/components/BackButton.jsx b/src/components/BackButton.jsx new file mode 100644 index 0000000..2abbfd6 --- /dev/null +++ b/src/components/BackButton.jsx @@ -0,0 +1,38 @@ +import styled from 'styled-components'; +import BackIcon from '../assets/icons/Page-left.png'; + +const BackButtonContainer = styled.button` + width: 210px; + height: 40px; + background-color: #FFFFFF; + border: 1px solid #BBBBBB; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + padding: 0 15px; + margin-top:40px; + margin-bottom: 10px; + margin-left: -580px; +`; + +const BackImage = styled.img` + margin-right: 10px; +`; + +const BackText = styled.span` + font-size: 14px; + color: #000000; + font-weight: bold; +`; + +const BackButton = () => { + return ( + + + 메인페이지로 돌아가기 + + ); +} + +export default BackButton; diff --git a/src/components/ExerciseCard.jsx b/src/components/ExerciseCard.jsx index c7480c1..aff80a7 100644 --- a/src/components/ExerciseCard.jsx +++ b/src/components/ExerciseCard.jsx @@ -1,6 +1,7 @@ -import { Link } from 'react-router-dom'; import styled from 'styled-components'; import PropTypes from 'prop-types'; +import {useState} from 'react'; +import ExerciseModal from './ExerciseModal'; const CourseCardContainer = styled.div` width: 360px; @@ -59,16 +60,26 @@ const Hash = styled.span` color: #727272; `; -const ExerciseCard = ({ id, image, title, tags }) => { - return ( - - - - - 00:00 - - +const ExerciseCard = ({ image, title, tags, description }) => { + const [isModalOpen, setIsModalOpen] = useState(false); + + const handleCardClick = (e) => { + e.stopPropagation(); + setIsModalOpen(true); + } + const handleCloseModal = () => { + setIsModalOpen(false); + }; + + return ( + <> + + + + 00:00 + + {title}
{tags && tags.map((tag, index) => ( @@ -79,14 +90,23 @@ const ExerciseCard = ({ id, image, title, tags }) => { ))}
- + {isModalOpen && ( + + )} + ); }; ExerciseCard.propTypes = { - id: PropTypes.number.isRequired, image: PropTypes.string, title: PropTypes.string.isRequired, + description: PropTypes.string.isRequired, tags: PropTypes.arrayOf(PropTypes.string) }; diff --git a/src/components/ExerciseList.jsx b/src/components/ExerciseList.jsx index 29cfd1b..49672e9 100644 --- a/src/components/ExerciseList.jsx +++ b/src/components/ExerciseList.jsx @@ -7,7 +7,7 @@ const Container = styled.div` width: 1200px; display: flex; flex-wrap: wrap; - gap: 20px; + gap: 40px; margin-top: 20px; margin-left:10px; `; diff --git a/src/components/ExerciseModal.jsx b/src/components/ExerciseModal.jsx new file mode 100644 index 0000000..192542c --- /dev/null +++ b/src/components/ExerciseModal.jsx @@ -0,0 +1,123 @@ +import styled from 'styled-components'; + +const ModalOverlay = styled.div` + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.75); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +`; + +const ModalContainer = styled.div` + width: 700px; + height: 650px; + background-color: #242424; + border-radius: 10px; + padding: 20px; + box-sizing: border-box; + position: relative; +`; + +const VideoPlaceholder = styled.div` + width: 630px; + height: 400px; + background-color: #5F5F5F; + margin-bottom: 10px; +`; + +const Title = styled.h2` + font-size: 28px; + font-weight: bold; + color: #F2F2F2; + margin-bottom: 10px; +`; + +const Description = styled.p` + font-size: 16px; + color: #F2F2F2; + font-weight: 200; + margin-bottom: 20px; +`; + +const TagTitle = styled.span` + font-size: 20px; + color: #5F5F5F; +`; + +const TagButton = styled.span` + margin-top: 80px; + height: 28px; + padding: 0 10px; + background-color: #242424; + border-radius: 50px; + border: 1px solid #444444; + font-size: 16px; + color: #F2F2F2; + margin-right: 5px; + display: inline-flex; + align-items: center; + margin-left: 10px; +`; + +const ButtonGroup = styled.div` + position: absolute; + right: 20px; + bottom: 20px; +`; + +const EnrollButton = styled.button` + width: 100px; + height: 40px; + background-color: #6968CC; + color: #F1F1F1; + font-size: 16px; + border-radius: 10px; + border: none; + margin-right: 10px; +`; + +const CloseButton = styled.button` + width: 100px; + height: 40px; + background-color: #F2F2F2; + color: #242424; + font-size: 16px; + border-radius: 10px; + border: none; +`; + +const Hash = styled.span` + color: #727272; +`; + +const ExerciseModal = ({ video, title, description, tags, onClose }) => { + return ( + + + + {title} + {description} +
+ # 관련 태그 + {tags.map((tag, index) => ( + + # + {tag} + + ))} +
+ + 수강하기 + 닫기 + +
+
+ ); +}; + +export default ExerciseModal ; \ No newline at end of file diff --git a/src/components/VideoUploader.jsx b/src/components/VideoUploader.jsx new file mode 100644 index 0000000..75617f8 --- /dev/null +++ b/src/components/VideoUploader.jsx @@ -0,0 +1,77 @@ +import styled from 'styled-components'; +import PropTypes from 'prop-types'; +import { useState } from 'react'; + +const VideoUploadContainer = styled.div` + width: 540px; + height: 500px; + background-color: #242424; + border: 1px solid #444444; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + position: relative; + overflow: hidden; + + &:hover { + opacity: 0.8; + } +`; + +const UploadText = styled.span` + color: #484848; + font-size: 36px; + font-weight: bold; + text-align: center; +`; + +const HiddenInput = styled.input` + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0; + cursor: pointer; +`; + +const VideoPreview = styled.video` + width: 100%; + height: 100%; + border-radius: 10px; +`; + +const VideoUploader = ({ onUpload }) => { + const [videoPreview, setVideoPreview] = useState(null); + + const handleFileChange = (event) => { + const file = event.target.files[0]; + if (file) { + // 파일 객체에서 URL을 생성하여 미리보기에 사용 + const videoURL = URL.createObjectURL(file); + setVideoPreview(videoURL); + onUpload && onUpload(file); + } + }; + + return ( + + + {videoPreview ? ( + + ) : ( + + 여기를 클릭해서
동영상 업로드 +
+ )} +
+ ); +}; + +VideoUploader.propTypes = { + onUpload: PropTypes.func, +}; + +export default VideoUploader; \ No newline at end of file diff --git a/src/pages/AddExercise.jsx b/src/pages/AddExercise.jsx index 8dad2b3..f246c52 100644 --- a/src/pages/AddExercise.jsx +++ b/src/pages/AddExercise.jsx @@ -1,13 +1,222 @@ -import Header from "../components/Header"; +import AddHeader from "../components/AddHeader"; +import VideoUploader from "../components/VideoUploader"; +import styled from "styled-components"; +import { useState } from "react"; +import UploadButton from "../components/UploadButton"; + +const PageContainer = styled.div` + display: flex; + flex-direction: column; + height: 100vh; +`; + +const CenteredContainer = styled.div` + display: flex; + flex-direction: row; + justify-content: center; + align-items: start; + flex: 1; + margin-top: 50px; + width: 1200px; + padding: 0 20px; +`; + +const TitleA = styled.h1` + font-size: 24px; + color: #ffffff; + font-weight: bold; + margin-bottom: 10px; + margin-left: -300px; +`; + +const TextContainer = styled.div` + margin-left: 20px; +`; + +const StyledInput = styled.input` + width: 650px; + height: 50px; + margin-top: 10px; + background-color: #242424; + border-radius: 10px; + border: 1px solid #444444; + padding: 10px; + margin-bottom: 20px; +`; + +const StyledTextarea = styled.textarea` + width: 650px; + height: 120px; + margin-top: 10px; + background-color: #242424; + border-radius: 10px; + border: 1px solid #444444; + padding: 10px; +`; + +const TitleText = styled.h2` + font-weight: bold; + margin-bottom: 10px; + font-size: 24px; + color: #ffffff; + margin-bottom: 10px; +`; + +const TitleC = styled.h2` + font-weight: bold; + font-size: 24px; + color: #ffffff; + margin-bottom: 10px; + margin-top:10px; + margin-left:-470px; +`; + +const LeftContainer = styled.div` + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + margin-left: 850px; +`; + +const RightContainer = styled.div` + flex: 1; + display: flex; + flex-direction: column; + align-items: center; +`; + +const FilterSection = styled.div` + display: flex; + align-items: center; + width: 600px; + margin-right: 10px; + margin-top: 20px; + margin-left: -10px; +`; + +const Title = styled.span` + font-size: 20px; + font-weight: bold; + color: #5f5f5f; + width: 120px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-right: 10px; +`; + +const Divider = styled.div` + width: 2px; + height: 20px; + background-color: #5f5f5f; + margin-right: 10px; +`; + +const Button = styled.button` + width: 100px; + height: 36px; + background-color: ${(props) => (props.selected ? "#6968CC" : "#1E1E1E")}; + color: #f2f2f2; + border-radius: 10px; + font-size: 16px; + border: none; + margin-right: 5px; + + &:focus { + outline: none; + } +`; + +const UploadButtonContainer = styled.div` + margin-top:20px; + margin-left: 520px; +`; const AddExercise = () => { - + const [title, setTitle] = useState(""); + const [description, setDesctiption] = useState(""); + const [selectedCategory, setSelectedCategory] = useState("전체"); + const [selectedPose, setSelectedPose] = useState("전체"); + + const handleTitleChange = (e) => { + setTitle(e.target.value); + }; + + const handleDescriptionChange = (e) => { + setDesctiption(e.target.value); + }; + return (
-
+ + + + + 동영상 및 스켈레톤 데이터 + + + + + 운동 제목 + + 운동 설명 + + + 카테고리 및 태그 +
+ + 카테고리 + + {["전체", "팔", "어깨", "무릎", "허벅지"].map((category) => ( + + ))} + + + + 자세 + + {["전체", "선 자세", "앉은 자세", "누운 자세"].map((pose) => ( + + ))} + +
+ + + +
+
+
); }; - export default AddExercise;