import { useMemo, useState } from 'react'; import { Text } from '@react-three/drei'; import * as THREE from 'three'; import type { Section3D, Position3D } from '../../lib/layoutApi'; import { PlantPosition, VisMode } from './types'; import { PlantSystem } from './PlantSystem'; import { SCALE } from './coordinates'; import { GridOverlay } from './GridOverlay'; import { SectionSummary } from './SummaryOverlay'; // Hierarchy context passed down from FacilityScene export interface HierarchyContext { facility?: string; building?: string; floor?: string; room?: string; } interface SmartRackProps { section: Section3D; visMode: VisMode; onPlantClick: (plant: PlantPosition) => void; highlightedTags?: string[]; dimMode?: boolean; hierarchy?: HierarchyContext; showGrid?: boolean; // Toggle grid visibility } export function SmartRack({ section, visMode, onPlantClick, highlightedTags, dimMode, hierarchy, showGrid = true }: SmartRackProps) { const [isHovered, setIsHovered] = useState(false); // Section positions are absolute (after our fix), scale them const scaledSection = { posX: section.posX * SCALE, posY: section.posY * SCALE, width: section.width * SCALE, height: section.height * SCALE, }; // Calculate grid dimensions and spacing const maxCols = Math.max(...section.positions.map(p => p.column), 1); const maxRows = Math.max(...section.positions.map(p => p.row), 1); const colSpacing = scaledSection.width / (maxCols + 1); const rowSpacing = scaledSection.height / (maxRows + 1); // Calculate plant positions using consistent grid logic const positions: PlantPosition[] = useMemo(() => { return section.positions.map((pos: Position3D) => ({ ...pos, // Grid-based positioning within section (RAW coords, no floor centering here) x: scaledSection.posX + (pos.column * colSpacing), z: scaledSection.posY + (pos.row * rowSpacing), y: 0.4 + (pos.tier * 0.6), // Full hierarchy breadcrumb breadcrumb: { facility: hierarchy?.facility, building: hierarchy?.building, floor: hierarchy?.floor, room: hierarchy?.room, section: section.code || section.name, tier: pos.tier, }, })); }, [section, scaledSection.posX, scaledSection.posY, colSpacing, rowSpacing, hierarchy]); const distinctTiers = [...new Set(positions.map(p => p.tier))].sort((a, b) => a - b); const distinctRows = [...new Set(positions.map(p => p.row))].sort((a, b) => a - b); const distinctCols = [...new Set(positions.map(p => p.column))].sort((a, b) => a - b); // Aggregate data for the summary overlay const aggregates = useMemo(() => { const plantsWithLife = section.positions.filter(p => p.plant); const plantCount = plantsWithLife.length; // Mock aggregates based on section data const hash = (section.id || section.name).split('').reduce((a, b) => { a = ((a << 5) - a) + b.charCodeAt(0); return a & a }, 0); const healthScore = plantCount > 0 ? 82 + (Math.abs(hash) % 15) : 0; const avgTemp = 74 + (Math.abs(hash) % 6); const avgHumidity = 52 + (Math.abs(hash) % 12); const warnings = (Math.abs(hash) % 10) > 7 ? 1 : 0; return { plantCount, healthScore, avgTemp, avgHumidity, warnings }; }, [section.positions, section.id, section.name]); return ( { e.stopPropagation(); setIsHovered(true); }} onPointerOut={() => setIsHovered(false)} > {/* Contextual Summary Overlay */} {/* Section/Table Label */} {visMode === 'STANDARD' && ( {section.code || section.name} )} {/* Shelf/Tier surfaces */} {distinctTiers.map(tier => ( ))} {/* Support posts */} {[0, 1].map(xOffset => [0, 1].map(zOffset => ( )) )} {/* Row labels */} {visMode === 'STANDARD' && distinctRows.map(row => ( R{row} ))} {/* Column labels */} {visMode === 'STANDARD' && distinctCols.map(col => ( C{col} ))} {/* Grid overlay with row/column labels */} {showGrid && visMode === 'STANDARD' && ( )} ); }