Add SettingsPanel component for managing dropper nicknames
This commit is contained in:
145
web/client/src/components/SettingsPanel.jsx
Normal file
145
web/client/src/components/SettingsPanel.jsx
Normal file
@@ -0,0 +1,145 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import { useInventoryStore } from '../store/inventoryStore';
|
||||
import './SettingsPanel.css';
|
||||
|
||||
function SettingsPanel({ isOpen, onClose }) {
|
||||
const droppers = useInventoryStore((state) => state.inventory.droppers) || [];
|
||||
const dropperNicknames = useInventoryStore((state) => state.dropperNicknames) || {};
|
||||
const setDropperNickname = useInventoryStore((state) => state.setDropperNickname);
|
||||
|
||||
const [editingDropper, setEditingDropper] = useState(null);
|
||||
const [editValue, setEditValue] = useState('');
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
const startEditing = useCallback((dropperName) => {
|
||||
setEditingDropper(dropperName);
|
||||
setEditValue(dropperNicknames[dropperName] || '');
|
||||
}, [dropperNicknames]);
|
||||
|
||||
const saveNickname = useCallback(async () => {
|
||||
if (!editingDropper) return;
|
||||
setSaving(true);
|
||||
await setDropperNickname(editingDropper, editValue);
|
||||
setSaving(false);
|
||||
setEditingDropper(null);
|
||||
setEditValue('');
|
||||
}, [editingDropper, editValue, setDropperNickname]);
|
||||
|
||||
const removeNickname = useCallback(async (dropperName) => {
|
||||
setSaving(true);
|
||||
await setDropperNickname(dropperName, '');
|
||||
setSaving(false);
|
||||
}, [setDropperNickname]);
|
||||
|
||||
const handleKeyDown = useCallback((e) => {
|
||||
if (e.key === 'Enter') saveNickname();
|
||||
if (e.key === 'Escape') {
|
||||
setEditingDropper(null);
|
||||
setEditValue('');
|
||||
}
|
||||
}, [saveNickname]);
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="settings-overlay" onClick={onClose}>
|
||||
<div className="settings-panel" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="settings-header">
|
||||
<h2>⚙️ Settings</h2>
|
||||
<button className="settings-close" onClick={onClose}>✕</button>
|
||||
</div>
|
||||
|
||||
<div className="settings-body">
|
||||
<div className="settings-section">
|
||||
<h3>🏷️ Dropper Nicknames</h3>
|
||||
<p className="settings-hint">
|
||||
Give your dispensers friendly names so they're easier to identify.
|
||||
</p>
|
||||
|
||||
{droppers.length === 0 ? (
|
||||
<div className="settings-empty">
|
||||
No droppers detected. Connect the bridge to discover droppers.
|
||||
</div>
|
||||
) : (
|
||||
<div className="dropper-nickname-list">
|
||||
{droppers.map((d) => {
|
||||
const nickname = dropperNicknames[d.name];
|
||||
const isEditing = editingDropper === d.name;
|
||||
const shortName = d.name.replace(/^minecraft:/, '');
|
||||
|
||||
return (
|
||||
<div key={d.name} className="dropper-nickname-row">
|
||||
<div className="dropper-nickname-info">
|
||||
<span className="dropper-raw-name">
|
||||
{shortName}
|
||||
{d.isDefault && <span className="dropper-badge default">default</span>}
|
||||
{d.clientId && <span className="dropper-badge client">client {d.clientId}</span>}
|
||||
</span>
|
||||
{nickname && !isEditing && (
|
||||
<span className="dropper-current-nick">"{nickname}"</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="dropper-nickname-actions">
|
||||
{isEditing ? (
|
||||
<>
|
||||
<input
|
||||
type="text"
|
||||
className="nickname-input"
|
||||
value={editValue}
|
||||
onChange={(e) => setEditValue(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder="Enter nickname..."
|
||||
maxLength={32}
|
||||
autoFocus
|
||||
disabled={saving}
|
||||
/>
|
||||
<button
|
||||
className="mc-btn green nick-btn"
|
||||
onClick={saveNickname}
|
||||
disabled={saving}
|
||||
>
|
||||
✓
|
||||
</button>
|
||||
<button
|
||||
className="mc-btn nick-btn"
|
||||
onClick={() => { setEditingDropper(null); setEditValue(''); }}
|
||||
disabled={saving}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<button
|
||||
className="mc-btn nick-btn"
|
||||
onClick={() => startEditing(d.name)}
|
||||
title="Edit nickname"
|
||||
>
|
||||
✏️
|
||||
</button>
|
||||
{nickname && (
|
||||
<button
|
||||
className="mc-btn red nick-btn"
|
||||
onClick={() => removeNickname(d.name)}
|
||||
title="Remove nickname"
|
||||
disabled={saving}
|
||||
>
|
||||
🗑️
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SettingsPanel;
|
||||
Reference in New Issue
Block a user