diff --git a/.github/delete-merged-branch-config.yml b/week4/.github/delete-merged-branch-config.yml similarity index 100% rename from .github/delete-merged-branch-config.yml rename to week4/.github/delete-merged-branch-config.yml diff --git a/.github/pull_request_template.md b/week4/.github/pull_request_template.md similarity index 100% rename from .github/pull_request_template.md rename to week4/.github/pull_request_template.md diff --git a/.gitignore b/week4/.gitignore similarity index 100% rename from .gitignore rename to week4/.gitignore diff --git a/faq.html b/week4/faq.html similarity index 100% rename from faq.html rename to week4/faq.html diff --git a/week4/folder.html b/week4/folder.html new file mode 100644 index 000000000..8a8b016e4 --- /dev/null +++ b/week4/folder.html @@ -0,0 +1,7 @@ + + + + + folder + + diff --git a/images/facebook.svg b/week4/images/facebook.svg similarity index 100% rename from images/facebook.svg rename to week4/images/facebook.svg diff --git a/images/hero.png b/week4/images/hero.png similarity index 100% rename from images/hero.png rename to week4/images/hero.png diff --git a/images/image1.png b/week4/images/image1.png similarity index 100% rename from images/image1.png rename to week4/images/image1.png diff --git a/images/image2.png b/week4/images/image2.png similarity index 100% rename from images/image2.png rename to week4/images/image2.png diff --git a/images/image3.png b/week4/images/image3.png similarity index 100% rename from images/image3.png rename to week4/images/image3.png diff --git a/images/image4.png b/week4/images/image4.png similarity index 100% rename from images/image4.png rename to week4/images/image4.png diff --git a/images/instagram.svg b/week4/images/instagram.svg similarity index 100% rename from images/instagram.svg rename to week4/images/instagram.svg diff --git a/images/logo.svg b/week4/images/logo.svg similarity index 100% rename from images/logo.svg rename to week4/images/logo.svg diff --git a/images/meta_image.png b/week4/images/meta_image.png similarity index 100% rename from images/meta_image.png rename to week4/images/meta_image.png diff --git a/images/sign/eye-off.svg b/week4/images/sign/eye-off.svg similarity index 100% rename from images/sign/eye-off.svg rename to week4/images/sign/eye-off.svg diff --git a/week4/images/sign/eye-on.svg b/week4/images/sign/eye-on.svg new file mode 100644 index 000000000..61afee898 --- /dev/null +++ b/week4/images/sign/eye-on.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/images/sign/google.png b/week4/images/sign/google.png similarity index 100% rename from images/sign/google.png rename to week4/images/sign/google.png diff --git a/images/sign/icgoogle.png b/week4/images/sign/icgoogle.png similarity index 100% rename from images/sign/icgoogle.png rename to week4/images/sign/icgoogle.png diff --git a/images/sign/ickakao.png b/week4/images/sign/ickakao.png similarity index 100% rename from images/sign/ickakao.png rename to week4/images/sign/ickakao.png diff --git a/images/sign/kakao.png b/week4/images/sign/kakao.png similarity index 100% rename from images/sign/kakao.png rename to week4/images/sign/kakao.png diff --git a/images/twitter.svg b/week4/images/twitter.svg similarity index 100% rename from images/twitter.svg rename to week4/images/twitter.svg diff --git a/images/youtube.svg b/week4/images/youtube.svg similarity index 100% rename from images/youtube.svg rename to week4/images/youtube.svg diff --git a/index.html b/week4/index.html similarity index 100% rename from index.html rename to week4/index.html diff --git a/week4/js/signModule/email_validation.js b/week4/js/signModule/email_validation.js new file mode 100644 index 000000000..8544c0980 --- /dev/null +++ b/week4/js/signModule/email_validation.js @@ -0,0 +1,55 @@ +import { printError } from "./print_error.js"; + +const emailInput = document.querySelector('#sign-email'); +const emailForm = emailInput.parentElement; + +const emailValidation = /^[A-Za-z0-9_\.\-]+@[A-Za-z0-9\.\-]{1,10}\.[A-za-z0-9\-]{2,3}$/; + +// 비어있는지 체크 +function emailEmptyCheck(e) { + if(!emailInput.value) { + emailInput.classList.add('error'); + printError(emailInput, '이메일을 입력해주세요.'); + } else { + if(emailForm.querySelector('.error-message')) { + emailForm.lastElementChild.remove(); + } + emailValidationCheck(e); + } +} + +// 올바른 이메일인지 체크 +function emailValidationCheck(e) { + if(emailValidation.test(emailInput.value)) { + emailInput.classList.remove('error'); + if(emailForm.querySelector('.error-message')) { + emailForm.lastElementChild.remove(); + } + if(document.querySelector('title').text === '회원가입') { + usingEmail(e); + } + } else { + emailInput.classList.add('error'); + printError(emailInput, '올바른 이메일 주소가 아닙니다.'); + } +} + +// 사용중인 이메일인지 체크 +function usingEmail(e) { + if(emailInput.value === 'test@codeit.com') { + emailInput.classList.add('error'); + printError(emailInput, '이미 사용 중인 이메일입니다.'); + } else { + if(emailInput.parentElement.querySelector('.error')) { + emailInput.classList.remove('error'); + emailInput.parentElement.lastElementChild.remove(); + } + } +} + +// 이벤트 등록 +function emailCheck(em) { + em.addEventListener('focusout', emailEmptyCheck, usingEmail); +} + +export { emailInput, emailCheck, emailEmptyCheck, emailValidation }; \ No newline at end of file diff --git a/week4/js/signModule/eye.js b/week4/js/signModule/eye.js new file mode 100644 index 000000000..1e360c892 --- /dev/null +++ b/week4/js/signModule/eye.js @@ -0,0 +1,26 @@ +const eyes = document.querySelectorAll('.eye'); + +function eyeBlink (e) { + const eyeClass = e.target.parentElement.classList; + const pwdText = e.target.parentElement.parentElement.firstElementChild; + console.log('o'); + if(eyeClass.contains('off')) { + e.target.src = '../../images/sign/eye-on.svg'; + eyeClass.remove('off'); + eyeClass.add('on'); + pwdText.type = 'text'; + } else { + e.target.src = '../../images/sign/eye-off.svg'; + eyeClass.remove('on'); + eyeClass.add('off'); + pwdText.type = 'password'; + } +} + +const eyeEvent = function (eyes) { + for(let eye of eyes) { + eye.addEventListener('click', eyeBlink); + } +} + +export { eyes, eyeEvent }; \ No newline at end of file diff --git a/week4/js/signModule/print_error.js b/week4/js/signModule/print_error.js new file mode 100644 index 000000000..ca9b3498f --- /dev/null +++ b/week4/js/signModule/print_error.js @@ -0,0 +1,13 @@ +function printError(input, message) { + if(input.parentElement.querySelector('.error-message')) { + input.parentElement.lastElementChild.remove(); + } + if(!input.parentElement.querySelector('.error-message')) { + const emailError = document.createElement('p'); + emailError.classList.add('error-message'); + emailError.textContent = message; + input.parentElement.append(emailError); + } +} + +export { printError }; \ No newline at end of file diff --git a/week4/js/signModule/pwd_confirm.js b/week4/js/signModule/pwd_confirm.js new file mode 100644 index 000000000..1c69f76b4 --- /dev/null +++ b/week4/js/signModule/pwd_confirm.js @@ -0,0 +1,29 @@ +import { printError } from "./print_error.js"; +import { pwdInput } from "./pwd_validation.js"; + +const pwdConfirmInput = document.querySelector('#sign-password-confirm'); +const pwdConfirmForm = pwdConfirmInput.parentElement; + +// 비밀번호 확인 +function pwdConfirm(e) { + if(!pwdConfirmInput.value) { + pwdConfirmInput.classList.add('error'); + printError(pwdConfirmInput, '비밀번호를 확인해주세요.'); + } else { + if(pwdConfirmInput.value !== pwdInput.value) { + pwdConfirmInput.classList.add('error'); + printError(pwdConfirmInput, '비밀번호가 일치하지 않아요.'); + } else { + pwdConfirmInput.classList.remove('error'); + if(pwdConfirmForm.querySelector('.error-message')) { + pwdConfirmForm.lastElementChild.remove(); + } + } + } +}; + +function pwdConfirmCheck(e) { + pwdConfirmInput.addEventListener('focusout', pwdConfirm); +}; + +export { pwdConfirmInput, pwdConfirm, pwdConfirmCheck }; \ No newline at end of file diff --git a/week4/js/signModule/pwd_validation.js b/week4/js/signModule/pwd_validation.js new file mode 100644 index 000000000..e1e13ac0d --- /dev/null +++ b/week4/js/signModule/pwd_validation.js @@ -0,0 +1,39 @@ +import { printError } from "./print_error.js"; + +const pwdInput = document.querySelector('#sign-password'); +const pwdForm = pwdInput.parentElement; + +const pwdValidation = /^(?=.*\d)(?=^.{8,15}$)(?=.*[a-zA-Z]).*$/; + +// 비어있는지 체크 +function pwdEmptyCheck(e) { + if(!pwdInput.value) { + pwdInput.classList.add('error'); + printError(pwdInput, '비밀번호를 입력해주세요.'); + } else if(pwdInput.value) { + if(pwdForm.querySelector('.error-message')) { + pwdForm.lastElementChild.remove(); + } + pwdValidationCheck(e); + } +} + +// 올바른 형식인지 체크 +function pwdValidationCheck(e) { + if(pwdValidation.test(pwdInput.value)) { + pwdInput.classList.remove('error'); + if(pwdForm.querySelector('.error-message')) { + pwdForm.lastElementChild.remove(); + } + } else { + printError(pwdInput, '비밀번호는 영문, 숫자 조합 8자 이상 입력해 주세요.'); + pwdInput.classList.add('error'); + } +} + +// 이벤트 등록 +function pwdCheck(pw) { + pw.addEventListener('focusout', pwdEmptyCheck); +} + +export { pwdInput, pwdCheck, pwdEmptyCheck, pwdValidation }; \ No newline at end of file diff --git a/week4/js/signin.js b/week4/js/signin.js new file mode 100644 index 000000000..becfbd4a7 --- /dev/null +++ b/week4/js/signin.js @@ -0,0 +1,11 @@ +import { eyes, eyeEvent } from './signModule/eye.js'; +import { emailInput, emailCheck } from './signModule/email_validation.js'; +import { pwdInput, pwdCheck } from './signModule/pwd_validation.js'; +import { btn, btnCheck } from './signin_btn.js'; + + + +eyeEvent(eyes); +emailCheck(emailInput); +pwdCheck(pwdInput); +btnCheck(btn); \ No newline at end of file diff --git a/week4/js/signin_btn.js b/week4/js/signin_btn.js new file mode 100644 index 000000000..0af280faa --- /dev/null +++ b/week4/js/signin_btn.js @@ -0,0 +1,24 @@ +import { printError } from "./signModule/print_error.js"; +import { emailInput } from "./signModule/email_validation.js"; +import { pwdInput } from "./signModule/pwd_validation.js"; + +const btn = document.querySelector('.sign-button'); + +function btnEmptyCheck(e) { + e.preventDefault(); + if(emailInput.value === 'test@codeit.com' && pwdInput.value === 'codeit101') { + location.href = "./folder.html"; + } + else { + emailInput.classList.add('error'); + printError(emailInput, '이메일을 확인해주세요.'); + pwdInput.classList.add('error'); + printError(pwdInput, '비밀번호를 확인해주세요.'); + } +} + +function btnCheck(btn) { + btn.addEventListener('click', btnEmptyCheck); +} + +export { btn, btnCheck }; \ No newline at end of file diff --git a/week4/js/signup.js b/week4/js/signup.js new file mode 100644 index 000000000..5a8c7ef3f --- /dev/null +++ b/week4/js/signup.js @@ -0,0 +1,12 @@ +import { eyes, eyeEvent } from './signModule/eye.js'; +import { emailInput, emailCheck } from './signModule/email_validation.js'; +import { pwdInput, pwdCheck } from './signModule/pwd_validation.js'; +import { pwdConfirmInput, pwdConfirmCheck } from './signModule/pwd_confirm.js'; +import { btn, btnCheck } from './signup_btn.js'; + + +emailCheck(emailInput); +pwdCheck(pwdInput); +pwdConfirmCheck(pwdConfirmInput); +eyeEvent(eyes); +btnCheck(btn); \ No newline at end of file diff --git a/week4/js/signup_btn.js b/week4/js/signup_btn.js new file mode 100644 index 000000000..6adcda5f8 --- /dev/null +++ b/week4/js/signup_btn.js @@ -0,0 +1,25 @@ +import { emailInput, emailEmptyCheck, emailValidation } from "./signModule/email_validation.js"; +import { pwdInput, pwdEmptyCheck, pwdValidation } from "./signModule/pwd_validation.js"; +import { pwdConfirmInput, pwdConfirm } from "./signModule/pwd_confirm.js"; + +const btn = document.querySelector('.sign-button'); + +let userEmail = 'test@codeit.com' + +function btnSignupCheck(e) { + e.preventDefault(); + if(emailInput.value && emailInput.value !== userEmail && emailValidation.test(emailInput.value)) { + if(pwdInput.value && pwdConfirmInput.value === pwdInput.value && pwdValidation.test(pwdInput.value)) { + location.href = './folder.html'; + } + } +} + +function btnCheck(btn) { + btn.addEventListener('click', btnSignupCheck); + btn.addEventListener('click', emailEmptyCheck); + btn.addEventListener('click', pwdEmptyCheck); + btn.addEventListener('click', pwdConfirm); +} + +export { btn, btnCheck }; \ No newline at end of file diff --git a/privacy.html b/week4/privacy.html similarity index 100% rename from privacy.html rename to week4/privacy.html diff --git a/signin.html b/week4/signin.html similarity index 84% rename from signin.html rename to week4/signin.html index d81529b3a..4b5d1c031 100644 --- a/signin.html +++ b/week4/signin.html @@ -28,20 +28,20 @@
-
-
+ diff --git a/signup.html b/week4/signup.html similarity index 83% rename from signup.html rename to week4/signup.html index dfb3ff103..759803f14 100644 --- a/signup.html +++ b/week4/signup.html @@ -35,30 +35,30 @@
-
-
-
+ diff --git a/src/index-media.css b/week4/src/index-media.css similarity index 89% rename from src/index-media.css rename to week4/src/index-media.css index e6b60e7f8..4631d4305 100644 --- a/src/index-media.css +++ b/week4/src/index-media.css @@ -15,21 +15,21 @@ .tablet-padding { display: block; + flex: 0 99 20rem; min-width: 3.2rem; } - - .tablet-padding-center { - min-width: 53.8rem; - } nav { - display: grid; - grid-template: 9.4rem / 1fr 133px 1fr 128px 1fr; + display: flex; max-width: 192rem; height: 9.4rem; padding: 0; } + .tablet-padding-center { + flex: 10 1 538px; + } + .hero-header { display: flex; flex-direction: column; @@ -77,21 +77,6 @@ } } -/* tablet nav padding */ -@media screen and (max-width: 862px) { - .tablet-padding { - width: 3.2rem; - } - - .tablet-padding-center { - min-width: auto; - } - - nav { - display: grid; - grid-template: 9.4rem / 32px 133px 1fr 128px 32px; - } -} /* mobile */ @media screen and (max-width: 767px) { diff --git a/src/reset.css b/week4/src/reset.css similarity index 100% rename from src/reset.css rename to week4/src/reset.css diff --git a/src/sign-media.css b/week4/src/sign-media.css similarity index 100% rename from src/sign-media.css rename to week4/src/sign-media.css diff --git a/src/sign.css b/week4/src/sign.css similarity index 95% rename from src/sign.css rename to week4/src/sign.css index c2404b9ab..6527d0a31 100644 --- a/src/sign.css +++ b/week4/src/sign.css @@ -185,5 +185,17 @@ button:hover { position: absolute; top: 23px; right: 15px; + height: 16px; } +.eye:hover { + cursor: pointer; +} + +.error { + border: 1px solid var(--linkbrary-red); +} + +.error-message { + color: var(--linkbrary-red); +} \ No newline at end of file diff --git a/src/style.css b/week4/src/style.css similarity index 99% rename from src/style.css rename to week4/src/style.css index 57ae1cf1b..4c1f73cb7 100644 --- a/src/style.css +++ b/week4/src/style.css @@ -50,6 +50,7 @@ nav { .cta-short { width: 12.8rem; + flex-shrink: 0; } .cta-long { diff --git a/week5/.gitignore b/week5/.gitignore new file mode 100644 index 000000000..722d5e71d --- /dev/null +++ b/week5/.gitignore @@ -0,0 +1 @@ +.vscode diff --git a/week5/faq.html b/week5/faq.html new file mode 100644 index 000000000..6612ae103 --- /dev/null +++ b/week5/faq.html @@ -0,0 +1,7 @@ + + + + + FAQ + + diff --git a/week5/folder.html b/week5/folder.html new file mode 100644 index 000000000..7d32e3070 --- /dev/null +++ b/week5/folder.html @@ -0,0 +1,17 @@ + + + + + 폴더 + + + + + + diff --git a/week5/images/eye-off.svg b/week5/images/eye-off.svg new file mode 100644 index 000000000..bec50d66f --- /dev/null +++ b/week5/images/eye-off.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/week5/images/eye-on.svg b/week5/images/eye-on.svg new file mode 100644 index 000000000..61afee898 --- /dev/null +++ b/week5/images/eye-on.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/week5/images/facebook.svg b/week5/images/facebook.svg new file mode 100644 index 000000000..b9c9d4939 --- /dev/null +++ b/week5/images/facebook.svg @@ -0,0 +1,3 @@ + + + diff --git a/week5/images/google.png b/week5/images/google.png new file mode 100644 index 000000000..e49ff34a3 Binary files /dev/null and b/week5/images/google.png differ diff --git a/week5/images/hero.png b/week5/images/hero.png new file mode 100644 index 000000000..11dc528fb Binary files /dev/null and b/week5/images/hero.png differ diff --git a/week5/images/image1.png b/week5/images/image1.png new file mode 100644 index 000000000..f6355852b Binary files /dev/null and b/week5/images/image1.png differ diff --git a/week5/images/image2.png b/week5/images/image2.png new file mode 100644 index 000000000..b9a3c674a Binary files /dev/null and b/week5/images/image2.png differ diff --git a/week5/images/image3.png b/week5/images/image3.png new file mode 100644 index 000000000..986a39679 Binary files /dev/null and b/week5/images/image3.png differ diff --git a/week5/images/image4.png b/week5/images/image4.png new file mode 100644 index 000000000..762bdc960 Binary files /dev/null and b/week5/images/image4.png differ diff --git a/week5/images/instagram.svg b/week5/images/instagram.svg new file mode 100644 index 000000000..0b9337b07 --- /dev/null +++ b/week5/images/instagram.svg @@ -0,0 +1,3 @@ + + + diff --git a/week5/images/kakao.svg b/week5/images/kakao.svg new file mode 100644 index 000000000..778fe9461 --- /dev/null +++ b/week5/images/kakao.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/week5/images/logo.svg b/week5/images/logo.svg new file mode 100644 index 000000000..282022090 --- /dev/null +++ b/week5/images/logo.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/week5/images/twitter.svg b/week5/images/twitter.svg new file mode 100644 index 000000000..14a6069a1 --- /dev/null +++ b/week5/images/twitter.svg @@ -0,0 +1,3 @@ + + + diff --git a/week5/images/youtube.svg b/week5/images/youtube.svg new file mode 100644 index 000000000..28ed0e8ba --- /dev/null +++ b/week5/images/youtube.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/week5/index.html b/week5/index.html new file mode 100644 index 000000000..e8019fb11 --- /dev/null +++ b/week5/index.html @@ -0,0 +1,131 @@ + + + + + Linkbrary + + + + + + + + + + + + + + +
+
+

+ 세상의 모든 정보를 +
+ 쉽게 저장하고 관리해 보세요 +

+ + 링크 추가하기 + + Linkbrary 서비스 소개 +
+
+
+
+

+ 원하는 링크를 저장하세요 +

+

+ 나중에 읽고 싶은 글, 다시 보고 싶은 영상, +
+ 사고 싶은 옷, 기억하고 싶은 모든 것을 +
+ 한 공간에 저장하세요. +

+ 링크의 내용이 담긴 카드들 +
+
+

+ 링크를 폴더로 + 관리하세요 +

+

+ 나만의 폴더를 무제한으로 만들고 +
+ 다양하게 활용할 수 있습니다. +

+ 폴더 이름 변경 기능 +
+
+

+ 저장한 링크를 + 공유해 보세요 +

+

+ 여러 링크를 폴더에 담고 공유할 수 있습니다. 가족, 친구, 동료들에게 쉽고 빠르게 링크를 + 공유해 보세요. +

+ 폴더 공유 기능 +
+
+

+ 저장한 링크를 + 검색해 보세요 +

+

중요한 정보들을 검색으로 쉽게 찾아보세요.

+ 링크 검색 기능 +
+
+ + + diff --git a/week5/privacy.html b/week5/privacy.html new file mode 100644 index 000000000..abf71aaaf --- /dev/null +++ b/week5/privacy.html @@ -0,0 +1,7 @@ + + + + + Privacy + + diff --git a/week5/signin.html b/week5/signin.html new file mode 100644 index 000000000..6708a3807 --- /dev/null +++ b/week5/signin.html @@ -0,0 +1,59 @@ + + + + + 로그인 + + + + + +
+ + + +

+ 회원이 아니신가요?회원 가입하기 +

+
+
+ +
+
+ + +

+
+
+ + +

+ +
+
+ + +
+ 소셜 로그인 + +
+
+ + + diff --git a/week5/signup.html b/week5/signup.html new file mode 100644 index 000000000..595e8f5f4 --- /dev/null +++ b/week5/signup.html @@ -0,0 +1,67 @@ + + + + + 회원가입 + + + + + +
+ + + +

+ 이미 회원이신가요?로그인 하기 +

+
+
+
+
+
+ + +

+
+
+ + +

+ +
+
+ + +

+ +
+
+ +
+
+ 다른 방식으로 가입하기 + +
+
+ + + diff --git a/week5/src/colors.css b/week5/src/colors.css new file mode 100644 index 000000000..e13967247 --- /dev/null +++ b/week5/src/colors.css @@ -0,0 +1,13 @@ +:root { + --primary: #6d6afe; + --red: #ff5b56; + --black: #111322; + --white: #ffffff; + + --gray100: #373740; + --gray60: #9fa6b2; + --gray20: #ccd5e3; + --gray10: #e7effb; + + --background: #f0f6ff; +} diff --git a/week5/src/global.css b/week5/src/global.css new file mode 100644 index 000000000..f20ab4ae7 --- /dev/null +++ b/week5/src/global.css @@ -0,0 +1,2 @@ +@import "./colors.css"; +@import "./reset.css"; diff --git a/week5/src/landing.css b/week5/src/landing.css new file mode 100644 index 000000000..5521d673c --- /dev/null +++ b/week5/src/landing.css @@ -0,0 +1,337 @@ +@import "./global.css"; + +body { + display: flex; + flex-direction: column; + align-items: center; +} + +nav { + display: flex; + justify-content: center; + position: sticky; + top: 0; + width: 100%; + background-color: #edf7ff; +} + +.gnb { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + height: 6.3rem; + padding: 0 3.2rem; + + @media (min-width: 768px) { + height: 9.4rem; + max-width: 86.3rem; + } + + @media (min-width: 1200px) { + height: 9.4rem; + max-width: 192rem; + padding: 0 20rem; + } +} + +.logo { + height: 1.6rem; + + @media (min-width: 768px) { + height: 2.4rem; + } +} + +header { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + background-color: #edf7ff; +} + +.hero-header { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 2.4rem; + padding: 2.8rem 3.2rem 0; + + @media (min-width: 768px) { + padding: 3.9rem 0 0; + row-gap: 4rem; + } + + @media (min-width: 1200px) { + padding: 7rem 0 0; + } +} + +.cta { + display: flex; + justify-content: center; + align-items: center; + height: 3.7rem; + cursor: pointer; + background-image: linear-gradient(135deg, var(--primary) 0%, #6ae3fe 100%); + border-radius: 0.8rem; + color: #f5f5f5; + font-size: 1.4rem; + font-weight: 600; + + @media (min-width: 768px) { + height: 5.4rem; + border-radius: 0.8rem; + font-size: 1.8rem; + } +} + +.cta-short { + width: 8rem; + + @media (min-width: 768px) { + width: 12.8rem; + } +} + +.cta-long { + width: 20rem; + + @media (min-width: 768px) { + width: 35rem; + } +} + +.slogan { + width: 24rem; + text-align: center; + font-size: 3.2rem; + line-height: 131.25%; + font-weight: 700; + + @media (min-width: 768px) { + width: 48rem; + font-size: 6.4rem; + line-height: 125%; + } + + @media (min-width: 1200px) { + width: 80rem; + } +} + +.slogan-gradient { + background-image: linear-gradient(119deg, var(--primary) 0%, #ff9f9f 100%); +} + +.background-clip-text { + background-clip: text; + -webkit-background-clip: text; + color: transparent; +} + +.hero-image { + width: 100%; + height: fit-content; + @media (min-width: 768px) { + width: 69.8rem; + height: 34.3rem; + } + + @media (min-width: 1200px) { + width: 120rem; + height: 59rem; + } +} + +article { + padding-bottom: 4rem; + + @media (min-width: 768px) { + padding-top: 3rem; + padding-bottom: 12rem; + } + + @media (min-width: 1200px) { + padding-top: 7rem; + } +} + +section { + display: grid; + justify-content: center; + row-gap: 1.6rem; + width: 100%; + height: fit-content; + padding: 4rem 3.2rem; + + @media (min-width: 768px) { + height: 41.5rem; + column-gap: 5.1rem; + row-gap: 1rem; + padding: 5rem 0; + } + + @media (min-width: 1200px) { + height: 55rem; + column-gap: 15.7rem; + } +} + +section:nth-of-type(odd) { + grid-template-areas: + "title" + "image" + "description"; + + @media (min-width: 768px) { + grid-template-areas: + ". image" + "title image" + "description image" + ". image"; + grid-template-columns: 26.2rem 38.5rem; + } + + @media (min-width: 1200px) { + grid-template-columns: 29.1rem 55rem; + } +} + +section:nth-of-type(even) { + grid-template: + "title" + "image" + "description"; + + @media (min-width: 768px) { + grid-template-areas: + "image ." + "image title" + "image description" + "image ."; + grid-template-columns: 38.5rem 26.2rem; + } + + @media (min-width: 1200px) { + grid-template-columns: 55rem 29.1rem; + } +} + +.title { + grid-area: title; + font-weight: 700; + letter-spacing: -0.03rem; + font-size: 2.4rem; + + @media (min-width: 768px) { + font-size: 4.8rem; + line-height: 5.8rem; + } +} + +.title-1-gradient { + background-image: linear-gradient(117deg, #fe8a8a 2.29%, #a4ceff 100%); +} +.title-2-gradient { + background-image: linear-gradient(304deg, #6fbaff 0%, #ffd88b 100%); +} +.title-3-gradient { + background-image: linear-gradient(133deg, #2945c7 0%, #dbe1f8 100%); +} +.title-4-gradient { + background-image: linear-gradient(310deg, #fe578f 0%, #68e8f9 100%); +} + +.description { + grid-area: description; + font-size: 1.5rem; + font-weight: 500; + color: #6b6b6b; + line-height: 150%; + + @media (min-width: 768px) { + font-size: 1.6rem; + } +} + +.line-break-tablet-desktop { + display: none; + + @media (min-width: 768px) { + display: inline; + } +} + +.content-image { + grid-area: image; + width: 100%; + padding-top: 0.4rem; + + @media (min-width: 768px) { + width: 38.5rem; + height: 31.5rem; + padding-top: 0; + } + + @media (min-width: 1200px) { + width: 55rem; + height: 45rem; + } +} + +footer { + display: flex; + justify-content: center; + width: 100%; + height: 16rem; + background-color: var(--black); +} + +.footer-box { + display: grid; + justify-content: space-between; + grid-template: + "footer-links sns" + ". ." 1fr + "copyright ."; + width: 100%; + padding: 3.2rem; + + @media (min-width: 768px) { + display: flex; + justify-content: space-between; + grid-template: "copyright footer-links sns"; + height: fit-content; + max-width: 192rem; + padding: 3.2rem 10.4rem 0; + } +} + +.copyright { + grid-area: copyright; + color: #676767; + font-family: Arial; + font-size: 1.6rem; +} + +.footer-links { + grid-area: footer-links; + display: flex; + column-gap: 3rem; + padding-right: 1.8rem; +} + +.footer-link { + color: #cfcfcf; + font-family: Arial; + font-size: 1.6rem; +} + +.sns { + grid-area: sns; + display: flex; + column-gap: 1.2rem; + height: 2rem; +} diff --git a/week5/src/reset.css b/week5/src/reset.css new file mode 100644 index 000000000..830b517e4 --- /dev/null +++ b/week5/src/reset.css @@ -0,0 +1,30 @@ +/* user agent stylesheet 초기화 */ + +* { + box-sizing: border-box; + margin: 0; + font-family: "Pretendard"; + word-break: keep-all; +} + +html, +body { + font-size: 62.5%; +} + +a { + color: inherit; + text-decoration: none; + cursor: pointer; +} + +input:focus { + outline: none; +} + +button { + border: none; + padding: unset; + background-color: unset; + cursor: pointer; +} diff --git a/week5/src/sign.css b/week5/src/sign.css new file mode 100644 index 000000000..25caf712b --- /dev/null +++ b/week5/src/sign.css @@ -0,0 +1,185 @@ +@import "./global.css"; + +body { + background-color: var(--background); + display: flex; + flex-direction: column; + align-items: center; + row-gap: 3rem; + padding: 12rem 3.2rem 0; + + @media (min-width: 768px) { + padding: 20rem 0 0; + } + + @media (min-width: 1200px) { + padding: 23.8rem 0 0; + } +} + +header { + display: flex; + flex-direction: column; + row-gap: 1.6rem; +} + +.logo-link { + display: flex; + justify-content: center; +} + +.header-logo { + height: 3.8rem; +} + +.header-message { + display: flex; + column-gap: 0.8rem; + font-size: 1.6rem; + font-weight: 400; + line-height: 150%; +} + +.header-link { + height: fit-content; + font-size: 1.6rem; + font-weight: 600; + line-height: normal; + color: var(--primary); + border-bottom: 0.1rem solid var(--primary); +} + +.sign-box { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 3.2rem; + width: 100%; + max-width: 40rem; + + @media (min-width: 768px) { + width: 40rem; + } +} + +.sign-form { + display: flex; + flex-direction: column; + row-gap: 3rem; + width: 100%; +} + +.sign-inputs { + display: flex; + flex-direction: column; + row-gap: 2.4rem; +} + +.sign-input-box { + display: flex; + flex-direction: column; + row-gap: 1.2rem; +} + +.sign-password { + position: relative; +} + +.sign-input-label { + font-size: 1.4rem; + font-weight: 400; +} + +.sign-input { + padding: 1.8rem 1.5rem; + border-radius: 0.8rem; + border: 0.1rem solid var(--gray20); + font-size: 1.6rem; + line-height: 150%; +} + +.sign-input:focus { + border-color: var(--primary); +} + +.sign-input.sign-input-error { + border-color: var(--red); +} + +.sign-input.sign-input-error:focus { + border-color: var(--red); +} + +.error-message { + display: none; + margin-top: -0.4rem; + font-size: 1.4rem; + font-weight: 400; + color: var(--red); +} + +.error-message.error-message-on { + display: inline-block; +} + +.eye-button { + position: absolute; + top: 5.1rem; + right: 1.5rem; + width: 1.6rem; + height: 1.6rem; +} + +.cta { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 5.4rem; + background-image: linear-gradient(135deg, var(--primary) 0%, #6ae3fe 100%); + border-radius: 0.8rem; + color: #f5f5f5; + font-size: 1.8rem; + font-weight: 600; +} + +.sns-box { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + padding: 1.2rem 2.4rem; + border-radius: 0.8rem; + border: 0.1rem solid var(--gray20); + background-color: var(--gray10); +} + +.sns-links { + display: flex; + column-gap: 1.6rem; +} + +.sns-link { + display: flex; + justify-content: center; + align-items: center; + width: 4.2rem; + height: 4.2rem; + border-radius: 50%; +} + +.sns-text { + font-size: 1.4rem; + font-weight: 400; + color: var(--gray100); +} + +.google-link { + background-color: var(--white); + border: 0.1rem solid #d3d4dd; +} + +.kakao-link { + padding-top: 0.2rem; + background-color: #f5e14b; +} diff --git a/week5/src/signin.js b/week5/src/signin.js new file mode 100644 index 000000000..c1bc21d9f --- /dev/null +++ b/week5/src/signin.js @@ -0,0 +1,84 @@ +import { + setInputError, + removeInputError, + isEmailValid, + togglePassword, + TEST_USER, +} from "./utils.js"; + +window.onload = function() { + if(localStorage.accessToken) location.href = './folder.html'; +} + +const emailInput = document.querySelector("#email"); +const emailErrorMessage = document.querySelector("#email-error-message"); +emailInput.addEventListener("focusout", (event) => validateEmailInput(event.target.value)); +function validateEmailInput(email) { + if (email === "") { + setInputError({ input: emailInput, errorMessage: emailErrorMessage }, "이메일을 입력해주세요."); + return; + } + if (!isEmailValid(email)) { + setInputError( + { input: emailInput, errorMessage: emailErrorMessage }, + "올바른 이메일 주소가 아닙니다." + ); + return; + } + removeInputError({ input: emailInput, errorMessage: emailErrorMessage }); +} + +const passwordInput = document.querySelector("#password"); +const passwordErrorMessage = document.querySelector("#password-error-message"); +passwordInput.addEventListener("focusout", (event) => validatePasswordInput(event.target.value)); +function validatePasswordInput(password) { + if (password === "") { + setInputError( + { input: passwordInput, errorMessage: passwordErrorMessage }, + "비밀번호를 입력해주세요." + ); + return; + } + removeInputError({ input: passwordInput, errorMessage: passwordErrorMessage }); +} + +const passwordToggleButton = document.querySelector("#password-toggle"); +passwordToggleButton.addEventListener("click", () => + togglePassword(passwordInput, passwordToggleButton) +); + +let token = null; + +const signForm = document.querySelector("#form"); +signForm.addEventListener("submit", submitForm); +function submitForm(event) { + event.preventDefault(); + + const isTestUser = emailInput.value === TEST_USER.email && passwordInput.value === TEST_USER.password; + const user = { + email: emailInput.value, + password: passwordInput.value, + }; + + if (isTestUser) { + fetch('https://bootcamp-api.codeit.kr/api/sign-in', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(user), + }) + .then((response) => response.json()) + .then((result) => { + token = result.data.accessToken; + localStorage.setItem('accessToken', token); + location.href = "./folder.html"; + }); + return; + }; + setInputError({ input: emailInput, errorMessage: emailErrorMessage }, "이메일을 확인해주세요."); + setInputError( + { input: passwordInput, errorMessage: passwordErrorMessage }, + "비밀번호를 확인해주세요." + ); +} diff --git a/week5/src/signup.js b/week5/src/signup.js new file mode 100644 index 000000000..07c8eb927 --- /dev/null +++ b/week5/src/signup.js @@ -0,0 +1,124 @@ +import { + setInputError, + removeInputError, + isEmailValid, + isPasswordValid, + togglePassword, +} from "./utils.js"; + +const emailInput = document.querySelector("#email"); +const emailErrorMessage = document.querySelector("#email-error-message"); +emailInput.addEventListener("focusout", (event) => validateEmailInput(event.target.value)); +async function validateEmailInput(email) { + if (email === "") { + setInputError({ input: emailInput, errorMessage: emailErrorMessage }, "이메일을 입력해주세요."); + return false; + } + if (!isEmailValid(email)) { + setInputError( + { input: emailInput, errorMessage: emailErrorMessage }, + "올바른 이메일 주소가 아닙니다." + ); + return false; + } + await fetch('https://bootcamp-api.codeit.kr/api/check-email', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + email: emailInput.value, + }), + }) + .then((response) => { + if(!response.ok) { + setInputError( + { input: emailInput, errorMessage: emailErrorMessage }, + "이미 사용 중인 이메일입니다." + ); + return false; + } + removeInputError({ input: emailInput, errorMessage: emailErrorMessage }); + return true; + }) +} + +const passwordInput = document.querySelector("#password"); +const passwordErrorMessage = document.querySelector("#password-error-message"); +passwordInput.addEventListener("focusout", (event) => validatePasswordInput(event.target.value)); + +function validatePasswordInput(password) { + if (password === "" || !isPasswordValid(password)) { + setInputError( + { input: passwordInput, errorMessage: passwordErrorMessage }, + "비밀번호는 영문, 숫자 조합 8자 이상 입력해 주세요." + ); + return false; + } + removeInputError({ input: passwordInput, errorMessage: passwordErrorMessage }); + return true; +} + +const confirmPasswordInput = document.querySelector("#confirm-password"); +const confirmPasswordErrorMessage = document.querySelector("#confirm-password-error-message"); +confirmPasswordInput.addEventListener("focusout", (event) => + validateConfirmPasswordInput(event.target.value) +); +function validateConfirmPasswordInput(confirmPassword) { + if (confirmPassword === "" || !isPasswordValid(confirmPassword)) { + setInputError( + { input: confirmPasswordInput, errorMessage: confirmPasswordErrorMessage }, + "비밀번호는 영문, 숫자 조합 8자 이상 입력해 주세요." + ); + return false; + } + if (passwordInput.value !== confirmPassword) { + setInputError( + { input: confirmPasswordInput, errorMessage: confirmPasswordErrorMessage }, + "비밀번호가 일치하지 않아요." + ); + return false; + } + removeInputError({ input: confirmPasswordInput, errorMessage: confirmPasswordErrorMessage }); + return true; +} + +const passwordToggleButton = document.querySelector("#password-toggle"); +passwordToggleButton.addEventListener("click", () => + togglePassword(passwordInput, passwordToggleButton) +); + +const confirmPasswordToggleButton = document.querySelector("#confirm-password-toggle"); +confirmPasswordToggleButton.addEventListener("click", () => + togglePassword(confirmPasswordInput, confirmPasswordToggleButton) +); + +let token = null; + +const signForm = document.querySelector("#form"); +signForm.addEventListener("submit", submitForm); +function submitForm(event) { + event.preventDefault(); + + const isEmailInputValid = validateEmailInput(emailInput.value); + const isPasswordInputValid = validatePasswordInput(passwordInput.value); + const isConfirmPasswordInputValid = validateConfirmPasswordInput(confirmPasswordInput.value); + if (isEmailInputValid && isPasswordInputValid && isConfirmPasswordInputValid) { + fetch('https://bootcamp-api.codeit.kr/api/sign-up', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + email: emailInput.value, + password: passwordInput.value, + }), + }) + .then((response) => response.json()) + .then((result) => { + token = result.data.accessToken; + localStorage.setItem('accessToken', token); + location.href = "./folder.html"; + }); + } +} diff --git a/week5/src/utils.js b/week5/src/utils.js new file mode 100644 index 000000000..538b7461e --- /dev/null +++ b/week5/src/utils.js @@ -0,0 +1,40 @@ +const SIGN_INPUT_ERROR_CLASSNAME = "sign-input-error"; +const ERROR_MESSAGE_CLASSNAME = "error-message-on"; +const EMAIL_REGEX = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/g; + +export function setInputError(elements, message) { + elements.input.className += ` ${SIGN_INPUT_ERROR_CLASSNAME}`; + elements.errorMessage.className += ` ${ERROR_MESSAGE_CLASSNAME}`; + elements.errorMessage.textContent = message; +} + +export function removeInputError(elements) { + elements.input.classList.remove(SIGN_INPUT_ERROR_CLASSNAME); + elements.errorMessage.classList.remove(ERROR_MESSAGE_CLASSNAME); + elements.errorMessage.textContent = ""; +} + +export function isEmailValid(email) { + return new RegExp(EMAIL_REGEX).test(email); +} + +export function isPasswordValid(password) { + const isEightLettersOrMore = password.length >= 8; + const hasNumberAndCharacter = password.match(/[0-9]/g) && password.match(/[a-zA-Z]/gi); + return isEightLettersOrMore && hasNumberAndCharacter; +} + +export function togglePassword(input, toggleButton) { + if (input.getAttribute("type") === "password") { + input.setAttribute("type", "text"); + toggleButton.getElementsByTagName("img")[0].setAttribute("src", "./images/eye-on.svg"); + return; + } + input.setAttribute("type", "password"); + toggleButton.getElementsByTagName("img")[0].setAttribute("src", "./images/eye-off.svg"); +} + +export const TEST_USER = { + email: "test@codeit.com", + password: "sprint101", +};