diff --git a/src/front/src/components/Article.jsx b/src/front/src/components/Article.jsx index 2a3ec66..b936f4c 100644 --- a/src/front/src/components/Article.jsx +++ b/src/front/src/components/Article.jsx @@ -3,82 +3,117 @@ import { Avatar,AvatarImage, AvatarFallback } from "./ui/avatar"; import ArticleDialog from "./ArticleDialog"; import { Link } from "react-router-dom"; import { AlertDialog, AlertDialogTrigger, AlertDialogContent,AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction } from "./ui/alert-dialog"; -import { GenerateLiElUUID } from "@/utils/common"; +import { FullDateFormatString, GenerateLiElUUID } from "@/utils/common"; import CommentCircleIcon from "./svg/CommentCircleIcon"; import { Comment } from "./Comment"; import { useEffect, useState } from "react"; import { MessageCircleIcon, MessageCircleOffIcon } from "lucide-react"; +import { getArticlePictures } from "@/utils/Items"; +import { getAccessTokenInfo } from "@/utils/Cookie"; +import { deleteArticle } from "@/utils/API"; -const Article = ({id, content, createdAt, updatedAt, images, user}) =>{ +const Article = ({id, contents, createdAt, updatedAt, images, user, afterDeleteFn}) =>{ const [commentVisibility, setCommentVisibility] = useState(false); + + //유저 프로필 이미지 const [profilePic, setProfilePic] = useState(""); + //아티클 첨부 이미지들 + const [articlePicItems, setArticlePicItems] = useState([]); + + //jwt Token의 유저 정보와 일치하는지 확인하는 메서드 + const [articleOwner, setArticleOwner] = useState(false); + + useEffect(() =>{ //유저 프로필 이미지가 있다면 if(user.image){ setProfilePic(`data:image/png;base64,${user.image}`); } - - //content설정 + + //아티클 첨부 이미지 업데이트 + if(images){ + setArticlePicItems(getArticlePictures(images)); + } + isWirter(); },[]); const toggleCommentVisibility = () =>{ setCommentVisibility(!commentVisibility); } + const isWirter = () =>{ + const {sub} = getAccessTokenInfo(); + if(sub === user.email){ + setArticleOwner(true) + } + } + + const doDelete = () =>{ + deleteArticle(id) + .then(() =>{ + alert("삭제가 완료되었습니다."); + afterDeleteFn(id); + }) + .catch(e => console.log(e)) + } + return ( - +
- +
-

username

- id +

{user.realName}

+ {user.email}
- - edit - - {/* delete */} - - - delete - - - - - 정말 게시글을 삭제하시겠어요? - - - 게시글을 삭제하면 되돌릴 수 없어요. 신중하게 선택해주세요. 정말 삭제하시겠어요? - - - - 취소 - {alert("삭제 api호출")}}>삭제 - - - + { + articleOwner ? + <> + + edit + + + + delete + + + + + 정말 게시글을 삭제하시겠어요? + + + 게시글을 삭제하면 되돌릴 수 없어요. 신중하게 선택해주세요. 정말 삭제하시겠어요? + + + + 취소 + 삭제 + + + + : + <> + }
- 2024.04.29 15:00 + + {FullDateFormatString(new Date(updatedAt))} +
- {content} + {contents}
    -
  • -
  • -
  • -
  • -
  • + {articlePicItems}
diff --git a/src/front/src/components/ArticleDialog.jsx b/src/front/src/components/ArticleDialog.jsx index bd61dda..433d8f4 100644 --- a/src/front/src/components/ArticleDialog.jsx +++ b/src/front/src/components/ArticleDialog.jsx @@ -3,40 +3,88 @@ import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar"; import { Textarea } from "@/components/ui/textarea"; import FileInput from "@/components/FileInput"; import { Button } from "@/components/ui/button"; +import { useState, useEffect } from "react"; +import { createArticle, fetchLoginUserProfile, updateArticle } from "@/utils/API"; +import { createArticleReqParam, updateArticleReqParam } from "@/utils/Parameter"; +import { getAccessTokenInfo } from "@/utils/Cookie"; const ArticleDialog = (props) =>{ - const resource = { - btnText: "생성", - clickCallback: () =>{ - const file = [...document.getElementById('picture').files]; + const [open, setOpen] = useState(false); - console.log(file); - alert("save!") - }, - initFn: () => {} + //여기서 유저정보를 어떻게 ..? + const {id, user, contents} = props; + const [userInfo, setUserInfo] = useState(null); + const [profilePic, setProfilePic] = useState(""); + //textArea + const [textContents, setTextContents] = useState(""); + const [files, setFiles] = useState([]); + const [mode, setMode] = useState("create"); + useEffect(() =>{ + setTextContents(""); + setFiles([]); + setMode("create"); + setUserInfo(null); + setProfilePic(""); + }, [open]); + + const fetchUserInfo = (dialogIsOpen) =>{ + fetchLoginUserProfile() + .then(async (userInfo) =>{ + setUserInfo(userInfo); + + if(id){ + handleEditMode(); + } + + setProfilePic(`data:image/png;base64,${userInfo.image}`); + }); + } + + const doArticleCreate = () =>{ + const reqParam = createArticleReqParam(textContents, files); + + createArticle(reqParam) + .then((data) => { + setOpen(false); + }) + } + + const doArticleUpdate = () =>{ + const reqParam = updateArticleReqParam(textContents, files); + + updateArticle(id, reqParam) + .then((data) =>{ + console.log(data); + setOpen(false); + }) + } + + const handleEditMode = () =>{ + setMode("edit"); + setTextContents(contents); } return ( - + {setOpen(isOpen); fetchUserInfo();}}> ev.preventDefault()}>
- - - hi + + +
-

username

- id +

{userInfo?.realName}

+ {userInfo?.email}
- - + + - +
diff --git a/src/front/src/components/FileInput.jsx b/src/front/src/components/FileInput.jsx index e9d07e5..ff70572 100644 --- a/src/front/src/components/FileInput.jsx +++ b/src/front/src/components/FileInput.jsx @@ -1,11 +1,11 @@ import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" -const FileInput = () =>{ +const FileInput = ({onChange}) =>{ return (
- + onChange([...ev.target.files])} accept=".png" multiple type="file"/>
); } diff --git a/src/front/src/components/Layout/Header.jsx b/src/front/src/components/Layout/Header.jsx index f030667..232553c 100644 --- a/src/front/src/components/Layout/Header.jsx +++ b/src/front/src/components/Layout/Header.jsx @@ -8,7 +8,7 @@ import { logout } from "@/utils/API"; const Header = (props) =>{ return ( -
+
diff --git a/src/front/src/routes/MainFeed.jsx b/src/front/src/routes/MainFeed.jsx index e8d1a6c..3adcd62 100644 --- a/src/front/src/routes/MainFeed.jsx +++ b/src/front/src/routes/MainFeed.jsx @@ -1,17 +1,31 @@ import Article from "@/components/Article"; import Header from "@/components/Layout/Header"; +import { readMainFeedArticles } from "@/utils/API"; +import { getArticleItems } from "@/utils/Items"; +import { useEffect, useState } from "react"; const MainFeed = () =>{ + const [articles, setArticles] = useState([]); + + useEffect(() =>{ + readMainFeedArticles() + .then((articles) =>{ + setArticles(articles); + }); + }, []); + + const afterArticleDelete = (articleId) =>{ + setArticles(articles.filter(item => item.id !== articleId)); + } + return (
-
-
-
-
-
+ { + getArticleItems(articles, afterArticleDelete) + }
diff --git a/src/front/src/routes/Mypage.jsx b/src/front/src/routes/Mypage.jsx index 85fcafe..6d65f98 100644 --- a/src/front/src/routes/Mypage.jsx +++ b/src/front/src/routes/Mypage.jsx @@ -71,8 +71,11 @@ const MyPage = () => {
{profilePic ? ( - - ) : ( + <> + + {username.charAt(0)} + + ) : ( {username.charAt(0)} )} diff --git a/src/front/src/routes/Test.jsx b/src/front/src/routes/Test.jsx index 63242db..1b8d433 100644 --- a/src/front/src/routes/Test.jsx +++ b/src/front/src/routes/Test.jsx @@ -21,6 +21,7 @@ import UserPage from "./UserPage"; import { createArticle, deleteArticle, fetchLoginUserProfile, readAllMyArticle, readMainFeedArticles, saveProfile, updateArticle, withdraw } from "@/utils/API"; import { createArticleReqParam, saveProfileReqParam, updateArticleReqParam, withdrawReqParam } from "@/utils/Parameter"; import { getAccessToken, getAccessTokenInfo } from "@/utils/Cookie"; +import { getArticleItems } from "@/utils/Items"; const Test = () => { @@ -29,6 +30,7 @@ const Test = () => { /**아티클 API 테스트 */ const [articles, setArticles] = useState([]); + const [articleItems, setArticleItems] = useState(<>); const [articleFiles, setArticleFiles] = useState([]); const resource = { @@ -73,6 +75,7 @@ const Test = () => { readAllMyArticle() .then((response) => { setArticles([...response]); + setArticleItems(getArticleItems([...response])); console.log(response); } ); @@ -133,6 +136,11 @@ const Test = () => { {setArticleFiles([...ev.target.files] || []);}}>
+
+ { + articleItems + } +
); diff --git a/src/front/src/utils/Cookie.js b/src/front/src/utils/Cookie.js index 8fbf56a..1174118 100644 --- a/src/front/src/utils/Cookie.js +++ b/src/front/src/utils/Cookie.js @@ -65,5 +65,5 @@ export const removeRefreshToken = () =>{ export const getAccessTokenInfo = () =>{ const token = getAccessToken().split(" ")[1]; const payload = token.substring(token.indexOf('.') + 1, token.lastIndexOf('.')); - return base64.decode(payload); + return JSON.parse(base64.decode(payload)); } \ No newline at end of file diff --git a/src/front/src/utils/Items.jsx b/src/front/src/utils/Items.jsx index a18d3d5..1d2ac5d 100644 --- a/src/front/src/utils/Items.jsx +++ b/src/front/src/utils/Items.jsx @@ -1,5 +1,6 @@ import Article from "@/components/Article"; import { SelectItem } from "@/components/ui/select" +import { GenerateLiElUUID } from "./common"; /** * @@ -13,13 +14,13 @@ export const getPasswordQuestionItems = (questions) =>{ /** * - * @param {{id: String, content: String, createdAt: String, + * @param {{id: String, contents: String, createdAt: String, * updatedAt: String, images: {id: String, img: Blob, articleId: String}[], * user: {realName: String, email: String, identity: String, location: String, description: String, image: Blob}, * }[] articles */ -export const getArticleItems = (articles) =>{ - const articleItems = articles.map(item =>
); +export const getArticleItems = (articles, afterDeleteFn) =>{ + const articleItems = articles.map(item =>
); return articleItems; } @@ -30,4 +31,19 @@ export const getArticleItems = (articles) =>{ */ export const getCommentItems = (comments) =>{ +} + +/** + * + * @param {{id:String, img: Blob, articlId: String}[]} images + */ +export const getArticlePictures = (images) =>{ + const imageItems = images.map(image =>{ + return + (
  • + +
  • ); + }); + + return imageItems; } \ No newline at end of file diff --git a/src/front/src/utils/common.js b/src/front/src/utils/common.js index dd2aa6d..b4a3b5d 100644 --- a/src/front/src/utils/common.js +++ b/src/front/src/utils/common.js @@ -45,4 +45,24 @@ export function anchorScrollCallback(e){ // [3] 해당 id를 갖은 요소에 scrollIntoview 메서드를 사용한다. IdMatchedPage?.scrollIntoView({ behavior: "smooth" }); +}; + +/** + * + * @param {Date} date + */ +export function FullDateFormatString(date){ + let month = date.getMonth() + 1; + let day = date.getDate(); + let hour = date.getHours(); + let minute = date.getMinutes(); + let second = date.getSeconds(); + + month = month >= 10 ? month : '0' + month; + day = day >= 10 ? day : '0' + day; + hour = hour >= 10 ? hour : '0' + hour; + minute = minute >= 10 ? minute : '0' + minute; + second = second >= 10 ? second : '0' + second; + + return date.getFullYear() + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second; }; \ No newline at end of file