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[]; };