feat: Add MiningAreasPanel component for managing mining areas with create, update, and delete functionalities
This commit is contained in:
@@ -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} />}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user