diff --git a/README.md b/README.md index 39e30e9..ab1eef2 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,3 @@ # Weekly-Mission -## Week 3 - -### 필수 요구사항 - -#### 전체 - -- [x] 로고 클릭시 루트 페이지(“/”)로 이동해야 합니다. -- [x] 로그인 페이지, 회원가입 페이지 모두 로고위 상단 여백이 동일해야 합니다. -- [x] input 요소에 focus in 일 때, input 안에 문자열은 검정색이어야 하고, 파랑색 테두리가 생겨야 합니다. -- [x] input 요소에 focus out 일 때, input 안에 문자열과 테두리는 회색이어야 합니다. -- [x] SNS 아이콘들은 클릭 가능함을 확인할 수 있고, 클릭시 각각 “https://www.google.com/”, “https://www.kakaocorp.com/page/” 으로 이동합니다. - -#### 로그인페이지 - -- [x] 회원 가입하기는 클릭 가능함을 확인할 수 있고, 클릭시 “/signup” 페이지로 이동합니다. -- [x] 이메일 input에서 focus out 일 때, 값이 없을 경우 alert으로 “이메일을 입력해주세요.” 메세지를 보입니다. -- [x] 이메일 input에서 focus out 일 때, 값이 있고, 이메일 형식에 맞지 않을 경우 alert으로 “올바른 이메일 주소가 아닙니다.” 메세지를 보입니다. -- [x] 이메일: test@codeit.com, 비밀번호: codeit101 으로 로그인 시도할 경우, “/my-link” 페이지로 이동합니다. -- [x] 이외의 로그인 시도의 경우, “이메일과 비밀번호를 확인해주세요.” 메세지가 담긴 alert 을 띄워 주세요. -- [x] 로그인 버튼 클릭 또는 Enter키 입력으로 로그인 실행돼야 합니다. -- [x] 비밀번호 찾기는 클릭 가능함을 확인할 수 있고, 클릭시 “/forgot-password” 페이지로 이동합니다. - -#### 회원가입 페이지 - -- [x] 로그인 하기는 클릭 가능함을 확인할 수 있고, 클릭시 “/signin” 페이지로 이동합니다. -- [x] 이메일 input에서 focus out 일 때, 값이 없을 경우 alert으로 “이메일을 입력해주세요.” 메세지를 보입니다. -- [x] 이메일 input에서 focus out 일 때, 값이 있고, 이메일 형식에 맞지 않을 경우 alert으로 “올바른 이메일 주소가 아닙니다.” 메세지를 보입니다. -- [x] 이메일 input에서 focus out 일 때, input 값이 test@codeit.com 일 경우, alert으로 “이미 사용 중인 아이디입니다.” 메세지를 보입니다. -- [x] 비밀번호 input에서 focus out 일 때, 값이 없거나 문자열만 있거나 숫자만 있는 경우, alert으로 “비밀번호는 영문, 숫자 조합 8자 이상 입력해 주세요.” 메세지를 보입니다. -- [x] 회원가입을 실행할 경우, 문제가 있는 경우 문제가 있는 부분을 alert 메세지로 알립니다. -- [x] 이외의 유효한 회원가입 시도의 경우, “/my-link”로 이동합니다. -- [x] 회원가입 버튼 클릭 또는 Enter키 입력으로 회원가입 실행돼야 합니다. - -#### 모바일 크기 - -- [x] 375px 보다 작은 크기의 기기는 고려하지 않습니다. -- [x] 좌우 여백 32px 제외하고 내부 요소들이 너비를 모두 차지합니다. -- [x] 내부 요소들의 너비는 기기의 너비가 커질수록 커지지만 400px을 넘지 않습니다. - -### 선택 요구사항 - -#### 전체 - -- [x] 비밀번호 input 요소에 비밀번호를 확인할 수 있는 아이콘을 추가합니다. -- [x] 비밀번호를 확인할 수 있는 아이콘 클릭시 비밀번호의 문자열이 보이기도 하고, 가려지기도 합니다. +## Week 4 diff --git a/api/common.js b/api/common.js new file mode 100644 index 0000000..6341152 --- /dev/null +++ b/api/common.js @@ -0,0 +1 @@ +export const BASE_URL = "https://bootcamp-api.codeit.kr"; diff --git a/api/folder.api.js b/api/folder.api.js new file mode 100644 index 0000000..e6af84f --- /dev/null +++ b/api/folder.api.js @@ -0,0 +1,8 @@ +import { BASE_URL } from "./common.js"; + +const folderUrl = BASE_URL + "/api/sample/folder"; +export async function fetchFolderData() { + const response = await fetch(folderUrl); + const { data } = await response.json(); + return data; +} diff --git a/api/user.api.js b/api/user.api.js new file mode 100644 index 0000000..f41c7eb --- /dev/null +++ b/api/user.api.js @@ -0,0 +1,9 @@ +import { BASE_URL } from "./common.js"; + +const userUrl = BASE_URL + "/api/sample/user"; + +export async function fetchUserData() { + const response = await fetch(userUrl); + const { data } = await response.json(); + return data; +} diff --git a/components/card/card-component.css b/components/card/card-component.css new file mode 100644 index 0000000..a147b30 --- /dev/null +++ b/components/card/card-component.css @@ -0,0 +1,99 @@ +@import url("/static/css/global_style.css"); + +.card-container { + position: relative; + display: flex; + flex-direction: column; + filter: drop-shadow(0rem 0.5rem 2.5rem rgba(0, 0, 0, 0.08)); + border-radius: 2.5rem; + overflow: hidden; + width: 34rem; + height: 33.4rem; + background-color: var(--linkbrary-white); +} + +.card-container:hover .card-image { + transform: scale(1.2); +} + +.card-container:hover .card-info { + background-color: var(--library-white-smoke); +} + +.card-image { + height: 20rem; + object-fit: cover; + overflow: hidden; + display: block; +} + +.star-icon { + position: absolute; + top: 1.6rem; + right: 1.6rem; + z-index: 1; +} + +.card-info { + padding: 1.5rem 2rem; + position: relative; + z-index: 1; + height: 13.4rem; +} + +.card-info-head { + display: flex; + width: 100%; + justify-content: space-between; +} + +.card-update-time { + font-weight: 400; + font-size: 1.3rem; + line-height: 1.6rem; + color: #666666; +} + +.kebab-icon { + width: 2.1rem; + height: 1.7rem; +} + +.card-description { + font-weight: 500; + font-size: 1.6rem; + line-height: 2.4rem; + margin: 1rem auto; + + text-overflow: ellipsis; + overflow: hidden; + word-break: break-word; + + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; +} + +.card-date { + font-weight: 400; + font-size: 1.5rem; + line-height: 1.8rem; + + color: #333333; +} + +@media screen and (max-width: 767px) { + .card-container { + width: 32.5rem; + height: 32.7rem; + } + + .card-image { + width: 32.5rem; + } + + .card-info { + height: 13.5rem; + padding: 1.5rem 2rem; + } +} diff --git a/components/card/card-list-component.css b/components/card/card-list-component.css new file mode 100644 index 0000000..a8a3703 --- /dev/null +++ b/components/card/card-list-component.css @@ -0,0 +1,25 @@ +@import url("/static/css/global_style.css"); + +.card-list-container { + display: grid; + grid-template-columns: repeat(3, 1fr); + justify-items: center; + gap: 2rem; + + margin-bottom: 10rem; +} + +@media screen and (max-width: 1100px) { + .card-list-container { + grid-template-columns: repeat(2, 1fr); + } +} + +@media screen and (max-width: 767px) { + .card-list-container { + grid-template-columns: repeat(1, 1fr); + gap: 2.5rem; + margin-top: 1.2rem; + margin-bottom: 6rem; + } +} diff --git a/components/card/cardComponent.js b/components/card/cardComponent.js new file mode 100644 index 0000000..3639dc7 --- /dev/null +++ b/components/card/cardComponent.js @@ -0,0 +1,114 @@ +import { StarComponent } from "../star/starComponent.js"; +export class CardComponent extends HTMLElement { + #prop = null; + constructor() { + super(); + + this.shadow = this.attachShadow({ mode: "open" }); + } + + connectedCallback() { + this.render(); + } + + get prop() { + return this.#prop; + } + + set prop(newProp) { + // 타입체킹 로직 추후 추가 + if (typeof newProp !== "object" && newProp !== null) { + console.warn("올바르지 않은 형식의 데이터가 들어왔습니다."); + return; + } + if ( + typeof newProp.imageSrc !== "string" || + typeof newProp.description !== "string" || + typeof newProp.date !== "string" || + typeof newProp.url !== "string" + ) { + console.warn("올바르지 않은 형식의 데이터가 들어왔습니다."); + return; + } + this.#prop = newProp; + } + + calculateTimeDiff(dateString) { + const updatedDate = new Date(dateString); + const today = new Date(); + const timeDiff = today - updatedDate; + + const MINUTE = 60 * 1000; + const HOUR = MINUTE * 60; + const DAY = HOUR * 24; + const MONTH = DAY * 31; + const YEAR = DAY * 365; + + const timeUnits = [ + { value: YEAR, label: "year" }, + { value: MONTH, label: "month" }, + { value: DAY, label: "day" }, + { value: HOUR, label: "hour" }, + { value: MINUTE, label: "minute" }, + ]; + + for (let i = 0; i < timeUnits.length; i++) { + const { value, label } = timeUnits[i]; + + if (timeDiff < value) { + continue; + } + + const formattedTimeDiff = Math.floor(timeDiff / value); + + return ( + formattedTimeDiff + + " " + + label + + (formattedTimeDiff > 1 ? "s" : "") + + " ago" + ); + } + } + + parseDate(dateString) { + const date = new Date(dateString); + const year = date.getFullYear(); + const month = date.getMonth() + 1; + const day = date.getDate(); + return [year, month, day].join(". "); + } + + get template() { + return ` +
+ + +
+
+
${this.calculateTimeDiff( + this.prop.date + )}
+ +
+
${this.prop.description}
+
${this.parseDate(this.prop.date)}
+
+
+ `; + } + render() { + // CSS + const linkElem = document.createElement("link"); + linkElem.setAttribute("rel", "stylesheet"); + linkElem.setAttribute("href", "/components/card/card-component.css"); + this.shadow.appendChild(linkElem); + const cardComponent = document.createElement("template"); + cardComponent.innerHTML = this.template; + this.shadow.appendChild(cardComponent.content.cloneNode(true)); + + const starIcon = new StarComponent(); + this.shadow.querySelector(".star-icon").appendChild(starIcon); + } +} +customElements.define("card-component", CardComponent); diff --git a/components/card/cardListComponent.js b/components/card/cardListComponent.js new file mode 100644 index 0000000..802fa00 --- /dev/null +++ b/components/card/cardListComponent.js @@ -0,0 +1,56 @@ +import { CardComponent } from "./cardComponent.js"; + +class CardListComponent extends HTMLElement { + #prop = null; + + constructor() { + super(); + this.attachShadow({ mode: "open" }); + } + + connectedCallback() { + this.render(); + } + + get prop() { + return this.#prop; + } + + set prop(newProp) { + // type checking 추후 구현 + this.#prop = newProp; + this.renderCards(); + } + + createCard() { + const cardComponent = new CardComponent(); + return cardComponent; + } + + renderCards() { + this.prop.forEach((card) => { + const cardComponent = this.createCard(); + cardComponent.prop = { + imageSrc: card.imageSource ?? "/static/imgs/default-card-img.png", + description: card.description, + date: card.createdAt, + url: card.url, + }; + this.cardListContainer.appendChild(cardComponent); + }); + } + + render() { + const linkElem = document.createElement("link"); + linkElem.setAttribute("rel", "stylesheet"); + linkElem.setAttribute("href", "/components/card/card-list-component.css"); + this.shadowRoot.appendChild(linkElem); + + const cardListContainer = document.createElement("div"); + cardListContainer.classList.add("card-list-container"); + this.shadowRoot.appendChild(cardListContainer); + this.cardListContainer = cardListContainer; + } +} + +customElements.define("card-list-component", CardListComponent); diff --git a/components/folder-info/folder-info.css b/components/folder-info/folder-info.css new file mode 100644 index 0000000..cba22ee --- /dev/null +++ b/components/folder-info/folder-info.css @@ -0,0 +1,35 @@ +@import url("/static/css/global_style.css"); + +.user { + margin: 2rem auto; +} + +.user-name { + font-weight: 400; + font-size: 1.6rem; + line-height: 1.9rem; + margin: 1.2rem 0 0; +} +.codeit-avatar { + width: 6.4rem; + height: 6.4rem; +} +.page-heading { + margin: 0; +} + +@media screen and (max-width: 767px) { + .user { + margin: 1rem auto; + } + .codeit-avatar { + width: 6.4rem; + height: 6.4rem; + } + .user-name { + font-weight: 400; + font-size: 1.6rem; + line-height: 1.9rem; + margin: 1.2rem 0 0; + } +} diff --git a/components/folder-info/folderInfo.js b/components/folder-info/folderInfo.js new file mode 100644 index 0000000..3a03b85 --- /dev/null +++ b/components/folder-info/folderInfo.js @@ -0,0 +1,69 @@ +export class FolderInfo extends HTMLElement { + #prop = null; + constructor() { + super(); + this.shadow = this.attachShadow({ mode: "open" }); + } + + connectedCallback() { + this.render(); + } + + get template() { + return `
+ +

${this.prop?.ownerName ?? "@코드잇"}

+
+

${ + this.prop?.folderName ?? "⭐️ 즐겨찾기" + }

`; + } + + get prop() { + return this.#prop; + } + + set prop(newProp) { + if (typeof newProp !== "object" && newProp !== null) { + console.warn("올바르지 않은 형식의 데이터가 들어왔습니다."); + return; + } + if ( + typeof newProp.profileSrc !== "string" || + typeof newProp.ownerName !== "string" || + typeof newProp.folderName !== "string" + ) { + console.warn("올바르지 않은 형식의 데이터가 들어왔습니다."); + return; + } + this.#prop = newProp; + this.showFolderInfo(); + } + + showFolderInfo() { + const { profileSrc, ownerName, folderName } = this.prop; + this.profileImageElem.setAttribute("src", profileSrc); + this.owerNameElem.innerText = ownerName; + this.folderNameElem.innerText = folderName; + } + + render() { + const styles = document.createElement("link"); + styles.href = "/components/folder-info/folder-info.css"; + styles.rel = "stylesheet"; + + this.shadow.appendChild(styles); + this.shadow.innerHTML += this.template; + + this.profileImageElem = this.shadow.querySelector(".codeit-avatar"); + this.owerNameElem = this.shadow.querySelector(".user-name"); + this.folderNameElem = this.shadow.querySelector(".page-heading"); + } +} + +customElements.define("folder-info-component", FolderInfo); diff --git a/components/footer/footer-component.css b/components/footer/footer-component.css new file mode 100644 index 0000000..8ec4c7d --- /dev/null +++ b/components/footer/footer-component.css @@ -0,0 +1,62 @@ +@import url("/static/css/global_style.css"); + +footer { + background-color: var(--linkbrary-black); + font-family: "Arial"; + font-weight: 400; + font-size: 1.6rem; +} + +.footer-wrap { + display: flex; + justify-content: space-between; + align-items: center; + max-width: 192rem; + margin: 0 auto; + padding: 3.2rem 3.2rem 10.4rem; +} + +.copyright { + width: 10.5rem; + height: 1.8rem; + color: #676767; + margin: 0; +} +.privacy-policy-faq { + display: flex; + justify-content: space-between; + gap: 30; +} + +.privacy-policy-faq a { + color: #cfcfcf; +} + +.privacy-policy-faq a:first-child { + margin-right: 3rem; +} + +footer img { + width: 1.8rem; + height: 1.8rem; + margin-right: 1.3rem; +} + +@media screen and (max-width: 767px) { + .footer-wrap { + display: grid; + padding: 3.2rem; + align-items: start; + grid-template-rows: repeat(2, 1fr); + grid-template-columns: repeat(2, 1fr); + gap: 6rem 0; + } + + .copyright { + order: 1; + } + + .footer-imgs { + justify-self: end; + } +} diff --git a/components/footer/footerComponent.js b/components/footer/footerComponent.js new file mode 100644 index 0000000..bf4ada0 --- /dev/null +++ b/components/footer/footerComponent.js @@ -0,0 +1,105 @@ +class FooterComponent extends HTMLElement { + constructor() { + super(); + + this.attachShadow({ mode: "open" }); + } + + connectedCallback() { + this.render(); + } + + createLinkElement() { + const linkElem = document.createElement("link"); + linkElem.setAttribute("rel", "stylesheet"); + linkElem.setAttribute("href", "/components/footer/footer-component.css"); + return linkElem; + } + + createCopyRightElement() { + const copyRight = document.createElement("p"); + copyRight.textContent = "©codeit - 2023"; + copyRight.classList.add("copyright"); + return copyRight; + } + + createPrivacyPolicyFaqElement() { + const privacyPolicyFaq = document.createElement("div"); + privacyPolicyFaq.classList.add("privacy-policy-faq"); + + const privacyPolicy = document.createElement("a"); + privacyPolicy.classList.add("privacy-policy"); + privacyPolicy.textContent = "Privacy Policy"; + privacyPolicy.href = "./privacy"; + + const faq = document.createElement("a"); + faq.classList.add("faq"); + faq.textContent = "FAQ"; + faq.href = "./faq"; + + privacyPolicyFaq.appendChild(privacyPolicy); + privacyPolicyFaq.appendChild(faq); + + return privacyPolicyFaq; + } + + createFooterImgsElement() { + const footerImgs = document.createElement("div"); + footerImgs.classList.add("footer-imgs"); + const socialMediaLinks = [ + { + href: "https://ko-kr.facebook.com/", + alt: "facebook", + src: "/static/imgs/facebook.svg", + }, + { + href: "https://twitter.com/?lang=ko", + alt: "twitter", + src: "/static/imgs/twitter.svg", + }, + { + href: "https://www.youtube.com/?gl=KR", + alt: "youtube", + src: "/static/imgs/youtube.svg", + }, + { + href: "https://www.instagram.com/", + alt: "insta", + src: "/static/imgs/insta.svg", + }, + ]; + + for (let i = 0; i < socialMediaLinks.length; i++) { + const socialMediaLink = document.createElement("a"); + socialMediaLink.href = socialMediaLinks[i].href; + + const socialMediaImg = document.createElement("img"); + socialMediaImg.alt = socialMediaLinks[i].alt; + socialMediaImg.src = socialMediaLinks[i].src; + socialMediaLink.appendChild(socialMediaImg); + footerImgs.appendChild(socialMediaLink); + } + return footerImgs; + } + + createFooterWrapElement() { + const footerWrap = document.createElement("div"); + footerWrap.classList.add("footer-wrap"); + + footerWrap.appendChild(this.createCopyRightElement()); + footerWrap.appendChild(this.createPrivacyPolicyFaqElement()); + footerWrap.appendChild(this.createFooterImgsElement()); + + return footerWrap; + } + + render() { + const footerContainer = document.createElement("footer"); + footerContainer.appendChild(this.createLinkElement()); + footerContainer.appendChild(this.createFooterWrapElement()); + + this.shadowRoot.appendChild(footerContainer); + } +} + +customElements.define("footer-component", FooterComponent); diff --git a/components/gnb/gnbComponent.js b/components/gnb/gnbComponent.js new file mode 100644 index 0000000..e11d6f1 --- /dev/null +++ b/components/gnb/gnbComponent.js @@ -0,0 +1,62 @@ +class GnbComponent extends HTMLElement { + #prop = null; + + constructor() { + super(); + this.attachShadow({ mode: "open" }); + } + + connectedCallback() { + this.render(); + } + + get prop() { + return this.#prop; + } + + set prop(newProp) { + this.#prop = newProp; + this.gnbContainer.innerHTML = this.template; + } + + get loginContent() { + return this.prop ? this.loggedInText : this.loginButton; + } + + get loginButton() { + return `로그인`; + } + + get loggedInText() { + return ` +
+ +

${this.prop.email}

+
+ `; + } + + get template() { + return ` + + + + ${this.loginContent} + `; + } + + render() { + const linkElem = document.createElement("link"); + linkElem.setAttribute("rel", "stylesheet"); + linkElem.setAttribute("href", "/components/gnb/gnt-component.css"); + this.shadowRoot.appendChild(linkElem); + + const gnbContainer = document.createElement("nav"); + gnbContainer.classList.add("gnb-container"); + this.gnbContainer = gnbContainer; + + gnbContainer.innerHTML = this.template; + this.shadowRoot.appendChild(gnbContainer); + } +} +customElements.define("gnb-header", GnbComponent); diff --git a/components/gnb/gnt-component.css b/components/gnb/gnt-component.css new file mode 100644 index 0000000..6e52094 --- /dev/null +++ b/components/gnb/gnt-component.css @@ -0,0 +1,100 @@ +@import url("/static/css/global_style.css"); + +nav { + background-color: var(--library-white-smoke); + justify-content: space-between; + display: flex; + max-width: 192rem; + margin: 0 auto; +} + +main { + height: auto; + overflow: hidden; +} + +nav { + align-items: center; + height: 9.4rem; + padding: 0 20rem; +} + +nav .logo { + width: 13.3rem; + height: 2.4rem; +} + +nav .login { + width: 12.8rem; + height: 5.3rem; + color: #f5f5f5; + background: linear-gradient( + 90.99deg, + var(--linkbrary-primary) 0.12%, + #6ae3fe 101.84% + ); + border: 0; + border-radius: 0.8rem; + padding: 1.6rem 2rem; + font-weight: 600; + font-size: 1.8rem; +} + +nav .profile-icon { + width: 2.8rem; + height: 2.8rem; + margin-right: 1rem; +} + +.user-profile { + display: flex; + align-items: center; +} + +.user-email { + display: inline-block; + vertical-align: middle; + margin: 0; + color: var(--library-dark-slate-gray); + font-size: 1.4rem; + line-height: 1.7rem; +} +@media screen and (max-width: 1199px) { + nav { + padding: 0; + gap: 53.8rem; + justify-content: center; + } +} + +@media screen and (max-width: 863px) { + nav { + padding: 0 3.2rem; + gap: 0; + justify-content: space-between; + } +} + +@media screen and (max-width: 767px) { + nav { + gap: 0; + justify-content: space-between; + } + + nav .login { + width: 8rem; + height: 3.7rem; + padding: 1rem 1.6rem; + font-size: 1.4rem; + } + nav .add-link { + width: 20rem; + height: 3.7rem; + padding: 1rem 1.6rem; + font-size: 1.4rem; + } + + .user-email { + display: none; + } +} diff --git a/components/searchBar/search-bar-component.css b/components/searchBar/search-bar-component.css new file mode 100644 index 0000000..433c45f --- /dev/null +++ b/components/searchBar/search-bar-component.css @@ -0,0 +1,30 @@ +@import url("/static/css/global_style.css"); + +.search-wrap { + position: relative; +} + +.search-bar-input { + max-width: 106rem; + width: 100%; + height: 5.4rem; + padding: 1.5rem 0rem 1.5rem 4.2rem; + margin: 4rem auto; + background: #f5f5f5; + border: none; + border-radius: 1rem; + outline: none; +} + +.search-lens-icon { + position: absolute; + top: 50%; + transform: translate(0%, -50%); + left: 1.6rem; +} + +@media screen and (max-width: 767px) { + .search-bar-input { + margin: 20px auto; + } +} diff --git a/components/searchBar/searchBarComponent.js b/components/searchBar/searchBarComponent.js new file mode 100644 index 0000000..1fbfdc1 --- /dev/null +++ b/components/searchBar/searchBarComponent.js @@ -0,0 +1,57 @@ +class SearchBarComponent extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: "open" }); + } + + connectedCallback() { + this.render(); + } + + createLinkElem() { + const linkElem = document.createElement("link"); + linkElem.setAttribute("rel", "stylesheet"); + linkElem.setAttribute( + "href", + "/components/searchBar/search-bar-component.css" + ); + return linkElem; + } + + createSearchBarContainer() { + const searchBarContainer = document.createElement("div"); + searchBarContainer.classList.add("search-wrap"); + const logoImage = this.createLogoImage(); + const searchBarInput = this.createSearchBarInput(); + + searchBarContainer.appendChild(logoImage); + searchBarContainer.appendChild(searchBarInput); + + return searchBarContainer; + } + + createLogoImage() { + const logoImage = document.createElement("img"); + logoImage.classList.add("search-lens-icon"); + logoImage.alt = "search-lens-icon"; + logoImage.src = "/static/imgs/search-bar-lens-icon.svg"; + return logoImage; + } + + createSearchBarInput() { + const searchBarInput = document.createElement("input"); + searchBarInput.classList.add("search-bar-input"); + searchBarInput.placeholder = "원하는 링크를 검색해 보세요"; + return searchBarInput; + } + + render() { + const linkElem = this.createLinkElem(); + const searchBarContainer = this.createSearchBarContainer(); + + this.shadowRoot.appendChild(linkElem); + this.shadowRoot.appendChild(searchBarContainer); + } +} + +customElements.define("search-bar", SearchBarComponent); diff --git a/components/star/starComponent.js b/components/star/starComponent.js new file mode 100644 index 0000000..57517fe --- /dev/null +++ b/components/star/starComponent.js @@ -0,0 +1,88 @@ +export class StarComponent extends HTMLElement { + #prop; + constructor() { + super(); + this.attachShadow({ mode: "open" }); + } + + static get observedAttributes() { + return ["is_starred"]; + } + + connectedCallback() { + this.render(); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name === "is_starred" && oldValue !== newValue) { + this.prop = newValue === "true"; + this.renderStarIcon(); + } + } + + get prop() { + return this.#prop; + } + + set prop(newProp) { + //추후 즐겨찾기(=starred 여부)에 대한 데이터 주고받기 할 경우 추가 + if (typeof newProp !== "boolean") { + console.warn("옳바르지 않은 형식의 데이터가 들어왔습니다."); + return; + } + this.#prop = newProp; + this.setAttribute("is_starred", this.prop); + } + + renderStarIcon() { + const pathColor = this.shadowRoot.querySelector("path"); + const fillOpacity = this.prop ? "1" : "0.2"; + const fillColor = this.prop + ? "var(--linkbrary-primary)" + : "var(--linkbrary-black)"; + + pathColor.setAttribute("fill", fillColor); + pathColor.setAttribute("fill-opacity", fillOpacity); + } + + get template() { + return ` + + + + `; + } + + toggleStarredStatus(event) { + event.stopPropagation(); + this.prop = !this.prop; + this.setAttribute("is_starred", this.prop); + } + + render() { + // CSS + const linkElem = document.createElement("link"); + linkElem.setAttribute("rel", "stylesheet"); + linkElem.setAttribute("href", "/static/css/global_style.css"); + this.shadowRoot.appendChild(linkElem); + + const starIcon = document.createElement("template"); + starIcon.innerHTML = this.template; + this.shadowRoot.appendChild(starIcon.content.cloneNode(true)); + + this.setAttribute("is_starred", this.prop); + this.addEventListener("click", this.toggleStarredStatus.bind(this)); + } +} +customElements.define("star-icon", StarComponent); diff --git a/forgot-password/index.html b/forgot-password/index.html index b138f08..873120b 100644 --- a/forgot-password/index.html +++ b/forgot-password/index.html @@ -1,15 +1,11 @@ - - - - - - Document - - - - - - - \ No newline at end of file + + + + + Linkbrary + + + + diff --git a/index.html b/index.html index ab7718c..395b6de 100644 --- a/index.html +++ b/index.html @@ -1,109 +1,136 @@ + + - - - - - - + + + + Linkbrary - - - - + + + + - - - - - - + + + + + - + - +
- +
-
-

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

- 링크 추가하기 -
- home-hero-img +
+

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

+ 링크 추가하기 +
+ home-hero-img
-
-

원하는 링크
저장하세요 -

-

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

- save-link-img -
-
- manage-link-img - -

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

-

- 나만의 폴더를 무제함으로 만들고
- 다양하게 활용할 수 있어요. -

- -
-
+
+

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

+

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

+ save-link-img +
+
+ manage-link-img -

- 저장한 링크를
해보세요 -

-

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

+

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

+

+ 나만의 폴더를 무제함으로 만들고
+ 다양하게 활용할 수 있어요. +

+
+
+

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

+

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

- share-link-img -
-
- search-link-img + share-link-img +
+
+ search-link-img -

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

-

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

-
+

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

+

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

+
- - - - - \ No newline at end of file + + + + + diff --git a/my-link/index.html b/my-link/index.html index b138f08..873120b 100644 --- a/my-link/index.html +++ b/my-link/index.html @@ -1,15 +1,11 @@ - - - - - - Document - - - - - - - \ No newline at end of file + + + + + Linkbrary + + + + diff --git a/shared/index.html b/shared/index.html new file mode 100644 index 0000000..7ccab91 --- /dev/null +++ b/shared/index.html @@ -0,0 +1,34 @@ + + + + + + + + + + Linkbrary + + +
+ +
+ +
+
+
+ + +
+ + + + + + + + + diff --git a/shared/shared.css b/shared/shared.css new file mode 100644 index 0000000..a39b5bd --- /dev/null +++ b/shared/shared.css @@ -0,0 +1,34 @@ +body { + margin: 0; +} + +header { + background-color: var(--library-white-smoke); +} + +main { + margin: 0 auto; + max-width: 106rem; +} + +.hero-section { + text-align: center; + padding: 2rem 0 6rem; +} + +@media screen and (max-width: 1100px) { + main { + max-width: 70.4rem; + } +} + +@media screen and (max-width: 767px) { + main { + max-width: 32.5rem; + } + + .hero-section { + text-align: center; + padding: 1rem 0 4rem; + } +} diff --git a/shared/shared.js b/shared/shared.js new file mode 100644 index 0000000..8d4a05a --- /dev/null +++ b/shared/shared.js @@ -0,0 +1,30 @@ +import { fetchFolderData } from "../api/folder.api.js"; +import { fetchUserData } from "../api/user.api.js"; + +document.addEventListener("DOMContentLoaded", () => { + const folderInfoComponent = document.querySelector("folder-info-component"); + const cardListComponent = document.querySelector("card-list-component"); + fetchFolderData() + .then((data) => { + const { + owner: { name: ownerName, profileImageSource: profileSrc }, + name: folderName, + links, + } = data.folder; + folderInfoComponent.prop = { profileSrc, ownerName, folderName }; + cardListComponent.prop = links; + }) + .catch((err) => { + console.error(err); + }); + //gnb user data 가져오기:로그인 기능 구현 후 수정 + const gnbComponent = document.querySelector("gnb-header"); + fetchUserData() + .then((data) => { + const { profileImageSource: profileSrc, email } = data; + gnbComponent.prop = { profileSrc, email }; + }) + .catch((err) => { + console.error(err); + }); +}); diff --git a/signin/index.html b/signin/index.html index 5ee5268..7851203 100644 --- a/signin/index.html +++ b/signin/index.html @@ -1,51 +1,61 @@ + + + + + + + - - - - - - + Linkbrary + - Document - - - +
-
- -
회원이 아니신가요?회원 가입하기
+
+ +
+ 회원이 아니신가요?회원 가입하기
-
-
-
- - -
-
-
- -
-
-
- - 비밀번호 찾기 - -
-
- - - \ No newline at end of file + + diff --git a/signin/signin.js b/signin/signin.js index d93a93b..9e81c1a 100644 --- a/signin/signin.js +++ b/signin/signin.js @@ -3,7 +3,7 @@ const password = document.querySelector("#signin-password"); const signinForm = document.querySelector("form"); const inputs = document.querySelectorAll(".input"); -function isValidEmail(e) { +const isValidEmail = (e) => { const emailRegex = "^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$"; if (e.sourceCapabilities === null) { return; @@ -13,33 +13,40 @@ function isValidEmail(e) { } else if (!email.value.match(emailRegex)) { alert("올바른 이메일 주소가 아닙니다."); } -} +}; -function isValidAccount(e) { +const isValidAccount = (e) => { e.preventDefault(); if (email.value === "test@codeit.com" && password.value === "codeit101") { + sessionStorage.clear(); + sessionStorage.setItem("isLoggedIn", "true"); + sessionStorage.setItem("email", email.value); + sessionStorage.setItem("password", password.value); location.href = "../my-link/"; } else { alert("이메일과 비밀번호를 확인해주세요."); } -} +}; // eys toggle -const eyeIcons = document.querySelector(".eye-icon"); +const eyeIcons = document.querySelectorAll(".eye-icon"); +const passwordEyeIcon = eyeIcons[0]; +const passwordCheckEyeIcon = eyeIcons[0]; -eyeIcons.addEventListener("pointerdown", (e) => { - const target = e.target.previousSibling; - e.preventDefault(); - if (eyeIcons.classList.contains("fa-eye-slash")) { - eyeIcons.classList.remove("fa-eye-slash"); - eyeIcons.classList.add("fa-eye"); - target.type = "text"; - } else if (eyeIcons.classList.contains("fa-eye")) { - eyeIcons.classList.remove("fa-eye"); - eyeIcons.classList.add("fa-eye-slash"); - target.type = "password"; - } +eyeIcons.forEach((eyeIcon) => { + let visiblity = false; + + eyeIcon.addEventListener("pointerdown", (e) => { + e.preventDefault(); + + const target = e.target.previousSibling; + visiblity = !visiblity; + + target.type = visibility ? "text" : "password"; + eyeIcon.classList.toggle("fa-eye-slash"); + eyeIcon.classList.toggle("fa-eye"); + }); }); // validation에 대한 event listener 등록 diff --git a/signup/index.html b/signup/index.html index f810d95..48417e8 100644 --- a/signup/index.html +++ b/signup/index.html @@ -1,56 +1,63 @@ + + + + + + + + Linkbrary + - - - - - - - Document - - - +
-
-
이미 회원이신가요?로그인하기
+
+ +
+ 이미 회원이신가요?로그인하기
-
-
-
- - -
-
- -
- - -
-
-
- -
- - -
-
+
+ +
+
+ + +
+
+ +
+ +
- - - + + +
- - - \ No newline at end of file + + diff --git a/signup/signup.js b/signup/signup.js index 14b2396..e40bfc8 100644 --- a/signup/signup.js +++ b/signup/signup.js @@ -3,7 +3,7 @@ const password = document.querySelector("#signup-password"); const signupForm = document.querySelector("form"); // validation 함수 -function isValidEmail(e) { +const isValidEmail = (e) => { const emailRegex = "^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$"; if (e.sourceCapabilities === null) { @@ -21,9 +21,9 @@ function isValidEmail(e) { return false; } return true; -} +}; -function isValidPassword(e) { +const isValidPassword = (e) => { const passwordRegex = /^(?=.*[a-zA-Z])(?=.*[0-9]).{8,25}$/; if (e.sourceCapabilities === null) { @@ -35,24 +35,27 @@ function isValidPassword(e) { return false; } return true; -} +}; -function isValidPasswordCheck(e) { +const isValidPasswordCheck = (e) => { const passwordCheck = document.getElementById("signup-password-check").value; if (password.value !== passwordCheck) { alert("비밀번호 확인이 일치하지 않습니다."); return false; } return true; -} +}; - -function isValidForm(e) { +const isValidForm = (e) => { e.preventDefault(); if (isValidEmail(e) && isValidPassword(e) && isValidPasswordCheck(e)) { + sessionStorage.clear(); + sessionStorage.setItem("isLoggedIn", "true"); + sessionStorage.setItem("email", email.value); + sessionStorage.setItem("password", password.value); location.href = "../my-link/"; } -} +}; // validation에 대한 event listener 등록 email.addEventListener("focusout", isValidEmail); @@ -66,14 +69,14 @@ const passwordCheckEyeIcon = eyeIcons[0]; eyeIcons.forEach((eyeIcon) => { let visiblity = false; - + eyeIcon.addEventListener("pointerdown", (e) => { e.preventDefault(); const target = e.target.previousSibling; - visiblity = !visiblity + visiblity = !visiblity; - visiblity ? target.type = "text": target.type = "password" + target.type = visibility ? "text" : "password"; eyeIcon.classList.toggle("fa-eye-slash"); eyeIcon.classList.toggle("fa-eye"); }); diff --git a/static/css/global_style.css b/static/css/global_style.css index df56ad9..cc3bb39 100644 --- a/static/css/global_style.css +++ b/static/css/global_style.css @@ -1,5 +1,5 @@ @font-face { - font-family: "Pretendard-Regular"; + font-family: "Pretendard"; src: url("https://cdn.jsdelivr.net/gh/Project-Noonnu/noonfonts_2107@1.1/Pretendard-Regular.woff") format("woff"); font-weight: 400; @@ -7,7 +7,17 @@ } html { + font-family: "Pretendard"; font-size: 62.5%; + --linkbrary-primary: #6d6afe; + --linkbrary-red: #ff5b56; + --linkbrary-black: #111322; + --linkbrary-white: #ffffff; + --library-dark-slate-gray: #3e3e43; + --library-blue-gray: #9fa6b2; + --library-light-blue-gray: #ccd5e3; + --library-alice-blue: #e7effb; + --library-white-smoke: #f0f6ff; } body { @@ -17,3 +27,8 @@ body { * { box-sizing: border-box; } + +a { + text-decoration: none; + text-align: center; +} diff --git a/static/css/home/style.css b/static/css/home/style.css index a94dd8d..d6f914d 100644 --- a/static/css/home/style.css +++ b/static/css/home/style.css @@ -1,4 +1,4 @@ -@import url("/global_style.css"); +@import url("/static/css/global_style.css"); body { margin: 0 auto; @@ -18,15 +18,7 @@ header { header, main { - background-color: #f0f6ff; -} - -nav, -.footer-wrap { - justify-content: space-between; - display: flex; - max-width: 192rem; - margin: 0 auto; + background-color: var(--library-white-smoke); } header, @@ -40,29 +32,6 @@ main { overflow: hidden; } -nav { - align-items: center; - height: 9.4rem; - padding: 0 20rem; -} - -nav .logo { - width: 13.3rem; - height: 2.4rem; -} - -nav .login { - width: 12.8rem; - height: 5.3rem; - color: #f5f5f5; - background: linear-gradient(90.99deg, #6d6afe 0.12%, #6ae3fe 101.84%); - border: 0; - border-radius: 0.8rem; - padding: 1.6rem 2rem; - font-weight: 600; - font-size: 1.8rem; -} - main .title { font-weight: 700; font-size: 6.4rem; @@ -72,7 +41,11 @@ main .title { } main .title-gradient { - background: linear-gradient(91.26deg, #6d6afe 17.28%, #ff9f9f 74.98%); + background: linear-gradient( + 91.26deg, + var(--linkbrary-primary) 17.28%, + #ff9f9f 74.98% + ); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; @@ -84,7 +57,11 @@ main .add-link { margin: 0 auto; height: 5.3rem; color: #f5f5f5; - background: linear-gradient(90.99deg, #6d6afe 0.12%, #6ae3fe 101.84%); + background: linear-gradient( + 90.99deg, + var(--linkbrary-primary) 0.12%, + #6ae3fe 101.84% + ); border-radius: 0.8rem; padding: 1.6rem 2rem; font-weight: 600; @@ -109,13 +86,13 @@ article .content-imgs { article .content-imgs.left { grid-row: 1/3; - grid-column : 1/2; + grid-column: 1/2; justify-self: end; } article .content-imgs.right { grid-row: 1/3; - grid-column : 2/3; + grid-column: 2/3; justify-self: start; } @@ -142,14 +119,14 @@ article { .article-heading.left { grid-row: 1/2; - grid-column : 1/2; + grid-column: 1/2; justify-self: end; align-self: end; } .article-heading.right { grid-row: 1/2; - grid-column : 2/3; + grid-column: 2/3; justify-self: start; align-self: end; } @@ -205,51 +182,7 @@ article p.right { align-self: start; } -footer { - background-color: #111322; - font-family: "Arial"; - font-weight: 400; - font-size: 1.6rem; -} - -.footer-wrap { - align-items: center; - padding: 3.2rem 3.2rem 10.4rem; -} - -.copyright { - width: 10.5rem; - height: 1.8rem; - color: #676767; - margin: 0; -} -.privacy-policy-faq { - display: flex; - justify-content: space-between; - gap: 30; -} - -.privacy-policy-faq a { - color: #cfcfcf; -} - -.privacy-policy-faq a:first-child { - margin-right: 3rem; -} - -footer img { - width: 1.8rem; - height: 1.8rem; - margin-right: 1.3rem; -} - @media screen and (max-width: 1199px) { - nav { - padding: 0; - gap: 53.8rem; - justify-content: center; - } - main .title { padding: 4rem 0; } @@ -272,33 +205,7 @@ footer img { width: 38.5rem; } } - -@media screen and (max-width: 863px) { - nav { - padding: 0 3.2rem; - gap: 0; - justify-content: space-between; - } -} - @media screen and (max-width: 767px) { - nav { - gap: 0; - justify-content: space-between; - } - - nav .login { - width: 8rem; - height: 3.7rem; - padding: 1rem 1.6rem; - font-size: 1.4rem; - } - nav .add-link { - width: 20rem; - height: 3.7rem; - padding: 1rem 1.6rem; - font-size: 1.4rem; - } main { height: auto; } @@ -353,21 +260,4 @@ footer img { article .hidden-br-mobile { display: none; } - - .footer-wrap { - display: grid; - padding: 3.2rem; - align-items: start; - grid-template-rows: repeat(2, 1fr); - grid-template-columns: repeat(2, 1fr); - gap: 6rem 0; - } - - .copyright { - order: 1; - } - - footer .imgs { - justify-self: end; - } } diff --git a/static/css/sign/sign.css b/static/css/sign/sign.css index 6c0825b..848d11d 100644 --- a/static/css/sign/sign.css +++ b/static/css/sign/sign.css @@ -5,7 +5,7 @@ } body { - background-color: #f0f6ff; + background-color: var(--library-white-smoke); margin: 0; } @@ -17,10 +17,6 @@ main { margin-right: auto; } -/* main > * { - -} */ - .linkbrary-logo { height: 3.8rem; } @@ -30,7 +26,7 @@ main { } .sign-prompt a { - color: #6d6afe; + color: var(--linkbrary-primary); margin-left: 0.8rem; } @@ -54,17 +50,17 @@ form .input-field { form input { width: 100%; height: 5.4rem; - border: 0.1rem solid #ccd5e3; + border: 0.1rem solid var(--library-light-blue-gray); border-radius: 0.8rem; padding: 1.8rem 1.5rem; font-size: 1.6rem; - color: #9fa6b2; + color: var(--library-blue-gray); } form input:focus { - border: 0.1rem solid #6d6afe; + border: 0.1rem solid var(--linkbrary-primary); outline: none; - color: #3e3e43; + color: var(--library-dark-slate-gray); } .password-input-wrap { @@ -84,7 +80,11 @@ form .submit-button { color: #f5f5f5; margin: 3rem 0 1.6rem; width: 100%; - background: linear-gradient(90.99deg, #6d6afe 0.12%, #6ae3fe 101.84%); + background: linear-gradient( + 90.99deg, + var(--linkbrary-primary) 0.12%, + #6ae3fe 101.84% + ); border: none; border-radius: 0.8rem; padding: 1.6rem 2rem; @@ -92,7 +92,7 @@ form .submit-button { form .link-find-password { text-decoration: none; - color: #3e3e43; + color: var(--library-dark-slate-gray); } .social-container { @@ -101,8 +101,8 @@ form .link-find-password { align-items: center; margin-top: 3.2rem; padding: 1.2rem 2.4rem; - background-color: #e7effb; - border: 0.1rem solid #ccd5e3; + background-color: var(--library-alice-blue); + border: 0.1rem solid var(--library-light-blue-gray); border-radius: 0.8rem; } @@ -114,8 +114,8 @@ form .link-find-password { @media screen and (max-width: 767px) { body { - margin-left: 3.2rem ; - margin-right: 3.2rem ; + margin-left: 3.2rem; + margin-right: 3.2rem; } main { margin-top: 12rem; diff --git a/static/data/card/card-mockup-img1.png b/static/data/card/card-mockup-img1.png new file mode 100644 index 0000000..0b515e4 Binary files /dev/null and b/static/data/card/card-mockup-img1.png differ diff --git a/static/data/card/card-mockup-img2.png b/static/data/card/card-mockup-img2.png new file mode 100644 index 0000000..da96017 Binary files /dev/null and b/static/data/card/card-mockup-img2.png differ diff --git a/static/data/card/card-mockup-img3.png b/static/data/card/card-mockup-img3.png new file mode 100644 index 0000000..d3a42ee Binary files /dev/null and b/static/data/card/card-mockup-img3.png differ diff --git a/static/data/card/card-mockup-img4.png b/static/data/card/card-mockup-img4.png new file mode 100644 index 0000000..233e433 Binary files /dev/null and b/static/data/card/card-mockup-img4.png differ diff --git a/static/data/card/card-mockup-img5.png b/static/data/card/card-mockup-img5.png new file mode 100644 index 0000000..4a494a5 Binary files /dev/null and b/static/data/card/card-mockup-img5.png differ diff --git a/static/data/card/card-mockup-img6.png b/static/data/card/card-mockup-img6.png new file mode 100644 index 0000000..80b5f44 Binary files /dev/null and b/static/data/card/card-mockup-img6.png differ diff --git a/static/data/card/card-mockup-img7.png b/static/data/card/card-mockup-img7.png new file mode 100644 index 0000000..6c940ba Binary files /dev/null and b/static/data/card/card-mockup-img7.png differ diff --git a/static/data/card/card-mockup-img8.png b/static/data/card/card-mockup-img8.png new file mode 100644 index 0000000..fd28a49 Binary files /dev/null and b/static/data/card/card-mockup-img8.png differ diff --git a/static/data/card/card-mockup-img9.png b/static/data/card/card-mockup-img9.png new file mode 100644 index 0000000..b0d83d1 Binary files /dev/null and b/static/data/card/card-mockup-img9.png differ diff --git a/static/data/card/cards.json b/static/data/card/cards.json new file mode 100644 index 0000000..88ba9b0 --- /dev/null +++ b/static/data/card/cards.json @@ -0,0 +1,57 @@ +{ + "cards": [ + { + "imageSrc": "/static/data/card/card-mockup-img1.png", + "updateTime": "10 minutes ago", + "description": "Lorem ipsum dolor sit amet consectetur. Metus amet habitant nunc consequat. Lorem ipsum dolor sit amet consectetur. Metus amet habitant nunc consequat.", + "date": "2023. 3. 15" + }, + { + "imageSrc": "/static/data/card/card-mockup-img2.png", + "updateTime": "10 minutes ago", + "description": "Lorem ipsum dolor sit amet consectetur. Metus amet habitant nunc consequat. Lorem ipsum dolor sit amet consectetur. Metus amet habitant nunc consequat.", + "date": "2023. 3. 15" + }, + { + "imageSrc": "/static/data/card/card-mockup-img3.png", + "updateTime": "10 minutes ago", + "description": "Lorem ipsum dolor sit amet consectetur. Metus amet habitant nunc consequat. Lorem ipsum dolor sit amet consectetur. Metus amet habitant nunc consequat.", + "date": "2023. 3. 15" + }, + { + "imageSrc": "/static/data/card/card-mockup-img4.png", + "updateTime": "10 minutes ago", + "description": "Lorem ipsum dolor sit amet consectetur. Metus amet habitant nunc consequat. Lorem ipsum dolor sit amet consectetur. Metus amet habitant nunc consequat.", + "date": "2023. 3. 15" + }, { + "imageSrc": "/static/data/card/card-mockup-img5.png", + "updateTime": "10 minutes ago", + "description": "Lorem ipsum dolor sit amet consectetur. Metus amet habitant nunc consequat. Lorem ipsum dolor sit amet consectetur. Metus amet habitant nunc consequat.", + "date": "2023. 3. 15" + }, + { + "imageSrc": "/static/data/card/card-mockup-img6.png", + "updateTime": "10 minutes ago", + "description": "Lorem ipsum dolor sit amet consectetur. Metus amet habitant nunc consequat. Lorem ipsum dolor sit amet consectetur. Metus amet habitant nunc consequat.", + "date": "2023. 3. 15" + }, + { + "imageSrc": "/static/data/card/card-mockup-img7.png", + "updateTime": "10 minutes ago", + "description": "Lorem ipsum dolor sit amet consectetur. Metus amet habitant nunc consequat. Lorem ipsum dolor sit amet consectetur. Metus amet habitant nunc consequat.", + "date": "2023. 3. 15" + }, + { + "imageSrc": "/static/data/card/card-mockup-img8.png", + "updateTime": "10 minutes ago", + "description": "Lorem ipsum dolor sit amet consectetur. Metus amet habitant nunc consequat. Lorem ipsum dolor sit amet consectetur. Metus amet habitant nunc consequat.", + "date": "2023. 3. 15" + }, + { + "imageSrc": "/static/data/card/card-mockup-img9.png", + "updateTime": "10 minutes ago", + "description": "Lorem ipsum dolor sit amet consectetur. Metus amet habitant nunc consequat. Lorem ipsum dolor sit amet consectetur. Metus amet habitant nunc consequat.", + "date": "2023. 3. 15" + } + ] +} diff --git a/static/imgs/default-card-img.png b/static/imgs/default-card-img.png new file mode 100644 index 0000000..b2da74e Binary files /dev/null and b/static/imgs/default-card-img.png differ diff --git a/static/imgs/kebab.svg b/static/imgs/kebab.svg new file mode 100644 index 0000000..c4dea2a --- /dev/null +++ b/static/imgs/kebab.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/static/imgs/search-bar-lens-icon.svg b/static/imgs/search-bar-lens-icon.svg new file mode 100644 index 0000000..0de52d7 --- /dev/null +++ b/static/imgs/search-bar-lens-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/static/imgs/users/codeit-avatar.png b/static/imgs/users/codeit-avatar.png new file mode 100644 index 0000000..af8caa6 Binary files /dev/null and b/static/imgs/users/codeit-avatar.png differ diff --git a/static/imgs/users/profile.svg b/static/imgs/users/profile.svg new file mode 100644 index 0000000..379af22 --- /dev/null +++ b/static/imgs/users/profile.svg @@ -0,0 +1,4 @@ + + + +