From c322c6bc31620400afc390e5f405740fefd6f5fd Mon Sep 17 00:00:00 2001
From: Jang seo yun <81643702+pipisebastian@users.noreply.github.com>
Date: Thu, 23 May 2024 13:24:44 +0900
Subject: [PATCH] =?UTF-8?q?Feature/#82=20=EC=BB=A4=EB=A6=AC=ED=81=98?=
=?UTF-8?q?=EB=9F=BC=20=ED=8E=B8=EC=A7=91=20=EB=AA=A8=EB=8B=AC=EC=B0=BD=20?=
=?UTF-8?q?=EA=B5=AC=ED=98=84=20(#208)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat: react beautiful dnd 라이브러리 설치
#82
* feat: 커리큘럼 편집 모달창 구현
#82
* refactor: components 필요없는 파일 제거
#82
* refactor: 주석 제거
#82
* feat: 전체보기 버튼 추가
---
package-lock.json | 111 +++++++++++-
package.json | 2 +
.../team/[teamId]/study/[studyId]/page.tsx | 2 +-
src/components/CurriculumCard/index.tsx | 48 -----
.../CurriculumCard/CurriculumItem/index.tsx | 0
src/containers/study/CurriculumCard/index.tsx | 77 ++++++++
.../study}/CurriculumCard/types.ts | 0
.../study/CurriculumModal/index.tsx | 171 ++++++++++++++++++
8 files changed, 357 insertions(+), 54 deletions(-)
delete mode 100644 src/components/CurriculumCard/index.tsx
rename src/{components => containers/study}/CurriculumCard/CurriculumItem/index.tsx (100%)
create mode 100644 src/containers/study/CurriculumCard/index.tsx
rename src/{components => containers/study}/CurriculumCard/types.ts (100%)
create mode 100644 src/containers/study/CurriculumModal/index.tsx
diff --git a/package-lock.json b/package-lock.json
index 9a633f05..dd35a588 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,6 +17,7 @@
"jotai": "^2.6.1",
"next": "14.0.4",
"react": "^18",
+ "react-beautiful-dnd": "^13.1.1",
"react-datepicker": "^6.6.0",
"react-dom": "^18",
"react-icons": "^5.0.1",
@@ -27,6 +28,7 @@
"@swc-jotai/react-refresh": "^0.1.0",
"@types/node": "^20",
"@types/react": "^18",
+ "@types/react-beautiful-dnd": "^13.1.8",
"@types/react-dom": "^18",
"eslint": "^8.56.0",
"eslint-config-airbnb": "^19.0.4",
@@ -1926,6 +1928,15 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@types/hoist-non-react-statics": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz",
+ "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==",
+ "dependencies": {
+ "@types/react": "*",
+ "hoist-non-react-statics": "^3.3.0"
+ }
+ },
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -1968,20 +1979,27 @@
"node_modules/@types/prop-types": {
"version": "15.7.11",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
- "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==",
- "devOptional": true
+ "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng=="
},
"node_modules/@types/react": {
"version": "18.2.47",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.47.tgz",
"integrity": "sha512-xquNkkOirwyCgoClNk85BjP+aqnIS+ckAJ8i37gAbDs14jfW/J23f2GItAf33oiUPQnqNMALiFeoM9Y5mbjpVQ==",
- "devOptional": true,
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2"
}
},
+ "node_modules/@types/react-beautiful-dnd": {
+ "version": "13.1.8",
+ "resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.8.tgz",
+ "integrity": "sha512-E3TyFsro9pQuK4r8S/OL6G99eq7p8v29sX0PM7oT8Z+PJfZvSQTx4zTQbUJ+QZXioAF0e7TGBEcA1XhYhCweyQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
"node_modules/@types/react-dom": {
"version": "18.2.18",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz",
@@ -1991,11 +2009,21 @@
"@types/react": "*"
}
},
+ "node_modules/@types/react-redux": {
+ "version": "7.1.33",
+ "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.33.tgz",
+ "integrity": "sha512-NF8m5AjWCkert+fosDsN3hAlHzpjSiXlVy9EgQEmLoBhaNXbmyeGs/aj5dQzKuF+/q+S7JQagorGDW8pJ28Hmg==",
+ "dependencies": {
+ "@types/hoist-non-react-statics": "^3.3.0",
+ "@types/react": "*",
+ "hoist-non-react-statics": "^3.3.0",
+ "redux": "^4.0.0"
+ }
+ },
"node_modules/@types/scheduler": {
"version": "0.16.8",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
- "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==",
- "devOptional": true
+ "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A=="
},
"node_modules/@types/semver": {
"version": "7.5.6",
@@ -4597,6 +4625,11 @@
"node": ">=10"
}
},
+ "node_modules/memoize-one": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
+ "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
+ },
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -5075,6 +5108,11 @@
}
]
},
+ "node_modules/raf-schd": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz",
+ "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ=="
+ },
"node_modules/react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
@@ -5086,6 +5124,24 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-beautiful-dnd": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz",
+ "integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.9.2",
+ "css-box-model": "^1.2.0",
+ "memoize-one": "^5.1.1",
+ "raf-schd": "^4.0.2",
+ "react-redux": "^7.2.0",
+ "redux": "^4.0.4",
+ "use-memo-one": "^1.1.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.5 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/react-clientside-effect": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz",
@@ -5178,6 +5234,35 @@
"react-dom": "^15.5.x || ^16.x || ^17.x || ^18.x"
}
},
+ "node_modules/react-redux": {
+ "version": "7.2.9",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz",
+ "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.15.4",
+ "@types/react-redux": "^7.1.20",
+ "hoist-non-react-statics": "^3.3.2",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.7.2",
+ "react-is": "^17.0.2"
+ },
+ "peerDependencies": {
+ "react": "^16.8.3 || ^17 || ^18"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ },
+ "react-native": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-redux/node_modules/react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
+ },
"node_modules/react-remove-scroll": {
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz",
@@ -5261,6 +5346,14 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
+ "node_modules/redux": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
+ "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
+ "dependencies": {
+ "@babel/runtime": "^7.9.2"
+ }
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz",
@@ -5989,6 +6082,14 @@
}
}
},
+ "node_modules/use-memo-one": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz",
+ "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/use-sidecar": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
diff --git a/package.json b/package.json
index 75909be4..58801b7d 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,7 @@
"jotai": "^2.6.1",
"next": "14.0.4",
"react": "^18",
+ "react-beautiful-dnd": "^13.1.1",
"react-datepicker": "^6.6.0",
"react-dom": "^18",
"react-icons": "^5.0.1",
@@ -28,6 +29,7 @@
"@swc-jotai/react-refresh": "^0.1.0",
"@types/node": "^20",
"@types/react": "^18",
+ "@types/react-beautiful-dnd": "^13.1.8",
"@types/react-dom": "^18",
"eslint": "^8.56.0",
"eslint-config-airbnb": "^19.0.4",
diff --git a/src/app/team/[teamId]/study/[studyId]/page.tsx b/src/app/team/[teamId]/study/[studyId]/page.tsx
index aa725f29..6e2c479c 100644
--- a/src/app/team/[teamId]/study/[studyId]/page.tsx
+++ b/src/app/team/[teamId]/study/[studyId]/page.tsx
@@ -5,9 +5,9 @@ import NextLink from 'next/link';
import { useState } from 'react';
import { MdOutlineArrowForwardIos } from 'react-icons/md';
-import CurriculumCard from '@/components/CurriculumCard';
import StudyAssetCard from '@/components/StudyAssetCard';
import Title from '@/components/Title';
+import CurriculumCard from '@/containers/study/CurriculumCard';
import Feed from '@/containers/study/Feed';
import DeleteStudyModal from '@/containers/study/Modal/DeleteStudyModal';
import TerminateStudyModal from '@/containers/study/Modal/TerminateStudyModal';
diff --git a/src/components/CurriculumCard/index.tsx b/src/components/CurriculumCard/index.tsx
deleted file mode 100644
index c2921870..00000000
--- a/src/components/CurriculumCard/index.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import { Flex, Image, Card } from '@chakra-ui/react';
-
-import CurriculumCardData from '@/mocks/curriculum';
-
-import CurriculumItem from './CurriculumItem';
-
-const CurriculumCard = () => {
- return (
-
-
-
-
- {CurriculumCardData.map((data) => {
- return (
-
- );
- })}
-
-
-
- );
-};
-
-export default CurriculumCard;
diff --git a/src/components/CurriculumCard/CurriculumItem/index.tsx b/src/containers/study/CurriculumCard/CurriculumItem/index.tsx
similarity index 100%
rename from src/components/CurriculumCard/CurriculumItem/index.tsx
rename to src/containers/study/CurriculumCard/CurriculumItem/index.tsx
diff --git a/src/containers/study/CurriculumCard/index.tsx b/src/containers/study/CurriculumCard/index.tsx
new file mode 100644
index 00000000..6ffed9eb
--- /dev/null
+++ b/src/containers/study/CurriculumCard/index.tsx
@@ -0,0 +1,77 @@
+'use client';
+
+import { Flex, Image, Card, IconButton, useDisclosure, Text } from '@chakra-ui/react';
+import { MdOutlineArrowForwardIos } from 'react-icons/md';
+
+import CurriculumCardData from '@/mocks/curriculum';
+
+import CurriculumItem from './CurriculumItem';
+import ActionModal from '../../../components/Modal/ActionModal';
+import CurriculumModal from '../CurriculumModal';
+
+const CurriculumCard = () => {
+ const { isOpen: isActionModalOpen, onOpen: onActionModalOpen, onClose: onActionModalClose } = useDisclosure();
+
+ return (
+
+
+ } size="icon_sm" variant="icon_orange" />
+ 전체 보기
+
+
+
+
+
+
+ {CurriculumCardData.map((data) => {
+ return (
+
+ );
+ })}
+
+
+
+ {
+ onActionModalClose();
+ }}
+ mainButtonText="다음"
+ onMainButtonClick={() => {
+ onActionModalClose();
+ }}
+ >
+
+
+
+ );
+};
+
+export default CurriculumCard;
diff --git a/src/components/CurriculumCard/types.ts b/src/containers/study/CurriculumCard/types.ts
similarity index 100%
rename from src/components/CurriculumCard/types.ts
rename to src/containers/study/CurriculumCard/types.ts
diff --git a/src/containers/study/CurriculumModal/index.tsx b/src/containers/study/CurriculumModal/index.tsx
new file mode 100644
index 00000000..2f9d5dd5
--- /dev/null
+++ b/src/containers/study/CurriculumModal/index.tsx
@@ -0,0 +1,171 @@
+/* eslint-disable react/jsx-props-no-spreading */
+
+'use client';
+
+import { Flex, IconButton, Input, InputGroup, InputRightElement, Text } from '@chakra-ui/react';
+import React, { useState, ChangeEvent } from 'react';
+import { DragDropContext, Draggable, Droppable, DropResult } from 'react-beautiful-dnd';
+import { AiOutlinePlus } from 'react-icons/ai';
+import { BiEdit, BiTrash } from 'react-icons/bi';
+
+import AutoResizeTextarea from '@/components/AutoResizeTextarea';
+
+const CurriculumModal = () => {
+ const [curriculums, setCurriculums] = useState([
+ { key: 'item-1', itemOrder: 1, content: 'item-1', isEdit: false },
+ { key: 'item-2', itemOrder: 2, content: 'item-2', isEdit: false },
+ { key: 'item-3', itemOrder: 3, content: 'item-3', isEdit: false },
+ { key: 'item-4', itemOrder: 4, content: 'item-4', isEdit: false },
+ ]);
+
+ const [addCurriculum, setAddCurriculum] = useState('');
+ const editCurriculumRef = React.useRef(null);
+
+ const handleNewContentChange = (event: ChangeEvent) => {
+ setAddCurriculum(event.target.value);
+ };
+
+ const handleEditCurriculumChange = (event: ChangeEvent, index: number) => {
+ const { value } = event.target;
+
+ setCurriculums((prevCurriculums) => {
+ const updatedCurriculums = [...prevCurriculums];
+ updatedCurriculums[index].content = value;
+ return updatedCurriculums;
+ });
+ };
+
+ const handleAddButtonClick = () => {
+ if (addCurriculum.trim() !== '') {
+ setCurriculums((prevCurriculums) => [
+ ...prevCurriculums,
+ {
+ key: `item-${prevCurriculums.length + 1}`,
+ itemOrder: prevCurriculums.length + 1,
+ content: addCurriculum,
+ isEdit: false,
+ },
+ ]);
+ setAddCurriculum('');
+ }
+ };
+
+ const handleEditButtonClick = (index: number) => {
+ setCurriculums((prevCurriculums) => {
+ const updatedCurriculums = [...prevCurriculums];
+ updatedCurriculums[index].isEdit = !updatedCurriculums[index].isEdit;
+
+ return updatedCurriculums;
+ });
+ };
+
+ const handleDeleteButtonClick = (index: number) => {
+ setCurriculums((prevCurriculums) =>
+ prevCurriculums
+ .filter((_, idx) => idx !== index)
+ .map((curriculum, idx) => ({
+ ...curriculum,
+ itemOrder: idx + 1,
+ })),
+ );
+ };
+
+ const onDragEnd = (result: DropResult) => {
+ const { destination, source } = result;
+
+ if (!destination) {
+ return;
+ }
+
+ setCurriculums((prevCurriculums) => {
+ const updatedCurriculums = [...prevCurriculums];
+ const movedCurriculum = { ...updatedCurriculums[source.index] };
+
+ updatedCurriculums.splice(source.index, 1);
+ updatedCurriculums.splice(destination.index, 0, movedCurriculum);
+
+ updatedCurriculums.forEach((curriculum, index) => {
+ const updatedCurriculum = { ...curriculum };
+ updatedCurriculum.itemOrder = index + 1;
+ updatedCurriculums[index] = updatedCurriculum;
+ });
+
+ return updatedCurriculums;
+ });
+ };
+
+ return (
+ <>
+
+
+ {(provided) => (
+
+ {curriculums.map((curriculum, index) => (
+
+ {(innerProvided) => (
+
+
+ {curriculum.itemOrder.toString().padStart(2, '0')}
+
+
+ ) => handleEditCurriculumChange(event, index)}
+ RightIconButton={
+ <>
+ }
+ onClick={() => {
+ handleEditButtonClick(index);
+ }}
+ size="icon_md"
+ variant="transparent"
+ />
+ }
+ onClick={() => {
+ handleDeleteButtonClick(index);
+ }}
+ size="icon_md"
+ variant="transparent"
+ />
+ >
+ }
+ />
+
+ )}
+
+ ))}
+ {provided.placeholder}
+
+ )}
+
+
+
+
+
+
+ }
+ onClick={handleAddButtonClick}
+ size="icon_md"
+ variant="transparent"
+ />
+
+
+ >
+ );
+};
+
+export default CurriculumModal;