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)
13 KiB
13 KiB
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
- Scan Plant Tag → Shows current position + batch info
- Move Plant → Scan new position QR → Confirm move
- Quick Place → Scan batch → Scan position → Repeat
- 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
- Prisma schema with full hierarchy
- API endpoints for CRUD + plant placement
- Layout Editor component (Figma-like)
- Room Grid Viewer with plant status
- Mobile placement flow with scanning
- METRC-compatible address format
- Audit reports by location