diff --git a/prisma/schema.prisma b/prisma/schema.prisma index f2a45c6..b8fca07 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -133,7 +133,7 @@ model SD_SERVEY_SALES_DETAIL_INFO { //전기 소매 회사 retail_company String? @db.VarChar(100) //전기 부대 설비 - supplementary_facilities Int? @db.Int + supplementary_facilities String? @db.VarChar(20) //전기 부대 설비 기타 supplementary_facilities_etc String? @db.VarChar(200) //설치 희망 시스템 @@ -145,7 +145,7 @@ model SD_SERVEY_SALES_DETAIL_INFO { //건축 연수 기타 construction_year_etc String? @db.VarChar(200) //지붕재 - roof_material Int? @db.Int + roof_material String? @db.VarChar(20) //지붕재 기타 roof_material_etc String? @db.VarChar(200) //지붕 모양 diff --git a/src/app/survey-sale/basic-info/page.tsx b/src/app/survey-sale/basic-info/page.tsx index 20cbeb8..2c75137 100644 --- a/src/app/survey-sale/basic-info/page.tsx +++ b/src/app/survey-sale/basic-info/page.tsx @@ -1,4 +1,4 @@ -import BasicForm from '@/components/survey-sale/detail/BasicForm' +import BasicForm from '@/components/survey-sale/detail/form/BasicForm' export default function page() { return ( diff --git a/src/app/survey-sale/page.tsx b/src/app/survey-sale/page.tsx index fc6e6a3..d2841f8 100644 --- a/src/app/survey-sale/page.tsx +++ b/src/app/survey-sale/page.tsx @@ -4,7 +4,6 @@ import SearchForm from '@/components/survey-sale/list/SearchForm' export default function page() { return ( <> - ) diff --git a/src/app/survey-sale/roof-info/page.tsx b/src/app/survey-sale/roof-info/page.tsx index 4555c3a..1c5358b 100644 --- a/src/app/survey-sale/roof-info/page.tsx +++ b/src/app/survey-sale/roof-info/page.tsx @@ -1,4 +1,4 @@ -import RoofInfoForm from '@/components/survey-sale/detail/RoofInfoForm' +import RoofInfoForm from '@/components/survey-sale/detail/form/RoofInfoForm' export default function page() { return ( diff --git a/src/components/survey-sale/common/NavTab.tsx b/src/components/survey-sale/common/NavTab.tsx index 8029afd..290473c 100644 --- a/src/components/survey-sale/common/NavTab.tsx +++ b/src/components/survey-sale/common/NavTab.tsx @@ -1,16 +1,19 @@ 'use client' import { useSurveySaleTabState } from '@/store/surveySaleTabState' -import { usePathname, useRouter } from 'next/navigation' +import { usePathname, useRouter, useSearchParams, useParams } from 'next/navigation' import { useEffect } from 'react' +import { usePopupController } from '@/store/popupController' export default function NavTab() { const router = useRouter() const pathname = usePathname() - if (pathname === '/survey-sale') { - return null - } + const searchParams = useSearchParams() + const id = searchParams.get('id') + + const params = useParams() + const detailId = params.id const { basicInfoSelected, roofInfoSelected, reset } = useSurveySaleTabState() @@ -18,14 +21,37 @@ export default function NavTab() { return () => { reset() } - }, []) + }, [reset]) + + if (pathname === '/survey-sale') { + return null + } const handleBasicInfoClick = () => { - router.push('/survey-sale/basic-info') + if (id) { + router.push(`/survey-sale/basic-info?id=${id}`) + return + } + if (detailId) { + router.push(`/survey-sale/${detailId}?tab=basic-info`) + return + } } const handleRoofInfoClick = () => { - router.push('/survey-sale/roof-info') + if (id) { + router.push(`/survey-sale/roof-info?id=${id}`) + return + } + if (detailId) { + router.push(`/survey-sale/${detailId}?tab=roof-info`) + return + } + if (pathname === '/survey-sale/basic-info') { + // TODO: 팝업 추가 + alert('Save essential information first') + return null + } } return ( diff --git a/src/components/survey-sale/detail/DataTable.tsx b/src/components/survey-sale/detail/DataTable.tsx index 0c48ece..21873c7 100644 --- a/src/components/survey-sale/detail/DataTable.tsx +++ b/src/components/survey-sale/detail/DataTable.tsx @@ -2,12 +2,21 @@ import { useServey } from '@/hooks/useSurvey' import { useParams } from 'next/navigation' +import { useEffect } from 'react' +import { useState } from 'react' export default function DataTable() { const params = useParams() const id = params.id const { surveyDetail, isLoadingSurveyDetail } = useServey(Number(id)) + const [isTemporary, setIsTemporary] = useState(false) + + useEffect(() => { + if (!surveyDetail?.representative || !surveyDetail?.store || !surveyDetail?.construction_point) { + setIsTemporary(true) + } + }, [surveyDetail]) if (isLoadingSurveyDetail) { return
Loading...
@@ -24,7 +33,13 @@ export default function DataTable() { 登録番号 - {surveyDetail?.id} + {isTemporary ? ( + + 仮保存 + + ) : ( + {surveyDetail?.id} + )} 登録日 @@ -39,7 +54,7 @@ export default function DataTable() { {surveyDetail?.submission_status && surveyDetail?.submission_date ? new Date(surveyDetail.submission_date).toLocaleString() - : '未提出'} + : '-'} diff --git a/src/components/survey-sale/detail/DetailForm.tsx b/src/components/survey-sale/detail/DetailForm.tsx index 2fed5f7..9ea07bf 100644 --- a/src/components/survey-sale/detail/DetailForm.tsx +++ b/src/components/survey-sale/detail/DetailForm.tsx @@ -1,15 +1,37 @@ 'use client' import { useServey } from '@/hooks/useSurvey' -import { useParams, useRouter } from 'next/navigation' +import { useSurveySaleTabState } from '@/store/surveySaleTabState' +import { useEffect, useState } from 'react' +import { useParams, useRouter, useSearchParams } from 'next/navigation' export default function DetailForm() { const router = useRouter() const params = useParams() const id = params.id - const { surveyDetail, deleteSurvey, submitSurvey, isLoadingSurveyDetail } = useServey(Number(id)) + const searchParams = useSearchParams() + const tab = searchParams.get('tab') + const { surveyDetail, deleteSurvey, submitSurvey, isLoadingSurveyDetail } = useServey(Number(id)) + const { setBasicInfoSelected, setRoofInfoSelected } = useSurveySaleTabState() + const [isTemporary, setIsTemporary] = useState(true) + + // TODO: 조사매물 지붕정보 퍼블 구현되면 탭 화면 변경 추가 + useEffect(() => { + if (tab === 'basic-info') { + setBasicInfoSelected() + } + if (tab === 'roof-info') { + setRoofInfoSelected() + } + if (surveyDetail?.representative && surveyDetail?.store && surveyDetail?.construction_point) { + setIsTemporary(false) + } + }, [tab, setBasicInfoSelected, setRoofInfoSelected, surveyDetail]) + + console.log('isTemporary', isTemporary) + console.log('surveyDetail', surveyDetail) if (isLoadingSurveyDetail) { return
Loading...
} @@ -68,16 +90,22 @@ export default function DetailForm() {
-
- -
-
- -
+ {isTemporary ? ( + <> + ) : ( + <> +
+ +
+
+ +
+ + )}
-
-
- -
-
- -
-
- - - ) -} diff --git a/src/components/survey-sale/detail/BasicForm.tsx b/src/components/survey-sale/detail/form/BasicForm.tsx similarity index 82% rename from src/components/survey-sale/detail/BasicForm.tsx rename to src/components/survey-sale/detail/form/BasicForm.tsx index 8eba9e3..c60f2d6 100644 --- a/src/components/survey-sale/detail/BasicForm.tsx +++ b/src/components/survey-sale/detail/form/BasicForm.tsx @@ -20,10 +20,14 @@ const defaultBasicInfoForm: SurveyBasicRequest = { submission_date: null, } +const REQUIRED_FIELDS: (keyof SurveyBasicRequest)[] = ['representative', 'store', 'construction_point'] + export default function BasicForm() { const searchParams = useSearchParams() const id = searchParams.get('id') + const router = useRouter() + const { setBasicInfoSelected } = useSurveySaleTabState() const { surveyDetail, createSurvey, isCreatingSurvey, updateSurvey, isUpdatingSurvey } = useServey(Number(id)) const [basicInfoData, setBasicInfoData] = useState(defaultBasicInfoForm) @@ -35,31 +39,53 @@ export default function BasicForm() { } }, [surveyDetail]) + useEffect(() => { + setBasicInfoSelected() + }, []) + + const focusInput = (input: keyof SurveyBasicRequest) => { + const inputElement = document.getElementById(input) + if (inputElement) { + inputElement.focus() + } + } + + const validateSurvey = (basicInfoData: SurveyBasicRequest) => { + const emptyField = REQUIRED_FIELDS.find((field) => !basicInfoData[field]) + + if (emptyField) { + focusInput(emptyField) + return false + } + return true + } + const handleChange = (key: keyof SurveyBasicRequest, value: string) => { setBasicInfoData({ ...basicInfoData, [key]: value }) } - const router = useRouter() - const handleSave = () => { + const handleSave = async (isTemporary: boolean) => { if (id) { - console.log('basicInfoData:: ', basicInfoData) updateSurvey(basicInfoData) - } else { - createSurvey(basicInfoData) + router.push(`/survey-sale/${id}?tab=basic-info`) + } + if (isTemporary) { + const saveId = await createSurvey(basicInfoData) + alert('save success temporary id: ' + saveId) + router.push(`/survey-sale/${saveId}?tab=basic-info`) + } else { + if (validateSurvey(basicInfoData)) { + const saveId = await createSurvey(basicInfoData) + alert('save success id: ' + saveId) + router.push(`/survey-sale/${saveId}?tab=basic-info`) + } } - router.push('/survey-sale') } if (isCreatingSurvey || isUpdatingSurvey) { return
Loading...
} - const { setBasicInfoSelected } = useSurveySaleTabState() - - useEffect(() => { - setBasicInfoSelected() - }, []) - return ( <>
@@ -72,6 +98,7 @@ export default function BasicForm() { id="representative" value={basicInfoData.representative} onChange={(e) => handleChange('representative', e.target.value)} + required />
@@ -82,6 +109,7 @@ export default function BasicForm() { id="store" value={basicInfoData.store ?? ''} onChange={(e) => handleChange('store', e.target.value)} + required />
@@ -92,6 +120,7 @@ export default function BasicForm() { id="construction_point" value={basicInfoData.construction_point ?? ''} onChange={(e) => handleChange('construction_point', e.target.value)} + required />
@@ -101,7 +130,6 @@ export default function BasicForm() {
現地調査日
- {/* TODO: 달력 라이브러리 추가 ?? */}
@@ -182,12 +210,12 @@ export default function BasicForm() {
-
-
diff --git a/src/components/survey-sale/detail/form/MultiCheckEtc.tsx b/src/components/survey-sale/detail/form/MultiCheckEtc.tsx new file mode 100644 index 0000000..d901df0 --- /dev/null +++ b/src/components/survey-sale/detail/form/MultiCheckEtc.tsx @@ -0,0 +1,141 @@ +import { SurveyDetailRequest } from '@/types/Survey' +import { useState } from 'react' + +const supplementary_facilities = [ + { id: 1, name: 'エコキュート' }, //에코큐트 + { id: 2, name: 'エネパーム' }, //에네팜 + { id: 3, name: '蓄電池システム' }, //축전지시스템 + { id: 4, name: '太陽光発電' }, //태양광발전 +] + +const roof_material = [ + { id: 1, name: 'スレート' }, //슬레이트 + { id: 2, name: 'アスファルトシングル' }, //아스팔트 싱글 + { id: 3, name: '瓦' }, //기와 + { id: 4, name: '金属屋根' }, //금속지붕 +] + +export default function MultiCheckbox({ + column, + setDetailInfoData, + detailInfoData, +}: { + column: string + setDetailInfoData: (data: any) => void + detailInfoData: SurveyDetailRequest +}) { + const selectList = column === 'supplementary_facilities' ? supplementary_facilities : roof_material + + const [isOtherChecked, setIsOtherChecked] = useState(false) + const [otherValue, setOtherValue] = useState('') + + const handleCheckbox = (dataIndex: number) => { + const value = String(detailInfoData[column as keyof SurveyDetailRequest] ?? '') + .split(',') + .map((v) => v.trim()) + .filter((v) => v.length > 0) + + let newValue: string[] + if (value.includes(String(dataIndex))) { + // 체크 해제 + newValue = value.filter((v) => v !== String(dataIndex)) + } else { + // 체크 + if (column === 'roof_material') { + // 기타가 체크되어 있는지 확인 + const isOtherSelected = isOtherChecked + // 현재 선택된 항목 수 + 기타 선택 여부 + const totalSelected = value.length + (isOtherSelected ? 1 : 0) + + if (totalSelected >= 2) { + alert('屋根材は最大2個まで選択可能です。') + return + } + } + newValue = [...value, String(dataIndex)] + } + + setDetailInfoData({ + ...detailInfoData, + [column]: newValue.join(', '), + }) + } + + const handleOtherCheckbox = () => { + if (column === 'roof_material') { + const value = String(detailInfoData[column as keyof SurveyDetailRequest] ?? '') + .split(',') + .map((v) => v.trim()) + .filter((v) => v.length > 0) + + // 현재 선택된 항목 수 + const currentSelected = value.length + + if (!isOtherChecked && currentSelected >= 2) { + alert('Up to two roofing materials can be selected.') + return + } + } + + setIsOtherChecked(!isOtherChecked) + if (!isOtherChecked) { + setOtherValue('') + setDetailInfoData({ + ...detailInfoData, + [`${column}_etc`]: null, + }) + } else { + setOtherValue('') + } + } + + const handleOtherInputChange = (e: React.ChangeEvent) => { + const value = e.target.value + setOtherValue(value) + setDetailInfoData({ + ...detailInfoData, + [`${column}_etc`]: value, + }) + } + + return ( + <> + {column === 'supplementary_facilities' ? ( + <> +
+ 電気袋設備※複数選択可能 +
+ + ) : ( + <> +
+ 屋根材※最大2個まで選択可能 +
+ + )} +
+ {selectList.map((item) => ( +
+ v.trim()) + .includes(String(item.id))} + onChange={() => handleCheckbox(item.id)} + /> + +
+ ))} +
+ + +
+
+
+ +
+ + ) +} diff --git a/src/components/survey-sale/detail/form/MultiCheckbox.tsx b/src/components/survey-sale/detail/form/MultiCheckbox.tsx deleted file mode 100644 index 7abfca9..0000000 --- a/src/components/survey-sale/detail/form/MultiCheckbox.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { SurveyDetailRequest } from '@/types/Survey' -import { useState } from 'react' - -const supplementary_facilities = [ - { id: 1, name: 'エコキュート' }, - { id: 2, name: 'エネパーム' }, - { id: 3, name: '蓄電池システム' }, - { id: 4, name: '太陽光発電' }, -] - -const roof_material = [ - { id: 1, name: 'スレート' }, - { id: 2, name: 'アスファルトシングル' }, - { id: 3, name: '瓦' }, - { id: 4, name: '金属屋根' }, -] - -export default function MultiCheckbox({ - column, - setDetailInfoData, - detailInfoData, -}: { - column: string - setDetailInfoData: (data: any) => void - detailInfoData: SurveyDetailRequest -}) { - const selectList = column === 'supplementary_facilities' ? supplementary_facilities : roof_material - - const [isOtherChecked, setIsOtherChecked] = useState(false) - - const handleCheckbox = (dataName: string) => { - const value = column === 'supplementary_facilities' ? detailInfoData.supplementary_facilities : detailInfoData.roof_material - setDetailInfoData({ - ...detailInfoData, - [column]: `${value}, ${dataName}`, - }) - } - - return ( - <> - {column === 'supplementary_facilities' ? ( - <> -
- 電気袋設備※複数選択可能 -
- - ) : ( - <> -
- 屋根材※最大2個まで選択可能 -
- - )} -
- {selectList.map((item) => ( -
- v.trim()) - .includes(item.name) - } - onChange={() => handleCheckbox(item.name)} - /> - -
- ))} -
- - -
-
-
- -
- - ) -} diff --git a/src/components/survey-sale/detail/form/RadioEtc.tsx b/src/components/survey-sale/detail/form/RadioEtc.tsx new file mode 100644 index 0000000..427995c --- /dev/null +++ b/src/components/survey-sale/detail/form/RadioEtc.tsx @@ -0,0 +1,128 @@ +'use client' +import { useState } from 'react' +import { SurveyDetailRequest } from '@/types/Survey' + +type RadioEtcKeys = 'house_structure' | 'rafter_material' | 'waterproof_material' | 'insulation_presence' + +const translateJapanese: Record = { + house_structure: '住宅構造', + rafter_material: '垂木材質', + waterproof_material: '防水材の種類', + insulation_presence: '断熱材の有無', +} + +const radioEtcData: Record = { + house_structure: [ + { + id: 1, + label: '木製', + }, + ], + rafter_material: [ + { + id: 1, + label: '木製', + }, + { + id: 2, + label: '強制', + }, + ], + waterproof_material: [ + { + id: 1, + label: 'アスファルト屋根940(22kg以上)', + }, + ], + insulation_presence: [ + { + id: 1, + label: 'なし', + }, + { + id: 2, + label: 'あり', + }, + ], +} + +export default function RadioEtc({ + column, + setDetailInfoData, + detailInfoData, +}: { + column: RadioEtcKeys + setDetailInfoData: (data: any) => void + detailInfoData: SurveyDetailRequest +}) { + const [isEtcSelected, setIsEtcSelected] = useState(false) + const [etcValue, setEtcValue] = useState('') + + const handleRadioChange = (e: React.ChangeEvent) => { + const value = e.target.value + if (column === 'insulation_presence') { + setIsEtcSelected(value === '2') + setDetailInfoData({ + ...detailInfoData, + [column]: Number(value), + }) + } else if (value === 'etc') { + setIsEtcSelected(true) + setDetailInfoData({ + ...detailInfoData, + [column]: null, + }) + } else { + setIsEtcSelected(false) + setEtcValue('') + setDetailInfoData({ + ...detailInfoData, + [column]: Number(value), + [`${column}_etc`]: null, + }) + } + } + + const handleEtcInputChange = (e: React.ChangeEvent) => { + const value = e.target.value + setEtcValue(value) + setDetailInfoData({ + ...detailInfoData, + [`${column}_etc`]: value, + }) + } + + return ( +
+
{translateJapanese[column]}
+ {radioEtcData[column].map((item) => ( +
+ + +
+ ))} + {column !== 'insulation_presence' && ( +
+ + +
+ )} +
+ +
+
+ ) +} diff --git a/src/components/survey-sale/detail/form/RoofInfoForm.tsx b/src/components/survey-sale/detail/form/RoofInfoForm.tsx new file mode 100644 index 0000000..f535af0 --- /dev/null +++ b/src/components/survey-sale/detail/form/RoofInfoForm.tsx @@ -0,0 +1,321 @@ +'use client' + +import { useSurveySaleTabState } from '@/store/surveySaleTabState' + +import { useServey } from '@/hooks/useSurvey' +import { SurveyDetailRequest } from '@/types/Survey' +import { useRouter, useSearchParams } from 'next/navigation' +import { useEffect, useState } from 'react' +import MultiCheckEtc from './MultiCheckEtc' +import SelectBoxEtc from './SelectBoxEtc' +import RadioEtc from './RadioEtc' + +const defaultDetailInfoForm: SurveyDetailRequest = { + contract_capacity: null, + retail_company: null, + supplementary_facilities: null, + supplementary_facilities_etc: null, + installation_system: null, + installation_system_etc: null, + construction_year: null, + construction_year_etc: null, + roof_material: null, + roof_material_etc: null, + roof_shape: null, + roof_shape_etc: null, + roof_slope: null, + house_structure: 1, + house_structure_etc: null, + rafter_material: 1, + rafter_material_etc: null, + rafter_size: null, + rafter_size_etc: null, + rafter_pitch: null, + rafter_pitch_etc: null, + rafter_direction: 1, + open_field_plate_kind: null, + open_field_plate_kind_etc: null, + open_field_plate_thickness: null, + leak_trace: false, + waterproof_material: null, + waterproof_material_etc: null, + insulation_presence: 1, + insulation_presence_etc: null, + structure_order: null, + structure_order_etc: null, + installation_availability: null, + installation_availability_etc: null, + memo: null, +} + +export default function RoofInfoForm() { + const { setRoofInfoSelected } = useSurveySaleTabState() + + useEffect(() => { + setRoofInfoSelected() + }, []) + + const router = useRouter() + const searchParams = useSearchParams() + const id = searchParams.get('id') + + const { surveyDetail, createSurveyDetail, validateSurveyDetail } = useServey(Number(id)) + + const [detailInfoData, setDetailInfoData] = useState(defaultDetailInfoForm) + + useEffect(() => { + if (surveyDetail?.detail_info) { + const { id, updated_at, created_at, ...rest } = surveyDetail.detail_info + setDetailInfoData(rest) + } + }, [surveyDetail]) + + const handleNumberInput = (key: keyof SurveyDetailRequest, value: number | string) => { + if (typeof value === 'string') { + const numberValue = value === '' ? null : Number(value) + setDetailInfoData({ ...detailInfoData, [key]: numberValue }) + } else { + setDetailInfoData({ ...detailInfoData, [key]: value }) + } + } + + const handleTextInput = (key: keyof SurveyDetailRequest, value: string) => { + setDetailInfoData({ ...detailInfoData, [key]: value || null }) + } + + const handleBooleanInput = (key: keyof SurveyDetailRequest, value: boolean) => { + setDetailInfoData({ ...detailInfoData, [key]: value }) + } + + const handleUnitInput = (value: string) => { + const numericValue = detailInfoData.contract_capacity?.replace(/[^0-9.]/g, '') || '' + setDetailInfoData({ + ...detailInfoData, + contract_capacity: numericValue ? `${numericValue} ${value}` : value, + }) + } + + // TODO: 조사매물 저장 요구사항 정립 이후 수정 필요 + const handleSave = async () => { + if (id) { + const emptyField = validateSurveyDetail(detailInfoData) + if (emptyField.trim() === '') { + createSurveyDetail({ surveyId: Number(id), surveyDetail: detailInfoData }) + router.push(`/survey-sale`) + } else { + alert(emptyField + ' is required') + focusOnInput(emptyField) + } + } else { + alert('save essential information first') + } + } + const focusOnInput = (field: string) => { + const input = document.getElementById(field) + if (input) { + input.focus() + } + } + return ( + <> +
+
電気関係
+
+
+ {/* 전기계약 용량 - contract_capacity */} +
電気契約容量
+
+ handleTextInput('contract_capacity', e.target.value)} + /> +
+
+ +
+
+ {/* 전기 소매 회사 - retail_company */} +
+
電気小売会社
+ handleTextInput('retail_company', e.target.value)} + /> +
+ {/* 전기 부대 설비 - supplementary_facilities */} +
+ +
+ {/* 설치 희망 시스템 - installation_system */} + +
+
+ +
+
屋根関係
+
+ {/* 건축 연수 - construction_year */} + + {/* 지붕재 - roof_material */} +
+ +
+ {/* 지붕 모양 - roof_shape */} + + {/* 지붕 경사도 - roof_slope */} +
+
屋根の斜面
+
+ handleTextInput('roof_slope', e.target.value)} + /> + +
+
+ {/* 주택 구조 - house_structure */} + + {/* 서까래 재질 - rafter_material */} + + {/* 서까래 크기 - rafter_size */} + + {/* 서까래 피치 - rafter_pitch */} + + {/* 서까래 방향 - rafter_direction */} +
+
垂木の方向
+
+
+ handleNumberInput('rafter_direction', e.target.value)} + checked={detailInfoData.rafter_direction === 1} + /> + +
+
+ handleNumberInput('rafter_direction', e.target.value)} + checked={detailInfoData.rafter_direction === 2} + /> + +
+
+
+ {/* 노지판 종류 - open_field_plate_kind */} + + {/* 노지판 두께 - open_field_plate_thickness */} +
+
+ 路地板厚※小幅板を選択した場合, 厚さ. 小幅板間の間隔寸法を記載 +
+
+ handleTextInput('open_field_plate_thickness', e.target.value)} + /> + mm +
+
+ {/* 누수 흔적 - leak_trace */} +
+
水漏れの痕跡
+
+
+ handleBooleanInput('leak_trace', true)} + /> + +
+
+ handleBooleanInput('leak_trace', false)} + /> + +
+
+
+ {/* 방수재 종류 - waterproof_material */} + + {/* 단열재 유무 - insulation_presence */} + + {/* 노지판 종류 - open_field_plate_kind */} + + {/* 설치 가능 여부 - installation_availability */} + + {/* 메모 - memo */} +
+
メモ
+
+ +
+
+ +
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+ + ) +} diff --git a/src/components/survey-sale/detail/form/SelectBoxEtc.tsx b/src/components/survey-sale/detail/form/SelectBoxEtc.tsx new file mode 100644 index 0000000..c813ffb --- /dev/null +++ b/src/components/survey-sale/detail/form/SelectBoxEtc.tsx @@ -0,0 +1,234 @@ +import type { SurveyDetailRequest } from '@/types/Survey' +import { useEffect, useState } from 'react' + +type SelectBoxKeys = + | 'installation_system' + | 'construction_year' + | 'roof_shape' + | 'rafter_pitch' + | 'rafter_size' + | 'open_field_plate_kind' + | 'structure_order' + | 'installation_availability' + +const font: Record = { + installation_system: 'data-input-form-tit red-f', + construction_year: 'data-input-form-tit red-f', + roof_shape: 'data-input-form-tit', + rafter_pitch: 'data-input-form-tit red-f', + rafter_size: 'data-input-form-tit red-f', + open_field_plate_kind: 'data-input-form-tit', + structure_order: 'data-input-form-tit red-f', + installation_availability: 'data-input-form-tit', +} + +const translateJapanese: Record = { + installation_system: '設置希望システム', + construction_year: '建築年数', + roof_shape: '屋根の形状', + rafter_pitch: '垂木傾斜', + rafter_size: '垂木サイズ', + open_field_plate_kind: '路地板の種類', + structure_order: '屋根構造の順序', + installation_availability: '屋根製品名 設置可否確認', +} + +const selectBoxOptions: Record = { + installation_system: [ + { + id: 1, + name: '太陽光発電', //태양광발전 + }, + { + id: 2, + name: 'ハイブリッド蓄電システム', //하이브리드축전지시스템 + }, + { + id: 3, + name: '蓄電池システム', //축전지시스템 + }, + ], + construction_year: [ + { + id: 1, + name: '新築', //신축 + }, + { + id: 2, + name: '既築', //기존 + }, + ], + roof_shape: [ + { + id: 1, + name: '切妻', //박공지붕 + }, + { + id: 2, + name: '寄棟', //기동 + }, + { + id: 3, + name: '片流れ', //한쪽흐름 + }, + ], + rafter_size: [ + { + id: 1, + name: '幅35mm以上×高さ48mm以上', + }, + { + id: 2, + name: '幅36mm以上×高さ46mm以上', + }, + { + id: 3, + name: '幅37mm以上×高さ43mm以上', + }, + { + id: 4, + name: '幅38mm以上×高さ40mm以上', + }, + ], + rafter_pitch: [ + { + id: 1, + name: '(455mm以下', + }, + { + id: 2, + name: '500mm以下', + }, + { + id: 3, + name: '606mm以下', + }, + ], + open_field_plate_kind: [ + { + id: 1, + name: '構造用合板', //구조용합판 + }, + { + id: 2, + name: 'OSB', //OSB + }, + { + id: 3, + name: 'パーティクルボード', //파티클보드 + }, + { + id: 4, + name: '小幅板', //소판 + }, + ], + structure_order: [ + { + id: 1, + name: '屋根材', //지붕재 + }, + { + id: 2, + name: '防水材', //방수재 + }, + { + id: 3, + name: '屋根の基礎', //지붕의기초 + }, + { + id: 4, + name: '垂木', //서까래 + }, + ], + installation_availability: [ + { + id: 1, + name: '確認済み', //확인완료 + }, + { + id: 2, + name: '未確認', //미확인 + }, + ], +} + +export default function SelectBoxForm({ + column, + setDetailInfoData, + detailInfoData, +}: { + column: SelectBoxKeys + setDetailInfoData: (data: any) => void + detailInfoData: SurveyDetailRequest +}) { + const [isEtcSelected, setIsEtcSelected] = useState(false) + const [etcValue, setEtcValue] = useState('') + + useEffect(() => { + setEtcValue(detailInfoData[`${column}_etc`] ?? '') + }, [detailInfoData[`${column}_etc`]]) + + const handleSelectChange = (e: React.ChangeEvent) => { + const value = e.target.value + if (column === 'installation_availability' || column === 'construction_year') { + setIsEtcSelected(value === '2') // 既築(2) 또는 未確認(2) 선택 시 input 활성화 + setDetailInfoData({ + ...detailInfoData, + [column]: Number(value), + }) + } else if (value === 'etc') { + setIsEtcSelected(true) + setDetailInfoData({ + ...detailInfoData, + [column]: null, + }) + } else { + setIsEtcSelected(false) + setEtcValue('') + setDetailInfoData({ + ...detailInfoData, + [`${column}_etc`]: null, + [column]: Number(value), + }) + } + } + + const handleEtcInputChange = (e: React.ChangeEvent) => { + const value = e.target.value + setEtcValue(value) + setDetailInfoData({ + ...detailInfoData, + [`${column}_etc`]: value, + }) + } + + return ( + <> +
+
{translateJapanese[column]}
+
+ +
+
+ +
+
+ + ) +} diff --git a/src/hooks/useSurvey.ts b/src/hooks/useSurvey.ts index 1e56750..00517b7 100644 --- a/src/hooks/useSurvey.ts +++ b/src/hooks/useSurvey.ts @@ -15,6 +15,7 @@ export function useServey(id?: number): { updateSurvey: (survey: SurveyBasicRequest) => void deleteSurvey: () => Promise submitSurvey: () => void + validateSurveyDetail: (surveyDetail: SurveyDetailRequest) => string } { const queryClient = useQueryClient() @@ -97,6 +98,38 @@ export function useServey(id?: number): { }, }) + const validateSurveyDetail = (surveyDetail: SurveyDetailRequest) => { + const requiredFields = [ + 'installation_system', + 'construction_year', + 'rafter_size', + 'rafter_pitch', + 'rafter_direction', + 'waterproof_material', + 'insulation_presence', + 'structure_order', + ] as const; + + const etcFields = [ + 'installation_system', + 'construction_year', + 'rafter_size', + 'rafter_pitch', + 'waterproof_material', + 'structure_order', + ] as const; + + const emptyField = requiredFields.find((field) => { + if (etcFields.includes(field as (typeof etcFields)[number])) { + return surveyDetail[field as keyof SurveyDetailRequest] === null && + surveyDetail[`${field}_etc` as keyof SurveyDetailRequest] === null; + } + return surveyDetail[field as keyof SurveyDetailRequest] === null; + }); + + return emptyField || ''; + }; + return { surveyList: surveyList || [], surveyDetail: surveyDetail || null, @@ -110,5 +143,6 @@ export function useServey(id?: number): { deleteSurvey, createSurveyDetail, submitSurvey, + validateSurveyDetail, } } diff --git a/src/types/Survey.ts b/src/types/Survey.ts index 39dcc01..3d08fb5 100644 --- a/src/types/Survey.ts +++ b/src/types/Survey.ts @@ -20,13 +20,13 @@ export type SurveyDetailInfo = { id: number contract_capacity: string | null retail_company: string | null - supplementary_facilities: number | null + supplementary_facilities: string | null // number 배열 supplementary_facilities_etc: string | null installation_system: number | null installation_system_etc: string | null construction_year: number | null construction_year_etc: string | null - roof_material: number | null + roof_material: string | null // number 배열 roof_material_etc: string | null roof_shape: number | null roof_shape_etc: string | null @@ -74,13 +74,13 @@ export type SurveyBasicRequest = { export type SurveyDetailRequest = { contract_capacity: string | null retail_company: string | null - supplementary_facilities: number | null + supplementary_facilities: string | null // number 배열 supplementary_facilities_etc: string | null installation_system: number | null installation_system_etc: string | null construction_year: number | null construction_year_etc: string | null - roof_material: number | null + roof_material: string | null // number 배열 roof_material_etc: string | null roof_shape: number | null roof_shape_etc: string | null