diff --git a/src/common/apis/post.tsx b/src/common/apis/post.tsx index 903f67e..ff6a14c 100644 --- a/src/common/apis/post.tsx +++ b/src/common/apis/post.tsx @@ -1,12 +1,15 @@ import axiosApi from "./AxiosApi.tsx"; -import {PostListRequest, PostRequest, PostSearchRequest, PostUpdateRequest} from "../types/post.tsx"; +import {PostFolderRequest, PostListRequest, PostRequest, PostSearchRequest, PostUpdateRequest} from "../types/post.tsx"; import {postListRequestToParameter, postListSearchRequestToParameter} from "../util/url.tsx"; export const createPost = async (postRequest: PostRequest) => await axiosApi.post("/post", postRequest); export const updatePost = async (postUpdateRequest: PostUpdateRequest) => - await axiosApi.patch(`/post`, postUpdateRequest); + await axiosApi.patch("/post", postUpdateRequest); + +export const updatePostFolder = async (postFolderRequest: PostFolderRequest) => + await axiosApi.patch("/post/folder", postFolderRequest); export const deletePost = async (id: string) => await axiosApi.patch(`/post/delete/${id}`); diff --git a/src/common/types/blog.tsx b/src/common/types/blog.tsx index 1306184..87de640 100644 --- a/src/common/types/blog.tsx +++ b/src/common/types/blog.tsx @@ -75,7 +75,7 @@ export const toFolderRequestList = (folderTypeList: FolderType[]) => { export interface PostListMemberRequest { username: string, - folderId: string | null, + folderIds: string[] | null, page: number, size: number, } \ No newline at end of file diff --git a/src/common/types/post.tsx b/src/common/types/post.tsx index bb9b05c..c13a32a 100644 --- a/src/common/types/post.tsx +++ b/src/common/types/post.tsx @@ -3,6 +3,7 @@ import {PageResponse} from "./common.tsx"; export interface PostRequest { title: string, content: string, + folderId: string | null, tagNames: string[], urls: string[], } @@ -11,6 +12,7 @@ export interface PostUpdateRequest { id: string, title: string, content: string, + folderId: string | null, tagNames: string[], urls: string[], } @@ -41,12 +43,22 @@ export interface PostResponse { title: string; content: string; username: string; + folder?: PostFolderResponse; tags: TagResponse[]; createdAt: Date; } +export interface PostFolderResponse { + id: string; + title: string; +} + export interface TagResponse { id: string; name: string; } +export interface PostFolderRequest { + postIds: string[], + folderId: string | null, +} diff --git a/src/common/util/url.tsx b/src/common/util/url.tsx index 59ecab2..3eec18c 100644 --- a/src/common/util/url.tsx +++ b/src/common/util/url.tsx @@ -22,10 +22,14 @@ export const commentListRequestToParameter = (commentListRequest: CommentListReq export const postListMemberRequestToParameter = (postListMemberRequest: PostListMemberRequest) => { let query = `?username=${postListMemberRequest.username}&page=${postListMemberRequest.page}&size=${postListMemberRequest.size}`; - if (postListMemberRequest.folderId) { - query += `&folderId=${postListMemberRequest.folderId}`; + if (!postListMemberRequest.folderIds) { + return query; } + postListMemberRequest.folderIds.forEach((folderId: string) => { + query += `&folderIds=${folderId}`; + }); + return query; } diff --git a/src/components/common/Header.tsx b/src/components/common/Header.tsx index 90317bd..13fea05 100644 --- a/src/components/common/Header.tsx +++ b/src/components/common/Header.tsx @@ -95,7 +95,7 @@ function Header() { className="w-full block px-4 py-2 text-gray-700 hover:bg-gray-100"> 게시글 작성 - 내 블로그 diff --git a/src/components/folder/FolderSelectBox.tsx b/src/components/folder/FolderSelectBox.tsx index 6eafbc6..fb7cb8a 100644 --- a/src/components/folder/FolderSelectBox.tsx +++ b/src/components/folder/FolderSelectBox.tsx @@ -6,8 +6,8 @@ import {getFolderTitle} from "../../common/util/string.tsx"; function FolderSelectBox({folders, depth = 0, selectedFolder, targetFolder, setTargetFolder, center}: { folders: FolderType[], depth?: number, - selectedFolder?: FolderType, - targetFolder: FolderType, + selectedFolder?: FolderType | null, + targetFolder: FolderType | null, setTargetFolder: (folder: FolderType) => void, center?: boolean, }) { @@ -36,7 +36,7 @@ function FolderSelectBox({folders, depth = 0, selectedFolder, targetFolder, setT
void, setFolderOpen: (open: boolean) => void, }) { diff --git a/src/components/post/TagCard.tsx b/src/components/post/TagCard.tsx index 1902c5e..f0c2c60 100644 --- a/src/components/post/TagCard.tsx +++ b/src/components/post/TagCard.tsx @@ -1,11 +1,11 @@ import {TagResponse} from "../../common/types/post.tsx"; -function TagCard({tag, onClick}: { tag: TagResponse, onClick: (tagName: string) => void }) { +function TagCard({tag, onClick}: { tag: TagResponse, onClick: () => void }) { return ( ); diff --git a/src/pages/blog/BlogPage.tsx b/src/pages/blog/BlogPage.tsx index aa4c984..ecb6345 100644 --- a/src/pages/blog/BlogPage.tsx +++ b/src/pages/blog/BlogPage.tsx @@ -115,6 +115,26 @@ function BlogPage() { setSelectedTagList([...selectedTagList, selectTag]); } + const handleSelectedFolder = (folder: FolderType) => { + setSelectedFolder(folder); + setPage(0); + } + + const getSelectedFolders = (folder: FolderType | null) => { + if (!folder) { + return []; + } + + const folderIds = [folder.id]; + + if (folder.subFolders.length > 0) { + folder.subFolders.forEach((subFolder: FolderType) => { + folderIds.push(...getSelectedFolders(subFolder)); + }); + } + return folderIds; + } + useEffect(() => { // todo: 내 게시글 불러오기 api console.log(page, setPageInfo.toString()); @@ -133,6 +153,8 @@ function BlogPage() { useEffect(() => { navigate(`/blog/${username}?folder=${selectedFolder?.id || ""}`); + setPage(-1); + setPage(0); }, [selectedFolder]); useEffect(() => { @@ -157,19 +179,26 @@ function BlogPage() { setMember(res.data); }) .catch(error => alert(error.response.data.message)); + }, [username]); + + useEffect(() => { + if (username === undefined) { + return; + } getMemberPosts({ username: username, - folderId: null, + folderIds: getSelectedFolders(selectedFolder), page: page, size: pageInfo.size, }) .then((res) => { + console.log(res.data.page); setPosts(res.data.content); setPageInfo(res.data.page); }) .catch(error => error.response.data.message); - }, [username, page]); + }, [page, selectedFolder, username]); return ( @@ -202,7 +231,7 @@ function BlogPage() { username={username} profileUrl={member.profileUrl} addTag={addTag} - setSelectedFolder={setSelectedFolder}/> + setSelectedFolder={handleSelectedFolder}/>
@@ -218,7 +247,7 @@ function BlogPage() { username={username} profileUrl={member.profileUrl} addTag={addTag} - setSelectedFolder={setSelectedFolder} + setSelectedFolder={handleSelectedFolder} bgColor={"bg-gray-50"} side={true}/> diff --git a/src/pages/post/PostPage.tsx b/src/pages/post/PostPage.tsx index 063756e..db145ce 100644 --- a/src/pages/post/PostPage.tsx +++ b/src/pages/post/PostPage.tsx @@ -45,10 +45,6 @@ function PostPage() { return; } - if (!confirm("댓글을 등록하시겠습니까?")) { - return; - } - if (parentCommentId !== null) { parentCommentId = findParentCommentId(comments, parentCommentId); } @@ -83,10 +79,6 @@ function PostPage() { return null; } const handleUpdateComment = (content: string, taggedUsername: string | null, originalComment: CommentType | null) => { - if (!confirm("댓글을 수정하시겠습니까?")) { - return; - } - if (!originalComment) { alert("업데이트 할 댓글이 존재하지 않습니다."); return; @@ -189,11 +181,15 @@ function PostPage() {
- Home -
{` > `}
- 폴더 -
{` > `}
-
{post.title}
+ + Home + + {post.folder && +
+
{` > `}
+ {post.folder.title} +
}
@@ -211,7 +207,7 @@ function PostPage() {
{post.tags.map(tag => { - navigate(`/search?word=${tag.name}&option=태그`) + navigate(`/search?keyword=${tag.name}&option=TAG&sort=createdAt&isDescending=true&tab=post`) }}/>)}
diff --git a/src/pages/post/WritePage.tsx b/src/pages/post/WritePage.tsx index 01066c7..1696b1b 100644 --- a/src/pages/post/WritePage.tsx +++ b/src/pages/post/WritePage.tsx @@ -40,7 +40,7 @@ function WritePage() { }); const [showTag, setShowTag] = useState(false); - const [targetFolder, setTargetFolder] = useState({id: crypto.randomUUID(), title: "", subFolders: []}); + const [targetFolder, setTargetFolder] = useState(null); const [uploadCount, setUploadCount] = useState(0); const [exitPage, setExitPage] = useState(false); @@ -59,11 +59,22 @@ function WritePage() { setPost({...post, tags: [...post.tags, post.inputTag], inputTag: ""}); } + const getFolderId = (targetFolder: FolderType) => { + if (!targetFolder || targetFolder.id === "empty") { + return null; + } + + return targetFolder.id; + } const handleSubmit = () => { if (uploadCount > 0) { alert("업로드 중인 이미지가 있습니다. 잠시만 기다려주세요."); return; } + if (!targetFolder) { + alert("폴더를 선택해주세요."); + return; + } setLoading(true); @@ -72,13 +83,14 @@ function WritePage() { createPost({ title: post.title, content: post.content, + folderId: getFolderId(targetFolder), tagNames: post.tags, urls: urls, }) .then(() => { alert("작성되었습니다."); setExitPage(true); - navigate(`/blog/${loginState.username}`); + navigate(`/blog/${loginState.username}?folder=`); }) .catch((error) => alert(error.response.data.message)) .finally(() => setLoading(false)); @@ -89,8 +101,12 @@ function WritePage() { alert("업로드 중인 이미지가 있습니다. 잠시만 기다려주세요."); return; } - + if (!targetFolder) { + alert("폴더를 선택해주세요."); + return; + } if (!id) return; + setLoading(true); const urls: string[] = getImgUrls(post.content); @@ -99,13 +115,14 @@ function WritePage() { id: id, title: post.title, content: post.content, + folderId: getFolderId(targetFolder), tagNames: post.tags, urls: urls, }) .then(() => { alert("수정되었습니다."); setExitPage(true); - navigate(`/blog/${loginState.username}`); + navigate(`/blog/${loginState.username}?folder=`); }) .catch((error) => alert(error.response.data.message)) .finally(() => setLoading(false)); @@ -119,7 +136,7 @@ function WritePage() { deletePost(id) .then(() => { alert("삭제되었습니다."); - navigate(`/blog/${loginState.username}`); + navigate(`/blog/${loginState.username}?folder=`); }) .catch((error) => alert(error.response.data.message)); } @@ -151,6 +168,11 @@ function WritePage() { }, []); const folderData: FolderType[] = [ + { + id: "empty", + title: "폴더 없음", + subFolders: [], + }, { id: crypto.randomUUID(), title: faker.lorem.words(), @@ -226,6 +248,11 @@ function WritePage() { content: res.data.content, tags: sortByName(res.data.tags.map((tag: TagResponse) => tag.name)), }); + setTargetFolder({ + id: res.data.folder.id, + title: res.data.folder.title, + subFolders: [], + }); }) .catch((error) => alert(error.response.data.message)); diff --git a/src/pages/setting/FolderSettingPage.tsx b/src/pages/setting/FolderSettingPage.tsx index 6f4fbb1..88baa9d 100644 --- a/src/pages/setting/FolderSettingPage.tsx +++ b/src/pages/setting/FolderSettingPage.tsx @@ -49,11 +49,7 @@ function FolderSettingPage() { } const [openMoveModal, setOpenMoveModal] = useState(false); - const [selectedFolder, setSelectedFolder] = useState({ - id: crypto.randomUUID(), - title: "", - subFolders: [], - }); + const [selectedFolder, setSelectedFolder] = useState(null); const [editFolderId, setEditFolderId] = useState(""); const [editFolderTitle, setEditFolderTitle] = useState(""); const [targetFolder, setTargetFolder] = useState({ @@ -65,11 +61,12 @@ function FolderSettingPage() { const folderMoveTypes = ["폴더 위로 옮깁니다.", "폴더 아래로 옮깁니다.", "폴더 내부로 옮깁니다."]; const handleMoveFolder = () => { - if (handleDisabled(folderMoveType)) { + if (handleDisabled(folderMoveType) || !selectedFolder) { alert("활성화된 동작 중에서 선택해주세요."); return; } + if (folderMoveType === 2) { setFolders(prevFolders => { const deleteFolderList = getDeleteFolderList(prevFolders, selectedFolder.id); @@ -85,6 +82,10 @@ function FolderSettingPage() { setOpenMoveModal(false); } const getModalMoveFolderList = (folders: FolderType[]) => { + if (!selectedFolder) { + return []; + } + const targetIndex = folders.findIndex((folder) => folder.id === targetFolder.id); if (targetIndex !== -1) { @@ -99,6 +100,10 @@ function FolderSettingPage() { }); } const getAddFolderModalList = (folders: FolderType[]) => { + if (!selectedFolder) { + return []; + } + return folders.map((folder: FolderType): FolderType => { if (folder.id === targetFolder.id) { return {...folder, subFolders: [...folder.subFolders, selectedFolder]}; @@ -132,7 +137,7 @@ function FolderSettingPage() { } const handleDisabled = (moveType: number) => { - if (targetFolder.title === "") { + if (targetFolder.title === "" || !selectedFolder) { return true; } @@ -307,7 +312,9 @@ function FolderSettingPage() {
-

{selectedFolder.title} 폴더를

+

+ {selectedFolder?.title} 폴더를 +

state.loginSlice); - const [inputSearch, setInputSearch] = useState(""); const [posts, setPosts] = useState([]); const [page, setPage] = useState(0); const [pageInfo, setPageInfo] = useState({number: 0, size: 10, totalElements: 0, totalPages: 0}); + const [isFolderEdit, setIsFolderEdit] = useState(false); + const [postIds, setPostIds] = useState([]); + const [folders, setFolders] = useState([]); + const [targetFolder, setTargetFolder] = useState(null); + + const handleIsFolderEdit = () => { + if (!isFolderEdit) { + setIsFolderEdit(true); + setPostIds([]); + setTargetFolder(null); + return; + } + + if (postIds.length === 0) { + setIsFolderEdit(false); + return; + } + + if (confirm("폴더 수정을 취소하시겠습니까?")) { + setIsFolderEdit(false); + return; + } + } + + const handleClickCheckBox = (event: ChangeEvent) => { + const {name, checked} = event.target; + + if (checked) { + setPostIds(prev => [...prev, name]); + } else { + setPostIds(prev => prev.filter(id => id !== name)); + } + } + const handleCheckBox = (id: string) => { + return postIds.indexOf(id) > -1; + } + + const getTargetFolderId = (targetFolder : FolderType | null) => { + if (!targetFolder || targetFolder.id === "empty") { + return null; + } + + return targetFolder.id; + } + const submitPostFolderUpdate = () => { + if (!confirm("변경사항을 저장하시겠습니까?")) { + return; + } + + updatePostFolder({ + postIds: postIds, + folderId: getTargetFolderId(targetFolder), + }) + .then(() => { + alert("폴더가 수정되었습니다."); + setIsFolderEdit(false); + }) + .catch((error) => alert(error.response.data.message)); + } + useEffect(() => { if (!loginState.username) { return; @@ -26,7 +88,7 @@ function PostSettingPage() { getMemberPosts({ username: loginState.username, - folderId: null, + folderIds: [], page: page, size: pageInfo.size, }) @@ -35,18 +97,91 @@ function PostSettingPage() { setPageInfo(res.data.page); }) .catch((error) => alert(error.response.data.message)); + + // todo: folders 데이터 + const folderData: FolderType[] = [ + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + subFolders: [ + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + subFolders: [ + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + subFolders: [], + }, + ], + }, + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + subFolders: [], + }, + ] + }, + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + subFolders: [ + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + subFolders: [], + }, + ], + }, + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + subFolders: [ + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + subFolders: [], + }, + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + subFolders: [], + }, + { + id: crypto.randomUUID(), + title: faker.lorem.words(), + subFolders: [], + }, + ] + }, + ]; + setFolders(folderData); + setFolders(prev => [{ + id: "empty", + title: "폴더 없음", + subFolders: [], + }, ...prev]); }, [loginState, page]); return (
-
-

게시글 관리

-
- - -
+
+

게시글 관리

+ {isFolderEdit && +
+ + + +
} + {!isFolderEdit && +
+ +
}
{posts.map((post: PostResponse) => ( @@ -56,10 +191,17 @@ function PostSettingPage() {

{post.title}

{fullDateToKorean(post.createdAt)}

-
- -
+ {!isFolderEdit && +
+ +
} + {isFolderEdit && + }
))} {posts.length === 0 &&