profit-planet-frontend/src/app/coffee-abonnements/hooks/useShippingFees.ts
2026-03-15 18:34:19 +01:00

88 lines
2.4 KiB
TypeScript

'use client';
import { useEffect, useMemo, useState } from 'react';
export type CoffeeShippingFeePieceCount = 60 | 120;
export type CoffeeShippingFee = {
pieceCount: CoffeeShippingFeePieceCount;
price: number;
};
type ShippingFeeMap = Record<CoffeeShippingFeePieceCount, number>;
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<CoffeeShippingFee[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(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 };
}