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 ( - - curriculum card - - - {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" /> + 전체 보기 + + + + curriculum card + + + {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;