diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 0000000..57cccd7
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,22 @@
+module.exports = {
+ "env": {
+ "node": true,
+ "es6": true,
+ "browser": true
+ },
+ "extends": "eslint:recommended",
+ "globals": {
+ "Atomics": "readonly",
+ "SharedArrayBuffer": "readonly"
+ },
+ "parserOptions": {
+ "ecmaVersion": 2018,
+ "sourceType": "module"
+ },
+ "rules": {
+ "semi": [2, "never"],
+ "no-use-before-define": "off",
+ "no-underscore-dangle": "off",
+ "no-shadow": "off"
+ }
+}
diff --git a/mission003/eastjun/css/index.css b/mission003/eastjun/css/index.css
new file mode 100644
index 0000000..714623f
--- /dev/null
+++ b/mission003/eastjun/css/index.css
@@ -0,0 +1,178 @@
+/*mdc override*/
+:root {
+ --mdc-theme-primary: #333;
+}
+
+.mdc-button__raised:not(:disabled), .mdc-button__unelevated:not(:disabled) {
+ background-color: var(--mdc-theme-primary,#333);
+}
+
+.mdc-dialog__surface {
+ width: 400px;
+}
+
+.mdc-card__actions {
+ padding: 0;
+}
+
+.mdc-text-field {
+ margin: 10px auto;
+}
+
+.mdc-dialog__actions {
+ padding-right: 0;
+}
+/*mdc override*/
+
+body {
+ margin: 0;
+}
+
+.background-img {
+ background-size: cover;
+ background-image: url("https://user-images.githubusercontent.com/56821976/71782829-d68f7000-3021-11ea-833d-9e286c629503.jpg");
+ position: fixed;
+ width: 100%;
+ height: 50vh;
+}
+
+.dark-overlay {
+ width: 100%;
+ background-color: rgba(0,0,0,.2);
+ height: 50vh;
+ z-index: 10;
+}
+
+.signup-card {
+ max-width: 400px;
+ margin: 100px auto;
+}
+
+.signup-card h2{
+ margin-top: 32px;
+}
+
+form.signup {
+ padding: 24px;
+}
+
+.text-center {
+ text-align: center;
+}
+
+.hidden {
+ display: none;
+}
+
+.bullet-o{
+ display: flex;
+ flex-flow: row wrap;
+ justify-content: center;
+}
+
+.bullet-o > div{
+ height: 15px;
+ width: 15px;
+ margin: 20px 10px;
+ border-radius: 100px;
+ z-index: 2;
+}
+
+.bullet{
+ background-color: lightgrey;
+
+}
+
+.bullet-active{
+ background-color: #333 !important;
+}
+
+.bullet-line{
+ opacity: 0.3;
+ background: lightgrey;
+ height: 3px;
+ width: 70px;
+ display: block;
+ left: 50%;
+ margin-top: -29px;
+ margin-left: -35px;
+ position: absolute;
+ z-index: 1;
+}
+
+.pdd-top-0 {
+ padding-top: 0px !important;
+}
+
+.width-100 {
+ width: 100%;
+}
+
+.flex-container {
+ display: flex;
+}
+
+.mrg-right-10 {
+ margin-right: 10px;
+}
+
+#add-family-button {
+ display: block;
+ margin: 0 auto;
+}
+
+.stepper {
+ position: relative;
+}
+
+.coffee-emoji {
+ font-size: 100px;
+ margin: 40px 0 70px 0;
+}
+
+.margin-center {
+ margin: 0% auto;
+}
+
+.my-info-view-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+}
+
+.text-bold {
+ font-weight: bold;
+}
+
+.my-info {
+ padding: 30px;
+}
+
+.my-info-card {
+ width: 400px;
+ padding: 30px;
+ position: relative;
+}
+
+#my-info-edit-button {
+ top: 16px;
+ right: 16px;
+ position: absolute;
+}
+
+.no-margin {
+ margin: 0px;
+}
+
+.margin-16 {
+ margin: 16px;
+}
+
+.margin-8 {
+ margin: 8px;
+}
+
+.text-gray {
+ color: gray;
+}
diff --git a/mission003/eastjun/index.html b/mission003/eastjun/index.html
new file mode 100644
index 0000000..d09f2a0
--- /dev/null
+++ b/mission003/eastjun/index.html
@@ -0,0 +1,134 @@
+
+
+
+
+ mission003
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mission003/eastjun/js/App.js b/mission003/eastjun/js/App.js
new file mode 100644
index 0000000..d07dc0d
--- /dev/null
+++ b/mission003/eastjun/js/App.js
@@ -0,0 +1,18 @@
+import Form from './components/Form.js'
+import User from './components/User.js'
+function FormApp() {
+ this.user = new User()
+ this.mdcInit = () => mdc.autoInit()
+
+ this.init = () => {
+ new Form({
+ mdcInit: this.mdcInit,
+ user: this.user
+ })
+ this.mdcInit()
+ }
+
+ this.init()
+}
+
+new FormApp()
diff --git a/mission003/eastjun/js/components/Family.js b/mission003/eastjun/js/components/Family.js
new file mode 100644
index 0000000..5b4d1fa
--- /dev/null
+++ b/mission003/eastjun/js/components/Family.js
@@ -0,0 +1,8 @@
+import { familyFormTemplate } from '../utils/templates.js'
+
+export default function Family() {
+ this.name = ''
+ this.relations = ''
+
+ this.render = (index) => familyFormTemplate(index)
+}
diff --git a/mission003/eastjun/js/components/Form.js b/mission003/eastjun/js/components/Form.js
new file mode 100644
index 0000000..e776f98
--- /dev/null
+++ b/mission003/eastjun/js/components/Form.js
@@ -0,0 +1,199 @@
+import validator from '../utils/validator.js'
+import { familyFormField } from '../utils/constants.js'
+import Family from './Family.js'
+import { myInfoViewTemplate, familyViewTemplate } from '../utils/templates.js'
+
+export default function Form({ mdcInit, user }) {
+ this.isAgreement = false
+ this.dialog = new mdc.dialog.MDCDialog(document.querySelector('.mdc-dialog'))
+
+ const $formTitle = document.querySelector('#my-dialog-title')
+
+ const addFamilyFieldTemplate = (event) => {
+ event.preventDefault()
+ const family = new Family()
+ user.addFamily(family)
+ const lastIndex = user.family.length - 1
+ const $familyForm = document.querySelector('.user-family-forms')
+ $familyForm.insertAdjacentHTML('beforeend', family.render(lastIndex))
+ mdcInit()
+ }
+
+ const isFilledForm = () => {
+ const $step1CompleteButton = document.querySelector('.step1-complete-btn')
+
+ if (this.isAgreement && user.isBasicInfoNotEmpty()) {
+ $step1CompleteButton.classList.remove('hidden')
+ $step1CompleteButton.addEventListener('click', activeStep2)
+ return
+ }
+ $step1CompleteButton.classList.add('hidden')
+ }
+
+ const step1 = () => {
+ const $step1TabClassList = document.querySelector('#step1').classList
+ const $bullet1ClassList = document.querySelector('.bullet1').classList
+ const $agreementClassList = document.querySelector('.privacy-policy-agree-container').classList
+
+ const active = () => {
+ $step1TabClassList.remove('hidden')
+ $bullet1ClassList.add('bullet-active')
+ }
+
+ const inActive = () => {
+ $step1TabClassList.add('hidden')
+ $bullet1ClassList.remove('bullet-active')
+ }
+
+ const togglePrivacyCheckbox = (value) => {
+ validator.isEmptyString(value) ? $agreementClassList.add('hidden') : $agreementClassList.remove('hidden')
+ }
+
+ return {
+ active,
+ inActive,
+ togglePrivacyCheckbox
+ }
+ }
+
+ const step2 = () => {
+ const $step2TabClassList = document.querySelector('#step2').classList
+ const $bullet2ClassList = document.querySelector('.bullet2').classList
+
+ const active = () => {
+ $step2TabClassList.remove('hidden')
+ $bullet2ClassList.add('bullet-active')
+ }
+
+ const inActive = () => {
+ $step2TabClassList.add('hidden')
+ $bullet2ClassList.remove('bullet-active')
+ }
+
+ return {
+ active,
+ inActive
+ }
+ }
+
+ const step3 = () => {
+ const $step3TabClassList = document.querySelector('#step3').classList
+ const $bullet3ClassList = document.querySelector('.bullet3').classList
+
+ const active = () => {
+ $step3TabClassList.remove('hidden')
+ $bullet3ClassList.add('bullet-active')
+ }
+
+ const inActive = () => {
+ $step3TabClassList.add('hidden')
+ $bullet3ClassList.remove('bullet-active')
+ }
+
+ const renderViewCard = () => {
+ document.querySelector('#my-info-view-card').innerHTML = myInfoViewTemplate(user)
+ const myFamilyInfoTemplate = user.family.map((member) => familyViewTemplate(member)).join('')
+ document.querySelector('.my-family-list-container').innerHTML = myFamilyInfoTemplate
+ const $myInfoEditButton = document.querySelector('#my-info-edit-button')
+ $myInfoEditButton.addEventListener('click', showEditDialog)
+ }
+
+ const showEditDialog = () => {
+ activeStep1()
+ this.dialog.open()
+ }
+
+ return {
+ active,
+ inActive,
+ renderViewCard
+ }
+ }
+
+ const activeStep1 = () => {
+ step3().inActive()
+ step2().inActive()
+ step1().active()
+ }
+
+ const activeStep2 = (event) => {
+ event.preventDefault()
+ step1().inActive()
+ step2().active()
+ $formTitle.innerText = '가족 구성원 입력'
+ }
+
+ const activeStep3 = (event) => {
+ event.preventDefault()
+ step2().inActive()
+ step3().active()
+ step3().renderViewCard()
+ $formTitle.innerText = '업데이트 완료'
+ }
+
+ this.update = {
+ name: (event) => {
+ user.update.name(event.target.value)
+ isFilledForm()
+ },
+ phone: (event) => {
+ const phone = event.target.value
+ user.update.phone(event.target.value)
+ step1().togglePrivacyCheckbox(phone)
+ isFilledForm()
+ },
+ email: (event) => {
+ const email = event.target.value
+ user.update.email(email)
+ step1().togglePrivacyCheckbox(email)
+ isFilledForm()
+ },
+ password: (event) => {
+ user.update.password(event.target.value)
+ isFilledForm()
+ },
+ introduce: (event) => {
+ user.update.introduce(event.target.value)
+ isFilledForm()
+ },
+ agreement: (event) => {
+ this.isAgreement = event.target.checked
+ isFilledForm()
+ },
+ family: (event) => {
+ const index = event.target.closest('.user-family-form-fields').dataset.index
+ const inputName = event.target.name
+ const value = event.target.value
+
+ switch (inputName) {
+ case familyFormField.NAME:
+ user.update.family.name(index, value)
+ break
+ case familyFormField.RELATIONS:
+ user.update.family.relations(index, value)
+ break
+ default:
+ break
+ }
+ }
+ }
+
+ this.initEventListener = () => {
+ document.querySelector('#name').addEventListener('keyup', this.update.name)
+ document.querySelector('#phone').addEventListener('keyup', this.update.phone)
+ document.querySelector('#email').addEventListener('keyup', this.update.email)
+ document.querySelector('#password').addEventListener('keyup', this.update.password)
+ document.querySelector('#introduce').addEventListener('keyup', this.update.introduce)
+ document.querySelector('#agreement').addEventListener('click', this.update.agreement)
+ document.querySelector('#add-family-button').addEventListener('click', addFamilyFieldTemplate)
+ document.querySelector('.user-family-forms').addEventListener('keyup', this.update.family)
+ document.querySelector('form.signup').addEventListener('submit', activeStep3)
+ }
+
+ this.init = () => {
+ this.dialog.open()
+ this.initEventListener()
+ }
+
+ this.init()
+}
diff --git a/mission003/eastjun/js/components/User.js b/mission003/eastjun/js/components/User.js
new file mode 100644
index 0000000..10d045f
--- /dev/null
+++ b/mission003/eastjun/js/components/User.js
@@ -0,0 +1,34 @@
+import validator from '../utils/validator.js'
+
+export default function User() {
+ this.name = ''
+ this.email = ''
+ this.phone = ''
+ this.password = ''
+ this.introduce = ''
+ this.family = []
+
+ this.update = {
+ name: (name) => this.name = name,
+ email: (email) => this.email = email,
+ phone: (phone) => this.phone = phone,
+ password: (password) => this.password = password,
+ introduce: (introduce) => this.introduce = introduce,
+ family: {
+ name: (index, name) => this.family[index].name = name,
+ relations: (index, relations) => this.family[index].relations = relations
+ }
+ }
+
+ this.addFamily = (member) => this.family.push(member)
+
+ this.isBasicInfoNotEmpty = () => {
+ if (validator.isNotEmptyString(this.name) &&
+ validator.isNotEmptyString(this.email) &&
+ validator.isNotEmptyString(this.phone) &&
+ validator.isNotEmptyString(this.password) &&
+ validator.isNotEmptyString(this.introduce)) {
+ return true
+ }
+ }
+}
diff --git a/mission003/eastjun/js/utils/constants.js b/mission003/eastjun/js/utils/constants.js
new file mode 100644
index 0000000..5578bad
--- /dev/null
+++ b/mission003/eastjun/js/utils/constants.js
@@ -0,0 +1,5 @@
+const familyFormField = {
+ NAME: 'family-name',
+ RELATIONS: 'family-relations'
+}
+export { familyFormField }
diff --git a/mission003/eastjun/js/utils/templates.js b/mission003/eastjun/js/utils/templates.js
new file mode 100644
index 0000000..d5713b9
--- /dev/null
+++ b/mission003/eastjun/js/utils/templates.js
@@ -0,0 +1,56 @@
+const familyFormTemplate = (index) => `
+ `
+
+const familyViewTemplate = (family) => `
+
+ ${family.name} (${family.relations})
+
`
+
+const myInfoViewTemplate = (formFields) => `
+
+
+
+
+
나의 정보
+
+
+
이름
+
${formFields.name}
+
+
+
이메일
+
${formFields.email}
+
+
+
전화번호
+
${formFields.phone}
+
+
+
소개
+
${formFields.introduce}
+
+
+
+
+
+
`
+
+export {
+ familyFormTemplate,
+ myInfoViewTemplate,
+ familyViewTemplate
+}
diff --git a/mission003/eastjun/js/utils/validator.js b/mission003/eastjun/js/utils/validator.js
new file mode 100644
index 0000000..77b4bfa
--- /dev/null
+++ b/mission003/eastjun/js/utils/validator.js
@@ -0,0 +1,13 @@
+const validator = {
+ isString(str) {
+ return typeof str === 'string' || str instanceof String
+ },
+ isEmptyString(str) {
+ return (!str || 0 === str.length)
+ },
+ isNotEmptyString(str) {
+ return (this.isString(str) && str.length > 0)
+ },
+}
+
+export default validator
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..f5029c0
--- /dev/null
+++ b/package.json
@@ -0,0 +1,32 @@
+{
+ "name": "frontend",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/EastjunDev/frontend.git"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "bugs": {
+ "url": "https://github.com/EastjunDev/frontend/issues"
+ },
+ "homepage": "https://github.com/EastjunDev/frontend#readme",
+ "devDependencies": {
+ "babel-eslint": "^10.0.3",
+ "eslint": "^5.16.0",
+ "eslint-config-airbnb-base": "^14.0.0",
+ "eslint-config-standard": "^14.1.0",
+ "eslint-plugin-html": "^6.0.0",
+ "eslint-plugin-import": "^2.19.1",
+ "eslint-plugin-node": "^10.0.0",
+ "eslint-plugin-promise": "^4.2.1",
+ "eslint-plugin-standard": "^4.0.1",
+ "eslint-plugin-vue": "^6.0.1"
+ }
+}