feat: Add MiningAreasPanel component for managing mining areas with create, update, and delete functionalities

This commit is contained in:
MayaTheShy
2026-02-19 23:02:17 -05:00
parent 8bcee0b16c
commit 90b1e9fdec
4 changed files with 1017 additions and 1 deletions

View File

@@ -414,12 +414,127 @@ function HomeMarker({ position }) {
);
}
// Mining area wireframe component
function MiningArea({ area, turtle, isSelected, onClick }) {
const meshRef = useRef();
const [hovered, setHovered] = useState(false);
// Calculate dimensions
const width = Math.abs(area.endX - area.startX) + 1;
const height = Math.abs(area.endY - area.startY) + 1;
const depth = Math.abs(area.endZ - area.startZ) + 1;
// Calculate center position
const centerX = (area.startX + area.endX) / 2;
const centerY = (area.startY + area.endY) / 2;
const centerZ = (area.startZ + area.endZ) / 2;
// Color based on turtle or status
const getColor = () => {
if (area.status === 'completed') return '#10b981'; // green
if (area.status === 'mining') return '#f59e0b'; // orange
if (turtle) return turtle.color || '#3b82f6'; // turtle color or blue
return '#6366f1'; // default purple
};
const color = getColor();
const opacity = isSelected ? 0.3 : hovered ? 0.2 : 0.1;
const lineWidth = isSelected ? 2.5 : hovered ? 2 : 1.5;
// Animate rotation slightly
useFrame((state) => {
if (meshRef.current && isSelected) {
meshRef.current.rotation.y = Math.sin(state.clock.elapsedTime * 0.5) * 0.1;
}
});
return (
<group
position={[centerX, centerY, centerZ]}
onClick={onClick}
onPointerOver={() => setHovered(true)}
onPointerOut={() => setHovered(false)}
ref={meshRef}
>
{/* Wireframe box */}
<lineSegments>
<edgesGeometry args={[new THREE.BoxGeometry(width, height, depth)]} />
<lineBasicMaterial color={color} linewidth={lineWidth} />
</lineSegments>
{/* Semi-transparent fill */}
<mesh>
<boxGeometry args={[width, height, depth]} />
<meshStandardMaterial
color={color}
transparent
opacity={opacity}
side={THREE.DoubleSide}
/>
</mesh>
{/* Label */}
<Text
position={[0, height / 2 + 1, 0]}
fontSize={0.5}
color={color}
anchorX="center"
anchorY="middle"
>
{area.areaName || `Area ${area.areaID}`}
</Text>
{/* Dimensions label */}
<Text
position={[0, height / 2 + 0.3, 0]}
fontSize={0.3}
color="white"
anchorX="center"
anchorY="middle"
>
{width}×{height}×{depth}
</Text>
{/* Status indicator */}
{area.status === 'mining' && (
<mesh position={[0, height / 2 + 1.8, 0]}>
<sphereGeometry args={[0.2, 16, 16]} />
<meshStandardMaterial color="#f59e0b" emissive="#f59e0b" emissiveIntensity={0.8} />
</mesh>
)}
</group>
);
}
// Main scene component
function Scene() {
const turtles = useTurtleStore((state) => state.getTurtleArray());
const selectedTurtleId = useTurtleStore((state) => state.selectedTurtleId);
const selectTurtle = useTurtleStore((state) => state.selectTurtle);
const worldBlocks = useTurtleStore((state) => state.worldBlocks || []);
// Mining areas state
const [miningAreas, setMiningAreas] = useState([]);
const [selectedAreaId, setSelectedAreaId] = useState(null);
// Load mining areas
useEffect(() => {
const fetchMiningAreas = async () => {
try {
const response = await fetch('http://localhost:3001/api/mining-areas');
if (response.ok) {
const data = await response.json();
setMiningAreas(data);
}
} catch (error) {
console.error('Failed to fetch mining areas:', error);
}
};
fetchMiningAreas();
const interval = setInterval(fetchMiningAreas, 5000); // Refresh every 5 seconds
return () => clearInterval(interval);
}, []);
// Calculate center point for camera focus
const centerPoint = useMemo(() => {
@@ -469,6 +584,20 @@ function Scene() {
{/* Render discovered blocks */}
<WorldBlocks blocks={worldBlocks} />
{/* Mining areas */}
{miningAreas.map((area) => {
const turtle = turtles.find(t => t.turtleID === area.turtleID);
return (
<MiningArea
key={area.areaID}
area={area}
turtle={turtle}
isSelected={selectedAreaId === area.areaID}
onClick={() => setSelectedAreaId(selectedAreaId === area.areaID ? null : area.areaID)}
/>
);
})}
{/* Home marker */}
{homePosition && <HomeMarker position={homePosition} />}