onsitesurvey/src/hooks/useSuitable.ts

333 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useInfiniteQuery } from '@tanstack/react-query'
import { transformObjectKeys } from '@/libs/axios'
import { useSuitableStore } from '@/store/useSuitableStore'
import { useAxios } from './useAxios'
import { useCommCode } from './useCommCode'
import { SUITABLE_HEAD_CODE, type Suitable, type SuitableDetail, type SuitableIds } from '@/types/Suitable'
export function useSuitable() {
const { axiosInstance } = useAxios()
const { getCommCode } = useCommCode()
const {
itemPerPage,
suitableCommCode,
setSuitableCommCode,
searchCategory,
clearSearchCategory,
searchKeyword,
clearSearchKeyword,
selectedItems,
clearSelectedItems,
} = useSuitableStore()
/**
* @description 지붕재 적합성 데이터 조회. API를 호출하여 검색 조건에 맞는 지붕재 적합성 데이터를 페이지 단위로 조회
*
* @param {Object} param
* @param {number} [param.pageNumber] 페이지 번호 (기본값: 1)
* @param {string} [param.category] 지붕재그룹코드
* @param {string} [param.keyword] 검색키워드
*
* @returns {Promise<Suitable[]>} 지붕재 적합성 데이터
*/
const getSuitables = async ({
pageNumber,
category,
keyword,
}: {
pageNumber?: number
category?: string
keyword?: string
}): Promise<Suitable[]> => {
try {
const params: Record<string, string | number> = {
pageNumber: pageNumber || 1,
itemPerPage: itemPerPage,
}
if (category) params.category = category
if (keyword) params.keyword = keyword
const response = await axiosInstance(null).get<Suitable[]>('/api/suitable/list', { params })
return response.data
} catch (error) {
console.error(`지붕재 적합성 데이터 조회 실패: ${error}`)
return []
}
}
/**
* @description 지붕재 적합성 데이터 아이디 조회. API를 호출하여 검색 조건에 맞는 지붕재 적합성 데이터의 main_id, detail_id를 조회
*
* @returns {Promise<SuitableIds[]>} 지붕재 적합성 데이터 아이디 목록
*/
const getSuitableIds = async (): Promise<SuitableIds[]> => {
try {
const params: Record<string, string> = {}
if (searchCategory) params.category = searchCategory
if (searchKeyword) params.keyword = searchKeyword
const response = await axiosInstance(null).get<SuitableIds[]>('/api/suitable/pick', { params })
return response.data
} catch (error) {
console.error(`지붕재 적합성 데이터 아이디 조회 실패: ${error}`)
return []
}
}
/**
* @description 지붕재 적합성 데이터 조회. API를 호출하여 지붕재 적합성 데이터의 모든 컬럼을 조회
*
* @param {string} ids 지붕재 적합성 데이터의 main_id (쉼표로 구분된 문자열)
* @param {string} [detailIds] 지붕재 적합성 데이터의 detail_id (쉼표로 구분된 문자열)
*
* @returns {Promise<Suitable[]>} 지붕재 적합성 데이터
*/
const getSuitableDetails = async (ids: string, detailIds?: string): Promise<Suitable[]> => {
try {
const params: Record<string, string> = { ids: ids }
if (detailIds?.trim()) params.detailIds = detailIds
const response = await axiosInstance(null).post<Suitable[]>('/api/suitable', params)
return response.data
} catch (error) {
console.error(`지붕재 적합성 상세 데이터 조회 실패: ${error}`)
return []
}
}
/**
* @description 지붕재 적합성 공통 코드 조회. API를 호출하여 지붕재 적합성 데이터에서 사용하는 공통 코드 조회 후 스토어에 맵핑하여 저장
*
* @returns {void}
*/
const getSuitableCommCode = (): void => {
const headCodes = Object.values(SUITABLE_HEAD_CODE) as SUITABLE_HEAD_CODE[]
for (const code of headCodes) {
getCommCode(code).then((res) => {
setSuitableCommCode(code, res)
})
}
}
/**
* @description 지붕재 적합성 공통 코드의 JP 코드명 조회
*
* @param {string} headCode 공통 코드의 head_code
* @param {string} code 공통 코드의 code
*
* @returns {string} 공통 코드의 JP 코드명
*/
const toCodeName = (headCode: string, code: string): string => {
const commCode = suitableCommCode.get(headCode)
return commCode?.find((item) => item.code === code)?.codeJp || ''
}
/**
* @description 지붕재 적합성 detail 데이터를 string 형태로 받아 파싱하여 반환
*
* @param {string} suitableDetailString 지붕재 적합성 detail string
*
* @returns {SuitableDetail[]} 파싱된 지붕재 적합성 detail 데이터
*/
const toSuitableDetail = (suitableDetailString: string): SuitableDetail[] => {
if (!suitableDetailString) return []
try {
const suitableDetailArray = transformObjectKeys(JSON.parse(suitableDetailString)) as SuitableDetail[]
if (!Array.isArray(suitableDetailArray)) {
throw new Error('suitableDetailArray is not an array')
}
return suitableDetailArray
} catch (error) {
console.error(`지붕재 적합성 detail 데이터 파싱 실패: ${error}`)
return []
}
}
/**
* @description 지붕재 적합성 상위 데이터 클릭 시 호출되는 함수. 지붕재 적합성 detail 데이터에서 id 목록을 추출하여 Set 형태로 반환
*
* @param {string} suitableDetailString 지붕재 적합성 detail 데이터
*
* @returns {Set<number>} 지붕재 적합성 detail 데이터의 id 목록
*/
const toSuitableDetailIds = (suitableDetailString: string): Set<number> => {
try {
if (!suitableDetailString) return new Set()
return new Set<number>(JSON.parse(suitableDetailString).map(({ id }: { id: number }) => id))
} catch (error) {
console.error(`지붕재 적합성 detail 데이터 파싱 실패: ${error}`)
return new Set()
}
}
/**
* @description 지붕재 적합성 조회 페이지에서 보여지는 지붕재 적합성 데이터 조회
*
* @returns {Object} 지붕재 적합성 데이터 조회 결과
*/
const {
data: suitables,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
isLoading,
// isError,
// error,
} = useInfiniteQuery<Suitable[]>({
queryKey: ['suitables', 'list', searchCategory, searchKeyword],
queryFn: async (context) => {
const pageParam = context.pageParam as number
if (pageParam === 1) clearSuitableStore({ items: true })
return await getSuitables({
pageNumber: pageParam,
...(searchCategory && { category: searchCategory }),
...(searchKeyword && { keyword: searchKeyword }),
})
},
getNextPageParam: (lastPage: Suitable[], allPages: Suitable[][]) => {
return lastPage.length === itemPerPage ? allPages.length + 1 : undefined
},
initialPageParam: 1,
staleTime: 1000 * 60 * 10,
gcTime: 1000 * 60 * 10,
enabled: searchCategory !== '' || searchKeyword !== '',
})
/**
* @description 지붕재 적합성 스토어 초기화. 선택된 지붕재 적합성 데이터, 검색 카테고리, 검색 키워드 초기화
*
* @param {Object} param
* @param {boolean} [param.items] 선택된 지붕재 적합성 데이터 초기화 여부
* @param {boolean} [param.category] 검색 카테고리 초기화 여부
* @param {boolean} [param.keyword] 검색 키워드 초기화 여부
*
* @returns {void}
*/
const clearSuitableStore = ({ items = false, category = false, keyword = false }: { items?: boolean; category?: boolean; keyword?: boolean }) => {
if (items) clearSelectedItems()
if (category) clearSearchCategory()
if (keyword) clearSearchKeyword()
}
/**
* @description 지붕재 적합성 비고에 따른 아이콘 반환
*
* @param {string} value 지붕재 적합성 비고
*
* @returns {string} 지붕재 적합성 비고에 따른 아이콘
*/
// TODO: 추후 지붕재 적합성 데이터 CUD 구현 시 ×, -, ー 데이터 관리 필요
const suitableCheckIcon = (value: string): string => {
const iconMap: Record<string, string> = {
'×': '/assets/images/sub/compliance_x_icon.svg',
'-': '/assets/images/sub/compliance_quest_icon.svg',
'ー': '/assets/images/sub/compliance_quest_icon.svg',
default: '/assets/images/sub/compliance_check_icon.svg',
}
return iconMap[value] || iconMap.default
}
/**
* @description 지붕재 적합성 비고에 따른 적합 결과 반환
*
* @param {string} value 지붕재 적합성 비고
*
* @returns {string} 지붕재 적합성 비고에 따른 적합 결과
*/
// TODO: 추후 지붕재 적합성 데이터 CUD 구현 시 ○, ×, -, ー 데이터 관리 필요
const suitableCheckMemo = (value: string): string => {
if (value === '○') return '設置可'
if (value === '×') return '設置不可'
if (value === '-' || value === 'ー') return 'お問い合わせください'
return `${value}で設置可`
}
/**
* @description 선택된 지붕재 적합성 데이터의 main_id, detail_id를 쉼표로 구분하여 반환. 선택된 지붕재 적합성 데이터가 없으면 빈 문자열 반환
*
* @returns {Object} 선택된 지붕재 적합성 데이터의 main_id, detail_id
* @returns {string} 선택된 지붕재 적합성 데이터의 main_id (쉼표로 구분된 문자열)
* @returns {string} 선택된 지붕재 적합성 데이터의 detail_id (쉼표로 구분된 문자열)
*/
const serializeSelectedItems = (): { ids: string; detailIds: string } => {
const ids: string[] = []
const detailIds: string[] = []
for (const [key, value] of selectedItems) {
ids.push(String(key))
for (const id of value) detailIds.push(String(id))
}
return { ids: ids.join(','), detailIds: detailIds.length > 0 ? detailIds.join(',') : '' }
}
/**
* @description 선택된 지붕재 적합성 데이터 조회. API를 호출하여 선택된 지붕재 적합성 데이터의 모든 컬럼을 조회
*
* @returns {Promise<Suitable[]>} 선택된 지붕재 적합성 데이터
*/
const getSelectedSuitables = async (): Promise<Suitable[]> => {
const { ids, detailIds } = serializeSelectedItems()
return await getSuitableDetails(ids, detailIds)
}
/**
* @description 선택된 지붕재 적합성 데이터 pdf 다운로드. form 태그를 생성하여 직접 API를 호출하여 pdf 다운로드
*
* @returns {Promise<void>} 선택된 지붕재 적합성 데이터 pdf 다운로드
*/
const downloadSuitablePdf = async (): Promise<void> => {
try {
const { ids, detailIds } = serializeSelectedItems()
const fileTitle = `(${
suitableCommCode.get(SUITABLE_HEAD_CODE.ROOF_MATL_GRP_CD)?.find((category) => category.code === searchCategory)?.codeJp
}) 屋根材適合表`
const form = document.createElement('form')
form.method = 'POST'
form.action = '/api/suitable/pdf'
form.target = '_blank'
const inputIds = document.createElement('input')
inputIds.type = 'hidden'
inputIds.name = 'ids'
inputIds.value = ids
const inputDetailIds = document.createElement('input')
inputDetailIds.type = 'hidden'
inputDetailIds.name = 'detailIds'
inputDetailIds.value = detailIds
const inputFileTitle = document.createElement('input')
inputFileTitle.type = 'hidden'
inputFileTitle.name = 'fileTitle'
inputFileTitle.value = fileTitle
form.appendChild(inputIds)
form.appendChild(inputDetailIds)
form.appendChild(inputFileTitle)
document.body.appendChild(form)
form.submit()
document.body.removeChild(form)
} catch (error) {
console.error(`지붕재 적합성 상세 데이터 pdf 다운로드 실패: ${error}`)
}
}
return {
getSuitables,
getSuitableIds,
getSuitableCommCode,
toCodeName,
toSuitableDetail,
toSuitableDetailIds,
suitables,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
isLoading,
getSelectedSuitables,
clearSuitableStore,
suitableCheckIcon,
suitableCheckMemo,
downloadSuitablePdf,
}
}