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 => { try { const params: Record = { pageNumber: pageNumber || 1, itemPerPage: itemPerPage, } if (category) params.category = category if (keyword) params.keyword = keyword const response = await axiosInstance(null).get('/api/suitable/list', { params }) return response.data } catch (error) { console.error('지붕재 데이터 로드 실패:', error) return [] } } const getSuitableIds = async (): Promise => { try { const params: Record = {} if (selectedCategory) params.category = selectedCategory if (searchKeyword) params.keyword = searchKeyword const response = await axiosInstance(null).get('/api/suitable/pick', { params }) return response.data } catch (error) { console.error('지붕재 아이디 로드 실패:', error) return [] } } const getSuitableDetails = async (ids: string, detailIds?: string): Promise => { try { const params: Record = { ids: ids } if (detailIds) params.detailIds = detailIds const response = await axiosInstance(null).post('/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 => { try { return new Set(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({ 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({ 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 = { '×': '/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 => { 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, } }