diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..17e15f2 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Current File", + "type": "python", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal" + } + ] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a439258..136f0db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1920,6 +1920,37 @@ } } }, + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "requires": { + "follow-redirects": "1.5.10" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", diff --git a/package.json b/package.json index 0e22c09..817c4b9 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "start": "node server" }, "dependencies": { + "axios": "^0.19.2", "compression": "^1.7.4", "dotenv": "^8.2.0", "express": "^4.17.1", diff --git a/pages/index.js b/pages/index.js index 6474ebb..16ce9cd 100644 --- a/pages/index.js +++ b/pages/index.js @@ -1,13 +1,19 @@ -import React from "react"; -import styled from "styled-components"; +import React, { useState } from "react"; import LoginForm from "../src/components/login-form"; +import VoteForm from "../src/components/vote-form"; + +import styled from "styled-components"; +import Axios from "axios"; export default function Home() { + const [login, setLogin] = useState(false); + return ( - 리액트 투-표 - +
리액트 투-표
+ {!login && } + {login && }
); } @@ -17,3 +23,7 @@ const Wrapper = styled.div` padding: 10rem 40rem; background-color: Azure; `; + +const Header = styled.h1` + font-size: 4rem; +`; diff --git a/src/components/candidate-form.js b/src/components/candidate-form.js new file mode 100644 index 0000000..e0ba0ff --- /dev/null +++ b/src/components/candidate-form.js @@ -0,0 +1,64 @@ +import React, { useEffect, useState } from "react"; + +import styled from "styled-components"; +import axios from "axios"; + +export default function CandidateForm(props) { + const { name, voteCount, rank, id } = props; + + const voteCandidate = () => { + axios + .put(process.env.API_HOST + "/candidates/" + id + "/vote/") + .then(function (response) { + console.log(response); + alert(name + "님에게 투표완료."); + }) + .catch(function (error) { + console.log(error); + alert("다시 시도해주세요."); + }); + }; + + return ( + + {rank}위: + + {name}[{voteCount}표] + + { + voteCandidate(); + }} + > + 투표 + + + ); +} + +const Wrapper = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + flex-direction: row; +`; + +const Rank = styled.p` + font-weight: bolder; + font-size: 2.5rem; + border: none; +`; +const Info = styled.p` + font-size: 2.5rem; + width: 40%; + border: none; +`; +const VoteButton = styled.button` + background: blue; + color: white; + border: none; + border-radius: 0.7rem; + font-size: 2rem; + height: 3.5rem; + width: 5.5rem; +`; diff --git a/src/components/login-form.js b/src/components/login-form.js index 418d945..c13cce1 100644 --- a/src/components/login-form.js +++ b/src/components/login-form.js @@ -1,8 +1,86 @@ -import React from "react"; +import React, { useState } from "react"; + +import axios from "axios"; + import styled from "styled-components"; -export default function LoginForm() { - return 안녕 나는 로그인 폼!; +export default function LoginForm(props) { + const { loginAccess } = props; + + const [userData, setUserData] = useState({ + email: "", + password: "", + }); + + const { email, password } = userData; + + const handleFormChange = (e) => { + const { name, value } = e.target; + setUserData({ ...userData, [name]: value }); + }; + + const checkData = () => { + if (email === "" || password === "") { + alert("모든 칸을 채워주세요."); + return false; + } else { + return true; + } + }; + + const submitData = () => { + if (!checkData()) return; + + axios + .post(process.env.API_HOST + "/auth/signin/", userData) + .then(function (response) { + console.log(response); + alert("로그인에 성공하셨습니다!"); + loginAccess(true); //왜 loginAccess = ture;는 안될까요? + }) + .catch(function (error) { + if (error.response.status === 404) { + alert("이메일이 존재하지 않습니다."); + setUserData({ + email: "", + password: "", + }); + console.log(error.response); + return; + } else if (error.response.status === 422) { + alert("비밀번호가 존재하지 않습니다."); + setUserData({ + email, + password: "", + }); + console.log(error.response); + return; + } + console.log(error); + }); + }; + + return ( + +
로그인
+ + + EMAIL + + + + PASSWORD + + + + 로그인 +
+ ); } const Wrapper = styled.div` @@ -12,3 +90,42 @@ const Wrapper = styled.div` font-size: 18px; padding: 3rem 4rem; `; + +const Header = styled.h2` + font-size: 3rem; + margin-bottom: 4rem; +`; + +const Row = styled.div` + width: 100%; +`; + +const Lable = styled.label` + font-size: 2rem; + margin-right: auto; +`; + +const Input = styled.input` + width: 75%; + padding: 0.5rem 1rem; + border: 1px, solid, grey; + border-radius: 1rem; +`; + +const UserInfo = styled.div` + display: flex; + flex-direction: row; + width: 100%; + margin-bottom: 2rem; +`; + +const SunmitButton = styled.button` + display: block; + margin-left: auto; + font-size: 1.8rem; + cursor: pointer; + padding: 0.5rem 1rem; + border-radius: 1rem; + border: 0; + outline: 0; +`; diff --git a/src/components/vote-form.js b/src/components/vote-form.js index 65bc549..8913452 100644 --- a/src/components/vote-form.js +++ b/src/components/vote-form.js @@ -1,8 +1,55 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; + +import CandidateForm from "./candidate-form"; + import styled from "styled-components"; +import axios from "axios"; + +export default function VoteForm(props) { + const [candidateList, setCandidateList] = useState([]); + const { name, voteCount, rank, id } = candidateList; + + useEffect(() => { + getCandidateList(); + }, [candidateList]); -export default function VoteForm() { - return 안녕 나는 투표 폼!; + const getCandidateList = async () => { + const data = await axios + .get(process.env.API_HOST + "/candidates/") + .then(function (response) { + return response.data; + }) + .catch(function (error) { + console.log(error); + }); + setCandidateList(data); + }; + + var i = 1; + + return ( + + + 프론트앤드 인기쟁이는 누구? + + CEOS 프론트엔드 개발자 인기 순위 및 투표 창입니다. + + {candidateList + .sort((a, b) => { + return b.voteCount - a.voteCount; + }) + .map((candidate, index) => ( + + ))} + + + ); } const Wrapper = styled.div` @@ -12,3 +59,22 @@ const Wrapper = styled.div` font-size: 18px; padding: 3rem 4rem; `; +const Header1 = styled.h2` + font-size: 30px; + font-weight: bolder; +`; + +const Strong = styled.strong` + color: red; +`; + +const Header2 = styled.p` + font-size: 26px; + font-weight: bolder; + color: grey; +`; + +const CandidateListWrapper = styled.div` + padding: 5rem 10rem; + border: 1px solid black; +`;