261 lines
8.4 KiB
TypeScript
261 lines
8.4 KiB
TypeScript
import { useInfiniteQuery, useQuery } from '@tanstack/react-query'
|
||
import { SHA256 } from 'crypto-js'
|
||
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,
|
||
selectedCategory,
|
||
clearSelectedCategory,
|
||
searchKeyword,
|
||
clearSearchKeyword,
|
||
selectedItems,
|
||
clearSelectedItems,
|
||
selectedItemsSearching,
|
||
} = 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 (selectedCategory) params.category = selectedCategory
|
||
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', selectedCategory, searchKeyword],
|
||
queryFn: async (context) => {
|
||
const pageParam = context.pageParam as number
|
||
if (pageParam === 1) clearSuitableStore({ items: true })
|
||
return await getSuitables({
|
||
pageNumber: pageParam,
|
||
...(selectedCategory && { category: selectedCategory }),
|
||
...(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: selectedCategory !== '' || searchKeyword !== '',
|
||
})
|
||
|
||
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 getSelectedItemsHash = (): string => {
|
||
const entries = Array.from(selectedItems.entries())
|
||
.map(([key, value]) => `${key}:${Array.from(value).sort().join(',')}`)
|
||
.sort()
|
||
.join('|')
|
||
return SHA256(entries).toString()
|
||
}
|
||
|
||
const {
|
||
data: selectedSuitables,
|
||
isLoading: isSelectedSuitablesLoading,
|
||
// refetch: refetchSelectedSuitables,
|
||
} = useQuery<Suitable[]>({
|
||
queryKey: ['suitables', 'selectedItems', getSelectedItemsHash(), selectedItemsSearching],
|
||
queryFn: async () => {
|
||
const { ids, detailIds } = serializeSelectedItems()
|
||
return await getSuitableDetails(ids, detailIds)
|
||
},
|
||
staleTime: Infinity,
|
||
gcTime: Infinity,
|
||
enabled: selectedItemsSearching,
|
||
refetchOnMount: false,
|
||
refetchOnWindowFocus: false,
|
||
refetchOnReconnect: false,
|
||
})
|
||
|
||
const clearSuitableStore = ({ items = false, category = false, keyword = false }: { items?: boolean; category?: boolean; keyword?: boolean }) => {
|
||
if (items) clearSelectedItems()
|
||
if (category) clearSelectedCategory()
|
||
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 downloadSuitablePdf = async (): Promise<void> => {
|
||
try {
|
||
const { ids, detailIds } = serializeSelectedItems()
|
||
const fileTitle = `(${
|
||
suitableCommCode.get(SUITABLE_HEAD_CODE.ROOF_MATL_GRP_CD)?.find((category) => category.code === selectedCategory)?.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,
|
||
getSuitableDetails,
|
||
getSuitableCommCode,
|
||
toCodeName,
|
||
toSuitableDetail,
|
||
toSuitableDetailIds,
|
||
suitables,
|
||
fetchNextPage,
|
||
hasNextPage,
|
||
isFetchingNextPage,
|
||
isLoading,
|
||
selectedSuitables,
|
||
isSelectedSuitablesLoading,
|
||
clearSuitableStore,
|
||
suitableCheckIcon,
|
||
suitableCheckMemo,
|
||
downloadSuitablePdf,
|
||
}
|
||
}
|