import { useState } from 'react' import type { Mode, SurveyDetailInfo, SurveyDetailRequest } from '@/types/Survey' import { useSurvey } from '@/hooks/useSurvey' import { ALERT_MESSAGES } from '@/types/Survey' type RadioEtcKeys = | 'structureOrder' | 'houseStructure' | 'rafterMaterial' | 'waterproofMaterial' | 'insulationPresence' | 'rafterDirection' | 'leakTrace' type SelectBoxKeys = | 'installationSystem' | 'constructionYear' | 'roofShape' | 'rafterPitch' | 'rafterSize' | 'openFieldPlateKind' | 'installationAvailability' export const supplementaryFacilities = [ /** 에코큐트 */ { id: 1, name: 'エコキュート' }, /** 에네팜 */ { id: 2, name: 'エネパーム' }, /** 축전지시스템 */ { id: 3, name: '蓄電池システム' }, /** 태양광발전 */ { id: 4, name: '太陽光発電' }, ] export const roofMaterial = [ /** 슬레이트 */ { id: 1, name: 'スレート' }, /** 아스팔트 싱글 */ { id: 2, name: 'アスファルトシングル' }, /** 기와 */ { id: 3, name: '瓦' }, /** 금속지붕 */ { id: 4, name: '金属屋根' }, ] export const selectBoxOptions: Record = { installationSystem: [ { /** 태양광발전 */ id: 1, name: '太陽光発電', }, { /** 하이브리드축전지시스템 */ id: 2, name: 'ハイブリッド蓄電システム', }, { /** 축전지시스템 */ id: 3, name: '蓄電池システム', }, ], constructionYear: [ { /** 신축 */ id: 1, name: '新築', }, { /** 기축 */ id: 2, name: '既築', }, ], roofShape: [ { /** 박공지붕 */ id: 1, name: '切妻', }, { /** 기동 */ id: 2, name: '寄棟', }, { /** 한쪽흐름 */ id: 3, name: '片流れ', }, ], rafterSize: [ { /** 35mm 이상×48mm 이상 */ id: 1, name: '幅35mm以上×高さ48mm以上', }, { /** 36mm 이상×46mm 이상 */ id: 2, name: '幅36mm以上×高さ46mm以上', }, { /** 37mm 이상×43mm 이상 */ id: 3, name: '幅37mm以上×高さ43mm以上', }, { /** 38mm 이상×40mm 이상 */ id: 4, name: '幅38mm以上×高さ40mm以上', }, ], rafterPitch: [ { /** 455mm 이하 */ id: 1, name: '455mm以下', }, { /** 500mm 이하 */ id: 2, name: '500mm以下', }, { /** 606mm 이하 */ id: 3, name: '606mm以下', }, ], openFieldPlateKind: [ { /** 구조용합판 */ id: 1, name: '構造用合板', }, { /** OSB */ id: 2, name: 'OSB', }, { /** 파티클보드 */ id: 3, name: 'パーティクルボード', }, { /** 소판 */ id: 4, name: '小幅板', }, ], installationAvailability: [ { /** 확인완료 */ id: 1, name: '確認済み', }, { /** 미확인 */ id: 2, name: '未確認', }, ], } export const radioEtcData: Record = { structureOrder: [ { /** 지붕재 - 방수재 - 지붕의기초 - 서까래 */ id: 1, label: '屋根材 > 防水材 > 屋根の基礎 > 垂木', }, ], houseStructure: [ { /** 목재 */ id: 1, label: '木製', }, ], rafterMaterial: [ { /** 목재 */ id: 1, label: '木製', }, { /** 강재 */ id: 2, label: '強制', }, ], waterproofMaterial: [ { /** 아스팔트 지붕 940(22kg 이상) */ id: 1, label: 'アスファルト屋根940(22kg以上)', }, ], insulationPresence: [ { /** 없음 */ id: 1, label: 'なし', }, { /** 있음 */ id: 2, label: 'あり', }, ], rafterDirection: [ { /** 수직 */ id: 1, label: '垂直垂木', }, { /** 수평 */ id: 2, label: '水平垂木', }, ], leakTrace: [ { /** 있음 */ id: 1, label: 'あり', }, { /** 없음 */ id: 2, label: 'なし', }, ], } const makeNumArr = (value: string) => { return value .split(',') .map((v) => v.trim()) .filter((v) => v.length > 0) } export default function RoofForm(props: { roofInfo: SurveyDetailRequest | SurveyDetailInfo setRoofInfo: (roofInfo: SurveyDetailRequest) => void mode: Mode }) { const { roofInfo, setRoofInfo, mode } = props const { showSurveyAlert } = useSurvey() const [isFlip, setIsFlip] = useState(true) const handleNumberInput = (key: keyof SurveyDetailRequest, value: number | string) => { /** 지붕 경사도, 노지판 두께 처리 - 최대 5자리, 소수점 1자리 처리 */ if (key === 'roofSlope' || key === 'openFieldPlateThickness') { const stringValue = value.toString() if (stringValue.length > 5) { showSurveyAlert('保存できるサイズを超えました。') return } if (stringValue.includes('.')) { const decimalPlaces = stringValue.split('.')[1].length if (decimalPlaces > 1) { showSurveyAlert('小数点以下1桁までしか許されません。') return } } } /** 전기 계약 용량 처리 - 단위 붙여서 저장*/ if (key === 'contractCapacity') { const remainValue = roofInfo.contractCapacity?.split(' ')[1] ?? roofInfo.contractCapacity if (Number.isNaN(Number(remainValue))) { setRoofInfo({ ...roofInfo, [key]: value + ' ' + remainValue }) return } setRoofInfo({ ...roofInfo, [key]: value.toString() }) } setRoofInfo({ ...roofInfo, [key]: value.toString() }) } /** 전기 계약 용량 단위 처리 */ const handleUnitInput = (value: string) => { const numericValue = roofInfo.contractCapacity?.replace(/[^0-9.]/g, '') || '' setRoofInfo({ ...roofInfo, contractCapacity: numericValue ? `${numericValue} ${value}` : '0 ' + value, }) } return (
setIsFlip(!isFlip)}>
電気 / 屋根情報
{/* 전기 관계 */}
電気関係
{/* 전기 계약 용량 */}
電気契約容量
{mode === 'READ' && } {mode !== 'READ' && (
handleNumberInput('contractCapacity', e.target.value)} />
)}
{/* 전기 소매 회사사 */}
電気小売会社
setRoofInfo({ ...roofInfo, retailCompany: e.target.value })} />
{/* 전기 부대 설비 */}
電気袋設備※複数選択可能
{/* 설치 희망 시스템 */}
設置希望システム
{/* 지붕 관계 */}
屋根関係
{/* 건축 연수 */}
建築研修
{/* 지붕재 */}
屋根材※最大2個まで選択可能
{/* 지붕 모양 */}
屋根の形状
{/* 지붕 경사도 */}
屋根の斜面
handleNumberInput('roofSlope', e.target.value)} />
{/* 주택구조조 */}
住宅構造
{/* 서까래 재질 */}
垂木材質
{/* 서까래 크기 */}
垂木サイズ
{/* 서까래 피치 */}
垂木サイズ
{/* 서까래 방향 */}
垂木の方向
{/* 노지판 종류 */}
路地板の種類
{roofInfo.openFieldPlateKind === '4' && (
{/* 노지판 두께 */}
路地板厚※小幅板を選択した場合, 厚さ. 小幅板間の間隔寸法を記載
handleNumberInput('openFieldPlateThickness', e.target.value)} /> mm
)}
{/* 누수 흔적 */}
水漏れの痕跡
{/* 방수재 종류 */}
防水材の種類
{/* 단열재 유무 */}
断熱材の有無
{/* 지붕 구조의 순서 */}
屋根構造の順序
{/* 지붕 제품명 설치 가능 여부 확인 */}
屋根製品名 設置可否確認
{/* 메모 */}
メモ
) } /** SelectBox 처리 */ const SelectedBox = ({ mode, column, detailInfoData, setRoofInfo, }: { mode: Mode column: string detailInfoData: SurveyDetailInfo setRoofInfo: (roofInfo: SurveyDetailRequest) => void }) => { const selectedId = detailInfoData?.[column as keyof SurveyDetailInfo] const etcValue = detailInfoData?.[`${column}Etc` as keyof SurveyDetailInfo] const [isEtcSelected, setIsEtcSelected] = useState(Boolean(etcValue)) const isSpecialCase = column === 'constructionYear' || column === 'installationAvailability' const showEtcOption = !isSpecialCase /** SelectBox 값 변경 처리 */ const handleSelectChange = (e: React.ChangeEvent) => { const value = e.target.value const isEtc = value === 'etc' const isSpecialEtc = isSpecialCase && value === '2' const updatedData = { ...detailInfoData, [column]: isEtc ? null : value, [`${column}Etc`]: isEtc ? '' : null, } if (isSpecialEtc) { updatedData[column] = value } setIsEtcSelected(isEtc || isSpecialEtc) setRoofInfo(updatedData) } /** 기타 입력 처리 */ const handleEtcInputChange = (e: React.ChangeEvent) => { setRoofInfo({ ...detailInfoData, [`${column}Etc`]: e.target.value }) } /** Input box 비활성화 처리 * - 읽기 모드 : 비활성화 * - 설치 가능 여부 : 기타 입력 창 항상 활성화 * - 건축 연수 : 신축(1) 체크 시 비활성화 * */ const isInputDisabled = () => { if (mode === 'READ') return true if (column === 'installationAvailability') return false if (column === 'constructionYear') { return detailInfoData.constructionYear === '1' || detailInfoData.constructionYear === null } return !isEtcSelected && !etcValue } return ( <>
{column === 'constructionYear' && }
) } /** RadioBox 선택 처리 */ const RadioSelected = ({ mode, column, detailInfoData, setRoofInfo, }: { mode: Mode column: string detailInfoData: SurveyDetailInfo setRoofInfo: (roofInfo: SurveyDetailRequest) => void }) => { const etcValue = detailInfoData?.[`${column}Etc` as keyof SurveyDetailInfo] const [etcChecked, setEtcChecked] = useState(Boolean(etcValue)) const selectedId = column === 'leakTrace' ? Number(detailInfoData?.[column as keyof SurveyDetailInfo]) || 2 : detailInfoData?.[column as keyof SurveyDetailInfo] const isSpecialColumn = column === 'rafterDirection' || column === 'leakTrace' || column === 'insulationPresence' const showEtcOption = !isSpecialColumn /** RadioBox 값 변경 처리 */ const handleRadioChange = (e: React.ChangeEvent) => { const value = e.target.value /** 누수 흔적 처리 - boolean 타입이므로 별도 처리 */ if (column === 'leakTrace') { setRoofInfo({ ...detailInfoData, leakTrace: value === '1' }) return } /** 기타 체크 처리 */ if (value === 'etc') { setEtcChecked(true) setRoofInfo({ ...detailInfoData, [column]: null, [`${column}Etc`]: '' }) return } /** 단열재 유무 - 있음(1) 선택 시 기타 체크 처리 * 서까래 방향 - 기타 입력 칸 없음 * */ const isInsulationPresence = column === 'insulationPresence' const isRafterDirection = column === 'rafterDirection' setEtcChecked(isInsulationPresence && value === '2') setRoofInfo({ ...detailInfoData, [column]: value, [`${column}Etc`]: isRafterDirection ? detailInfoData[`${column}Etc` as keyof SurveyDetailInfo] : null, }) } /** 기타 입력 처리 */ const handleEtcInputChange = (e: React.ChangeEvent) => { setRoofInfo({ ...detailInfoData, [`${column}Etc`]: e.target.value }) } /** Input box 비활성화 처리 * - 읽기 모드 : 비활성화 * - 단열재 유무 : 단열재 없음(1) 체크 시 비활성화 * */ const isInputDisabled = () => { if (mode === 'READ') return true if (column === 'insulationPresence') { return detailInfoData.insulationPresence !== '2' } return !etcChecked && !etcValue } return ( <> {radioEtcData[column as keyof typeof radioEtcData].map((item) => (
))} {showEtcOption && (
)} {(showEtcOption || column === 'insulationPresence') && (
)} ) } /** 다중 선택 처리 */ const MultiCheck = ({ mode, column, roofInfo, setRoofInfo, }: { mode: Mode column: string roofInfo: SurveyDetailInfo setRoofInfo: (roofInfo: SurveyDetailRequest) => void }) => { const { showSurveyAlert } = useSurvey() const multiCheckData = column === 'supplementaryFacilities' ? supplementaryFacilities : roofMaterial const etcValue = roofInfo?.[`${column}Etc` as keyof SurveyDetailInfo] const [isOtherCheck, setIsOtherCheck] = useState(Boolean(etcValue)) const isRoofMaterial = column === 'roofMaterial' const selectedValues = makeNumArr(String(roofInfo[column as keyof SurveyDetailInfo] ?? '')) /** 다중 선택 처리 */ const handleCheckbox = (id: number) => { const isOtherSelected = Boolean(etcValue) let newValue: string[] if (selectedValues.includes(String(id))) { newValue = selectedValues.filter((v) => v !== String(id)) } else { /** 지붕 재료 처리 - 최대 2개 선택 처리 */ if (isRoofMaterial) { const totalSelected = selectedValues.length + (isOtherSelected || isOtherCheck ? 1 : 0) if (totalSelected >= 2) { showSurveyAlert(ALERT_MESSAGES.ROOF_MATERIAL_MAX_SELECT_ERROR) return } } newValue = [...selectedValues, String(id)] } setRoofInfo({ ...roofInfo, [column]: newValue.join(',') }) } /** 기타 선택 처리 */ const handleOtherCheckbox = () => { if (isRoofMaterial) { const currentSelected = selectedValues.length if (!isOtherCheck && currentSelected >= 2) { showSurveyAlert(ALERT_MESSAGES.ROOF_MATERIAL_MAX_SELECT_ERROR) return } } const newIsOtherCheck = !isOtherCheck setIsOtherCheck(newIsOtherCheck) /** 기타 선택 해제 시 값도 null로 설정 */ setRoofInfo({ ...roofInfo, [`${column}Etc`]: newIsOtherCheck ? '' : null, }) } /** 기타 입력 처리 */ const handleOtherInputChange = (e: React.ChangeEvent) => { setRoofInfo({ ...roofInfo, [`${column}Etc`]: e.target.value }) } /** Input box 비활성화 처리 */ const isInputDisabled = () => { return mode === 'READ' || (!isOtherCheck && !etcValue) } return ( <>
{multiCheckData.map((item) => (
handleCheckbox(item.id)} />
))}
) }