'use client'; import { useEffect, useMemo, useState } from 'react'; export type CoffeeShippingFeePieceCount = 60 | 120; export type CoffeeShippingFee = { pieceCount: CoffeeShippingFeePieceCount; price: number; }; type ShippingFeeMap = Record; function normalizePieceCount(v: any): CoffeeShippingFeePieceCount | null { const n = typeof v === 'number' ? v : (typeof v === 'string' && /^\d+$/.test(v) ? Number(v) : NaN); if (n === 60 || n === 120) return n; return null; } export function useShippingFees() { const base = process.env.NEXT_PUBLIC_API_BASE_URL || ''; const [fees, setFees] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const feeByPieceCount: ShippingFeeMap = useMemo(() => { const map: ShippingFeeMap = { 60: 0, 120: 0 }; for (const row of fees) { map[row.pieceCount] = row.price; } return map; }, [fees]); useEffect(() => { let active = true; (async () => { setLoading(true); setError(null); try { const res = await fetch(`${base}/api/shipping-fees`, { method: 'GET', credentials: 'include', }); if (!res.ok) { const text = await res.text().catch(() => ''); throw new Error(text || `HTTP ${res.status}`); } const raw = (await res.json().catch(() => [])) as any; if (!active) return; if (!Array.isArray(raw)) { setFees([]); return; } const next = raw .map((r: any) => { const pieceCount = normalizePieceCount(r?.pieceCount); if (!pieceCount) return null; const price = r?.price != null && r?.price !== '' ? Number(r.price) : 0; return { pieceCount, price: Number.isFinite(price) && price >= 0 ? price : 0, } satisfies CoffeeShippingFee; }) .filter(Boolean) as CoffeeShippingFee[]; next.sort((a, b) => a.pieceCount - b.pieceCount); setFees(next); } catch (e: any) { if (!active) return; setError(e?.message ?? 'Failed to load shipping fees'); setFees([]); } finally { if (active) setLoading(false); } })(); return () => { active = false; }; }, [base]); return { fees, feeByPieceCount, loading, error }; }