diff --git a/src/app/plant-builder/PlantBuilder.tsx b/src/app/plant-builder/PlantBuilder.tsx index 699ebd5..6c54fbe 100644 --- a/src/app/plant-builder/PlantBuilder.tsx +++ b/src/app/plant-builder/PlantBuilder.tsx @@ -509,12 +509,21 @@ export const PlantBuilder = () => { ) : step === "builder" ? ( -
- {/* Sidebar Container */} +
+
+ +
+ {/* Sidebar Container (overlay; does not shift canvas) */}
{showComponentLibrary && (
@@ -533,15 +542,6 @@ export const PlantBuilder = () => { )}
-
- -
) : step === "compliance" ? (
@@ -624,14 +624,14 @@ export const PlantBuilder = () => { Add Component -
- +
+
@@ -822,4 +822,4 @@ export const PlantBuilder = () => { ); }; -export default PlantBuilder; \ No newline at end of file +export default PlantBuilder; diff --git a/src/app/plant-operator/plant-builder/PlantBuilder.tsx b/src/app/plant-operator/plant-builder/PlantBuilder.tsx index 1b3fb49..99c67ee 100644 --- a/src/app/plant-operator/plant-builder/PlantBuilder.tsx +++ b/src/app/plant-operator/plant-builder/PlantBuilder.tsx @@ -59,7 +59,7 @@ import { PlacedComponent, Connection, } from "./types"; -import { createPlant } from "@/services/plant-builder/plants"; +import { createPlant, fetchPlantById, Plant } from "@/services/plant-builder/plants"; import { createDigitalTwin, fetchDigitalTwinJsonForPlant } from "@/services/plant-builder/digitalTwins"; import { updateComponentInstance, deleteComponentInstance } from "@/services/plant-builder/componentInstances"; import { buildConnectionPayloadForComponent, StoredConnectionPayload } from "@/lib/plant-builder/connection-utils"; @@ -167,6 +167,20 @@ export const PlantBuilder = () => { setStep("builder"); + const mapPlantToInfo = (plant: Plant): PlantInfo => { + const metadata = plant.metadata || {}; + return { + plantName: plant.name || "New Plant", + projectName: metadata.projectName || "", + projectType: metadata.projectType || "", + primaryFuelType: metadata.primaryFuelType || "", + country: plant.location || metadata.country || "", + status: plant.status || "", + commercialOperationalDate: metadata.commercialOperationalDate || "", + investment: metadata.investment, + }; + }; + (async () => { try { toast.info("Loading digital twin from database…"); @@ -222,6 +236,13 @@ export const PlantBuilder = () => { setComponents(mappedComponents); setConnections(mappedConnections); setOriginalComponents(mappedComponents); // Track originals for delete detection + + try { + const plant = await fetchPlantById(plantId); + setPlantInfo(mapPlantToInfo(plant)); + } catch (err) { + console.warn("Failed to load plant details:", err); + } // Set global IDs for Canvas persistence try { @@ -261,7 +282,6 @@ export const PlantBuilder = () => { const payload = { name: info.plantName, - user_id: 1, // hardcoded for now location: info.country, status: info.status, metadata: { @@ -858,12 +878,22 @@ export const PlantBuilder = () => {
) : step === "builder" ? ( -
- {/* Sidebar Container */} +
+
+ +
+ {/* Sidebar Container (overlay; does not shift canvas) */}
{showComponentLibrary && (
@@ -882,16 +912,6 @@ export const PlantBuilder = () => { )}
-
- -
) : step === "compliance" ? (
@@ -966,23 +986,15 @@ export const PlantBuilder = () => {

Process Flow Diagram

- -
- +
+
@@ -1037,23 +1049,7 @@ export const PlantBuilder = () => {
- +
diff --git a/src/components/plant-builder/ComponentLibrary.tsx b/src/components/plant-builder/ComponentLibrary.tsx index 0888bdf..1e8c6a6 100644 --- a/src/components/plant-builder/ComponentLibrary.tsx +++ b/src/components/plant-builder/ComponentLibrary.tsx @@ -184,11 +184,19 @@ const ComponentLibrary = () => { - {Object.entries(grouped).map(([category, items]) => ( + {Object.entries(grouped).map(([category, items]) => { + const normalized = category.trim().toLowerCase(); + const hideCategory = + (type === "equipment" && (normalized === "equipment" || normalized === "equipments")) || + (type === "carrier" && (normalized === "carrier" || normalized === "carriers")) || + (type === "gate" && (normalized === "gate" || normalized === "gates")); + return (
-
- {category} -
+ {!hideCategory && ( +
+ {category} +
+ )}
{items.map((component) => ( { ))}
- ))} + )})}
); diff --git a/src/components/plant-builder/PlantComponent.tsx b/src/components/plant-builder/PlantComponent.tsx index ba38ba6..58ba9c9 100644 --- a/src/components/plant-builder/PlantComponent.tsx +++ b/src/components/plant-builder/PlantComponent.tsx @@ -1,5 +1,6 @@ // src/components/plant-builder/PlantComponent.tsx import { useState, useRef, useEffect } from "react"; +import type { RefObject } from "react"; import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Building2, Zap, ArrowRightLeft, X } from "lucide-react"; @@ -12,6 +13,8 @@ import type { Position, PlacedComponent } from "@/app/plant-operator/plant-build interface PlantComponentProps { component: PlacedComponent; + canvasRef: RefObject; + zoom: number; onClick: () => void; onMove: (id: string, position: Position) => void; onConnectStart: (id: string) => void; @@ -76,6 +79,8 @@ const getBaseShapeClasses = (type: string) => { /* ─────────────────────── COMPONENT ─────────────────────── */ const PlantComponent = ({ component, + canvasRef, + zoom, onClick, onMove, onConnectStart, @@ -85,6 +90,8 @@ const PlantComponent = ({ }: PlantComponentProps) => { const [position, setPosition] = useState(component.position); const cardRef = useRef(null); + const didDragRef = useRef(false); + const ignoreClickRef = useRef(false); // keep local position in sync if parent updates it useEffect(() => { @@ -110,11 +117,27 @@ const PlantComponent = ({ /* ───── drag ───── */ const handleMouseDown = (e: React.MouseEvent) => { e.stopPropagation(); - const startX = e.clientX - position.x; - const startY = e.clientY - position.y; + didDragRef.current = false; + const canvas = canvasRef.current; + if (!canvas) return; + const rect = canvas.getBoundingClientRect(); + const startX = (e.clientX - rect.left + canvas.scrollLeft) / zoom - position.x; + const startY = (e.clientY - rect.top + canvas.scrollTop) / zoom - position.y; + const originClientX = e.clientX; + const originClientY = e.clientY; const move = (ev: MouseEvent) => { - const newPos = { x: ev.clientX - startX, y: ev.clientY - startY }; + if (!didDragRef.current) { + const dx = ev.clientX - originClientX; + const dy = ev.clientY - originClientY; + if (Math.hypot(dx, dy) > 4) { + didDragRef.current = true; + } + } + const newPos = { + x: (ev.clientX - rect.left + canvas.scrollLeft) / zoom - startX, + y: (ev.clientY - rect.top + canvas.scrollTop) / zoom - startY, + }; setPosition(newPos); onMove(component.id, newPos); }; @@ -122,6 +145,7 @@ const PlantComponent = ({ const up = () => { document.removeEventListener("mousemove", move); document.removeEventListener("mouseup", up); + ignoreClickRef.current = didDragRef.current; }; document.addEventListener("mousemove", move); @@ -153,7 +177,13 @@ const PlantComponent = ({ className="absolute cursor-move select-none" style={{ left: position.x, top: position.y }} onMouseDown={handleMouseDown} - onClick={onClick} + onDoubleClick={() => { + if (ignoreClickRef.current) { + ignoreClickRef.current = false; + return; + } + onClick(); + }} > { return apiFetch(PLANTS_PATH); } +export async function fetchPlantById(plantId: number): Promise { + return apiFetch(`${PLANTS_PATH}/${plantId}`); +} + export async function createPlant(payload: { name: string; - user_id: number; location?: string; status?: string; metadata?: Record;