236 lines
7.6 KiB
TypeScript
236 lines
7.6 KiB
TypeScript
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()
|
||
|
||
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 []
|
||
}
|
||
}
|
||
|
||
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 []
|
||
}
|
||
}
|
||
|
||
const getSuitableDetails = async (ids: string, detailIds?: string): Promise<Suitable[]> => {
|
||
try {
|
||
const params: Record<string, string> = { ids: ids }
|
||
if (detailIds) params.detailIds = detailIds
|
||
const response = await axiosInstance(null).post<Suitable[]>('/api/suitable', params)
|
||
return response.data
|
||
} catch (error) {
|
||
console.error('지붕재 상세 데이터 로드 실패:', error)
|
||
return []
|
||
}
|
||
}
|
||
|
||
const getSuitableCommCode = () => {
|
||
const headCodes = Object.values(SUITABLE_HEAD_CODE) as SUITABLE_HEAD_CODE[]
|
||
for (const code of headCodes) {
|
||
getCommCode(code).then((res) => {
|
||
setSuitableCommCode(code, res)
|
||
})
|
||
}
|
||
}
|
||
|
||
const toCodeName = (headCode: string, code: string): string => {
|
||
const commCode = suitableCommCode.get(headCode)
|
||
return commCode?.find((item) => item.code === code)?.codeJp || ''
|
||
}
|
||
|
||
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('지붕재 데이터 파싱 실패:', error)
|
||
return []
|
||
}
|
||
}
|
||
|
||
const toSuitableDetailIds = (suitableDetailString: string): Set<number> => {
|
||
try {
|
||
return new Set<number>(JSON.parse(suitableDetailString).map(({ id }: { id: number }) => id))
|
||
} catch (error) {
|
||
console.error('지붕재 데이터 파싱 실패:', error)
|
||
return new Set()
|
||
}
|
||
}
|
||
|
||
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 !== '',
|
||
})
|
||
|
||
const clearSuitableStore = ({ items = false, category = false, keyword = false }: { items?: boolean; category?: boolean; keyword?: boolean }) => {
|
||
if (items) clearSelectedItems()
|
||
if (category) clearSearchCategory()
|
||
if (keyword) clearSearchKeyword()
|
||
}
|
||
|
||
// 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
|
||
}
|
||
|
||
// TODO: 추후 지붕재 적합성 데이터 CUD 구현 시 ○, ×, -, ー 데이터 관리 필요
|
||
const suitableCheckMemo = (value: string): string => {
|
||
if (value === '○') return '設置可'
|
||
if (value === '×') return '設置不可'
|
||
if (value === '-' || value === 'ー') return 'お問い合わせください'
|
||
return `${value}で設置可`
|
||
}
|
||
|
||
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(',') : '' }
|
||
}
|
||
|
||
const getSelectedSuitables = async (): Promise<Suitable[]> => {
|
||
const { ids, detailIds } = serializeSelectedItems()
|
||
return await getSuitableDetails(ids, detailIds)
|
||
}
|
||
|
||
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,
|
||
}
|
||
}
|