diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 02afa56..537f24c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -87,7 +87,7 @@ jobs: if: contains(github.ref, 'develop') run: | aws deploy create-deployment \ - --application-name duaily-dev-deploy \ + --application-name ${{ secrets.DEPLOY_APPLICATION_NAME_DEV }} \ --deployment-config-name CodeDeployDefault.AllAtOnce \ --deployment-group-name ${{ secrets.DEPLOY_GROUP_DEV }} \ --s3-location bucket=${{ secrets.S3_BUCKET_NAME_DEV }},bundleType=zip,key=$PROJECT_NAME/$GITHUB_SHA.zip @@ -96,7 +96,7 @@ jobs: if: contains(github.ref, 'main') run: | aws deploy create-deployment \ - --application-name duaily-deploy \ + --application-name ${{ secrets.DEPLOY_APPLICATION_NAME }} \ --deployment-config-name CodeDeployDefault.AllAtOnce \ --deployment-group-name ${{ secrets.DEPLOY_GROUP }} \ --s3-location bucket=${{ secrets.S3_BUCKET_NAME }},bundleType=zip,key=$PROJECT_NAME/$GITHUB_SHA.zip diff --git a/package.json b/package.json index 35cdf36..c5fecc9 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "react-quill": "^2.0.0", "react-router-dom": "^6.8.1", "sass": "^1.58.1", - "swiper": "^9.0.5", + "swiper": "^11.1.4", "uuid": "^9.0.0", "vite": "^4.1.0", "vite-tsconfig-paths": "^4.0.5", diff --git a/src/apis/reissue.ts b/src/apis/reissue.ts index be0d7e8..973a4d3 100644 --- a/src/apis/reissue.ts +++ b/src/apis/reissue.ts @@ -1,8 +1,5 @@ -import axios, { AxiosError } from 'axios'; -import { - ApiResponseType, - ApiResponseWithDataType, -} from '@/types/apiResponseType'; +import axios from 'axios'; +import { ApiResponseWithDataType } from '@/types/apiResponseType'; export const reissue = async () => { try { @@ -16,6 +13,6 @@ export const reissue = async () => { ); return data; } catch (err) { - alert((err as AxiosError).response?.data.message); + console.error(err); } }; diff --git a/src/assets/trade/example1.jpg b/src/assets/trade/example1.jpg new file mode 100644 index 0000000..5a82753 Binary files /dev/null and b/src/assets/trade/example1.jpg differ diff --git a/src/assets/trade/example1.png b/src/assets/trade/example1.png deleted file mode 100644 index 69dac8c..0000000 Binary files a/src/assets/trade/example1.png and /dev/null differ diff --git a/src/assets/trade/example2.jpg b/src/assets/trade/example2.jpg new file mode 100644 index 0000000..9093515 Binary files /dev/null and b/src/assets/trade/example2.jpg differ diff --git a/src/assets/trade/example2.png b/src/assets/trade/example2.png deleted file mode 100644 index ab99aad..0000000 Binary files a/src/assets/trade/example2.png and /dev/null differ diff --git a/src/assets/trade/example3.jpg b/src/assets/trade/example3.jpg new file mode 100644 index 0000000..5952fb5 Binary files /dev/null and b/src/assets/trade/example3.jpg differ diff --git a/src/assets/trade/example3.png b/src/assets/trade/example3.png deleted file mode 100644 index 925f465..0000000 Binary files a/src/assets/trade/example3.png and /dev/null differ diff --git a/src/components/Common/Footer/index.tsx b/src/components/Common/Footer/index.tsx index 2ee78b5..ed5f4a1 100644 --- a/src/components/Common/Footer/index.tsx +++ b/src/components/Common/Footer/index.tsx @@ -40,7 +40,7 @@ export default function Footer() { ); }} > - 빈집거래 서비스 관련 + 농가거래 서비스 관련 diff --git a/src/components/Common/Navbar/index.tsx b/src/components/Common/Navbar/index.tsx index 0bebe97..0314b3e 100644 --- a/src/components/Common/Navbar/index.tsx +++ b/src/components/Common/Navbar/index.tsx @@ -37,7 +37,7 @@ function DesktopNavbar({ 오도이촌 소개 - 빈집거래 + 농가거래 오도이촌 소개 - 빈집거래 + 농가거래 커뮤니티 {token ? : } diff --git a/src/components/Trade/AddressModal/index.tsx b/src/components/Trade/AddressModal/index.tsx index 9440d15..7d8428c 100644 --- a/src/components/Trade/AddressModal/index.tsx +++ b/src/components/Trade/AddressModal/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useRef } from 'react'; import DaumPostcodeEmbed, { Address } from 'react-daum-postcode'; import styles from './styles.module.scss'; @@ -8,6 +8,7 @@ type AddressModalProps = { }; function AddressModal({ callback, setIsPostcodeOpen }: AddressModalProps) { + const modalRef = useRef(null); const handleComplete = (data: Address) => { let fullAddress = data.address; let extraAddress = ''; @@ -25,9 +26,19 @@ function AddressModal({ callback, setIsPostcodeOpen }: AddressModalProps) { callback(fullAddress, data.zonecode); setIsPostcodeOpen((pre) => !pre); }; + useEffect(() => { + const handleCloseModal = (e: Event | React.MouseEvent) => { + if (!modalRef.current || !modalRef.current!.contains(e.target as Node)) + setIsPostcodeOpen(false); + }; + window.addEventListener('mousedown', handleCloseModal); + return () => { + window.removeEventListener('mousedown', handleCloseModal); + }; + }, [modalRef]); return (
-
+
diff --git a/src/components/Trade/Board/index.tsx b/src/components/Trade/Board/index.tsx index 2e662d3..1f2762e 100644 --- a/src/components/Trade/Board/index.tsx +++ b/src/components/Trade/Board/index.tsx @@ -2,13 +2,14 @@ import { useEffect, useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import dayjs from 'dayjs'; import { RentalType, TradeBoardType } from '@/types/Board/tradeType'; -import { convertRentalTypeName, priceCount } from '@/utils/utils'; +import { convertRentalTypeName, getHouseName, priceCount } from '@/utils/utils'; import styles from './styles.module.scss'; type TradeBoardProps = Omit; export default function TradeBoard({ houseId, + houseType, rentalType, city, price, @@ -97,7 +98,9 @@ export default function TradeBoard({ )}
-

{title}

+

+ [{getHouseName(houseType)}] {title} +

위치 : {city}

diff --git a/src/components/Trade/Dropdown/index.tsx b/src/components/Trade/Dropdown/index.tsx index 0a79cb3..50dc5e8 100644 --- a/src/components/Trade/Dropdown/index.tsx +++ b/src/components/Trade/Dropdown/index.tsx @@ -1,11 +1,15 @@ import { motion } from 'framer-motion'; import { MenuType } from '@/types/Board/tradeType'; -import { TradeCategoryType, TradeCityType } from '@/constants/trade'; +import { + TradeCategoryType, + TradeCityType, + TradeHouseType, +} from '@/constants/trade'; import { tradeDropdownVariants } from '@/constants/variants'; import styles from './styles.module.scss'; type DropdownProps = { - menu: TradeCategoryType[] | TradeCityType[]; + menu: TradeHouseType[] | TradeCategoryType[] | TradeCityType[]; setMenu: React.Dispatch>; setSelectedMenu: React.Dispatch>; }; diff --git a/src/components/Trade/Info/index.tsx b/src/components/Trade/Info/index.tsx index d78ddf2..e1f9096 100644 --- a/src/components/Trade/Info/index.tsx +++ b/src/components/Trade/Info/index.tsx @@ -1,6 +1,6 @@ import dayjs from 'dayjs'; import { TradeBoardDetailType } from '@/types/Board/tradeType'; -import { getRentalName } from '@/utils/utils'; +import { getHouseName, getRentalName, priceCount } from '@/utils/utils'; import styles from './styles.module.scss'; // TODO: undefined 처리하기 @@ -12,43 +12,35 @@ function TradeBoardInfo({ info }: TradeBoardInfoProps) { return (
- 기본정보 + 임대정보 및 판매자 정보 +
+ 유형

{info?.houseType && getHouseName(info?.houseType)}

+
위치

{info?.city}

- 면적{' '} + 가격{' '}

- {info?.size}m2 + {info?.rentalType && getRentalName(info?.rentalType)}{' '} + {info?.price && priceCount(info?.price)}

- 용도

{info?.purpose}

-
-
- 준공연도

{dayjs(info?.createdDate).format('YYYY')}년

-
-
- 층수

{info?.floorNum === 0 ? '-' : `${info?.floorNum}층`}

+ 전화번호

{info?.contact}

- 임대정보 및 판매자 정보 + 기본정보
- 가격{' '} -

- {info?.rentalType && getRentalName(info?.rentalType)} {info?.price} - 만원 -

+ 면적

{info?.size}㎡

- 전화번호

{info?.contact}

+ 준공연도

{dayjs(info?.createdDate).format('YYYY')}년

+
+
+ 용도

{info?.purpose}

- {info?.userType === 'AGENT' && ( -
- 공인중개사명

{info?.agentName !== '' ? info?.agentName : 'X'}

-
- )}
); diff --git a/src/components/Trade/SearchBar/index.tsx b/src/components/Trade/SearchBar/index.tsx index 79ad6e1..565b638 100644 --- a/src/components/Trade/SearchBar/index.tsx +++ b/src/components/Trade/SearchBar/index.tsx @@ -1,16 +1,18 @@ import { useEffect, useRef, useState } from 'react'; import { GoTriangleDown } from 'react-icons/go'; import { AnimatePresence } from 'framer-motion'; -import { MenuType, RentalType } from '@/types/Board/tradeType'; -import { convertRentalTypeName } from '@/utils/utils'; -import { tradeCategory, tradeCity } from '@/constants/trade'; +import { HouseType, MenuType, RentalType } from '@/types/Board/tradeType'; +import { convertHouseTypeName, convertRentalTypeName } from '@/utils/utils'; +import { houseCategory, tradeCategory, tradeCity } from '@/constants/trade'; import styles from './styles.module.scss'; import Dropdown from '../Dropdown'; type SearchBarProps = { + houseType: string; rentalType: string; city: string; search: string; + setHouseType: React.Dispatch>; setRentalType: React.Dispatch>; setCity: React.Dispatch>; setSearch: React.Dispatch>; @@ -19,9 +21,11 @@ type SearchBarProps = { }; export default function SearchBar({ + houseType, rentalType, city, search, + setHouseType, setRentalType, setCity, setSearch, @@ -60,8 +64,38 @@ export default function SearchBar({
+ setSelectedMenu('houseType')} + > +
+

+ {houseType === '' + ? '유형' + : convertHouseTypeName(houseType as HouseType)} +

+ +
+ + {selectedMenu === 'houseType' && ( + + )} + +
+ +
+

{rentalType === '' - ? '유형' + ? '거래' : convertRentalTypeName(rentalType as RentalType)}

@@ -126,7 +160,7 @@ export default function SearchBar({ ? styles.selectedSearchItem : styles.searchItem } - style={{ flexGrow: '3' }} + style={{ flexGrow: '1' }} onClick={() => setSelectedMenu('search')} >
diff --git a/src/constants/trade.ts b/src/constants/trade.ts index a1e6cf0..0961920 100644 --- a/src/constants/trade.ts +++ b/src/constants/trade.ts @@ -1,10 +1,26 @@ -import { RecommendedTagType, RentalType } from '@/types/Board/tradeType'; +import { + HouseType, + RecommendedTagType, + RentalType, +} from '@/types/Board/tradeType'; export type TradeCategoryType = { content: string; type: RentalType | ''; }; +export type TradeHouseType = { + content: string; + type: HouseType | ''; +}; + +export const houseCategory: TradeHouseType[] = [ + { content: '전체', type: '' }, + { content: '토지', type: 'LAND' }, + { content: '주택', type: 'HOUSE' }, + { content: '농가', type: 'FARM_HOUSE' }, +]; + export const tradeCategory: TradeCategoryType[] = [ { content: '전체', type: '' }, { content: '매매', type: 'SALE' }, diff --git a/src/hooks/useSwiperRef.ts b/src/hooks/useSwiperRef.ts new file mode 100644 index 0000000..f39a4c7 --- /dev/null +++ b/src/hooks/useSwiperRef.ts @@ -0,0 +1,14 @@ +import { useState, useRef, useEffect } from 'react'; + +const useSwiperRef = () => { + const [wrapper, setWrapper] = useState(null); + const ref = useRef(null); + + useEffect(() => { + setWrapper(ref.current); + }, []); + + return [wrapper, ref] as const; +}; + +export default useSwiperRef; diff --git a/src/pages/Introduce/index.tsx b/src/pages/Introduce/index.tsx index a43e504..cc8cb2a 100644 --- a/src/pages/Introduce/index.tsx +++ b/src/pages/Introduce/index.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; import { motion } from 'framer-motion'; -import { Scrollbar } from 'swiper'; +import { Scrollbar } from 'swiper/modules'; import { Swiper, SwiperSlide } from 'swiper/react'; import ReviewBoard from '@/components/Introduce/ReviewBoard'; import TrendBoard from '@/components/Introduce/TrendBoard'; diff --git a/src/pages/Main/index.tsx b/src/pages/Main/index.tsx index 8acd749..e1abe5b 100644 --- a/src/pages/Main/index.tsx +++ b/src/pages/Main/index.tsx @@ -1,17 +1,18 @@ import 'swiper/css'; import 'swiper/css/pagination'; import 'swiper/css/navigation'; -import { useEffect, useRef, useState } from 'react'; +import { useState } from 'react'; import { Link, useNavigate } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; import dayjs from 'dayjs'; import { motion } from 'framer-motion'; -import { Autoplay, Pagination, Navigation } from 'swiper'; +import { Autoplay, Pagination, Navigation } from 'swiper/modules'; import { Swiper, SwiperSlide } from 'swiper/react'; import footer from '@/assets/common/footer.png'; import { QueryKeys, restFetcher } from '@/queryClient'; import { CommunityBoardType } from '@/types/Board/communityType'; import { IntroBoardType } from '@/types/Board/introType'; +import useSwiperRef from '@/hooks/useSwiperRef'; import { getPrefixCategoryName } from '@/utils/utils'; import { ApiResponseWithDataType } from '@/types/apiResponseType'; import { jumbotronData } from '@/constants/main_dummy'; @@ -19,13 +20,14 @@ import { opacityVariants } from '@/constants/variants'; import styles from './styles.module.scss'; export default function MainPage() { - const [_, updateState] = useState(false); const [introToggle, setIntroToggle] = useState<'trend' | 'review'>('trend'); const navigate = useNavigate(); - const introNextRef = useRef(null); - const introPrevRef = useRef(null); - const commuNextRef = useRef(null); - const commuPrevRef = useRef(null); + + const [introPrevEl, introPrevRef] = useSwiperRef(); + const [introNextEl, introNextRef] = useSwiperRef(); + const [commuNextEl, commuNextRef] = useSwiperRef(); + const [commuPrevEl, commuPrevRef] = useSwiperRef(); + const { data: introData } = useQuery< ApiResponseWithDataType >([QueryKeys.PREVIEW_BOARD, QueryKeys.INTRO_BOARD, introToggle], () => @@ -52,10 +54,6 @@ export default function MainPage() { }), ); - useEffect(() => { - updateState(true); - }, [introNextRef, commuNextRef]); - return ( {jumbotronData.map((data, idx) => ( @@ -151,8 +149,8 @@ export default function MainPage() {

{user?.nick_name}님의{' '} - {user?.userType === 'AGENT' ? '빈집거래 활동' : '마이페이지'} + {user?.userType === 'AGENT' ? '농가거래 활동' : '마이페이지'}

{user?.userType === 'AGENT' ? : } @@ -68,6 +68,7 @@ function MyHomePage() { (null); + + const [paginationEl, paginationRef] = useSwiperRef(); const handleDeleteButtonClick = async (houseId: number) => { - if (houseId === 0) throw new Error('없는 빈집거래 게시물입니다.'); + if (houseId === 0) throw new Error('없는 농가거래 게시물입니다.'); await DeleteHouseAPI(houseId); handleToastMessageProps('POST_DELETE_SUCCESS', () => { handleModalClose(); @@ -104,7 +106,10 @@ export default function TradeBoardPage() { {getUserType(data?.data.userType || 'NONE')} -

{data?.data.title}

+

+ {data?.data.houseType && `[${getHouseName(data?.data.houseType)}]`}{' '} + {data?.data.title} +

{data?.data.nickName} @@ -157,7 +162,7 @@ export default function TradeBoardPage() { spaceBetween={50} slidesPerView={1} navigation - pagination={{ el: ref.current }} + pagination={{ el: paginationEl }} scrollbar={{ draggable: true }} > {data?.data.imageUrls.map((url, index) => ( @@ -166,13 +171,27 @@ export default function TradeBoardPage() { ))} -

+
+ {data?.data.userType === 'AGENT' && ( +
+
+
+ 공인중개사명{' '} +

{data.data.agentName !== '' ? data.data.agentName : 'X'}

+
+
+ 상세 설명{' '} +

{data.data.agentDetail && data.data.agentDetail}

+
+
+
+ )}
매물 특징 @@ -197,7 +216,7 @@ export default function TradeBoardPage() {
- 빈집거래 프로세스가 궁금하신가요? + 농가거래 프로세스가 궁금하신가요? + ))} + +
+
+
    {tradeCategory.slice(1).map((item) => (
- +
- +
- +
- + {user?.userType === 'AGENT' && (
- +
)} + {user?.userType === 'AGENT' && ( +
+ + +
+ )}
@@ -289,21 +350,19 @@ export default function TradeWritePage() {
- +
- +
-
+ {/*
-
+
*/}
diff --git a/src/pages/Trade/Write/styles.module.scss b/src/pages/Trade/Write/styles.module.scss index 7566d47..a25facd 100644 --- a/src/pages/Trade/Write/styles.module.scss +++ b/src/pages/Trade/Write/styles.module.scss @@ -307,6 +307,10 @@ border-color: var(--main-color); } +.essential{ + color: var(--main-color); +} + /* Chrome, Safari, Edge, Opera */ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { diff --git a/src/pages/Trade/index.tsx b/src/pages/Trade/index.tsx index f33b036..8574be3 100644 --- a/src/pages/Trade/index.tsx +++ b/src/pages/Trade/index.tsx @@ -27,6 +27,7 @@ export default function TradePage() { const [currentPage, setCurrentPage] = useState(0); const [isLastPage, setIsLastPage] = useState(false); + const [houseType, setHouseType] = useState(''); const [rentalType, setRentalType] = useState(''); const [city, setCity] = useState(''); const [search, setSearch] = useState(''); @@ -89,18 +90,20 @@ export default function TradePage() { >
-

빈집거래

+

농가거래

-            {`자신의 빈집을 등록하거나 다양한 지역의
-빈집을 구경할 수 있는 공간입니다.`}
+            주말농장 토지나 시골집을 등록하고
+            
구경할 수 있는 공간입니다.
{ } }; +// 매매 타입 이름 가져오기 +export const getHouseName = (house: HouseType) => { + switch (house) { + case 'LAND': + return '토지'; + case 'HOUSE': + return '주택'; + case 'FARM_HOUSE': + return '농가'; + default: + return ''; + } +}; + // 매매 타입 이름 가져오기 export const getRentalName = (rental: RentalType) => { switch (rental) { @@ -125,7 +140,7 @@ export const checkBeforePost = ( return true; }; -// 빈집거래 글쓰기 필수 입력사항 체크 +// 농가거래 글쓰기 필수 입력사항 체크 export const checkBeforeTradePost = ( user: User, tradeBoardForm: TradeBoardForm, @@ -139,9 +154,8 @@ export const checkBeforeTradePost = ( monthlyPrice, contact, agentName, + agentDetail, size, - floorNum, - createdDate, purpose, title, code, @@ -183,18 +197,17 @@ export const checkBeforeTradePost = ( alert('중개사 이름을 입력해주세요.'); return false; } - if (size === '') { - alert('평수를 입력해주세요.'); - return false; - } - if (floorNum < 0) { - alert('1층 이상의 값만 작성해주세요.'); + + if (user.userType === 'AGENT' && agentDetail === '') { + alert('상세 설명을 적어주세요.'); return false; } - if (createdDate === '') { - alert('준공일을 입력해주세요.'); + + if (size === '') { + alert('평수를 입력해주세요.'); return false; } + if (purpose === '') { alert('용도를 입력해주세요.'); return false; @@ -271,6 +284,12 @@ export const convertRentalTypeName = (typeName: RentalType) => { if (typeName === 'MONTHLYRENT') return '월세'; }; +export const convertHouseTypeName = (typeName: HouseType) => { + if (typeName === 'LAND') return '토지'; + if (typeName === 'HOUSE') return '주택'; + if (typeName === 'FARM_HOUSE') return '농가'; +}; + // 문자가 자음이거나 모음인지 확인하는 함수 export const isConsonant = (char: string) => { const pattern = /[ㄱ-ㅎ|ㅏ-ㅣ]/; diff --git a/yarn.lock b/yarn.lock index 98d3311..7011d01 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3419,11 +3419,6 @@ slice-ansi@^5.0.0: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== -ssr-window@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/ssr-window/-/ssr-window-4.0.2.tgz#dc6b3ee37be86ac0e3ddc60030f7b3bc9b8553be" - integrity sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ== - stop-iteration-iterator@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" @@ -3569,12 +3564,10 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -swiper@^9.0.5: - version "9.2.4" - resolved "https://registry.yarnpkg.com/swiper/-/swiper-9.2.4.tgz#2fa3cf58cef586366f674a10fa56fe6eec2026fe" - integrity sha512-L7y3K/iiMXNYQ94FbfcJn7jex4QPnS4+voXGupTdC+UHW4XrR40QDdm4c9hXJ+Br0Il7PP0vP1W3goM9/Ly6Sg== - dependencies: - ssr-window "^4.0.2" +swiper@^11.1.4: + version "11.1.4" + resolved "https://registry.yarnpkg.com/swiper/-/swiper-11.1.4.tgz#2f8e303e8bf9e5bc40a3885fc637ae60ff27996c" + integrity sha512-1n7kbYJB2dFEpUHRFszq7gys/ofIBrMNibwTiMvPHwneKND/t9kImnHt6CfGPScMHgI+dWMbGTycCKGMoOO1KA== synckit@^0.8.5: version "0.8.5"