Skip to content
Merged
5 changes: 5 additions & 0 deletions src/apis/slopeManage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
23 changes: 14 additions & 9 deletions src/apis/slopeMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export interface Slope {
};
slopeInspectionHistory: {
historyNumber: string;
inspectionDate: string;
inspectionDate: Date;
};
priority: {
usage: string; // 비탈면용도
Expand Down Expand Up @@ -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;
},
Expand Down
71 changes: 59 additions & 12 deletions src/pages/LoginPage/components/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,25 @@ 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<LoginFormType>({
phone: '',
password: '',
rememberPhone: false,
});
const [showPw, setShowPw] = useState(false);
const [isPhoneValid, setIsPhoneValid] = useState<boolean>(true);

//로그인이 되어있으면 이동
Expand Down Expand Up @@ -100,6 +103,10 @@ const Login = () => {
onSubmit();
}
};

const changePasswdMode = () => {
setShowPw(!showPw);
};
return (
<>
<InputWrapper>
Expand All @@ -121,15 +128,32 @@ const Login = () => {
{!isPhoneValid && (
<ErrorText>"-"를 제외한 숫자만 입력해 주세요</ErrorText>
)}
<Input
name="password"
value={loginForm.password}
placeholder="비밀번호"
type="password"
autoComplete="current-password"
onChange={handleChange}
onKeyDown={handleKeyDown}
/>
{!showPw ? (
<PWrapper>
<Input
name="password"
value={loginForm.password}
placeholder="비밀번호"
type="password"
autoComplete="current-password"
onChange={handleChange}
onKeyDown={handleKeyDown}
/>
<ShowOnIcon onClick={changePasswdMode} />
</PWrapper>
) : (
<PWrapper>
<Input
name="password"
value={loginForm.password}
placeholder="비밀번호"
autoComplete="current-password"
onChange={handleChange}
onKeyDown={handleKeyDown}
/>
<ShowOffIcon onClick={changePasswdMode} />
</PWrapper>
)}

<CheckboxWrapper>
<HiddenCheckbox
Expand All @@ -140,7 +164,8 @@ const Login = () => {
rememberPhone: e.target.checked,
}))
}
/>
></HiddenCheckbox>

<Checkbox />
<CheckboxLabel>아이디 저장</CheckboxLabel>
</CheckboxWrapper>
Expand All @@ -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;
`;
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ const createDefaultSlope = (): Slope => {
},
slopeInspectionHistory: {
historyNumber: '',
inspectionDate: '',
inspectionDate: today,
},
priority: {
usage: '',
Expand Down
23 changes: 14 additions & 9 deletions src/pages/ManagePage/StepSlope/components/ImgsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -364,15 +364,20 @@ const ImgsModal = ({ isOpen, onClose, selectedRow }: ImgsModalProps) => {
<AddressWrapper>
<InfoLabel>주소:</InfoLabel>
<AddressValue>
{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})`
: ''}{' '}
</AddressValue>
</AddressWrapper>
</InfoSection>
Expand Down
43 changes: 42 additions & 1 deletion src/pages/ManagePage/StepSlope/components/SlopeForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const SlopeForm = ({
e.preventDefault();
onSubmit(formData);
};

console.log(formData);
return (
<ModalOverlay $isOpen={isOpen}>
<ModalContent>
Expand Down Expand Up @@ -775,6 +775,47 @@ const SlopeForm = ({
/>
</FormGroup>
</Section>
<Section>
<SectionTitle>급경사지 일제조사</SectionTitle>
<FormGroup>
<Label>SMC번호(급경사지일제조사이력번호)</Label>
<Input
type="text"
value={formData.slopeInspectionHistory.historyNumber || ''}
onChange={(e) =>
setFormData({
...formData,
slopeInspectionHistory: {
...formData.slopeInspectionHistory,
historyNumber: e.target.value,
},
})
}
/>
</FormGroup>
<FormGroup>
<Label>일제조사일자</Label>
<Input
type="date"
value={
formData.slopeInspectionHistory.inspectionDate
? new Date(formData.slopeInspectionHistory.inspectionDate)
.toISOString()
.split('T')[0]
: ''
}
onChange={(e) =>
setFormData({
...formData,
slopeInspectionHistory: {
...formData.slopeInspectionHistory,
inspectionDate: new Date(e.target.value),
},
})
}
/>
</FormGroup>
</Section>
<Section>
<SectionTitle>급경사지 정보</SectionTitle>
<FormGroup>
Expand Down
67 changes: 55 additions & 12 deletions src/pages/ManagePage/StepSlope/pages/SteepSlopeAdd.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,23 +109,66 @@ const SteepSlopeAdd: React.FC = () => {
}
};

// 이미지 백업 복구 실행
// 이미지복구 및 코멘트 복구 병렬 실행
const handleImageRestore = async (): Promise<void> => {
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);
}
Expand Down Expand Up @@ -196,19 +239,19 @@ const SteepSlopeAdd: React.FC = () => {

{/* 이미지 백업 복구 섹션 */}
<BackupSection>
<SectionTitle>이미지 백업 연결</SectionTitle>
<SectionTitle>백업 연결</SectionTitle>
<BackupRestoreCard $isRestoring={isRestoring}>
<BackupIconLarge $isRestoring={isRestoring}>
<RestoreIcon
sx={{ width: '32px', height: '32px', color: 'white' }}
/>
</BackupIconLarge>

<BackupTitle>이미지 복구</BackupTitle>
<BackupTitle>이미지 & 결함사진(댓글) 복구</BackupTitle>
<BackupDescription>
급경사지 데이터가 초기화 된 경우
<br />
기존에 등록했던 이미지를 복원합니다
기존에 등록했던 이미지와 결함사진(댓글)을 복원합니다
</BackupDescription>

<RestoreButton
Expand Down
6 changes: 5 additions & 1 deletion src/pages/MapPage/BottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,11 @@ const BottomSheet = () => {
return (
<div>
<InfoTable selectItem={selectItem} />
<CommentList slopeId={selectItem._id} />
<CommentList
historyNumber={
selectItem.slopeInspectionHistory?.historyNumber
}
/>
</div>
);
} else if (slopeData.length === 0) {
Expand Down
4 changes: 4 additions & 0 deletions src/pages/MapPage/components/InfoTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ const InfoTable = ({ selectItem }: InfotableProps) => {
</AddressValue>
</ValueColumn>
</AddressWrapper>
<InfoRow>
<Label>붕괴위험지구</Label>
<Value>{selectItem?.collapseRisk?.designated ? 'Y' : 'N'}</Value>
</InfoRow>
<Line />
<InfoRow>
<Label>최고수직고</Label>
Expand Down
Loading
Loading