diff --git a/index.html b/index.html
index 6e23e05..40cb487 100644
--- a/index.html
+++ b/index.html
@@ -16,6 +16,9 @@
rel="preconnect"
href="https://fonts.gstatic.com"
crossorigin />
+
TEMP
diff --git a/package-lock.json b/package-lock.json
index c5414bc..c982485 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,7 @@
"@fullcalendar/interaction": "^6.1.8",
"@fullcalendar/react": "^6.1.8",
"axios": "^1.4.0",
+ "date-fns": "^2.30.0",
"fullcalendar": "^6.1.8",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -30,6 +31,7 @@
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
+ "react-datepicker": "^4.16.0",
"ts-node": "^10.9.1",
"typescript": "^5.0.2",
"vite": "^4.4.5",
@@ -2622,6 +2624,16 @@
"url": "https://opencollective.com/unts"
}
},
+ "node_modules/@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "dev": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
"node_modules/@remix-run/router": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.7.2.tgz",
@@ -3461,6 +3473,12 @@
"node": ">= 6"
}
},
+ "node_modules/classnames": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
+ "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==",
+ "dev": true
+ },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -3563,6 +3581,21 @@
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
},
+ "node_modules/date-fns": {
+ "version": "2.30.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
+ "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
+ "dependencies": {
+ "@babel/runtime": "^7.21.0"
+ },
+ "engines": {
+ "node": ">=0.11"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/date-fns"
+ }
+ },
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -4751,6 +4784,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -4994,6 +5036,23 @@
"node": ">=6.0.0"
}
},
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dev": true,
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/prop-types/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "dev": true
+ },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@@ -5039,6 +5098,24 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-datepicker": {
+ "version": "4.16.0",
+ "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.16.0.tgz",
+ "integrity": "sha512-hNQ0PAg/LQoVbDUO/RWAdm/RYmPhN3cz7LuQ3hqbs24OSp69QCiKOJRrQ4jk1gv1jNR5oYu8SjjgfDh8q6Q1yw==",
+ "dev": true,
+ "dependencies": {
+ "@popperjs/core": "^2.11.8",
+ "classnames": "^2.2.6",
+ "date-fns": "^2.30.0",
+ "prop-types": "^15.7.2",
+ "react-onclickoutside": "^6.12.2",
+ "react-popper": "^2.3.0"
+ },
+ "peerDependencies": {
+ "react": "^16.9.0 || ^17 || ^18",
+ "react-dom": "^16.9.0 || ^17 || ^18"
+ }
+ },
"node_modules/react-dom": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
@@ -5051,6 +5128,41 @@
"react": "^18.2.0"
}
},
+ "node_modules/react-fast-compare": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
+ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==",
+ "dev": true
+ },
+ "node_modules/react-onclickoutside": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.13.0.tgz",
+ "integrity": "sha512-ty8So6tcUpIb+ZE+1HAhbLROvAIJYyJe/1vRrrcmW+jLsaM+/powDRqxzo6hSh9CuRZGSL1Q8mvcF5WRD93a0A==",
+ "dev": true,
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/Pomax/react-onclickoutside/blob/master/FUNDING.md"
+ },
+ "peerDependencies": {
+ "react": "^15.5.x || ^16.x || ^17.x || ^18.x",
+ "react-dom": "^15.5.x || ^16.x || ^17.x || ^18.x"
+ }
+ },
+ "node_modules/react-popper": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz",
+ "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==",
+ "dev": true,
+ "dependencies": {
+ "react-fast-compare": "^3.0.1",
+ "warning": "^4.0.2"
+ },
+ "peerDependencies": {
+ "@popperjs/core": "^2.0.0",
+ "react": "^16.8.0 || ^17 || ^18",
+ "react-dom": "^16.8.0 || ^17 || ^18"
+ }
+ },
"node_modules/react-router": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.14.2.tgz",
@@ -5869,6 +5981,15 @@
}
}
},
+ "node_modules/warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "dev": true,
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
diff --git a/package.json b/package.json
index 6d9ca5a..9b54aa8 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,7 @@
"@fullcalendar/interaction": "^6.1.8",
"@fullcalendar/react": "^6.1.8",
"axios": "^1.4.0",
+ "date-fns": "^2.30.0",
"fullcalendar": "^6.1.8",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -32,6 +33,7 @@
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
+ "react-datepicker": "^4.16.0",
"ts-node": "^10.9.1",
"typescript": "^5.0.2",
"vite": "^4.4.5",
diff --git a/src/api/account.ts b/src/api/account.ts
index 4e6cc9d..05fc3ad 100644
--- a/src/api/account.ts
+++ b/src/api/account.ts
@@ -1,8 +1,13 @@
import { baseInstance, authInstance } from 'api/index'
//회원가입 -- base
-export const signUp = async newAccount => {
- const res = await baseInstance.post('/signup', newAccount)
+export const signUp = async ({ password, email, hireDate, username }) => {
+ const res = await baseInstance.post('/signup', {
+ password: password,
+ email: email,
+ hireDate: hireDate,
+ username: username
+ })
return res.data
}
@@ -15,26 +20,23 @@ export const signIn = async (email: string, password: string) => {
return res
}
-//로그아웃 -- auth -- API X => **remove token**
-// export const signOut = async () => {
-// const res = await authInstance.post('/signin')
-// return res.data
-// }
-
//이메일 중복체크 -- base
export const checkEmailAvailable = async (email: string) => {
- const res = await baseInstance.post('/update', email)
+ const res = await baseInstance.get(`/emailCheck?email=${email}`)
return res.data
}
//정보 수정(update) -- auth
-export const updateProfile = async account => {
- const res = await authInstance.post('/findPassword', account)
+export const updateProfile = async (profile: string, password: string) => {
+ const res = await authInstance.post('/update', {
+ profile: profile,
+ password: password
+ })
return res.data
}
-//비밀번호 재설정(이메일 전송) -- auth
+//비밀번호 재설정(이메일 전송) -- base
export const resetPassword = async (email: string) => {
- const res = await authInstance.post('/findPassword', email)
+ const res = await baseInstance.post('/findPassword', { email: email })
return res.data
}
diff --git a/src/assets/calendarIcon.png b/src/assets/calendarIcon.png
new file mode 100644
index 0000000..eccb953
Binary files /dev/null and b/src/assets/calendarIcon.png differ
diff --git a/src/components/form/DatePickerForm.tsx b/src/components/form/DatePickerForm.tsx
new file mode 100644
index 0000000..fd5a9e3
--- /dev/null
+++ b/src/components/form/DatePickerForm.tsx
@@ -0,0 +1,58 @@
+import { styled } from 'styled-components'
+import DatePicker from 'react-datepicker'
+import 'react-datepicker/dist/react-datepicker.css'
+import { TitleText } from 'components/index'
+import { signupTexts } from 'constants/index'
+import { ko } from 'date-fns/esm/locale'
+
+export const DatePickerForm = ({ date, setDate }) => {
+ //코드 더러움 수정 필요
+ return (
+ <>
+
+ {signupTexts.hireDate}
+
+
+ >
+ )
+}
+
+const DatePickerWrapper = styled.div`
+ position: relative;
+ img {
+ width: 25px;
+ height: 24px;
+ position: absolute;
+ z-index: 1;
+ top: 53px;
+ right: 40px;
+ cursor: pointer;
+ }
+ input {
+ background: #ffffff;
+ border: 1px solid #d9d9d9;
+ border-radius: 10px;
+ padding-left: 10px;
+ width: 434px;
+ height: 60px;
+ margin-bottom: 6px;
+ &::placeholder {
+ font-size: 12px;
+ opacity: 0.75;
+ }
+ &:focus {
+ outline: 1px solid ${props => props.theme.colors.primaryBlue};
+ }
+ }
+`
diff --git a/src/components/form/EmailValidationForm.tsx b/src/components/form/EmailValidationForm.tsx
new file mode 100644
index 0000000..42d84d9
--- /dev/null
+++ b/src/components/form/EmailValidationForm.tsx
@@ -0,0 +1,75 @@
+import { InputField } from 'components/index'
+import { styled } from 'styled-components'
+import { checkEmailAvailable } from 'api/index'
+import { AxiosResponse, AxiosError } from 'axios'
+import { signupTexts } from 'constants/index'
+
+interface EmailResponse extends AxiosResponse {
+ response: {
+ email: string
+ available: boolean
+ }
+}
+
+export const EmailValidationForm = ({
+ setEmail,
+ email,
+ title,
+ ph,
+ setIsEmailInUse
+}) => {
+ const handleEmailCheck = async () => {
+ try {
+ const res: EmailResponse = await checkEmailAvailable(email)
+ // 중복되는 이메일이 없는 경우 (해당 이메일로 가입 가능)
+ if (email && res?.response?.available === true) {
+ setIsEmailInUse(true)
+ alert(`${signupTexts.emailAvailable}`)
+ }
+ // 중복되는 이메일이 있는 경우 (해당 이메일로 가입 불가능)
+ if (email && res?.response?.available === false) {
+ setIsEmailInUse(false)
+ alert(`${signupTexts.emailInUse}`)
+ }
+ } catch (err) {
+ if (err instanceof AxiosError) {
+ console.error(err)
+ alert(err)
+ }
+ }
+ }
+
+ return (
+ //코드 더러움 수정 필요
+
+
+
+ )
+}
+
+const ValidationFormWrapper = styled.div`
+ position: relative;
+ span {
+ font-size: 32px;
+ position: absolute;
+ z-index: 1;
+ top: 50px;
+ right: 36px;
+ cursor: pointer;
+ }
+`
diff --git a/src/components/form/InputField.tsx b/src/components/form/InputField.tsx
index c4126bc..30f65f2 100644
--- a/src/components/form/InputField.tsx
+++ b/src/components/form/InputField.tsx
@@ -14,7 +14,7 @@ export const InputField = ({ inputRef, title, ph, fn, val, type }) => {
)
}
-const TitleText = styled.div`
+export const TitleText = styled.div`
padding: 10px;
line-height: 16px;
letter-spacing: -0.48px;
diff --git a/src/components/form/ResetPasswordForm.tsx b/src/components/form/ResetPasswordForm.tsx
index 4310e65..2c4f104 100644
--- a/src/components/form/ResetPasswordForm.tsx
+++ b/src/components/form/ResetPasswordForm.tsx
@@ -2,11 +2,21 @@ import { styled } from 'styled-components'
import { useRef, useEffect, useState } from 'react'
import { resetTexts } from 'constants/index'
import { InputField } from 'components/index'
+import { resetPassword } from 'api/index'
export const ResetPasswordForm = () => {
const [email, setEmail] = useState('')
- const submitHandler = () => {}
+ const handlePasswordReset = async e => {
+ e.preventDefault()
+ try {
+ if (email) {
+ await resetPassword(email)
+ }
+ } catch (error) {
+ console.log(error)
+ }
+ }
const inputRef = useRef(null)
@@ -24,13 +34,15 @@ export const ResetPasswordForm = () => {
>
- {resetTexts.reset}
+
+ {resetTexts.reset}
+
)
}
diff --git a/src/components/form/SignUpForm.tsx b/src/components/form/SignUpForm.tsx
index 1497c9e..706bc12 100644
--- a/src/components/form/SignUpForm.tsx
+++ b/src/components/form/SignUpForm.tsx
@@ -1,49 +1,106 @@
import { styled } from 'styled-components'
-import { useRef, useEffect } from 'react'
+import { useRef, useEffect, useState } from 'react'
import { signupTexts } from 'constants/index'
-import { InputField, SignInCallToAction } from 'components/index'
+import { signUp } from 'api/index'
+import {
+ InputField,
+ SignInCallToAction,
+ DatePickerForm,
+ EmailValidationForm
+} from 'components/index'
+import { dateFormatter } from 'utils/index'
+import { AxiosError, AxiosResponse } from 'axios'
+import { useNavigate } from 'react-router-dom'
export const SignUpForm = () => {
+ const navigate = useNavigate()
+ // 유저 입력 데이터들
+ const [name, setName] = useState('')
+ const [email, setEmail] = useState('')
+ const [password, setPassword] = useState('')
+ const [verification, setVerification] = useState('')
+ const [startDate, setStartDate] = useState(new Date())
+ // 이메일 중복확인 데이터
+ const [isEmailInUse, setIsEmailInUse] = useState(true)
+
const inputRef = useRef(null)
useEffect(() => {
inputRef?.current?.focus()
}, [])
+ const handleSignUp = async e => {
+ e.preventDefault()
+ try {
+ // 유저 입력 데이터들 부재시
+ if (!(email && password && startDate && name)) {
+ alert(`${signupTexts.requiredData}`)
+ return
+ }
+ // 이메일 중복확인 미완료시
+ if (!isEmailInUse) {
+ alert(`${signupTexts.validationRequired}`)
+ return
+ }
+ const res: AxiosResponse = await signUp({
+ password: password,
+ email: email,
+ hireDate: dateFormatter(startDate),
+ username: name
+ })
+ // 회원가입 성공시 로그인 화면으로 이동
+ if (res.status === 200) {
+ navigate('/')
+ return
+ }
+ } catch (err) {
+ if (err instanceof AxiosError) {
+ const error = e.response?.data.error.message
+ alert(error)
+ }
+ }
+ }
+
return (
-
+ setIsEmailInUse={setIsEmailInUse}
+ />
- {signupTexts.registerBtn}
+
+
+ {signupTexts.registerBtn}
+
)
diff --git a/src/components/form/index.ts b/src/components/form/index.ts
index af32d58..c779303 100644
--- a/src/components/form/index.ts
+++ b/src/components/form/index.ts
@@ -8,3 +8,5 @@ export * from 'components/form/InputField'
export * from 'components/form/SignUpCallToAction'
export * from 'components/form/SignInCallToAction'
export * from 'components/form/UpdateImageForm'
+export * from 'components/form/DatePickerForm'
+export * from 'components/form/EmailValidationForm'
diff --git a/src/constants/signupTexts.ts b/src/constants/signupTexts.ts
index c159062..424f856 100644
--- a/src/constants/signupTexts.ts
+++ b/src/constants/signupTexts.ts
@@ -1,5 +1,4 @@
export const signupTexts = {
- intro: 'Welcome👀',
signin: '회원가입',
username: '이름',
email: '이메일 주소',
@@ -12,5 +11,10 @@ export const signupTexts = {
usernameText: '이름을 입력해주세요.',
emailText: '이메일을 입력해주세요',
pwdText: '영어 대문자, 영어 소문자, 숫자, 특수문자를 모두 포함 (8글자 이상)',
- pwdCheckText: '비밀번호를 다시 입력해주세요.'
+ pwdCheckText: '비밀번호를 다시 입력해주세요.',
+ hireDate: '입사날짜',
+ emailInUse: '이미 사용중인 이메일입니다.',
+ emailAvailable: '사용가능한 이메일입니다.',
+ requiredData: '회원기입에 실패했습니다. 입력항목을 다시 확인해주세요.',
+ validationRequired: '이메일 인증을 진행해주세요.'
}
diff --git a/src/utils/dateFormatter.ts b/src/utils/dateFormatter.ts
new file mode 100644
index 0000000..70d9c59
--- /dev/null
+++ b/src/utils/dateFormatter.ts
@@ -0,0 +1,14 @@
+export let dateFormatter = selected => {
+ return (
+ selected.getFullYear() +
+ '-' +
+ (selected.getMonth() + 1 < 9
+ ? '0' + (selected.getMonth() + 1)
+ : selected.getMonth() + 1) +
+ '-' +
+ (selected.getDate() < 9 ? '0' + selected.getDate() : selected.getDate()) +
+ 'T' +
+ '09:00:00' +
+ 'Z'
+ )
+}
diff --git a/src/utils/index.ts b/src/utils/index.ts
new file mode 100644
index 0000000..f4fe8d6
--- /dev/null
+++ b/src/utils/index.ts
@@ -0,0 +1 @@
+export * from 'utils/dateFormatter'
diff --git a/tsconfig.json b/tsconfig.json
index 2ab7ceb..ff87f95 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -34,7 +34,8 @@
"api/*": ["src/api/*"],
"styles/*": ["src/styles/*"],
"assets/*": ["src/assets/*"],
- "constants/*": ["src/constants/*"]
+ "constants/*": ["src/constants/*"],
+ "utils/*": ["src/utils/*"]
}
},
"include": [