feat: rebuild LitterList with create/edit/delete and LitterForm integration
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Activity } from 'lucide-react'
|
||||
import { Activity, Plus, Edit2, Trash2, ChevronRight } from 'lucide-react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import axios from 'axios'
|
||||
import LitterForm from '../components/LitterForm'
|
||||
|
||||
function LitterList() {
|
||||
const [litters, setLitters] = useState([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [showForm, setShowForm] = useState(false)
|
||||
const [editingLitter, setEditingLitter] = useState(null)
|
||||
const [prefill, setPrefill] = useState(null)
|
||||
const navigate = useNavigate()
|
||||
|
||||
useEffect(() => {
|
||||
fetchLitters()
|
||||
@@ -14,47 +20,126 @@ function LitterList() {
|
||||
try {
|
||||
const res = await axios.get('/api/litters')
|
||||
setLitters(res.data)
|
||||
setLoading(false)
|
||||
} catch (error) {
|
||||
console.error('Error fetching litters:', error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleCreate = () => {
|
||||
setEditingLitter(null)
|
||||
setPrefill(null)
|
||||
setShowForm(true)
|
||||
}
|
||||
|
||||
const handleEdit = (e, litter) => {
|
||||
e.stopPropagation()
|
||||
setEditingLitter(litter)
|
||||
setPrefill(null)
|
||||
setShowForm(true)
|
||||
}
|
||||
|
||||
const handleDelete = async (e, id) => {
|
||||
e.stopPropagation()
|
||||
if (!window.confirm('Delete this litter record? Puppies will be unlinked but not deleted.')) return
|
||||
try {
|
||||
await axios.delete(`/api/litters/${id}`)
|
||||
fetchLitters()
|
||||
} catch (error) {
|
||||
console.error('Error deleting litter:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
fetchLitters()
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <div className="container loading">Loading litters...</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<h1 style={{ marginBottom: '2rem' }}>Litters</h1>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '2rem' }}>
|
||||
<h1>Litters</h1>
|
||||
<button className="btn btn-primary" onClick={handleCreate}>
|
||||
<Plus size={18} style={{ marginRight: '0.5rem' }} />
|
||||
New Litter
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{litters.length === 0 ? (
|
||||
<div className="card" style={{ textAlign: 'center', padding: '4rem' }}>
|
||||
<Activity size={64} style={{ color: 'var(--text-secondary)', margin: '0 auto 1rem' }} />
|
||||
<h2>No litters recorded yet</h2>
|
||||
<p style={{ color: 'var(--text-secondary)' }}>Start tracking breeding records</p>
|
||||
<p style={{ color: 'var(--text-secondary)', marginBottom: '1.5rem' }}>Create a litter after a breeding cycle to track puppies</p>
|
||||
<button className="btn btn-primary" onClick={handleCreate}>
|
||||
<Plus size={18} style={{ marginRight: '0.5rem' }} />
|
||||
Create First Litter
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ display: 'grid', gap: '1rem' }}>
|
||||
{litters.map(litter => (
|
||||
<div key={litter.id} className="card">
|
||||
<h3>{litter.sire_name} × {litter.dam_name}</h3>
|
||||
<p style={{ color: 'var(--text-secondary)', marginTop: '0.5rem' }}>
|
||||
Breeding Date: {new Date(litter.breeding_date).toLocaleDateString()}
|
||||
</p>
|
||||
{litter.whelping_date && (
|
||||
<p style={{ color: 'var(--text-secondary)' }}>
|
||||
Whelping Date: {new Date(litter.whelping_date).toLocaleDateString()}
|
||||
</p>
|
||||
)}
|
||||
<p style={{ marginTop: '0.5rem' }}>
|
||||
<strong>Puppies:</strong> {litter.puppy_count || litter.puppies?.length || 0}
|
||||
</p>
|
||||
<div
|
||||
key={litter.id}
|
||||
className="card"
|
||||
style={{ cursor: 'pointer', transition: 'border-color 0.2s' }}
|
||||
onClick={() => navigate(`/litters/${litter.id}`)}
|
||||
>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<h3 style={{ marginBottom: '0.5rem' }}>
|
||||
🐾 {litter.sire_name} × {litter.dam_name}
|
||||
</h3>
|
||||
<div style={{ display: 'flex', gap: '1.5rem', flexWrap: 'wrap', color: 'var(--text-secondary)', fontSize: '0.9rem' }}>
|
||||
<span>📅 Bred: {new Date(litter.breeding_date).toLocaleDateString()}</span>
|
||||
{litter.whelping_date && (
|
||||
<span>🐕 Whelped: {new Date(litter.whelping_date).toLocaleDateString()}</span>
|
||||
)}
|
||||
<span style={{ color: 'var(--accent)', fontWeight: 600 }}>
|
||||
{litter.actual_puppy_count ?? litter.puppies?.length ?? litter.puppy_count ?? 0} puppies
|
||||
</span>
|
||||
</div>
|
||||
{litter.notes && (
|
||||
<p style={{ marginTop: '0.5rem', fontSize: '0.85rem', color: 'var(--text-secondary)', fontStyle: 'italic' }}>
|
||||
{litter.notes}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
|
||||
<button
|
||||
className="btn-icon"
|
||||
title="Edit litter"
|
||||
onClick={(e) => handleEdit(e, litter)}
|
||||
>
|
||||
<Edit2 size={16} />
|
||||
</button>
|
||||
<button
|
||||
className="btn-icon"
|
||||
title="Delete litter"
|
||||
onClick={(e) => handleDelete(e, litter.id)}
|
||||
style={{ color: '#e53e3e' }}
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</button>
|
||||
<ChevronRight size={20} style={{ color: 'var(--text-secondary)' }} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showForm && (
|
||||
<LitterForm
|
||||
litter={editingLitter}
|
||||
prefill={prefill}
|
||||
onClose={() => setShowForm(false)}
|
||||
onSave={handleSave}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user