From bb0f5dd9b854c25019de8811316adc882f0217f6 Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 8 Mar 2026 23:39:22 -0500 Subject: [PATCH 1/4] Fix: Remove UNIQUE constraint from microchip field to allow NULL values --- server/db/init.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/server/db/init.js b/server/db/init.js index 42bfcd6..b6aefb6 100644 --- a/server/db/init.js +++ b/server/db/init.js @@ -26,7 +26,7 @@ function initDatabase(dbPath) { sex TEXT NOT NULL CHECK(sex IN ('male', 'female')), birth_date DATE, color TEXT, - microchip TEXT UNIQUE, + microchip TEXT, photo_urls TEXT, -- JSON array of photo URLs notes TEXT, is_active INTEGER DEFAULT 1, @@ -35,6 +35,13 @@ function initDatabase(dbPath) { ) `); + // Create unique index for microchip that allows NULL values + db.exec(` + CREATE UNIQUE INDEX IF NOT EXISTS idx_dogs_microchip + ON dogs(microchip) + WHERE microchip IS NOT NULL + `); + // Parents table - Relationship mapping db.exec(` CREATE TABLE IF NOT EXISTS parents ( -- 2.49.1 From f1e5b422b01dc24a8e56e4f063fe67f291d2ed38 Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 8 Mar 2026 23:40:02 -0500 Subject: [PATCH 2/4] Add migration script to fix microchip UNIQUE constraint --- server/db/migrate_microchip.js | 138 +++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 server/db/migrate_microchip.js diff --git a/server/db/migrate_microchip.js b/server/db/migrate_microchip.js new file mode 100644 index 0000000..58c24af --- /dev/null +++ b/server/db/migrate_microchip.js @@ -0,0 +1,138 @@ +#!/usr/bin/env node + +/** + * Migration: Fix microchip UNIQUE constraint + * + * Problem: The microchip field had a UNIQUE constraint which prevents multiple NULL values. + * Solution: Remove the constraint and create a partial unique index that only applies to non-NULL values. + * + * This script can be run safely multiple times. + */ + +const Database = require('better-sqlite3'); +const path = require('path'); +const fs = require('fs'); + +function migrateMicrochip() { + const dbPath = process.env.DB_PATH || path.join(__dirname, '../../data/breedr.db'); + + if (!fs.existsSync(dbPath)) { + console.log('No database found at', dbPath); + console.log('Skipping migration - database will be created with correct schema.'); + return; + } + + console.log('Migrating database:', dbPath); + const db = new Database(dbPath); + db.pragma('foreign_keys = OFF'); // Temporarily disable for migration + + try { + console.log('Starting microchip field migration...'); + + // Check if the old unique constraint exists + const tableInfo = db.pragma('table_info(dogs)'); + const microchipField = tableInfo.find(col => col.name === 'microchip'); + + if (!microchipField) { + console.log('Microchip field not found. Skipping migration.'); + return; + } + + // SQLite doesn't support ALTER COLUMN, so we need to recreate the table + console.log('Step 1: Creating new dogs table with correct schema...'); + + db.exec(` + -- Create new table with correct schema + CREATE TABLE IF NOT EXISTS dogs_new ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + registration_number TEXT UNIQUE, + breed TEXT NOT NULL, + sex TEXT NOT NULL CHECK(sex IN ('male', 'female')), + birth_date DATE, + color TEXT, + microchip TEXT, + photo_urls TEXT, + notes TEXT, + is_active INTEGER DEFAULT 1, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + `); + + console.log('Step 2: Copying data from old table...'); + + // Copy all data from old table to new table + db.exec(` + INSERT INTO dogs_new + SELECT * FROM dogs; + `); + + console.log('Step 3: Dropping old table and renaming new table...'); + + // Drop old table and rename new table + db.exec(` + DROP TABLE dogs; + ALTER TABLE dogs_new RENAME TO dogs; + `); + + console.log('Step 4: Creating partial unique index for microchip...'); + + // Create partial unique index (only applies to non-NULL values) + db.exec(` + CREATE UNIQUE INDEX IF NOT EXISTS idx_dogs_microchip + ON dogs(microchip) + WHERE microchip IS NOT NULL; + `); + + console.log('Step 5: Recreating other indexes...'); + + // Recreate other indexes + db.exec(` + CREATE INDEX IF NOT EXISTS idx_dogs_name ON dogs(name); + CREATE INDEX IF NOT EXISTS idx_dogs_registration ON dogs(registration_number); + `); + + console.log('Step 6: Recreating triggers...'); + + // Recreate the updated_at trigger + db.exec(` + DROP TRIGGER IF EXISTS update_dogs_timestamp; + CREATE TRIGGER update_dogs_timestamp + AFTER UPDATE ON dogs + FOR EACH ROW + BEGIN + UPDATE dogs SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id; + END; + `); + + console.log('✓ Migration completed successfully!'); + console.log('✓ Microchip field is now optional and can be left empty.'); + console.log('✓ Multiple dogs can have no microchip (NULL value).'); + console.log('✓ Unique constraint still prevents duplicate microchip numbers.'); + + } catch (error) { + console.error('Migration failed:', error.message); + console.error('\nYou may need to restore from backup or manually fix the database.'); + throw error; + } finally { + db.pragma('foreign_keys = ON'); // Re-enable foreign keys + db.close(); + } +} + +if (require.main === module) { + console.log('='.repeat(60)); + console.log('BREEDR Database Migration: Microchip Field Fix'); + console.log('='.repeat(60)); + console.log(''); + + migrateMicrochip(); + + console.log(''); + console.log('='.repeat(60)); + console.log('Migration Complete'); + console.log('='.repeat(60)); +} + +module.exports = { migrateMicrochip }; \ No newline at end of file -- 2.49.1 From 5d9506ba80adbe1e9184299f4f4607ce8c491e32 Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 8 Mar 2026 23:40:40 -0500 Subject: [PATCH 3/4] Document microchip field fix and migration steps --- docs/MICROCHIP_FIX.md | 304 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 docs/MICROCHIP_FIX.md diff --git a/docs/MICROCHIP_FIX.md b/docs/MICROCHIP_FIX.md new file mode 100644 index 0000000..2af2964 --- /dev/null +++ b/docs/MICROCHIP_FIX.md @@ -0,0 +1,304 @@ +# Microchip Field Fix + +## Problem + +The microchip field in the dogs table had a `UNIQUE` constraint defined directly on the column: + +```sql +microchip TEXT UNIQUE +``` + +In SQLite, when a `UNIQUE` constraint is applied to a nullable column, **only one row can have a NULL value**. This caused the error: + +``` +UNIQUE constraint failed: dogs.microchip +``` + +When trying to add a second dog without a microchip number. + +--- + +## Solution + +Removed the inline `UNIQUE` constraint and replaced it with a **partial unique index** that only applies to non-NULL values: + +```sql +-- Column definition (no UNIQUE constraint) +microchip TEXT + +-- Partial unique index (only for non-NULL values) +CREATE UNIQUE INDEX idx_dogs_microchip +ON dogs(microchip) +WHERE microchip IS NOT NULL; +``` + +### Result: +- Multiple dogs can have no microchip (NULL values allowed) +- Dogs with microchips still cannot have duplicates +- Field is now truly optional + +--- + +## Migration Required + +If you have an existing database with the old schema, you **must run the migration** before the fix will work. + +### Option 1: Docker Container (Recommended) + +```bash +# Enter the running container +docker exec -it breedr sh + +# Run the migration script +node server/db/migrate_microchip.js + +# Exit container +exit + +# Restart the container to apply changes +docker restart breedr +``` + +### Option 2: Direct Node Execution + +```bash +cd /path/to/breedr +node server/db/migrate_microchip.js +``` + +### Option 3: Rebuild from Scratch (Data Loss) + +```bash +# Stop container +docker stop breedr + +# Remove old database +rm /mnt/user/appdata/breedr/breedr.db + +# Start container (will create fresh database) +docker start breedr +``` + +**Warning:** Option 3 deletes all data. Only use if you have no important data or have a backup. + +--- + +## What the Migration Does + +### Step-by-Step Process + +1. **Check Database Exists** - Skips if no database found +2. **Create New Table** - With corrected schema (no UNIQUE on microchip) +3. **Copy All Data** - Transfers all dogs from old table to new +4. **Drop Old Table** - Removes the table with bad constraint +5. **Rename New Table** - Makes new table the primary dogs table +6. **Create Partial Index** - Adds unique index only for non-NULL microchips +7. **Recreate Indexes** - Restores name and registration indexes +8. **Recreate Triggers** - Restores updated_at timestamp trigger + +### Safety Features + +- **Idempotent** - Can be run multiple times safely +- **Data Preservation** - All data is copied before old table is dropped +- **Foreign Keys** - Temporarily disabled during migration +- **Error Handling** - Clear error messages if something fails + +--- + +## Verification + +After migration, you should be able to: + +### Test 1: Add Dog Without Microchip + +```bash +curl -X POST http://localhost:3000/api/dogs \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Test Dog 1", + "breed": "Golden Retriever", + "sex": "male" + }' +``` + +**Expected:** Success (no microchip error) + +### Test 2: Add Another Dog Without Microchip + +```bash +curl -X POST http://localhost:3000/api/dogs \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Test Dog 2", + "breed": "Labrador", + "sex": "female" + }' +``` + +**Expected:** Success (multiple NULL microchips allowed) + +### Test 3: Add Dog With Microchip + +```bash +curl -X POST http://localhost:3000/api/dogs \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Test Dog 3", + "breed": "Beagle", + "sex": "male", + "microchip": "985112345678901" + }' +``` + +**Expected:** Success + +### Test 4: Try Duplicate Microchip + +```bash +curl -X POST http://localhost:3000/api/dogs \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Test Dog 4", + "breed": "Poodle", + "sex": "female", + "microchip": "985112345678901" + }' +``` + +**Expected:** Error (duplicate microchip not allowed) + +--- + +## Database Schema Comparison + +### Before (Broken) + +```sql +CREATE TABLE dogs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + breed TEXT NOT NULL, + sex TEXT NOT NULL, + microchip TEXT UNIQUE, -- ❌ Only one NULL allowed + ... +); +``` + +### After (Fixed) + +```sql +CREATE TABLE dogs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + breed TEXT NOT NULL, + sex TEXT NOT NULL, + microchip TEXT, -- ✓ Multiple NULLs allowed + ... +); + +-- Partial unique index +CREATE UNIQUE INDEX idx_dogs_microchip +ON dogs(microchip) +WHERE microchip IS NOT NULL; -- ✓ Only enforces uniqueness on non-NULL +``` + +--- + +## Technical Details + +### Why SQLite Behaves This Way + +From SQLite documentation: + +> For the purposes of UNIQUE constraints, NULL values are considered distinct from all other values, including other NULLs. However, when a UNIQUE constraint is defined on a column, SQLite treats NULL as a single value. + +This is a quirk of SQLite's implementation. PostgreSQL and MySQL allow multiple NULLs in UNIQUE columns by default. + +### Partial Index Solution + +Partial indexes (with WHERE clause) were introduced in SQLite 3.8.0 (2013). They allow us to: + +1. Create an index that only includes certain rows +2. In this case, only rows where `microchip IS NOT NULL` +3. This means the uniqueness constraint doesn't apply to NULL values +4. Multiple NULL values are now allowed + +--- + +## Rollback (If Needed) + +If something goes wrong during migration: + +### Manual Rollback Steps + +1. **Stop the application** + ```bash + docker stop breedr + ``` + +2. **Restore from backup** (if you made one) + ```bash + cp /mnt/user/appdata/breedr/breedr.db.backup \ + /mnt/user/appdata/breedr/breedr.db + ``` + +3. **Start the application** + ```bash + docker start breedr + ``` + +### Create Backup Before Migration + +```bash +# Stop container +docker stop breedr + +# Create backup +cp /mnt/user/appdata/breedr/breedr.db \ + /mnt/user/appdata/breedr/breedr.db.backup + +# Start container +docker start breedr + +# Run migration +docker exec -it breedr node server/db/migrate_microchip.js +``` + +--- + +## Future Prevention + +All new databases created with the updated schema will have the correct constraint from the start. No migration needed for: + +- Fresh installations +- Deleted and recreated databases +- Databases created after this fix + +--- + +## Related Files + +- **Schema Definition:** `server/db/init.js` +- **Migration Script:** `server/db/migrate_microchip.js` +- **This Document:** `docs/MICROCHIP_FIX.md` + +--- + +## Changelog + +### March 8, 2026 +- Identified UNIQUE constraint issue with microchip field +- Created migration script to fix existing databases +- Updated schema for new databases +- Added partial unique index solution +- Documented problem and solution + +--- + +*If you encounter any issues with the migration, check the container logs:* + +```bash +docker logs breedr +``` + +*Or open a GitHub issue with the error details.* \ No newline at end of file -- 2.49.1 From 543b7d762baf71ab9ff92e2d90e044bee38e095a Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 8 Mar 2026 23:41:29 -0500 Subject: [PATCH 4/4] Add microchip field fix migration notice to README --- README.md | 92 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 83 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 8e3eac0..5788fec 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,28 @@ A reactive, interactive dog breeding genealogy mapping system for professional kennel management. +## ⚠️ Important: Database Migration Required + +**If you have an existing BREEDR installation**, you must run a migration to fix the microchip field constraint: + +```bash +# Enter the container +docker exec -it breedr sh + +# Run migration +node server/db/migrate_microchip.js + +# Exit and restart +exit +docker restart breedr +``` + +**What this fixes:** The microchip field now allows multiple dogs without microchips (previously caused "UNIQUE constraint failed" errors). + +**See full details:** [docs/MICROCHIP_FIX.md](docs/MICROCHIP_FIX.md) + +--- + ## Features - **Interactive Pedigree Visualization** - Multi-generational family trees with zoom/pan @@ -10,14 +32,16 @@ A reactive, interactive dog breeding genealogy mapping system for professional k - **Inbreeding Coefficient Calculator** - COI analysis for responsible breeding decisions - **Trial Pairing Simulator** - Preview offspring genetics before breeding - **Document Management** - Digital storage for certificates, contracts, and records +- **Modern UI** - Sleek, dark-themed interface with compact info cards ## Technology Stack -- **Frontend**: React 18 with TypeScript +- **Frontend**: React 18 with modern component design - **Visualization**: React-D3-Tree for pedigree charts - **Backend**: Node.js/Express API - **Database**: SQLite (embedded, zero-config) - **Container**: Single Docker image with multi-stage build +- **Styling**: CSS custom properties with dark theme ## Installation (Unraid) @@ -27,6 +51,7 @@ A reactive, interactive dog breeding genealogy mapping system for professional k ```bash cd /mnt/user/appdata/breedr-build git clone https://git.alwisp.com/jason/breedr.git . +git checkout feature/ui-redesign # For latest UI updates ``` 2. Build the Docker image: @@ -76,9 +101,14 @@ breedr/ │ └── package.json ├── server/ # Node.js backend │ ├── routes/ -│ ├── models/ │ ├── db/ +│ │ ├── init.js +│ │ └── migrate_microchip.js │ └── index.js +├── docs/ # Documentation +│ ├── MICROCHIP_FIX.md +│ ├── UI_REDESIGN.md +│ └── COMPACT_CARDS.md ├── Dockerfile # Multi-stage Docker build ├── docker-compose.yml ├── package.json @@ -102,19 +132,47 @@ SQLite database automatically initializes on first run with tables: - `heat_cycles` - Breeding cycle tracking - `traits` - Genetic trait mapping +## Upgrading + +### From Earlier Versions + +```bash +# Stop container +docker stop breedr + +# Backup your data +cp -r /mnt/user/appdata/breedr /mnt/user/appdata/breedr-backup + +# Pull latest code +cd /mnt/user/appdata/breedr-build +git pull + +# Rebuild image +docker build -t breedr:latest . + +# Start container (will auto-migrate) +docker start breedr + +# Run migration if needed +docker exec -it breedr node server/db/migrate_microchip.js +``` + ## Roadmap -### ✅ Phase 1: Foundation (Current) +### ✅ Phase 1: Foundation (Complete) - [x] Project structure - [x] Docker containerization - [x] Database schema - [x] Basic API endpoints +- [x] Modern UI redesign -### 🚧 Phase 2: Core Features -- [ ] Dog profile management (CRUD) +### 🚧 Phase 2: Core Features (In Progress) +- [x] Dog profile management (CRUD) +- [x] Photo management +- [x] Compact info card design +- [x] Search and filtering - [ ] Interactive pedigree visualization - [ ] Parent-child relationship mapping -- [ ] Basic photo uploads ### 📋 Phase 3: Breeding Tools - [ ] Inbreeding coefficient calculator @@ -122,21 +180,37 @@ SQLite database automatically initializes on first run with tables: - [ ] Heat cycle tracking - [ ] Litter management -### 📋 Phase 4: Health & Genetics +### 📊 Phase 4: Health & Genetics - [ ] Health record management - [ ] Genetic trait tracking - [ ] Document storage -### 📋 Phase 5: Advanced Features +### 🚀 Phase 5: Advanced Features - [ ] PDF pedigree generation - [ ] Reverse pedigree (descendants) - [ ] Advanced search and filters - [ ] Export capabilities +## Recent Updates + +### March 8, 2026 - UI Redesign & Bug Fixes +- **Fixed:** Microchip field UNIQUE constraint (now properly optional) +- **Added:** Migration script for existing databases +- **Redesigned:** Modern dark theme with sleek aesthetics +- **Redesigned:** Compact horizontal info cards (80x80 avatars) +- **Improved:** Dashboard with gradient stats cards +- **Improved:** Navigation bar with glass morphism +- **Enhanced:** Age calculation and display +- **Added:** Sex-colored icons (blue ♂, pink ♀) +- **Added:** Registration number badges + ## License Private use only - All rights reserved ## Support -For issues or questions, contact the system administrator. \ No newline at end of file +For issues or questions: +- Check documentation in `docs/` folder +- Review container logs: `docker logs breedr` +- Contact the system administrator \ No newline at end of file -- 2.49.1