diff --git a/.firebaserc b/.firebaserc
new file mode 100644
index 000000000..18bd3657b
--- /dev/null
+++ b/.firebaserc
@@ -0,0 +1,5 @@
+{
+ "projects": {
+ "default": "admin-acaf2"
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..dbb58ffbf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,66 @@
+# 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
diff --git a/404.html b/404.html
new file mode 100644
index 000000000..829eda8fd
--- /dev/null
+++ b/404.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+ Page Not Found
+
+
+
+
+
+
404
+
Page Not Found
+
The specified file was not found on this website. Please check the URL for mistakes and try again.
+
Why am I seeing this?
+
This page was generated by the Firebase Command-Line Interface. To modify it, edit the 404.html
file in your project's configured public
directory.
+
+
+
diff --git a/README.md b/README.md
deleted file mode 100644
index a29ee163b..000000000
--- a/README.md
+++ /dev/null
@@ -1,49 +0,0 @@
-
-# :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)
-
-
diff --git a/css/main.css b/css/main.css
new file mode 100644
index 000000000..6e26d79a8
--- /dev/null
+++ b/css/main.css
@@ -0,0 +1,446 @@
+:root {
+ --text-color: #fff;
+ --background-color: #fff;
+ --accent-color: #fff;
+ --modal-box-width: 600px;
+}
+
+html {
+ width: 70vw;
+ height: 70vw;
+ margin: auto;
+}
+
+* {
+ font-family: "Noto Sans KR", sans-serif;
+}
+
+body {
+ font-family: "Noto Sans KR", sans-serif;
+ min-width: 20rem;
+ background-color: black;
+ display: grid;
+ grid-template-columns: 100px 5fr;
+ grid-template-rows: 100px auto 50px;
+ grid-template-areas:
+ "header header"
+ "aside main"
+ "footer footer";
+}
+
+body.masked > *:not(main, aside) {
+ opacity: 0.2;
+ pointer-events: none; /* 클릭 또는 터치 이벤트 무시 */
+ user-select: none; /* 선택 불가능하게 설정 */
+}
+
+header {
+ grid-area: header;
+ background-color: black;
+}
+
+aside {
+ grid-area: aside;
+ background-color: wheat;
+}
+
+main {
+ grid-area: main;
+ background-color: darkgray;
+}
+
+footer {
+ grid-area: footer;
+ background-color: gray;
+}
+
+a {
+ text-decoration: none;
+ white-space: nowrap;
+ color: #fff;
+}
+
+li {
+ white-space: nowrap;
+}
+
+.navbar {
+ display: flex;
+ align-items: center;
+ height: 100%;
+}
+
+.navbar__logo {
+ display: flex;
+ font-size: 24px;
+ color: #fff;
+ width: 100%;
+}
+
+.navbar__logo i {
+ color: orange;
+ font-size: 40px;
+ padding-right: 10px;
+}
+
+.navbar__menu {
+ display: flex;
+ list-style: none;
+ padding-right: 50px;
+}
+
+.navbar__menu li {
+ border-radius: 4px;
+ padding: 8px 12px;
+}
+
+.navbar__menu li:hover {
+ background-color: orange;
+ transition: background-color 0.5s ease;
+}
+
+.navbar__tooglebtn {
+ display: none;
+ font-size: 24px;
+ color: orange;
+ margin-left: auto;
+}
+
+.staff-box__modal {
+ transform: scale(1);
+ transition: opacity 0.3s, transform 0.3s;
+}
+
+.staff-box {
+ display: flex;
+ justify-content: space-around;
+ flex-wrap: wrap;
+ height: 100%;
+ width: 100%;
+}
+
+.staff-box__item {
+ height: 20rem;
+ width: 20rem;
+ overflow: hidden;
+ margin: 1rem;
+ border-radius: 4%;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ background-color: #f8f9fa;
+ box-shadow: rgba(0, 0, 0, 0.04) 0px 4px 16px 0px;
+}
+
+.staff-box__item:hover {
+ transform: translateY(-10px);
+ transition: box-shadow 0.25s ease-in 0s, transform 0.25s ease-in 0s;
+}
+
+.staff-box__item-id {
+ display: flex;
+ background-color: skyblue;
+}
+
+.staff-box__item-id div {
+ margin-left: auto;
+ padding: 10px 10px 5px 0;
+}
+
+.staff-box__item-img {
+ display: flex;
+ overflow: hidden;
+}
+
+.staff-box__item-img img {
+ width: 180px;
+ border-radius: 50%;
+ margin: auto;
+}
+
+.staff-box__item-detail {
+ display: flex;
+ background-color: skyblue;
+}
+
+.staff-box__item-detail ul {
+ padding: 5px 0 5px 10px;
+}
+
+.staff-box__item-detail li {
+ margin-bottom: 5px;
+}
+
+@media screen and (max-width: 1148px) {
+ /* .navbar {
+ height: auto;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: space-around;
+ padding: 30px 0px;
+ background-color: black;
+ }
+
+ .navbar__menu {
+ display: none;
+ flex-direction: column;
+ align-items: center;
+ width: 100%;
+ margin: auto;
+ }
+
+ .navbar__menu li {
+ text-align: center;
+ width: 100%;
+ }
+
+ .navbar__tooglebtn {
+ display: block;
+ }
+
+ .navbar__menu.active {
+ display: flex;
+ } */
+
+ .staff-box__item {
+ width: 27rem;
+ }
+ html {
+ width: 100vw;
+ }
+}
+
+.modal {
+ opacity: 0;
+ visibility: hidden;
+ transform: scale(0.5);
+ transition: opacity 0.3s, transform 0.3s;
+ /* transform: translateY(-50%);
+ transition: opacity 0.3s, visibility 0.3s, transform 0.3s; */
+}
+
+.modal.active {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.4);
+ z-index: 1;
+ opacity: 1;
+ visibility: visible;
+ transform: scale(1);
+ /* transform: translateY(0); */
+}
+
+.modal-box {
+ display: none;
+}
+
+.modal-box.active {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: var(--modal-box-width);
+ height: calc(4.5 / 3.5 * var(--modal-box-width));
+ background-color: #fff;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ border-radius: 4%;
+ display: flex;
+ justify-content: space-between;
+ flex-direction: column;
+ overflow: hidden;
+}
+
+.modal-box__nav {
+ display: flex;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+.modal-box__nav__submitTooglebtn {
+ display: none;
+}
+
+.modal-box__nav__submitTooglebtn.active {
+ margin-left: auto;
+ display: block;
+}
+
+.modal-box__nav__submitTooglebtn a i {
+ font-size: 30px;
+ color: black;
+ padding: 10px 10px 5px 0;
+}
+
+.modal-box__nav__modifyTooglebtn {
+ display: none;
+}
+
+.modal-box__nav__modifyTooglebtn.active {
+ margin-left: auto;
+ display: block;
+}
+
+.modal-box__nav__modifyTooglebtn a i {
+ font-size: 30px;
+ color: black;
+ padding: 10px 10px 5px 0;
+}
+
+.modal-box__inputInfo {
+ display: flex;
+ padding: 10px 0 5px 20px;
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+.modal-box__inputInfo div {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+
+.modal-box__inputInfo div input,
+.modal-box__inputInfo select {
+ border-radius: 4px;
+}
+
+.submitBtn {
+ display: none;
+}
+
+.submitBtn.active {
+ display: block;
+ width: auto;
+ height: 100px;
+}
+
+.submitButton {
+ display: none;
+}
+
+.submitButton.active {
+ display: block;
+ width: 100%;
+ height: 100%;
+ font-size: 20px;
+ margin: auto;
+ background-color: #f8f9fa;
+ border: none;
+}
+
+.submitButton.active:hover {
+ transition: background-color 0.5s ease;
+ background-color: orange;
+}
+
+.modifyBtn {
+ display: none;
+}
+
+.modifyBtn.active {
+ display: block;
+ width: auto;
+ height: 100px;
+}
+
+.modifyButton {
+ display: none;
+}
+
+.modifyButton.active {
+ display: block;
+ width: 100%;
+ height: 101%;
+ font-size: 20px;
+ margin: auto;
+ background-color: #f8f9fa;
+ border: none;
+}
+
+.modifyButton.active:hover {
+ transition: background-color 0.5s ease;
+ background-color: orange;
+}
+
+.modal-box__inputImg {
+ display: flex;
+ overflow: auto;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+ background-color: #f8f9fa;
+}
+
+.modal-box__inputImg img {
+ border-radius: 50%;
+ width: auto;
+ height: 100%;
+}
+
+.preview-image {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ border-radius: 50%;
+}
+
+.staff-box__item.active {
+ box-shadow: rgba(0, 255, 0, 1) 0px 4px 16px 0px;
+}
+
+.staff-box__item.active.select {
+ box-shadow: rgba(255, 0, 0, 1) 0px 4px 16px 0px;
+}
+
+.staff-box__delete {
+ display: none;
+}
+
+.staff-box__delete.active {
+ display: flex;
+ width: 100%;
+ height: 100%;
+ justify-content: center;
+ align-items: center;
+ background-color: rgba(0, 255, 0, 1);
+}
+
+.staff-box__delete.active:hover {
+ background-color: rgba(255, 0, 0, 1);
+ transition: background-color 0.5s ease;
+}
+
+.staff-box__delete.active i {
+ font-size: 100px;
+ color: #fff;
+ padding: 20px 6px;
+}
+
+.staff-box__item.modify {
+ box-shadow: orange 0px 4px 16px 0px;
+}
+
+.staff-box__modify {
+ display: none;
+}
+
+.staff-box__modify.active {
+ display: flex;
+ width: 100%;
+ height: 100%;
+ justify-content: center;
+ align-items: center;
+ background-color: gray;
+}
+
+.staff-box__modify.active:hover {
+ background-color: orange;
+ transition: background-color 0.5s ease;
+}
+
+.staff-box__modify.active i {
+ font-size: 100px;
+ color: #fff;
+ padding: 20px 6px;
+}
diff --git a/css/reset.css b/css/reset.css
new file mode 100644
index 000000000..82935aece
--- /dev/null
+++ b/css/reset.css
@@ -0,0 +1,124 @@
+html,
+body,
+div,
+span,
+applet,
+object,
+iframe,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+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;
+}
+/* HTML5 display-role reset for older browsers */
+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;
+}
diff --git a/firebase.json b/firebase.json
new file mode 100644
index 000000000..6cf82049c
--- /dev/null
+++ b/firebase.json
@@ -0,0 +1,33 @@
+{
+ "firestore": {
+ "rules": "firestore.rules",
+ "indexes": "firestore.indexes.json"
+ },
+ "hosting": {
+ "public": "public",
+ "ignore": [
+ "firebase.json",
+ "**/.*",
+ "**/node_modules/**"
+ ]
+ },
+ "storage": {
+ "rules": "storage.rules"
+ },
+ "functions": [
+ {
+ "source": "functions",
+ "codebase": "default"
+ },
+ {
+ "source": "hojin",
+ "codebase": "hojin",
+ "ignore": [
+ "node_modules",
+ ".git",
+ "firebase-debug.log",
+ "firebase-debug.*.log"
+ ]
+ }
+ ]
+}
diff --git a/firestore.indexes.json b/firestore.indexes.json
new file mode 100644
index 000000000..415027e5d
--- /dev/null
+++ b/firestore.indexes.json
@@ -0,0 +1,4 @@
+{
+ "indexes": [],
+ "fieldOverrides": []
+}
diff --git a/firestore.rules b/firestore.rules
new file mode 100644
index 000000000..cd1a4346f
--- /dev/null
+++ b/firestore.rules
@@ -0,0 +1,9 @@
+rules_version = '2';
+
+service cloud.firestore {
+ match /databases/{database}/documents {
+ match /{document=**} {
+ allow read, write: if false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/hojin/.gitignore b/hojin/.gitignore
new file mode 100644
index 000000000..40b878db5
--- /dev/null
+++ b/hojin/.gitignore
@@ -0,0 +1 @@
+node_modules/
\ No newline at end of file
diff --git a/hojin/index.js b/hojin/index.js
new file mode 100644
index 000000000..e81477f69
--- /dev/null
+++ b/hojin/index.js
@@ -0,0 +1,19 @@
+/**
+ * Import function triggers from their respective submodules:
+ *
+ * const {onCall} = require("firebase-functions/v2/https");
+ * const {onDocumentWritten} = require("firebase-functions/v2/firestore");
+ *
+ * See a full list of supported triggers at https://firebase.google.com/docs/functions
+ */
+
+const {onRequest} = require("firebase-functions/v2/https");
+const logger = require("firebase-functions/logger");
+
+// Create and deploy your first functions
+// https://firebase.google.com/docs/functions/get-started
+
+// exports.helloWorld = onRequest((request, response) => {
+// logger.info("Hello logs!", {structuredData: true});
+// response.send("Hello from Firebase!");
+// });
diff --git a/hojin/package.json b/hojin/package.json
new file mode 100644
index 000000000..392196b98
--- /dev/null
+++ b/hojin/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "functions",
+ "description": "Cloud Functions for Firebase",
+ "scripts": {
+ "serve": "firebase emulators:start --only functions",
+ "shell": "firebase functions:shell",
+ "start": "npm run shell",
+ "deploy": "firebase deploy --only functions",
+ "logs": "firebase functions:log"
+ },
+ "engines": {
+ "node": "18"
+ },
+ "main": "index.js",
+ "dependencies": {
+ "firebase-admin": "^11.8.0",
+ "firebase-functions": "^4.3.1"
+ },
+ "devDependencies": {
+ "firebase-functions-test": "^3.1.0"
+ },
+ "private": true
+}
diff --git a/index.html b/index.html
new file mode 100644
index 000000000..7106a6aa4
--- /dev/null
+++ b/index.html
@@ -0,0 +1,203 @@
+
+
+
+
+
+
+ 직원 관리 시스템
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/js/delete.js b/js/delete.js
new file mode 100644
index 000000000..1d9096d9b
--- /dev/null
+++ b/js/delete.js
@@ -0,0 +1,63 @@
+const selectedElements = document.querySelectorAll(".staff-box__item.active.select");
+const selectedEmployeeIDs = [...selectedElements].map((item) => item.getAttribute("data-id"));
+
+function toggleClassOnElement(element, className) {
+ element?.classList.toggle(className);
+}
+
+document.body.addEventListener("click", function (event) {
+ const clickedItem = event.target.closest(".staff-box__item");
+ if (!clickedItem || !clickedItem.classList.contains("active")) return;
+
+ toggleClassOnElement(clickedItem, "select");
+});
+
+document.querySelector(".employee-delete").addEventListener("click", function () {
+ [...document.querySelectorAll(".staff-box__item")].forEach((item) => toggleClassOnElement(item, "active"));
+ toggleClassOnElement(document.querySelector("body"), "masked");
+ toggleClassOnElement(document.querySelector(".staff-box__delete"), "active");
+});
+
+document.querySelector(".staff-box__delete").addEventListener("click", async function () {
+ const selectedElements = document.querySelectorAll(".staff-box__item.active.select");
+ const selectedEmployeeIDs = [...selectedElements].map((item) => item.getAttribute("data-id"));
+
+ document.querySelectorAll(".staff-box__item.active.select").forEach((item) => item.remove());
+
+ await deleteDataFromFirebase(selectedEmployeeIDs);
+
+ [...document.querySelectorAll(".staff-box__item")].forEach((item) => toggleClassOnElement(item, "active"));
+ toggleClassOnElement(document.querySelector("body"), "masked");
+ toggleClassOnElement(document.querySelector(".staff-box__delete"), "active");
+});
+
+const db = firebase.firestore();
+const storage = firebase.storage().ref();
+
+async function deleteDataFromFirebase(selectedEmployeeIDs) {
+ try {
+ const employeesRef = db.collection("user");
+
+ for (const id of selectedEmployeeIDs) {
+ const employeeDoc = await employeesRef.doc(id).get();
+ const employeeData = employeeDoc.data();
+
+ if (employeeData && employeeData.imageUrl) {
+ const imageUrl = employeeData.imageUrl;
+ const cleanUrl = imageUrl.split("?")[0];
+ const imageKey = cleanUrl.split("%2F").pop();
+ const imageKeyDecoded = decodeURIComponent(imageKey);
+
+ try {
+ await storage.child(`image/${imageKeyDecoded}`).delete();
+ } catch (error) {
+ console.error("Error processing imageUrl for employee:", id, error);
+ }
+ }
+
+ await employeesRef.doc(id).delete();
+ }
+ } catch (error) {
+ console.error("Error:", error);
+ }
+}
diff --git a/js/main.js b/js/main.js
new file mode 100644
index 000000000..62f6e00c2
--- /dev/null
+++ b/js/main.js
@@ -0,0 +1,30 @@
+const toogleBtn = document.querySelector(".navbar__tooglebtn");
+
+function toggleClassOnElement(element, className) {
+ element?.classList.toggle(className);
+}
+
+const toggleNavbar = () => {
+ toggleClassOnElement(document.querySelector(".navbar__menu"), "active");
+ console.log("click");
+};
+
+toogleBtn.addEventListener("click", toggleNavbar);
+
+document.getElementById("image").addEventListener("change", function (e) {
+ if (e.target.files && e.target.files[0]) {
+ const reader = new FileReader();
+
+ reader.onload = function (e) {
+ document.querySelector(".modal-box__inputImg").innerHTML = ``;
+ };
+
+ reader.readAsDataURL(e.target.files[0]);
+ }
+});
+
+document.addEventListener("dragstart", function (event) {
+ if (event.target.tagName == "IMG") {
+ event.preventDefault();
+ }
+});
diff --git a/js/modify.js b/js/modify.js
new file mode 100644
index 000000000..c2b1fd4ef
--- /dev/null
+++ b/js/modify.js
@@ -0,0 +1,196 @@
+const modal = document.querySelector(".modal");
+const modalBox = document.querySelector(".modal-box");
+const form = document.querySelector("form");
+const modifyButton = document.querySelector(".modifyButton");
+
+const db = firebase.firestore();
+const storage = firebase.storage();
+
+function createStaffTemplate(docData, doc) {
+ return `
+
+
+
${docData.employeeNumber}
+
+
+
+
+
+
+ - ${docData.userName}
+ - ${docData.dept} / ${docData.position}
+ - ${docData.userNumber}
+
+
+
+ `;
+}
+
+function toggleClassOnElement(element, className) {
+ element?.classList.toggle(className);
+}
+
+const toggleEmployeeModify = () => {
+ [...document.querySelectorAll(".staff-box__item")].forEach((item) => toggleClassOnElement(item, "modify"));
+ toggleClassOnElement(document.querySelector(".modal-box__nav__modifyTooglebtn"), "active");
+ toggleClassOnElement(document.querySelector(".staff-box__modify"), "active");
+ toggleClassOnElement(document.querySelector(".employee-modify"), "active");
+ toggleClassOnElement(document.querySelector(".modifyButton"), "active");
+ toggleClassOnElement(document.querySelector(".modifyBtn"), "active");
+ toggleClassOnElement(document.querySelector("body"), "masked");
+};
+
+const toggleModal = () => {
+ toggleClassOnElement(document.querySelector(".modal-box"), "active");
+ toggleClassOnElement(document.querySelector(".modal"), "active");
+};
+
+document.querySelector(".modal-box__nav__modifyTooglebtn").addEventListener("click", toggleModal);
+document.querySelector(".employee-modify").addEventListener("click", toggleEmployeeModify);
+document.querySelector(".staff-box__modify").addEventListener("click", toggleEmployeeModify);
+
+function populateModalForm(data) {
+ modalBox.querySelector("input[name='employeeNumber']").value = data.employeeNumber;
+ modalBox.querySelector("input[name='userName']").value = data.userName;
+ modalBox.querySelector("select[name='dept']").value = data.dept;
+ modalBox.querySelector("select[name='position']").value = data.position;
+ modalBox.querySelector("input[name='userNumber']").value = data.userNumber;
+ modalBox.querySelector(".modal-box__inputImg img").src = data.imageUrl;
+}
+
+document.body.addEventListener("click", async function (event) {
+ const clickedItem = event.target.closest(".staff-box__item.modify");
+
+ if (clickedItem && clickedItem.classList.contains("modify")) {
+ const previouslySelected = document.querySelector(".staff-box__item.modify.select");
+ if (previouslySelected) {
+ previouslySelected.classList.remove("select");
+ }
+
+ clickedItem.classList.add("select");
+
+ const selectedElement = document.querySelector(".staff-box__item.modify.select");
+ const docId = selectedElement.getAttribute("data-id");
+
+ fetchDataFromFirebase(docId);
+
+ toggleClassOnElement(modal, "active");
+ toggleClassOnElement(modalBox, "active");
+ }
+});
+
+function fetchDataFromFirebase(docId) {
+ const userRef = db.collection("user").doc(docId);
+
+ userRef
+ .get()
+ .then((doc) => {
+ if (doc.exists) {
+ const userData = doc.data();
+
+ populateModalForm(userData);
+ } else {
+ console.error("No such document!");
+ }
+ })
+ .catch((error) => {
+ console.error("Error getting document:", error);
+ });
+}
+
+modifyButton.addEventListener("click", async function (event) {
+ const selectedElement = document.querySelector(".staff-box__item.modify.select");
+ const docId = selectedElement.getAttribute("data-id");
+ deleteImgFromFirebase([docId]);
+ toggleModal();
+ event.preventDefault();
+
+ const formData = new FormData(form);
+ const updatedData = {};
+ formData.forEach((value, key) => (updatedData[key] = value));
+
+ let file = document.querySelector("#image").files[0];
+ if (file) {
+ let storageRef = storage.ref();
+ let savePath = storageRef.child("image/" + file.name);
+ let uploadImage = savePath.put(file);
+
+ await new Promise((resolve, reject) => {
+ uploadImage.on(
+ "state_changed",
+ null,
+ (error) => {
+ console.error("실패사유는", error);
+ reject(error);
+ },
+ async () => {
+ const url = await uploadImage.snapshot.ref.getDownloadURL();
+ updatedData.imageUrl = url;
+ resolve();
+ }
+ );
+ });
+ }
+
+ const selectedItem = document.querySelector(".staff-box__item.modify.select");
+ if (selectedItem) {
+ const docId = selectedItem.getAttribute("data-id");
+ await updateEmployeeInFirebase(docId, updatedData);
+ loadStaffDataForModify();
+ form.reset();
+ } else {
+ console.error("No employee item selected.");
+ }
+ selectedItem.classList.remove("select");
+ [...document.querySelectorAll(".staff-box__item")].forEach((item) => toggleClassOnElement(item, "modify"));
+});
+
+async function updateEmployeeInFirebase(docId, data) {
+ try {
+ await db.collection("user").doc(docId).update(data);
+ } catch (error) {
+ console.error("Error updating the employee data in Firebase:", error);
+ }
+}
+
+async function loadStaffDataForModify() {
+ try {
+ const staffBox = document.querySelector(".staff-box");
+ staffBox.innerHTML = ""; // clear existing data
+ const snapshot = await db.collection("user").get();
+ snapshot.forEach((doc) => {
+ const staffElement = createStaffTemplate(doc.data(), doc);
+ staffBox.insertAdjacentHTML("beforeend", staffElement);
+ });
+ [...document.querySelectorAll(".staff-box__item")].forEach((item) => toggleClassOnElement(item, "modify"));
+ } catch (error) {
+ console.error("Error loading staff data from Firebase:", error);
+ }
+}
+
+async function deleteImgFromFirebase(selectedEmployeeIDs) {
+ try {
+ const employeesRef = db.collection("user");
+
+ for (const id of selectedEmployeeIDs) {
+ const employeeDoc = await employeesRef.doc(id).get();
+ const employeeData = employeeDoc.data();
+
+ if (employeeData && employeeData.imageUrl) {
+ const imageUrl = employeeData.imageUrl;
+ const cleanUrl = imageUrl.split("?")[0];
+ const imageKey = cleanUrl.split("%2F").pop();
+ const imageKeyDecoded = decodeURIComponent(imageKey);
+ console.log(imageKeyDecoded);
+
+ try {
+ await storage.ref().child(`image/${imageKeyDecoded}`).delete();
+ } catch (error) {
+ console.error("Error deleting imageUrl for employee:", id, error);
+ }
+ }
+ }
+ } catch (error) {
+ console.error("Error in deleteImgFromFirebase:", error);
+ }
+}
diff --git a/js/register.js b/js/register.js
new file mode 100644
index 000000000..d8dddd2e6
--- /dev/null
+++ b/js/register.js
@@ -0,0 +1,98 @@
+const form = document.querySelector("form");
+const fileInput = document.querySelector('input[type="file"]');
+const submitButton = document.querySelector(".submitButton");
+const staffBox = document.querySelector(".staff-box");
+
+function toggleClassOnElement(element, className) {
+ element?.classList.toggle(className);
+}
+
+const toggleEmployeeActive = () => {
+ toggleClassOnElement(document.querySelector(".modal"), "active");
+ toggleClassOnElement(document.querySelector(".modal-box"), "active");
+ toggleClassOnElement(document.querySelector(".submitButton"), "active");
+ toggleClassOnElement(document.querySelector(".submitBtn"), "active");
+ toggleClassOnElement(document.querySelector(".modal-box__nav__submitTooglebtn"), "active");
+ form.reset();
+ document.querySelector(".modal-box__inputImg").innerHTML = "";
+};
+
+document.querySelector(".modal-box__nav__submitTooglebtn").addEventListener("click", toggleEmployeeActive);
+document.querySelector(".employee-register").addEventListener("click", toggleEmployeeActive);
+
+const db = firebase.firestore();
+const storage = firebase.storage();
+
+$(".submitButton").click(function (event) {
+ submitButton.disabled = true;
+
+ event.preventDefault();
+
+ let file = document.querySelector("#image").files[0];
+ let storageRef = storage.ref();
+ let savePath = storageRef.child("image/" + file.name);
+ let uploadImage = savePath.put(file);
+
+ uploadImage.on(
+ "state_changed",
+ null,
+ (error) => {
+ console.error("실패사유는", error);
+ },
+ () => {
+ uploadImage.snapshot.ref.getDownloadURL().then((url) => {
+ let uploadUserdata = {
+ userName: $("#userName").val(),
+ employeeNumber: $("#employeeNumber").val(),
+ dept: $("#dept").val(),
+ position: $("#position").val(),
+ userNumber: $("#userNumber").val(),
+ imageUrl: url,
+ date: new Date(),
+ };
+
+ db.collection("user")
+ .add(uploadUserdata)
+ .then((docRef) => {
+ const staffElement = createStaffTemplate(uploadUserdata, docRef);
+ staffBox.insertAdjacentHTML("beforeend", staffElement);
+ toggleEmployeeActive();
+ submitButton.disabled = false;
+ })
+ .catch((error) => {
+ console.error("Error adding document: ", error);
+ });
+ });
+ }
+ );
+});
+
+function createStaffTemplate(docData, doc) {
+ return `
+
+
+
${docData.employeeNumber}
+
+
+
+
+
+
+ - ${docData.userName}
+ - ${docData.dept} / ${docData.position}
+ - ${docData.userNumber}
+
+
+
+ `;
+}
+
+async function loadStaffData() {
+ const snapshot = await db.collection("user").get();
+ snapshot.forEach((doc) => {
+ const staffElement = createStaffTemplate(doc.data(), doc);
+ staffBox.insertAdjacentHTML("beforeend", staffElement);
+ });
+}
+
+loadStaffData();
diff --git a/readme.MD b/readme.MD
new file mode 100644
index 000000000..014058925
--- /dev/null
+++ b/readme.MD
@@ -0,0 +1,19 @@
+# 직원 관리 시스템
+
+배포주소 :https://frolicking-meerkat-ab332e.netlify.app/
+
+## 직원등록
+
+![직원등록](https://im.ezgif.com/tmp/ezgif-1-bca4cdb3cd.gif)
+
+## 직원삭제
+
+![직원삭제](https://im.ezgif.com/tmp/ezgif-1-8435bf6cdb.gif)
+
+## 직원수정
+
+![직원수정](https://im.ezgif.com/tmp/ezgif-1-6955ca35cd.gif)
+
+## userflow
+
+![userflow](https://github.com/KDT1-FE/Y_FE_JAVASCRIPT_PICTURE/assets/134940630/50ad8421-5253-425e-907e-1cb93e9778f7)
diff --git a/storage.rules b/storage.rules
new file mode 100644
index 000000000..f08744f03
--- /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 false;
+ }
+ }
+}