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;
+`;