diff --git a/src/app/api/comm-code/route.ts b/src/app/api/comm-code/route.ts index 71acdd1..d91adf6 100644 --- a/src/app/api/comm-code/route.ts +++ b/src/app/api/comm-code/route.ts @@ -1,15 +1,14 @@ import { NextRequest, NextResponse } from 'next/server' import { loggerWrapper } from '@/libs/api-wrapper' import { prisma } from '@/libs/prisma' -import { CommonCode } from '@/types/Inquiry' +import { CommCode } from '@/types/CommCode' async function getCommCode(request: NextRequest): Promise { try { const searchParams = request.nextUrl.searchParams const headId = searchParams.get('headId') - if (headId === 'QNA_CD') { - return getQnaCd() - } + + if (!headId) return NextResponse.json({ error: '필수 파라미터가 없습니다' }, { status: 400 }) // @ts-ignore const commHeadData = await prisma.BC_COMM_H.findFirst({ @@ -21,15 +20,10 @@ async function getCommCode(request: NextRequest): Promise { }, }) - if (!commHeadData) { - return NextResponse.json({ error: `${headId}를 찾을 수 없습니다` }, { status: 404 }) - } + if (!commHeadData) return NextResponse.json({ error: `${headId}를 찾을 수 없습니다` }, { status: 404 }) - if (headId === 'SALES_OFFICE_CD') { - return getSaleOffice(commHeadData.HEAD_CD) - } // @ts-ignore - const roofMaterials: CommCode[] = await prisma.BC_COMM_L.findMany({ + const results: CommCode[] = await prisma.BC_COMM_L.findMany({ where: { HEAD_CD: commHeadData.HEAD_CD, }, @@ -37,79 +31,17 @@ async function getCommCode(request: NextRequest): Promise { HEAD_CD: true, CODE: true, CODE_JP: true, + REF_CHR1: true, + REF_NUM1: true, }, orderBy: { CODE: 'asc', }, }) - return NextResponse.json(roofMaterials) + return NextResponse.json(results) } catch (error) { console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error) return NextResponse.json({ error: '데이터 조회 중 오류가 발생했습니다' }, { status: 500 }) } } - -const getSaleOffice = async (headCode: string) => { - // @ts-ignore - const commCodeSaleOffice: CommCode[] = await prisma.BC_COMM_L.findMany({ - where: { - HEAD_CD: headCode, - REF_NUM1: 1, - }, - select: { - CODE: true, - CODE_JP: true, - REF_CHR1: true, - REF_NUM1: true, - }, - }) - return NextResponse.json(commCodeSaleOffice) -} - -/** - * @description QNA 공통 코드 조회 - * @returns {CommonCode[]} QNA 공통 코드 목록 - */ -const getQnaCd = async () => { - // @ts-ignore - const headCdList: { HEAD_CD: string; HEAD_ID: string }[] = await prisma.BC_COMM_H.findMany({ - where: { - OR: [{ HEAD_ID: { in: ['QNA_CLS_LRG_CD'] } }, { HEAD_ID: { in: ['QNA_CLS_MID_CD'] } }, { HEAD_ID: { in: ['QNA_CLS_SML_CD'] } }], - }, - select: { - HEAD_CD: true, - HEAD_ID: true, - }, - }) - const result: CommonCode[] = [] - // @ts-ignore - const commCodeQna: CommCode[] = await prisma.BC_COMM_L.findMany({ - where: { - HEAD_CD: { - in: headCdList.map((item) => item.HEAD_CD).filter(Boolean), - }, - }, - select: { - HEAD_CD: true, - CODE: true, - CODE_JP: true, - REF_CHR1: true, - }, - }) - commCodeQna.forEach((item) => { - result.push({ - // @ts-ignore - headCd: item.HEAD_CD, - // @ts-ignore - headId: headCdList.find((headCd) => headCd.HEAD_CD === item.HEAD_CD)?.HEAD_ID ?? '', - // @ts-ignore - code: item.CODE, - // @ts-ignore - name: item.CODE_JP, - // @ts-ignore - refChar1: item.REF_CHR1 ?? '', - }) - }) - return NextResponse.json(result) -} export const GET = loggerWrapper(getCommCode) diff --git a/src/components/inquiry/RegistForm.tsx b/src/components/inquiry/RegistForm.tsx index e633035..3ded646 100644 --- a/src/components/inquiry/RegistForm.tsx +++ b/src/components/inquiry/RegistForm.tsx @@ -2,7 +2,7 @@ import { useInquiry } from '@/hooks/useInquiry' import { useSessionStore } from '@/store/session' -import { InquiryRequest } from '@/types/Inquiry' +import { InquiryCommCode, InquiryRequest } from '@/types/Inquiry' import { useEffect, useState } from 'react' import { useRouter } from 'next/navigation' import { CONFIRM_MESSAGE, SUCCESS_MESSAGE, useAlertMsg, WARNING_MESSAGE } from '@/hooks/useAlertMsg' @@ -152,15 +152,15 @@ export default function RegistForm() { 選択してください {commonCodeList - .filter((code) => code.headId === 'QNA_CLS_LRG_CD') + .filter((code) => code.headCd === InquiryCommCode.QNA_CLS_LRG_CD.headCd) .map((code) => ( ))} - {commonCodeList.filter((code) => code.refChar1 === inquiryRequest.qnaClsLrgCd).length > 0 && inquiryRequest.qnaClsLrgCd && ( + {commonCodeList.filter((code) => code.refChr1 === inquiryRequest.qnaClsLrgCd).length > 0 && inquiryRequest.qnaClsLrgCd && (
)} - {commonCodeList.filter((code) => code.refChar1 === inquiryRequest.qnaClsMidCd).length > 0 && inquiryRequest.qnaClsMidCd && ( + {commonCodeList.filter((code) => code.refChr1 === inquiryRequest.qnaClsMidCd).length > 0 && inquiryRequest.qnaClsMidCd && (
diff --git a/src/components/popup/SurveySaleSubmitPopup.tsx b/src/components/popup/SurveySaleSubmitPopup.tsx index 9104989..71b531b 100644 --- a/src/components/popup/SurveySaleSubmitPopup.tsx +++ b/src/components/popup/SurveySaleSubmitPopup.tsx @@ -63,7 +63,8 @@ export default function SurveySaleSubmitPopup() { /** Admin 제출 폼 데이터 삽입 - 1차 판매점*/ if (session?.role === 'Admin') { getCommCode('SALES_OFFICE_CD').then((codes) => { - setCommCodeList(codes) + const filteredCodes = codes.filter((code) => code.refNum1 === '1') + setCommCodeList(filteredCodes) }) setSubmitData((prev) => ({ ...prev, @@ -221,8 +222,7 @@ export default function SurveySaleSubmitPopup() { onChange={(e) => { const selectedOffice = commCodeList.find((item) => item.code === e.target.value) if (selectedOffice) { - //@ts-ignore - const receiver = selectedOffice.REF_CHR1.split(';') + const receiver = selectedOffice.refChr1?.split(';') || [] setSubmitData((prev) => ({ ...prev, receiver: receiver, diff --git a/src/components/survey-sale/detail/ButtonForm.tsx b/src/components/survey-sale/detail/ButtonForm.tsx index c5ef64e..31c326c 100644 --- a/src/components/survey-sale/detail/ButtonForm.tsx +++ b/src/components/survey-sale/detail/ButtonForm.tsx @@ -97,12 +97,10 @@ export default function ButtonForm({ mode, setMode, data }: ButtonFormProps) { /** 저장 로직 */ const handleSave = (isTemporary: boolean) => { - const emptyField = validateSurveyDetail(data.roof) - if (isTemporary) { tempSaveProcess() } else { - saveProcess(emptyField) + saveProcess() } } @@ -134,7 +132,9 @@ export default function ButtonForm({ mode, setMode, data }: ButtonFormProps) { } /** 저장 로직 */ - const saveProcess = async (emptyField: string | null) => { + const saveProcess = async () => { + const emptyField = validateSurveyDetail(data.roof) + if (emptyField?.trim() === '') { showConfirm(CONFIRM_MESSAGE.SAVE_CONFIRM, async () => { await handleSuccessfulSave() diff --git a/src/hooks/useAxios.ts b/src/hooks/useAxios.ts index 27b04a8..b905511 100644 --- a/src/hooks/useAxios.ts +++ b/src/hooks/useAxios.ts @@ -91,15 +91,13 @@ export function useAxios() { // Handle uppercase snake_case (e.g., USER_NAME -> userName) // Handle lowercase snake_case (e.g., user_name -> userName) - if (/^[A-Z_]+$/.test(key) || /^[a-z_]+$/.test(key)) { + if (/^[A-Z0-9_]+$/.test(key) || /^[a-z0-9_]+$/.test(key)) { transformedKey = snakeToCamel(key) } // Handle single uppercase word (e.g., ROLE -> role) - else if (/^[A-Z]+$/.test(key)) { + else if (/^[A-Z0-9]+$/.test(key)) { transformedKey = key.toLowerCase() } - // Preserve existing camelCase - acc[transformedKey] = transformObjectKeys(obj[key]) return acc }, {}) @@ -109,7 +107,7 @@ export function useAxios() { } const snakeToCamel = (str: string): string => { - return str.toLowerCase().replace(/([-_][a-z])/g, (group) => group.toUpperCase().replace('-', '').replace('_', '')) + return str.toLowerCase().replace(/_([a-z])/g, (match, letter) => letter.toUpperCase()) } return { diff --git a/src/hooks/useInquiry.ts b/src/hooks/useInquiry.ts index 25bb533..cd87401 100644 --- a/src/hooks/useInquiry.ts +++ b/src/hooks/useInquiry.ts @@ -1,11 +1,12 @@ -import { InquiryList, Inquiry, InquirySaveResponse, CommonCode } from '@/types/Inquiry' +import { InquiryList, Inquiry, InquirySaveResponse, InquiryCommCode } from '@/types/Inquiry' import { useAxios } from '@/hooks/useAxios' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useInquiryFilterStore } from '@/store/inquiryFilterStore' import { useMemo } from 'react' import { useRouter } from 'next/navigation' import { useAlertMsg } from '@/hooks/useAlertMsg' - +import { useCommCode } from './useCommCode' +import { CommCode } from '@/types/CommCode' /** * @description 문의사항 관련 기능을 제공하는 커스텀 훅 @@ -20,7 +21,7 @@ import { useAlertMsg } from '@/hooks/useAlertMsg' * @returns {boolean} isSavingInquiry - 문의사항 저장 중 상태 * @returns {Function} saveInquiry - 문의사항 저장 함수 * @returns {Function} downloadFile - 파일 다운로드 함수 - * @returns {CommonCode[]} commonCodeList - 공통 코드 목록 + * @returns {CommCode[]} commonCodeList - 공통 코드 목록 */ export function useInquiry( qnoNo?: number, @@ -33,7 +34,7 @@ export function useInquiry( isSavingInquiry: boolean saveInquiry: (formData: FormData) => Promise downloadFile: (encodeFileNo: number, srcFileNm: string) => Promise - commonCodeList: CommonCode[] + commonCodeList: CommCode[] } { const queryClient = useQueryClient() const { inquiryListRequest, offset, isMyInquiry } = useInquiryFilterStore() @@ -216,7 +217,8 @@ export function useInquiry( } /** - * @description 공통 코드 목록 조회 + * @description 문의 공통 코드 목록 조회 + * - 문의 유형 대분류, 중분류, 소분류 코드 조회 이후 데이터 리로드 하지 않음 * * @returns {Object} 공통 코드 목록 데이터 * @returns {CommonCode[]} data - 공통 코드 목록 @@ -225,15 +227,13 @@ export function useInquiry( const { data: commonCodeList, isLoading: isLoadingCommonCodeList } = useQuery({ queryKey: ['commonCodeList'], queryFn: async () => { - const isListQuery = false - const shouldThrowError = false - - const resp = await tryFunction( - () => axiosInstance(null).get('/api/comm-code', { params: { headId: 'QNA_CD' } }), - isListQuery, - shouldThrowError, - ) - return resp?.data ?? [] + const { getCommCode } = useCommCode() + const resp = await Promise.all([ + getCommCode(InquiryCommCode.QNA_CLS_LRG_CD.headId), + getCommCode(InquiryCommCode.QNA_CLS_MID_CD.headId), + getCommCode(InquiryCommCode.QNA_CLS_SML_CD.headId), + ]) + return resp.flat() }, staleTime: Infinity, gcTime: Infinity, diff --git a/src/hooks/useSurvey.ts b/src/hooks/useSurvey.ts index 5e14604..d5f2d09 100644 --- a/src/hooks/useSurvey.ts +++ b/src/hooks/useSurvey.ts @@ -1,4 +1,11 @@ -import type { SubmitTargetResponse, SurveyBasicInfo, SurveyDetailRequest, SurveyRegistRequest } from '@/types/Survey' +import { + radioEtcData, + selectBoxOptions, + type SubmitTargetResponse, + type SurveyBasicInfo, + type SurveyDetailRequest, + type SurveyRegistRequest, +} from '@/types/Survey' import { useMemo } from 'react' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useSurveyFilterStore } from '@/store/surveyFilterStore' @@ -380,7 +387,7 @@ export function useSurvey( }) /** - * @description 조사 매물 상세 데이터 유효성 검사 + * @description 조사 매물 상세 데이터 필수값 검증 * * @param {SurveyDetailRequest} surveyDetail 검사할 조사 매물 상세 데이터 * @returns {string} 빈 필드 이름 또는 빈 문자열 @@ -395,15 +402,25 @@ export function useSurvey( } const checkRequiredField = (field: string): string => { + /** 기타 (직접입력) 항목이 있는 필드 체크 */ if (ETC_FIELDS.includes(field as (typeof ETC_FIELDS)[number])) { + /** 기타 (직접입력) 항목이 비어있거나 값 선택/입력을 하지 않은 경우 해당 필드 반환 */ if ( isEmptyValue(surveyDetail[field as keyof SurveyDetailRequest]) && isEmptyValue(surveyDetail[`${field}Etc` as keyof SurveyDetailRequest]) ) { return field } + /** 직접입력 필드가 기타 선택이 아닌 필드 체크 - 건축연수, 단열재 유무 + * 건축연수 필드가 기축(O) 선택이고 직접입력 필드가 비어있거나 값 선택/입력을 하지 않은 경우 해당 필드 반환 + * 단열재 유무 필드가 2(있음) 선택이고 직접입력 필드가 비어있거나 값 선택/입력을 하지 않은 경우 해당 필드 반환 + */ } else if (SPECIAL_CONDITIONS.includes(field as (typeof SPECIAL_CONDITIONS)[number])) { - if (surveyDetail[field as keyof SurveyDetailRequest] === '2' && isEmptyValue(surveyDetail[`${field}Etc` as keyof SurveyDetailRequest])) { + if ( + (surveyDetail.constructionYear === selectBoxOptions.constructionYear[1].code && isEmptyValue(surveyDetail.constructionYearEtc)) || + (surveyDetail.insulationPresence === '2' && isEmptyValue(surveyDetail.insulationPresenceEtc)) + ) { + /** 건축연수, 단열재 유무 필드가 비어있거나 값 선택/입력을 하지 않은 경우 해당 필드 반환 */ return `${field}Etc` } else if (isEmptyValue(surveyDetail[field as keyof SurveyDetailRequest])) { return field diff --git a/src/types/CommCode.ts b/src/types/CommCode.ts index 5847047..d12ea08 100644 --- a/src/types/CommCode.ts +++ b/src/types/CommCode.ts @@ -2,4 +2,6 @@ export type CommCode = { headCd: string code: string codeJp: string + refChr1: string + refNum1: string } diff --git a/src/types/Inquiry.ts b/src/types/Inquiry.ts index c54d9dc..888ef6e 100644 --- a/src/types/Inquiry.ts +++ b/src/types/Inquiry.ts @@ -180,13 +180,17 @@ export type InquirySaveResponse = { mailYn: string } -/** - * @description 공통 코드 타입 - */ -export type CommonCode = { - headCd: string - headId: string - code: string - name: string - refChar1: string +export const InquiryCommCode = { + QNA_CLS_LRG_CD: { + headCd: '204200', + headId: 'QNA_CLS_LRG_CD', + }, + QNA_CLS_MID_CD: { + headCd: '204300', + headId: 'QNA_CLS_MID_CD', + }, + QNA_CLS_SML_CD: { + headCd: '204400', + headId: 'QNA_CLS_SML_CD', + }, }