import { useEffect, useState } from 'react'; import { Link, useParams } from 'react-router-dom'; import { api } from '../../api/client.js'; const formatPrice = (value) => value == null ? '–' : new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(value); const statusBadge = { ok: 'bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-300', pending: 'bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-300', error: 'bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-300', }; export default function PackageProducts() { const { packageId } = useParams(); const [products, setProducts] = useState([]); const [sourceUrl, setSourceUrl] = useState(''); const [nameOverride, setNameOverride] = useState(''); const [error, setError] = useState(''); const [busy, setBusy] = useState(false); const load = () => api.get(`/packages/${packageId}/products`).then(setProducts); useEffect(() => { load(); }, [packageId]); const handleAdd = async (e) => { e.preventDefault(); setError(''); setBusy(true); try { await api.post('/products', { package_id: Number(packageId), source_url: sourceUrl, name_override: nameOverride || null }); setSourceUrl(''); setNameOverride(''); load(); } catch (err) { setError(err.message); } finally { setBusy(false); } }; const handleRefresh = async (id) => { setBusy(true); try { await api.post(`/products/${id}/refresh-price`, {}); load(); } finally { setBusy(false); } }; const handleDelete = async (id) => { if (!confirm('Produkt wirklich entfernen?')) return; await api.delete(`/products/${id}`); load(); }; return (

← Zurück zu den Paketen

Produkte im Paket

setSourceUrl(e.target.value)} className="w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-sm dark:border-neutral-700 dark:bg-neutral-900" />
setNameOverride(e.target.value)} className="w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-sm dark:border-neutral-700 dark:bg-neutral-900" />
{error &&

{error}

}
{products.map((p) => ( ))} {products.length === 0 && ( )}
Produkt Preis Status Zuletzt aktualisiert Aktionen
{p.name_override || p.scraped_name || p.source_url} {p.scrape_status === 'error' &&

{p.scrape_error}

}
{formatPrice(p.scraped_price)} {p.scrape_status} {p.price_updated_at ? new Date(p.price_updated_at.replace(' ', 'T')).toLocaleString('de-DE') : '–'}
Noch keine Produkte im Paket.
); }