import { useState, useEffect, useCallback, useRef } from 'react'; import { purchaserApi } from '../api'; import type { PlanDetail } from '../api/purchaser'; import { CACHE_CONFIG, PAGINATION_CONFIG } from '../config/app'; import { usePlanRealtime } from './useRealtime'; import type { PlanWithSteps, Company, PlanFactory, YarnRatio, } from '../types'; interface UsePlanDataOptions { companyId: string | null; pageSize?: number; } interface PlanDataState { plans: PlanWithSteps[]; factories: Company[]; planFactories: PlanFactory[]; operatorNames: Record; yarnRatios: Record; loading: boolean; loadingMore: boolean; hasMore: boolean; currentPage: number; error: Error | null; } function mapPlanDetail(plan: PlanDetail): PlanWithSteps { return { ...plan, processSteps: plan.process_steps, inventoryRecords: plan.inventory_records, priceHistory: plan.price_history, }; } export function usePlanData({ companyId, pageSize = PAGINATION_CONFIG.defaultPageSize }: UsePlanDataOptions) { const [state, setState] = useState({ plans: [], factories: [], planFactories: [], operatorNames: {}, yarnRatios: {}, loading: true, loadingMore: false, hasMore: true, currentPage: 1, error: null }); const cacheRef = useRef<{ data: PlanDataState | null; timestamp: number; }>({ data: null, timestamp: 0 }); const CACHE_DURATION = CACHE_CONFIG.planDataDuration; const fetchData = useCallback(async (page: number = 1, append: boolean = false) => { if (!companyId) { setState(prev => ({ ...prev, loading: false })); return; } const isFirstPage = page === 1; if (isFirstPage) { const now = Date.now(); if (cacheRef.current.data && now - cacheRef.current.timestamp < CACHE_DURATION) { setState(cacheRef.current.data); return; } setState(prev => ({ ...prev, loading: true, error: null })); } else { setState(prev => ({ ...prev, loadingMore: true })); } try { const res = await purchaserApi.listPlans({ page, page_size: pageSize }); const { plans: planDetails, factories, plan_factories, operator_names } = res.data; const plansWithSteps = planDetails.map(mapPlanDetail); const yarnRatiosMap: Record = {}; planDetails.forEach(p => { if (p.yarn_ratios && p.yarn_ratios.length > 0) { yarnRatiosMap[p.id] = p.yarn_ratios; } }); setState(prev => { const newState: PlanDataState = { plans: append ? [...prev.plans, ...plansWithSteps] : plansWithSteps, factories: isFirstPage ? factories : prev.factories, planFactories: isFirstPage ? plan_factories : prev.planFactories, operatorNames: isFirstPage ? operator_names : { ...prev.operatorNames, ...operator_names }, yarnRatios: isFirstPage ? yarnRatiosMap : { ...prev.yarnRatios, ...yarnRatiosMap }, loading: false, loadingMore: false, hasMore: res.meta?.has_more ?? planDetails.length === pageSize, currentPage: page, error: null }; if (isFirstPage) { cacheRef.current = { data: newState, timestamp: Date.now() }; } return newState; }); } catch (err) { setState(prev => ({ ...prev, loading: false, loadingMore: false, error: err instanceof Error ? err : new Error('Unknown error') })); } }, [companyId, pageSize]); const loadMore = useCallback(() => { if (state.loadingMore || !state.hasMore) return; fetchData(state.currentPage + 1, true); }, [fetchData, state.currentPage, state.hasMore, state.loadingMore]); const refetch = useCallback(() => { cacheRef.current.timestamp = 0; fetchData(1, false); }, [fetchData]); useEffect(() => { fetchData(1, false); }, [fetchData]); usePlanRealtime(companyId, () => { cacheRef.current.timestamp = 0; fetchData(1, false); }); return { ...state, loadMore, refetch }; }