Commit deade4a1 authored by Lee Soobeom's avatar Lee Soobeom
Browse files

editpost 기능

parent 4415469c
......@@ -7,6 +7,7 @@ import { Header, Body } from "./home";
import { Board } from "./board";
import Posting from "./post/posting";
import { Layout } from "./commons";
import { EditPost } from "./post/editpost";
export const App = () => {
return (
......@@ -26,7 +27,9 @@ export const App = () => {
}
/>
<Route path="board" element={<Board />} />
<Route path="post/:postId" element={<IntoPost />} />
<Route path="post/:postId/" element={<IntoPost />}>
<Route path="edit" element={<EditPost />} />
</Route>
<Route
path="profile"
element={
......
......@@ -4,7 +4,6 @@ import { PostType } from "../types";
export const posting = async (post: PostType) => {
const { data } = await axios.post(`${baseUrl}/posts/`, post);
return data;
};
......@@ -24,3 +23,13 @@ export const getPostByPostId = async (_id: string) => {
const { data } = await axios.get(`${baseUrl}/posts/${_id}`);
return data;
};
export const deletePost = async (_id: string) => {
const { data } = await axios.delete(`${baseUrl}/posts/${_id}`);
return data;
};
export const updating = async (post: PostType) => {
const { data } = await axios.put(`${baseUrl}/posts/${post._id}`, post);
return data;
};
import React, { FormEvent, useState } from "react";
import { useNavigate, useLocation } from "react-router-dom";
import isLength from "validator/lib/isLength";
import equals from "validator/lib/equals";
import { catchErrors } from "../helpers";
import { PostType } from "../types";
import { postApi } from "../apis";
import { PostState } from "./intopost";
export function EditPost() {
const [city, setCity] = useState<string>("질문종류");
const [theme, setTheme] = useState<string>("질문종류");
const [title, setTitle] = useState<string>("");
const [text, setText] = useState<string>("");
const navigate = useNavigate();
const location = useLocation() as PostState;
const post = location.state;
const [user, setUser] = useState<PostType>({
title: post.title,
text: post.text,
theme: post.theme,
city: post.city,
date: post.date,
user: post.user,
counts: 0,
_id: post._id,
});
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const [disabled, setDisabled] = useState(false);
const [success, setSuccess] = useState(false);
async function handlePostSubmit(event: FormEvent) {
event.preventDefault();
try {
setError("");
console.log("user data", user);
if (postingFormMatch()) {
setLoading(true);
const res = await postApi.updating(user);
navigate("/board", { replace: true });
setSuccess(true);
setError("");
}
} catch (error) {
console.log("에러발생");
catchErrors(error, setError);
} finally {
setLoading(false);
}
}
function postingFormMatch() {
if (!isLength(user.title ?? "", { min: 1 })) {
setError("제목을 입력해 주세요.");
return false;
} else if (!isLength(user.text ?? "", { min: 1 })) {
setError("내용을 입력해 주세요.");
return false;
} else if (equals(city, "질문종류")) {
setError("테마를 선택해 주세요.");
return false;
} else if (equals(theme, "질문종류")) {
setError("도시를 선택해 주세요.");
return false;
} else {
return true;
}
}
const titleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
const title = event.currentTarget.value;
const newUser = { ...user, title: title };
console.log(event.currentTarget.value);
setTitle(event.currentTarget.value);
setUser(newUser);
};
const textChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
const text = event.currentTarget.value;
const newUser = { ...user, text: text };
console.log(event.currentTarget.value);
setText(event.currentTarget.value);
setUser(newUser);
};
const cityChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const city = event.currentTarget.value;
const newUser = { ...user, city: city };
console.log(event.currentTarget.value);
setCity(event.currentTarget.value);
setUser(newUser);
};
const themeChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const theme = event.currentTarget.value;
const newUser = { ...user, theme: theme };
console.log(event.currentTarget.value);
setTheme(event.currentTarget.value);
setUser(newUser);
};
return (
<div className="flex flex-col border-3">
<form onSubmit={handlePostSubmit} className="w-full items-center">
<div className="flex flex-row relative">
<p className="basis-1/12 gap-x-8">Id</p>
<p className="basis-8/12 invisible">empty</p>
<select
name="city"
id="Questions"
className="border border-3 border-black w-1/12"
onChange={cityChange}
defaultValue="질문종류"
>
<option value="질문종류">도시</option>
<option value="Seoul">서울</option>
<option value="Busan">부산</option>
<option value="Incheon">인천</option>
<option value="Daegoo">대구</option>
<option value="Kwangjoo">광주</option>
<option value="Daejeon">대전</option>
<option value="Woolsan">울산</option>
<option value="Sejong">세종</option>
<option value="Dokdo">독도</option>
<option value="Jeju">제주</option>
</select>
<select
name="theme"
id="Questions"
className="border border-3 border-black w-1/12"
onChange={themeChange}
defaultValue="질문종류"
>
<option value="질문종류">테마</option>
<option value="cycling">사이클링</option>
<option value="surfing">서핑</option>
<option value="activity">액티비티</option>
<option value="camping">캠핑</option>
<option value="sking">스키</option>
<option value="boat">보트</option>
<option value="desert">사막</option>
<option value="golf">골프</option>
<option value="cave">동굴</option>
<option value="history">문화재</option>
<option value="zoo">동물원</option>
<option value="cycling">사이클링</option>
</select>
<button
type="submit"
className="border border-black basis-1/12 gap-x-8"
>
글쓰기
</button>
</div>
<div className="flex border-4">
<textarea
name="title"
onChange={titleChange}
placeholder="title"
className="w-full h-8"
></textarea>
</div>
<div className="flex border-2">
<textarea
onChange={textChange}
name="text"
placeholder="text"
className="w-full h-96"
></textarea>
</div>
</form>
</div>
);
}
import React from "react";
import { useLocation } from "react-router-dom";
import React, { MouseEvent } from "react";
import { useLocation, useNavigate, Link } from "react-router-dom";
import { postApi } from "../apis";
import { PostType } from "../types";
interface PostState {
export interface PostState {
state: PostType;
}
export function IntoPost() {
const location = useLocation() as PostState;
const post = location.state;
const navigate = useNavigate();
// console.log(post);
const handleDeleteClick = async (event: MouseEvent<HTMLButtonElement>) => {
const postId = event.currentTarget.id;
const res = await postApi.deletePost(postId);
navigate("/board", { replace: true });
console.log("delete post", res);
};
return (
<div>
<div>
<div className="flex flex-row basis-8">
<div className="border-2 border-current rounded">
<button id={post._id} onClick={handleDeleteClick}>
delete
</button>
</div>
<div className="border-2 border-current rounded">
<Link to={`/post/${post._id}/edit`} state={post}>
<button>update</button>
</Link>
</div>
</div>
<div className="flex flex-row">
<div className="flex basis-3/4">제목: {post.title}</div>
<div className="flex basis-1/4">작성자: nickname</div>
<div className="flex basis-3/4 border-2 border-black rounded">
제목: {post.title}
</div>
<div className="flex basis-1/4 border-2 border-black rounded">
작성자: nickname
</div>
</div>
<div className="flex flex-row">
<div className="flex basis-1/4">도시: {post.city}</div>
<div className="flex basis-1/4">테마: {post.theme}</div>
<div className="flex basis-1/4">작성일: {post.date}</div>
<div className="flex basis-1/4">조회수: {post.counts}</div>
<div className="flex basis-1/4 border-2 border-black rounded">
도시: {post.city}
</div>
<div className="flex basis-1/4 border-2 border-black rounded">
테마: {post.theme}
</div>
<div className="flex basis-1/4 border-2 border-black rounded">
작성일: {post.date}
</div>
<div className="flex basis-1/4 border-2 border-black rounded">
조회수: {post.counts}
</div>
</div>
</div>
<div>{post.text}</div>
<div className="border-2 border-black rounded h-96">{post.text}</div>
</div>
);
}
......@@ -7,7 +7,7 @@ export type Props = {
post: PostType;
handleClick: MouseEventHandler;
};
// Link opts = state : send obj , useLocation location.state
export default function Post({ handleClick, post }: Props) {
return (
<div className="flex flex-row h-16 divide-x-2 border-2 border-solid">
......
......@@ -30,14 +30,15 @@ export default function Posting() {
const [success, setSuccess] = useState(false);
async function handlePostSubmit(event: FormEvent) {
event.preventDefault(); // prevent onSubmit -> rerendering
event.preventDefault();
try {
setError("");
console.log("user data", user);
if (postingFormMatch()) {
setLoading(true);
const res = await postApi.posting(user);
// console.log("서버연결됬나요", res);
console.log("서버연결됬나요", res);
// console.log("user save");
navigate("/board", { replace: true });
setSuccess(true);
......
......@@ -16,7 +16,7 @@ export interface PostType {
text: string;
theme: string;
city: string;
date: string;
date: string | number;
counts: number;
_id: string;
user: string;
......
......@@ -15,10 +15,9 @@ export const postCreate = asyncWrap(async (reqExp, res, next) => {
theme: string;
city: string;
date: Date;
counts: number;
};
console.log("body", req.body);
// 1) title 빈 문자열인지 확인
if (!isLength(title ?? "", { min: 1 })) {
return res.status(422).send("제목을 한 글자 이상 입력해주세요");
......@@ -46,10 +45,12 @@ export const postCreate = asyncWrap(async (reqExp, res, next) => {
text,
theme,
city,
date,
date: Date.now(),
user: userId,
});
console.log("post", newPost);
return res.json(newPost);
});
......@@ -90,3 +91,44 @@ export const getOnePost = asyncWrap(async (req, res) => {
return res.json(post);
});
export const deleteOnePost = asyncWrap(async (req, res) => {
const { postId } = req.params;
console.log(postId);
const deleteCount = await postDb.deletePost(postId);
return res.json(deleteCount);
});
export const updatePost = asyncWrap(async (reqExp, res) => {
const req = reqExp as TypedRequestAuth<{ userId: string }>;
const { title, text, theme, city, date } = req.body as {
title: string;
text: string;
theme: string;
city: string;
date: Date;
counts: number;
};
const userId = req.auth.userId;
const { postId } = req.params;
const updatePost = await postDb.updateOnePost(
{
title,
text,
theme,
city,
date: Date.now(),
counts: req.body.counts,
user: userId,
},
postId
);
console.log("게시글 수정 후", updatePost);
return res.json(updatePost);
});
......@@ -24,7 +24,6 @@ export const addOneCount = async (_id: string, counts: number) => {
{ counts: counts },
{ new: true }
);
// console.log(newCounts);
return newCounts;
......@@ -34,3 +33,17 @@ export const getPost = async (_id: string) => {
const post = await Post.findOne({ _id: _id });
return post;
};
export const deletePost = async (_id: string) => {
const res = await Post.deleteOne({ _id: _id });
return res;
};
export const updateOnePost = async (post: PostType, _id: string) => {
const newPost = await Post.findOneAndUpdate(
{ _id: _id },
{ post },
{ new: true }
);
return newPost;
};
......@@ -6,7 +6,7 @@ export interface PostType {
theme: string;
city: string;
user: Types.ObjectId | string;
date: Date;
date: Date | number;
counts?: number;
}
......@@ -31,7 +31,7 @@ const PostSchema = new Schema<PostType>({
},
date: {
type: Date,
default: Date.now,
default: Date.now(),
},
counts: {
type: Number,
......
......@@ -4,5 +4,16 @@ import { postCtrl, authCtrl } from "../controllers";
const router = express.Router();
router.route("/").post(authCtrl.requireLogin, postCtrl.postCreate);
router.route("/").get(postCtrl.getAllPost);
router
.route("/:postId")
.post(authCtrl.requireLogin, postCtrl.addCounts)
.get(authCtrl.requireLogin, postCtrl.getOnePost);
router.route("/:postId").delete(authCtrl.requireLogin, postCtrl.deleteOnePost); // authenticate
router.route("/:postId").put(authCtrl.requireLogin, postCtrl.updatePost);
router.param("postId", postCtrl.userByPostId);
export default router;
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment