Files
breedr/FRONTEND_FIX_REQUIRED.md

409 lines
9.9 KiB
Markdown

# Frontend Fix Required: Add Dog Form
## Problem
The database and API are correct, but the **Add Dog frontend form** is still trying to use old `sire`/`dam` column names.
## Error Details
Server logs show migration completed successfully:
```
[Validation] ✓ Dogs table has no sire/dam columns
[Validation] ✓ Parents table exists
```
But when adding a dog, you still get the "no such column: sire" error.
## Root Cause
The frontend `DogForm` or `AddDogModal` component is sending:
- `sire` and `dam` (wrong)
But the API expects:
- `sire_id` and `dam_id` (correct)
## Files to Fix
Look for these files in `client/src/`:
- `components/DogForm.jsx`
- `components/AddDogModal.jsx`
- `components/EditDogModal.jsx`
- `pages/DogManagement.jsx`
Or any component that has the Add/Edit Dog form.
## The Fix
### 1. Check Form State
Look for state variables like:
```javascript
const [formData, setFormData] = useState({
name: '',
breed: '',
sex: '',
sire: '', // ❌ WRONG - should be sire_id
dam: '', // ❌ WRONG - should be dam_id
// ...
});
```
**Change to:**
```javascript
const [formData, setFormData] = useState({
name: '',
breed: '',
sex: '',
sire_id: null, // ✅ CORRECT
dam_id: null, // ✅ CORRECT
// ...
});
```
### 2. Check Form Submission
Look for the API call:
```javascript
const response = await fetch('/api/dogs', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: formData.name,
breed: formData.breed,
sex: formData.sex,
sire: formData.sire, // ❌ WRONG
dam: formData.dam, // ❌ WRONG
// ...
})
});
```
**Change to:**
```javascript
const response = await fetch('/api/dogs', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: formData.name,
breed: formData.breed,
sex: formData.sex,
sire_id: formData.sire_id, // ✅ CORRECT
dam_id: formData.dam_id, // ✅ CORRECT
// ...
})
});
```
### 3. Check Select Dropdowns
Look for parent selection dropdowns:
```jsx
<select
value={formData.sire} // WRONG
onChange={(e) => setFormData({ ...formData, sire: e.target.value })} // WRONG
>
<option value="">Select Sire</option>
{males.map(dog => (
<option key={dog.id} value={dog.id}>{dog.name}</option>
))}
</select>
```
**Change to:**
```jsx
<select
value={formData.sire_id || ''} // CORRECT
onChange={(e) => setFormData({
...formData,
sire_id: e.target.value ? parseInt(e.target.value) : null // ✅ CORRECT - convert to number or null
})}
>
<option value="">Select Sire</option>
{males.map(dog => (
<option key={dog.id} value={dog.id}>{dog.name}</option>
))}
</select>
```
### 4. Check Edit Mode
When editing an existing dog:
```javascript
// ❌ WRONG - trying to access old columns
setFormData({
...dog,
sire: dog.sire?.id || '',
dam: dog.dam?.id || '',
});
```
**Change to:**
```javascript
// ✅ CORRECT - use correct field names
setFormData({
...dog,
sire_id: dog.sire?.id || null,
dam_id: dog.dam?.id || null,
});
```
### 5. Check Litter Mode
If the form has litter selection mode:
```javascript
if (useLitter && selectedLitter) {
dogData.sire = selectedLitter.sire_id; // ❌ WRONG field name
dogData.dam = selectedLitter.dam_id; // ❌ WRONG field name
}
```
**Change to:**
```javascript
if (useLitter && selectedLitter) {
dogData.sire_id = selectedLitter.sire_id; // ✅ CORRECT
dogData.dam_id = selectedLitter.dam_id; // ✅ CORRECT
}
```
## Quick Search & Replace
In your frontend code, search for:
1. **State initialization**: `sire: ''``sire_id: null`
2. **State initialization**: `dam: ''``dam_id: null`
3. **API payload**: `sire:``sire_id:`
4. **API payload**: `dam:``dam_id:`
5. **Form handlers**: `formData.sire``formData.sire_id`
6. **Form handlers**: `formData.dam``formData.dam_id`
## Testing After Fix
1. Open Add Dog modal
2. Fill in name, breed, sex
3. Select a sire from dropdown
4. Select a dam from dropdown
5. Click Submit
6. Should work without "sire column" error!
## API Contract (For Reference)
The server `POST /api/dogs` expects:
```json
{
"name": "string",
"breed": "string",
"sex": "male" | "female",
"sire_id": number | null,
"dam_id": number | null,
"litter_id": number | null,
"registration_number": "string" | null,
"birth_date": "YYYY-MM-DD" | null,
"color": "string" | null,
"microchip": "string" | null,
"notes": "string" | null
}
```
## Why This Happens
The database migration fixed the backend, but:
- Frontend code wasn't updated to use new field names
- Old form components still reference `sire`/`dam`
- API expects `sire_id`/`dam_id` (correct)
## Example Complete Fix
Here's a complete example of a corrected form:
```jsx
import React, { useState, useEffect } from 'react';
function DogForm({ dog, onSubmit, onCancel }) {
const [formData, setFormData] = useState({
name: '',
breed: '',
sex: 'male',
birth_date: '',
sire_id: null, // ✅ CORRECT
dam_id: null, // ✅ CORRECT
litter_id: null,
registration_number: '',
microchip: '',
color: '',
notes: ''
});
const [males, setMales] = useState([]);
const [females, setFemales] = useState([]);
useEffect(() => {
// Load existing dogs for parent selection
fetch('/api/dogs')
.then(res => res.json())
.then(dogs => {
setMales(dogs.filter(d => d.sex === 'male'));
setFemales(dogs.filter(d => d.sex === 'female'));
});
// If editing, populate form
if (dog) {
setFormData({
...dog,
sire_id: dog.sire?.id || null, // ✅ CORRECT
dam_id: dog.dam?.id || null, // ✅ CORRECT
birth_date: dog.birth_date || '',
registration_number: dog.registration_number || '',
microchip: dog.microchip || '',
color: dog.color || '',
notes: dog.notes || ''
});
}
}, [dog]);
const handleSubmit = async (e) => {
e.preventDefault();
const payload = {
name: formData.name,
breed: formData.breed,
sex: formData.sex,
sire_id: formData.sire_id, // ✅ CORRECT
dam_id: formData.dam_id, // ✅ CORRECT
litter_id: formData.litter_id,
birth_date: formData.birth_date || null,
registration_number: formData.registration_number || null,
microchip: formData.microchip || null,
color: formData.color || null,
notes: formData.notes || null
};
const url = dog ? `/api/dogs/${dog.id}` : '/api/dogs';
const method = dog ? 'PUT' : 'POST';
try {
const response = await fetch(url, {
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to save dog');
}
const savedDog = await response.json();
onSubmit(savedDog);
} catch (error) {
console.error('Error saving dog:', error);
alert(error.message);
}
};
return (
<form onSubmit={handleSubmit}>
{/* Basic fields */}
<input
type="text"
placeholder="Name"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
required
/>
<input
type="text"
placeholder="Breed"
value={formData.breed}
onChange={(e) => setFormData({ ...formData, breed: e.target.value })}
required
/>
<select
value={formData.sex}
onChange={(e) => setFormData({ ...formData, sex: e.target.value })}
required
>
<option value="male">Male</option>
<option value="female">Female</option>
</select>
{/* Parent selection */}
<label>Sire (Father)</label>
<select
value={formData.sire_id || ''} // CORRECT
onChange={(e) => setFormData({
...formData,
sire_id: e.target.value ? parseInt(e.target.value) : null // ✅ CORRECT
})}
>
<option value="">Select Sire (Optional)</option>
{males.map(dog => (
<option key={dog.id} value={dog.id}>{dog.name}</option>
))}
</select>
<label>Dam (Mother)</label>
<select
value={formData.dam_id || ''} // CORRECT
onChange={(e) => setFormData({
...formData,
dam_id: e.target.value ? parseInt(e.target.value) : null // ✅ CORRECT
})}
>
<option value="">Select Dam (Optional)</option>
{females.map(dog => (
<option key={dog.id} value={dog.name}</option>
))}
</select>
{/* Other fields */}
<input
type="date"
value={formData.birth_date}
onChange={(e) => setFormData({ ...formData, birth_date: e.target.value })}
/>
<input
type="text"
placeholder="Registration Number"
value={formData.registration_number}
onChange={(e) => setFormData({ ...formData, registration_number: e.target.value })}
/>
<input
type="text"
placeholder="Microchip"
value={formData.microchip}
onChange={(e) => setFormData({ ...formData, microchip: e.target.value })}
/>
<input
type="text"
placeholder="Color"
value={formData.color}
onChange={(e) => setFormData({ ...formData, color: e.target.value })}
/>
<textarea
placeholder="Notes"
value={formData.notes}
onChange={(e) => setFormData({ ...formData, notes: e.target.value })}
/>
<button type="submit">{dog ? 'Update' : 'Create'} Dog</button>
<button type="button" onClick={onCancel}>Cancel</button>
</form>
);
}
export default DogForm;
```
## Summary
**Backend is correct** - Database and API use `sire_id`/`dam_id`
**Frontend needs update** - Forms still use `sire`/`dam`
**Fix**: Replace all instances of `sire`/`dam` with `sire_id`/`dam_id` in frontend forms.