diff --git a/client/src/pages/ExternalDogs.jsx b/client/src/pages/ExternalDogs.jsx new file mode 100644 index 0000000..383ffc6 --- /dev/null +++ b/client/src/pages/ExternalDogs.jsx @@ -0,0 +1,143 @@ +import { useState, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Users, Plus, Search, ExternalLink, Award, Filter } from 'lucide-react'; + +export default function ExternalDogs() { + const [dogs, setDogs] = useState([]); + const [loading, setLoading] = useState(true); + const [search, setSearch] = useState(''); + const [sexFilter, setSexFilter] = useState('all'); + const navigate = useNavigate(); + + useEffect(() => { + fetch('/api/dogs/external') + .then(r => r.json()) + .then(data => { setDogs(data); setLoading(false); }) + .catch(() => setLoading(false)); + }, []); + + const filtered = dogs.filter(d => { + const matchSearch = d.name.toLowerCase().includes(search.toLowerCase()) || + (d.breed || '').toLowerCase().includes(search.toLowerCase()); + const matchSex = sexFilter === 'all' || d.sex === sexFilter; + return matchSearch && matchSex; + }); + + const sires = filtered.filter(d => d.sex === 'male'); + const dams = filtered.filter(d => d.sex === 'female'); + + if (loading) return
Loading external dogs...
; + + return ( +
+ {/* Header */} +
+
+ +
+

External Dogs

+

External sires, dams, and ancestors used in your breeding program

+
+
+ +
+ + {/* Filters */} +
+
+ + setSearch(e.target.value)} + className="search-input" + /> +
+
+ + +
+ {filtered.length} dog{filtered.length !== 1 ? 's' : ''} +
+ + {filtered.length === 0 ? ( +
+ +

No external dogs yet

+

Add sires, dams, or ancestors that aren't part of your kennel roster.

+ +
+ ) : ( +
+ {(sexFilter === 'all' || sexFilter === 'male') && sires.length > 0 && ( +
+

♂ Sires ({sires.length})

+
+ {sires.map(dog => )} +
+
+ )} + {(sexFilter === 'all' || sexFilter === 'female') && dams.length > 0 && ( +
+

♀ Dams ({dams.length})

+
+ {dams.map(dog => )} +
+
+ )} +
+ )} +
+ ); +} + +function DogCard({ dog, navigate }) { + const photo = dog.photo_urls?.[0]; + return ( +
navigate(`/dogs/${dog.id}`)} + role="button" + tabIndex={0} + onKeyDown={e => e.key === 'Enter' && navigate(`/dogs/${dog.id}`)} + > +
+ {photo + ? {dog.name} + :
+ } + {dog.is_champion === 1 && 🏆} + Ext +
+
+
+ {dog.is_champion === 1 && } + {dog.name} +
+
{dog.breed}
+
+ {dog.sex === 'male' ? '\u2642 Sire' : '\u2640 Dam'} + {dog.birth_date && <> · {dog.birth_date}} +
+ {(dog.sire || dog.dam) && ( +
+ {dog.sire && S: {dog.sire.name}} + {dog.dam && D: {dog.dam.name}} +
+ )} +
+
+ ); +}