sandbox/client/src/components/BrainRuleEditor.js

179 lines
7.5 KiB
JavaScript

import React, { useState } from 'react';
const OPERATORS = [
{ value: 'lt', label: '< (less than)' },
{ value: 'lte', label: '≤ (less than or equal)' },
{ value: 'eq', label: '= (equal to)' },
{ value: 'gte', label: '≥ (greater than or equal)' },
{ value: 'gt', label: '> (greater than)' },
];
const ACTIONS = [
{ value: 'seek_fulfillment', label: 'Seek Fulfillment' },
{ value: 'alert', label: 'Trigger Alert' },
{ value: 'change_state', label: 'Change Character State' },
{ value: 'generate_event', label: 'Generate Roleplay Event' },
{ value: 'activate_rule', label: 'Activate Other Rule' },
];
export default function BrainRuleEditor({ brainRules, needs, onAdd, onUpdate, onDelete }) {
const [editingId, setEditingId] = useState(null);
const [editForm, setEditForm] = useState({});
const startEdit = (rule) => {
setEditingId(rule.id);
const condition = typeof rule.condition === 'string' ? JSON.parse(rule.condition) : rule.condition;
const action = typeof rule.action === 'string' ? JSON.parse(rule.action) : rule.action;
setEditForm({ ...rule, condition, action });
};
const cancelEdit = () => {
setEditingId(null);
setEditForm({});
};
const saveEdit = () => {
onUpdate(editingId, {
condition: editForm.condition,
action: editForm.action,
priority: editForm.priority,
enabled: editForm.enabled,
});
setEditingId(null);
};
const handleAdd = () => {
const newRule = {
condition: { need: needs?.[0]?.name || '', operator: 'lt', value: 30 },
action: { type: 'seek_fulfillment', target: '' },
priority: 0,
enabled: true,
};
onAdd(newRule);
};
return (
<div className="brain-rule-editor">
<div className="editor-toolbar">
<h3>Brain Rules</h3>
<button className="btn-primary" onClick={handleAdd}>Add Rule</button>
</div>
{(!brainRules || brainRules.length === 0) ? (
<p className="empty-state">No brain rules defined. Rules define how your character reacts to changing needs.</p>
) : (
<div className="brain-rules-list">
{brainRules.map((rule) => {
const condition = typeof rule.condition === 'string' ? JSON.parse(rule.condition) : rule.condition;
const action = typeof rule.action === 'string' ? JSON.parse(rule.action) : rule.action;
return (
<div key={rule.id} className={`brain-rule-card ${rule.enabled ? '' : 'disabled-rule'}`}>
{editingId === rule.id ? (
<div className="rule-edit-form">
<div className="form-row">
<label>Condition: Need</label>
<select
value={editForm.condition?.need || ''}
onChange={(e) => setEditForm({ ...editForm, condition: { ...editForm.condition, need: e.target.value } })}
>
{needs?.map((n) => (
<option key={n.id} value={n.name}>{n.name}</option>
))}
</select>
</div>
<div className="form-row">
<label>Operator</label>
<select
value={editForm.condition?.operator || 'lt'}
onChange={(e) => setEditForm({ ...editForm, condition: { ...editForm.condition, operator: e.target.value } })}
>
{OPERATORS.map((o) => (
<option key={o.value} value={o.value}>{o.label}</option>
))}
</select>
</div>
<div className="form-row">
<label>Threshold Value</label>
<input
type="number"
value={editForm.condition?.value || 0}
onChange={(e) => setEditForm({ ...editForm, condition: { ...editForm.condition, value: +e.target.value } })}
/>
</div>
<div className="form-row">
<label>Action Type</label>
<select
value={editForm.action?.type || 'seek_fulfillment'}
onChange={(e) => setEditForm({ ...editForm, action: { ...editForm.action, type: e.target.value } })}
>
{ACTIONS.map((a) => (
<option key={a.value} value={a.value}>{a.label}</option>
))}
</select>
</div>
<div className="form-row">
<label>Action Target</label>
<input
value={editForm.action?.target || ''}
onChange={(e) => setEditForm({ ...editForm, action: { ...editForm.action, target: e.target.value } })}
placeholder="Target need or behavior"
/>
</div>
<div className="form-row">
<label>Priority</label>
<input
type="number"
value={editForm.priority || 0}
onChange={(e) => setEditForm({ ...editForm, priority: +e.target.value })}
/>
</div>
<div className="form-row">
<label>Enabled</label>
<input
type="checkbox"
checked={editForm.enabled}
onChange={(e) => setEditForm({ ...editForm, enabled: e.target.checked })}
/>
</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="rule-header">
<div className="rule-condition">
<span className="condition-text">
IF <strong>{condition?.need}</strong> {OPERATORS.find(o => o.value === condition?.operator)?.label || condition?.operator} <strong>{condition?.value}</strong>
</span>
</div>
<div className="rule-action-type">
<span className="action-text">
THEN <strong>{ACTIONS.find(a => a.value === action?.type)?.label || action?.type}</strong>
{action?.target ? `: ${action.target}` : ''}
</span>
</div>
<span className={`rule-status ${rule.enabled ? 'active' : 'inactive'}`}>
{rule.enabled ? 'Active' : 'Disabled'}
</span>
</div>
<div className="rule-meta">
<span>Priority: {rule.priority}</span>
</div>
<div className="rule-actions">
<button className="btn-small" onClick={() => startEdit(rule)}>Edit</button>
<button className="btn-danger-small" onClick={() => onDelete(rule.id)}>Delete</button>
</div>
</>
)}
</div>
);
})}
</div>
)}
</div>
);
}