ca-grow-ops-manager/docs/LAYOUT-DESIGNER-SPEC.md
fullsizemalt 32fd739ccf
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: Complete Phases 8-13 implementation
Phase 8: Visitor Management
- Visitor/VisitorLog/AccessZone models
- Check-in/out with badge generation
- Zone occupancy tracking
- Kiosk and management pages

Phase 9: Messaging & Communication
- Announcements with priority levels
- Acknowledgement tracking
- Shift notes for team handoffs
- AnnouncementBanner component

Phase 10: Compliance & Audit Trail
- Immutable AuditLog model
- Document versioning and approval workflow
- Acknowledgement tracking for SOPs
- CSV export for audit logs

Phase 11: Accessibility & i18n
- WCAG 2.1 AA compliance utilities
- react-i18next with EN/ES translations
- User preferences context (theme, font size, etc)
- High contrast and reduced motion support

Phase 12: Hardware Integration
- QR code generation for batches/plants/visitors
- Printable label system
- Visitor badge printing

Phase 13: Advanced Features
- Environmental monitoring (sensors, readings, alerts)
- Financial tracking (transactions, P&L reports)
- AI/ML insights (yield predictions, anomaly detection)
2025-12-11 00:26:25 -08:00

13 KiB
Raw Blame History

Facility Layout Designer & Plant Addressing System

🎯 Vision

A Figma-like visual layout editor for designing facility layouts with precise plant addressing. Every plant gets a unique, hierarchical address that maps to physical location:

PROPERTY → BUILDING → ROOM → SECTION → ROW → COLUMN → POSITION

Example: MAIN.GROW1.VEG-A.LEFT.R3.C5.P1 = Main Property, Grow Building 1, Veg Room A, Left Section, Row 3, Column 5, Position 1


📐 Data Model

// Hierarchical Location Structure
model Property {
  id          String   @id @default(uuid())
  name        String
  address     String?
  licenseNum  String?  // DCC License Number
  buildings   Building[]
  createdAt   DateTime @default(now())
}

model Building {
  id          String   @id @default(uuid())
  property    Property @relation(fields: [propertyId], references: [id])
  propertyId  String
  name        String   // "Grow Building 1", "Processing"
  code        String   // Short code: "GROW1"
  type        String   // CULTIVATION, PROCESSING, DRYING, STORAGE
  rooms       Room[]
  layout      Json?    // Visual layout data (x, y, width, height)
  createdAt   DateTime @default(now())
}

model Room {
  id          String   @id @default(uuid())
  building    Building @relation(fields: [buildingId], references: [id])
  buildingId  String
  name        String
  code        String   // "VEG-A"
  type        String   // VEG, FLOWER, CLONE, DRY, CURE, TRIM
  sections    Section[]
  layout      Json?    // Position within building
  createdAt   DateTime @default(now())
}

model Section {
  id          String   @id @default(uuid())
  room        Room     @relation(fields: [roomId], references: [id])
  roomId      String
  name        String   // "Left Side", "Center Aisle"
  code        String   // "LEFT", "CENTER"
  type        String   // FLOOR, TABLE, RACK, HANGER, TRAY
  rows        Row[]
  layout      Json?    // Position within room, dimensions
  createdAt   DateTime @default(now())
}

model Row {
  id          String   @id @default(uuid())
  section     Section  @relation(fields: [sectionId], references: [id])
  sectionId   String
  number      Int      // Row 1, 2, 3...
  positions   Position[]
  layout      Json?    // Position within section
}

model Position {
  id          String   @id @default(uuid())
  row         Row      @relation(fields: [rowId], references: [id])
  rowId       String
  column      Int      // Column number
  slot        Int      @default(1) // For multi-plant positions
  plant       Plant?   @relation(fields: [plantId], references: [id])
  plantId     String?  @unique
  status      String   @default("EMPTY") // EMPTY, OCCUPIED, RESERVED, DAMAGED
}

model Plant {
  id          String   @id @default(uuid())
  tagNumber   String   @unique // METRC tag
  batch       Batch    @relation(fields: [batchId], references: [id])
  batchId     String
  position    Position?
  address     String   // Full hierarchical address
  status      String   // ACTIVE, HARVESTED, DESTROYED, TRANSFERRED
  history     PlantLocationHistory[]
  createdAt   DateTime @default(now())
}

model PlantLocationHistory {
  id          String   @id @default(uuid())
  plant       Plant    @relation(fields: [plantId], references: [id])
  plantId     String
  fromAddress String?
  toAddress   String
  movedBy     String   // User ID
  movedAt     DateTime @default(now())
  reason      String?  // TRANSPLANT, REORGANIZE, HARVEST
}

🎨 Layout Editor UI

Canvas Features (Figma-like)

  • Drag & Drop - Add rooms, sections, rows to canvas
  • Resize Handles - Adjust dimensions
  • Grid Snap - Align to grid
  • Zoom & Pan - Navigate large layouts
  • Layers - Show/hide different elements
  • Templates - Pre-built room layouts

Element Types

1. Floor Plan Elements

┌─────────────────────────────────────────────────────────┐
│  BUILDING: Grow 1                                       │
│  ┌─────────────────┐  ┌─────────────────┐              │
│  │   VEG ROOM A    │  │   VEG ROOM B    │              │
│  │   ┌───┬───┬───┐ │  │                 │              │
│  │   │ T1│ T2│ T3│ │  │                 │              │
│  │   ├───┼───┼───┤ │  │                 │              │
│  │   │ T4│ T5│ T6│ │  │                 │              │
│  │   └───┴───┴───┘ │  │                 │              │
│  └─────────────────┘  └─────────────────┘              │
│  ┌──────────────────────────────────────┐              │
│  │         FLOWER ROOM A                │              │
│  │   [====] [====] [====] [====]        │  4 ft aisles │
│  │   [====] [====] [====] [====]        │              │
│  │   [====] [====] [====] [====]        │              │
│  └──────────────────────────────────────┘              │
└─────────────────────────────────────────────────────────┘

2. Grow Table/Bed (Grid-based)

┌────────────────────────────────────────┐
│  TABLE T1 - VEG-A.LEFT.T1              │
│  Capacity: 6 rows × 8 columns = 48     │
│  ┌──┬──┬──┬──┬──┬──┬──┬──┐            │
│  │🌱│🌱│🌱│🌱│🌱│🌱│🌱│🌱│ R1          │
│  ├──┼──┼──┼──┼──┼──┼──┼──┤            │
│  │🌱│🌱│🌱│🌱│  │  │🌱│🌱│ R2          │
│  ├──┼──┼──┼──┼──┼──┼──┼──┤            │
│  │🌱│🌱│🌱│🌱│🌱│🌱│🌱│🌱│ R3          │
│  ├──┼──┼──┼──┼──┼──┼──┼──┤            │
│  │  │  │  │  │  │  │  │  │ R4 (empty)  │
│  └──┴──┴──┴──┴──┴──┴──┴──┘            │
│   C1 C2 C3 C4 C5 C6 C7 C8              │
└────────────────────────────────────────┘

3. Drying Rack (Hanger-based)

┌────────────────────────────────────────┐
│  DRY RACK 1 - DRY.MAIN.RACK1           │
│  Type: Vertical Hangers                │
│                                        │
│  ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐       │
│  │H1│ │H2│ │H3│ │H4│ │H5│ │H6│ Level 1│
│  └──┘ └──┘ └──┘ └──┘ └──┘ └──┘       │
│  ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐       │
│  │H1│ │H2│ │H3│ │H4│ │H5│ │H6│ Level 2│
│  └──┘ └──┘ └──┘ └──┘ └──┘ └──┘       │
│  ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐       │
│  │H1│ │H2│ │H3│ │H4│ │H5│ │  │ Level 3│
│  └──┘ └──┘ └──┘ └──┘ └──┘ └──┘       │
└────────────────────────────────────────┘

🔧 API Endpoints

Layout Management

POST   /api/layout/property
POST   /api/layout/building
POST   /api/layout/room
POST   /api/layout/section
POST   /api/layout/row

PUT    /api/layout/:entityType/:id
DELETE /api/layout/:entityType/:id

GET    /api/layout/property/:id/full   # Full hierarchy
GET    /api/layout/room/:id/positions  # All positions with status

Plant Placement

POST   /api/plants
  Body: { tagNumber, batchId, positionId }
  
POST   /api/plants/:id/move
  Body: { toPositionId, reason }
  
POST   /api/plants/bulk-place
  Body: { batchId, positionIds[] }  # Place batch into positions
  
GET    /api/plants/by-address/:address  # Lookup by address string

Position Status

GET    /api/positions/available
  Query: ?roomId=x&count=48  # Find 48 available positions
  
POST   /api/positions/reserve
  Body: { positionIds[], batchId, until }
  
GET    /api/rooms/:id/occupancy
  Returns: { total, occupied, empty, reserved }

🎯 METRC Integration Requirements

Address Format for METRC

METRC requires precise location tracking. Our address format maps to:

Our Field METRC Field
Property License Premises
Building Area
Room Room
Section + Row + Column Location (free text)

Sync Strategy

1. Plant created → Sync to METRC with location
2. Plant moved → Update METRC location
3. Plant harvested → Report harvest location
4. Audit → Generate location report matching METRC

🖥️ Frontend Components

1. LayoutEditor (Canvas)

interface LayoutEditorProps {
  propertyId: string;
  mode: 'view' | 'edit';
  onSave?: (layout: Layout) => void;
}

// Uses react-konva or @tldraw/tldraw for canvas
<LayoutEditor 
  propertyId={propertyId}
  mode="edit"
  onSave={handleSave}
/>

2. RoomGrid (Position Grid)

interface RoomGridProps {
  roomId: string;
  onPositionClick?: (position: Position) => void;
  selectedBatch?: string;  // Highlight batch plants
}

<RoomGrid 
  roomId={roomId}
  selectedBatch={batchId}
  onPositionClick={(pos) => showPlantDetails(pos.plant)}
/>

3. PlantPlacer (Batch Placement Tool)

interface PlantPlacerProps {
  batchId: string;
  plantCount: number;
  onComplete: (placements: Placement[]) => void;
}

// Select multiple positions to place plants
<PlantPlacer 
  batchId={batch.id}
  plantCount={48}
  onComplete={handlePlacement}
/>

4. AddressLookup (Quick Find)

// Scan or type address to jump to location
<AddressLookup 
  onFound={(plant) => navigateToPlant(plant)}
/>

// Input: "MAIN.GROW1.VEG-A.LEFT.R3.C5"
// Output: Highlights position, shows plant details

📱 Mobile Flow

  1. Scan Plant Tag → Shows current position + batch info
  2. Move Plant → Scan new position QR → Confirm move
  3. Quick Place → Scan batch → Scan position → Repeat
  4. Audit Walk → Show expected vs actual by position

🗓️ Implementation Phases

Phase 1: Data Model & API (1 week)

  • Create Prisma models
  • Migrate database
  • Build CRUD endpoints
  • Address generation logic

Phase 2: Basic Layout Editor (2 weeks)

  • Canvas with react-konva
  • Add/edit rooms, sections
  • Grid generator for positions
  • Save/load layouts

Phase 3: Plant Placement (1 week)

  • Place plants in positions
  • Move plants (with history)
  • Bulk placement tool
  • Position reservation

Phase 4: Visualization (1 week)

  • Room grid view
  • Color coding (by batch, stage, health)
  • Occupancy heatmaps
  • Print floor plans

Phase 5: METRC Sync (2 weeks)

  • Location format mapping
  • Sync on plant create/move
  • Audit report generation
  • Discrepancy detection

📦 Tech Stack

Component Technology
Canvas Editor react-konva or @tldraw/tldraw
Grid Display Custom React + CSS Grid
Address Parser Regex + validation
QR Codes qrcode.react
Mobile Scan @zxing/browser
State Mgmt Zustand (for editor state)

🎨 Example Layouts

Vegetative Room - Tables

{
  "type": "ROOM",
  "code": "VEG-A",
  "layout": {
    "width": 40,  // feet
    "height": 30,
    "sections": [
      {
        "code": "LEFT",
        "type": "TABLE",
        "x": 0, "y": 0,
        "rows": 6,
        "columns": 8,
        "spacing": 12  // inches between plants
      },
      {
        "code": "RIGHT",
        "type": "TABLE",
        "x": 20, "y": 0,
        "rows": 6,
        "columns": 8
      }
    ],
    "aisles": [
      { "x": 18, "y": 0, "width": 4, "height": 30 }
    ]
  }
}

Drying Room - Racks

{
  "type": "ROOM",
  "code": "DRY-A",
  "layout": {
    "width": 20,
    "height": 30,
    "sections": [
      {
        "code": "RACK1",
        "type": "HANGER",
        "x": 0, "y": 0,
        "levels": 4,
        "hangersPerLevel": 20
      },
      {
        "code": "RACK2",
        "type": "HANGER",
        "x": 10, "y": 0,
        "levels": 4,
        "hangersPerLevel": 20
      }
    ]
  }
}

Deliverables

  1. Prisma schema with full hierarchy
  2. API endpoints for CRUD + plant placement
  3. Layout Editor component (Figma-like)
  4. Room Grid Viewer with plant status
  5. Mobile placement flow with scanning
  6. METRC-compatible address format
  7. Audit reports by location