Files
Moonbase/prisma/seed.ts
tonym 9c0312676a v0.10.0 — Location spine: garden moved onto a shared place tree
Completes the Location feature (schema + migration landed earlier under 98cabc5).

- Hierarchical Location model is the shared spine; unlimited nesting
- Garden add/edit now picks a reusable Location (inline create) instead of
  freeform text; garden groups By Location off the real tree
- Dual-write keeps legacy Plant.zone in sync with location.name (Release A)
- /api/locations CRUD + /api/locations/[id]/log (logs a location + its sub-locations)
- Location tree pure-core (buildTree/computePath/descendantIds) with tests
- Location added to backup/restore export
- Bump APP_VERSION 0.10.0 + plain-English changelog entry

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 23:36:54 -05:00

96 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { PrismaClient, PlantCategory } from "@prisma/client";
import { hashSync } from "bcryptjs";
const db = new PrismaClient();
async function main() {
console.log("Seeding Moon Base...");
// Admin accounts — only created if they don't exist yet.
const password = hashSync("moonbase!", 10);
await db.user.upsert({
where: { username: "bonna" },
update: { passwordHash: password },
create: {
id: "cluser0000000bonna",
email: "bonna@moonbase.local",
username: "bonna",
name: "Bonna Moon",
passwordHash: password,
role: "ADMIN",
mustChangePassword: true,
},
});
await db.user.upsert({
where: { username: "tony" },
update: { passwordHash: password },
create: {
id: "cluser0000000tony0",
email: "tony@moonbase.local",
username: "tony",
name: "Tony Moon",
passwordHash: password,
role: "ADMIN",
mustChangePassword: true,
},
});
if (process.env.SEED_MODE === "minimal") {
console.log("Minimal seed complete (admin accounts only).");
return;
}
// Sample plants for dev — a small starter food forest
const plants = [
{ commonName: "Apple", variety: "Honeycrisp", category: PlantCategory.CANOPY_TREE, zone: "Back yard, north fence", source: "Jung's Nursery", plantedAt: new Date("2024-04-15") },
{ commonName: "Apple", variety: "Liberty", category: PlantCategory.CANOPY_TREE, zone: "Back yard, north fence", source: "Jung's Nursery", plantedAt: new Date("2024-04-15") },
{ commonName: "Pear", variety: "Bartlett", category: PlantCategory.CANOPY_TREE, zone: "Back yard, east side", source: "Jung's Nursery", plantedAt: new Date("2024-04-20") },
{ commonName: "Elderberry", variety: "Bob Gordon", category: PlantCategory.UNDERSTORY_TREE, zone: "Back fence, south", source: "Raintree Nursery", plantedAt: new Date("2024-05-01") },
{ commonName: "Serviceberry", species: "Amelanchier canadensis", category: PlantCategory.UNDERSTORY_TREE, zone: "Front yard", source: "local nursery", plantedAt: new Date("2023-04-10") },
{ commonName: "Hazelnut", variety: "American", category: PlantCategory.LARGE_SHRUB, zone: "Back yard, west fence", source: "Oikos Tree Crops", plantedAt: new Date("2024-04-15") },
{ commonName: "Comfrey", species: "Symphytum officinale", category: PlantCategory.HERB, zone: "Under apple trees", source: "from seed", plantedAt: new Date("2024-05-15"), notes: "Chop-and-drop around the apples. Cut before it flowers to prevent spreading." },
{ commonName: "Raspberry", variety: "Heritage", category: PlantCategory.SHRUB, zone: "Back fence, east", source: "division", plantedAt: new Date("2023-05-01") },
{ commonName: "Strawberry", variety: "Ozark Beauty", category: PlantCategory.GROUNDCOVER, zone: "Under pear tree", source: "Jung's Nursery", plantedAt: new Date("2024-05-10") },
{ commonName: "Garlic", variety: "Music", category: PlantCategory.BULB, zone: "Raised bed #1", source: "Keene Organics", plantedAt: new Date("2024-10-10") },
];
// Starter Location tree — a root property node with the garden areas under it.
const root = await db.location.upsert({
where: { id: "seed-loc-root" },
update: {},
create: { id: "seed-loc-root", name: "Moon Homestead", kind: "OTHER", path: "Moon Homestead" },
});
const zoneNames = Array.from(new Set(plants.map((p) => p.zone).filter(Boolean) as string[]));
const zoneToLocId = new Map<string, string>();
for (const z of zoneNames) {
const id = `seed-loc-${z.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
await db.location.upsert({
where: { id },
update: {},
create: { id, name: z, kind: "AREA", parentId: root.id, path: `Moon Homestead ${z}` },
});
zoneToLocId.set(z, id);
}
for (const p of plants) {
await db.plant.upsert({
where: { id: `seed-${p.commonName.toLowerCase().replace(/\s/g, "-")}-${p.variety?.toLowerCase().replace(/\s/g, "-") ?? "0"}` },
update: {},
create: {
id: `seed-${p.commonName.toLowerCase().replace(/\s/g, "-")}-${p.variety?.toLowerCase().replace(/\s/g, "-") ?? "0"}`,
...p,
locationId: p.zone ? zoneToLocId.get(p.zone) : undefined,
},
});
}
console.log("Demo seed complete.");
}
main()
.catch(console.error)
.finally(() => db.$disconnect());