From bdaaec8119fea753b04494532eb7a61d96e28936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B1=84=EC=A7=80=EC=9B=90?= Date: Wed, 14 May 2025 22:06:39 +0900 Subject: [PATCH 1/4] =?UTF-8?q?remove:=20=ED=97=A4=EB=8D=94=20=EB=92=A4?= =?UTF-8?q?=EB=A1=9C=EA=B0=80=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/PageHeader/PageHeader.tsx | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/components/PageHeader/PageHeader.tsx b/src/components/PageHeader/PageHeader.tsx index 77312c2..0857f07 100644 --- a/src/components/PageHeader/PageHeader.tsx +++ b/src/components/PageHeader/PageHeader.tsx @@ -11,7 +11,6 @@ import { Link, useNavigate } from "react-router-dom"; import useUserStore from "@stores/useUserStore"; import AccountCircleIcon from "@mui/icons-material/AccountCircle"; import apiClient from "@apis/apiClient"; -import ArrowBackIcon from "@mui/icons-material/ArrowBackIos"; interface PageHeaderProps { title: string; @@ -36,11 +35,11 @@ const PageHeader = ({ title, userName, action }: PageHeaderProps) => { try { const accessToken = sessionStorage.getItem("_ZA"); const deviceId = localStorage.getItem("deviceId"); - + if (!accessToken || !deviceId) { throw new Error("로그아웃 정보가 부족합니다."); } - + await apiClient.post( "/users/logout", {}, @@ -52,12 +51,11 @@ const PageHeader = ({ title, userName, action }: PageHeaderProps) => { withCredentials: true, } ); - + sessionStorage.removeItem("_ZA"); clearUser(); navigate("/sign-in"); } catch (error) { - console.error("로그아웃 실패", error); sessionStorage.removeItem("_ZA"); clearUser(); navigate("/sign-in"); @@ -83,16 +81,6 @@ const PageHeader = ({ title, userName, action }: PageHeaderProps) => { }} > - { - navigate(-1)} - sx={{ - color: "#222222", - }} - > - - - } Date: Thu, 15 May 2025 01:17:35 +0900 Subject: [PATCH 2/4] =?UTF-8?q?design:=20=EA=B0=9C=EC=9D=B8=20=EB=A7=A4?= =?UTF-8?q?=EB=AC=BC=20=EA=B4=80=EB=A0=A8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DeleteConfirm/DeleteConfirmModal.tsx | 139 +++-- src/components/Toast/Toast.tsx | 37 ++ .../AgentPropertyDetailPage.tsx | 149 ++--- .../PrivatePropertyListPage.tsx | 61 +- .../PropertyAddButtonList/BulkUploadModal.tsx | 8 +- .../PropertyAddButtonList.tsx | 4 +- .../PropertyAddModal/PropertyAddModal.tsx | 556 ++++++++++-------- .../PropertyTable/PropertyTable.tsx | 66 ++- 8 files changed, 589 insertions(+), 431 deletions(-) create mode 100644 src/components/Toast/Toast.tsx diff --git a/src/components/DeleteConfirm/DeleteConfirmModal.tsx b/src/components/DeleteConfirm/DeleteConfirmModal.tsx index 7f3f8a6..6d05bc8 100644 --- a/src/components/DeleteConfirm/DeleteConfirmModal.tsx +++ b/src/components/DeleteConfirm/DeleteConfirmModal.tsx @@ -1,66 +1,79 @@ import { - Dialog, - DialogTitle, - DialogContent, - DialogActions, - Button, - IconButton, - Typography, - } from "@mui/material"; - import CloseIcon from "@mui/icons-material/Close"; - - interface DeleteConfirmModalProps { - open: boolean; - onConfirm: () => void; - onCancel: () => void; - } - - const DeleteConfirmModal = ({ open, onConfirm, onCancel }: DeleteConfirmModalProps) => { - return ( - void; + onCancel: () => void; + category: string; +} + +const DeleteConfirmModal = ({ + open, + onConfirm, + onCancel, + category = "", +}: DeleteConfirmModalProps) => { + return ( + + - - 경고 - - - - - - - - 정말 삭제하시겠습니까? - - - - - - - - - ); - }; - - export default DeleteConfirmModal; - \ No newline at end of file + + {category} 삭제 + + + + + + + + + 이 {category}을 정말 삭제할까요? + + + + + + + + + ); +}; + +export default DeleteConfirmModal; diff --git a/src/components/Toast/Toast.tsx b/src/components/Toast/Toast.tsx new file mode 100644 index 0000000..2a5fc0e --- /dev/null +++ b/src/components/Toast/Toast.tsx @@ -0,0 +1,37 @@ +import { toast, ToastContainer } from "react-toastify"; +import "react-toastify/dist/ReactToastify.css"; + +interface ToastProps { + message: string; + type: "success" | "error"; +} + +export const showToast = ({ message, type }: ToastProps) => { + toast[type](message, { + autoClose: 2000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + style: { + marginTop: "6px", + top: "40px", + }, + }); +}; + +export const ToastProvider = () => { + return ( + + ); +}; diff --git a/src/pages/PrivatePropertyListPage/AgentPropertyDetailPage/AgentPropertyDetailPage.tsx b/src/pages/PrivatePropertyListPage/AgentPropertyDetailPage/AgentPropertyDetailPage.tsx index f0831f4..f79c9f2 100644 --- a/src/pages/PrivatePropertyListPage/AgentPropertyDetailPage/AgentPropertyDetailPage.tsx +++ b/src/pages/PrivatePropertyListPage/AgentPropertyDetailPage/AgentPropertyDetailPage.tsx @@ -318,7 +318,7 @@ const AgentPropertyDetailPage = () => { apiClient .delete(`/properties/${propertyUid}`) .then(() => { - toast.success("매물 삭제 성공"); + toast.success("매물을 삭제했습니다."); navigate("/properties/private"); }) .catch((err) => { @@ -414,6 +414,7 @@ const AgentPropertyDetailPage = () => { open={deleteModalOpen} onConfirm={confirmDelete} onCancel={() => setDeleteModalOpen(false)} + category="매물" /> @@ -636,85 +637,83 @@ const AgentPropertyDetailPage = () => { 계약 정보 - - {/* 계약 시작일 */} - - - 계약 시작일 - - {contractInfo?.contractStartDate - ? dayjs(contractInfo.contractStartDate).format( - "YYYY.MM.DD" - ) - : "-"} - - - + {!contractInfo || contractInfo?.contractUid === null ? ( + + 관련된 계약 정보가 없습니다. + + ) : ( + + {/* 계약 시작일 */} + + + 계약 시작일 + + {contractInfo.contractStartDate + ? dayjs(contractInfo.contractStartDate).format( + "YYYY.MM.DD" + ) + : "-"} + + + - {/* 계약 종료일 */} - - - 계약 종료일 - - {contractInfo?.contractEndDate - ? dayjs(contractInfo.contractEndDate).format("YYYY.MM.DD") - : "-"} - - - + {/* 계약 종료일 */} + + + 계약 종료일 + + {contractInfo.contractEndDate + ? dayjs(contractInfo.contractEndDate).format( + "YYYY.MM.DD" + ) + : "-"} + + + - {/* 계약일 */} - - - 계약일 - - {contractInfo?.contractDate - ? dayjs(contractInfo.contractDate).format("YYYY.MM.DD") - : "-"} - - - + {/* 계약일 */} + + + 계약일 + + {contractInfo.contractDate + ? dayjs(contractInfo.contractDate).format("YYYY.MM.DD") + : "-"} + + + - {/* 임대인/매도인 */} - - - 임대인/매도인 - - {contractInfo - ? getCustomerNames( - contractInfo.customers, - "LESSOR_OR_SELLER" - ) - : "-"} - - - + {/* 임대인/매도인 */} + + + 임대인/매도인 + + {getCustomerNames( + contractInfo.customers, + "LESSOR_OR_SELLER" + )} + + + - {/* 임차인/매수인 */} - - - 임차인/매수인 - - {contractInfo - ? getCustomerNames( - contractInfo.customers, - "LESSEE_OR_BUYER" - ) - : "-"} - - + {/* 임차인/매수인 */} + + + 임차인/매수인 + + {getCustomerNames( + contractInfo.customers, + "LESSEE_OR_BUYER" + )} + + + - + )} - + {property.details && ( { display: "flex", flexDirection: "column", height: "100%", + minHeight: "230px", }} > @@ -740,6 +740,7 @@ const AgentPropertyDetailPage = () => { display: "flex", flexDirection: "column", height: "100%", + minHeight: "230px", }} > setTab(v)} sx={{ mb: 2 }}> @@ -773,7 +774,7 @@ const AgentPropertyDetailPage = () => { {contractHistories.length === 0 ? ( - 히스토리 없음 + 매물과 관련된 계약 히스토리가 없습니다. ) : ( contractHistories.map((history, idx) => { @@ -839,7 +840,7 @@ const AgentPropertyDetailPage = () => { {counselHistories.length === 0 ? ( - 히스토리 없음 + 매물과 관련된 상담 히스토리가 없습니다. ) : ( counselHistories.map((counsel) => ( diff --git a/src/pages/PrivatePropertyListPage/PrivatePropertyListPage.tsx b/src/pages/PrivatePropertyListPage/PrivatePropertyListPage.tsx index 59496df..46665b6 100644 --- a/src/pages/PrivatePropertyListPage/PrivatePropertyListPage.tsx +++ b/src/pages/PrivatePropertyListPage/PrivatePropertyListPage.tsx @@ -82,7 +82,7 @@ function PrivatePropertyListPage() { const { user } = useUserStore(); const navigate = useNavigate(); const [page, setPage] = useState(0); - const [rowsPerPage, setRowsPerPage] = useState(10); + const [rowsPerPage, setRowsPerPage] = useState(50); const [totalElements, setTotalElements] = useState(0); const [region, setRegion] = useState({ @@ -94,10 +94,13 @@ function PrivatePropertyListPage() { selectedDong: "", }); const updateLegalDistrictCode = (newState: typeof region) => { - const code = newState.selectedDong || newState.selectedSigungu || newState.selectedSido; - + const code = + newState.selectedDong || + newState.selectedSigungu || + newState.selectedSido; + let prefix = ""; - + if (newState.selectedDong) { prefix = String(code).slice(0, 8); // 동: 앞 8자리 (정확한 법정동코드) } else if (newState.selectedSigungu) { @@ -105,7 +108,7 @@ function PrivatePropertyListPage() { } else if (newState.selectedSido) { prefix = String(code).slice(0, 2); // 시/도: 앞 2자리 } - + setFilter((prev) => ({ ...prev, legalDistrictCode: prefix || undefined, @@ -253,7 +256,7 @@ function PrivatePropertyListPage() { if (loading) { return ( - + @@ -263,7 +266,7 @@ function PrivatePropertyListPage() { return ( - + {/* 상단 필터 바 */} @@ -324,7 +327,11 @@ function PrivatePropertyListPage() { {/* 카테고리/판매유형/상세필터/등록 버튼 */} - + - setFilterModalOpen(true)} - sx={{ - // 기존 스타일 유지 - border: "1px solid #164F9E", - color: "#164F9E", - minWidth: "40px", - padding: "5px", - borderRadius: "14px", - "&:hover": { - backgroundColor: "#F5F5F5", - border: "1px solid #164F9E", - }, - }} - > - - - + setFilterModalOpen(true)} + sx={{ + // 기존 스타일 유지 + border: "1px solid #164F9E", + color: "#164F9E", + minWidth: "40px", + padding: "5px", + borderRadius: "20px", + "&:hover": { + backgroundColor: "#F5F5F5", + border: "1px solid #164F9E", + }, + }} + > + + + diff --git a/src/pages/PrivatePropertyListPage/PropertyAddButtonList/BulkUploadModal.tsx b/src/pages/PrivatePropertyListPage/PropertyAddButtonList/BulkUploadModal.tsx index 09a0c9a..3002ef4 100644 --- a/src/pages/PrivatePropertyListPage/PropertyAddButtonList/BulkUploadModal.tsx +++ b/src/pages/PrivatePropertyListPage/PropertyAddButtonList/BulkUploadModal.tsx @@ -64,7 +64,7 @@ function BulkUploadModal({ open, handleClose, fetchPropertyData }: Props) { try { setIsLoading(true); await apiClient.post("/properties/bulk", formData); - toast.success("매물 정보 업로드에 성공했습니다"); + toast.success("매물 정보를 업로드 했습니다."); handleClose(); fetchPropertyData(); } catch (error) { @@ -131,7 +131,11 @@ function BulkUploadModal({ open, handleClose, fetchPropertyData }: Props) { )} - - - - - {/* 두 번째 줄: 검색 결과 건수(오른쪽 끝) */} - 공개 매물 검색 결과 : {filteredPropertyList.length}건 + + + + + ), + sx: { + borderRadius: "20px", + }, + }} + sx={{ + maxWidth: "920px", + "& .MuiOutlinedInput-root": { + borderRadius: "20px", + }, + }} + /> + + + + + {/* 단위/주소 스위치: 두 컨테이너 사이로 이동 */} @@ -591,7 +608,7 @@ function PublicPropertyListPage() { > setUseMetric((prev) => !prev)} color="primary" @@ -599,11 +616,19 @@ function PublicPropertyListPage() { /> } label={useMetric ? "제곱미터(m²)" : "평(py)"} - sx={{ "& .MuiFormControlLabel-label": { fontSize: "13px" } }} + sx={{ + mb: "12px", + ml: "0px", + "& .MuiFormControlLabel-label": { + fontSize: "14px", + marginLeft: "4px", + fontWeight: 500, + }, + }} /> setUseRoadAddress((prev) => !prev)} color="primary" @@ -611,7 +636,15 @@ function PublicPropertyListPage() { /> } label={useRoadAddress ? "도로명 주소" : "지번 주소"} - sx={{ "& .MuiFormControlLabel-label": { fontSize: "13px" } }} + sx={{ + mb: "12px", + ml: "0px", + "& .MuiFormControlLabel-label": { + fontSize: "14px", + marginLeft: "4px", + fontWeight: 500, + }, + }} /> {/* 매물 리스트 컨테이너 */} From 301ea4bd7a0ac169a939db980309f8959d617419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B1=84=EC=A7=80=EC=9B=90?= Date: Thu, 15 May 2025 09:11:04 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=EC=A0=84=EB=B0=98=EC=A0=81=EC=9D=B8=20?= =?UTF-8?q?=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/apiClient.ts | 18 +- .../ContractDetailContent.tsx | 81 +++- .../ContractDetailPage/ContractDetailPage.tsx | 20 +- .../ContractAddModal/ContractAddModal.tsx | 139 +++---- .../ContractEditModal/ContractEditModal.tsx | 154 ++++--- .../ContractListPage/ContractListPage.tsx | 82 +++- .../ContractTable/ContractTable.tsx | 13 +- .../CounselDetailPage/CounselDetailPage.tsx | 289 +++++++++---- .../styles/CounselDetailPage.module.css | 13 +- src/pages/CounselListPage/CounselListPage.tsx | 125 +++++- .../CustomerDetailPage/CustomerDetailPage.tsx | 45 ++- .../CustomerAddButtonList.tsx | 3 +- .../CustomerAddModal/CustomerAddModal.tsx | 380 ++++++++++++------ .../CustomerBulkUploadModal.tsx | 12 +- .../CustomerListPage/CustomerListPage.tsx | 35 +- .../CustomerTable/CustomerTable.tsx | 18 +- .../CustomerTable/DeleteConfirmModal.tsx | 44 -- .../DashboardPage/CompletedContractsModal.tsx | 21 +- src/pages/DashboardPage/DashboardPage.tsx | 20 - .../DashboardPage/OngoingContractsModal.tsx | 23 +- .../DashboardPage/RecentContractsModal.tsx | 303 +++++++------- .../DashboardPage/RecentCustomersModal.tsx | 13 +- .../MessageTemplatePage.tsx | 37 +- .../AgentPropertyDetailPage.tsx | 49 ++- .../ContractDetailContent.tsx | 14 + .../PropertyAddButtonList/BulkUploadModal.tsx | 11 +- .../PropertyAddModal/PropertyAddModal.tsx | 1 - .../PropertyEditModal/PropertyEditModal.tsx | 256 +++++++----- .../PublicPropertyListPage.tsx | 1 - .../AddScheduleModal/AddScheduleModal.tsx | 81 ++-- src/pages/SchedulePage/SchedulePage.tsx | 102 ++--- src/pages/SignInPage/SignInPage.tsx | 14 +- src/pages/SignUpPage/SignUpPage.tsx | 53 ++- .../SubmitSurveyPage/SubmitSurveyPage.tsx | 21 +- 34 files changed, 1550 insertions(+), 941 deletions(-) delete mode 100644 src/pages/CustomerListPage/CustomerTable/DeleteConfirmModal.tsx create mode 100644 src/pages/PrivatePropertyListPage/ContractDetailPage/ContractDetailContent.tsx diff --git a/src/apis/apiClient.ts b/src/apis/apiClient.ts index 658909e..4932675 100644 --- a/src/apis/apiClient.ts +++ b/src/apis/apiClient.ts @@ -45,17 +45,21 @@ apiClient.interceptors.response.use( const originalRequest = error.config; const isLoginRequest = originalRequest?.url?.includes("/users/login"); - if (error.response.status === 401 && !originalRequest.__isRetryRequest && !isLoginRequest) { + if ( + error.response.status === 401 && + !originalRequest.__isRetryRequest && + !isLoginRequest + ) { try { originalRequest.__isRetryRequest = true; const deviceId = localStorage.getItem("deviceId"); -const refreshResponse = await reissueTokenClient.get("/users/reissue", { - headers: { - "X-Device-Id": deviceId ?? "", - }, -});; + const refreshResponse = await reissueTokenClient.get("/users/reissue", { + headers: { + "X-Device-Id": deviceId ?? "", + }, + }); const newAccessToken = refreshResponse?.data?.data?.accessToken ?? null; if (!newAccessToken) { @@ -71,7 +75,7 @@ const refreshResponse = await reissueTokenClient.get("/users/reissue", { return axios(originalRequest); } catch (refreshError) { sessionStorage.removeItem("_ZA"); - toast.error("인증이 만료되었습니다. 다시 로그인하세요."); + toast.error("인증이 만료되었습니다. 다시 로그인하세요."); window.location.replace("/sign-in"); return Promise.reject(refreshError); } diff --git a/src/pages/ContractDetailPage/ContractDetailContent.tsx b/src/pages/ContractDetailPage/ContractDetailContent.tsx index 0370470..f1ea774 100644 --- a/src/pages/ContractDetailPage/ContractDetailContent.tsx +++ b/src/pages/ContractDetailPage/ContractDetailContent.tsx @@ -86,7 +86,7 @@ const ContractDetailContent = ({ contract, histories }: Props) => { <> {/* 계약 기본정보, 당사자 정보 */} - + 계약 기본 정보 @@ -143,8 +143,14 @@ const ContractDetailContent = ({ contract, histories }: Props) => { ); })()} /> - - + + {contract.status === "CANCELLED" && ( { - + 계약 당사자 정보 - + { {/* 첨부문서, 히스토리 */} - + 첨부 문서 @@ -245,12 +254,21 @@ const ContractDetailContent = ({ contract, histories }: Props) => { )) ) : ( - 첨부 문서 없음 + + 첨부 문서 없음 + )} - + 상태 변경 이력 @@ -268,10 +286,40 @@ const ContractDetailContent = ({ contract, histories }: Props) => { {histories.map((h, idx) => ( - {statusKoreanMap[h.prevStatus] ?? h.prevStatus} + item.value === h.prevStatus + )?.color, + borderColor: CONTRACT_STATUS_TYPES.find( + (item) => item.value === h.prevStatus + )?.color, + fontWeight: 500, + fontSize: 13, + height: 28, + }} + /> - {statusKoreanMap[h.currentStatus] ?? h.currentStatus} + item.value === h.currentStatus + )?.color, + borderColor: CONTRACT_STATUS_TYPES.find( + (item) => item.value === h.currentStatus + )?.color, + fontWeight: 500, + fontSize: 13, + height: 28, + }} + /> {h.changedAt} @@ -279,9 +327,16 @@ const ContractDetailContent = ({ contract, histories }: Props) => { ) : ( - - 히스토리 없음 - + + 히스토리 없음 + )} diff --git a/src/pages/ContractDetailPage/ContractDetailPage.tsx b/src/pages/ContractDetailPage/ContractDetailPage.tsx index 1b7f889..188667d 100644 --- a/src/pages/ContractDetailPage/ContractDetailPage.tsx +++ b/src/pages/ContractDetailPage/ContractDetailPage.tsx @@ -10,7 +10,8 @@ import { toast } from "react-toastify"; import DeleteConfirmModal from "@components/DeleteConfirm/DeleteConfirmModal"; import useUserStore from "@stores/useUserStore"; import EditIcon from "@mui/icons-material/Edit"; -import DeleteIcon from "@mui/icons-material/DeleteOutline"; +import DeleteIcon from "@mui/icons-material/Delete"; +import { showToast } from "@components/Toast/Toast"; const ContractDetailPage = () => { const { contractUid } = useParams<{ contractUid: string }>(); @@ -33,12 +34,18 @@ const ContractDetailPage = () => { apiClient .delete(`/contracts/${contractUid}`) .then(() => { - toast.success("계약 삭제 성공"); + showToast({ + message: "계약을 삭제했습니다.", + type: "success", + }); navigate("/contracts"); }) .catch((err) => { console.error("계약 삭제 실패", err); - toast.error("계약 삭제 중 오류가 발생했습니다."); + showToast({ + message: "계약 삭제 중 오류가 발생했습니다.", + type: "error", + }); }) .finally(() => { setDeleteModalOpen(false); @@ -77,9 +84,7 @@ const ContractDetailPage = () => { return (
-
- -
+
{editModalOpen && ( @@ -104,10 +109,11 @@ const ContractDetailPage = () => { open={deleteModalOpen} onConfirm={confirmDelete} onCancel={() => setDeleteModalOpen(false)} + category="계약" />
- +
@@ -523,12 +636,16 @@ function CounselDetailPage() {
주소 - {data.property.address} + + {data.property.address} +
매물 유형 - {PROPERTY_CATEGORIES[data.property.type as PropertyCategory] || data.property.type} + {PROPERTY_CATEGORIES[ + data.property.type as PropertyCategory + ] || data.property.type}
{data.property.price !== null && data.property.price !== 0 && ( @@ -539,22 +656,24 @@ function CounselDetailPage() {
)} - {data.property.deposit !== null && data.property.deposit !== 0 && ( -
- 보증금 - - {data.property.deposit.toLocaleString()}원 - -
- )} - {data.property.monthlyRent !== null && data.property.monthlyRent !== 0 && ( -
- 월세 - - {data.property.monthlyRent.toLocaleString()}원 - -
- )} + {data.property.deposit !== null && + data.property.deposit !== 0 && ( +
+ 보증금 + + {data.property.deposit.toLocaleString()}원 + +
+ )} + {data.property.monthlyRent !== null && + data.property.monthlyRent !== 0 && ( +
+ 월세 + + {data.property.monthlyRent.toLocaleString()}원 + +
+ )}
전용면적 @@ -569,11 +688,17 @@ function CounselDetailPage() {
층수 - {data.property.floor}층 + + {data.property.floor}층 +
준공년도 - {data.property.constructionYear}년 + + {data.property.constructionYear + ? `${data.property.constructionYear}년` + : "-"} +
특징 @@ -582,7 +707,9 @@ function CounselDetailPage() { 엘리베이터 )} {data.property.parkingCapacity > 0 && ( - 주차 {data.property.parkingCapacity}대 + + 주차 {data.property.parkingCapacity}대 + )} {data.property.hasPet && ( 반려동물 @@ -592,7 +719,9 @@ function CounselDetailPage() { {data.property.description && (
상세 설명 - {data.property.description} + + {data.property.description} +
)}
@@ -675,9 +804,7 @@ function CounselDetailPage() { 상담 삭제 - - 정말로 상담을 삭제하시겠습니까? - + 정말로 상담을 삭제하시겠습니까? diff --git a/src/pages/CounselDetailPage/styles/CounselDetailPage.module.css b/src/pages/CounselDetailPage/styles/CounselDetailPage.module.css index c61ba05..fa94396 100644 --- a/src/pages/CounselDetailPage/styles/CounselDetailPage.module.css +++ b/src/pages/CounselDetailPage/styles/CounselDetailPage.module.css @@ -38,10 +38,13 @@ } .cardTitle { - font-size: 18px; - font-weight: 600; - margin-bottom: 16px; - color: #333; + margin: 0; + font-size: 1.2rem !important; + font-weight: 700 !important; + padding-bottom: 8px !important; + line-height: 1.6; + letter-spacing: 0.0075em; + margin-bottom: 0.35em !important; } .infoGrid { @@ -179,4 +182,4 @@ border-radius: 4px; cursor: pointer; font-weight: 500; -} \ No newline at end of file +} diff --git a/src/pages/CounselListPage/CounselListPage.tsx b/src/pages/CounselListPage/CounselListPage.tsx index d7825c6..de202f0 100644 --- a/src/pages/CounselListPage/CounselListPage.tsx +++ b/src/pages/CounselListPage/CounselListPage.tsx @@ -12,6 +12,9 @@ import { CircularProgress, Button, } from "@mui/material"; +import { DatePicker } from "@mui/x-date-pickers/DatePicker"; +import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; +import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; import PageHeader from "@components/PageHeader/PageHeader"; import useUserStore from "@stores/useUserStore"; import { useEffect, useState } from "react"; @@ -59,7 +62,9 @@ function CounselListPage() { const [startDate, setStartDate] = useState(null); const [endDate, setEndDate] = useState(null); const [selectedType, setSelectedType] = useState(null); - const [selectedCompleted, setSelectedCompleted] = useState(null); + const [selectedCompleted, setSelectedCompleted] = useState( + null + ); const COUNSEL_TYPES = [ { value: "PURCHASE", label: "매수" }, @@ -159,19 +164,92 @@ function CounselListPage() {
- setStartDate(e.target.value)} - /> - ~ - setEndDate(e.target.value)} - /> + + + + 상담일 + + + { + setStartDate( + newValue ? newValue.format("YYYY-MM-DD") : null + ); + }} + format="YYYY/MM/DD" + slotProps={{ + textField: { + size: "small", + sx: { + backgroundColor: "white", + width: "170px", + "& .MuiOutlinedInput-root": { + "& fieldset": { + borderColor: "#E0E0E0", + borderRadius: "20px", + }, + "&:hover fieldset": { + borderColor: "#164F9E", + }, + "&.Mui-focused fieldset": { + borderColor: "#164F9E", + }, + }, + }, + }, + }} + /> + + ~ + + { + setEndDate( + newValue ? newValue.format("YYYY-MM-DD") : null + ); + }} + format="YYYY/MM/DD" + slotProps={{ + textField: { + size: "small", + sx: { + backgroundColor: "white", + width: "170px", + "& .MuiOutlinedInput-root": { + "& fieldset": { + borderColor: "#E0E0E0", + borderRadius: "20px", + }, + "&:hover fieldset": { + borderColor: "#164F9E", + }, + "&.Mui-focused fieldset": { + borderColor: "#164F9E", + }, + }, + }, + }, + }} + /> + + +
@@ -226,7 +304,8 @@ function CounselListPage() { onClick={() => setIsModalOpen(true)} sx={{ backgroundColor: "#164F9E", - "&:hover": { backgroundColor: "#0D3B7A" }, + boxShadow: "none", + "&:hover": { backgroundColor: "#0D3B7A", boxShadow: "none" }, height: "36px", fontSize: "13px", padding: "0 16px", @@ -291,15 +370,23 @@ function CounselListPage() { - {(!counsel.dueDate || counsel.completed) ? "의뢰 마감" : "의뢰 진행중"} + {!counsel.dueDate || counsel.completed + ? "의뢰 마감" + : "의뢰 진행중"} @@ -333,4 +420,4 @@ function CounselListPage() { ); } -export default CounselListPage; \ No newline at end of file +export default CounselListPage; diff --git a/src/pages/CustomerDetailPage/CustomerDetailPage.tsx b/src/pages/CustomerDetailPage/CustomerDetailPage.tsx index ef2ffda..994e904 100644 --- a/src/pages/CustomerDetailPage/CustomerDetailPage.tsx +++ b/src/pages/CustomerDetailPage/CustomerDetailPage.tsx @@ -24,6 +24,7 @@ import { toast } from "react-toastify"; import DeleteConfirmModal from "@components/DeleteConfirm/DeleteConfirmModal"; import RegionSelect from "@components/RegionSelect/RegionSelect"; import { formatPhoneNumber } from "@utils/numberUtil"; +import { showToast } from "@components/Toast/Toast"; interface CustomerData { uid: number; @@ -177,15 +178,24 @@ function CustomerDetailPage() { try { if (!editedCustomer.name) { - toast.error("이름을 입력해주세요."); + showToast({ + message: "이름을 입력해주세요.", + type: "error", + }); return; } if (!editedCustomer.phoneNo) { - toast.error("전화번호를 입력해주세요."); + showToast({ + message: "전화번호를 입력해주세요.", + type: "error", + }); return; } if (editedCustomer.birthDay && !/^\d{8}$/.test(editedCustomer.birthDay)) { - toast.error("생년월일을 올바르게 입력해주세요. ex)19910501"); + showToast({ + message: "생년월일을 올바르게 입력해주세요. ex)19910501", + type: "error", + }); return; } @@ -215,13 +225,20 @@ function CustomerDetailPage() { ); if (response.status === 200) { - toast.success("고객 정보를 수정했습니다."); + showToast({ + message: "고객 정보를 수정했습니다.", + type: "success", + }); setIsEditing(false); fetchCustomerData(); } } catch (error: any) { console.error("Failed to update customer:", error); - toast.error(error.response?.data?.message || "고객 정보 수정에 실패했습니다."); + showToast({ + message: + error.response?.data?.message || "고객 정보 수정에 실패했습니다.", + type: "error", + }); } }; @@ -237,7 +254,10 @@ function CustomerDetailPage() { try { const response = await apiClient.delete(`/customers/${customerId}`); if (response.status === 200) { - toast.success("고객이 삭제되었습니다."); + showToast({ + message: "고객을 삭제했습니다.", + type: "success", + }); navigate("/customers"); } } catch (error) { @@ -328,18 +348,16 @@ function CustomerDetailPage() { }} >