"use client"; import React, { useEffect, useState } from 'react'; import { PhotoIcon } from '@heroicons/react/24/solid'; import Link from 'next/link'; import PageLayout from '../../components/PageLayout'; import useCoffeeManagement, { CoffeeItem } from './hooks/useCoffeeManagement'; import useCoffeeShippingFees, { CoffeeShippingFee, CoffeeShippingFeePieceCount, } from './hooks/useCoffeeShippingFees'; export default function AdminSubscriptionsPage() { const { listProducts, setProductState, deleteProduct } = useCoffeeManagement(); const { listShippingFees, updateShippingFee } = useCoffeeShippingFees(); const formatPriceDraft = (price: number) => { if (!Number.isFinite(price)) return ''; return price.toFixed(2).replace('.', ','); }; const parsePriceDraft = (raw: string) => { const normalized = (raw ?? '') .trim() .replace(/\s+/g, '') .replace(/,/g, '.'); if (!normalized) return NaN; return Number(normalized); }; const [shippingFees, setShippingFees] = useState([]); const [shippingFeesLoading, setShippingFeesLoading] = useState(false); const [shippingFeesError, setShippingFeesError] = useState(null); const [shippingFeeDraft, setShippingFeeDraft] = useState>({ 60: '', 120: '', }); const [shippingFeeFieldError, setShippingFeeFieldError] = useState>({ 60: null, 120: null, }); const [shippingFeeSaving, setShippingFeeSaving] = useState>({ 60: false, 120: false, }); const [shippingFeeSavedAt, setShippingFeeSavedAt] = useState>({ 60: null, 120: null, }); const [items, setItems] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); async function load() { setLoading(true); setError(null); try { const data = await listProducts(); setItems(Array.isArray(data) ? data : []); } catch (e: any) { setError(e?.message ?? 'Failed to load products'); } finally { setLoading(false); } } useEffect(() => { load(); loadShippingFees(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); async function loadShippingFees() { setShippingFeesLoading(true); setShippingFeesError(null); try { const list = await listShippingFees(); setShippingFees(Array.isArray(list) ? list : []); const findPrice = (pieceCount: CoffeeShippingFeePieceCount) => { const row = (Array.isArray(list) ? list : []).find((r) => r.pieceCount === pieceCount); return row ? row.price : 0; }; setShippingFeeDraft({ 60: formatPriceDraft(findPrice(60)), 120: formatPriceDraft(findPrice(120)), }); setShippingFeeFieldError({ 60: null, 120: null }); } catch (e: any) { setShippingFeesError(e?.message ?? 'Failed to load shipping fees'); } finally { setShippingFeesLoading(false); } } const saveShippingFee = async (pieceCount: CoffeeShippingFeePieceCount) => { if (shippingFeeSaving[pieceCount]) return; const raw = (shippingFeeDraft[pieceCount] ?? '').trim(); const price = parsePriceDraft(raw); if (!Number.isFinite(price) || price < 0) { setShippingFeeFieldError((prev) => ({ ...prev, [pieceCount]: 'Enter a valid price (≥ 0).', })); return; } setShippingFeeFieldError((prev) => ({ ...prev, [pieceCount]: null })); setShippingFeeSaving((prev) => ({ ...prev, [pieceCount]: true })); try { const updated = await updateShippingFee(pieceCount, price); setShippingFees((prev) => { const next = prev.filter((r) => r.pieceCount !== pieceCount); next.push(updated); next.sort((a, b) => a.pieceCount - b.pieceCount); return next; }); setShippingFeeDraft((prev) => ({ ...prev, [pieceCount]: formatPriceDraft(updated.price) })); setShippingFeeSavedAt((prev) => ({ ...prev, [pieceCount]: Date.now() })); } catch (e: any) { setShippingFeesError(e?.message ?? 'Failed to update shipping fee'); } finally { setShippingFeeSaving((prev) => ({ ...prev, [pieceCount]: false })); } }; const availabilityBadge = (avail: boolean) => ( {avail ? 'Available' : 'Unavailable'} ); const [deleteTarget, setDeleteTarget] = useState(null); return (
{/* Header */}

Coffees

Manage all coffees.

Create Coffee
{error && (
{error}
)} {/* Shipping Fees */}

Shipping Fees (ABO)

Edit the shipping prices for 60 and 120 pieces.

{shippingFeesError && (
{shippingFeesError}
)}
{([60, 120] as CoffeeShippingFeePieceCount[]).map((pieceCount) => { const saving = shippingFeeSaving[pieceCount]; const savedAt = shippingFeeSavedAt[pieceCount]; const fieldError = shippingFeeFieldError[pieceCount]; const current = shippingFees.find((r) => r.pieceCount === pieceCount); const draft = shippingFeeDraft[pieceCount] ?? ''; return (
{pieceCount} pieces
{typeof current?.price === 'number' && Number.isFinite(current.price) ? (
Current: €{formatPriceDraft(current.price)}
) : null} {savedAt ? (
Saved
) : null}
{fieldError ? (
{fieldError}
) : (
Enter a price in EUR (≥ 0).
)}
{ const v = e.target.value; setShippingFeeDraft((prev) => ({ ...prev, [pieceCount]: v })); setShippingFeeFieldError((prev) => ({ ...prev, [pieceCount]: null })); }} placeholder="0.00" />
); })}
{loading && (
Loading…
)} {!loading && items.map(item => (

{item.title}

{availabilityBadge(!!item.state)}
{item.pictureUrl ? ( {item.title} ) : ( )}

{item.description}

Price
{item.currency || 'EUR'} {Number.isFinite(Number(item.price)) ? Number(item.price).toFixed(2) : String(item.price)}
{item.billing_interval && item.interval_count ? (
Subscription billing: {item.billing_interval} (x{item.interval_count})
) : null}
Edit
))} {!loading && !items.length && (
No subscriptions found.
)}
{/* Confirm Delete Modal */} {deleteTarget && (
setDeleteTarget(null)} />

Delete coffee?

You are about to delete the coffee “{deleteTarget.title}”. This action cannot be undone.

)}
); }