ca-grow-ops-manager/backend/prisma/seed.js
fullsizemalt 4663b0ac86
Some checks failed
Deploy to Production / deploy (push) Failing after 0s
Test / backend-test (push) Failing after 0s
Test / frontend-test (push) Failing after 0s
feat: Navigation refactor with RBAC, DevTools for quick user switching, enhanced seed data
- Refactored navigation with grouped sections (Operations, Cultivation, Analytics, etc.)
- Added RBAC-based navigation filtering by user role
- Created DevTools panel for quick user switching during testing
- Added collapsible sidebar sections on desktop
- Mobile: bottom nav bar (4 items + More) with slide-up sheet
- Enhanced seed data with [DEMO] prefix markers
- Added multiple demo users: Owner, Manager, Cultivator, Worker
- Fixed domain to runfoo.run
- Added Audit Log and SOP Library pages to navigation
- Created usePermissions hook and RoleBadge component
2025-12-11 11:07:22 -08:00

373 lines
15 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const { PrismaClient, RoomType } = require('@prisma/client');
const bcrypt = require('bcryptjs');
const prisma = new PrismaClient();
// Demo data marker - all demo entities use this prefix for easy identification
const DEMO_PREFIX = '[DEMO]';
async function hashPassword(password) {
return bcrypt.hash(password, 10);
}
async function main() {
console.log('🌱 Seeding database with demo data...\n');
// ==================== ROLES ====================
console.log('📋 Creating Roles...');
const rolesData = [
{
name: 'Facility Owner',
description: 'Full access to all features and settings',
permissions: { admin: true, all: true },
isSystem: true
},
{
name: 'Manager',
description: 'Operational control and reporting',
permissions: {
users: { view: true, manage: true },
tasks: { view: true, manage: true, assign: true },
inventory: { view: true, manage: true },
reports: { view: true, export: true },
visitors: { view: true, manage: true },
compliance: { view: true }
},
isSystem: true
},
{
name: 'Cultivator',
description: 'Plant care and daily operations',
permissions: {
tasks: { view: true, complete: true },
inventory: { view: true },
batches: { view: true, update: true },
rooms: { view: true }
},
isSystem: true
},
{
name: 'Worker',
description: 'Basic task completion and logging',
permissions: {
tasks: { view: true, complete: true },
timeclock: { view: true, punch: true }
},
isSystem: true
},
{
name: 'Viewer',
description: 'Read-only access to dashboards',
permissions: {
dashboard: { view: true },
reports: { view: true }
},
isSystem: true
}
];
for (const r of rolesData) {
const existing = await prisma.role.findUnique({ where: { name: r.name } });
if (!existing) {
await prisma.role.create({ data: r });
console.log(` ✓ Created Role: ${r.name}`);
}
}
const ownerRole = await prisma.role.findUnique({ where: { name: 'Facility Owner' } });
const managerRole = await prisma.role.findUnique({ where: { name: 'Manager' } });
const cultivatorRole = await prisma.role.findUnique({ where: { name: 'Cultivator' } });
const workerRole = await prisma.role.findUnique({ where: { name: 'Worker' } });
// ==================== USERS ====================
console.log('\n👥 Creating Demo Users...');
const usersData = [
{
email: 'admin@runfoo.run',
password: 'password123',
name: 'Travis (Owner)',
role: 'OWNER',
roleId: ownerRole?.id,
rate: 100.00
},
{
email: 'manager@demo.local',
password: 'demo1234',
name: `${DEMO_PREFIX} Sarah Chen`,
role: 'MANAGER',
roleId: managerRole?.id,
rate: 45.00
},
{
email: 'cultivator@demo.local',
password: 'demo1234',
name: `${DEMO_PREFIX} Mike Thompson`,
role: 'CULTIVATOR',
roleId: cultivatorRole?.id,
rate: 28.00
},
{
email: 'worker@demo.local',
password: 'demo1234',
name: `${DEMO_PREFIX} Alex Rivera`,
role: 'WORKER',
roleId: workerRole?.id,
rate: 22.00
}
];
for (const u of usersData) {
const existing = await prisma.user.findUnique({ where: { email: u.email } });
if (!existing) {
const passwordHash = await hashPassword(u.password);
await prisma.user.create({
data: {
email: u.email,
passwordHash,
name: u.name,
role: u.role,
roleId: u.roleId,
rate: u.rate
}
});
console.log(` ✓ Created User: ${u.name} (${u.email})`);
}
}
// ==================== ROOMS ====================
console.log('\n🏠 Creating Demo Rooms...');
const rooms = [
{ name: `${DEMO_PREFIX} Veg Room 1`, type: RoomType.VEG, sqft: 1200, capacity: 500, targetTemp: 78, targetHumidity: 65 },
{ name: `${DEMO_PREFIX} Veg Room 2`, type: RoomType.VEG, sqft: 1000, capacity: 400, targetTemp: 78, targetHumidity: 65 },
{ name: `${DEMO_PREFIX} Flower Room A`, type: RoomType.FLOWER, sqft: 2500, capacity: 300, targetTemp: 75, targetHumidity: 50 },
{ name: `${DEMO_PREFIX} Flower Room B`, type: RoomType.FLOWER, sqft: 2500, capacity: 300, targetTemp: 75, targetHumidity: 50 },
{ name: `${DEMO_PREFIX} Flower Room C`, type: RoomType.FLOWER, sqft: 2000, capacity: 250, targetTemp: 75, targetHumidity: 50 },
{ name: `${DEMO_PREFIX} Dry Room`, type: RoomType.DRY, sqft: 800, capacity: 100, targetTemp: 60, targetHumidity: 55 },
{ name: `${DEMO_PREFIX} Cure Room`, type: RoomType.CURE, sqft: 600, capacity: 50, targetTemp: 62, targetHumidity: 60 },
{ name: `${DEMO_PREFIX} Mother Room`, type: RoomType.MOTHER, sqft: 400, capacity: 50, targetTemp: 76, targetHumidity: 60 },
];
const createdRooms = [];
for (const r of rooms) {
let room = await prisma.room.findFirst({ where: { name: r.name } });
if (!room) {
room = await prisma.room.create({ data: r });
console.log(` ✓ Created Room: ${r.name}`);
}
createdRooms.push(room);
}
// ==================== BATCHES ====================
console.log('\n🌿 Creating Demo Batches...');
const cultivator = await prisma.user.findFirst({ where: { email: 'cultivator@demo.local' } });
const batchesData = [
{
name: `${DEMO_PREFIX} OG Kush - Batch 001`,
strain: 'OG Kush',
status: 'ACTIVE',
stage: 'VEGETATIVE',
source: 'CLONE',
plantCount: 48,
startDate: new Date(Date.now() - 14 * 24 * 60 * 60 * 1000), // 14 days ago
roomId: createdRooms.find(r => r.name.includes('Veg Room 1'))?.id
},
{
name: `${DEMO_PREFIX} Blue Dream - Batch 002`,
strain: 'Blue Dream',
status: 'ACTIVE',
stage: 'FLOWERING',
source: 'CLONE',
plantCount: 36,
startDate: new Date(Date.now() - 45 * 24 * 60 * 60 * 1000), // 45 days ago
roomId: createdRooms.find(r => r.name.includes('Flower Room A'))?.id
},
{
name: `${DEMO_PREFIX} Girl Scout Cookies - Batch 003`,
strain: 'Girl Scout Cookies',
status: 'ACTIVE',
stage: 'FLOWERING',
source: 'SEED',
plantCount: 24,
startDate: new Date(Date.now() - 52 * 24 * 60 * 60 * 1000), // 52 days ago
roomId: createdRooms.find(r => r.name.includes('Flower Room B'))?.id
},
{
name: `${DEMO_PREFIX} Purple Punch - Batch 004`,
strain: 'Purple Punch',
status: 'ACTIVE',
stage: 'DRYING',
source: 'CLONE',
plantCount: 30,
startDate: new Date(Date.now() - 75 * 24 * 60 * 60 * 1000), // 75 days ago
roomId: createdRooms.find(r => r.name.includes('Dry Room'))?.id
},
{
name: `${DEMO_PREFIX} Gelato - Batch 005`,
strain: 'Gelato #41',
status: 'ACTIVE',
stage: 'CLONE_IN',
source: 'CLONE',
plantCount: 60,
startDate: new Date(), // Today
roomId: createdRooms.find(r => r.name.includes('Veg Room 2'))?.id
}
];
const createdBatches = [];
for (const b of batchesData) {
let batch = await prisma.batch.findFirst({ where: { name: b.name } });
if (!batch) {
batch = await prisma.batch.create({ data: b });
console.log(` ✓ Created Batch: ${b.name} (${b.stage})`);
}
createdBatches.push(batch);
}
// ==================== IPM SCHEDULES ====================
console.log('\n🛡 Creating IPM Schedules...');
for (const batch of createdBatches) {
if (batch.stage === 'VEGETATIVE' || batch.stage === 'FLOWERING') {
const existing = await prisma.iPMSchedule.findFirst({ where: { batchId: batch.id } });
if (!existing) {
const nextTreatment = new Date();
nextTreatment.setDate(nextTreatment.getDate() + Math.floor(Math.random() * 5) - 2); // -2 to +2 days
await prisma.iPMSchedule.create({
data: {
batchId: batch.id,
product: 'Pyganic 5.0',
intervalDays: 10,
nextTreatment
}
});
console.log(` ✓ Created IPM Schedule for: ${batch.name}`);
}
}
}
// ==================== SUPPLIES ====================
console.log('\n📦 Creating Demo Supplies...');
const supplies = [
{ name: `${DEMO_PREFIX} Nitrile Gloves (L)`, category: 'PPE', quantity: 5, minThreshold: 10, unit: 'box', location: 'Ante Room', vendor: 'Uline' },
{ name: `${DEMO_PREFIX} Nitrile Gloves (M)`, category: 'PPE', quantity: 8, minThreshold: 10, unit: 'box', location: 'Ante Room', vendor: 'Uline' },
{ name: `${DEMO_PREFIX} Tyvek Suits`, category: 'PPE', quantity: 25, minThreshold: 20, unit: 'each', location: 'Ante Room', vendor: 'Uline' },
{ name: `${DEMO_PREFIX} Hypochlorous Acid`, category: 'CLEANING', quantity: 12, minThreshold: 5, unit: 'gallon', location: 'Janitor Closet', vendor: 'Athena' },
{ name: `${DEMO_PREFIX} Isopropyl Alcohol 99%`, category: 'CLEANING', quantity: 6, minThreshold: 4, unit: 'gallon', location: 'Trim Room', vendor: 'GrowGen' },
{ name: `${DEMO_PREFIX} Rockwool Cubes 4"`, category: 'NUTRIENTS', quantity: 450, minThreshold: 200, unit: 'cube', location: 'Veg Storage', vendor: 'GrowGen' },
{ name: `${DEMO_PREFIX} Coco Coir`, category: 'NUTRIENTS', quantity: 15, minThreshold: 10, unit: 'bag', location: 'Veg Storage', vendor: 'Botanicare' },
{ name: `${DEMO_PREFIX} Trimmers (Fiskars)`, category: 'MAINTENANCE', quantity: 12, minThreshold: 15, unit: 'pair', location: 'Trim Room', vendor: 'Amazon' },
{ name: `${DEMO_PREFIX} Pruning Shears`, category: 'MAINTENANCE', quantity: 8, minThreshold: 6, unit: 'pair', location: 'Flower Room A', vendor: 'Amazon' },
{ name: `${DEMO_PREFIX} pH Test Kit`, category: 'MAINTENANCE', quantity: 3, minThreshold: 2, unit: 'kit', location: 'Nutrient Mix Station', vendor: 'Bluelab' },
];
for (const s of supplies) {
const existing = await prisma.supplyItem.findFirst({ where: { name: s.name } });
if (!existing) {
await prisma.supplyItem.create({ data: s });
console.log(` ✓ Created Supply: ${s.name} (qty: ${s.quantity})`);
}
}
// ==================== TASKS ====================
console.log('\n✅ Creating Demo Tasks...');
const manager = await prisma.user.findFirst({ where: { email: 'manager@demo.local' } });
const tasksData = [
{
title: `${DEMO_PREFIX} Morning Walkthrough - Veg Rooms`,
description: 'Check all veg rooms for pest issues, water levels, and plant health',
status: 'PENDING',
priority: 'HIGH',
dueDate: new Date()
},
{
title: `${DEMO_PREFIX} IPM Treatment - Flower Room A`,
description: 'Apply Pyganic 5.0 root drench as per schedule',
status: 'IN_PROGRESS',
priority: 'HIGH',
dueDate: new Date()
},
{
title: `${DEMO_PREFIX} Trim Batch 004 - Purple Punch`,
description: 'Process dried material from Batch 004',
status: 'PENDING',
priority: 'MEDIUM',
dueDate: new Date(Date.now() + 24 * 60 * 60 * 1000) // Tomorrow
},
{
title: `${DEMO_PREFIX} Inventory Audit - PPE Supplies`,
description: 'Count and verify PPE stock levels, update system',
status: 'PENDING',
priority: 'LOW',
dueDate: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000) // 3 days
},
{
title: `${DEMO_PREFIX} Equipment Maintenance - HVAC Check`,
description: 'Quarterly HVAC filter replacement and system check',
status: 'COMPLETED',
priority: 'MEDIUM',
dueDate: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000) // 2 days ago
}
];
for (const t of tasksData) {
const existing = await prisma.task.findFirst({ where: { title: t.title } });
if (!existing) {
await prisma.task.create({
data: {
...t,
createdById: manager?.id,
assignedToId: cultivator?.id,
batchId: createdBatches[0]?.id
}
});
console.log(` ✓ Created Task: ${t.title} (${t.status})`);
}
}
// ==================== ANNOUNCEMENTS ====================
console.log('\n📢 Creating Demo Announcements...');
const announcementsData = [
{
title: `${DEMO_PREFIX} Welcome to 777 Wolfpack Grow Ops`,
body: 'This is a demo environment. All data shown is sample data for testing purposes. Login credentials: admin@runfoo.run / password123',
priority: 'INFO',
requiresAck: false,
createdById: manager?.id
},
{
title: `${DEMO_PREFIX} Scheduled Facility Inspection - Dec 15`,
body: 'State inspector visit scheduled for December 15th. Please ensure all compliance documentation is up to date.',
priority: 'WARNING',
requiresAck: true,
createdById: manager?.id
}
];
for (const a of announcementsData) {
if (a.createdById) {
const existing = await prisma.announcement.findFirst({ where: { title: a.title } });
if (!existing) {
await prisma.announcement.create({ data: a });
console.log(` ✓ Created Announcement: ${a.title}`);
}
}
}
console.log('\n✨ Seeding complete!\n');
console.log('Demo Login Credentials:');
console.log(' Owner: admin@runfoo.run / password123');
console.log(' Manager: manager@demo.local / demo1234');
console.log(' Cultivator: cultivator@demo.local / demo1234');
console.log(' Worker: worker@demo.local / demo1234');
}
main()
.catch((e) => {
console.error('Seeding failed:', e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});