Files
breedr/FRONTEND_FIX_REQUIRED.md

9.9 KiB

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:

const [formData, setFormData] = useState({
  name: '',
  breed: '',
  sex: '',
  sire: '',  // ❌ WRONG - should be sire_id
  dam: '',   // ❌ WRONG - should be dam_id
  // ...
});

Change to:

const [formData, setFormData] = useState({
  name: '',
  breed: '',
  sex: '',
  sire_id: null,  // ✅ CORRECT
  dam_id: null,   // ✅ CORRECT
  // ...
});

2. Check Form Submission

Look for the API call:

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:

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:

<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:

<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:

// ❌ WRONG - trying to access old columns
setFormData({
  ...dog,
  sire: dog.sire?.id || '',
  dam: dog.dam?.id || '',
});

Change to:

// ✅ 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:

if (useLitter && selectedLitter) {
  dogData.sire = selectedLitter.sire_id;  // ❌ WRONG field name
  dogData.dam = selectedLitter.dam_id;    // ❌ WRONG field name
}

Change to:

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.sireformData.sire_id
  6. Form handlers: formData.damformData.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:

{
  "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:

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.