616 lines
24 KiB
TypeScript

import { useEffect, useState } from 'react'
import type { Mode, SurveyDetailInfo, SurveyDetailRequest } from '@/types/Survey'
import { useAlertMsg, WARNING_MESSAGE } from '@/hooks/useAlertMsg'
import { radioEtcData, supplementaryFacilities } from '@/types/Survey'
import { useSurveyOptionStore } from '@/store/surveyOptionStore'
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 { showErrorAlert } = useAlertMsg()
const [isFlip, setIsFlip] = useState<boolean>(true)
const handleNumberInput = (key: keyof SurveyDetailRequest, value: number | string) => {
/** 지붕 경사도, 노지판 두께 처리 - 최대 5자리, 소수점 1자리 처리 */
if (key === 'roofSlope' || key === 'openFieldPlateThickness') {
const stringValue = value.toString()
if (stringValue.length > 5) {
showErrorAlert(WARNING_MESSAGE.SAVE_SIZE_OVERFLOW)
return
}
if (stringValue.includes('.')) {
const decimalPlaces = stringValue.split('.')[1].length
if (decimalPlaces > 1) {
showErrorAlert(WARNING_MESSAGE.DECIMAL_POINT_CANNOT_EXCEED)
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 (
<div className={`sale-detail-toggle-bx ${isFlip ? 'act' : ''}`}>
<div className="sale-detail-toggle-head" onClick={() => setIsFlip(!isFlip)}>
<div className="sale-detail-toggle-name"> / </div>
<div className="sale-detail-toggle-btn-wrap">
<button className="sale-detail-toggle-btn"></button>
</div>
</div>
<div className="sale-detail-toggle-cont">
<div className="sale-frame">
{/* 전기 관계 */}
<div className="sale-roof-title"></div>
<div className="data-form-wrap">
<div className="data-input-form-bx">
{/* 전기 계약 용량 */}
<div className="data-input-form-tit"></div>
{mode === 'READ' && <input type="text" className="input-frame" value={roofInfo?.contractCapacity ?? ''} readOnly={mode === 'READ'} />}
{mode !== 'READ' && (
<div className="data-input mb10">
<input
type="number"
inputMode="numeric"
id="contractCapacity"
className="input-frame"
value={roofInfo?.contractCapacity?.split(' ')[0] ?? ''}
onChange={(e) => handleNumberInput('contractCapacity', e.target.value)}
/>
<div className="data-input mt10">
<select
className="select-form"
name="contractCapacityUnit"
id="contractCapacityUnit"
value={roofInfo?.contractCapacity?.split(' ')[1] ?? ''}
onChange={(e) => handleUnitInput(e.target.value)}
>
<option value="" hidden>
</option>
<option value="A">A</option>
<option value="kVA">kVA</option>
</select>
</div>
</div>
)}
</div>
<div className="data-input-form-bx">
{/* 전기 소매 회사사 */}
<div className="data-input-form-tit"></div>
<input
type="text"
className="input-frame"
value={roofInfo?.retailCompany ?? ''}
disabled={mode === 'READ'}
onChange={(e) => setRoofInfo({ ...roofInfo, retailCompany: e.target.value })}
/>
</div>
<div className="data-input-form-bx">
{/* 전기 부대 설비 */}
<div className="data-input-form-tit">
<span></span>
</div>
<MultiCheck mode={mode} column="supplementaryFacilities" roofInfo={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
{/* 설치 희망 시스템 */}
<div className="data-input-form-bx">
<div className="data-input-form-tit red-f"></div>
<SelectedBox mode={mode} column="installationSystem" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
</div>
</div>
<div className="sale-frame">
{/* 지붕 관계 */}
<div className="sale-roof-title"></div>
<div className="data-form-wrap">
<div className="data-input-form-bx">
{/* 건축 연수 */}
<div className="data-input-form-tit red-f"></div>
<div className="data-input mb5">
<SelectedBox mode={mode} column="constructionYear" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
</div>
<div className="data-input-form-bx">
{/* 지붕재 */}
<div className="data-input-form-tit">
<span>2</span>
</div>
<MultiCheck mode={mode} column="roofMaterial" roofInfo={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
<div className="data-input-form-bx">
{/* 지붕 모양 */}
<div className="data-input-form-tit"></div>
<div className="data-input mb5">
<SelectedBox mode={mode} column="roofShape" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
</div>
<div className="data-input-form-bx">
{/* 지붕 경사도 */}
<div className="data-input-form-tit"></div>
<div className="data-input flex">
<input
type="number"
inputMode="numeric"
id="roofSlope"
className="input-frame"
value={roofInfo?.roofSlope ?? ''}
disabled={mode === 'READ'}
onChange={(e) => handleNumberInput('roofSlope', e.target.value)}
/>
<span></span>
</div>
</div>
<div className="data-input-form-bx">
{/* 주택구조조 */}
<div className="data-input-form-tit"></div>
<RadioSelected mode={mode} column="houseStructure" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
<div className="data-input-form-bx">
{/* 서까래 재질 */}
<div className="data-input-form-tit"></div>
<RadioSelected mode={mode} column="rafterMaterial" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
<div className="data-input-form-bx">
{/* 서까래 크기 */}
<div className="data-input-form-tit red-f"></div>
<div className="data-input mb5">
<SelectedBox mode={mode} column="rafterSize" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
</div>
<div className="data-input-form-bx">
{/* 서까래 피치 */}
<div className="data-input-form-tit red-f"></div>
<div className="data-input mb5">
<SelectedBox mode={mode} column="rafterPitch" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
</div>
<div className="data-input-form-bx">
{/* 서까래 방향 */}
<div className="data-input-form-tit red-f"></div>
<div className="data-check-wrap mb0">
<RadioSelected mode={mode} column="rafterDirection" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
</div>
<div className="data-input-form-bx">
{/* 노지판 종류 */}
<div className="data-input-form-tit"></div>
<div className="data-input mb5">
<SelectedBox mode={mode} column="openFieldPlateKind" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
</div>
{roofInfo.openFieldPlateKind === 'S' && (
<div className="data-input-form-bx">
{/* 노지판 두께 */}
<div className="data-input-form-tit">
<span>, . </span>
</div>
<div className="data-input flex">
<input
type="number"
inputMode="numeric"
id="openFieldPlateThickness"
className="input-frame"
value={roofInfo?.openFieldPlateThickness ?? ''}
disabled={mode === 'READ'}
onChange={(e) => handleNumberInput('openFieldPlateThickness', e.target.value)}
/>
<span>mm</span>
</div>
</div>
)}
<div className="data-input-form-bx">
{/* 누수 흔적 */}
<div className="data-input-form-tit "></div>
<div className="data-check-wrap mb0">
<RadioSelected mode={mode} column="leakTrace" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
</div>
<div className="data-input-form-bx">
{/* 방수재 종류 */}
<div className="data-input-form-tit red-f"></div>
<RadioSelected mode={mode} column="waterproofMaterial" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
<div className="data-input-form-bx">
{/* 단열재 유무 */}
<div className="data-input-form-tit red-f"></div>
<RadioSelected mode={mode} column="insulationPresence" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
<div className="data-input-form-bx">
{/* 지붕 구조의 순서 */}
<div className="data-input-form-tit red-f"></div>
<RadioSelected mode={mode} column="structureOrder" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
<div className="data-input-form-bx">
{/* 지붕 제품명 설치 가능 여부 확인 */}
<div className="data-input-form-tit"> </div>
<SelectedBox mode={mode} column="installationAvailability" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
<div className="data-input-form-bx">
{/* 메모 */}
<div className="data-input-form-tit"></div>
<div className="data-input">
<textarea
className="textarea-form"
name="memo"
id="memo"
value={roofInfo?.memo ?? ''}
disabled={mode === 'READ'}
onChange={(e) => {
if (e.target.value.length >= 2000) {
showErrorAlert(WARNING_MESSAGE.CONTENTS_MAX_LENGTH)
return
}
setRoofInfo({ ...roofInfo, memo: e.target.value })
}}
maxLength={2000}
></textarea>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
/** SelectBox 처리 */
const SelectedBox = ({
mode,
column,
detailInfoData,
setRoofInfo,
}: {
mode: Mode
column: string
detailInfoData: SurveyDetailInfo
setRoofInfo: (roofInfo: SurveyDetailRequest) => void
}) => {
const { selectBoxOptions, initialized, loading, loadOptions } = useSurveyOptionStore()
const selectedId = detailInfoData?.[column as keyof SurveyDetailInfo]
const etcValue = detailInfoData?.[`${column}Etc` as keyof SurveyDetailInfo]
const [isEtcSelected, setIsEtcSelected] = useState<boolean>(Boolean(etcValue))
useEffect(() => {
if (!initialized && !loading) loadOptions()
}, [initialized, loading])
const isSpecialCase = column === 'constructionYear' || column === 'installationAvailability'
const showEtcOption = !isSpecialCase
/** SelectBox 값 변경 처리 */
const handleSelectChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
const value = e.target.value
const isEtc = value === 'etc'
const updatedData = {
...detailInfoData,
[column]: isEtc ? null : value,
[`${column}Etc`]: isEtc ? '' : null,
}
setIsEtcSelected(isEtc)
setRoofInfo(updatedData)
}
/** 기타 입력 처리 */
const handleEtcInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setRoofInfo({ ...detailInfoData, [`${column}Etc`]: e.target.value })
}
/** Input box 비활성화 처리
* - 읽기 모드 : 비활성화
* - 설치 가능 여부 : 기타 입력 창 항상 활성화
* - 건축 연수 : 新築 신축 (N) 체크 시 비활성화
* */
const isInputDisabled = () => {
if (mode === 'READ') return true
if (column === 'installationAvailability') return false
if (column === 'constructionYear') {
return detailInfoData.constructionYear === selectBoxOptions.constructionYear[0].code || detailInfoData.constructionYear === null
}
return !isEtcSelected && !etcValue
}
return (
<>
<select
className="select-form mb10"
name={column}
id={column}
disabled={mode === 'READ'}
value={selectedId ? String(selectedId) : etcValue || isEtcSelected ? 'etc' : ''}
onChange={handleSelectChange}
>
{selectBoxOptions[column as keyof typeof selectBoxOptions].map((item) => (
<option key={item.code ?? String(item.id)} value={item.code ?? String(item.id)}>
{item.name}
</option>
))}
{showEtcOption && (
<option key="etc" value="etc">
()
</option>
)}
<option key="" value="" hidden>
</option>
</select>
<div className={`data-input ${column === 'constructionYear' ? 'flex' : ''}`}>
<input
id={`${column}Etc`}
type={column === 'constructionYear' ? 'number' : 'text'}
inputMode={column === 'constructionYear' ? 'numeric' : 'text'}
className="input-frame"
placeholder="-"
value={detailInfoData[`${column}Etc` as keyof SurveyDetailInfo]?.toString() ?? ''}
onChange={handleEtcInputChange}
readOnly={isInputDisabled()}
/>
{column === 'constructionYear' && <span></span>}
</div>
</>
)
}
/** 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>(Boolean(etcValue))
const selectedId =
/** 누수 흔적 boolean 타입이므로 number 타입으로 변환 - 값이 없을 경우 2(없음) 으로 초기화*/
column === 'leakTrace'
? Number(detailInfoData?.[column as keyof SurveyDetailInfo]) || radioEtcData.leakTrace[1].id
: detailInfoData?.[column as keyof SurveyDetailInfo]
const isSpecialColumn = column === 'rafterDirection' || column === 'leakTrace' || column === 'insulationPresence'
const showEtcOption = !isSpecialColumn
/** RadioBox 값 변경 처리 */
const handleRadioChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value
/** 누수 흔적 처리 - boolean 타입이므로 별도 처리 */
if (column === 'leakTrace') {
setRoofInfo({ ...detailInfoData, leakTrace: value === String(radioEtcData.leakTrace[0].id) })
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 === String(radioEtcData.insulationPresence[1].id))
setRoofInfo({
...detailInfoData,
[column]: value,
[`${column}Etc`]: isRafterDirection ? detailInfoData[`${column}Etc` as keyof SurveyDetailInfo] : null,
})
}
/** 기타 입력 처리 */
const handleEtcInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setRoofInfo({ ...detailInfoData, [`${column}Etc`]: e.target.value })
}
/** Input box 비활성화 처리
* - 읽기 모드 : 비활성화
* - 단열재 유무 : 단열재 없음(1) 체크 시 비활성화
* */
const isInputDisabled = () => {
if (mode === 'READ') return true
if (column === 'insulationPresence') {
return detailInfoData.insulationPresence === String(radioEtcData.insulationPresence[0].id)
}
return !etcChecked && !etcValue
}
return (
<>
{radioEtcData[column as keyof typeof radioEtcData].map((item) => (
<div className="radio-form-box mb10" key={item.code ?? String(item.id)}>
<input
type="radio"
name={column}
id={`${column}_${item.code ?? item.id}`}
disabled={mode === 'READ'}
checked={Number(selectedId) === (item.code ?? item.id)}
onChange={handleRadioChange}
value={item.code ?? item.id}
/>
<label htmlFor={`${column}_${item.code ?? item.id}`}>{item.label}</label>
</div>
))}
{showEtcOption && (
<div className="radio-form-box mb10">
<input
type="radio"
name={column}
id={`${column}Etc`}
value="etc"
disabled={mode === 'READ'}
checked={etcChecked || Boolean(etcValue)}
onChange={handleRadioChange}
/>
<label htmlFor={`${column}Etc`}> ()</label>
</div>
)}
{(showEtcOption || column === 'insulationPresence') && (
<div className="data-input">
<input
id={`${column}Etc`}
type="text"
className="input-frame"
placeholder="-"
value={detailInfoData[`${column}Etc` as keyof SurveyDetailInfo]?.toString() ?? ''}
onChange={handleEtcInputChange}
readOnly={isInputDisabled()}
/>
</div>
)}
</>
)
}
/** 다중 선택 처리 */
const MultiCheck = ({
mode,
column,
roofInfo,
setRoofInfo,
}: {
mode: Mode
column: string
roofInfo: SurveyDetailInfo
setRoofInfo: (roofInfo: SurveyDetailRequest) => void
}) => {
const { showErrorAlert } = useAlertMsg()
const { roofMaterial, initialized, loading, loadOptions } = useSurveyOptionStore()
const multiCheckData = column === 'supplementaryFacilities' ? supplementaryFacilities : roofMaterial
const [etcValue, setEtcValue] = useState<string | null>(roofInfo?.[`${column}Etc` as keyof SurveyDetailInfo]?.toString() ?? null)
useEffect(() => {
const newValue = roofInfo?.[`${column}Etc` as keyof SurveyDetailInfo]?.toString() ?? null
setEtcValue(newValue)
}, [roofInfo, column])
useEffect(() => {
if (!initialized && !loading) loadOptions()
}, [initialized, loading])
const isRoofMaterial = column === 'roofMaterial'
const selectedValues = makeNumArr(String(roofInfo[column as keyof SurveyDetailInfo] ?? ''))
/** 다중 선택 처리 */
const handleCheckbox = (item: { id: number; code: string | null; name: string }) => {
const isOtherSelected = etcValue !== null
let newValue: string[]
if (selectedValues.includes(item.code ?? String(item.id))) {
newValue = selectedValues.filter((v) => v !== item.code && v !== String(item.id))
} else {
/** 지붕 재료 처리 - 최대 2개 선택 처리 */
if (isRoofMaterial) {
const totalSelected = selectedValues.length + (isOtherSelected ? 1 : 0)
if (totalSelected >= 2) {
showErrorAlert(WARNING_MESSAGE.ROOF_MATERIAL_MAX_SELECT)
return
}
}
newValue = [...selectedValues, item.code ?? String(item.id)]
}
setRoofInfo({ ...roofInfo, [column]: newValue.join(',') })
}
/** 기타 선택 처리 */
const handleOtherCheckbox = () => {
setEtcValue(etcValue !== null ? null : '')
const isOtherSelected = etcValue !== null
if (isRoofMaterial) {
/** 지붕 재료 기타 선택 포함 최대 2개 선택 처리 */
if (!isOtherSelected && selectedValues.length >= 2) {
showErrorAlert(WARNING_MESSAGE.ROOF_MATERIAL_MAX_SELECT)
setEtcValue(null)
return
}
}
/** 기타 선택 해제 시 값도 null로 설정 */
setRoofInfo({
...roofInfo,
[`${column}Etc`]: isOtherSelected ? null : '',
})
}
/** 기타 입력 처리 */
const handleOtherInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setRoofInfo({ ...roofInfo, [`${column}Etc`]: e.target.value })
}
/** Input box 비활성화 처리 */
const isInputDisabled = () => {
return mode === 'READ' || etcValue === null
}
return (
<>
<div className="data-check-wrap">
{multiCheckData.map((item) => (
<div className="check-form-box" key={item.id}>
<input
type="checkbox"
id={`${column}_${item.id}`}
checked={selectedValues.includes(item.code ?? String(item.id))}
disabled={mode === 'READ'}
onChange={() => handleCheckbox(item)}
/>
<label htmlFor={`${column}_${item.id}`}>{item.name}</label>
</div>
))}
<div className="check-form-box">
<input type="checkbox" id={`${column}Etc`} checked={etcValue !== null} disabled={mode === 'READ'} onChange={handleOtherCheckbox} />
<label htmlFor={`${column}Etc`}> ()</label>
</div>
</div>
<div className="data-input">
<input
type="text"
className="input-frame"
placeholder="-"
value={roofInfo[`${column}Etc` as keyof SurveyDetailInfo]?.toString() ?? ''}
onChange={handleOtherInputChange}
readOnly={isInputDisabled()}
/>
</div>
</>
)
}