diff --git a/src/apis/slopeManage.tsx b/src/apis/slopeManage.tsx index 6ca9da7..36a475a 100644 --- a/src/apis/slopeManage.tsx +++ b/src/apis/slopeManage.tsx @@ -104,6 +104,11 @@ export const slopeManageAPI = { console.log(response); return response.data; }, + restoreComment: async () => { + const response = await api.post('/slopes/comments/restore'); + console.log(response); + return response.data; + }, }; export interface UpdateAllImg { formData: FormData; diff --git a/src/apis/slopeMap.tsx b/src/apis/slopeMap.tsx index 488fa66..aff9b70 100644 --- a/src/apis/slopeMap.tsx +++ b/src/apis/slopeMap.tsx @@ -68,7 +68,7 @@ export interface Slope { }; slopeInspectionHistory: { historyNumber: string; - inspectionDate: string; + inspectionDate: Date; }; priority: { usage: string; // 비탈면용도 @@ -119,18 +119,23 @@ export const slopeMapAPI = { }; export const slopeCommentAPI = { - getComment: async (slopeId: string) => { - const response = await api.get(`slopes/${slopeId}/comments`); + getComment: async (historyNumber: string) => { + const response = await api.get(`slopes/${historyNumber}/comments`); // console.log('급경사지 코멘트 조회 완료', response.data); return response.data.data; }, createComment: async (formData: FormData) => { - const slopeId = formData.get('slopeId'); - const response = await api.post(`slopes/${slopeId}/comments`, formData, { - headers: { - 'Content-Type': 'multipart/form-data', - }, - }); + const historyNumber = formData.get('historyNumber'); + console.log(historyNumber, formData); + const response = await api.post( + `slopes/${historyNumber}/comments`, + formData, + { + headers: { + 'Content-Type': 'multipart/form-data', + }, + } + ); // console.log('급경사지 코멘트 생성 완료', response.data); return response.data; }, diff --git a/src/pages/LoginPage/components/Login.tsx b/src/pages/LoginPage/components/Login.tsx index 79e2c03..48f6873 100644 --- a/src/pages/LoginPage/components/Login.tsx +++ b/src/pages/LoginPage/components/Login.tsx @@ -5,15 +5,17 @@ import { CheckboxLabel, CheckboxWrapper, ErrorText, - HiddenCheckbox, Input, + HiddenCheckbox, InputWrapper, LoginButton, } from './commonStyle'; import { useMutation } from '@tanstack/react-query'; import { authAPI, LoginFormType } from '../../../apis/Auth'; import { useNotificationStore } from '../../../hooks/notificationStore'; - +import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'; +import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined'; +import styled from 'styled-components'; const Login = () => { const nav = useNavigate(); const [loginForm, setLoginForm] = useState({ @@ -21,6 +23,7 @@ const Login = () => { password: '', rememberPhone: false, }); + const [showPw, setShowPw] = useState(false); const [isPhoneValid, setIsPhoneValid] = useState(true); //로그인이 되어있으면 이동 @@ -100,6 +103,10 @@ const Login = () => { onSubmit(); } }; + + const changePasswdMode = () => { + setShowPw(!showPw); + }; return ( <> @@ -121,15 +128,32 @@ const Login = () => { {!isPhoneValid && ( "-"를 제외한 숫자만 입력해 주세요 )} - + {!showPw ? ( + + + + + ) : ( + + + + + )} { rememberPhone: e.target.checked, })) } - /> + > + 아이디 저장 @@ -153,3 +178,25 @@ const Login = () => { }; export default Login; + +const PWrapper = styled.div` + display: flex; + width: 100%; + position: relative; +`; +const ShowOnIcon = styled(VisibilityOutlinedIcon)` + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + cursor: pointer; + color: #666; +`; +const ShowOffIcon = styled(VisibilityOffOutlinedIcon)` + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + cursor: pointer; + color: #666; +`; diff --git a/src/pages/ManagePage/StepSlope/components/AddSlopeContainer.tsx b/src/pages/ManagePage/StepSlope/components/AddSlopeContainer.tsx index 0c1f863..0594492 100644 --- a/src/pages/ManagePage/StepSlope/components/AddSlopeContainer.tsx +++ b/src/pages/ManagePage/StepSlope/components/AddSlopeContainer.tsx @@ -97,7 +97,7 @@ const createDefaultSlope = (): Slope => { }, slopeInspectionHistory: { historyNumber: '', - inspectionDate: '', + inspectionDate: today, }, priority: { usage: '', diff --git a/src/pages/ManagePage/StepSlope/components/ImgsModal.tsx b/src/pages/ManagePage/StepSlope/components/ImgsModal.tsx index 74125ff..dc39d09 100644 --- a/src/pages/ManagePage/StepSlope/components/ImgsModal.tsx +++ b/src/pages/ManagePage/StepSlope/components/ImgsModal.tsx @@ -364,15 +364,20 @@ const ImgsModal = ({ isOpen, onClose, selectedRow }: ImgsModalProps) => { 주소: - {selectedData?.location?.province || ''} - {selectedData?.location?.city || ''} - {selectedData?.location?.district || ''} - {selectedData?.location?.address || ''} - {selectedData?.location?.mainLotNumber - ? selectedData?.location?.subLotNumber - ? ` ${selectedData?.location?.mainLotNumber}-${selectedData?.location?.subLotNumber}` - : ` ${selectedData?.location?.mainLotNumber}` - : ''} + {selectedData?.location?.province || ''}{' '} + {selectedData?.location?.city || ''}{' '} + {selectedData?.location?.district || ''}{' '} + {selectedData?.location?.address || ''}{' '} + {selectedData?.location?.mountainAddress === 'Y' + ? '산' + : ''}{' '} + {selectedData?.location?.mainLotNumber || ''} + {selectedData?.location?.subLotNumber + ? `-${selectedData.location.subLotNumber}` + : ''}{' '} + {selectedData?.location?.roadAddress + ? `(${selectedData?.location?.roadAddress})` + : ''}{' '} diff --git a/src/pages/ManagePage/StepSlope/components/SlopeForm.tsx b/src/pages/ManagePage/StepSlope/components/SlopeForm.tsx index ff9408d..76c34fb 100644 --- a/src/pages/ManagePage/StepSlope/components/SlopeForm.tsx +++ b/src/pages/ManagePage/StepSlope/components/SlopeForm.tsx @@ -21,7 +21,7 @@ const SlopeForm = ({ e.preventDefault(); onSubmit(formData); }; - + console.log(formData); return ( @@ -775,6 +775,47 @@ const SlopeForm = ({ /> +
+ 급경사지 일제조사 + + + + setFormData({ + ...formData, + slopeInspectionHistory: { + ...formData.slopeInspectionHistory, + historyNumber: e.target.value, + }, + }) + } + /> + + + + + setFormData({ + ...formData, + slopeInspectionHistory: { + ...formData.slopeInspectionHistory, + inspectionDate: new Date(e.target.value), + }, + }) + } + /> + +
급경사지 정보 diff --git a/src/pages/ManagePage/StepSlope/pages/SteepSlopeAdd.tsx b/src/pages/ManagePage/StepSlope/pages/SteepSlopeAdd.tsx index aaf60c4..977211a 100644 --- a/src/pages/ManagePage/StepSlope/pages/SteepSlopeAdd.tsx +++ b/src/pages/ManagePage/StepSlope/pages/SteepSlopeAdd.tsx @@ -109,23 +109,66 @@ const SteepSlopeAdd: React.FC = () => { } }; - // 이미지 백업 복구 실행 + // 이미지복구 및 코멘트 복구 병렬 실행 const handleImageRestore = async (): Promise => { setIsRestoring(true); try { - const response = await slopeManageAPI.restoreImg(); - showNotification( - `이미지 복구 완료\n(복구된 이미지: ${response.summary.restoredImages}개 / 이미지와 연결된 급경사지: ${response.summary.restoredSlopes}개)`, - { + console.log('백업 복구 시작 (병렬 처리)...'); + + // 두 API를 병렬로 실행 + const results = await Promise.allSettled([ + slopeManageAPI.restoreImg(), + slopeManageAPI.restoreComment(), + ]); + + // 결과 처리 + const [imageResult, commentResult] = results; + + // 이미지 복구 결과 처리 + if (imageResult.status === 'fulfilled') { + showNotification( + `이미지 복구 완료\n(복구된 이미지: ${imageResult.value.summary.restoredImages}개 / 이미지와 연결된 급경사지: ${imageResult.value.summary.restoredSlopes}개)`, + { severity: 'success' } + ); + } else { + console.error('이미지 복구 실패:', imageResult.reason); + showNotification('이미지 복구 실패', { severity: 'error' }); + } + + // 코멘트 복구 결과 처리 + if (commentResult.status === 'fulfilled') { + showNotification('코멘트 복구 완료', { severity: 'success' }); + } else { + console.error('코멘트 복구 실패:', commentResult.reason); + showNotification('코멘트 복구 실패', { severity: 'error' }); + } + + // 전체 결과 요약 + const successCount = results.filter( + (r) => r.status === 'fulfilled' + ).length; + const totalCount = results.length; + + if (successCount === totalCount) { + showNotification('모든 백업 복구가 완료되었습니다!', { severity: 'success', - } - ); + }); + } else if (successCount > 0) { + showNotification(`일부 복구 완료 (${successCount}/${totalCount})`, { + severity: 'warning', + }); + } else { + showNotification('모든 복구 작업이 실패했습니다.', { + severity: 'error', + }); + } } catch (error) { - showNotification('이미지 복구 실패', { + // allSettled는 절대 reject되지 않음 + console.error('예상치 못한 오류:', error); + showNotification('예상치 못한 오류가 발생했습니다.', { severity: 'error', }); - console.error('이미지 복구 오류:', error); } finally { setIsRestoring(false); } @@ -196,7 +239,7 @@ const SteepSlopeAdd: React.FC = () => { {/* 이미지 백업 복구 섹션 */} - 이미지 백업 연결 + 백업 연결 { /> - 이미지 복구 + 이미지 & 결함사진(댓글) 복구 급경사지 데이터가 초기화 된 경우
- 기존에 등록했던 이미지를 복원합니다 + 기존에 등록했던 이미지와 결함사진(댓글)을 복원합니다
{ return (
- +
); } else if (slopeData.length === 0) { diff --git a/src/pages/MapPage/components/InfoTable.tsx b/src/pages/MapPage/components/InfoTable.tsx index f6d2477..20debb8 100644 --- a/src/pages/MapPage/components/InfoTable.tsx +++ b/src/pages/MapPage/components/InfoTable.tsx @@ -185,6 +185,10 @@ const InfoTable = ({ selectItem }: InfotableProps) => { + + + {selectItem?.collapseRisk?.designated ? 'Y' : 'N'} + diff --git a/src/pages/MapPage/components/comment/CommentContainer.tsx b/src/pages/MapPage/components/comment/CommentContainer.tsx index 488e70e..480b99c 100644 --- a/src/pages/MapPage/components/comment/CommentContainer.tsx +++ b/src/pages/MapPage/components/comment/CommentContainer.tsx @@ -55,9 +55,7 @@ const CommentContainer = ({ comment, fetchComment }: CommentContainerProps) => { setIsModiOpen={setIsModiOpen} onSubmit={handleUpdate} defaultComment={comment.content} - defaultImages={comment.imageUrls.map( - (url) => `${import.meta.env.VITE_SERVER_ADDRESS}${url}` - )} + defaultImages={comment.imageUrls} commentId={comment._id} /> @@ -115,8 +113,7 @@ const CommentContainer = ({ comment, fetchComment }: CommentContainerProps) => { {comment.content} - {comment.imageUrls.map((tmpurl, index) => { - const url = `${import.meta.env.VITE_SERVER_ADDRESS}${tmpurl}`; + {comment.imageUrls.map((url, index) => { return ( {`Comment ); diff --git a/src/pages/MapPage/components/comment/CommentList.tsx b/src/pages/MapPage/components/comment/CommentList.tsx index 05c9d97..f41c204 100644 --- a/src/pages/MapPage/components/comment/CommentList.tsx +++ b/src/pages/MapPage/components/comment/CommentList.tsx @@ -6,13 +6,13 @@ import { slopeCommentAPI } from '../../../../apis/slopeMap'; import { CommentData, CommentListProps } from '../../interface'; import NoInfo from '../NoInfo'; -const CommentList = ({ slopeId }: CommentListProps) => { +const CommentList = ({ historyNumber }: CommentListProps) => { const [commentData, setCommentData] = useState([]); const [isAddOpen, setIsAddOpen] = useState(false); const handleCreate = async (content: string, images: File[]) => { try { const formData = new FormData(); - formData.append('slopeId', slopeId); + formData.append('historyNumber', historyNumber); formData.append('content', content); // 이미지 파일들 추가 @@ -23,7 +23,7 @@ const CommentList = ({ slopeId }: CommentListProps) => { await slopeCommentAPI.createComment(formData); // 생성 후 코멘트 목록 다시 조회 - const newData = await slopeCommentAPI.getComment(slopeId); + const newData = await slopeCommentAPI.getComment(historyNumber); setCommentData(newData); setIsAddOpen(false); } catch (error) { @@ -33,7 +33,7 @@ const CommentList = ({ slopeId }: CommentListProps) => { //코멘트 조회 const fetchComment = async () => { try { - const data = await slopeCommentAPI.getComment(slopeId); + const data = await slopeCommentAPI.getComment(historyNumber); setCommentData(data); } catch (error) { console.log('코멘트 조회 실패', error); @@ -41,8 +41,7 @@ const CommentList = ({ slopeId }: CommentListProps) => { }; useEffect(() => { fetchComment(); - }, [slopeId]); - console.log(commentData); + }, [historyNumber]); return ( void; } export interface CommentListProps { - slopeId: string; + historyNumber: string; } export interface CommentUpdateModalProps { isModiOpen: boolean;