Add StorageOverview component for inventory management display
This commit is contained in:
123
web/client/src/components/StorageOverview.jsx
Normal file
123
web/client/src/components/StorageOverview.jsx
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useInventoryStore } from '../store/inventoryStore';
|
||||||
|
import './StorageOverview.css';
|
||||||
|
|
||||||
|
function StorageOverview() {
|
||||||
|
const inventory = useInventoryStore((state) => state.inventory);
|
||||||
|
const activity = useInventoryStore((state) => state.activity);
|
||||||
|
const connected = useInventoryStore((state) => state.connected);
|
||||||
|
const lastUpdate = useInventoryStore((state) => state.lastUpdate);
|
||||||
|
const craftTurtleOk = useInventoryStore((state) => state.craftTurtleOk);
|
||||||
|
const requestScan = useInventoryStore((state) => state.requestScan);
|
||||||
|
|
||||||
|
const usedPercent = inventory.totalSlots > 0
|
||||||
|
? Math.round((inventory.usedSlots / inventory.totalSlots) * 100)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
const getUsageColor = () => {
|
||||||
|
if (usedPercent >= 90) return 'critical';
|
||||||
|
if (usedPercent >= 75) return 'warning';
|
||||||
|
return 'normal';
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeSinceUpdate = lastUpdate
|
||||||
|
? Math.floor((Date.now() - lastUpdate) / 1000)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="storage-overview">
|
||||||
|
<div className="overview-header">
|
||||||
|
<h2>📊 Storage</h2>
|
||||||
|
<button className="mc-btn blue" onClick={requestScan} title="Refresh inventory scan">
|
||||||
|
🔍 Scan
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Storage capacity bar */}
|
||||||
|
<div className="capacity-section">
|
||||||
|
<div className="capacity-label">
|
||||||
|
<span>Capacity</span>
|
||||||
|
<span className={`capacity-percent ${getUsageColor()}`}>{usedPercent}%</span>
|
||||||
|
</div>
|
||||||
|
<div className="capacity-bar">
|
||||||
|
<div
|
||||||
|
className={`capacity-fill ${getUsageColor()}`}
|
||||||
|
style={{ width: `${usedPercent}%` }}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<div className="capacity-detail">
|
||||||
|
{inventory.usedSlots} / {inventory.totalSlots} slots
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Stats grid */}
|
||||||
|
<div className="stats-grid">
|
||||||
|
<div className="stat-item">
|
||||||
|
<span className="stat-label">Total Items</span>
|
||||||
|
<span className="stat-value">{(inventory.grandTotal || 0).toLocaleString()}</span>
|
||||||
|
</div>
|
||||||
|
<div className="stat-item">
|
||||||
|
<span className="stat-label">Item Types</span>
|
||||||
|
<span className="stat-value">{(inventory.itemList || []).length}</span>
|
||||||
|
</div>
|
||||||
|
<div className="stat-item">
|
||||||
|
<span className="stat-label">Chests</span>
|
||||||
|
<span className="stat-value">{inventory.chestCount || 0}</span>
|
||||||
|
</div>
|
||||||
|
<div className="stat-item">
|
||||||
|
<span className="stat-label">Free Slots</span>
|
||||||
|
<span className="stat-value">{inventory.freeSlots || 0}</span>
|
||||||
|
</div>
|
||||||
|
<div className="stat-item">
|
||||||
|
<span className="stat-label">Furnaces</span>
|
||||||
|
<span className="stat-value">{inventory.furnaceCount || 0}</span>
|
||||||
|
</div>
|
||||||
|
<div className="stat-item">
|
||||||
|
<span className="stat-label">Craft Turtle</span>
|
||||||
|
<span className={`stat-value ${craftTurtleOk ? 'ok' : 'offline'}`}>
|
||||||
|
{craftTurtleOk ? '✅ Online' : '❌ Offline'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Peripherals */}
|
||||||
|
<div className="peripherals-section">
|
||||||
|
<h3>Peripherals</h3>
|
||||||
|
<div className="peripheral-list">
|
||||||
|
<div className={`peripheral-item ${inventory.dropperOk ? 'ok' : 'error'}`}>
|
||||||
|
<span>{inventory.dropperOk ? '✅' : '❌'} Dropper</span>
|
||||||
|
</div>
|
||||||
|
<div className={`peripheral-item ${inventory.barrelOk ? 'ok' : 'error'}`}>
|
||||||
|
<span>{inventory.barrelOk ? '✅' : '❌'} Barrel</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Activity indicators */}
|
||||||
|
<div className="activity-section">
|
||||||
|
<h3>Activity</h3>
|
||||||
|
<div className="activity-list">
|
||||||
|
{activity.sorting && <div className="activity-item active">📦 Sorting</div>}
|
||||||
|
{activity.smelting && <div className="activity-item active">🔥 Smelting</div>}
|
||||||
|
{activity.dispensing && <div className="activity-item active">📤 Dispensing</div>}
|
||||||
|
{activity.composting && <div className="activity-item active">🌱 Composting</div>}
|
||||||
|
{activity.defragging && <div className="activity-item active">🔧 Defragging</div>}
|
||||||
|
{activity.crafting && <div className="activity-item active">🔨 Crafting</div>}
|
||||||
|
{!activity.sorting && !activity.smelting && !activity.dispensing &&
|
||||||
|
!activity.composting && !activity.defragging && !activity.crafting && (
|
||||||
|
<div className="activity-item idle">💤 Idle</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Last update */}
|
||||||
|
{lastUpdate > 0 && (
|
||||||
|
<div className="last-update">
|
||||||
|
Last update: {timeSinceUpdate !== null ? `${timeSinceUpdate}s ago` : 'Never'}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StorageOverview;
|
||||||
Reference in New Issue
Block a user