fix(3d-viewer): remove duplicate function definitions from file
- Cleans up duplicate Facility3DViewerPage, FacilityScene, and PlantInstances functions - Ensures single clean export default
This commit is contained in:
parent
699cb73621
commit
e6d6fa6efc
1 changed files with 16 additions and 126 deletions
|
|
@ -49,123 +49,6 @@ class ErrorBoundary extends Component<{ children: ReactNode }, ErrorBoundaryStat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... unchanged parts ...
|
|
||||||
|
|
||||||
// Update Facility3DViewerPage to use ErrorBoundary
|
|
||||||
export default function Facility3DViewerPage() {
|
|
||||||
const [status, setStatus] = useState('Initializing...');
|
|
||||||
const [floorData, setFloorData] = useState<Floor3DData | null>(null);
|
|
||||||
const [selectedPlant, setSelectedPlant] = useState<any | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
loadData();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
async function loadData() {
|
|
||||||
setStatus('Loading layout...');
|
|
||||||
try {
|
|
||||||
const props = await layoutApi.getProperties();
|
|
||||||
if (props[0]?.buildings[0]?.floors[0]) {
|
|
||||||
const floorId = props[0].buildings[0].floors[0].id;
|
|
||||||
setStatus('Fetching 3D assets...');
|
|
||||||
const data = await layoutApi.getFloor3D(floorId);
|
|
||||||
setFloorData(data);
|
|
||||||
setStatus('');
|
|
||||||
} else {
|
|
||||||
setStatus('No floor layout found');
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
setStatus('Error: ' + (err as Error).message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="h-screen w-full relative bg-gray-950 text-white overflow-hidden">
|
|
||||||
{/* Header Overlay */}
|
|
||||||
<div className="absolute top-0 left-0 right-0 p-4 z-10 flex items-center justify-between bg-gradient-to-b from-black/80 to-transparent pointer-events-none">
|
|
||||||
<div className="pointer-events-auto flex items-center gap-4">
|
|
||||||
<Link to="/metrc" className="btn btn-ghost btn-sm text-white">
|
|
||||||
<ArrowLeft size={16} /> Back
|
|
||||||
</Link>
|
|
||||||
<div>
|
|
||||||
<h1 className="text-xl font-bold flex items-center gap-2">
|
|
||||||
Facility Viewer 3D
|
|
||||||
<span className="badge badge-accent text-xs">BETA</span>
|
|
||||||
</h1>
|
|
||||||
<p className="text-xs text-gray-400">
|
|
||||||
{floorData ? `${floorData.floor.name} • ${floorData.stats.occupiedPositions} Plants` : 'Loading...'}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Legend */}
|
|
||||||
<div className="flex gap-4 text-xs bg-black/50 p-2 rounded-lg backdrop-blur pointer-events-auto">
|
|
||||||
<div className="flex items-center gap-1"><div className="w-2 h-2 rounded-full bg-[#4ade80]"></div> Veg</div>
|
|
||||||
<div className="flex items-center gap-1"><div className="w-2 h-2 rounded-full bg-[#a855f7]"></div> Flower</div>
|
|
||||||
<div className="flex items-center gap-1"><div className="w-2 h-2 rounded-full bg-[#f59e0b]"></div> Dry</div>
|
|
||||||
<div className="flex items-center gap-1"><div className="w-2 h-2 rounded-full bg-[#374151]"></div> Empty</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Error/Status Overlay */}
|
|
||||||
{status && (
|
|
||||||
<div className="absolute inset-0 flex items-center justify-center z-50 bg-black/80 backdrop-blur-sm">
|
|
||||||
<div className="text-center">
|
|
||||||
<Loader2 size={32} className="animate-spin text-accent mx-auto mb-2" />
|
|
||||||
<p className="text-gray-300">{status}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Selection Overlay */}
|
|
||||||
{selectedPlant && (
|
|
||||||
<div className="absolute bottom-4 right-4 z-20 w-80 bg-gray-900/90 border border-gray-700 rounded-lg p-4 shadow-xl backdrop-blur-md animate-in slide-in-from-right-10 pointer-events-auto">
|
|
||||||
<div className="flex justify-between items-start mb-2">
|
|
||||||
<h3 className="font-bold text-accent">{selectedPlant.plant.tagNumber}</h3>
|
|
||||||
<button
|
|
||||||
onClick={() => setSelectedPlant(null)}
|
|
||||||
className="text-gray-400 hover:text-white"
|
|
||||||
>
|
|
||||||
×
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2 text-sm">
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<span className="text-gray-400">Strain:</span>
|
|
||||||
<span>{selectedPlant.plant.strain || 'Unknown'}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<span className="text-gray-400">Stage:</span>
|
|
||||||
<span className="badge badge-accent bg-opacity-20">{selectedPlant.plant.stage || 'N/A'}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<span className="text-gray-400">Batch:</span>
|
|
||||||
<span>{selectedPlant.plant.batchName || '-'}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<span className="text-gray-400">Location:</span>
|
|
||||||
<span className="font-mono text-xs">R{selectedPlant.row} T{selectedPlant.tier} S{selectedPlant.slot}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Canvas camera={{ position: [10, 15, 10], fov: 50 }}>
|
|
||||||
<ErrorBoundary>
|
|
||||||
<Suspense fallback={<Html center>Loading 3D Scene...</Html>}>
|
|
||||||
{floorData && (
|
|
||||||
<FacilityScene
|
|
||||||
data={floorData}
|
|
||||||
onSelectPlant={setSelectedPlant}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Suspense>
|
|
||||||
</ErrorBoundary>
|
|
||||||
</Canvas>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function PlantInstances({ positions, onPlantClick }: { positions: any[], onPlantClick: (p: any) => void }) {
|
function PlantInstances({ positions, onPlantClick }: { positions: any[], onPlantClick: (p: any) => void }) {
|
||||||
if (!positions || !Array.isArray(positions) || positions.length === 0) return null;
|
if (!positions || !Array.isArray(positions) || positions.length === 0) return null;
|
||||||
|
|
||||||
|
|
@ -178,7 +61,7 @@ function PlantInstances({ positions, onPlantClick }: { positions: any[], onPlant
|
||||||
// Limit instances to prevent crash on huge datasets just in case
|
// Limit instances to prevent crash on huge datasets just in case
|
||||||
if (safePositions.length > 5000) {
|
if (safePositions.length > 5000) {
|
||||||
console.warn('Too many positions to render:', safePositions.length);
|
console.warn('Too many positions to render:', safePositions.length);
|
||||||
return null;
|
return null; // Or return a subset
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -363,6 +246,11 @@ export default function Facility3DViewerPage() {
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<Loader2 size={32} className="animate-spin text-accent mx-auto mb-2" />
|
<Loader2 size={32} className="animate-spin text-accent mx-auto mb-2" />
|
||||||
<p className="text-gray-300">{status}</p>
|
<p className="text-gray-300">{status}</p>
|
||||||
|
{status.includes('Error') && (
|
||||||
|
<button onClick={() => window.location.reload()} className="btn btn-sm btn-outline mt-4">
|
||||||
|
Retry
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -401,14 +289,16 @@ export default function Facility3DViewerPage() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Canvas camera={{ position: [10, 15, 10], fov: 50 }}>
|
<Canvas camera={{ position: [10, 15, 10], fov: 50 }}>
|
||||||
<Suspense fallback={null}>
|
<ErrorBoundary>
|
||||||
{floorData && (
|
<Suspense fallback={<Html center>Loading 3D Scene...</Html>}>
|
||||||
<FacilityScene
|
{floorData && (
|
||||||
data={floorData}
|
<FacilityScene
|
||||||
onSelectPlant={setSelectedPlant}
|
data={floorData}
|
||||||
/>
|
onSelectPlant={setSelectedPlant}
|
||||||
)}
|
/>
|
||||||
</Suspense>
|
)}
|
||||||
|
</Suspense>
|
||||||
|
</ErrorBoundary>
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue