- 제출 대상 판매점 ID, 일련번호 컬럼 추가 - 임시 저장 시 일련번호에 '임시저장000' 으로 저장 - 조사매물 목록 조회 필터링 조건 수정 - url 에러 핸들링
758 lines
24 KiB
TypeScript
758 lines
24 KiB
TypeScript
import { useState } from 'react'
|
||
import type { Mode, SurveyDetailInfo, SurveyDetailRequest } from '@/types/Survey'
|
||
|
||
type RadioEtcKeys = 'houseStructure' | 'rafterMaterial' | 'waterproofMaterial' | 'insulationPresence' | 'rafterDirection' | 'leakTrace'
|
||
type SelectBoxKeys =
|
||
| 'installationSystem'
|
||
| 'constructionYear'
|
||
| 'roofShape'
|
||
| 'rafterPitch'
|
||
| 'rafterSize'
|
||
| 'openFieldPlateKind'
|
||
| 'structureOrder'
|
||
| '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<SelectBoxKeys, { id: number; name: string }[]> = {
|
||
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: [
|
||
{
|
||
id: 1,
|
||
name: '幅35mm以上×高さ48mm以上',
|
||
},
|
||
{
|
||
id: 2,
|
||
name: '幅36mm以上×高さ46mm以上',
|
||
},
|
||
{
|
||
id: 3,
|
||
name: '幅37mm以上×高さ43mm以上',
|
||
},
|
||
{
|
||
id: 4,
|
||
name: '幅38mm以上×高さ40mm以上',
|
||
},
|
||
],
|
||
rafterPitch: [
|
||
{
|
||
id: 1,
|
||
name: '455mm以下',
|
||
},
|
||
{
|
||
id: 2,
|
||
name: '500mm以下',
|
||
},
|
||
{
|
||
id: 3,
|
||
name: '606mm以下',
|
||
},
|
||
],
|
||
openFieldPlateKind: [
|
||
{
|
||
id: 1,
|
||
name: '構造用合板', //구조용합판
|
||
},
|
||
{
|
||
id: 2,
|
||
name: 'OSB', //OSB
|
||
},
|
||
{
|
||
id: 3,
|
||
name: 'パーティクルボード', //파티클보드
|
||
},
|
||
{
|
||
id: 4,
|
||
name: '小幅板', //소판
|
||
},
|
||
],
|
||
structureOrder: [
|
||
{
|
||
id: 1,
|
||
name: '屋根材', //지붕재
|
||
},
|
||
{
|
||
id: 2,
|
||
name: '防水材', //방수재
|
||
},
|
||
{
|
||
id: 3,
|
||
name: '屋根の基礎', //지붕의기초
|
||
},
|
||
{
|
||
id: 4,
|
||
name: '垂木', //서까래
|
||
},
|
||
],
|
||
installationAvailability: [
|
||
{
|
||
id: 1,
|
||
name: '確認済み', //확인완료
|
||
},
|
||
{
|
||
id: 2,
|
||
name: '未確認', //미확인
|
||
},
|
||
],
|
||
}
|
||
|
||
export const radioEtcData: Record<RadioEtcKeys, { id: number; label: string }[]> = {
|
||
houseStructure: [
|
||
{
|
||
id: 1,
|
||
label: '木製',
|
||
},
|
||
],
|
||
rafterMaterial: [
|
||
{
|
||
id: 1,
|
||
label: '木製',
|
||
},
|
||
{
|
||
id: 2,
|
||
label: '強制',
|
||
},
|
||
],
|
||
waterproofMaterial: [
|
||
{
|
||
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 [isFlip, setIsFlip] = useState<boolean>(true)
|
||
|
||
const handleNumberInput = (key: keyof SurveyDetailRequest, value: number | string) => {
|
||
if (key === 'roofSlope' || key === 'openFieldPlateThickness') {
|
||
const stringValue = value.toString()
|
||
if (stringValue.length > 5) {
|
||
alert('保存できるサイズを超えました。')
|
||
return
|
||
}
|
||
if (stringValue.includes('.')) {
|
||
const decimalPlaces = stringValue.split('.')[1].length
|
||
if (decimalPlaces > 1) {
|
||
alert('小数点以下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 (
|
||
<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 ?? ''} disabled={mode === 'READ'} />}
|
||
{mode !== 'READ' && (
|
||
<div className="data-input mb5">
|
||
<input
|
||
type="number"
|
||
id="contractCapacity"
|
||
className="input-frame"
|
||
value={roofInfo?.contractCapacity?.split(' ')[0] ?? ''}
|
||
onChange={(e) => handleNumberInput('contractCapacity', e.target.value)}
|
||
/>
|
||
<div className="data-input">
|
||
<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"
|
||
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>
|
||
<div className="data-input-form-bx">
|
||
{/* 노지판 두께 */}
|
||
<div className="data-input-form-tit">
|
||
路地板厚<span>※小幅板を選択した場合, 厚さ. 小幅板間の間隔寸法を記載</span>
|
||
</div>
|
||
<div className="data-input flex">
|
||
<input
|
||
type="text"
|
||
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>
|
||
<SelectedBox 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"
|
||
placeholder="TextArea Filed"
|
||
value={roofInfo?.memo ?? ''}
|
||
disabled={mode === 'READ'}
|
||
onChange={(e) => setRoofInfo({ ...roofInfo, memo: e.target.value })}
|
||
></textarea>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
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>(Boolean(etcValue))
|
||
|
||
const isSpecialCase = column === 'constructionYear' || column === 'installationAvailability'
|
||
const showEtcOption = !isSpecialCase
|
||
|
||
const handleSelectChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||
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<HTMLInputElement>) => {
|
||
setRoofInfo({ ...detailInfoData, [`${column}Etc`]: e.target.value })
|
||
}
|
||
|
||
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 (
|
||
<>
|
||
<select
|
||
className="select-form mb10"
|
||
name={column}
|
||
id={column}
|
||
disabled={mode === 'READ'}
|
||
value={selectedId ? Number(selectedId) : etcValue ? 'etc' : ''}
|
||
onChange={handleSelectChange}
|
||
>
|
||
{selectBoxOptions[column as keyof typeof selectBoxOptions].map((item) => (
|
||
<option key={item.id} value={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
|
||
type={column === 'constructionYear' ? 'number' : 'text'}
|
||
className="input-frame"
|
||
placeholder="-"
|
||
value={detailInfoData[`${column}Etc` as keyof SurveyDetailInfo]?.toString() ?? ''}
|
||
onChange={handleEtcInputChange}
|
||
disabled={isInputDisabled()}
|
||
/>
|
||
{column === 'constructionYear' && <span>年</span>}
|
||
</div>
|
||
</>
|
||
)
|
||
}
|
||
|
||
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 =
|
||
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
|
||
|
||
const handleRadioChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||
const value = e.target.value
|
||
|
||
if (column === 'leakTrace') {
|
||
setRoofInfo({ ...detailInfoData, leakTrace: value === '1' })
|
||
return
|
||
}
|
||
|
||
if (value === 'etc') {
|
||
setEtcChecked(true)
|
||
setRoofInfo({ ...detailInfoData, [column]: null, [`${column}Etc`]: '' })
|
||
return
|
||
}
|
||
|
||
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<HTMLInputElement>) => {
|
||
setRoofInfo({ ...detailInfoData, [`${column}Etc`]: e.target.value })
|
||
}
|
||
|
||
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) => (
|
||
<div className="radio-form-box mb10" key={item.id}>
|
||
<input
|
||
type="radio"
|
||
name={column}
|
||
id={`${column}_${item.id}`}
|
||
disabled={mode === 'READ'}
|
||
checked={Number(selectedId) === item.id}
|
||
onChange={handleRadioChange}
|
||
value={item.id}
|
||
/>
|
||
<label htmlFor={`${column}_${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 && (
|
||
<div className="data-input">
|
||
<input
|
||
type="text"
|
||
className="input-frame"
|
||
placeholder="-"
|
||
value={detailInfoData[`${column}Etc` as keyof SurveyDetailInfo]?.toString() ?? ''}
|
||
onChange={handleEtcInputChange}
|
||
disabled={isInputDisabled()}
|
||
/>
|
||
</div>
|
||
)}
|
||
</>
|
||
)
|
||
}
|
||
|
||
const MultiCheck = ({
|
||
mode,
|
||
column,
|
||
roofInfo,
|
||
setRoofInfo,
|
||
}: {
|
||
mode: Mode
|
||
column: string
|
||
roofInfo: SurveyDetailInfo
|
||
setRoofInfo: (roofInfo: SurveyDetailRequest) => void
|
||
}) => {
|
||
const multiCheckData = column === 'supplementaryFacilities' ? supplementaryFacilities : roofMaterial
|
||
const etcValue = roofInfo?.[`${column}Etc` as keyof SurveyDetailInfo]
|
||
const [isOtherCheck, setIsOtherCheck] = useState<boolean>(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 {
|
||
if (isRoofMaterial) {
|
||
const totalSelected = selectedValues.length + (isOtherSelected ? 1 : 0)
|
||
if (totalSelected >= 2) {
|
||
alert('屋根材は最大2個まで選択できます。')
|
||
return
|
||
}
|
||
}
|
||
newValue = [...selectedValues, String(id)]
|
||
}
|
||
setRoofInfo({ ...roofInfo, [column]: newValue.join(',') })
|
||
}
|
||
|
||
const handleOtherCheckbox = () => {
|
||
if (isRoofMaterial) {
|
||
const currentSelected = selectedValues.length
|
||
if (!isOtherCheck && currentSelected >= 2) {
|
||
alert('屋根材は最大2個まで選択できます。')
|
||
return
|
||
}
|
||
}
|
||
|
||
const newIsOtherCheck = !isOtherCheck
|
||
setIsOtherCheck(newIsOtherCheck)
|
||
|
||
// 기타 선택 해제 시 값도 null로 설정
|
||
setRoofInfo({
|
||
...roofInfo,
|
||
[`${column}Etc`]: newIsOtherCheck ? '' : null,
|
||
})
|
||
}
|
||
|
||
const handleOtherInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||
setRoofInfo({ ...roofInfo, [`${column}Etc`]: e.target.value })
|
||
}
|
||
|
||
const isInputDisabled = () => {
|
||
return mode === 'READ' || (!isOtherCheck && !etcValue)
|
||
}
|
||
|
||
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(String(item.id))}
|
||
disabled={mode === 'READ'}
|
||
onChange={() => handleCheckbox(item.id)}
|
||
/>
|
||
<label htmlFor={`${column}_${item.id}`}>{item.name}</label>
|
||
</div>
|
||
))}
|
||
<div className="check-form-box">
|
||
<input
|
||
type="checkbox"
|
||
id={`${column}Etc`}
|
||
checked={isOtherCheck || Boolean(etcValue)}
|
||
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}
|
||
disabled={isInputDisabled()}
|
||
/>
|
||
</div>
|
||
</>
|
||
)
|
||
}
|