feature/ui-redesign #5
304
docs/VERIFICATION_CHECKLIST.md
Normal file
304
docs/VERIFICATION_CHECKLIST.md
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
# BREEDR Verification Checklist
|
||||||
|
|
||||||
|
## Microchip Field Fix Verification
|
||||||
|
|
||||||
|
### ✅ Schema Files (All Correct)
|
||||||
|
|
||||||
|
#### 1. Database Schema: `server/db/init.js`
|
||||||
|
- [x] **Line 29:** `microchip TEXT,` (no UNIQUE constraint)
|
||||||
|
- [x] **Lines 38-43:** Partial unique index created
|
||||||
|
```sql
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_dogs_microchip
|
||||||
|
ON dogs(microchip)
|
||||||
|
WHERE microchip IS NOT NULL
|
||||||
|
```
|
||||||
|
|
||||||
|
**Status:** ✅ Correct for future installations
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 2. UI Form: `client/src/components/DogForm.jsx`
|
||||||
|
- [x] **Line 150:** Microchip input has NO `required` attribute
|
||||||
|
- [x] Label shows "Microchip Number" (no asterisk)
|
||||||
|
- [x] Field is truly optional in the UI
|
||||||
|
|
||||||
|
**Status:** ✅ Correct - users can leave microchip blank
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 3. Migration Script: `server/db/migrate_microchip.js`
|
||||||
|
- [x] Exists and is executable
|
||||||
|
- [x] Safely migrates existing databases
|
||||||
|
- [x] Idempotent (can run multiple times)
|
||||||
|
- [x] Preserves all data during migration
|
||||||
|
|
||||||
|
**Status:** ✅ Available for existing installations
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 4. Migration Helper: `migrate-now.sh`
|
||||||
|
- [x] Shell script for easy execution
|
||||||
|
- [x] Checks if container is running
|
||||||
|
- [x] Runs migration inside container
|
||||||
|
- [x] Restarts container after migration
|
||||||
|
|
||||||
|
**Status:** ✅ User-friendly migration tool
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 5. Documentation: `docs/MICROCHIP_FIX.md`
|
||||||
|
- [x] Problem explanation
|
||||||
|
- [x] Solution details
|
||||||
|
- [x] Migration instructions (3 options)
|
||||||
|
- [x] Verification tests
|
||||||
|
- [x] Troubleshooting guide
|
||||||
|
|
||||||
|
**Status:** ✅ Complete documentation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 6. README: `README.md`
|
||||||
|
- [x] Migration notice at top
|
||||||
|
- [x] Link to detailed documentation
|
||||||
|
- [x] Upgrade instructions included
|
||||||
|
- [x] Recent updates section added
|
||||||
|
|
||||||
|
**Status:** ✅ Users will see migration notice
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## For Future Installations
|
||||||
|
|
||||||
|
### Fresh Install (No Migration Needed)
|
||||||
|
|
||||||
|
When a user does a **fresh install** (no existing database):
|
||||||
|
|
||||||
|
1. Container starts
|
||||||
|
2. `server/db/init.js` runs automatically
|
||||||
|
3. Database created with **correct schema**
|
||||||
|
4. Microchip field is **optional from the start**
|
||||||
|
5. No migration required ✅
|
||||||
|
|
||||||
|
### Existing Installation (Migration Required)
|
||||||
|
|
||||||
|
When a user **upgrades** from an old version:
|
||||||
|
|
||||||
|
1. Pull latest code
|
||||||
|
2. Rebuild Docker image
|
||||||
|
3. Start container
|
||||||
|
4. **Must run migration:** `docker exec -it breedr node server/db/migrate_microchip.js`
|
||||||
|
5. Restart container
|
||||||
|
6. Microchip field now optional ✅
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing Checklist
|
||||||
|
|
||||||
|
### Test 1: Add Dog Without Microchip
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:3000/api/dogs \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"name":"TestDog1","breed":"Lab","sex":"male"}'
|
||||||
|
```
|
||||||
|
**Expected:** ✅ Success (201 Created)
|
||||||
|
|
||||||
|
### Test 2: Add Multiple Dogs Without Microchips
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:3000/api/dogs \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"name":"TestDog2","breed":"Lab","sex":"female"}'
|
||||||
|
```
|
||||||
|
**Expected:** ✅ Success (multiple NULL values allowed)
|
||||||
|
|
||||||
|
### Test 3: Add Dog With Microchip
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:3000/api/dogs \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"name":"TestDog3","breed":"Lab","sex":"male","microchip":"123456789"}'
|
||||||
|
```
|
||||||
|
**Expected:** ✅ Success
|
||||||
|
|
||||||
|
### Test 4: Duplicate Microchip Should Fail
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:3000/api/dogs \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"name":"TestDog4","breed":"Lab","sex":"female","microchip":"123456789"}'
|
||||||
|
```
|
||||||
|
**Expected:** ❌ Error (UNIQUE constraint still enforced for non-NULL)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Database Verification
|
||||||
|
|
||||||
|
### Check Schema Directly
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enter container
|
||||||
|
docker exec -it breedr sh
|
||||||
|
|
||||||
|
# Open SQLite CLI
|
||||||
|
sqlite3 /app/data/breedr.db
|
||||||
|
|
||||||
|
# Check table schema
|
||||||
|
.schema dogs
|
||||||
|
|
||||||
|
# Should show:
|
||||||
|
# microchip TEXT, (no UNIQUE)
|
||||||
|
|
||||||
|
# Check indexes
|
||||||
|
.indexes dogs
|
||||||
|
|
||||||
|
# Should show:
|
||||||
|
# idx_dogs_microchip (partial index)
|
||||||
|
|
||||||
|
# Verify partial index
|
||||||
|
SELECT sql FROM sqlite_master
|
||||||
|
WHERE type='index' AND name='idx_dogs_microchip';
|
||||||
|
|
||||||
|
# Should show:
|
||||||
|
# CREATE UNIQUE INDEX idx_dogs_microchip
|
||||||
|
# ON dogs(microchip) WHERE microchip IS NOT NULL
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rollback Plan
|
||||||
|
|
||||||
|
If something goes wrong:
|
||||||
|
|
||||||
|
### Option A: Restore Backup
|
||||||
|
```bash
|
||||||
|
docker stop breedr
|
||||||
|
cp /mnt/user/appdata/breedr/breedr.db.backup \
|
||||||
|
/mnt/user/appdata/breedr/breedr.db
|
||||||
|
docker start breedr
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option B: Re-run Migration
|
||||||
|
```bash
|
||||||
|
docker exec -it breedr node server/db/migrate_microchip.js
|
||||||
|
docker restart breedr
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option C: Fresh Database (Data Loss)
|
||||||
|
```bash
|
||||||
|
docker stop breedr
|
||||||
|
rm /mnt/user/appdata/breedr/breedr.db
|
||||||
|
docker start breedr
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deployment Verification
|
||||||
|
|
||||||
|
After deploying to production:
|
||||||
|
|
||||||
|
- [ ] Check container logs for schema initialization
|
||||||
|
- [ ] Verify database schema with SQLite CLI
|
||||||
|
- [ ] Test adding dog without microchip via UI
|
||||||
|
- [ ] Test adding dog with microchip via UI
|
||||||
|
- [ ] Confirm no UNIQUE constraint errors
|
||||||
|
- [ ] Verify partial index exists
|
||||||
|
- [ ] Test duplicate microchip still fails
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Issues
|
||||||
|
|
||||||
|
### Issue: Still Getting UNIQUE Constraint Error
|
||||||
|
|
||||||
|
**Cause:** Migration not run on existing database
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
docker exec -it breedr node server/db/migrate_microchip.js
|
||||||
|
docker restart breedr
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue: Migration Script Not Found
|
||||||
|
|
||||||
|
**Cause:** Old code still in container
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
cd /mnt/user/appdata/breedr-build
|
||||||
|
git pull
|
||||||
|
docker build -t breedr:latest .
|
||||||
|
docker stop breedr && docker rm breedr
|
||||||
|
# Recreate container with new image
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue: Microchip Required in UI
|
||||||
|
|
||||||
|
**Cause:** Browser cached old JavaScript
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
- Hard refresh: Ctrl+Shift+R (Windows/Linux) or Cmd+Shift+R (Mac)
|
||||||
|
- Clear browser cache
|
||||||
|
- Try incognito/private window
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Manifest
|
||||||
|
|
||||||
|
### Core Files (Must Be Present)
|
||||||
|
- ✅ `server/db/init.js` - Database schema (corrected)
|
||||||
|
- ✅ `server/db/migrate_microchip.js` - Migration script
|
||||||
|
- ✅ `client/src/components/DogForm.jsx` - UI form (microchip optional)
|
||||||
|
- ✅ `migrate-now.sh` - Helper script
|
||||||
|
- ✅ `docs/MICROCHIP_FIX.md` - Detailed documentation
|
||||||
|
- ✅ `docs/VERIFICATION_CHECKLIST.md` - This file
|
||||||
|
- ✅ `README.md` - Updated with migration notice
|
||||||
|
|
||||||
|
### Validation Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check all files exist
|
||||||
|
ls -la server/db/init.js
|
||||||
|
ls -la server/db/migrate_microchip.js
|
||||||
|
ls -la client/src/components/DogForm.jsx
|
||||||
|
ls -la migrate-now.sh
|
||||||
|
ls -la docs/MICROCHIP_FIX.md
|
||||||
|
ls -la docs/VERIFICATION_CHECKLIST.md
|
||||||
|
|
||||||
|
# Verify microchip field in init.js
|
||||||
|
grep -n "microchip TEXT" server/db/init.js
|
||||||
|
# Should show line 29 with NO UNIQUE
|
||||||
|
|
||||||
|
# Verify partial index
|
||||||
|
grep -A2 "idx_dogs_microchip" server/db/init.js
|
||||||
|
# Should show WHERE clause
|
||||||
|
|
||||||
|
# Verify form has no required
|
||||||
|
grep -n 'name="microchip"' client/src/components/DogForm.jsx
|
||||||
|
# Should NOT have required attribute
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sign-Off
|
||||||
|
|
||||||
|
### Pre-Deployment Checklist
|
||||||
|
- [x] Schema file correct (no UNIQUE on microchip)
|
||||||
|
- [x] Partial index created (WHERE IS NOT NULL)
|
||||||
|
- [x] UI form allows empty microchip
|
||||||
|
- [x] Migration script tested
|
||||||
|
- [x] Documentation complete
|
||||||
|
- [x] README updated
|
||||||
|
- [x] Tests passing
|
||||||
|
|
||||||
|
### Post-Deployment Checklist
|
||||||
|
- [ ] Container started successfully
|
||||||
|
- [ ] Schema verified in database
|
||||||
|
- [ ] UI allows empty microchip
|
||||||
|
- [ ] Multiple NULL values work
|
||||||
|
- [ ] Unique constraint still enforced for non-NULL
|
||||||
|
- [ ] No errors in logs
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Verified:** March 8, 2026
|
||||||
|
|
||||||
|
**Status:** ✅ All files correct for future installations
|
||||||
|
|
||||||
|
**Migration Required:** Only for existing databases (one-time)
|
||||||
37
migrate-now.sh
Normal file
37
migrate-now.sh
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Quick migration script for microchip field fix
|
||||||
|
# Run this after deploying the updated code
|
||||||
|
|
||||||
|
echo "======================================================"
|
||||||
|
echo "BREEDR: Microchip Field Migration"
|
||||||
|
echo "======================================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if container is running
|
||||||
|
if ! docker ps | grep -q breedr; then
|
||||||
|
echo "Error: breedr container is not running"
|
||||||
|
echo "Please start the container first: docker start breedr"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Running migration inside container..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
docker exec breedr node server/db/migrate_microchip.js
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo ""
|
||||||
|
echo "======================================================"
|
||||||
|
echo "Migration completed successfully!"
|
||||||
|
echo "======================================================"
|
||||||
|
echo ""
|
||||||
|
echo "Restarting container to apply changes..."
|
||||||
|
docker restart breedr
|
||||||
|
echo ""
|
||||||
|
echo "Done! Microchip field is now optional."
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo "Migration failed. Check the error above."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -32,6 +32,11 @@ const upload = multer({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Helper function to convert empty strings to null
|
||||||
|
const emptyToNull = (value) => {
|
||||||
|
return (value === '' || value === undefined) ? null : value;
|
||||||
|
};
|
||||||
|
|
||||||
// GET all dogs
|
// GET all dogs
|
||||||
router.get('/', (req, res) => {
|
router.get('/', (req, res) => {
|
||||||
try {
|
try {
|
||||||
@@ -96,10 +101,21 @@ router.post('/', (req, res) => {
|
|||||||
|
|
||||||
const db = getDatabase();
|
const db = getDatabase();
|
||||||
|
|
||||||
|
// Convert empty strings to null for optional fields
|
||||||
const result = db.prepare(`
|
const result = db.prepare(`
|
||||||
INSERT INTO dogs (name, registration_number, breed, sex, birth_date, color, microchip, notes, photo_urls)
|
INSERT INTO dogs (name, registration_number, breed, sex, birth_date, color, microchip, notes, photo_urls)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
`).run(name, registration_number, breed, sex, birth_date, color, microchip, notes, '[]');
|
`).run(
|
||||||
|
name,
|
||||||
|
emptyToNull(registration_number),
|
||||||
|
breed,
|
||||||
|
sex,
|
||||||
|
emptyToNull(birth_date),
|
||||||
|
emptyToNull(color),
|
||||||
|
emptyToNull(microchip), // Convert empty string to NULL
|
||||||
|
emptyToNull(notes),
|
||||||
|
'[]'
|
||||||
|
);
|
||||||
|
|
||||||
const dogId = result.lastInsertRowid;
|
const dogId = result.lastInsertRowid;
|
||||||
|
|
||||||
@@ -127,12 +143,23 @@ router.put('/:id', (req, res) => {
|
|||||||
|
|
||||||
const db = getDatabase();
|
const db = getDatabase();
|
||||||
|
|
||||||
|
// Convert empty strings to null for optional fields
|
||||||
db.prepare(`
|
db.prepare(`
|
||||||
UPDATE dogs
|
UPDATE dogs
|
||||||
SET name = ?, registration_number = ?, breed = ?, sex = ?,
|
SET name = ?, registration_number = ?, breed = ?, sex = ?,
|
||||||
birth_date = ?, color = ?, microchip = ?, notes = ?
|
birth_date = ?, color = ?, microchip = ?, notes = ?
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
`).run(name, registration_number, breed, sex, birth_date, color, microchip, notes, req.params.id);
|
`).run(
|
||||||
|
name,
|
||||||
|
emptyToNull(registration_number),
|
||||||
|
breed,
|
||||||
|
sex,
|
||||||
|
emptyToNull(birth_date),
|
||||||
|
emptyToNull(color),
|
||||||
|
emptyToNull(microchip), // Convert empty string to NULL
|
||||||
|
emptyToNull(notes),
|
||||||
|
req.params.id
|
||||||
|
);
|
||||||
|
|
||||||
// Update parent relationships
|
// Update parent relationships
|
||||||
db.prepare('DELETE FROM parents WHERE dog_id = ?').run(req.params.id);
|
db.prepare('DELETE FROM parents WHERE dog_id = ?').run(req.params.id);
|
||||||
|
|||||||
Reference in New Issue
Block a user