diff --git a/.firebaserc b/.firebaserc
new file mode 100644
index 000000000..f22135cc1
--- /dev/null
+++ b/.firebaserc
@@ -0,0 +1,5 @@
+{
+ "projects": {
+ "default": "employee-management-59973"
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..a012efddd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,71 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+firebase-debug.log*
+firebase-debug.*.log*
+
+# Firebase cache
+.firebase/
+
+# Firebase config
+
+# Uncomment this if you'd like others to create their own Firebase project.
+# For a team working on the same Firebase project(s), it is recommended to leave
+# it commented so all members can deploy to the same project(s) in .firebaserc.
+# .firebaserc
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+
+# firebase file
+.firebaserc
+firestore.rules
+storage.rules
diff --git a/README.md b/README.md
index a29ee163b..4fe068465 100644
--- a/README.md
+++ b/README.md
@@ -1,49 +1,53 @@
+# ✨ 직원 관리 서비스
-# :camera: 직원 사진 관리 서비스
-
-직원들의 사진을 관리할 수 있는 사진 관리자 서비스를 만들어 보세요.
-
-과제 수행 및 리뷰 기간은 별도 공지를 참고하세요!
-## [과제 수행 및 제출 방법]
-1. 현재 저장소를 로컬에 클론(Clone)합니다.
-2. 자신의 본명으로 브랜치를 생성합니다.(구분 가능하도록 본명을 꼭 파스칼케이스로 표시하세요, git branch KDT0_이름)
-3. 자신의 본명 브랜치에서 과제를 수행합니다.
-4. 과제 수행이 완료되면, 자신의 본명 브랜치를 원격 저장소에 푸시(Push)합니다.(main 브랜치에 푸시하지 않도록 꼭 주의하세요, git push origin KDT0_이름)
-5. 저장소에서 main 브랜치를 대상으로 Pull Request 생성하면, 과제 제출이 완료됩니다!(E.g, main <== KDT0_이름)
-6. Pull Request 링크를 LMS로도 제출해 주셔야 합니다.
-7. main 혹은 다른 사람의 브랜치로 절대 병합하지 않도록 주의하세요!
-8. Pull Request에서 보이는 설명을 다른 사람들이 이해하기 쉽도록 꼼꼼하게 작성하세요!
-9. Pull Request에서 과제 제출 후 절대 병합(Merge)하지 않도록 주의하세요!
-10. 과제 수행 및 제출 과정에서 문제가 발생한 경우, 바로 담당 멘토나 강사에서 얘기하세요!
-
-## [필수 요구사항]
-- “AWS S3 / Firebase 같은 서비스”를 이용하여 사진을 관리할 수 있는 페이지를 구현하세요.
-- 프로필 페이지를 개발하세요.
-- 스크롤이 가능한 형태의 리스팅 페이지를 개발하세요.
-- 전체 페이지 데스크탑-모바일 반응형 페이지를 개발하세요.
-- 사진을 등록, 수정, 삭제가 가능해야 합니다.
-- 유저 플로우를 제작하여 리드미에 추가하세요.
-* CSS
- * 애니메이션 구현
- * 상대수치 사용(rem, em)
-* JavaScript
- * DOM event 조작
-
-## [선택 요구사항]
-- 사진 관리 페이지와 관련된 기타 기능도 고려해 보세요.
-- 페이지가 보여지기 전에 로딩 애니메이션이 보이도록 만들어보세요.
-- 직원을 등록, 수정, 삭제가 가능하게 해보세요.
-- 직원 검색 기능을 추가해 보세요.
-- infinity scroll 기능을 추가해 보세요.
-- 사진을 편집할 수 있는 기능을 추가해 보세요.
-- LocalStorage 사용
-
-## [화면 예시]
-![Untitled (1)](https://github.com/KDT1-FE/Y_FE_JAVASCRIPT_PICTURE/assets/38754963/5dda6755-2501-4af4-bc3e-b63a353c44c2)
-
-![Untitled (2)](https://github.com/KDT1-FE/Y_FE_JAVASCRIPT_PICTURE/assets/38754963/6c1805f1-2b00-453e-a729-2b483612726d)
-
-## [흐름]
-![Untitled](https://github.com/KDT1-FE/Y_FE_JAVASCRIPT_PICTURE/assets/38754963/e2934c05-26f6-4ef6-88d4-beed76aa007a)
+## 📍 프로젝트 소개
+* 진행 기간 : 2023-08-07~2023-08-18
+* 배포주소 : https://my-employee-management.netlify.app/
+* 기술 스택 :
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/css/animation.css b/src/css/animation.css
new file mode 100644
index 000000000..13a968ce1
--- /dev/null
+++ b/src/css/animation.css
@@ -0,0 +1,85 @@
+.animation {
+ z-index: 999;
+}
+#link {
+ color: #fff;
+ display: block;
+ font: 12px "Helvetica Neue", Helvetica, Arial, sans-serif;
+ text-align: center;
+ text-decoration: none;
+}
+#link:hover {
+ color: #cccccc;
+}
+
+#link,
+#link:hover {
+ transition: color 0.5s ease-out;
+}
+
+body {
+ background: #28288c;
+}
+
+@keyframes rotate-loading {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes loading-text-opacity {
+ 0%,
+ 20%,
+ 100% {
+ opacity: 0;
+ }
+ 50% {
+ opacity: 1;
+ }
+}
+
+.loading-container,
+.loading {
+ width: 100px;
+ height: 100px;
+ position: relative;
+ border-radius: 100%;
+}
+
+.loading-container {
+ margin: 40vh auto;
+}
+
+.loading {
+ border: 2px solid transparent;
+ border-color: transparent #ececec transparent #ececec;
+ animation: rotate-loading 1.5s linear 0s infinite normal;
+ transform-origin: 50% 50%;
+}
+
+.loading-container:hover .loading {
+ border-color: transparent #ffffff transparent #ffffff;
+}
+.loading-container:hover .loading,
+.loading-container .loading {
+ transition: all 0.5s ease-in-out;
+}
+
+#loading-text {
+ animation: loading-text-opacity 2s linear 0s infinite normal;
+ color: #ffffff;
+ font-family: "Noto Sans KR", sans-serif;
+ white-space: nowrap;
+ font-size: 10px;
+ font-weight: bold;
+ margin-top: 45px;
+ opacity: 0;
+ position: absolute;
+ text-align: center;
+ text-transform: uppercase;
+ top: 0;
+ width: 100px;
+}
diff --git a/src/css/header.css b/src/css/header.css
new file mode 100644
index 000000000..151b756e3
--- /dev/null
+++ b/src/css/header.css
@@ -0,0 +1,128 @@
+* {
+ font-family: "Noto Sans KR", sans-serif;
+ white-space: nowrap;
+}
+.header {
+ position: sticky;
+ top: 0;
+ display: flex;
+ width: 95%;
+ margin: 0 auto;
+ padding-top: 20px;
+ padding-bottom: 20px;
+ align-items: center;
+ justify-content: space-between;
+ background-color: #fff;
+ border-bottom: 1px solid #4b4848;
+ z-index: 9;
+}
+
+.header__icon {
+ display: inline-flex;
+ align-items: center;
+}
+.main__icon {
+ width: 2.8rem;
+ height: 2.8rem;
+ object-fit: cover;
+ cursor: pointer;
+}
+.header__icon > span {
+ font-size: 28px;
+ font-weight: 700;
+ margin-left: 5px;
+ text-decoration: none;
+ color: black;
+ cursor: pointer;
+}
+.header__icon:hover > span {
+ color: #4b4848;
+}
+
+.employee-search__wrapper {
+ display: flex;
+ width: 33%;
+ height: 35px;
+ border: 2px solid #4b4848;
+ border-radius: 6px;
+ align-items: center;
+}
+.search-icon {
+ width: 15px;
+ height: 15px;
+ margin: 5px 10px auto 5px;
+}
+.employee-search__input {
+ width: 100%;
+ margin-right: 3px;
+ border: none;
+ font-size: 18px;
+ background-color: inherit;
+ overflow: hidden;
+}
+.employee-search__input:focus {
+ font-weight: 500;
+ outline: none;
+}
+
+.button__wrapper {
+ display: flex;
+ align-items: center;
+}
+.employee-add__button,
+.employee-delete__button {
+ margin-left: 15px;
+}
+.employee-add__button,
+.employee-delete__button {
+ padding: 0.8rem 1.6rem;
+ font-size: 16px;
+ font-weight: 600;
+ background-color: #28288c;
+ color: white;
+ border: none;
+ border-radius: 8px;
+ box-shadow: 5px 5px rgba(0, 0, 0, 0.5);
+ cursor: pointer;
+}
+
+.employee-add__button:hover {
+ background-color: #6e6ed7;
+}
+.delete__button:hover,
+.employee-delete__button:hover {
+ background-color: #e17878;
+}
+
+@media screen and (max-width: 391px) {
+ .header {
+ padding-bottom: 55px;
+ }
+ .main__icon {
+ width: 2rem;
+ height: 2rem;
+ }
+ .header__icon > span {
+ font-size: 18px;
+ }
+ .employee-search__wrapper {
+ position: absolute;
+ top: 70px;
+ width: 95%;
+ height: 30px;
+ margin: 5px 0 5px 10px;
+ }
+ .employee-search__wrapper > .search-icon {
+ width: 2rem;
+ height: 2rem;
+ }
+ .employee-search__input {
+ font-size: 14px;
+ }
+ .employee-add__button,
+ .employee-delete__button {
+ font-size: 12px;
+ margin-left: 0;
+ margin-right: 15px;
+ }
+}
diff --git a/src/css/modal.css b/src/css/modal.css
new file mode 100644
index 000000000..9b4cb0921
--- /dev/null
+++ b/src/css/modal.css
@@ -0,0 +1,183 @@
+.modal__background,
+.detail-modal__background {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ background: rgba(0, 0, 0, 0.8);
+ z-index: 98;
+}
+.employee-detail__modal,
+.employee-add__modal {
+ width: 800px;
+ height: 650px;
+ margin: 20px auto;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ background: #fff;
+ border-radius: 8px;
+ padding: 10px;
+ z-index: 99;
+}
+.employee-add__modal > h1,
+.employee-detail__modal > h1 {
+ font-size: 35px;
+ margin: 0.4em;
+}
+.close-icon,
+.close__icon {
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ z-index: 99;
+ cursor: pointer;
+}
+.employee-add__modal > form,
+.employee-detail__modal > form {
+ width: 100%;
+ margin-top: 10px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 18px;
+}
+
+.add-file__wrapper {
+ flex-direction: column;
+}
+.add-data__file,
+.update-data__file {
+ margin: 20px auto;
+}
+
+.add-data__img,
+.detail__img {
+ width: 230px;
+ height: 230px;
+ object-fit: cover;
+ background-color: #ececec;
+}
+
+.add-file__wrapper,
+.employee-add__modal > form > input,
+.add-data {
+ width: 450px;
+ display: flex;
+ flex: 1 1 auto;
+ margin: 5px;
+ justify-content: space-between;
+ align-items: center;
+}
+.employee-add__modal > form > input,
+.employee-detail__modal > form > input {
+ margin: 40px auto 30px auto;
+}
+.add-data > label {
+ margin-right: 10px;
+ font-weight: 600;
+ font-size: 18px;
+ font-weight: 500;
+ color: #4b4848;
+}
+.add-data > input {
+ width: 50%;
+ height: 4vh;
+}
+
+.employee-add__modal button,
+.edit__button,
+.delete__button {
+ width: 300px;
+ height: 45px;
+ margin-top: 30px;
+ background-color: #28288c;
+ border-radius: 8px;
+ border: none;
+ color: white;
+ cursor: pointer;
+ font-size: 16px;
+ font-weight: 600;
+ box-shadow: 6px 6px rgba(0, 0, 0, 0.5);
+}
+.employee-add__modal button:hover,
+.edit__button:hover {
+ background-color: #6e6ed7;
+}
+
+.employee-add__modal input::placeholder,
+.employee-add__modal input,
+.employee-detail__modal input::placeholder,
+.employee-detail__modal input {
+ color: black;
+ font-weight: 600;
+ font-size: 16px;
+}
+
+.detail-button__wrapper {
+ display: flex;
+}
+.edit__button,
+.delete__button {
+ width: 210px;
+ margin-top: 30px;
+ font-size: 15px;
+}
+.delete__button {
+ margin-left: 30px;
+}
+
+@media screen and (max-width: 391px) {
+ .employee-detail__modal,
+ .employee-add__modal {
+ box-sizing: border-box;
+ width: 90%;
+ height: 80%;
+ position: relative;
+ top: 50px;
+ left: 20px;
+ }
+
+ .employee-detail__modal > h1,
+ .employee-add__modal > h1 {
+ font-size: 20px;
+ }
+ .employee__form {
+ width: 90%;
+ }
+ .employee-detail__modal img,
+ .employee-add__modal img {
+ width: 150px;
+ height: 150px;
+ }
+ .employee-add__modal label,
+ .employee-add__modal input,
+ .employee-add__modal input::placeholder,
+ .employee-detail__modal label,
+ .employee-detail__modal input,
+ .employee-detail__modal input::placeholder {
+ width: 100%;
+ font-size: 14px;
+ }
+ .employee-add__modal input,
+ .employee-detail__modal input {
+ height: 20px;
+ }
+ .add-data__file,
+ .update-data__file {
+ margin-left: 130px;
+ }
+ .add-data {
+ width: 90%;
+ justify-content: space-between;
+ }
+ .detail-button__wrapper > button {
+ width: 80px;
+ height: 40px;
+ margin-top: 20%;
+ font-size: 13px;
+ }
+}
diff --git a/src/css/reset.css b/src/css/reset.css
new file mode 100644
index 000000000..d2999490c
--- /dev/null
+++ b/src/css/reset.css
@@ -0,0 +1,48 @@
+html, body, div, span, applet, object, iframe,
+p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ vertical-align: baseline;
+}
+h1, h2, h3, h4, h5, h6 {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ vertical-align: baseline;
+}
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+ display: block;
+}
+body {
+ line-height: 1;
+}
+ol, ul {
+ list-style: none;
+}
+blockquote, q {
+ quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
\ No newline at end of file
diff --git a/src/css/table.css b/src/css/table.css
new file mode 100644
index 000000000..a7c2496f1
--- /dev/null
+++ b/src/css/table.css
@@ -0,0 +1,75 @@
+.employee-table__warpper {
+ width: 93%;
+ margin: 30px auto;
+}
+
+.employee-table {
+ width: 100%;
+ font-size: 18px;
+}
+
+.employee-table tbody > tr {
+ height: 35px;
+ font-weight: 600;
+ margin-top: 3px;
+ border-top: 1px solid #4b4848;
+ border-bottom: 1px solid #4b4848;
+}
+
+.row-data {
+ width: 100%;
+ text-align: center;
+ transform: scale(1);
+ transition: transform 0.8s;
+ font-weight: 600;
+}
+
+.row-data > input[type="checkbox"] {
+ margin-top: 20px;
+}
+
+.employee-table th,
+.employee-table td {
+ vertical-align: middle;
+}
+
+.row-data:hover {
+ transform: scale(1.02);
+ transition: transform 0.8s;
+}
+.employee__img {
+ margin: 10px;
+ width: 100px;
+ height: 100px;
+ object-fit: cover;
+ border: 2px solid #4b4848;
+ border-radius: 50%;
+ box-shadow: 5px 5px rgba(0, 0, 0, 0.5);
+ cursor: pointer;
+ transform: scale(1);
+ transition: transform 0.8s;
+}
+.employee__img:hover {
+ transform: scale(1.1);
+ transition: transform 0.8s;
+}
+
+@media screen and (max-width: 391px) {
+ .employee-table__warpper {
+ width: 95%;
+ }
+ .employee-table {
+ width: 100%;
+ font-size: 14px;
+ }
+ .employee-table th:nth-of-type(4),
+ .employee-table th:nth-of-type(5),
+ .employee-table td:nth-of-type(4),
+ .employee-table td:nth-of-type(5) {
+ display: none;
+ }
+ .employee__img {
+ width: 80px;
+ height: 80px;
+ }
+}
diff --git a/src/image/free-icon-team-management-6103262.png b/src/image/free-icon-team-management-6103262.png
new file mode 100644
index 000000000..7963b29ec
Binary files /dev/null and b/src/image/free-icon-team-management-6103262.png differ
diff --git a/src/js/animation.js b/src/js/animation.js
new file mode 100644
index 000000000..9411d4537
--- /dev/null
+++ b/src/js/animation.js
@@ -0,0 +1,10 @@
+const animation = document.querySelector(".animation");
+const main = document.querySelector(".main");
+
+main.style.display = "none";
+
+setTimeout(() => {
+ animation.remove();
+ document.querySelector("body").style.background = "none";
+ main.style.display = "block";
+}, 1500);
diff --git a/src/js/firebaseData.js b/src/js/firebaseData.js
new file mode 100644
index 000000000..0d5af9c30
--- /dev/null
+++ b/src/js/firebaseData.js
@@ -0,0 +1,328 @@
+import { checkedArr, checked } from "./main.js";
+import { firebaseConfig } from "./firebaseKeys.js";
+
+firebase.initializeApp(firebaseConfig);
+
+const db = firebase.firestore();
+const storage = firebase.storage();
+
+const detailModal = document.querySelector(".detail-modal__background");
+const closeIcon = document.querySelector(".close__icon");
+
+readEmployee();
+createEmployee();
+deleteEmployee();
+searchEmployee();
+
+closeIcon.addEventListener("click", () => {
+ detailModal.style.display = "none";
+});
+
+// 직원 데이터 create
+function createEmployee() {
+ const employeeForm = document.querySelector("#employee__form");
+
+ employeeForm.addEventListener("submit", (e) => {
+ e.preventDefault(); // 새로고침 방지
+
+ const file = document.querySelector(`input[type="file"]`).files[0];
+
+ const storageRef = storage.ref();
+ const imgUrl = storageRef.child("image/" + file.name);
+ const upload = imgUrl.put(file);
+
+ upload.on(
+ "state_changed",
+ null,
+ (error) => {
+ console.error(error);
+ },
+ () => {
+ // 이미지 업로드 성공 시 firebase에 추가
+ upload.snapshot.ref.getDownloadURL().then((url) => {
+ const name = document.querySelector(".add-data__name");
+ const email = document.querySelector(".add-data__email");
+ const tel = document.querySelector(".add-data__tel");
+ const date = document.querySelector(".add-data__date");
+
+ let employee = {
+ 이미지: url,
+ 이름: name.value,
+ 이메일: email.value,
+ 전화번호: tel.value,
+ 입사날짜: date.value,
+ };
+
+ // 직원 등록
+ db.collection("직원")
+ .add(employee)
+ .then((result) => {
+ alert("직원 등록이 완료되었습니다.");
+ window.location.href = "./index.html";
+ })
+ .catch((err) => {
+ console.log(err);
+ });
+ });
+ }
+ );
+ });
+}
+
+// 직원 데이터 read
+function readEmployee() {
+ db.collection("직원")
+ .orderBy("이름", "asc")
+ .get()
+ .then((result) => {
+ result.forEach((doc) => {
+ const { 이미지, 이름, 이메일, 전화번호, 입사날짜 } = doc.data();
+
+ const employee = {
+ id: doc.id,
+ img: 이미지,
+ name: 이름,
+ email: 이메일,
+ tel: 전화번호,
+ date: 입사날짜,
+ };
+
+ createEmployeeElement(employee);
+ });
+ })
+ .catch((err) => {
+ console.log(err);
+ });
+
+ resetEmployee();
+}
+
+// 직원테이블 엘리먼트 생성
+function createEmployeeElement(employee) {
+ const tr = document.createElement("tr");
+
+ const inputTd = document.createElement("td");
+ const checkbox = document.createElement("input");
+ checkbox.type = "checkbox";
+ checkbox.className = "checkbox";
+ checkbox.addEventListener("click", () => {
+ checked(checkbox);
+ });
+ inputTd.append(checkbox);
+
+ const imgTd = document.createElement("td");
+ const img = document.createElement("img");
+ img.src = employee.img;
+ img.className = "employee__img";
+ imgTd.append(img);
+
+ const name = document.createElement("td");
+ name.innerText = employee.name;
+ const tel = document.createElement("td");
+ tel.innerText = employee.tel;
+ const email = document.createElement("td");
+ email.innerText = employee.email;
+ email.className = "employee--list__email";
+ const dateTime = document.createElement("td");
+ dateTime.innerText = employee.date;
+
+ tr.className = "row-data";
+ tr.append(inputTd, imgTd, name, email, tel, dateTime);
+
+ // 직원 테이블에 추가
+ const employeeTable = document.querySelector(".employee-table");
+ employeeTable.append(tr);
+
+ img.addEventListener("click", () => {
+ setModal(employee);
+ });
+}
+
+// 검색 전 직원 row 삭제
+function resetEmployee() {
+ const rows = document.querySelectorAll(".row-data");
+ rows.forEach((row) => {
+ row.remove();
+ });
+}
+
+// 검색 데이터 read
+function searchData(search, field) {
+ db.collection("직원")
+ .where(field, ">=", search)
+ .where(field, "<=", search + "\uf8ff")
+ .get()
+ .then((result) => {
+ result.forEach((doc) => {
+ const { 이미지, 이름, 이메일, 전화번호, 입사날짜 } = doc.data();
+
+ const employee = {
+ id: doc.id,
+ img: 이미지,
+ name: 이름,
+ email: 이메일,
+ tel: 전화번호,
+ date: 입사날짜,
+ };
+
+ resetEmployee();
+ createEmployeeElement(employee);
+ });
+ })
+ .catch((err) => {
+ console.log(err);
+ });
+}
+
+// 직원 검색
+function searchEmployee() {
+ const searchInput = document.querySelector(".employee-search__input");
+
+ searchInput.addEventListener("input", (e) => {
+ let searchString = e.target.value;
+ if (searchString === "") {
+ resetEmployee();
+ readEmployee();
+ return;
+ } else if (/[a-zA-Z]/.test(searchString)) {
+ // searchString이 영어일 때
+ searchData(searchString, "이메일");
+ } else if (/[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]/.test(searchString)) {
+ // searchString이 한글일 때
+ searchData(searchString, "이름");
+ }
+ });
+}
+
+// 직원 상세 모달
+function setModal(employee) {
+ detailModal.style.display = "block";
+ const detailImg = document.querySelector(".detail__img");
+ detailImg.src = employee.img;
+ const detailName = document.querySelector(".detail__name");
+ detailName.placeholder = employee.name;
+ const detailEmail = document.querySelector(".detail__email");
+ detailEmail.placeholder = employee.email;
+ const detailTel = document.querySelector(".detail__tel");
+ detailTel.placeholder = employee.tel;
+ const detailDate = document.querySelector(".detail__date");
+ detailDate.value = employee.date;
+
+ updateEmployee(employee);
+ deleteDetailEmployee(employee);
+}
+
+// 직원 데이터 update
+function updateEmployee(employee) {
+ let imgFile;
+ const fileDom = document.querySelector(".update-data__file");
+ fileDom.addEventListener("change", () => {
+ const imgBox = document.querySelector(".detail__img");
+
+ if (fileDom.files.length) {
+ let reader = new FileReader();
+
+ reader.onload = function (e) {
+ imgBox.src = e.target.result;
+ };
+ reader.readAsDataURL(fileDom.files[0]);
+ } else {
+ imgBox.src = "";
+ }
+
+ const file = fileDom.files[0];
+
+ let storageRef = storage.ref();
+ let imgUrl = storageRef.child("image/" + file.name);
+ let upload = imgUrl.put(file);
+
+ upload.on(
+ "state_changed",
+ null,
+ (error) => {
+ console.error(error);
+ },
+ () => {
+ upload.snapshot.ref.getDownloadURL().then(async (url) => {
+ imgFile = url;
+ });
+ }
+ );
+ });
+
+ const updateBtn = document.querySelector(".edit__button");
+
+ updateBtn.addEventListener("click", () => {
+ const img = document.querySelector(".detail__img");
+ const name = document.querySelector(".detail__name");
+ const email = document.querySelector(".detail__email");
+ const tel = document.querySelector(".detail__tel");
+ const date = document.querySelector(".detail__date");
+
+ let newEmployee = {
+ 이미지: imgFile || img.src,
+ 이름: name.value || name.placeholder,
+ 이메일: email.value || email.placeholder,
+ 전화번호: tel.value || tel.placeholder,
+ 입사날짜: date.value || date.placeholder,
+ };
+ db.collection("직원")
+ .doc(employee.id)
+ .update(newEmployee)
+ .then(() => {
+ alert("직원 정보가 저장되었습니다.");
+ window.location.href = "./index.html";
+ });
+ });
+}
+
+// 직원 상세 모달에서 delete
+function deleteDetailEmployee(employee) {
+ const deleteBtn = document.querySelector(".delete__button");
+
+ deleteBtn.addEventListener("click", () => {
+ if (confirm("직원을 삭제하시겠습니까?")) {
+ db.collection("직원")
+ .get()
+ .then(async () => {
+ await db.collection("직원").doc(employee.id).delete();
+ alert("직원 정보가 삭제되었습니다.");
+ window.location.href = "./index.html";
+ })
+ .catch((error) => {
+ console.log(error);
+ });
+ }
+ });
+}
+
+// checked 직원 데이터 delete
+function deleteEmployee() {
+ const employeeDeleteBtn = document.querySelector(".employee-delete__button");
+
+ employeeDeleteBtn.addEventListener("click", () => {
+ if (checkedArr.length == 0) {
+ alert("삭제할 직원을 선택해주세요.");
+ return;
+ }
+
+ if (confirm("직원을 삭제하시겠습니까?")) {
+ const arrLength = checkedArr.length;
+
+ for (let i = 0; i < arrLength; i++) {
+ db.collection("직원")
+ .where("이메일", "==", `${checkedArr[i]}`)
+ .get()
+ .then((result) => {
+ result.forEach(async (doc) => {
+ await db.collection("직원").doc(`${doc.id}`).delete();
+ window.location.href = "./index.html";
+ });
+ })
+ .catch((error) => {
+ console.log(error);
+ });
+ }
+ }
+ });
+}
diff --git a/src/js/firebaseKeys.js b/src/js/firebaseKeys.js
new file mode 100644
index 000000000..45525e2bd
--- /dev/null
+++ b/src/js/firebaseKeys.js
@@ -0,0 +1,9 @@
+export const firebaseConfig = {
+ apiKey: "AIzaSyA0BYrAIfJUyc09tEbBSZkRmDW6pRerOG8",
+ authDomain: "employee-management-59973.firebaseapp.com",
+ projectId: "employee-management-59973",
+ storageBucket: "employee-management-59973.appspot.com",
+ messagingSenderId: "541264054170",
+ appId: "1:541264054170:web:9ca90b515958475b6f9c23",
+ measurementId: "G-FT6KJR7WDD",
+};
diff --git a/src/js/main.js b/src/js/main.js
new file mode 100644
index 000000000..176f03eef
--- /dev/null
+++ b/src/js/main.js
@@ -0,0 +1,106 @@
+const modal = document.querySelector(".modal__background");
+const employeeAddBtn = document.querySelector(".employee-add__button");
+const closeIcon = document.querySelector(".close-icon");
+
+export let checkedArr = new Array();
+
+checkedAll();
+readURL();
+
+// 직원 등록 모달창 display
+employeeAddBtn.addEventListener("click", () => {
+ modal.style.display = "block";
+});
+closeIcon.addEventListener("click", () => {
+ closeIcon.parentNode.parentNode.style.display = "none";
+});
+
+function readURL() {
+ const fileDom = document.querySelector(".add-data__file");
+ fileDom.addEventListener("change", () => {
+ const imgBox = document.querySelector(".add-data__img");
+
+ if (fileDom.files.length) {
+ let reader = new FileReader();
+
+ reader.onload = function (e) {
+ console.log(e);
+ imgBox.src = e.target.result;
+ };
+ reader.readAsDataURL(fileDom.files[0]);
+ } else {
+ imgBox.src = "";
+ }
+ });
+}
+
+// checkedArr 배열 중복 제거
+function delDupArr() {
+ let uniqueArr = [];
+ checkedArr.forEach((element) => {
+ if (!uniqueArr.includes(element)) {
+ uniqueArr.push(element);
+ }
+ });
+}
+
+// 전체 체크 or 해제
+function checkedAll() {
+ const checkboxAll = document.querySelector(".checkbox__all");
+
+ checkboxAll.addEventListener("click", () => {
+ const isChecked = checkboxAll.checked;
+ const checkboxes = document.querySelectorAll(".checkbox");
+
+ if (isChecked) {
+ checkedArr.length = 0; // 배열 초기화
+
+ for (const checkbox of checkboxes) {
+ // 전체 체크박스 선택
+ let td = checkbox.parentNode;
+ let tr = td.parentNode;
+ checkbox.checked = true;
+
+ // 선택된 모든 직원의 이메일 배열에 삽입
+ let email = tr.children[3].innerHTML;
+ checkedArr.push(email);
+ delDupArr();
+
+ tr.style.backgroundColor = "#aaa";
+ tr.style.color = "white";
+ }
+ } else {
+ for (const checkbox of checkboxes) {
+ // 전체 체크박스 해제
+ let td = checkbox.parentNode;
+ let tr = td.parentNode;
+ checkbox.checked = false;
+
+ checkedArr = []; // 배열 초기화
+
+ tr.style.backgroundColor = "#fff";
+ tr.style.color = "black";
+ }
+ }
+ console.log(checkedArr);
+ });
+}
+
+export function checked(checkbox) {
+ checkbox.addEventListener("change", () => {
+ let td = checkbox.parentNode;
+ let tr = td.parentNode;
+ let email = tr.children[3].innerHTML;
+
+ // checked에 따라 직원 이메일 배열에 추가 or 삭제
+ if (checkbox.checked) {
+ checkedArr.push(email);
+ } else {
+ checkedArr = checkedArr.filter((element) => element !== email);
+ }
+
+ // checked에 따라 table row 색상 변화
+ tr.style.backgroundColor = checkbox.checked ? "#aaa" : "#fff";
+ tr.style.color = checkbox.checked ? "#fff" : "#000";
+ });
+}
diff --git a/storage.rules b/storage.rules
new file mode 100644
index 000000000..c807920e6
--- /dev/null
+++ b/storage.rules
@@ -0,0 +1,12 @@
+rules_version = '2';
+
+// Craft rules based on data in your Firestore database
+// allow write: if firestore.get(
+// /databases/(default)/documents/users/$(request.auth.uid)).data.isAdmin;
+service firebase.storage {
+ match /b/{bucket}/o {
+ match /{allPaths=**} {
+ allow read, write: if true;
+ }
+ }
+}