feat: 조사매물 내 공통코드 동기화를 위한 surveyOptionStore 추가

This commit is contained in:
Daseul Kim 2025-08-05 17:21:43 +09:00
parent 7e2d93426f
commit b571197ffc
3 changed files with 267 additions and 16 deletions

View File

@ -1,7 +1,8 @@
import { useState } from 'react'
import { useEffect, useState } from 'react'
import type { Mode, SurveyDetailInfo, SurveyDetailRequest } from '@/types/Survey'
import { useAlertMsg, WARNING_MESSAGE } from '@/hooks/useAlertMsg'
import { radioEtcData, selectBoxOptions, supplementaryFacilities, roofMaterial } from '@/types/Survey'
import { radioEtcData, supplementaryFacilities } from '@/types/Survey'
import { useSurveyOptionStore } from '@/store/surveyOptionStore'
const makeNumArr = (value: string) => {
return value
@ -292,10 +293,15 @@ const SelectedBox = ({
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
@ -509,10 +515,15 @@ const MultiCheck = ({
setRoofInfo: (roofInfo: SurveyDetailRequest) => void
}) => {
const { showErrorAlert } = useAlertMsg()
const { roofMaterial, initialized, loading, loadOptions } = useSurveyOptionStore()
const multiCheckData = column === 'supplementaryFacilities' ? supplementaryFacilities : roofMaterial
const etcValue = roofInfo?.[`${column}Etc` as keyof SurveyDetailInfo]
const [isOtherCheck, setIsOtherCheck] = useState<boolean>(Boolean(etcValue))
useEffect(() => {
if (!initialized && !loading) loadOptions()
}, [initialized, loading])
const isRoofMaterial = column === 'roofMaterial'
const selectedValues = makeNumArr(String(roofInfo[column as keyof SurveyDetailInfo] ?? ''))

View File

@ -0,0 +1,198 @@
import { create } from 'zustand'
import { axiosInstance } from '@/libs/axios'
import { selectBoxOptions as defaultSelectBoxOptions, roofMaterial as defaultRoofMaterial, type SelectBoxKeys } from '@/types/Survey'
import { type CommCode } from '@/types/CommCode'
export const SURVEY_OPTION_HEAD_ID: { [key: string]: string } = {
/* 지붕재 종류 : HEAD_ID = 'ROOF_MATL' (HEAD_CD = 114200) */
roofMaterial: 'ROOF_MATL',
/* 건축 연수 : HEAD_ID = 'BUILDING' (HEAD_CD = 114000) */
constructionYear: 'BUILDING',
/* 서까래 피치 : HEAD_ID = 'RAFT_BASE_CD' (HEAD_CD = 203800) */
rafterPitch: 'RAFT_BASE_CD',
/* 골목판 종류 : HEAD_ID = 'ROOF_BOARD' (HEAD_CD = 114600) */
openFieldPlateKind: 'ROOF_BOARD',
}
/*
* 2025-08-04
*
* 목록 : SURVEY_OPTION_HEAD_ID -> roofMaterial, constructionYear, rafterPitch, openFieldPlateKind
* 제약사항 : BC_COMM_L.CODE code값 . null .
* 설명 : 공통코드 code값을 code_jp(name) .
* code가 null인 name값 (= , )
*
* comm_cd : {
* // 지붕재 종류 : HEAD_ID = 'ROOF_MATL' (HEAD_CD = 114200)
* roofMaterial : [
* // 슬레이트
* {
* head_cd: '114200',
* code: '7',
* code_jp: 'スレート',
* },
* // 아스팔트 싱글
* {
* head_cd: '114200',
* code: '8',
* code_jp: 'アスファルトシングル',
* },
* // 기와
* {
* head_cd: '114200',
* code: null,
* code_jp: '瓦',
* },
* // 금속지붕
* {
* head_cd: '114200',
* code: null,
* code_jp: '金属屋根',
* },
* ],
* // 건축 연수 : HEAD_ID = 'BUILDING' (HEAD_CD = 114000)
* constructionYear : [
* // 신축
* {
* head_cd: '114000',
* code: 'N',
* code_jp: '新築',
* },
* // 기축
* {
* head_cd: '114000',
* code: 'O',
* code_jp: '既築',
* },
* ],
* // 서까래 피치 : HEAD_ID = 'RAFT_BASE_CD' (HEAD_CD = 203800)
* rafterPitch : [
* // 455mm 이하
* {
* head_cd: '203800',
* code: 'HEI_455',
* code_jp: '455mm以下',
* },
* // 500mm 이하
* {
* head_cd: '203800',
* code: 'HEI_500',
* code_jp: '500mm以下',
* },
* // 606mm 이하
* {
* head_cd: '203800',
* code: 'HEI_606',
* code_jp: '606mm以下',
* },
* ],
* // 골목판 종류 : HEAD_ID = 'ROOF_BOARD' (HEAD_CD = 114600)
* openFieldPlateKind : [
* // 구조용합판
* {
* head_cd: '114600',
* code: null,
* code_jp: '構造用合板',
* },
* // OSB
* {
* head_cd: '114600',
* code: 'O',
* code_jp: 'OSB',
* },
* // 파티클보드
* {
* head_cd: '114600',
* code: 'A',
* code_jp: 'パーティクルボード',
* },
* // 소판
* {
* head_cd: '114600',
* code: 'S',
* code_jp: '小幅板',
* },
* ]
* }
*/
interface SurveyOptionState {
/* 지붕재종류 데이터 */
roofMaterial: typeof defaultRoofMaterial
/* 셀렉트박스 옵션 데이터 */
selectBoxOptions: typeof defaultSelectBoxOptions
/* 로딩 여부 */
loading: boolean
/* 옵션 로드 완료 여부 */
initialized: boolean
/* 옵션 로드 */
loadOptions: () => Promise<void>
}
export const useSurveyOptionStore = create<SurveyOptionState>((set, get) => ({
roofMaterial: defaultRoofMaterial,
selectBoxOptions: defaultSelectBoxOptions,
loading: false,
initialized: false,
loadOptions: async () => {
if (get().initialized || get().loading) return
set({ loading: true })
try {
const promises = Object.entries(SURVEY_OPTION_HEAD_ID).map(async ([optionKey, headId]) => {
try {
const response = await axiosInstance(process.env.NEXT_PUBLIC_API_URL).get<CommCode[]>('/api/comm-code', {
params: { headId: headId },
})
const commCodeData = response.data
return { optionKey, commCodeData }
} catch (error) {
console.error(`${optionKey} 로드 실패:`, error)
return { optionKey, commCodeData: [] }
}
})
const results: { optionKey: string; commCodeData: CommCode[] }[] = await Promise.all(promises)
set((prev) => {
const newState = {
roofMaterial: [...prev.roofMaterial],
selectBoxOptions: { ...prev.selectBoxOptions },
}
results.forEach(({ optionKey, commCodeData }) => {
if (optionKey === 'roofMaterial') {
// 지붕재 종류 업데이트
newState.roofMaterial = newState.roofMaterial.map((item) => {
if (item.code) {
const commCode = commCodeData.find((c) => c.code === item.code)
if (commCode) {
return { ...item, code: commCode.code, name: commCode.codeJp }
}
}
return item
})
} else {
// 셀렉트박스 옵션 업데이트
const key = optionKey as SelectBoxKeys
newState.selectBoxOptions[key] = newState.selectBoxOptions[key].map((item) => {
if (item.code) {
const commCode = commCodeData.find((c) => c.code === item.code)
if (commCode) {
return { ...item, code: commCode.code, name: commCode.codeJp }
}
}
return item
})
}
})
return { ...newState, initialized: true, loading: false }
})
} catch (error) {
console.error('옵션 데이터 로드 중 오류 발생:', error)
set({ loading: false })
}
},
}))

View File

@ -332,7 +332,7 @@ type RadioEtcKeys =
| 'insulationPresence'
| 'rafterDirection'
| 'leakTrace'
type SelectBoxKeys =
export type SelectBoxKeys =
| 'installationSystem'
| 'constructionYear'
| 'roofShape'
@ -341,155 +341,187 @@ type SelectBoxKeys =
| 'openFieldPlateKind'
| 'installationAvailability'
export const supplementaryFacilities = [
export const supplementaryFacilities: { id: number; code: string | null; name: string }[] = [
/** 에코큐트 */
{ id: 1, name: 'エコキュート' },
{ id: 1, code: null, name: 'エコキュート' },
/** 에네팜 */
{ id: 2, name: 'エネパーム' },
{ id: 2, code: null, name: 'エネパーム' },
/** 축전지시스템 */
{ id: 3, name: '蓄電池システム' },
{ id: 3, code: null, name: '蓄電池システム' },
/** 태양광발전 */
{ id: 4, name: '太陽光発電' },
{ id: 4, code: null, name: '太陽光発電' },
]
export const roofMaterial = [
// 지붕재 종류 : HEAD_ID = 'ROOF_MATL' (114200)
export const roofMaterial: { id: number; code: string | null; name: string }[] = [
/** 슬레이트 */
{ id: 1, name: 'スレート' },
{ id: 1, code: '7', name: 'スレート' },
/** 아스팔트 싱글 */
{ id: 2, name: 'アスファルトシングル' },
{ id: 2, code: '8', name: 'アスファルトシングル' },
/** 기와 */
{ id: 3, name: '瓦' },
{ id: 3, code: null, name: '瓦' },
/** 금속지붕 */
{ id: 4, name: '金属屋根' },
{ id: 4, code: null, name: '金属屋根' },
]
export const selectBoxOptions: Record<SelectBoxKeys, { id: number; name: string }[]> = {
export const selectBoxOptions: Record<SelectBoxKeys, { id: number; code: string | null; name: string }[]> = {
installationSystem: [
{
/** 태양광발전 */
id: 1,
code: null,
name: '太陽光発電',
},
{
/** 하이브리드축전지시스템 */
id: 2,
code: null,
name: 'ハイブリッド蓄電システム',
},
{
/** 축전지시스템 */
id: 3,
code: null,
name: '蓄電池システム',
},
],
// 건축 연수 : HEAD_ID = 'BUILDING' (114000)
constructionYear: [
{
/** 신축 */
id: 1,
code: 'N',
name: '新築',
},
{
/** 기축 */
id: 2,
code: 'O',
name: '既築',
},
],
roofShape: [
{
/** 박공지붕 */
id: 1,
code: null,
name: '切妻',
},
{
/** 기동 */
id: 2,
code: null,
name: '寄棟',
},
{
/** 한쪽흐름 */
id: 3,
code: null,
name: '片流れ',
},
],
rafterSize: [
{
/** 35mm 이상×48mm 이상 */
id: 1,
code: null,
name: '幅35mm以上×高さ48mm以上',
},
{
/** 36mm 이상×46mm 이상 */
id: 2,
code: null,
name: '幅36mm以上×高さ46mm以上',
},
{
/** 37mm 이상×43mm 이상 */
id: 3,
code: null,
name: '幅37mm以上×高さ43mm以上',
},
{
/** 38mm 이상×40mm 이상 */
id: 4,
code: null,
name: '幅38mm以上×高さ40mm以上',
},
],
// 서까래 피치 : HEAD_ID = 'RAFT_BASE_CD' (203800)
rafterPitch: [
{
/** 455mm 이하 */
id: 1,
code: 'HEI_455',
name: '455mm以下',
},
{
/** 500mm 이하 */
id: 2,
code: 'HEI_500',
name: '500mm以下',
},
{
/** 606mm 이하 */
id: 3,
code: 'HEI_606',
name: '606mm以下',
},
],
// 골목판 종류 : HEAD_ID = 'ROOF_BOARD' (114600)
openFieldPlateKind: [
{
/** 구조용합판 */
id: 1,
code: null,
name: '構造用合板',
},
{
/** OSB */
id: 2,
code: 'O',
name: 'OSB',
},
{
/** 파티클보드 */
id: 3,
code: 'A',
name: 'パーティクルボード',
},
{
/** 소판 */
id: 4,
code: 'S',
name: '小幅板',
},
],
installationAvailability: [
{
/** 확인완료 */
id: 1,
code: null,
name: '確認済み',
},
{
/** 미확인 */
id: 2,
code: null,
name: '未確認',
},
],
}
export const radioEtcData: Record<RadioEtcKeys, { id: number; label: string }[]> = {
export const radioEtcData: Record<RadioEtcKeys, { id: number; code: string | null; label: string }[]> = {
structureOrder: [
{
/** 지붕재 - 방수재 - 지붕의기초 - 서까래 */
id: 1,
code: null,
label: '屋根材 > 防水材 > 屋根の基礎 > 垂木',
},
],
@ -497,6 +529,7 @@ export const radioEtcData: Record<RadioEtcKeys, { id: number; label: string }[]>
{
/** 목재 */
id: 1,
code: null,
label: '木製',
},
],
@ -504,11 +537,13 @@ export const radioEtcData: Record<RadioEtcKeys, { id: number; label: string }[]>
{
/** 목재 */
id: 1,
code: null,
label: '木製',
},
{
/** 강재 */
id: 2,
code: null,
label: '強制',
},
],
@ -516,6 +551,7 @@ export const radioEtcData: Record<RadioEtcKeys, { id: number; label: string }[]>
{
/** 아스팔트 지붕 940(22kg 이상) */
id: 1,
code: null,
label: 'アスファルト屋根94022kg以上',
},
],
@ -523,11 +559,13 @@ export const radioEtcData: Record<RadioEtcKeys, { id: number; label: string }[]>
{
/** 없음 */
id: 1,
code: null,
label: 'なし',
},
{
/** 있음 */
id: 2,
code: null,
label: 'あり',
},
],
@ -535,11 +573,13 @@ export const radioEtcData: Record<RadioEtcKeys, { id: number; label: string }[]>
{
/** 수직 */
id: 1,
code: null,
label: '垂直垂木',
},
{
/** 수평 */
id: 2,
code: null,
label: '水平垂木',
},
],
@ -547,12 +587,14 @@ export const radioEtcData: Record<RadioEtcKeys, { id: number; label: string }[]>
{
/** 있음 */
id: 1,
code: null,
label: 'あり',
},
{
/** 없음 */
id: 2,
code: null,
label: 'なし',
},
],
}
}