sandbox/client/src/components/UIElementEditor.js

161 lines
7.2 KiB
JavaScript

import React, { useState } from 'react';
const ELEMENT_TYPES = [
{ value: 'progress_bar', label: 'Progress Bar' },
{ value: 'gauge', label: 'Gauge' },
{ value: 'icon', label: 'Icon' },
{ value: 'text_label', label: 'Text Label' },
{ value: 'radial', label: 'Radial Dial' },
{ value: 'waveform', label: 'Waveform' },
];
const COLORS = ['#e74c3c', '#f39c12', '#3498db', '#8e44ad', '#e91e63', '#ff6b6b', '#2ecc71', '#1abc9c'];
export default function UIElementEditor({ uiElements, needs, onAdd, onUpdate, onDelete }) {
const [editingId, setEditingId] = useState(null);
const [editForm, setEditForm] = useState({});
const startEdit = (el) => {
setEditingId(el.id);
setEditForm({ ...el, config: typeof el.config === 'string' ? JSON.parse(el.config) : el.config });
};
const cancelEdit = () => {
setEditingId(null);
setEditForm({});
};
const saveEdit = () => {
onUpdate(editingId, editForm);
setEditingId(null);
};
const handleAdd = () => {
onAdd({
need_id: needs?.[0]?.id || null,
element_type: 'progress_bar',
config: { color: '#3498db', label: 'New Element', animation: 'smooth' },
});
};
return (
<div className="ui-element-editor">
<div className="editor-toolbar">
<h3>UI Elements</h3>
<button className="btn-primary" onClick={handleAdd}>Add UI Element</button>
</div>
{(!uiElements || uiElements.length === 0) ? (
<p className="empty-state">No UI elements defined. These represent how needs will visually appear.</p>
) : (
<div className="ui-element-list">
{uiElements.map((el) => {
const config = typeof el.config === 'string' ? JSON.parse(el.config) : el.config;
const need = needs?.find((n) => n.id === el.need_id);
return (
<div key={el.id} className="ui-element-card">
{editingId === el.id ? (
<div className="ui-edit-form">
<div className="form-row">
<label>Linked Need</label>
<select value={editForm.need_id || ''} onChange={(e) => setEditForm({ ...editForm, need_id: e.target.value })}>
<option value="">None</option>
{needs?.map((n) => (
<option key={n.id} value={n.id}>{n.name}</option>
))}
</select>
</div>
<div className="form-row">
<label>Element Type</label>
<select value={editForm.element_type} onChange={(e) => setEditForm({ ...editForm, element_type: e.target.value })}>
{ELEMENT_TYPES.map((t) => (
<option key={t.value} value={t.value}>{t.label}</option>
))}
</select>
</div>
<div className="form-row">
<label>Color</label>
<div className="color-picker">
{COLORS.map((c) => (
<button
key={c}
className={`color-swatch ${editForm.config?.color === c ? 'selected' : ''}`}
style={{ backgroundColor: c }}
onClick={() => setEditForm({ ...editForm, config: { ...editForm.config, color: c } })}
/>
))}
</div>
</div>
<div className="form-row">
<label>Label</label>
<input value={editForm.config?.label || ''} onChange={(e) => setEditForm({ ...editForm, config: { ...editForm.config, label: e.target.value } })} />
</div>
<div className="form-row">
<label>Animation</label>
<select value={editForm.config?.animation || 'none'} onChange={(e) => setEditForm({ ...editForm, config: { ...editForm.config, animation: e.target.value } })}>
<option value="none">None</option>
<option value="smooth">Smooth</option>
<option value="pulse">Pulse</option>
<option value="bounce">Bounce</option>
</select>
</div>
<div className="form-actions">
<button className="btn-primary" onClick={saveEdit}>Save</button>
<button className="btn-secondary" onClick={cancelEdit}>Cancel</button>
</div>
</div>
) : (
<>
<div className="ui-element-preview" style={{ borderLeftColor: config?.color || '#95a5a6' }}>
<div className="ui-element-header">
<h4>{config?.label || el.element_type}</h4>
<span className="element-type-badge">{ELEMENT_TYPES.find(t => t.value === el.element_type)?.label || el.element_type}</span>
</div>
<div className="ui-element-details">
<span>Linked to: <strong>{need?.name || 'None'}</strong></span>
<span>Animation: <strong>{config?.animation || 'none'}</strong></span>
</div>
<div className="ui-element-visual">
{el.element_type === 'progress_bar' && (
<div className="visual-progress-bar">
<div className="visual-progress-fill" style={{ width: '60%', backgroundColor: config?.color }} />
</div>
)}
{el.element_type === 'gauge' && (
<div className="visual-gauge" style={{ color: config?.color }}>
<span className="gauge-value">75%</span>
</div>
)}
{el.element_type === 'radial' && (
<div className="visual-radial" style={{ borderColor: config?.color }}>
<span>60%</span>
</div>
)}
{(el.element_type === 'icon' || el.element_type === 'text_label') && (
<div className="visual-icon" style={{ color: config?.color }}>
{config?.label || el.element_type}
</div>
)}
{el.element_type === 'waveform' && (
<div className="visual-waveform" style={{ color: config?.color }}>
~ ~ ~
</div>
)}
</div>
</div>
<div className="ui-element-actions">
<button className="btn-small" onClick={() => startEdit(el)}>Edit</button>
<button className="btn-danger-small" onClick={() => onDelete(el.id)}>Delete</button>
</div>
</>
)}
</div>
);
})}
</div>
)}
</div>
);
}