feat: Implement persistence for plant placements in layout editor
Some checks are pending
Test / backend-test (push) Waiting to run
Test / frontend-test (push) Waiting to run

This commit is contained in:
fullsizemalt 2026-01-01 16:29:45 -08:00
parent ec9e98e696
commit 7ec8b1fc57
4 changed files with 34 additions and 16 deletions

View file

@ -644,6 +644,8 @@ model FacilityPlant {
tagNumber String @unique // METRC tag tagNumber String @unique // METRC tag
batchId String? batchId String?
batch Batch? @relation(fields: [batchId], references: [id]) batch Batch? @relation(fields: [batchId], references: [id])
plantTypeId String?
plantType PlantType? @relation(fields: [plantTypeId], references: [id])
position FacilityPosition @relation(fields: [positionId], references: [id]) position FacilityPosition @relation(fields: [positionId], references: [id])
positionId String @unique positionId String @unique
address String // Full hierarchical address address String // Full hierarchical address

View file

@ -591,7 +591,7 @@ export async function layoutRoutes(fastify: FastifyInstance, options: FastifyPlu
handler: async (request, reply) => { handler: async (request, reply) => {
try { try {
const { id } = request.params as any; const { id } = request.params as any;
const { batchId } = request.body as any; const { batchId, plantTypeId } = request.body as any;
const position = await prisma.facilityPosition.findUnique({ const position = await prisma.facilityPosition.findUnique({
where: { id }, where: { id },
@ -614,6 +614,7 @@ export async function layoutRoutes(fastify: FastifyInstance, options: FastifyPlu
data: { data: {
tagNumber: `P-${Date.now()}-${Math.floor(Math.random() * 1000)}`, tagNumber: `P-${Date.now()}-${Math.floor(Math.random() * 1000)}`,
batchId, batchId,
plantTypeId,
positionId: id, positionId: id,
address, address,
status: 'ACTIVE' status: 'ACTIVE'

View file

@ -55,15 +55,23 @@ export function LayoutEditor({ floorId, className }: LayoutEditorProps) {
rows: section.rows, rows: section.rows,
columns: section.columns, columns: section.columns,
tiers: 1, // TODO: get from section tiers: 1, // TODO: get from section
slots: section.positions.map(pos => ({ slots: section.positions.map(pos => {
// Find plant type by ID (new way) or strain name (legacy way)
const mappedPlantType = pos.plant
? plantTypes.find(pt => pt.id === pos.plant?.plantTypeId) ||
plantTypes.find(pt => pt.strain === pos.plant?.strain)
: undefined;
return {
id: pos.id, id: pos.id,
row: pos.row, row: pos.row,
column: pos.column, column: pos.column,
tier: pos.tier, tier: pos.tier,
status: pos.status as PlantSlotData['status'], status: pos.status as PlantSlotData['status'],
plantType: pos.plant ? plantTypes.find(pt => pt.strain === pos.plant?.strain) : undefined, plantType: mappedPlantType,
tagNumber: pos.plant?.tagNumber, tagNumber: pos.plant?.tagNumber,
})), };
}),
})) }))
) || []; ) || [];
@ -72,10 +80,17 @@ export function LayoutEditor({ floorId, className }: LayoutEditorProps) {
}, []); }, []);
const handleSlotDrop = useCallback(async (slot: PlantSlotData, plantType: LayoutPlantType) => { const handleSlotDrop = useCallback(async (slot: PlantSlotData, plantType: LayoutPlantType) => {
console.log('Dropped', plantType.name, 'onto slot', slot.id); try {
// TODO: Implement actual placement via API await layoutApi.occupyPosition(slot.id, { plantTypeId: plantType.id });
// For now, just log the action
}, []); // Reload floor data to reflect changes
const floor = await layoutApi.getFloor3D(floorId);
setFloorData(floor);
} catch (e) {
console.error('Failed to place plant:', e);
setError('Failed to place plant. Please try again.');
}
}, [floorId]);
const handleDragStart = useCallback((plantType: LayoutPlantType) => { const handleDragStart = useCallback((plantType: LayoutPlantType) => {
setDraggingType(plantType); setDraggingType(plantType);

View file

@ -246,8 +246,8 @@ export const layoutApi = {
return response.data; return response.data;
}, },
async placeBatchInPosition(positionId: string, batchId: string): Promise<void> { async occupyPosition(positionId: string, data: { batchId?: string; plantTypeId?: string }): Promise<void> {
await api.post(`/layout/positions/${positionId}/occupy`, { batchId }); await api.post(`/layout/positions/${positionId}/occupy`, data);
}, },
async fillSection(sectionId: string, batchId: string, maxCount?: number): Promise<{ plantsCreated: number; message: string }> { async fillSection(sectionId: string, batchId: string, maxCount?: number): Promise<{ plantsCreated: number; message: string }> {