From 66eab9c348658aed0e8a8601043b8f48e3046748 Mon Sep 17 00:00:00 2001
From: jonghun
Date: Thu, 19 Oct 2023 20:42:01 +0900
Subject: [PATCH] =?UTF-8?q?feat:=20=EB=B9=88=EC=A7=91=EA=B1=B0=EB=9E=98=20?=
=?UTF-8?q?=EC=83=81=EC=84=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=88=98?=
=?UTF-8?q?=EC=A0=95/=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?=
=?UTF-8?q?=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/apis/houses.ts | 22 +++++++++
src/components/Trade/Quill/index.tsx | 57 +++++++++++++++++++---
src/pages/Trade/Board/index.tsx | 32 +++++++++++-
src/pages/Trade/Board/styles.module.scss | 6 ++-
src/pages/Trade/Write/index.tsx | 62 +++++++++++++++---------
src/pages/Trade/index.tsx | 33 ++++++-------
src/types/Board/tradeType.ts | 3 +-
7 files changed, 163 insertions(+), 52 deletions(-)
diff --git a/src/apis/houses.ts b/src/apis/houses.ts
index 774d5e6..0a83b60 100644
--- a/src/apis/houses.ts
+++ b/src/apis/houses.ts
@@ -16,3 +16,25 @@ export const PostHouseAPI = async (form: TradeBoardForm) => {
return err.response?.data;
}
};
+
+export const PutHouseAPI = async (id: number, form: TradeBoardForm) => {
+ try {
+ const response = await axios.put<
+ ApiResponseWithDataType
+ >(`/houses/${id}`, form);
+ return response.data;
+ } catch (error) {
+ const err = error as AxiosError;
+ return err.response?.data;
+ }
+};
+
+export const DeleteHouseAPI = async (id: number) => {
+ try {
+ const response = await axios.delete(`/houses/${id}`);
+ return response.data;
+ } catch (error) {
+ const err = error as AxiosError;
+ return err.response?.data;
+ }
+};
diff --git a/src/components/Trade/Quill/index.tsx b/src/components/Trade/Quill/index.tsx
index 0a9060d..a4a3cdb 100644
--- a/src/components/Trade/Quill/index.tsx
+++ b/src/components/Trade/Quill/index.tsx
@@ -1,7 +1,9 @@
import { useRef } from 'react';
import ReactQuill from 'react-quill';
-import { useNavigate } from 'react-router-dom';
-import { TradeBoardForm } from '@/types/Board/tradeType';
+import { useLocation, useNavigate } from 'react-router-dom';
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { QueryKeys, restFetcher } from '@/queryClient';
+import { TradeBoardDetailType, TradeBoardForm } from '@/types/Board/tradeType';
import getImageUrls from '@/utils/Quill/getImageUrls';
import { PostHouseAPI } from '@/apis/houses';
import userStore from '@/store/userStore';
@@ -9,7 +11,6 @@ import useQuillModules from '@/hooks/useQuillModules';
import 'react-quill/dist/quill.snow.css';
import { checkBeforeTradePost } from '@/utils/utils';
import styles from './styles.module.scss';
-// TODO: 이미지 10개 이상 등록 불가
type TradeQuillProps = {
form: TradeBoardForm;
@@ -26,11 +27,33 @@ export default function TradeQuill({
}: TradeQuillProps) {
const { user } = userStore();
const navigate = useNavigate();
-
+ const { state }: { state: { data: TradeBoardDetailType } } = useLocation();
const QuillRef = useRef();
// 이미지를 업로드 하기 위한 함수
const modules = useQuillModules(QuillRef);
+ const queryClient = useQueryClient();
+ const { mutate } = useMutation(
+ (tradeBoardForm: TradeBoardForm) =>
+ restFetcher({
+ method: 'PUT',
+ path: `houses/${state.data.houseId}`,
+ body: {
+ ...tradeBoardForm,
+ },
+ }),
+ {
+ onSuccess: () => {
+ alert('게시글을 수정하였습니다.');
+ queryClient.refetchQueries([QueryKeys.TRADE_BOARD]);
+ navigate(`/trade`);
+ },
+ onError: () => {
+ alert('게시글 수정을 실패했습니다.');
+ },
+ },
+ );
+
const onPost = async () => {
const imageUrls = [thumbnail, ...getImageUrls(form.code)];
@@ -43,12 +66,25 @@ export default function TradeQuill({
try {
await PostHouseAPI(newForm);
+ alert('등록되었습니다.');
navigate(`/trade`);
} catch (error) {
console.error(error);
}
};
+ const onUpdate = async () => {
+ const imageUrls = [thumbnail, ...getImageUrls(form.code)];
+
+ const newForm = {
+ ...form,
+ imageUrls,
+ };
+
+ if (!checkBeforeTradePost(user!, newForm)) return;
+
+ mutate(newForm);
+ };
return (
@@ -72,6 +108,7 @@ export default function TradeQuill({
QuillRef.current = element;
}
}}
+ value={form.code}
onChange={(value) => setForm((prev) => ({ ...prev, code: value }))}
modules={modules}
placeholder="사진 5장 이상은 필수입니다. 5장 이상(건물 외관, 내부 포함) 업로드 되지 않을 시, 반려됩니다."
@@ -88,9 +125,15 @@ export default function TradeQuill({
>
임시저장
-
+ {state ? (
+
+ ) : (
+
+ )}
);
diff --git a/src/pages/Trade/Board/index.tsx b/src/pages/Trade/Board/index.tsx
index 5c37806..c081c04 100644
--- a/src/pages/Trade/Board/index.tsx
+++ b/src/pages/Trade/Board/index.tsx
@@ -17,6 +17,7 @@ import 'swiper/css';
import 'swiper/css/pagination';
import 'swiper/css/navigation';
import { TradeBoardDetailType } from '@/types/Board/tradeType';
+import { DeleteHouseAPI } from '@/apis/houses';
import userStore from '@/store/userStore';
import { getMoveInType, getRentalName, getUserType } from '@/utils/utils';
import { ApiResponseWithDataType } from '@/types/apiResponseType';
@@ -39,6 +40,19 @@ export default function TradeBoardPage() {
const [modal, setModal] = useState(false);
const ref = useRef(null);
+ const handleDeleteButtonClick = async (houseId: number) => {
+ if (houseId === 0) throw new Error('없는 빈집거래 게시물입니다.');
+ await DeleteHouseAPI(houseId);
+ alert('삭제되었습니다.');
+ navigate('/trade');
+ };
+
+ const handleEditButtonClick = () => {
+ navigate(`/trade/write`, {
+ state: { data: data?.data },
+ });
+ };
+
useEffect(() => {
updateState(true);
}, []);
@@ -86,7 +100,23 @@ export default function TradeBoardPage() {
{user?.nick_name === data?.data.nickName ? (
- 수정 | 삭제
+ {' '}
+ |{' '}
+
) : null}
diff --git a/src/pages/Trade/Board/styles.module.scss b/src/pages/Trade/Board/styles.module.scss
index 45a0497..f6999f0 100644
--- a/src/pages/Trade/Board/styles.module.scss
+++ b/src/pages/Trade/Board/styles.module.scss
@@ -62,6 +62,7 @@
& > div {
display: flex;
+ align-items: center;
gap: 1rem;
font-size: 1.25rem;
font-weight: 400;
@@ -70,8 +71,11 @@
font-weight: 200;
}
- & > div > span {
+ & > div > button {
cursor: pointer;
+ background: inherit;
+ border: none;
+ color: var(--darkgray-color);
&:hover {
color: var(--main-color);
}
diff --git a/src/pages/Trade/Write/index.tsx b/src/pages/Trade/Write/index.tsx
index f8c0046..1a84ce6 100644
--- a/src/pages/Trade/Write/index.tsx
+++ b/src/pages/Trade/Write/index.tsx
@@ -1,12 +1,16 @@
import { useRef, useState } from 'react';
-import { Navigate } from 'react-router-dom';
+import { Navigate, useLocation } from 'react-router-dom';
import { FiSearch } from 'react-icons/fi';
import { MdUploadFile } from 'react-icons/md';
import { AxiosError } from 'axios';
import { motion } from 'framer-motion';
import AddressModal from '@/components/Trade/AddressModal';
import TradeQuill from '@/components/Trade/Quill';
-import { RentalType, TradeBoardForm } from '@/types/Board/tradeType';
+import {
+ RentalType,
+ TradeBoardDetailType,
+ TradeBoardForm,
+} from '@/types/Board/tradeType';
import { uploadFile } from '@/apis/uploadS3';
import userStore from '@/store/userStore';
import { getRentalPriceType } from '@/utils/utils';
@@ -20,43 +24,45 @@ const { VITE_S3_DOMAIN, VITE_CLOUD_FRONT_DOMAIN } = import.meta.env;
export default function TradeWritePage() {
const { user } = userStore();
+ const { state }: { state: { data: TradeBoardDetailType } } = useLocation();
+
const [form, setForm] = useState({
- rentalType: 'SALE',
- city: '',
- zipCode: '',
- size: '',
- purpose: '',
- floorNum: 0,
- contact: '',
- createdDate: '',
- price: 0,
- monthlyPrice: 0,
- agentName: '',
- title: '',
- code: '',
- imageUrls: [],
- tmpYn: false,
- recommendedTag: [],
+ rentalType: state?.data.rentalType || 'SALE',
+ city: state?.data.city || '',
+ zipCode: state?.data.zipCode || '',
+ size: state?.data.size || '',
+ purpose: state?.data.purpose || '',
+ floorNum: state?.data.floorNum || 0,
+ contact: state?.data.contact || '',
+ createdDate: state?.data.createdDate || '',
+ price: state?.data.price || 0,
+ monthlyPrice: state?.data.monthlyPrice || 0,
+ agentName: state?.data.agentName || '',
+ title: state?.data.title || '',
+ code: state?.data.code || '',
+ imageUrls: state?.data.imageUrls || [],
+ tmpYn: state?.data.tmpYn || false,
+ recommendedTag: state?.data.recommendedTag || [],
});
- const [thumbnail, setThumbnail] = useState('');
- const [thumbnailTitle, setThumbnailTitle] = useState('');
+ const [thumbnail, setThumbnail] = useState(state.data.imageUrls[0] || '');
+ const [thumbnailTitle, setThumbnailTitle] = useState(
+ state.data.imageUrls[0].split('/')[3] || '',
+ );
const thumbnailRef = useRef(null);
// 매물특징
- // const appendSpecialCategory = (category: string) => {};
const [isPostcodeOpen, setIsPostcodeOpen] = useState(false);
- const postCodeCallback = (fullAddress: string, zipCode?: string) => {
+ const postCodeCallback = (fullAddress: string, zipCodePost?: string) => {
setForm((prev: TradeBoardForm) => ({
...prev,
city: fullAddress,
- zipCode: zipCode ?? '',
+ zipCode: zipCodePost ?? '',
}));
};
const onChangeForm = (e: React.ChangeEvent) => {
const { name, value } = e.target;
-
setForm((prev) => ({ ...prev, [name]: value }));
};
@@ -212,6 +218,7 @@ export default function TradeWritePage() {
type="number"
placeholder="만원으로 표기"
name="price"
+ value={form.price}
onChange={onChangeForm}
/>
@@ -226,6 +233,7 @@ export default function TradeWritePage() {
type="number"
placeholder="만원으로 표기"
name="monthlyPrice"
+ value={form.monthlyPrice}
onChange={onChangeForm}
/>
@@ -236,6 +244,7 @@ export default function TradeWritePage() {
type="text"
placeholder="01000000000 표기(매물 관련 연락 가능한 연락처)"
name="contact"
+ value={form.contact}
onChange={onChangeForm}
/>
@@ -247,6 +256,7 @@ export default function TradeWritePage() {
type="text"
placeholder="부동산명 표기"
name="agentName"
+ value={form.agentName}
onChange={onChangeForm}
/>
@@ -266,6 +276,7 @@ export default function TradeWritePage() {
type="text"
placeholder="m2로 표기"
name="size"
+ value={form.size}
onChange={onChangeForm}
/>
@@ -278,6 +289,7 @@ export default function TradeWritePage() {
type="text"
placeholder="년도로 표기"
name="createdDate"
+ value={form.createdDate}
onChange={onChangeForm}
/>
@@ -290,6 +302,7 @@ export default function TradeWritePage() {
type="text"
placeholder="용도 및 방 개수, 화장실 개수 포함"
name="purpose"
+ value={form.purpose}
onChange={onChangeForm}
/>
@@ -300,6 +313,7 @@ export default function TradeWritePage() {
type="number"
placeholder="아파트, 빌라와 같은 다가구 주택만 작성"
name="floorNum"
+ value={form.floorNum}
onChange={onChangeForm}
/>
diff --git a/src/pages/Trade/index.tsx b/src/pages/Trade/index.tsx
index 615fa7b..3d52db7 100644
--- a/src/pages/Trade/index.tsx
+++ b/src/pages/Trade/index.tsx
@@ -49,25 +49,21 @@ export default function TradePage() {
return restFetcher({ method: 'GET', path: 'houses', params });
};
- const { refetch: fetchBoard, isLoading: isBoardLoading } = useQuery<
- ApiResponseWithDataType
- >([QueryKeys.TRADE_BOARD], () => fetchBoardList(), {
- onSuccess: (response) => {
- setBoardListData(response.data.content);
- setIsLastPage(response.data.last);
+ const {
+ refetch: fetchBoard,
+ isLoading: isBoardLoading,
+ isInitialLoading: isMoreBoardLoading,
+ } = useQuery>(
+ [QueryKeys.TRADE_BOARD, currentPage],
+ () => fetchBoardList(currentPage),
+ {
+ onSuccess: (response) => {
+ setBoardListData((prev) => [...prev, ...response.data.content]);
+ setIsLastPage(response.data.last);
+ },
+ staleTime: 0,
},
- });
-
- const { isInitialLoading: isMoreBoardLoading } = useQuery<
- ApiResponseWithDataType
- >([QueryKeys.TRADE_BOARD, currentPage], () => fetchBoardList(currentPage), {
- enabled: currentPage !== 0,
- onSuccess: (response) => {
- setBoardListData((prev) => [...prev, ...response.data.content]);
- setIsLastPage(response.data.last);
- },
- staleTime: 0,
- });
+ );
const handleMoreBoards = () => {
if (!isLastPage) {
@@ -76,6 +72,7 @@ export default function TradePage() {
};
useEffect(() => {
+ setBoardListData([]);
fetchBoard();
setCurrentPage(0);
}, [recommendedTags, dealState]);
diff --git a/src/types/Board/tradeType.ts b/src/types/Board/tradeType.ts
index df7c943..6a841f0 100644
--- a/src/types/Board/tradeType.ts
+++ b/src/types/Board/tradeType.ts
@@ -47,7 +47,7 @@ export type TradeBoardDetailType = {
houseId: number;
rentalType: RentalType;
city: string;
- zipcode: string;
+ zipCode: string;
size: string;
purpose: string;
floorNum: number;
@@ -64,6 +64,7 @@ export type TradeBoardDetailType = {
createdAt: Date;
isCompleted: boolean;
isScraped: boolean;
+ tmpYn: boolean;
recommendedTag: RecommendedTagType[];
recommendedTagName: string[];
};