Merge pull request 'feature/ui-redesign' (#4) from feature/ui-redesign into master
Reviewed-on: #4
This commit was merged in pull request #4.
This commit is contained in:
92
README.md
92
README.md
@@ -2,6 +2,28 @@
|
|||||||
|
|
||||||
A reactive, interactive dog breeding genealogy mapping system for professional kennel management.
|
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
|
## Features
|
||||||
|
|
||||||
- **Interactive Pedigree Visualization** - Multi-generational family trees with zoom/pan
|
- **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
|
- **Inbreeding Coefficient Calculator** - COI analysis for responsible breeding decisions
|
||||||
- **Trial Pairing Simulator** - Preview offspring genetics before breeding
|
- **Trial Pairing Simulator** - Preview offspring genetics before breeding
|
||||||
- **Document Management** - Digital storage for certificates, contracts, and records
|
- **Document Management** - Digital storage for certificates, contracts, and records
|
||||||
|
- **Modern UI** - Sleek, dark-themed interface with compact info cards
|
||||||
|
|
||||||
## Technology Stack
|
## Technology Stack
|
||||||
|
|
||||||
- **Frontend**: React 18 with TypeScript
|
- **Frontend**: React 18 with modern component design
|
||||||
- **Visualization**: React-D3-Tree for pedigree charts
|
- **Visualization**: React-D3-Tree for pedigree charts
|
||||||
- **Backend**: Node.js/Express API
|
- **Backend**: Node.js/Express API
|
||||||
- **Database**: SQLite (embedded, zero-config)
|
- **Database**: SQLite (embedded, zero-config)
|
||||||
- **Container**: Single Docker image with multi-stage build
|
- **Container**: Single Docker image with multi-stage build
|
||||||
|
- **Styling**: CSS custom properties with dark theme
|
||||||
|
|
||||||
## Installation (Unraid)
|
## Installation (Unraid)
|
||||||
|
|
||||||
@@ -27,6 +51,7 @@ A reactive, interactive dog breeding genealogy mapping system for professional k
|
|||||||
```bash
|
```bash
|
||||||
cd /mnt/user/appdata/breedr-build
|
cd /mnt/user/appdata/breedr-build
|
||||||
git clone https://git.alwisp.com/jason/breedr.git .
|
git clone https://git.alwisp.com/jason/breedr.git .
|
||||||
|
git checkout feature/ui-redesign # For latest UI updates
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Build the Docker image:
|
2. Build the Docker image:
|
||||||
@@ -76,9 +101,14 @@ breedr/
|
|||||||
│ └── package.json
|
│ └── package.json
|
||||||
├── server/ # Node.js backend
|
├── server/ # Node.js backend
|
||||||
│ ├── routes/
|
│ ├── routes/
|
||||||
│ ├── models/
|
|
||||||
│ ├── db/
|
│ ├── db/
|
||||||
|
│ │ ├── init.js
|
||||||
|
│ │ └── migrate_microchip.js
|
||||||
│ └── index.js
|
│ └── index.js
|
||||||
|
├── docs/ # Documentation
|
||||||
|
│ ├── MICROCHIP_FIX.md
|
||||||
|
│ ├── UI_REDESIGN.md
|
||||||
|
│ └── COMPACT_CARDS.md
|
||||||
├── Dockerfile # Multi-stage Docker build
|
├── Dockerfile # Multi-stage Docker build
|
||||||
├── docker-compose.yml
|
├── docker-compose.yml
|
||||||
├── package.json
|
├── package.json
|
||||||
@@ -102,19 +132,47 @@ SQLite database automatically initializes on first run with tables:
|
|||||||
- `heat_cycles` - Breeding cycle tracking
|
- `heat_cycles` - Breeding cycle tracking
|
||||||
- `traits` - Genetic trait mapping
|
- `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
|
## Roadmap
|
||||||
|
|
||||||
### ✅ Phase 1: Foundation (Current)
|
### ✅ Phase 1: Foundation (Complete)
|
||||||
- [x] Project structure
|
- [x] Project structure
|
||||||
- [x] Docker containerization
|
- [x] Docker containerization
|
||||||
- [x] Database schema
|
- [x] Database schema
|
||||||
- [x] Basic API endpoints
|
- [x] Basic API endpoints
|
||||||
|
- [x] Modern UI redesign
|
||||||
|
|
||||||
### 🚧 Phase 2: Core Features
|
### 🚧 Phase 2: Core Features (In Progress)
|
||||||
- [ ] Dog profile management (CRUD)
|
- [x] Dog profile management (CRUD)
|
||||||
|
- [x] Photo management
|
||||||
|
- [x] Compact info card design
|
||||||
|
- [x] Search and filtering
|
||||||
- [ ] Interactive pedigree visualization
|
- [ ] Interactive pedigree visualization
|
||||||
- [ ] Parent-child relationship mapping
|
- [ ] Parent-child relationship mapping
|
||||||
- [ ] Basic photo uploads
|
|
||||||
|
|
||||||
### 📋 Phase 3: Breeding Tools
|
### 📋 Phase 3: Breeding Tools
|
||||||
- [ ] Inbreeding coefficient calculator
|
- [ ] Inbreeding coefficient calculator
|
||||||
@@ -122,21 +180,37 @@ SQLite database automatically initializes on first run with tables:
|
|||||||
- [ ] Heat cycle tracking
|
- [ ] Heat cycle tracking
|
||||||
- [ ] Litter management
|
- [ ] Litter management
|
||||||
|
|
||||||
### 📋 Phase 4: Health & Genetics
|
### 📊 Phase 4: Health & Genetics
|
||||||
- [ ] Health record management
|
- [ ] Health record management
|
||||||
- [ ] Genetic trait tracking
|
- [ ] Genetic trait tracking
|
||||||
- [ ] Document storage
|
- [ ] Document storage
|
||||||
|
|
||||||
### 📋 Phase 5: Advanced Features
|
### 🚀 Phase 5: Advanced Features
|
||||||
- [ ] PDF pedigree generation
|
- [ ] PDF pedigree generation
|
||||||
- [ ] Reverse pedigree (descendants)
|
- [ ] Reverse pedigree (descendants)
|
||||||
- [ ] Advanced search and filters
|
- [ ] Advanced search and filters
|
||||||
- [ ] Export capabilities
|
- [ ] 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
|
## License
|
||||||
|
|
||||||
Private use only - All rights reserved
|
Private use only - All rights reserved
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
For issues or questions, contact the system administrator.
|
For issues or questions:
|
||||||
|
- Check documentation in `docs/` folder
|
||||||
|
- Review container logs: `docker logs breedr`
|
||||||
|
- Contact the system administrator
|
||||||
304
docs/MICROCHIP_FIX.md
Normal file
304
docs/MICROCHIP_FIX.md
Normal file
@@ -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.*
|
||||||
@@ -26,7 +26,7 @@ function initDatabase(dbPath) {
|
|||||||
sex TEXT NOT NULL CHECK(sex IN ('male', 'female')),
|
sex TEXT NOT NULL CHECK(sex IN ('male', 'female')),
|
||||||
birth_date DATE,
|
birth_date DATE,
|
||||||
color TEXT,
|
color TEXT,
|
||||||
microchip TEXT UNIQUE,
|
microchip TEXT,
|
||||||
photo_urls TEXT, -- JSON array of photo URLs
|
photo_urls TEXT, -- JSON array of photo URLs
|
||||||
notes TEXT,
|
notes TEXT,
|
||||||
is_active INTEGER DEFAULT 1,
|
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
|
// Parents table - Relationship mapping
|
||||||
db.exec(`
|
db.exec(`
|
||||||
CREATE TABLE IF NOT EXISTS parents (
|
CREATE TABLE IF NOT EXISTS parents (
|
||||||
|
|||||||
138
server/db/migrate_microchip.js
Normal file
138
server/db/migrate_microchip.js
Normal file
@@ -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 };
|
||||||
Reference in New Issue
Block a user