feat: add full hierarchy context for breadcrumb navigation
Some checks are pending
Deploy to Production / deploy (push) Waiting to run
Test / backend-test (push) Waiting to run
Test / frontend-test (push) Waiting to run

- Pass facility/building/floor/room context through component tree
- HierarchyContext interface in SmartRack
- Breadcrumb now includes full path: Facility → Building → Floor → Room → Section → Tier
This commit is contained in:
fullsizemalt 2025-12-18 20:26:12 -08:00
parent ddaf67ab1e
commit cfaf5ebe2e
4 changed files with 136 additions and 11 deletions

View file

@ -0,0 +1,108 @@
/**
* FIX SECTION POSITIONS - 777 Wolfpack 3D Viewer
*
* Problem: All sections (A1-A6, B1-B6) have identical coordinates,
* causing all Flower Rooms to stack on top of each other in the 3D viewer.
*
* Solution: Offset each room's sections horizontally so they don't overlap.
* - Flower Room A sections: X offset = 0
* - Flower Room B sections: X offset = 700 (one room width)
* - Flower Room C sections: X offset = 0, Y offset = 700 (below Room A)
*
* Run: node prisma/seed-fix-sections.js
*/
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
async function main() {
console.log('🔧 Fixing section positions for 3D viewer...\n');
// Define room offsets (in pixels)
const roomOffsets = {
'Flower Room A': { x: 0, y: 0 },
'Flower Room B': { x: 700, y: 0 }, // Offset right
'Flower Room C': { x: 0, y: 700 }, // Offset down
'Veg Room 1': { x: 1400, y: 0 }, // Far right
'Veg Room 2': { x: 1400, y: 700 }, // Far right, down
'Dry Room': { x: 0, y: 1400 }, // Bottom left
'Cure Room': { x: 700, y: 1400 }, // Bottom center
'Mother Room': { x: 1400, y: 1400 }, // Bottom right
};
// Update room positions to match the layout
console.log('📍 Updating room positions...\n');
for (const [roomName, offset] of Object.entries(roomOffsets)) {
const result = await prisma.facilityRoom.updateMany({
where: { name: roomName },
data: {
posX: offset.x,
posY: offset.y,
width: 650, // Standard room width
height: 650 // Standard room height
}
});
console.log(` ${roomName}: posX=${offset.x}, posY=${offset.y} (updated ${result.count})`);
}
console.log('\n📐 Updating section positions (relative to rooms)...\n');
// Get all rooms with their sections
const rooms = await prisma.facilityRoom.findMany({
include: { sections: true }
});
for (const room of rooms) {
const offset = roomOffsets[room.name];
if (!offset) continue;
for (const section of room.sections) {
// Section positions are currently in a 0-700 grid
// Keep them relative, but now the room offset handles separation
const newPosX = section.posX + offset.x;
const newPosY = section.posY + offset.y;
await prisma.facilitySection.update({
where: { id: section.id },
data: {
posX: newPosX,
posY: newPosY
}
});
console.log(` ${room.name} / ${section.code}: (${section.posX}, ${section.posY}) → (${newPosX}, ${newPosY})`);
}
}
// Update floor dimensions to fit all rooms
console.log('\n📏 Updating floor dimensions...\n');
await prisma.facilityFloor.updateMany({
data: {
width: 2100, // 3 columns × 700
height: 2100 // 3 rows × 700
}
});
console.log(' Floor dimensions set to 2100 × 2100');
// Verify
console.log('\n✅ Verification:');
const verification = await prisma.facilityRoom.findMany({
select: { name: true, posX: true, posY: true, width: true, height: true },
orderBy: { name: 'asc' }
});
console.table(verification);
console.log('\n🎉 Section positions fixed! Redeploy to see changes.');
}
main()
.catch((e) => {
console.error('Fix failed:', e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});

View file

@ -84,6 +84,11 @@ export function FacilityScene({
onPlantClick={onPlantClick} onPlantClick={onPlantClick}
highlightedTags={highlightedTags} highlightedTags={highlightedTags}
dimMode={dimMode} dimMode={dimMode}
hierarchy={{
facility: data.floor.property,
building: data.floor.building,
floor: data.floor.name,
}}
/> />
))} ))}
</group> </group>

View file

@ -2,7 +2,7 @@ import { Text } from '@react-three/drei';
import * as THREE from 'three'; import * as THREE from 'three';
import type { Room3D } from '../../lib/layoutApi'; import type { Room3D } from '../../lib/layoutApi';
import { PlantPosition, VisMode, COLORS } from './types'; import { PlantPosition, VisMode, COLORS } from './types';
import { SmartRack } from './SmartRack'; import { SmartRack, HierarchyContext } from './SmartRack';
// Convert pixel coordinates to world units // Convert pixel coordinates to world units
const SCALE = 0.1; const SCALE = 0.1;
@ -28,9 +28,10 @@ interface RoomObjectProps {
onPlantClick: (plant: PlantPosition) => void; onPlantClick: (plant: PlantPosition) => void;
highlightedTags?: string[]; highlightedTags?: string[];
dimMode?: boolean; dimMode?: boolean;
hierarchy?: Omit<HierarchyContext, 'room'>;
} }
export function RoomObject({ room, visMode, onPlantClick, highlightedTags, dimMode }: RoomObjectProps) { export function RoomObject({ room, visMode, onPlantClick, highlightedTags, dimMode, hierarchy }: RoomObjectProps) {
const env = getMockRoomEnv(room.name); const env = getMockRoomEnv(room.name);
// Scale room dimensions to world units // Scale room dimensions to world units
@ -127,7 +128,7 @@ export function RoomObject({ room, visMode, onPlantClick, highlightedTags, dimMo
onPlantClick={onPlantClick} onPlantClick={onPlantClick}
highlightedTags={highlightedTags} highlightedTags={highlightedTags}
dimMode={dimMode} dimMode={dimMode}
roomName={room.name} hierarchy={{ ...hierarchy, room: room.name }}
/> />
))} ))}
</group> </group>

View file

@ -6,21 +6,27 @@ import { PlantPosition, VisMode } from './types';
import { PlantSystem } from './PlantSystem'; import { PlantSystem } from './PlantSystem';
// Convert pixel coordinates to world units // Convert pixel coordinates to world units
// NOTE: Section posX/posY are RELATIVE to the room (already in room's local space)
// So we scale them for placement within the room group
const SCALE = 0.1; const SCALE = 0.1;
// Hierarchy context passed down from FacilityScene
export interface HierarchyContext {
facility?: string;
building?: string;
floor?: string;
room?: string;
}
interface SmartRackProps { interface SmartRackProps {
section: Section3D; section: Section3D;
visMode: VisMode; visMode: VisMode;
onPlantClick: (plant: PlantPosition) => void; onPlantClick: (plant: PlantPosition) => void;
highlightedTags?: string[]; highlightedTags?: string[];
dimMode?: boolean; dimMode?: boolean;
roomName?: string; hierarchy?: HierarchyContext;
} }
export function SmartRack({ section, visMode, onPlantClick, highlightedTags, dimMode, roomName }: SmartRackProps) { export function SmartRack({ section, visMode, onPlantClick, highlightedTags, dimMode, hierarchy }: SmartRackProps) {
// Section positions are RELATIVE to room, scale them // Section positions are absolute (after our fix), scale them
const scaledSection = { const scaledSection = {
posX: section.posX * SCALE, posX: section.posX * SCALE,
posY: section.posY * SCALE, posY: section.posY * SCALE,
@ -39,16 +45,21 @@ export function SmartRack({ section, visMode, onPlantClick, highlightedTags, dim
return section.positions.map((pos: Position3D) => ({ return section.positions.map((pos: Position3D) => ({
...pos, ...pos,
// Position plants WITHIN the section, starting from section origin // Position plants WITHIN the section
x: scaledSection.posX + (pos.column * colSpacing), x: scaledSection.posX + (pos.column * colSpacing),
z: scaledSection.posY + (pos.row * rowSpacing), z: scaledSection.posY + (pos.row * rowSpacing),
y: 0.4 + (pos.tier * 0.6), y: 0.4 + (pos.tier * 0.6),
// Full hierarchy breadcrumb
breadcrumb: { breadcrumb: {
facility: hierarchy?.facility,
building: hierarchy?.building,
floor: hierarchy?.floor,
room: hierarchy?.room,
section: section.code || section.name, section: section.code || section.name,
room: roomName, tier: pos.tier,
}, },
})); }));
}, [section, scaledSection, roomName]); }, [section, scaledSection, hierarchy]);
const distinctTiers = [...new Set(positions.map(p => p.tier))].sort((a, b) => a - b); 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 distinctRows = [...new Set(positions.map(p => p.row))].sort((a, b) => a - b);