diff --git a/package-lock.json b/package-lock.json index 6d171fd..8b5bd18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,11 +16,13 @@ "@types/react": "^18.0.27", "@types/react-dom": "^18.0.10", "@types/react-router-dom": "^5.3.3", + "axios": "^1.3.4", "electron-is-dev": "^2.0.0", "quill-image-resize": "^3.0.9", "quill-image-resize-module-ts": "^3.0.3", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-hook-form": "^7.43.3", "react-quill": "^2.0.0", "react-router-dom": "^6.8.0", "react-scripts": "5.0.1", @@ -5285,20 +5287,19 @@ } }, "node_modules/axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", - "dev": true, + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", "dependencies": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "node_modules/axios/node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -15808,6 +15809,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -16194,6 +16200,21 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "node_modules/react-hook-form": { + "version": "7.43.3", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.43.3.tgz", + "integrity": "sha512-LV6Fixh+hirrl6dXbM78aB6n//82aKbsNbcofF3wc6nx1UJLu3Jj/gsg1E5C9iISnLX+du8VTUyBUz2aCy+H7w==", + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -18629,6 +18650,30 @@ "node": ">=12.0.0" } }, + "node_modules/wait-on/node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/wait-on/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -23327,20 +23372,19 @@ "integrity": "sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==" }, "axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", - "dev": true, + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", "requires": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" }, "dependencies": { "form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -30830,6 +30874,11 @@ } } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -31128,6 +31177,12 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "react-hook-form": { + "version": "7.43.3", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.43.3.tgz", + "integrity": "sha512-LV6Fixh+hirrl6dXbM78aB6n//82aKbsNbcofF3wc6nx1UJLu3Jj/gsg1E5C9iISnLX+du8VTUyBUz2aCy+H7w==", + "requires": {} + }, "react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -32939,6 +32994,29 @@ "lodash": "^4.17.21", "minimist": "^1.2.7", "rxjs": "^7.8.0" + }, + "dependencies": { + "axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dev": true, + "requires": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } } }, "walker": { diff --git a/package.json b/package.json index f801677..03fbe2f 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,13 @@ "@types/react": "^18.0.27", "@types/react-dom": "^18.0.10", "@types/react-router-dom": "^5.3.3", + "axios": "^1.3.4", "electron-is-dev": "^2.0.0", "quill-image-resize": "^3.0.9", "quill-image-resize-module-ts": "^3.0.3", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-hook-form": "^7.43.3", "react-quill": "^2.0.0", "react-router-dom": "^6.8.0", "react-scripts": "5.0.1", diff --git a/public/img/hide.png b/public/img/hide.png new file mode 100644 index 0000000..62dc80a Binary files /dev/null and b/public/img/hide.png differ diff --git a/public/img/show.png b/public/img/show.png new file mode 100644 index 0000000..a659b82 Binary files /dev/null and b/public/img/show.png differ diff --git a/src/App.tsx b/src/App.tsx index 712a746..e6a5619 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,3 +1,5 @@ +import CreatePage from "Components/Create"; +import LoginPage from "Components/Login"; import Edit from "Edit"; import Home from "home"; import LetterTest from "Pages/LetterTest"; @@ -9,7 +11,8 @@ function App() { - } /> + } /> + } /> }> } /> diff --git a/src/Components/Create.tsx b/src/Components/Create.tsx new file mode 100644 index 0000000..0492044 --- /dev/null +++ b/src/Components/Create.tsx @@ -0,0 +1,190 @@ +import axios from "axios"; +import React, { useState } from "react"; +import { useForm } from "react-hook-form"; +import { useNavigate } from "react-router-dom"; + +interface UserValue { + email: string; + password: string; + nickname: string; + passwordConfirm: string; + idCheck: boolean; + emailCheck: boolean; + numberCheck: boolean; +} + +function CreatePage() { + const navigate = useNavigate(); + const [admireNumber, setAdmireNumber] = useState(""); + const handleInputAdmire = (e: any) => { + setAdmireNumber(e.target.value); + }; + + const Idcheck = (e: React.MouseEvent) => { + e.preventDefault(); + axios + .get( + `https://seohamserver.shop/user/check-join-nickname/?nickName=${watch( + "nickname" + )}`, + { + headers: { "Content-Type": "application/json" }, + } + ) + .then((res) => { + if (res.data.result.valid === true) { + alert("사용가능한 닉네임입니다."); + setValue("idCheck", true); + console.log(watch("idCheck")); + } else { + alert("사용불가능한 아이디이거나 중복됩니다."); + setValue("idCheck", false); + console.log(watch("idCheck")); + } + }); + }; + + // const Idcheck = (e: React.MouseEvent) => { + // e.preventDefault(); + // fetch( + // `https://seohamserver.shop/user/check-join-nickname/?nickName=${watch( + // "nickname" + // )}`, + // { + // method: "GET", + // headers: { + // "Content-Type": "application/json", + // }, + // } + // ) + // .then((res) => res.json()) + // .then((res) => { + // if (res.result.valid === true) { + // alert("사용가능한 닉네임입니다."); + // setValue("idCheck", true); + // console.log(watch("idCheck")); + // } else { + // alert("사용불가능한 아이디이거나 중복됩니다."); + // setValue("idCheck", false); + // console.log(watch("idCheck")); + // } + // }); + // }; + + const regExpEm = + /^[A-Za-z0-9_]+[A-Za-z0-9]*[@]{1}[A-Za-z0-9]+[A-Za-z0-9]*[.]{1}[A-Za-z]{1,3}$/; + const regExgPw = /^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{8,25}$/; + + const { + register, + handleSubmit, + watch, + setValue, + formState: { errors, isValid }, + } = useForm({ + mode: "onChange", + }); + + return ( + <> +
+

회원가입

+

계정

+
+ + {errors.email?.type === "required" && ( +
이메일을 입력해주세요
+ )} + {errors.email?.type === "pattern" && ( +
잘못된 이메일 형식입니다
+ )} +
+ +
+
+ + +
+

닉네임

+
+ + {errors.nickname?.type === "required" && ( +
닉네임을 입력해주세요
+ )} + {errors.nickname?.type === "minLength" && ( +
닉네임 길이를 지켜주세요
+ )} + {errors.nickname?.type === "maxLength" && ( +
닉네임 길이를 지켜주세요
+ )} + +
+

비밀번호

+
+ + {errors.password?.type === "pattern" && ( +
+ 비밀번호는 영문+숫자+특수문자 조합으로 8자리 이상 입력해주세요 +
+ )} +
+
+ { + if (watch("password") !== value) { + return "비밀번호가 일치하지 않습니다."; + } + }, + }, + })} + /> + {errors.passwordConfirm?.type === "pattern" && ( +
비밀번호 확인을 위해 입력해주세요
+ )} + {errors.passwordConfirm &&
{errors.passwordConfirm.message}
} +
+
+ +
+ + ); +} + +export default CreatePage; diff --git a/src/Components/Login.tsx b/src/Components/Login.tsx new file mode 100644 index 0000000..6154220 --- /dev/null +++ b/src/Components/Login.tsx @@ -0,0 +1,165 @@ +import { isLogAtom } from "atom"; +import axios from "axios"; +import React, { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { Link } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; +import { useRecoilState } from "recoil"; +import { + LoginButton, + LoginInfoText, + LoginInputDiv, + StyledInput, + TextHeader, +} from "./loginStyled"; + +function LoginPage() { + const navigate = useNavigate(); + const [isLoggedIn, setIsLoggedIn] = useRecoilState(isLogAtom); + const [show, setShow] = useState(false); + useEffect(() => { + if (isLoggedIn === true) { + navigate("/edit"); + } + }); + + const regExpEm = + /^[A-Za-z0-9_]+[A-Za-z0-9]*[@]{1}[A-Za-z0-9]+[A-Za-z0-9]*[.]{1}[A-Za-z]{1,3}$/; + const regExgPw = /^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{8,25}$/; + + const { + register, + watch, + formState: { errors, isValid }, + } = useForm<{ email: string; password: string }>({ mode: "onChange" }); + + // const onSubmit = handleSubmit((data) => { + // fetch("https://seohamserver.shop/user/login", { + // method: "POST", + // headers: { + // "Content-Type": "application/json", + // }, + // body: JSON.stringify({ + // email: data.email, + // passWord: data.password, + // }), + // }) + // .then((res) => res.json()) + // .then((res) => { + // if (res.isSuccess === true) { + // localStorage.setItem("login_token", res.result.jwt); + // localStorage.setItem("userIdx", res.result.userIdx); + // setIsLoggedIn(true); + // alert("로그인 되었습니다"); + // navigate("/edit"); + // } else { + // alert("이메일과 비밀번호를 다시 한 번 확인해주세요"); + // } + // }); + // }); + + const onSubmit = (e: React.MouseEvent) => { + e.preventDefault(); + axios + .post( + "https://seohamserver.shop/user/login", + { + email: watch("email"), + passWord: watch("password"), + }, + { headers: { "Content-Type": "application/json" } } + ) + .then((res) => { + if (res.data.isSuccess === true) { + localStorage.setItem("login_token", res.data.result.jwt); + localStorage.setItem("userIdx", res.data.result.userIdx); + setIsLoggedIn(true); + alert("로그인 되었습니다"); + navigate("/edit"); + } else { + alert("이메일과 비밀번호를 다시 한 번 확인해주세요"); + } + }); + }; + + const onShowClick = (e: React.MouseEvent) => { + setShow(!show); + }; + + return ( +
+ 서함 + + + + {errors.email?.type === "required" &&
이메일을 입력해주세요
} + {errors.email?.type === "pattern" &&
잘못된 이메일 형식입니다
} + + + + + {errors.password?.type === "pattern" && ( +
+ 비밀번호는 영문+숫자+특수문자 조합으로 8자리 이상 입력해주세요 +
+ )} +
+ + 로그인 + +
+
+ + 계정찾기 + + + 비밀번호찾기 + + + 회원가입 + +
+
+ ); +} + +export default LoginPage; diff --git a/src/Components/loginStyled.tsx b/src/Components/loginStyled.tsx new file mode 100644 index 0000000..5e96fa9 --- /dev/null +++ b/src/Components/loginStyled.tsx @@ -0,0 +1,55 @@ +import styled from "styled-components"; +//padding 이나 margin 순서 위부터! 시계방향으로! +export const LoginButton = styled.button` + background-color: #f47c7c; + color: #ffffff; + border: none; + border-radius: 5px; + padding: 8px 48px 8px 48px; + width: 240px; + height: 40px; + top: calc(50% - 40px / 2 + 70.5px); + cursor: pointer; +`; + +export const TextHeader = styled.h1` + margin: 40px 0px 40px 0px; + padding: 20px 0px 28px 0px; + text-align: center; + font-weight: bold; + font-size: 36px; + color: #ff8080; +`; + +export const StyledInput = styled.input` + border-radius: 4px; + border-width: 1px; + padding: 4px 0px 4px 0px; + width: 200px; + height: 25px; + top: 20%; + left: 9px; + bottom: 17.5%; + background-color: transparent; + border-color: #989898; + line-height: 17px; + align-items: center; +`; + +export const LoginInputDiv = styled.div` + justify-content: center; + margin: 12px 0px 12px 0px; + display: flex; +`; + +export const LoginInfoText = styled.button` + font-size: 12px; + font-weight: 400; + line-height: 14px; + align-items: center; + text-align: center; + letter-spacing: -0.05em; + background-color: transparent; + border-color: transparent black transparent transparent; + border-right: solid; +`; diff --git a/src/home.tsx b/src/home.tsx index 2d2e7e7..07ec7bf 100644 --- a/src/home.tsx +++ b/src/home.tsx @@ -9,7 +9,7 @@ import QuillToolbar, { formats, modules } from "EditorToolBar"; import { useRecoilState } from "recoil"; import { letterState } from "atom"; import { Link } from "react-router-dom"; -import TagCreater from "Components/TagCreater"; +import TagCreater from "./Components/TagCreater"; import CreateTag from "./Components/TagMaker"; Quill.register("modules/ImageResize", ImageResize); @@ -32,8 +32,8 @@ Quill.register(Font, true); function Home() { const [value, setValue] = useState(""); - const [test, Settest] = useState(true); - const [back, setBack] = useState(true); + const [test, Settest] = useState(true); + const [back, setBack] = useState(true); const navigate = useNavigate(); const onImage = () => { if (back === true) {