// ============================================================
// COMPONENTS — shared UI bits
// ============================================================
const { useState, useEffect, useRef, useMemo } = React;
// ---------- Icons (inline SVG) ----------
const Icon = {
search: ,
cart: ,
heart: ,
user: ,
arrow: ,
arrowL: ,
paw: ,
share: ,
filter: ,
};
// ---------- Top bar ----------
function TopBar({ active, onNav, cartCount }) {
return (
);
}
// ---------- Footer ----------
function Footer({ onNav }) {
return (
);
}
// ---------- Breed Card ----------
function BreedCard({ breed, onClick }) {
return (
PHOTO · {breed.name.split(' ')[0].toUpperCase()}
{breed.name}
{breed.group} · {breed.size} · {breed.lifespan}
{breed.short}
{breed.tags.slice(0, 3).map((t, i) => {t})}
);
}
// ---------- Trait bars ----------
function TraitBars({ traits, max = 5 }) {
const labels = {
energie: 'Energie', vriendelijk: 'Vriendelijk', training: 'Trainbaarheid',
verzorging: 'Verzorging', kindvriendelijk: 'Met kinderen', alleenZijn: 'Alleen kunnen zijn',
blaffen: 'Blaffen', allergie: 'Hypoallergeen'
};
return (
{Object.entries(labels).map(([k, label]) => {
const v = traits[k] || 0;
return (
);
})}
);
}
// ---------- Cost chart ----------
function CostChart({ breed }) {
const items = [
{ label: 'Voer/mnd', val: parseInt(breed.care.voer.match(/\d+/)?.[0] || 50), max: 120, color: '' },
{ label: 'Verzekering', val: 35, max: 120, color: '' },
{ label: 'Dierenarts', val: 28, max: 120, color: '' },
{ label: 'Trimmer', val: breed.traits.verzorging * 12, max: 120, color: 'alt' },
{ label: 'Speelgoed', val: 12, max: 120, color: 'alt' },
{ label: 'Verzekering+', val: 18, max: 120, color: 'alt' },
];
const total = items.reduce((s, i) => s + i.val, 0);
return (
{items.map((it, i) => (
))}
Geschat per maand
€{total},–
);
}
// ---------- Schedule chart ----------
function ScheduleChart({ breed }) {
const days = ['Ma','Di','Wo','Do','Vr','Za','Zo'];
const energie = breed.traits.energie || 3;
// Build daily blocks based on energy
const buildDay = (i) => {
const blocks = [];
blocks.push('b-walk'); // morning walk
if (energie >= 3) blocks.push('b-train');
blocks.push('b-rest');
if (energie >= 4) blocks.push('b-walk');
blocks.push('b-play');
blocks.push('b-rest');
blocks.push('b-walk'); // evening walk
if (i === 5 || i === 6) blocks.push('b-play'); // weekend extras
return blocks;
};
return (
{days.map((d, i) => (
{d}
{buildDay(i).map((b, j) => )}
))}
Wandelen
Training
Spelen
Rust
);
}
// ---------- Product card ----------
function ProductCard({ product, inCart, onAdd }) {
return (
{product.badge && {product.badge}}
{product.tag}
{product.brand}
{product.name}
★★★★★
{product.rating} · {product.reviews} reviews
€{product.price.toFixed(2)}
{product.old && €{product.old.toFixed(2)}}
);
}
// Export to window
Object.assign(window, {
Icon, TopBar, Footer, BreedCard, TraitBars, CostChart, ScheduleChart, ProductCard,
});