From 5395b0e4d7aa7d68eb1a58a605b60a921ae611d3 Mon Sep 17 00:00:00 2001 From: typhoon Date: Thu, 13 Feb 2025 16:49:53 +0900 Subject: [PATCH 01/11] =?UTF-8?q?feat:=20=ED=8F=B4=EB=8D=94=20=EC=A0=95?= =?UTF-8?q?=EB=A0=AC=20=ED=95=A8=EC=88=98=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/types/blog.tsx | 7 + src/pages/setting/FolderSettingPage.tsx | 252 +++++++++++++++++---- src/pages/setting/OldFolderSettingPage.tsx | 58 +++++ src/pages/setting/SettingPage.tsx | 13 +- 4 files changed, 279 insertions(+), 51 deletions(-) create mode 100644 src/common/types/blog.tsx create mode 100644 src/pages/setting/OldFolderSettingPage.tsx diff --git a/src/common/types/blog.tsx b/src/common/types/blog.tsx new file mode 100644 index 0000000..0a5e4d9 --- /dev/null +++ b/src/common/types/blog.tsx @@ -0,0 +1,7 @@ +export interface FolderResponse { + id: string, + title: string, + depth: number, + order: number, + parentOrder: number, +} \ No newline at end of file diff --git a/src/pages/setting/FolderSettingPage.tsx b/src/pages/setting/FolderSettingPage.tsx index 6991b24..0538b3d 100644 --- a/src/pages/setting/FolderSettingPage.tsx +++ b/src/pages/setting/FolderSettingPage.tsx @@ -1,55 +1,213 @@ -import {DndContext, DragEndEvent} from "@dnd-kit/core"; -import {restrictToVerticalAxis} from "@dnd-kit/modifiers"; -import {SortableContext} from "@dnd-kit/sortable"; +import {faker} from "@faker-js/faker/locale/ko"; +import {useState} from "react"; +import {FolderResponse} from "../../common/types/blog.tsx"; import {FillButton} from "../../components/common/FillButton.tsx"; -import {FolderType} from "./SettingPage.tsx"; -import FolderCard from "../../components/setting/FolderCard.tsx"; -import {OutlineButton} from "../../components/common/OutlineButton.tsx"; - -function FolderSettingPage({ - setSelectedFolder, - folders, - setShowModal, - setShowFolderAddModal, - handleDragEnd, - isHover, - handleHover, - submitFolderChange - }: { - setSelectedFolder: (folder: FolderType) => void, - folders: FolderType[], - setShowModal: (modal: boolean) => void, - setShowFolderAddModal: (modal: boolean) => void, - handleDragEnd: (event: DragEndEvent) => void, - isHover: boolean, - handleHover: (hover: boolean) => void, - submitFolderChange: () => void, -}) { +import {TextButton} from "../../components/common/TextButton.tsx"; + +function FolderSettingPage() { + + const folderData: FolderResponse[] = [ + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + depth: 1, + order: 1, + parentOrder: 0, + }, + { + id: crypto.randomUUID(), + title: "DigLog의 블로그", + depth: 0, + order: 0, + parentOrder: -1, + }, + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + depth: 1, + order: 0, + parentOrder: 0, + }, + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + depth: 1, + order: 3, + parentOrder: 0, + }, + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + depth: 2, + order: 0, + parentOrder: 0, + }, + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + depth: 2, + order: 1, + parentOrder: 1, + }, + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + depth: 2, + order: 2, + parentOrder: 1, + }, + + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + depth: 2, + order: 0, + parentOrder: 3, + }, + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + depth: 1, + order: 2, + parentOrder: 0, + }, + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + depth: 2, + order: 0, + parentOrder: 1, + }, + ]; + + const sortFolders = (folders: FolderResponse[]) => { + const result: FolderResponse[] = []; + + const rootFolder = folders.findIndex(folder => folder.depth === 0); + result.push(folders[rootFolder]); + + const mainFolders = folders.filter((folder) => + folder.depth === 1 && folder.parentOrder === 0) + .sort((a, b) => a.order - b.order); + + mainFolders.forEach((folder) => { + result.push(folder); + + const subFolders = folders.filter((subFolder) => + subFolder.depth === 2 && subFolder.parentOrder === folder.order) + .sort((a, b) => a.order - b.order); + result.push(...subFolders); + }); + + return result; + }; + + const [folders, setFolders] = useState(sortFolders(folderData)); + + const addFolder = (parentFolder: FolderResponse, title: string) => { + if (parentFolder.depth >= 2) { + return; + } + + const subFolders = folders.filter(folder => + folder.depth === parentFolder.depth + 1 && folder.parentOrder === parentFolder.order); + const maxOrder = subFolders.reduce((max, folder) => { + return folder.order > max ? folder.order : max; + }, -1); + + const newFolder: FolderResponse = { + id: crypto.randomUUID(), + title: title, + depth: parentFolder.depth + 1, + order: maxOrder + 1, + parentOrder: parentFolder.order, + }; + + setFolders(sortFolders([...folders, newFolder])); + } + + const moveFolder = (moveFolder: FolderResponse, targetFolder: FolderResponse) => { + // maxDepth 초과하는 경우 return + if (targetFolder.depth === 2 && moveFolder.depth === 1 && + folders.findIndex(folder => folder.depth === 2 && folder.parentOrder === moveFolder.order) !== -1) { + alert("하위 폴더를 이동한 후에 이동해주세요."); + return; + } + + // 이동할 폴더 분리 + const moveFolders: FolderResponse[] = []; + folders.forEach(folder => { + if (folder.depth === moveFolder.depth && folder.parentOrder === moveFolder.parentOrder && folder.order === moveFolder.order) { + moveFolders.push({ + ...folder, + depth: targetFolder.depth, + order: targetFolder.order, + parentOrder: targetFolder.parentOrder, + }); + } else if (folder.depth === moveFolder.depth + 1 && folder.parentOrder === moveFolder.order) { + moveFolders.push({ + ...folder, + parentOrder: targetFolder.order, + }); + } + }); + setFolders(folders.filter(folder => + !(folder.depth === moveFolder.depth && folder.parentOrder === moveFolder.parentOrder && folder.order === moveFolder.order) && + !(folder.depth === moveFolder.depth + 1 && folder.parentOrder === moveFolder.order))); + + // 앞으로 이동하는 폴더 order-- + setFolders(prevFolders => + prevFolders.map(folder => + folder.depth === moveFolder.depth && folder.parentOrder === moveFolder.parentOrder && folder.order > moveFolder.order + ? {...folder, order: folder.order - 1} : folder) + ); + setFolders(prevFolders => + prevFolders.map(folder => + folder.depth === moveFolder.depth + 1 && folder.parentOrder > moveFolder.order + ? {...folder, parentOrder: folder.parentOrder - 1} : folder) + ); + + // 뒤로 이동하는 폴더 뒤의 order++ + setFolders(prevFolders => + prevFolders.map(folder => + folder.depth === targetFolder.depth && folder.parentOrder === targetFolder.parentOrder && folder.order >= targetFolder.order + ? {...folder, order: folder.order + 1} : folder) + ); + setFolders(prevFolders => + prevFolders.map(folder => + folder.depth === targetFolder.depth + 1 && folder.parentOrder >= targetFolder.order + ? {...folder, parentOrder: folder.parentOrder + 1} : folder) + ); + + // 분리했던 폴더 추가 + setFolders(prevFolders => + sortFolders([...prevFolders, ...moveFolders])); + } + + const handleSubmit = () => { + alert("변경사항이 적용되었습니다."); + } return (

폴더 관리

-
- - - {folders.map((folder) => ( -
- -
- ))} -
-
-
-
- setShowFolderAddModal(true)} addStyle={"font-normal"}/> - + {folders.map((folder: FolderResponse) => ( +
+ depth: {folder.depth} parentOrder : {folder.parentOrder} order : {folder.order} {folder.title} + {folder.depth <= 1 && addFolder(folder, faker.lorem.words())} + addStyle={"text-xs"}/>} + moveFolder(folder, { + id: "", + title: "", + depth: 2, + parentOrder: 1, + order: 0, + })} addStyle={"text-sm"}/> +
+ ))} +
+
); diff --git a/src/pages/setting/OldFolderSettingPage.tsx b/src/pages/setting/OldFolderSettingPage.tsx new file mode 100644 index 0000000..1c1e6b9 --- /dev/null +++ b/src/pages/setting/OldFolderSettingPage.tsx @@ -0,0 +1,58 @@ +import {DndContext, DragEndEvent} from "@dnd-kit/core"; +import {restrictToVerticalAxis} from "@dnd-kit/modifiers"; +import {SortableContext} from "@dnd-kit/sortable"; +import {FillButton} from "../../components/common/FillButton.tsx"; +import {FolderType} from "./SettingPage.tsx"; +import FolderCard from "../../components/setting/FolderCard.tsx"; +import {OutlineButton} from "../../components/common/OutlineButton.tsx"; + +function OldFolderSettingPage({ + setSelectedFolder, + folders, + setShowModal, + setShowFolderAddModal, + handleDragEnd, + isHover, + handleHover, + submitFolderChange + }: { + setSelectedFolder: (folder: FolderType) => void, + folders: FolderType[], + setShowModal: (modal: boolean) => void, + setShowFolderAddModal: (modal: boolean) => void, + handleDragEnd: (event: DragEndEvent) => void, + isHover: boolean, + handleHover: (hover: boolean) => void, + submitFolderChange: () => void, +}) { + + return ( +
+

폴더 관리

+
+ + + {folders.map((folder) => ( +
+ +
+ ))} +
+
+
+
+ setShowFolderAddModal(true)} addStyle={"font-normal"}/> + +
+
+ ); +} + +export default OldFolderSettingPage; \ No newline at end of file diff --git a/src/pages/setting/SettingPage.tsx b/src/pages/setting/SettingPage.tsx index 1c592b7..7167a7f 100644 --- a/src/pages/setting/SettingPage.tsx +++ b/src/pages/setting/SettingPage.tsx @@ -4,11 +4,12 @@ import {faker} from "@faker-js/faker/locale/ko"; import {DragEndEvent} from "@dnd-kit/core"; import {arrayMove} from "@dnd-kit/sortable"; import {useNavigate} from "react-router-dom"; -import FolderSettingPage from "./FolderSettingPage.tsx"; +import OldFolderSettingPage from "./OldFolderSettingPage.tsx"; import PostSettingPage from "./PostSettingPage.tsx"; import ProfileSettingPage from "./ProfileSettingPage.tsx"; import FolderMoveModal from "../../components/setting/FolderMoveModal.tsx"; import FolderAddModal from "../../components/setting/FolderAddModal.tsx"; +import FolderSettingPage from "./FolderSettingPage.tsx"; export interface FolderType { id: string; @@ -23,7 +24,7 @@ function SettingPage() { const [showFolderModal, setShowFolderModal] = useState(false); const [showFolderAddModal, setShowFolderAddModal] = useState(false); - const tabList = ["프로필", "폴더", "게시글"]; + const tabList = ["프로필", "폴더", "폴더2", "게시글"]; const folderData: FolderType[] = [ { id: "1", @@ -48,7 +49,7 @@ function SettingPage() { }, ]; - const [selectedTab, setSelectedTab] = useState("프로필"); + const [selectedTab, setSelectedTab] = useState("폴더2"); const [folders, setFolders] = useState(folderData); const [selectedFolder, setSelectedFolder] = useState(null); const [isHover, setIsHover] = useState(false); @@ -175,7 +176,7 @@ function SettingPage() {
{(selectedTab === "폴더") &&
-
} + {(selectedTab === "폴더2") && +
+ +
} {(selectedTab === "게시글") &&
From acce5f050191afb0d303ba7661fac02334ea5fce Mon Sep 17 00:00:00 2001 From: typhoon0678 Date: Thu, 13 Feb 2025 18:20:58 +0900 Subject: [PATCH 02/11] =?UTF-8?q?refactor:=20moveFolder=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/setting/FolderSettingPage.tsx | 80 +++++++++++++------------ 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/src/pages/setting/FolderSettingPage.tsx b/src/pages/setting/FolderSettingPage.tsx index 0538b3d..d9a3e39 100644 --- a/src/pages/setting/FolderSettingPage.tsx +++ b/src/pages/setting/FolderSettingPage.tsx @@ -134,8 +134,10 @@ function FolderSettingPage() { return; } - // 이동할 폴더 분리 const moveFolders: FolderResponse[] = []; + const anotherFolders: FolderResponse[] = []; + + // 이동할 폴더 분리 folders.forEach(folder => { if (folder.depth === moveFolder.depth && folder.parentOrder === moveFolder.parentOrder && folder.order === moveFolder.order) { moveFolders.push({ @@ -149,39 +151,30 @@ function FolderSettingPage() { ...folder, parentOrder: targetFolder.order, }); + } else { + anotherFolders.push(folder); } }); - setFolders(folders.filter(folder => - !(folder.depth === moveFolder.depth && folder.parentOrder === moveFolder.parentOrder && folder.order === moveFolder.order) && - !(folder.depth === moveFolder.depth + 1 && folder.parentOrder === moveFolder.order))); // 앞으로 이동하는 폴더 order-- - setFolders(prevFolders => - prevFolders.map(folder => - folder.depth === moveFolder.depth && folder.parentOrder === moveFolder.parentOrder && folder.order > moveFolder.order - ? {...folder, order: folder.order - 1} : folder) - ); - setFolders(prevFolders => - prevFolders.map(folder => - folder.depth === moveFolder.depth + 1 && folder.parentOrder > moveFolder.order - ? {...folder, parentOrder: folder.parentOrder - 1} : folder) - ); + anotherFolders.forEach(folder => { + if (folder.depth === moveFolder.depth && folder.parentOrder === moveFolder.parentOrder && folder.order > moveFolder.order) { + folder.order--; + } else if (folder.depth === moveFolder.depth + 1 && folder.parentOrder > moveFolder.order) { + folder.order--; + } + }); // 뒤로 이동하는 폴더 뒤의 order++ - setFolders(prevFolders => - prevFolders.map(folder => - folder.depth === targetFolder.depth && folder.parentOrder === targetFolder.parentOrder && folder.order >= targetFolder.order - ? {...folder, order: folder.order + 1} : folder) - ); - setFolders(prevFolders => - prevFolders.map(folder => - folder.depth === targetFolder.depth + 1 && folder.parentOrder >= targetFolder.order - ? {...folder, parentOrder: folder.parentOrder + 1} : folder) - ); - - // 분리했던 폴더 추가 - setFolders(prevFolders => - sortFolders([...prevFolders, ...moveFolders])); + anotherFolders.forEach(folder => { + if (folder.depth === targetFolder.depth && folder.parentOrder === targetFolder.parentOrder && folder.order >= targetFolder.order) { + folder.order++; + } else if (folder.depth === targetFolder.depth + 1 && folder.parentOrder >= targetFolder.order) { + folder.parentOrder++; + } + }); + + setFolders(sortFolders([...anotherFolders, ...moveFolders])); } const handleSubmit = () => { @@ -195,17 +188,30 @@ function FolderSettingPage() {
depth: {folder.depth} parentOrder : {folder.parentOrder} order : {folder.order} {folder.title} - {folder.depth <= 1 && addFolder(folder, faker.lorem.words())} - addStyle={"text-xs"}/>} - moveFolder(folder, { - id: "", - title: "", - depth: 2, - parentOrder: 1, - order: 0, - })} addStyle={"text-sm"}/> +
+ {folder.depth <= 1 && + addFolder(folder, faker.lorem.words())} + addStyle={"text-xs"}/>} + moveFolder(folder, { + id: "", + title: "", + depth: 1, + parentOrder: 0, + order: 1, + })} addStyle={"text-xs"}/> +
))} +
From 7815801f35f12bec1b77a191e13c5ff68b52aba6 Mon Sep 17 00:00:00 2001 From: typhoon0678 Date: Fri, 14 Feb 2025 09:45:22 +0900 Subject: [PATCH 03/11] =?UTF-8?q?feat:=20=ED=8F=B4=EB=8D=94=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20=EB=AA=A8=EB=8B=AC=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/blog/CategorySelectBox.tsx | 67 ++++++++++++ src/layout/ModalLayout.tsx | 14 ++- src/pages/setting/FolderSettingPage.tsx | 127 ++++++++++++++++++++-- 3 files changed, 195 insertions(+), 13 deletions(-) create mode 100644 src/components/blog/CategorySelectBox.tsx diff --git a/src/components/blog/CategorySelectBox.tsx b/src/components/blog/CategorySelectBox.tsx new file mode 100644 index 0000000..3540bd4 --- /dev/null +++ b/src/components/blog/CategorySelectBox.tsx @@ -0,0 +1,67 @@ +import {FolderResponse} from "../../common/types/blog.tsx"; +import {MdOutlineArrowDropDown} from "react-icons/md"; +import {useEffect, useRef, useState} from "react"; + +function CategorySelectBox({folders, targetFolder, setTargetFolder, center}: { + folders: FolderResponse[], + targetFolder: FolderResponse, + setTargetFolder: (folder: FolderResponse) => void, + center?: boolean, +}) { + + const folderRef = useRef(null); + + const [folderOpen, setFolderOpen] = useState(false); + + const handleFolderOpen = () => { + setFolderOpen(prev => !prev); + } + + const handleClickOutside = (event: MouseEvent) => { + if (folderRef.current && !folderRef.current.contains(event.target as Node)) { + setFolderOpen(false); + } + }; + + useEffect(() => { + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + return ( +
+ +
+ {folders.map((folder) => { + let displayTitle; + if (folder.depth <= 1) { + displayTitle = folder.title; + } else { + displayTitle = `${folders.find(f => f.depth === 1 && f.order === folder.parentOrder)!.title} > ${folder.title}`; + } + return
+ +
; + })} +
+
+ ); +} + +export default CategorySelectBox; \ No newline at end of file diff --git a/src/layout/ModalLayout.tsx b/src/layout/ModalLayout.tsx index 131b304..3efe563 100644 --- a/src/layout/ModalLayout.tsx +++ b/src/layout/ModalLayout.tsx @@ -1,10 +1,14 @@ -import {ReactNode} from "react"; +import {ReactNode, Ref} from "react"; -function ModalLayout({children}: { children: ReactNode }) { +function ModalLayout({children, customRef, addStyle}: { + children: ReactNode, + customRef?: Ref, + addStyle?: string, +}) { return ( -
-
-
+
+
+
{children}
diff --git a/src/pages/setting/FolderSettingPage.tsx b/src/pages/setting/FolderSettingPage.tsx index d9a3e39..ae794ad 100644 --- a/src/pages/setting/FolderSettingPage.tsx +++ b/src/pages/setting/FolderSettingPage.tsx @@ -1,8 +1,10 @@ import {faker} from "@faker-js/faker/locale/ko"; -import {useState} from "react"; +import {useEffect, useRef, useState} from "react"; import {FolderResponse} from "../../common/types/blog.tsx"; import {FillButton} from "../../components/common/FillButton.tsx"; import {TextButton} from "../../components/common/TextButton.tsx"; +import ModalLayout from "../../layout/ModalLayout.tsx"; +import CategorySelectBox from "../../components/blog/CategorySelectBox.tsx"; function FolderSettingPage() { @@ -177,10 +179,79 @@ function FolderSettingPage() { setFolders(sortFolders([...anotherFolders, ...moveFolders])); } + const [openMoveModal, setOpenMoveModal] = useState(false); + const [selectedFolder, setSelectedFolder] = useState({ + id: crypto.randomUUID(), + title: "DigLog의 블로그", + depth: 0, + order: 0, + parentOrder: -1, + }); + const [targetFolder, setTargetFolder] = useState({ + id: crypto.randomUUID(), + title: "DigLog의 블로그", + depth: 0, + order: 0, + parentOrder: -1, + }); + const [folderMoveType, setFolderMoveType] = useState(0); + const folderMoveTypes = ["폴더 위로 옮깁니다.", "폴더 아래로 옮깁니다.", "폴더 내부로 옮깁니다."]; + + const handleMoveFolder = () => { + if (handleDisabled(folderMoveType)) { + alert("활성화된 동작 중에서 선택해주세요."); + return; + } + + let moveTargetFolder; + if (folderMoveType === 0) { + moveTargetFolder = targetFolder; + } else if (folderMoveType === 1) { + moveTargetFolder = {...targetFolder, order: targetFolder.order + 1}; + } else { + const maxOrder = folders.filter(folder => folder.depth === targetFolder.depth + 1 && folder.parentOrder === targetFolder.order) + .reduce((max, f) => { + return f.order > max ? f.order : max; + }, -1); + moveTargetFolder = {...targetFolder, depth: targetFolder.depth + 1, parentOrder: targetFolder.order, order: maxOrder + 1}; + } + + moveFolder(selectedFolder, moveTargetFolder); + setOpenMoveModal(false); + } + + const handleDisabled = (moveType: number) => { + if (!targetFolder) { + return true; + } else if (targetFolder.depth === 0 && moveType !== 2) { + return true; + } else if (targetFolder.depth == 2 && moveType == 2) { + return true; + } else if (moveType === 2 && selectedFolder.depth === 1 && targetFolder.depth !== 0 && + folders.findIndex(folder => folder.depth === 2 && folder.parentOrder === selectedFolder.order) !== -1) { + return true; + } + return false; + } + const handleSubmit = () => { alert("변경사항이 적용되었습니다."); } + const modalRef = useRef(null); + const handleClickOutside = (event: MouseEvent) => { + if (modalRef.current && !modalRef.current.contains(event.target as Node)) { + setOpenMoveModal(false); + } + }; + + useEffect(() => { + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + return (

폴더 관리

@@ -192,13 +263,10 @@ function FolderSettingPage() { {folder.depth <= 1 && addFolder(folder, faker.lorem.words())} addStyle={"text-xs"}/>} - moveFolder(folder, { - id: "", - title: "", - depth: 1, - parentOrder: 0, - order: 1, - })} addStyle={"text-xs"}/> + { + setOpenMoveModal(true); + setSelectedFolder(folder); + }} addStyle={"text-xs"}/>
))} @@ -215,6 +283,49 @@ function FolderSettingPage() {
+ {openMoveModal && ( + +
+
+

{selectedFolder.title} 폴더를

+ +
+ +
    + {folderMoveTypes.map((moveType, index) => +
  • + setFolderMoveType(index)} + disabled={handleDisabled(index)}/> + +
  • + )} +
+
+
+
+ { + setOpenMoveModal(false); + }}/> + +
+
+
+ )}
); } From 2d31f0d1db3358dd54ee8b3bf7aab81bf5de85a3 Mon Sep 17 00:00:00 2001 From: typhoon0678 Date: Fri, 14 Feb 2025 10:35:24 +0900 Subject: [PATCH 04/11] =?UTF-8?q?feat:=20=ED=8F=B4=EB=8D=94=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/blog/CategorySelectBox.tsx | 22 ++---- src/pages/setting/FolderSettingPage.tsx | 88 +++++++++++++++++++---- src/pages/setting/SettingPage.tsx | 8 +-- 3 files changed, 83 insertions(+), 35 deletions(-) diff --git a/src/components/blog/CategorySelectBox.tsx b/src/components/blog/CategorySelectBox.tsx index 3540bd4..1c56bf2 100644 --- a/src/components/blog/CategorySelectBox.tsx +++ b/src/components/blog/CategorySelectBox.tsx @@ -9,20 +9,17 @@ function CategorySelectBox({folders, targetFolder, setTargetFolder, center}: { center?: boolean, }) { - const folderRef = useRef(null); - const [folderOpen, setFolderOpen] = useState(false); - const handleFolderOpen = () => { setFolderOpen(prev => !prev); } + const folderRef = useRef(null); const handleClickOutside = (event: MouseEvent) => { if (folderRef.current && !folderRef.current.contains(event.target as Node)) { setFolderOpen(false); } }; - useEffect(() => { document.addEventListener('mousedown', handleClickOutside); return () => { @@ -41,24 +38,17 @@ function CategorySelectBox({folders, targetFolder, setTargetFolder, center}: {
- {folders.map((folder) => { - let displayTitle; - if (folder.depth <= 1) { - displayTitle = folder.title; - } else { - displayTitle = `${folders.find(f => f.depth === 1 && f.order === folder.parentOrder)!.title} > ${folder.title}`; - } - return
+ {folders.map((folder) => +
-
; - })} +
)}
); diff --git a/src/pages/setting/FolderSettingPage.tsx b/src/pages/setting/FolderSettingPage.tsx index ae794ad..74add85 100644 --- a/src/pages/setting/FolderSettingPage.tsx +++ b/src/pages/setting/FolderSettingPage.tsx @@ -111,6 +111,7 @@ function FolderSettingPage() { return; } + const id = crypto.randomUUID(); const subFolders = folders.filter(folder => folder.depth === parentFolder.depth + 1 && folder.parentOrder === parentFolder.order); const maxOrder = subFolders.reduce((max, folder) => { @@ -118,13 +119,15 @@ function FolderSettingPage() { }, -1); const newFolder: FolderResponse = { - id: crypto.randomUUID(), + id: id, title: title, depth: parentFolder.depth + 1, order: maxOrder + 1, parentOrder: parentFolder.order, }; + setEditFolderId(id); + setEditFolderTitle(""); setFolders(sortFolders([...folders, newFolder])); } @@ -187,6 +190,8 @@ function FolderSettingPage() { order: 0, parentOrder: -1, }); + const [editFolderId, setEditFolderId] = useState(""); + const [editFolderTitle, setEditFolderTitle] = useState(""); const [targetFolder, setTargetFolder] = useState({ id: crypto.randomUUID(), title: "DigLog의 블로그", @@ -213,7 +218,12 @@ function FolderSettingPage() { .reduce((max, f) => { return f.order > max ? f.order : max; }, -1); - moveTargetFolder = {...targetFolder, depth: targetFolder.depth + 1, parentOrder: targetFolder.order, order: maxOrder + 1}; + moveTargetFolder = { + ...targetFolder, + depth: targetFolder.depth + 1, + parentOrder: targetFolder.order, + order: maxOrder + 1 + }; } moveFolder(selectedFolder, moveTargetFolder); @@ -234,6 +244,28 @@ function FolderSettingPage() { return false; } + const handleEdit = (editFolder: FolderResponse) => { + if (editFolder.title.trim() === "") { + alert("폴더 이름을 입력해주세요."); + return; + } else if (folders.findIndex(folder => folder.title === editFolderTitle.trim()) !== -1) { + alert("중복된 폴더 이름입니다."); + return; + } + + setFolders(prevFolders => + prevFolders.map(folder => + folder.id === editFolder.id ? editFolder : folder)); + + setEditFolderId(""); + } + const handleDelete = (deleteFolder: FolderResponse) => { + if (!confirm("삭제하시겠습니까?")) { + return; + } + setFolders(sortFolders(folders.filter(folder => folder !== deleteFolder))); + } + const handleSubmit = () => { alert("변경사항이 적용되었습니다."); } @@ -257,17 +289,42 @@ function FolderSettingPage() {

폴더 관리

{folders.map((folder: FolderResponse) => (
- depth: {folder.depth} parentOrder : {folder.parentOrder} order : {folder.order} {folder.title} -
- {folder.depth <= 1 && - addFolder(folder, faker.lorem.words())} - addStyle={"text-xs"}/>} - { - setOpenMoveModal(true); - setSelectedFolder(folder); - }} addStyle={"text-xs"}/> -
+ className={`ml-${folder.depth * 8 - 8} ${folder.depth === 0 && "hidden"} flex justify-between items-center h-12 px-4 my-4 border border-gray-300 hover:border-gray-400 text-sm`}> + {folder.id === editFolderId + ?
+ setEditFolderTitle(e.target.value)} + placeholder="폴더 이름"/> +
+ setEditFolderId("")} + addStyle={"!bg-gray-400 hover:brightness-110"}/> + handleEdit({...folder, title: editFolderTitle})}/> +
+
+ :
+

{folder.title}

+
+ {folder.depth <= 1 && + addFolder(folder, `폴더_${crypto.randomUUID().substring(0, 4)}`)} + addStyle={"text-xs hover:text-lime-600"}/>} + { + setEditFolderId(folder.id); + setEditFolderTitle(folder.title); + }} addStyle={"text-xs hover:text-lime-600"}/> + { + handleDelete(folder); + }} addStyle={"text-xs hover:text-lime-600"}/> + { + setOpenMoveModal(true); + setSelectedFolder(folder); + }} addStyle={"text-xs hover:text-lime-600"}/> +
+
}
))}
@@ -321,7 +378,8 @@ function FolderSettingPage() { { setOpenMoveModal(false); }}/> - +
diff --git a/src/pages/setting/SettingPage.tsx b/src/pages/setting/SettingPage.tsx index 7167a7f..3aae1bb 100644 --- a/src/pages/setting/SettingPage.tsx +++ b/src/pages/setting/SettingPage.tsx @@ -175,7 +175,7 @@ function SettingPage() {
{(selectedTab === "폴더") && -
+
} {(selectedTab === "폴더2") && -
+
} {(selectedTab === "게시글") && -
+
} {(selectedTab === "프로필") && -
+
}
From 75b50e51367c178852e343544dc3da4440106552 Mon Sep 17 00:00:00 2001 From: typhoon0678 Date: Sun, 16 Feb 2025 15:50:49 +0900 Subject: [PATCH 05/11] =?UTF-8?q?style:=20folder=20UI=20=EC=9E=91=EC=97=85?= =?UTF-8?q?=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/apis/post.tsx | 9 + src/components/setting/FolderCard.tsx | 151 ++++++------ src/components/setting/OldFolderCard.tsx | 102 ++++++++ src/pages/setting/FolderSettingPage.tsx | 268 ++++++++++++++------- src/pages/setting/OldFolderSettingPage.tsx | 4 +- 5 files changed, 365 insertions(+), 169 deletions(-) create mode 100644 src/components/setting/OldFolderCard.tsx diff --git a/src/common/apis/post.tsx b/src/common/apis/post.tsx index c7e5473..81eaa26 100644 --- a/src/common/apis/post.tsx +++ b/src/common/apis/post.tsx @@ -16,3 +16,12 @@ export const getPost = async (id: string) => export const getPosts = async (postListRequest: PostListRequest) => await axiosApi.get(`/post${postListRequestToParameter(postListRequest)}`); + +export const testApi = async (folderRequestList: FolderRequest[]) => + await axiosApi.post("/test", folderRequestList); + +export interface FolderRequest { + id: string; + name: string; + subFolders: FolderRequest[]; +} \ No newline at end of file diff --git a/src/components/setting/FolderCard.tsx b/src/components/setting/FolderCard.tsx index b35cacd..7e36097 100644 --- a/src/components/setting/FolderCard.tsx +++ b/src/components/setting/FolderCard.tsx @@ -1,26 +1,34 @@ -import {FolderType} from "../../pages/setting/SettingPage.tsx"; -import {DndContext, DragEndEvent} from "@dnd-kit/core"; -import {SortableContext, useSortable} from "@dnd-kit/sortable"; -import {CSS} from "@dnd-kit/utilities"; +import {FillButton} from "../common/FillButton.tsx"; import {MdOutlineMenu} from "react-icons/md"; import {TextButton} from "../common/TextButton.tsx"; -import {restrictToVerticalAxis} from "@dnd-kit/modifiers"; -import {useState} from "react"; -import {FillButton} from "../common/FillButton.tsx"; +import {FolderResponse} from "../../common/types/blog.tsx"; +import {useSortable} from "@dnd-kit/sortable"; +import {CSS} from "@dnd-kit/utilities"; -function FolderCard({setSelectedFolder, folder, setShowModal, handleDrag, isHover, handleHover, isSub}: { - setSelectedFolder: (folder: FolderType) => void, - folder: FolderType, - setShowModal: (modal: boolean) => void, - handleDrag?: (event: DragEndEvent) => void, - isHover: boolean, - handleHover: (hover: boolean) => void, - isSub?: boolean +function FolderCard({ + folder, + editFolderId, + editFolderTitle, + setEditFolderId, + setEditFolderTitle, + handleEdit, + addFolder, + handleDelete, + setSelectedFolder, + setOpenMoveModal + }: { + folder: FolderResponse, + editFolderId: string, + editFolderTitle: string, + setEditFolderId: (editFolderId: string) => void, + setEditFolderTitle: (editFolderTitle: string) => void, + handleEdit: (editFolder: FolderResponse) => void, + addFolder: (targetFolder: FolderResponse, editTitle: string) => void, + handleDelete: (deleteFolder: FolderResponse) => void, + setOpenMoveModal: (openMoveModal: boolean) => void, + setSelectedFolder: (selectedFolder: FolderResponse) => void, }) { - const [isEdit, setIsEdit] = useState(false); - const [folderNameInput, setFolderNameInput] = useState(folder.name); - const {attributes, listeners, setNodeRef, transform, transition} = useSortable({id: folder.id}); const style = { @@ -28,73 +36,48 @@ function FolderCard({setSelectedFolder, folder, setShowModal, handleDrag, isHove transition, }; - const handleFolderNameChange = () => { - folder.name = folderNameInput; - setIsEdit(false); - } - return ( -
-
{ - if (isSub) { - handleHover(true); - } - }} - onMouseLeave={() => { - if (isSub) { - handleHover(false); - } - }}> - - {(isEdit) - ? { - setFolderNameInput(e.target.value) - }}/> - :

{folder.name}

} -
- {(isSub && !isEdit) && - { - setSelectedFolder(folder); - setShowModal(true); - }}/>} - {(isEdit) - ? - : setIsEdit(true)}/>} -
-
- {folder.subFolders && ( - - - {folder.subFolders.map((subFolder) => ( - - ))} - - - )} +
+ {folder.id === editFolderId + ?
+ setEditFolderTitle(e.target.value)} + placeholder="폴더 이름"/> +
+ setEditFolderId("")} + addStyle={"!bg-gray-400 hover:brightness-110"}/> + handleEdit({...folder, title: editFolderTitle})}/> +
+
+ :
+
+ +

{folder.parentOrder} {folder.order} {folder.title}

+
+
+ {folder.depth <= 1 && + addFolder(folder, `폴더_${crypto.randomUUID().substring(0, 4)}`)} + addStyle={"text-xs hover:text-lime-600"}/>} + { + setEditFolderId(folder.id); + setEditFolderTitle(folder.title); + }} addStyle={"text-xs hover:text-lime-600"}/> + { + handleDelete(folder); + }} addStyle={"text-xs hover:text-lime-600"}/> + { + setOpenMoveModal(true); + setSelectedFolder(folder); + }} addStyle={"text-xs hover:text-lime-600"}/> +
+
}
); } diff --git a/src/components/setting/OldFolderCard.tsx b/src/components/setting/OldFolderCard.tsx new file mode 100644 index 0000000..8c06e82 --- /dev/null +++ b/src/components/setting/OldFolderCard.tsx @@ -0,0 +1,102 @@ +import {FolderType} from "../../pages/setting/SettingPage.tsx"; +import {DndContext, DragEndEvent} from "@dnd-kit/core"; +import {SortableContext, useSortable} from "@dnd-kit/sortable"; +import {CSS} from "@dnd-kit/utilities"; +import {MdOutlineMenu} from "react-icons/md"; +import {TextButton} from "../common/TextButton.tsx"; +import {restrictToVerticalAxis} from "@dnd-kit/modifiers"; +import {useState} from "react"; +import {FillButton} from "../common/FillButton.tsx"; + +function OldFolderCard({setSelectedFolder, folder, setShowModal, handleDrag, isHover, handleHover, isSub}: { + setSelectedFolder: (folder: FolderType) => void, + folder: FolderType, + setShowModal: (modal: boolean) => void, + handleDrag?: (event: DragEndEvent) => void, + isHover: boolean, + handleHover: (hover: boolean) => void, + isSub?: boolean +}) { + + const [isEdit, setIsEdit] = useState(false); + const [folderNameInput, setFolderNameInput] = useState(folder.name); + + const {attributes, listeners, setNodeRef, transform, transition} = useSortable({id: folder.id}); + + const style = { + transform: CSS.Translate.toString(transform), + transition, + }; + + const handleFolderNameChange = () => { + folder.name = folderNameInput; + setIsEdit(false); + } + + return ( +
+
{ + if (isSub) { + handleHover(true); + } + }} + onMouseLeave={() => { + if (isSub) { + handleHover(false); + } + }}> + + {(isEdit) + ? { + setFolderNameInput(e.target.value) + }}/> + :

{folder.name}

} +
+ {(isSub && !isEdit) && + { + setSelectedFolder(folder); + setShowModal(true); + }}/>} + {(isEdit) + ? + : setIsEdit(true)}/>} +
+
+ {folder.subFolders && ( + + + {folder.subFolders.map((subFolder) => ( + + ))} + + + )} +
+ ); +} + +export default OldFolderCard; \ No newline at end of file diff --git a/src/pages/setting/FolderSettingPage.tsx b/src/pages/setting/FolderSettingPage.tsx index 74add85..891feb2 100644 --- a/src/pages/setting/FolderSettingPage.tsx +++ b/src/pages/setting/FolderSettingPage.tsx @@ -2,9 +2,12 @@ import {faker} from "@faker-js/faker/locale/ko"; import {useEffect, useRef, useState} from "react"; import {FolderResponse} from "../../common/types/blog.tsx"; import {FillButton} from "../../components/common/FillButton.tsx"; -import {TextButton} from "../../components/common/TextButton.tsx"; import ModalLayout from "../../layout/ModalLayout.tsx"; import CategorySelectBox from "../../components/blog/CategorySelectBox.tsx"; +import {SortableContext, verticalListSortingStrategy} from "@dnd-kit/sortable"; +import {DndContext, DragEndEvent} from "@dnd-kit/core"; +import FolderCard from "../../components/setting/FolderCard.tsx"; +import {FolderRequest, testApi} from "../../common/apis/post.tsx"; function FolderSettingPage() { @@ -144,14 +147,16 @@ function FolderSettingPage() { // 이동할 폴더 분리 folders.forEach(folder => { - if (folder.depth === moveFolder.depth && folder.parentOrder === moveFolder.parentOrder && folder.order === moveFolder.order) { + if (folder.depth === 0) { + moveFolders.push(folder); + } else if (folder.depth === moveFolder.depth && folder.parentOrder === moveFolder.parentOrder && folder.order === moveFolder.order) { moveFolders.push({ ...folder, depth: targetFolder.depth, order: targetFolder.order, parentOrder: targetFolder.parentOrder, }); - } else if (folder.depth === moveFolder.depth + 1 && folder.parentOrder === moveFolder.order) { + } else if (folder.depth > moveFolder.depth && folder.parentOrder === moveFolder.order) { moveFolders.push({ ...folder, parentOrder: targetFolder.order, @@ -161,25 +166,67 @@ function FolderSettingPage() { } }); - // 앞으로 이동하는 폴더 order-- - anotherFolders.forEach(folder => { - if (folder.depth === moveFolder.depth && folder.parentOrder === moveFolder.parentOrder && folder.order > moveFolder.order) { - folder.order--; - } else if (folder.depth === moveFolder.depth + 1 && folder.parentOrder > moveFolder.order) { - folder.order--; - } - }); - - // 뒤로 이동하는 폴더 뒤의 order++ - anotherFolders.forEach(folder => { - if (folder.depth === targetFolder.depth && folder.parentOrder === targetFolder.parentOrder && folder.order >= targetFolder.order) { - folder.order++; - } else if (folder.depth === targetFolder.depth + 1 && folder.parentOrder >= targetFolder.order) { - folder.parentOrder++; - } - }); - setFolders(sortFolders([...anotherFolders, ...moveFolders])); + + // anotherFolders.forEach((folder) => { + // if (moveFolder.depth === 2 && targetFolder.depth === 2) { + // if (folder.depth === 2 && moveFolder.parentOrder === targetFolder.parentOrder && folder.parentOrder === moveFolder.parentOrder) { + // if (folder.order > moveFolder.order && folder.order <= targetFolder.order) { + // resultFolders.push({...folder, order: --folder.order}); + // } else if (folder.order < moveFolder.order && folder.order >= targetFolder.order) { + // resultFolders.push({...folder, order: ++folder.order}); + // } else { + // resultFolders.push(folder); + // } + // } else if (folder.depth === 2 && moveFolder.parentOrder !== targetFolder.parentOrder) { + // if (folder.parentOrder === moveFolder.parentOrder && folder.order > moveFolder.order) { + // resultFolders.push({...folder, order: --folder.order}); + // } else if (folder.parentOrder === targetFolder.parentOrder && folder.order >= targetFolder.order) { + // resultFolders.push({...folder, order: ++folder.order}); + // } else { + // resultFolders.push(folder); + // } + // } else { + // resultFolders.push(folder); + // } + // } else if (moveFolder.depth === 2 && targetFolder.depth === 1) { + // if (folder.depth === 2 && folder.parentOrder >= targetFolder.order) { + // resultFolders.push({...folder, parentOrder: ++folder.parentOrder}); + // } else if (folder.depth === 2 && folder.parentOrder === moveFolder.parentOrder && folder.order > moveFolder.order) { + // resultFolders.push({...folder, order: --folder.order}); + // } else if (folder.depth === 1 && folder.order >= targetFolder.order) { + // resultFolders.push({...folder, order: ++folder.order}); + // } else { + // resultFolders.push(folder); + // } + // } else if (moveFolder.depth === 1 && targetFolder.depth === 2) { + // if (folder.depth === 1 && folder.order > moveFolder.order) { + // resultFolders.push({...folder, order: --folder.order}); + // } else if (folder.depth === 2 && folder.parentOrder === targetFolder.parentOrder && folder.order >= targetFolder.order) { + // resultFolders.push({...folder, order: ++folder.order});x + // } else if (folder.depth === 2 && folder.parentOrder > moveFolder.order) { + // resultFolders.push({...folder, parentOrder: --folder.parentOrder}); + // } else { + // resultFolders.push(folder); + // } + // } else if (moveFolder.depth === 1 && targetFolder.depth === 1) { + // if (folder.depth === 1 && folder.order > moveFolder.order && folder.order <= targetFolder.order) { + // resultFolders.push({...folder, order: --folder.order}); + // } else if (folder.depth === 1 && folder.order < moveFolder.order && folder.order >= targetFolder.order) { + // resultFolders.push({...folder, order: ++folder.order}); + // } else if (folder.depth === 2 && folder.parentOrder > moveFolder.order && folder.parentOrder <= targetFolder.order) { + // resultFolders.push({...folder, parentOrder: --folder.parentOrder}); + // } else if (folder.depth === 2 && folder.parentOrder < moveFolder.order && folder.parentOrder >= targetFolder.order) { + // resultFolders.push({...folder, parentOrder: ++folder.parentOrder}); + // } else { + // resultFolders.push(folder); + // } + // } else { + // resultFolders.push(folder); + // } + // }); + // + // setFolders(sortFolders([...resultFolders, ...moveFolders])); } const [openMoveModal, setOpenMoveModal] = useState(false); @@ -230,6 +277,21 @@ function FolderSettingPage() { setOpenMoveModal(false); } + const handleOnDragEnd = ({active, over}: DragEndEvent) => { + if (!over || active.id === over.id) { + return; + } + + const activeFolder = folders.find(folder => folder.id === active.id); + const overFolder = folders.find(folder => folder.id === over.id); + + if (!activeFolder || !overFolder) { + return; + } + + moveFolder(activeFolder, overFolder); + } + const handleDisabled = (moveType: number) => { if (!targetFolder) { return true; @@ -260,13 +322,26 @@ function FolderSettingPage() { setEditFolderId(""); } const handleDelete = (deleteFolder: FolderResponse) => { + if (deleteFolder.depth === 1) { + const findIndex = folders.findIndex(folder => + folder.depth === 2 && folder.parentOrder === deleteFolder.order); + if (findIndex !== -1) { + alert("하위 폴더를 삭제한 후에 삭제할 수 있습니다."); + return; + } + } + if (!confirm("삭제하시겠습니까?")) { return; } + setFolders(sortFolders(folders.filter(folder => folder !== deleteFolder))); } const handleSubmit = () => { + if (!confirm("변경사항을 적용하시겠습니까?")) { + return; + } alert("변경사항이 적용되었습니다."); } @@ -287,46 +362,24 @@ function FolderSettingPage() { return (

폴더 관리

- {folders.map((folder: FolderResponse) => ( -
- {folder.id === editFolderId - ?
- setEditFolderTitle(e.target.value)} - placeholder="폴더 이름"/> -
- setEditFolderId("")} - addStyle={"!bg-gray-400 hover:brightness-110"}/> - handleEdit({...folder, title: editFolderTitle})}/> -
-
- :
-

{folder.title}

-
- {folder.depth <= 1 && - addFolder(folder, `폴더_${crypto.randomUUID().substring(0, 4)}`)} - addStyle={"text-xs hover:text-lime-600"}/>} - { - setEditFolderId(folder.id); - setEditFolderTitle(folder.title); - }} addStyle={"text-xs hover:text-lime-600"}/> - { - handleDelete(folder); - }} addStyle={"text-xs hover:text-lime-600"}/> - { - setOpenMoveModal(true); - setSelectedFolder(folder); - }} addStyle={"text-xs hover:text-lime-600"}/> -
-
} -
- ))} + + + {folders.map((folder: FolderResponse) => ( + + ))} + +
+ { + + const folderRequestList: FolderRequest[] = [ + { + id: crypto.randomUUID(), + name: faker.lorem.words(), + subFolders: [ + { + id: crypto.randomUUID(), + name: faker.lorem.words(), + subFolders: [], + }, + { + id: crypto.randomUUID(), + name: faker.lorem.words(), + subFolders: [], + }, + ], + }, + { + id: crypto.randomUUID(), + name: faker.lorem.words(), + subFolders: [ + { + id: crypto.randomUUID(), + name: faker.lorem.words(), + subFolders: [], + }, + { + id: crypto.randomUUID(), + name: faker.lorem.words(), + subFolders: [], + }, + ], + }, + { + id: crypto.randomUUID(), + name: faker.lorem.words(), + subFolders: [ + { + id: crypto.randomUUID(), + name: faker.lorem.words(), + subFolders: [], + }, + ], + }, + ] + + testApi(folderRequestList) + .then((res) => console.log(res.data)) + .catch((error) => console.log(error)); + }}/>
{openMoveModal && ( @@ -351,28 +456,25 @@ function FolderSettingPage() { setTargetFolder={setTargetFolder} center={true} /> -
- -
    - {folderMoveTypes.map((moveType, index) => -
  • - setFolderMoveType(index)} - disabled={handleDisabled(index)}/> - -
  • - )} -
-
+
    + {folderMoveTypes.map((moveType, index) => +
  • + setFolderMoveType(index)} + disabled={handleDisabled(index)}/> + +
  • + )} +
{ diff --git a/src/pages/setting/OldFolderSettingPage.tsx b/src/pages/setting/OldFolderSettingPage.tsx index 1c1e6b9..86b8b35 100644 --- a/src/pages/setting/OldFolderSettingPage.tsx +++ b/src/pages/setting/OldFolderSettingPage.tsx @@ -3,7 +3,7 @@ import {restrictToVerticalAxis} from "@dnd-kit/modifiers"; import {SortableContext} from "@dnd-kit/sortable"; import {FillButton} from "../../components/common/FillButton.tsx"; import {FolderType} from "./SettingPage.tsx"; -import FolderCard from "../../components/setting/FolderCard.tsx"; +import OldFolderCard from "../../components/setting/OldFolderCard.tsx"; import {OutlineButton} from "../../components/common/OutlineButton.tsx"; function OldFolderSettingPage({ @@ -35,7 +35,7 @@ function OldFolderSettingPage({ {folders.map((folder) => (
- Date: Sun, 16 Feb 2025 17:45:02 +0900 Subject: [PATCH 06/11] =?UTF-8?q?feat:=20folderResponse=20->=20FolderType?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/types/blog.tsx | 54 ++- src/components/blog/CategorySelectBox.tsx | 10 +- src/components/setting/FolderCard.tsx | 85 ---- src/components/setting/FolderCardList.tsx | 162 ++++++++ src/pages/setting/FolderSettingPage.tsx | 472 ++++++++-------------- 5 files changed, 387 insertions(+), 396 deletions(-) delete mode 100644 src/components/setting/FolderCard.tsx create mode 100644 src/components/setting/FolderCardList.tsx diff --git a/src/common/types/blog.tsx b/src/common/types/blog.tsx index 0a5e4d9..ea850e4 100644 --- a/src/common/types/blog.tsx +++ b/src/common/types/blog.tsx @@ -1,7 +1,55 @@ -export interface FolderResponse { +export interface FolderRequest { + id: string | null, + title: string, + depth: number, + orderIndex: number, + parentOrderIndex: number, +} + +export interface FolderType { id: string, title: string, + subFolders: FolderType[], +} + +export interface FolderResponse { + folderId: string, + title: string, depth: number, - order: number, - parentOrder: number, + orderIndex: number, + parentFolderId: string | null, +} + +export const toFolderTypeList = (folderResponseList: FolderResponse[]) => { + const result: FolderType[] = []; + + folderResponseList.sort((a, b) => a.orderIndex - b.orderIndex) + .forEach(folderResponse => { + const folderType = toFolderType(folderResponse); + + if (folderResponse.depth === 0) { + result.push(folderType); + } else if (folderResponse.depth === 1) { + result[result.length - 1].subFolders.push(folderType); + } else if (folderResponse.depth === 2) { + const subFoldersLength = result[result.length - 1].subFolders.length; + result[result.length - 1].subFolders[subFoldersLength - 1].subFolders.push(folderType); + } + }); + + return result; +} + +const toFolderType = (folderResponse: FolderResponse) => { + return { + id: folderResponse.folderId, + title: folderResponse.title, + subFolders: [], + }; +} + +export const toFolderRequestList = (folderTypeList: FolderType[]) => { + const result: FolderType[] = []; + + return result; } \ No newline at end of file diff --git a/src/components/blog/CategorySelectBox.tsx b/src/components/blog/CategorySelectBox.tsx index 1c56bf2..c13fe31 100644 --- a/src/components/blog/CategorySelectBox.tsx +++ b/src/components/blog/CategorySelectBox.tsx @@ -1,11 +1,11 @@ -import {FolderResponse} from "../../common/types/blog.tsx"; +import {FolderType} from "../../common/types/blog.tsx"; import {MdOutlineArrowDropDown} from "react-icons/md"; import {useEffect, useRef, useState} from "react"; function CategorySelectBox({folders, targetFolder, setTargetFolder, center}: { - folders: FolderResponse[], - targetFolder: FolderResponse, - setTargetFolder: (folder: FolderResponse) => void, + folders: FolderType[], + targetFolder: FolderType, + setTargetFolder: (folder: FolderType) => void, center?: boolean, }) { @@ -41,7 +41,7 @@ function CategorySelectBox({folders, targetFolder, setTargetFolder, center}: { {folders.map((folder) =>
- { - - const folderRequestList: FolderRequest[] = [ - { - id: crypto.randomUUID(), - name: faker.lorem.words(), - subFolders: [ - { - id: crypto.randomUUID(), - name: faker.lorem.words(), - subFolders: [], - }, - { - id: crypto.randomUUID(), - name: faker.lorem.words(), - subFolders: [], - }, - ], - }, - { - id: crypto.randomUUID(), - name: faker.lorem.words(), - subFolders: [ - { - id: crypto.randomUUID(), - name: faker.lorem.words(), - subFolders: [], - }, - { - id: crypto.randomUUID(), - name: faker.lorem.words(), - subFolders: [], - }, - ], - }, - { - id: crypto.randomUUID(), - name: faker.lorem.words(), - subFolders: [ - { - id: crypto.randomUUID(), - name: faker.lorem.words(), - subFolders: [], - }, - ], - }, - ] - - testApi(folderRequestList) - .then((res) => console.log(res.data)) - .catch((error) => console.log(error)); - }}/>
{openMoveModal && ( From ade318af9a39b462cd71d33e91a10b8976254e72 Mon Sep 17 00:00:00 2001 From: typhoon Date: Sun, 16 Feb 2025 19:41:07 +0900 Subject: [PATCH 07/11] =?UTF-8?q?feat:=20=EC=88=98=EC=A0=95,=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/blog/CategorySelectBox.tsx | 45 ++++++--- src/pages/setting/FolderSettingPage.tsx | 114 ++++++++++++---------- 2 files changed, 98 insertions(+), 61 deletions(-) diff --git a/src/components/blog/CategorySelectBox.tsx b/src/components/blog/CategorySelectBox.tsx index c13fe31..63a9992 100644 --- a/src/components/blog/CategorySelectBox.tsx +++ b/src/components/blog/CategorySelectBox.tsx @@ -2,8 +2,9 @@ import {FolderType} from "../../common/types/blog.tsx"; import {MdOutlineArrowDropDown} from "react-icons/md"; import {useEffect, useRef, useState} from "react"; -function CategorySelectBox({folders, targetFolder, setTargetFolder, center}: { +function CategorySelectBox({folders, depth, targetFolder, setTargetFolder, center}: { folders: FolderType[], + depth: number, targetFolder: FolderType, setTargetFolder: (folder: FolderType) => void, center?: boolean, @@ -39,19 +40,41 @@ function CategorySelectBox({folders, targetFolder, setTargetFolder, center}: {
{folders.map((folder) => -
- -
)} + )}
); } +function CategorySelectCard({folder, depth, setTargetFolder, setFolderOpen}: { + folder: FolderType, + depth: number, + setTargetFolder: (folder: FolderType) => void, + setFolderOpen: (open: boolean) => void, +}) { + return ( +
+ + {folder.subFolders.length > 0 && + folder.subFolders.map((subFolder => + ))} +
+ ); +} + export default CategorySelectBox; \ No newline at end of file diff --git a/src/pages/setting/FolderSettingPage.tsx b/src/pages/setting/FolderSettingPage.tsx index 2125444..7c3ccf9 100644 --- a/src/pages/setting/FolderSettingPage.tsx +++ b/src/pages/setting/FolderSettingPage.tsx @@ -6,6 +6,7 @@ import ModalLayout from "../../layout/ModalLayout.tsx"; import CategorySelectBox from "../../components/blog/CategorySelectBox.tsx"; import {DragEndEvent} from "@dnd-kit/core"; import FolderCardList from "../../components/setting/FolderCardList.tsx"; +import {arrayMove} from "@dnd-kit/sortable"; function FolderSettingPage() { @@ -111,27 +112,6 @@ function FolderSettingPage() { orderIndex: 13, parentFolderId: idList[11], }, - { - folderId: idList[14], - title: faker.lorem.words(), - depth: 1, - orderIndex: 14, - parentFolderId: idList[6], - }, - { - folderId: idList[15], - title: faker.lorem.words(), - depth: 2, - orderIndex: 15, - parentFolderId: idList[14], - }, - { - folderId: idList[16], - title: faker.lorem.words(), - depth: 2, - orderIndex: 16, - parentFolderId: idList[14], - }, ]; const [tempId, setTempId] = useState(0); @@ -144,15 +124,27 @@ function FolderSettingPage() { const addFolder = (parentFolder: FolderType | null, title: string) => { if (parentFolder === null) { - setFolders([...folders, {id: getTempId(), title: title, subFolders: []}]); + const tempId = getTempId(); + setFolders([...folders, {id: tempId, title: title, subFolders: []}]); + setEditFolderTitle(title); + setEditFolderId(tempId); return; } - setFolders(prevFolders => - prevFolders.map(folder => folder.id === parentFolder.id - ? {...folder, subFolders: [...folder.subFolders, {id: getTempId(), title: title, subFolders: []}]} - : folder) - ); + setFolders(prevFolders => getAddFolderList(prevFolders, parentFolder.id, title)); + } + const getAddFolderList = (folders: FolderType[], id: string, title: string) => { + return folders.map((folder: FolderType): FolderType => { + if (folder.id === id) { + const tempId = getTempId(); + setEditFolderTitle(title); + setEditFolderId(tempId); + return {...folder, subFolders: [...folder.subFolders, {id: tempId, title: title, subFolders: []}]}; + } else if (folder.subFolders.length > 0) { + return {...folder, subFolders: getAddFolderList(folder.subFolders, id, title)}; + } + return folder; + }) } const moveFolder = (moveFolder: FolderType, targetFolder: FolderType) => { @@ -211,14 +203,22 @@ function FolderSettingPage() { return; } - const activeFolder = folders.find(folder => folder.id === active.id); - const overFolder = folders.find(folder => folder.id === over.id); + setFolders(prevFolders => dndMoveFolder(prevFolders, active.id, over.id)); + } + const dndMoveFolder = (folders: FolderType[], activeId: string, overId: string) => { + const activeIndex = folders.findIndex(folder => folder.id === activeId); + const overIndex = folders.findIndex(folder => folder.id === overId); - if (!activeFolder || !overFolder) { - return; + if (activeIndex !== -1 && overIndex !== -1) { + return arrayMove(folders, activeIndex, overIndex); } - moveFolder(activeFolder, overFolder); + return folders.map((folder: FolderType): FolderType => { + if (folder.subFolders.length > 0) { + return {...folder, subFolders: dndMoveFolder(folder.subFolders, activeId, overId)}; + } + return folder; + }); } const handleDisabled = (moveType: number) => { @@ -244,27 +244,40 @@ function FolderSettingPage() { return; } - // setFolders(prevFolders => - // prevFolders.map(folder => - // folder.id === editFolder.id ? editFolder : folder)); - + setFolders(prevFolders => getEditFolderList(prevFolders, editFolder.id, editFolder.title)); setEditFolderId(""); } + const getEditFolderList = (folders: FolderType[], id: string, title: string) => { + return folders.map((folder: FolderType): FolderType => { + if (folder.id === id) { + return {...folder, title: title}; + } else if (folder.subFolders.length > 0) { + return {...folder, subFolders: getEditFolderList(folder.subFolders, id, title)}; + } + return folder; + }) + } + + const handleDelete = (deleteFolder: FolderType) => { - // if (deleteFolder.depth === 1) { - // const findIndex = folders.findIndex(folder => - // folder.depth === 2 && folder.parentOrder === deleteFolder.order); - // if (findIndex !== -1) { - // alert("하위 폴더를 삭제한 후에 삭제할 수 있습니다."); - // return; - // } - // } - // - // if (!confirm("삭제하시겠습니까?")) { - // return; - // } - // - // setFolders(sortFolders(folders.filter(folder => folder !== deleteFolder))); + if (deleteFolder.subFolders.length > 0) { + alert("하위 폴더를 모두 제거한 후에 제거할 수 있습니다."); + return; + } + + setFolders(prevFolders => getDeleteFolderList(prevFolders, deleteFolder.id)); + } + const getDeleteFolderList = (folders: FolderType[], id: string) => { + if (folders.findIndex(folder => folder.id === id) !== -1) { + return folders.filter(folder => folder.id !== id); + } + + return folders.map((folder: FolderType): FolderType => { + if (folder.subFolders.length > 0) { + return {...folder, subFolders: getDeleteFolderList(folder.subFolders, id)}; + } + return folder; + }) } const handleSubmit = () => { @@ -318,6 +331,7 @@ function FolderSettingPage() {

{selectedFolder.title} 폴더를

Date: Sun, 16 Feb 2025 20:26:11 +0900 Subject: [PATCH 08/11] =?UTF-8?q?feat:=20=ED=8F=B4=EB=8D=94=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20=ED=95=A8=EC=88=98=20=EC=9E=91=EC=84=B1=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/blog/CategorySelectBox.tsx | 15 ++-- src/pages/setting/FolderSettingPage.tsx | 89 ++++++++++++----------- 2 files changed, 58 insertions(+), 46 deletions(-) diff --git a/src/components/blog/CategorySelectBox.tsx b/src/components/blog/CategorySelectBox.tsx index 63a9992..2978423 100644 --- a/src/components/blog/CategorySelectBox.tsx +++ b/src/components/blog/CategorySelectBox.tsx @@ -2,9 +2,10 @@ import {FolderType} from "../../common/types/blog.tsx"; import {MdOutlineArrowDropDown} from "react-icons/md"; import {useEffect, useRef, useState} from "react"; -function CategorySelectBox({folders, depth, targetFolder, setTargetFolder, center}: { +function CategorySelectBox({folders, depth, selectedFolder, targetFolder, setTargetFolder, center}: { folders: FolderType[], depth: number, + selectedFolder: FolderType, targetFolder: FolderType, setTargetFolder: (folder: FolderType) => void, center?: boolean, @@ -43,20 +44,23 @@ function CategorySelectBox({folders, depth, targetFolder, setTargetFolder, cente )} + setFolderOpen={setFolderOpen}/> + )}
); } -function CategorySelectCard({folder, depth, setTargetFolder, setFolderOpen}: { +function CategorySelectCard({folder, depth, selectedFolder, setTargetFolder, setFolderOpen}: { folder: FolderType, depth: number, + selectedFolder: FolderType, setTargetFolder: (folder: FolderType) => void, setFolderOpen: (open: boolean) => void, }) { - return ( + return folder.id !== selectedFolder.id ? (
- ); + ) : <>; } export default CategorySelectBox; \ No newline at end of file diff --git a/src/pages/setting/FolderSettingPage.tsx b/src/pages/setting/FolderSettingPage.tsx index 7c3ccf9..62784bd 100644 --- a/src/pages/setting/FolderSettingPage.tsx +++ b/src/pages/setting/FolderSettingPage.tsx @@ -144,27 +144,20 @@ function FolderSettingPage() { return {...folder, subFolders: getAddFolderList(folder.subFolders, id, title)}; } return folder; - }) - } - - const moveFolder = (moveFolder: FolderType, targetFolder: FolderType) => { - - setFolders(prevFolders => { - return prevFolders; }); } const [openMoveModal, setOpenMoveModal] = useState(false); const [selectedFolder, setSelectedFolder] = useState({ id: crypto.randomUUID(), - title: "DigLog의 블로그", + title: "", subFolders: [], }); const [editFolderId, setEditFolderId] = useState(""); const [editFolderTitle, setEditFolderTitle] = useState(""); const [targetFolder, setTargetFolder] = useState({ id: crypto.randomUUID(), - title: "DigLog의 블로그", + title: "--------------", subFolders: [], }); const [folderMoveType, setFolderMoveType] = useState(0); @@ -176,62 +169,75 @@ function FolderSettingPage() { return; } - // let moveTargetFolder; - // if (folderMoveType === 0) { - // moveTargetFolder = targetFolder; - // } else if (folderMoveType === 1) { - // moveTargetFolder = {...targetFolder, order: targetFolder.order + 1}; - // } else { - // const maxOrder = folders.filter(folder => folder.depth === targetFolder.depth + 1 && folder.parentOrder === targetFolder.order) - // .reduce((max, f) => { - // return f.order > max ? f.order : max; - // }, -1); - // moveTargetFolder = { - // ...targetFolder, - // depth: targetFolder.depth + 1, - // parentOrder: targetFolder.order, - // order: maxOrder + 1 - // }; - // } + if (folderMoveType === 2) { + setFolders(prevFolders => { + const deleteFolderList = getDeleteFolderList(prevFolders, selectedFolder.id); + return getAddFolderModalList(deleteFolderList); + }); + } else { + setFolders(prevFolders => { + const deleteFolderList = getDeleteFolderList(prevFolders, selectedFolder.id); + return getModalMoveFolderList(deleteFolderList); + }); + } - // moveFolder(selectedFolder, moveTargetFolder); setOpenMoveModal(false); } + const getModalMoveFolderList = (folders: FolderType[]) => { + const targetIndex = folders.findIndex((folder) => folder.id === targetFolder.id); + + if (targetIndex !== -1) { + const moveIndex = (folderMoveType === 0) ? targetIndex : targetIndex + 1; + return folders.slice(0, moveIndex).concat(selectedFolder, folders.slice(moveIndex)); + } + return folders.map((folder: FolderType): FolderType => { + if (folder.subFolders.length > 0) { + return {...folder, subFolders: getModalMoveFolderList(folder.subFolders)}; + } + return folder; + }); + } + const getAddFolderModalList = (folders: FolderType[]) => { + return folders.map((folder: FolderType): FolderType => { + if (folder.id === targetFolder.id) { + return {...folder, subFolders: [...folder.subFolders, selectedFolder]}; + } else if (folder.subFolders.length > 0) { + return {...folder, subFolders: getAddFolderModalList(folder.subFolders)}; + } + return folder; + }); + } const handleOnDragEnd = ({active, over}: DragEndEvent) => { if (!over || active.id === over.id) { return; } - setFolders(prevFolders => dndMoveFolder(prevFolders, active.id, over.id)); + setFolders(prevFolders => getMoveFolderList(prevFolders, active.id as string, over.id as string)); } - const dndMoveFolder = (folders: FolderType[], activeId: string, overId: string) => { + const getMoveFolderList = (folders: FolderType[], activeId: string, overId: string) => { const activeIndex = folders.findIndex(folder => folder.id === activeId); const overIndex = folders.findIndex(folder => folder.id === overId); if (activeIndex !== -1 && overIndex !== -1) { return arrayMove(folders, activeIndex, overIndex); } - return folders.map((folder: FolderType): FolderType => { if (folder.subFolders.length > 0) { - return {...folder, subFolders: dndMoveFolder(folder.subFolders, activeId, overId)}; + return {...folder, subFolders: getMoveFolderList(folder.subFolders, activeId, overId)}; } return folder; }); } const handleDisabled = (moveType: number) => { - // if (!targetFolder) { - // return true; - // } else if (targetFolder.depth === 0 && moveType !== 2) { - // return true; - // } else if (targetFolder.depth == 2 && moveType == 2) { - // return true; - // } else if (moveType === 2 && selectedFolder.depth === 1 && targetFolder.depth !== 0 && - // folders.findIndex(folder => folder.depth === 2 && folder.parentOrder === selectedFolder.order) !== -1) { - // return true; - // } + if (!targetFolder) { + return true; + } + if (moveType !== 2) { + return false; + } + return false; } @@ -332,6 +338,7 @@ function FolderSettingPage() { Date: Sun, 16 Feb 2025 22:02:25 +0900 Subject: [PATCH 09/11] =?UTF-8?q?feat:=20=EB=AA=A8=EB=8B=AC=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20=EA=B8=B0=EB=8A=A5=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/blog/CategorySelectBox.tsx | 2 + src/components/setting/FolderCardList.tsx | 1 + src/pages/setting/FolderSettingPage.tsx | 60 ++++++++++++++++++++--- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/components/blog/CategorySelectBox.tsx b/src/components/blog/CategorySelectBox.tsx index 2978423..10916ff 100644 --- a/src/components/blog/CategorySelectBox.tsx +++ b/src/components/blog/CategorySelectBox.tsx @@ -42,6 +42,7 @@ function CategorySelectBox({folders, depth, selectedFolder, targetFolder, setTar className={`${folderOpen ? "" : "hidden"} absolute z-50 w-full top-12 left-0 bg-white divide-y divide-gray-300 rounded-lg shadow-sm`}> {folders.map((folder) => 0 && folder.subFolders.map((subFolder => 0 &&
({ id: crypto.randomUUID(), - title: "--------------", + title: "", subFolders: [], }); const [folderMoveType, setFolderMoveType] = useState(0); @@ -231,15 +231,57 @@ function FolderSettingPage() { } const handleDisabled = (moveType: number) => { - if (!targetFolder) { + if (targetFolder.title === "") { return true; } - if (moveType !== 2) { - return false; + + const maxDepth = 2; + + const selectedFolderMaxDepth = getMaxFolderDepth(folders, selectedFolder.id) || 1; + const selectedFolderDepth = getFolderDepth(folders, selectedFolder.id) || 1; + const targetFolderDepth = getFolderDepth(folders, targetFolder.id) || 1; + if (moveType === 2) { + return selectedFolderMaxDepth + targetFolderDepth > maxDepth; + } else { + return selectedFolderMaxDepth - selectedFolderDepth + targetFolderDepth > maxDepth; + } + } + // 하위 폴더의 최대 깊이 + const getMaxFolderDepth = (folders: FolderType[], folderId: string, currentDepth: number = 1): number | null => { + for (const folder of folders) { + if (folder.id === folderId) { + return calculateMaxDepth(folder.subFolders); + } + + const depth = getMaxFolderDepth(folder.subFolders, folder.id, currentDepth + 1); + if (depth !== null) { + return depth; + } } - return false; + return null; } + const calculateMaxDepth = (folders: FolderType[]): number => { + if (folders.length === 0) return 0; + + const depths = folders.map(folder => 1 + calculateMaxDepth(folder.subFolders)); + return Math.max(...depths); // 최대 깊이 반환 + }; + // 폴더의 현재 깊이 + const getFolderDepth = (folders: FolderType[], targetId: string, currentDepth: number = 1): number | null => { + for (const folder of folders) { + if (folder.id === targetId) { + return currentDepth; + } + + const depth = getFolderDepth(folder.subFolders, targetId, currentDepth + 1); + if (depth !== null) { + return depth; + } + } + + return null; + }; const handleEdit = (editFolder: FolderType) => { if (editFolder.title.trim() === "") { @@ -287,10 +329,10 @@ function FolderSettingPage() { } const handleSubmit = () => { - if (!confirm("변경사항을 적용하시겠습니까?")) { + if (!confirm("변경사항을 저장하시겠습니까?")) { return; } - alert("변경사항이 적용되었습니다."); + alert("변경사항이 저장되었습니다."); } const modalRef = useRef(null); @@ -307,6 +349,10 @@ function FolderSettingPage() { }; }, []); + useEffect(() => { + setTargetFolder({...targetFolder, title: ""}); + }, [openMoveModal]); + return (

폴더 관리

From dcddcda0634a196d698c80e4ceeb67a62acfa99a Mon Sep 17 00:00:00 2001 From: typhoon0678 Date: Sun, 16 Feb 2025 23:00:40 +0900 Subject: [PATCH 10/11] =?UTF-8?q?feat:=20=ED=8F=B4=EB=8D=94=20request=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EB=B3=80=ED=99=98=20=EC=9D=B4=EC=A0=84=20?= =?UTF-8?q?FolderCardList=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/apis/post.tsx | 3 - src/common/types/blog.tsx | 23 ++- src/components/setting/FolderAddModal.tsx | 20 +-- src/components/setting/OldFolderCard.tsx | 102 ------------ src/pages/post/WritePage.tsx | 4 +- src/pages/setting/FolderSettingPage.tsx | 7 +- src/pages/setting/OldFolderSettingPage.tsx | 58 ------- src/pages/setting/SettingPage.tsx | 178 +-------------------- 8 files changed, 44 insertions(+), 351 deletions(-) delete mode 100644 src/components/setting/OldFolderCard.tsx delete mode 100644 src/pages/setting/OldFolderSettingPage.tsx diff --git a/src/common/apis/post.tsx b/src/common/apis/post.tsx index 81eaa26..c0dc6d0 100644 --- a/src/common/apis/post.tsx +++ b/src/common/apis/post.tsx @@ -17,9 +17,6 @@ export const getPost = async (id: string) => export const getPosts = async (postListRequest: PostListRequest) => await axiosApi.get(`/post${postListRequestToParameter(postListRequest)}`); -export const testApi = async (folderRequestList: FolderRequest[]) => - await axiosApi.post("/test", folderRequestList); - export interface FolderRequest { id: string; name: string; diff --git a/src/common/types/blog.tsx b/src/common/types/blog.tsx index ea850e4..326188b 100644 --- a/src/common/types/blog.tsx +++ b/src/common/types/blog.tsx @@ -49,7 +49,26 @@ const toFolderType = (folderResponse: FolderResponse) => { } export const toFolderRequestList = (folderTypeList: FolderType[]) => { - const result: FolderType[] = []; + const result: FolderRequest[] = []; + + const getFolderRequestList = (folderTypeList: FolderType[], depth: number = 0, parentIndex: number = -1) => { + folderTypeList.forEach(folder => { + const id = (!folder.id.startsWith("temp") ? folder.id : null); + result.push({ + id: id, + title: folder.title, + depth: depth, + orderIndex: result.length, + parentOrderIndex: parentIndex, + }); + + if (folder.subFolders.length > 0) { + getFolderRequestList(folder.subFolders, depth + 1, result.length - 1); + } + }); + } + + getFolderRequestList(folderTypeList); return result; -} \ No newline at end of file +} diff --git a/src/components/setting/FolderAddModal.tsx b/src/components/setting/FolderAddModal.tsx index e54b4cc..ee9d235 100644 --- a/src/components/setting/FolderAddModal.tsx +++ b/src/components/setting/FolderAddModal.tsx @@ -1,9 +1,9 @@ import ModalLayout from "../../layout/ModalLayout.tsx"; import {useState} from "react"; import {FillButton} from "../common/FillButton.tsx"; -import {FolderType} from "../../pages/setting/SettingPage.tsx"; import {TextButton} from "../common/TextButton.tsx"; import {MdOutlineArrowDropDown} from "react-icons/md"; +import {FolderType} from "../../common/types/blog.tsx"; function FolderAddModal({folders, setShowFolderAddModal}: { folders: FolderType[], @@ -38,36 +38,36 @@ function FolderAddModal({folders, setShowFolderAddModal}: {
{folders.map((folder) => { if (!folder.subFolders) { - return
+ return
} else { - return
{folder.subFolders.map((subFolder: FolderType) => )}
; diff --git a/src/components/setting/OldFolderCard.tsx b/src/components/setting/OldFolderCard.tsx deleted file mode 100644 index 8c06e82..0000000 --- a/src/components/setting/OldFolderCard.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import {FolderType} from "../../pages/setting/SettingPage.tsx"; -import {DndContext, DragEndEvent} from "@dnd-kit/core"; -import {SortableContext, useSortable} from "@dnd-kit/sortable"; -import {CSS} from "@dnd-kit/utilities"; -import {MdOutlineMenu} from "react-icons/md"; -import {TextButton} from "../common/TextButton.tsx"; -import {restrictToVerticalAxis} from "@dnd-kit/modifiers"; -import {useState} from "react"; -import {FillButton} from "../common/FillButton.tsx"; - -function OldFolderCard({setSelectedFolder, folder, setShowModal, handleDrag, isHover, handleHover, isSub}: { - setSelectedFolder: (folder: FolderType) => void, - folder: FolderType, - setShowModal: (modal: boolean) => void, - handleDrag?: (event: DragEndEvent) => void, - isHover: boolean, - handleHover: (hover: boolean) => void, - isSub?: boolean -}) { - - const [isEdit, setIsEdit] = useState(false); - const [folderNameInput, setFolderNameInput] = useState(folder.name); - - const {attributes, listeners, setNodeRef, transform, transition} = useSortable({id: folder.id}); - - const style = { - transform: CSS.Translate.toString(transform), - transition, - }; - - const handleFolderNameChange = () => { - folder.name = folderNameInput; - setIsEdit(false); - } - - return ( -
-
{ - if (isSub) { - handleHover(true); - } - }} - onMouseLeave={() => { - if (isSub) { - handleHover(false); - } - }}> - - {(isEdit) - ? { - setFolderNameInput(e.target.value) - }}/> - :

{folder.name}

} -
- {(isSub && !isEdit) && - { - setSelectedFolder(folder); - setShowModal(true); - }}/>} - {(isEdit) - ? - : setIsEdit(true)}/>} -
-
- {folder.subFolders && ( - - - {folder.subFolders.map((subFolder) => ( - - ))} - - - )} -
- ); -} - -export default OldFolderCard; \ No newline at end of file diff --git a/src/pages/post/WritePage.tsx b/src/pages/post/WritePage.tsx index a101ef3..6a9808f 100644 --- a/src/pages/post/WritePage.tsx +++ b/src/pages/post/WritePage.tsx @@ -326,7 +326,9 @@ function WritePage() { />
{path.endsWith("/edit") - && handleDelete(id)} addStyle={"bg-red-400 hover:bg-red-700"}/>} + ? handleDelete(id)} + addStyle={"bg-red-400 hover:bg-red-700"}/> + :
} {path.endsWith("/edit") ? : } diff --git a/src/pages/setting/FolderSettingPage.tsx b/src/pages/setting/FolderSettingPage.tsx index 5477cb4..b34bb39 100644 --- a/src/pages/setting/FolderSettingPage.tsx +++ b/src/pages/setting/FolderSettingPage.tsx @@ -1,6 +1,6 @@ import {faker} from "@faker-js/faker/locale/ko"; import {useEffect, useRef, useState} from "react"; -import {FolderResponse, FolderType, toFolderTypeList} from "../../common/types/blog.tsx"; +import {FolderResponse, FolderType, toFolderRequestList, toFolderTypeList} from "../../common/types/blog.tsx"; import {FillButton} from "../../components/common/FillButton.tsx"; import ModalLayout from "../../layout/ModalLayout.tsx"; import CategorySelectBox from "../../components/blog/CategorySelectBox.tsx"; @@ -332,6 +332,9 @@ function FolderSettingPage() { if (!confirm("변경사항을 저장하시겠습니까?")) { return; } + + console.log(toFolderRequestList(folders)); + alert("변경사항이 저장되었습니다."); } @@ -369,7 +372,7 @@ function FolderSettingPage() { handleDelete={handleDelete} setOpenMoveModal={setOpenMoveModal} setSelectedFolder={setSelectedFolder}/> - diff --git a/src/pages/setting/OldFolderSettingPage.tsx b/src/pages/setting/OldFolderSettingPage.tsx deleted file mode 100644 index 86b8b35..0000000 --- a/src/pages/setting/OldFolderSettingPage.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import {DndContext, DragEndEvent} from "@dnd-kit/core"; -import {restrictToVerticalAxis} from "@dnd-kit/modifiers"; -import {SortableContext} from "@dnd-kit/sortable"; -import {FillButton} from "../../components/common/FillButton.tsx"; -import {FolderType} from "./SettingPage.tsx"; -import OldFolderCard from "../../components/setting/OldFolderCard.tsx"; -import {OutlineButton} from "../../components/common/OutlineButton.tsx"; - -function OldFolderSettingPage({ - setSelectedFolder, - folders, - setShowModal, - setShowFolderAddModal, - handleDragEnd, - isHover, - handleHover, - submitFolderChange - }: { - setSelectedFolder: (folder: FolderType) => void, - folders: FolderType[], - setShowModal: (modal: boolean) => void, - setShowFolderAddModal: (modal: boolean) => void, - handleDragEnd: (event: DragEndEvent) => void, - isHover: boolean, - handleHover: (hover: boolean) => void, - submitFolderChange: () => void, -}) { - - return ( -
-

폴더 관리

-
- - - {folders.map((folder) => ( -
- -
- ))} -
-
-
-
- setShowFolderAddModal(true)} addStyle={"font-normal"}/> - -
-
- ); -} - -export default OldFolderSettingPage; \ No newline at end of file diff --git a/src/pages/setting/SettingPage.tsx b/src/pages/setting/SettingPage.tsx index 3aae1bb..c422191 100644 --- a/src/pages/setting/SettingPage.tsx +++ b/src/pages/setting/SettingPage.tsx @@ -1,162 +1,14 @@ import BasicLayout from "../../layout/BasicLayout.tsx"; import {useState} from "react"; -import {faker} from "@faker-js/faker/locale/ko"; -import {DragEndEvent} from "@dnd-kit/core"; -import {arrayMove} from "@dnd-kit/sortable"; -import {useNavigate} from "react-router-dom"; -import OldFolderSettingPage from "./OldFolderSettingPage.tsx"; import PostSettingPage from "./PostSettingPage.tsx"; import ProfileSettingPage from "./ProfileSettingPage.tsx"; -import FolderMoveModal from "../../components/setting/FolderMoveModal.tsx"; -import FolderAddModal from "../../components/setting/FolderAddModal.tsx"; import FolderSettingPage from "./FolderSettingPage.tsx"; -export interface FolderType { - id: string; - name: string; - subFolders?: FolderType[]; -} - function SettingPage() { - const navigate = useNavigate(); - - const [showFolderModal, setShowFolderModal] = useState(false); - const [showFolderAddModal, setShowFolderAddModal] = useState(false); - - const tabList = ["프로필", "폴더", "폴더2", "게시글"]; - const folderData: FolderType[] = [ - { - id: "1", - name: faker.lorem.words(), - subFolders: [ - {id: "4", name: faker.lorem.words()}, - {id: "5", name: faker.lorem.words()} - ] - }, - { - id: "2", - name: faker.lorem.words(), - }, - { - id: "3", - name: faker.lorem.words(), - subFolders: [ - {id: "6", name: faker.lorem.words()}, - {id: "7", name: faker.lorem.words()}, - {id: "8", name: faker.lorem.words()} - ] - }, - ]; - - const [selectedTab, setSelectedTab] = useState("폴더2"); - const [folders, setFolders] = useState(folderData); - const [selectedFolder, setSelectedFolder] = useState(null); - const [isHover, setIsHover] = useState(false); - - const handleHover = (hover: boolean) => { - setIsHover(hover); - } - - const handleDragEnd = (event: DragEndEvent) => { - const {active, over} = event; - - if (!over || active.id === over.id) { - return; - } - - let folderIndex = -1; - let oldIndex = -1; - let newIndex = -1; - - oldIndex = folders.findIndex(folder => folder.id === active.id); - newIndex = folders.findIndex(folder => folder.id === over.id); - - if (oldIndex !== -1 && newIndex !== -1) { - setFolders(folder => { - return arrayMove(folder, oldIndex, newIndex); - }); - return; - } - - for (const folder of folders) { - if (folder.subFolders) { - oldIndex = folder.subFolders.findIndex(folder => folder.id === active.id); - newIndex = folder.subFolders.findIndex(folder => folder.id === over.id); - - if (oldIndex !== -1 && newIndex !== -1) { - folderIndex = folders.indexOf(folder); - break; - } - } - } - - if (folderIndex !== -1 && oldIndex !== -1 && newIndex !== -1) { - setFolders(currentFolders => { - const updatedSubFolders = [...currentFolders[folderIndex].subFolders!]; - const [movedFolder] = updatedSubFolders.splice(oldIndex, 1); - updatedSubFolders.splice(newIndex, 0, movedFolder); - - const updatedFolders = [...currentFolders]; - updatedFolders[folderIndex] = { - ...updatedFolders[folderIndex], - subFolders: updatedSubFolders - }; - - return updatedFolders; - }); - } - } - - const handleFolderMove = (folderId: string) => { - setFolders(prevFolders => { - const newFolders = [...prevFolders]; - let folderToMove; - let targetFolder: FolderType | undefined; + const tabList = ["프로필", "폴더", "게시글"]; - newFolders.forEach(folder => { - if (folder.subFolders) { - const subFolderIndex = folder.subFolders.findIndex(sub => sub.id === selectedFolder?.id); - if (subFolderIndex !== -1) { - folderToMove = folder.subFolders[subFolderIndex]; - folder.subFolders.splice(subFolderIndex, 1); - } - } - - if (folder.id === folderId) { - targetFolder = folder; - } - }); - - if (!folderToMove) { - return prevFolders; - } - - if (folderId === "top") { - newFolders.push(folderToMove); - return newFolders; - } - - if (!targetFolder) { - return prevFolders; - } - - if (!targetFolder.subFolders) { - targetFolder.subFolders = []; - } - - targetFolder.subFolders.push(folderToMove); - - return newFolders; - }); - } - - const submitFolderChange = () => { - if (confirm("변경사항을 저장하시겠습니까?")) { - alert("저장되었습니다."); - navigate(0); - } - } + const [selectedTab, setSelectedTab] = useState("프로필"); return ( @@ -174,19 +26,11 @@ function SettingPage() { )}
- {(selectedTab === "폴더") && + {(selectedTab === "프로필") &&
- +
} - {(selectedTab === "폴더2") && + {(selectedTab === "폴더") &&
} @@ -194,19 +38,7 @@ function SettingPage() {
} - {(selectedTab === "프로필") && -
- -
}
- {showFolderModal && } - {showFolderAddModal && } ); } From ba3c5a1995054157aeb3cdf68411648587e40092 Mon Sep 17 00:00:00 2001 From: typhoon0678 Date: Sun, 16 Feb 2025 23:06:34 +0900 Subject: [PATCH 11/11] =?UTF-8?q?fix:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20type=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/setting/FolderMoveModal.tsx | 6 +- src/pages/post/WritePage.tsx | 64 ++++++++++++++-------- 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/src/components/setting/FolderMoveModal.tsx b/src/components/setting/FolderMoveModal.tsx index af0fde7..0f509d4 100644 --- a/src/components/setting/FolderMoveModal.tsx +++ b/src/components/setting/FolderMoveModal.tsx @@ -1,10 +1,10 @@ -import {FolderType} from "../../pages/setting/SettingPage.tsx"; import {FillButton} from "../common/FillButton.tsx"; import {useState} from "react"; import {TextButton} from "../common/TextButton.tsx"; import {useSelector} from "react-redux"; import {RootState} from "../../store.tsx"; import ModalLayout from "../../layout/ModalLayout.tsx"; +import {FolderType} from "../../common/types/blog.tsx"; function FolderMoveModal({selectedFolder, folders, handleFolderMove, setShowModal}: { selectedFolder: FolderType | null, @@ -27,7 +27,7 @@ function FolderMoveModal({selectedFolder, folders, handleFolderMove, setShowModa

- "{selectedFolder?.name}" 폴더를 이동할 곳을 골라주세요. + "{selectedFolder?.title}" 폴더를 이동할 곳을 골라주세요.

))}
diff --git a/src/pages/post/WritePage.tsx b/src/pages/post/WritePage.tsx index 6a9808f..7b2cb69 100644 --- a/src/pages/post/WritePage.tsx +++ b/src/pages/post/WritePage.tsx @@ -14,6 +14,7 @@ import {uploadImage} from "../../common/apis/image.tsx"; import {checkUUID} from "../../common/util/regex.tsx"; import {TagResponse} from "../../common/types/post.tsx"; import {sortByName} from "../../common/util/sort.tsx"; +import {FolderType} from "../../common/types/blog.tsx"; interface WritePostType { inputTag: string; @@ -22,11 +23,6 @@ interface WritePostType { content: string; } -interface FolderType { - name: string; - subFolders?: FolderType[]; -} - function WritePage() { const loginState = useSelector((state: RootState) => state.loginSlice); @@ -166,21 +162,45 @@ function WritePage() { const folderData: FolderType[] = [ { - name: faker.lorem.words(), + id: crypto.randomUUID(), + title: faker.lorem.words(), subFolders: [ - {name: faker.lorem.words()}, - {name: faker.lorem.words()} + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + subFolders: [], + }, + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + subFolders: [], + }, ] }, { - name: faker.lorem.words(), + id: crypto.randomUUID(), + title: faker.lorem.words(), + subFolders: [], }, { - name: faker.lorem.words(), + id: crypto.randomUUID(), + title: faker.lorem.words(), subFolders: [ - {name: faker.lorem.words()}, - {name: faker.lorem.words()}, - {name: faker.lorem.words()} + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + subFolders: [], + }, + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + subFolders: [], + }, + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + subFolders: [], + }, ] }, ]; @@ -237,36 +257,36 @@ function WritePage() { className={`${folderOpen ? "" : "hidden"} absolute z-50 top-12 left-0 bg-white divide-y divide-gray-500 rounded-lg shadow-sm`}> {folderData.map((folder) => { if (!folder.subFolders) { - return
+ return
} else { - return
{folder.subFolders.map((subFolder: FolderType) => )}
;