v0.14.0 — Animals & Pets
Livestock and household pets as leaves on the Location tree, mirroring Garden. - Animal model (species/breed/sex/birthdate/location, soft-delete) + AnimalLog (feeding, vet, weight, medication, breeding, acquired, died); Task.animalId for recurring care reminders - API-first /api/v1/animals (CRUD) + logs, animals:read/write scopes; animals service shared with the coming MCP - Animals pages: list grouped by species with auto-computed age, detail with facts + latest weight + care log; add/edit/remove + log entry. Nav entry. Backup/restore cover the new tables - Pure age.ts (newborn/days/months/years) with tests; verified end-to-end against a running server (create, vet + weigh-in logs, read-back) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
66
prisma/migrations/20260616051637_v0_14_animals/migration.sql
Normal file
66
prisma/migrations/20260616051637_v0_14_animals/migration.sql
Normal file
@@ -0,0 +1,66 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "AnimalLogType" AS ENUM ('NOTE', 'FEEDING', 'VET', 'WEIGHT', 'MEDICATION', 'BREEDING', 'ACQUIRED', 'DIED');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Task" ADD COLUMN "animalId" TEXT;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Animal" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"species" TEXT,
|
||||
"breed" TEXT,
|
||||
"sex" TEXT,
|
||||
"locationId" TEXT,
|
||||
"birthdate" TIMESTAMP(3),
|
||||
"acquiredAt" TIMESTAMP(3),
|
||||
"source" TEXT,
|
||||
"notes" TEXT,
|
||||
"imageUrl" TEXT,
|
||||
"active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Animal_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "AnimalLog" (
|
||||
"id" TEXT NOT NULL,
|
||||
"animalId" TEXT NOT NULL,
|
||||
"type" "AnimalLogType" NOT NULL,
|
||||
"date" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"notes" TEXT,
|
||||
"weight" DECIMAL(10,2),
|
||||
"actorId" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "AnimalLog_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Animal_locationId_idx" ON "Animal"("locationId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Animal_species_idx" ON "Animal"("species");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Animal_active_idx" ON "Animal"("active");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "AnimalLog_animalId_idx" ON "AnimalLog"("animalId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "AnimalLog_date_idx" ON "AnimalLog"("date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Task_animalId_idx" ON "Task"("animalId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Task" ADD CONSTRAINT "Task_animalId_fkey" FOREIGN KEY ("animalId") REFERENCES "Animal"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Animal" ADD CONSTRAINT "Animal_locationId_fkey" FOREIGN KEY ("locationId") REFERENCES "Location"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "AnimalLog" ADD CONSTRAINT "AnimalLog_animalId_fkey" FOREIGN KEY ("animalId") REFERENCES "Animal"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -101,6 +101,7 @@ model Location {
|
||||
updatedAt DateTime @updatedAt
|
||||
plants Plant[]
|
||||
items Item[]
|
||||
animals Animal[]
|
||||
|
||||
@@index([parentId])
|
||||
@@index([kind])
|
||||
@@ -239,6 +240,8 @@ model Task {
|
||||
plant Plant? @relation(fields: [plantId], references: [id])
|
||||
itemId String?
|
||||
item Item? @relation(fields: [itemId], references: [id], onDelete: SetNull)
|
||||
animalId String?
|
||||
animal Animal? @relation(fields: [animalId], references: [id], onDelete: SetNull)
|
||||
recurrence RecurrenceType @default(YEARLY)
|
||||
intervalDays Int? // for CUSTOM: every N days
|
||||
month Int? // 1–12: for YEARLY, which month
|
||||
@@ -252,6 +255,7 @@ model Task {
|
||||
@@index([nextDue])
|
||||
@@index([plantId])
|
||||
@@index([itemId])
|
||||
@@index([animalId])
|
||||
@@index([active])
|
||||
}
|
||||
|
||||
@@ -395,3 +399,58 @@ model ItemLog {
|
||||
@@index([itemId])
|
||||
@@index([date])
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Animals & pets — livestock and household pets, leaves in a Location with
|
||||
// their own care log (vet, feeding, weight, meds) and recurring reminders.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
enum AnimalLogType {
|
||||
NOTE
|
||||
FEEDING
|
||||
VET
|
||||
WEIGHT
|
||||
MEDICATION
|
||||
BREEDING
|
||||
ACQUIRED
|
||||
DIED
|
||||
}
|
||||
|
||||
model Animal {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
species String? // "Dog", "Cat", "Chicken", "Goat"
|
||||
breed String?
|
||||
sex String? // freeform: "Female", "Male", "Wether"
|
||||
locationId String?
|
||||
location Location? @relation(fields: [locationId], references: [id], onDelete: SetNull)
|
||||
birthdate DateTime?
|
||||
acquiredAt DateTime?
|
||||
source String?
|
||||
notes String?
|
||||
imageUrl String?
|
||||
active Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
logs AnimalLog[]
|
||||
tasks Task[]
|
||||
|
||||
@@index([locationId])
|
||||
@@index([species])
|
||||
@@index([active])
|
||||
}
|
||||
|
||||
model AnimalLog {
|
||||
id String @id @default(cuid())
|
||||
animalId String
|
||||
animal Animal @relation(fields: [animalId], references: [id], onDelete: Cascade)
|
||||
type AnimalLogType
|
||||
date DateTime @default(now())
|
||||
notes String?
|
||||
weight Decimal? @db.Decimal(10, 2) // for WEIGHT entries
|
||||
actorId String?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([animalId])
|
||||
@@index([date])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user