diff --git a/src/App.jsx b/src/App.jsx index 6a93646..3a0b532 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -13,6 +13,8 @@ import LoadingModal from './components/Modal/LoadingModal'; import CreateObjectModal from './components/Modal/CreateObjectModal'; import SearchObjectModal from './components/Modal/SearchObjectModal'; import ControllerObjectModal from './components/Modal/ControllerObjectModal'; +import CreateProjectModal from './components/Modal/CreateProjectModal'; +import LoginModal from './components/Modal/LoginModal'; function App() { return ( @@ -25,15 +27,17 @@ function App() { + + - } /> + } /> + } /> } /> } /> } /> - } /> diff --git a/src/apis/Project/ProjectController.js b/src/apis/Project/ProjectController.js new file mode 100644 index 0000000..ca1ff20 --- /dev/null +++ b/src/apis/Project/ProjectController.js @@ -0,0 +1,46 @@ +import basicApi from '..'; + +export const getProjectList = async (memberId) => { + try { + const response = await basicApi.get(`api/project/${memberId}`); + return response.data; + } catch (error) { + console.error(error); + throw error; + } +}; + +export const postFile = async (file) => { + const formData = FormData(); + formData.append('file', file); + try { + const response = await basicApi.post(`file`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + return response.data; + } catch (error) { + console.error(error); + throw error; + } +}; + +export const postProject = async (title, file) => { + try { + const data = { + name: title, + thumbnailUrl: file, + memberId: 8, + }; + const response = await basicApi.post('api/project', data, { + headers: { + 'Content-Type': 'application/json', + }, + }); + return response.data; + } catch (error) { + console.error(error); + throw error; + } +}; diff --git a/src/apis/User/LoginController.js b/src/apis/User/LoginController.js index 36b2427..2e0c547 100644 --- a/src/apis/User/LoginController.js +++ b/src/apis/User/LoginController.js @@ -1,8 +1,19 @@ import basicApi from '../index'; -export const loginController = async () => { +export const loginController = async (email, password) => { try { - const response = await basicApi.post('/posts'); + const response = await basicApi.post( + 'api/login', + { + email, + password, + }, + { + headers: { + 'Content-Type': 'application/json', + }, + }, + ); return response.data; } catch (error) { console.error('Error fetching posts:', error); @@ -10,6 +21,16 @@ export const loginController = async () => { } }; +export const signUp = async () => { + try { + const response = await basicApi.get(`/api/sign-in`); + return response.data; + } catch (error) { + console.error(error); + throw error; + } +}; + export const userData = async () => { try { const response = await basicApi.get('/get'); diff --git a/src/assets/logo.svg b/src/assets/logo.svg new file mode 100644 index 0000000..43e4bf4 --- /dev/null +++ b/src/assets/logo.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/components/LoginPage/LoginFormContainer.jsx b/src/components/LoginPage/LoginFormContainer.jsx new file mode 100644 index 0000000..e48f8dd --- /dev/null +++ b/src/components/LoginPage/LoginFormContainer.jsx @@ -0,0 +1,49 @@ +import styled from 'styled-components'; +import SubmitBtn from './atom/SubmitBtn'; + +function LoginFormContainer({ + email, + password, + setEmail, + setPassword, + LoginHandle, +}) { + return ( + + setEmail(e.target.value)} + required + /> + setPassword(e.target.value)} + required + /> + + + ); +} + +export default LoginFormContainer; + +const Container = styled.form` + width: 308px; + height: 221px; + margin-top: 52px; + display: flex; + flex-direction: column; + gap: 32px; +`; + +const InputBox = styled.input` + width: 308px; + height: 48px; + padding: 0 18px; + border: 1px solid #6b7684; + border-radius: 8px; +`; diff --git a/src/components/LoginPage/SocialLoginBtn.jsx b/src/components/LoginPage/SocialLoginBtn.jsx deleted file mode 100644 index 69580d4..0000000 --- a/src/components/LoginPage/SocialLoginBtn.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import styled from 'styled-components'; -import { ReactComponent as Kakao } from '../../assets/socialLogin/kakao.svg'; -import { ReactComponent as Naver } from '../../assets/socialLogin/naver.svg'; - -function SocialLoginBtn({ type, onClick }) { - return ( - - {type === 'kakao' && ( - - - 카카오로 로그인하기 - - )} - {type === 'naver' && ( - - - 네이버로 로그인하기 - - )} - - ); -} -export default SocialLoginBtn; - -const SocialLoginBtnContainer = styled.div` - width: 22.86458vw; - height: 5vh; - border: 1px solid; - border-radius: 12px; - display: flex; - justify-content: center; - align-items: center; -`; -const TextContainer = styled.div` - display: flex; - justify-content: center; - align-items: center; -`; -const SocialLoginText = styled.span` - margin-left: 0.6vw; - font-size: 1.0416vw; -`; diff --git a/src/components/LoginPage/atom/SubmitBtn.jsx b/src/components/LoginPage/atom/SubmitBtn.jsx new file mode 100644 index 0000000..95c2a1e --- /dev/null +++ b/src/components/LoginPage/atom/SubmitBtn.jsx @@ -0,0 +1,23 @@ +import styled from 'styled-components'; + +function SubmitBtn({ type, onClick }) { + return ( + + 로그인 + + ); +} + +export default SubmitBtn; + +const SubmitButton = styled.button` + width: 308px; + height: 48px; + background-color: #3182f6; + border: none; + border-radius: 8px; + color: white; + &:hover { + background-color: #1b64da; + } +`; diff --git a/src/components/Modal/CreateProjectModal.jsx b/src/components/Modal/CreateProjectModal.jsx new file mode 100644 index 0000000..1549818 --- /dev/null +++ b/src/components/Modal/CreateProjectModal.jsx @@ -0,0 +1,97 @@ +import styled from 'styled-components'; +import { useRecoilState } from 'recoil'; +import { useState } from 'react'; +import CancelBtn from './atom/CancelBtn'; +import { CreateProjectModalState } from '../../store/modalState'; +import { postFile, postProject } from '../../apis/Project/ProjectController'; + +function CreateProjectModal() { + const [modalValue, setModalValue] = useRecoilState(CreateProjectModalState); + const [title, setTitle] = useState(''); + const [file, setFile] = useState(null); + + if (!modalValue) { + return null; + } + + const CancelHandler = () => { + setModalValue(false); + }; + + const handleSummit = async () => { + if (file) { + try { + const fileUrl = await postFile(file); + await postProject(title, fileUrl); + setTitle(''); + setFile(null); + setModalValue(false); + } catch (error) { + console.error(error); + } + } else { + console.warn('No file selected'); + } + }; + + return ( + + + + + + 프로젝트 만들기 + setTitle(e.target.value)} + /> + setFile(e.target.files[0])} // 파일 선택 처리 + /> + + + + ); +} + +export default CreateProjectModal; + +const ModalBackdrop = styled.div` + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 999; +`; + +const ModalBox = styled.div` + position: absolute; + display: flex; + flex-direction: column; + width: 22.5vw; + height: 56.388888888888886vh; + background-color: #ffffff; + border-radius: 12px; + padding: 1.04vw; +`; + +const CancelPostion = styled.div` + position: absolute; + top: 1vw; + right: 1vw; +`; + +const MainTitle = styled.p` + font-size: 1.2vw; +`; diff --git a/src/components/Modal/LoginModal.jsx b/src/components/Modal/LoginModal.jsx new file mode 100644 index 0000000..a24e2ef --- /dev/null +++ b/src/components/Modal/LoginModal.jsx @@ -0,0 +1,101 @@ +import { useRecoilState } from 'recoil'; +import { useState } from 'react'; +import styled from 'styled-components'; +import CancelBtn from './atom/CancelBtn'; +import { LoginModalState } from '../../store/modalState'; +import { loginController } from '../../apis/User/LoginController'; + +function LoginModal() { + const [modalValue, setModalValue] = useRecoilState(LoginModalState); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + if (!modalValue) { + return null; + } + const CancelHandler = () => { + setModalValue(false); + }; + + const LoginHandle = async () => { + try { + await loginController(email, password); + setEmail(''); + setPassword(''); + } catch (error) { + error(error); + } + }; + return ( + + + + + + 로그인 + setEmail(e.target.value)} + /> + setPassword(e.target.value)} + /> + + 생성 + + + + ); +} + +export default LoginModal; + +const ModalBackdrop = styled.div` + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 999; +`; + +const ModalBox = styled.div` + position: absolute; + display: flex; + align-items: center; + flex-direction: column; + width: 18.54vw; + height: 48.3vh; + background-color: #ffffff; + border-radius: 12px; + padding: 1.04vw; +`; + +const CancelPostion = styled.div` + position: absolute; + top: 1vw; + right: 1vw; +`; + +const MainTitle = styled.p` + font-size: 1.2vw; +`; + +const LoginInput = styled.input` + width: 14.166666666666666vw; + height: 4.907407407407407vh; + padding: 0.6vw; +`; + +const LoginBtn = styled.button` + width: 14.16vw; + height: 4.44vh; +`; diff --git a/src/components/myPage/SideMenu.jsx b/src/components/myPage/SideMenu.jsx index cc6405a..3a78dde 100644 --- a/src/components/myPage/SideMenu.jsx +++ b/src/components/myPage/SideMenu.jsx @@ -1,15 +1,18 @@ import styled from 'styled-components'; import { useLocation } from 'react-router-dom'; +import { useSetRecoilState } from 'recoil'; import Logo from './atom/Logo'; import MenuBtn from './atom/MenuBtn'; import SearchProject from './atom/SearchProject'; +import { LoginModalState } from '../../store/modalState'; const types = ['About', 'OverView', 'Comments', 'Projects', 'Starred']; function SideMenu() { const location = useLocation(); + const setLoginModal = useSetRecoilState(LoginModalState); const shouldShow = [ - '/', + '/list', '/Projects', '/About', '/OverView', @@ -34,6 +37,9 @@ function SideMenu() { ))} + ); } diff --git a/src/pages/ItemPage.jsx b/src/pages/ItemPage.jsx index 1d9b9d7..4ed4ed9 100644 --- a/src/pages/ItemPage.jsx +++ b/src/pages/ItemPage.jsx @@ -1,6 +1,11 @@ +import { useEffect } from 'react'; import ProjectCard from '../components/ItemPage/ProjectCard'; +import { getProjectList } from '../apis/Project/ProjectController'; function ItemPage() { + useEffect(() => { + getProjectList(8); + }, []); return (
{ - navigate(-1); + const nav = useNavigate(); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const LoginHandle = () => { + loginController(email, password); + setEmail(''); + setPassword(''); + nav('/list'); }; - const NAVER = process.env.REACT_APP_NAVER; - const KAKAO = process.env.REACT_APP_KAKAO; - - const routeToSocialLogin = (type) => { - const url = type === 'naver' ? NAVER : KAKAO; - window.location.href = url; - }; - return ( - - - - - MoreView - routeToSocialLogin('kakao')} - /> - 또는 - routeToSocialLogin('naver')} + + + - + ); } + export default LoginPage; -const LoginContainer = styled.div` - width: 28.645vw; - height: 63.888vh; - border: 1px solid; - border-radius: 12px; +const Container = styled.div` + width: 100vw; + height: 100vh; display: flex; - align-items: center; flex-direction: column; - margin: 3vw auto; - position: relative; -`; - -const LogoStyle = styled.h1` - font-family: 'Anta', sans-serif; - font-weight: 400; - font-style: normal; - font-size: 3.5vw; - margin: 4.6vw 0 7vw 0; -`; - -const Despite = styled.p` - margin: 1vw 0 1vw 0; - color: #b1acac; -`; - -const BackPosition = styled.div` - position: absolute; - top: 1.5vw; - left: 2vw; + align-items: center; + justify-content: center; `; diff --git a/src/store/modalState.js b/src/store/modalState.js index 30e5f78..83d022f 100644 --- a/src/store/modalState.js +++ b/src/store/modalState.js @@ -19,3 +19,13 @@ export const ControllerModalState = atom({ key: 'ControllerModalState', default: false, }); + +export const CreateProjectModalState = atom({ + key: 'CreateProjectModalState', + default: false, +}); + +export const LoginModalState = atom({ + key: 'LoginModalState', + default: false, +}); diff --git a/src/store/userState.js b/src/store/userState.js new file mode 100644 index 0000000..e69de29