/
h0tnanny
/
IotPlatform
Обзор
Документация
Войти
/
h0tnanny
/
IotPlatform
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
client/src/components/WorkflowEditor/VariablesPanel.tsx
285 строк
10 KB
h0tnanny
fix readonly permissions
01 фев 2026, 23:32
01 фев 2026, 23:32
01880f6
Код
Авторство
О чём код?
import { useState } from 'react'; import { Plus, Trash2, Edit2, Check, X, AlertCircle } from 'lucide-react'; import type { Field, FieldType } from '../../types/api.types'; import styles from './VariablesPanel.module.scss'; interface VariablesPanelProps { variables: Field[]; onUpdate: (variables: Field[]) => void; /** Только просмотр — без добавления/редактирования/удаления */ readOnly?: boolean; } export function VariablesPanel({ variables, onUpdate, readOnly = false }: VariablesPanelProps) { const [editingIndex, setEditingIndex] = useState<number | null>(null); const [editForm, setEditForm] = useState<Field>({ name: '', description: '', type: 'Number', value: 0, }); const [validationError, setValidationError] = useState<string>(''); // Валидация имени переменной (только английские буквы, цифры и _) const validateVariableName = (name: string): string | null => { if (!name) { return 'Имя переменной не может быть пустым'; } if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) { return 'Имя переменной должно содержать только английские буквы, цифры и подчеркивание'; } // Проверяем уникальность const isDuplicate = variables.some((v, idx) => v.name === name && idx !== editingIndex ); if (isDuplicate) { return 'Переменная с таким именем уже существует'; } return null; }; const handleAddVariable = () => { setEditingIndex(variables.length); setEditForm({ name: '', description: '', type: 'Number', value: 0, }); setValidationError(''); }; const handleEditVariable = (index: number) => { setEditingIndex(index); setEditForm({ ...variables[index] }); setValidationError(''); }; const handleSave = () => { const error = validateVariableName(editForm.name); if (error) { setValidationError(error); return; } // Валидация значения по типу let validatedValue = editForm.value; switch (editForm.type) { case 'Number': validatedValue = Number(editForm.value) || 0; break; case 'Double': validatedValue = parseFloat(String(editForm.value)) || 0.0; break; case 'Boolean': validatedValue = Boolean(editForm.value); break; case 'String': validatedValue = String(editForm.value); break; } const updatedVariables = [...variables]; if (editingIndex !== null && editingIndex < variables.length) { // Редактирование существующей updatedVariables[editingIndex] = { ...editForm, value: validatedValue }; } else { // Добавление новой updatedVariables.push({ ...editForm, value: validatedValue }); } onUpdate(updatedVariables); setEditingIndex(null); setValidationError(''); }; const handleCancel = () => { setEditingIndex(null); setValidationError(''); }; const handleDelete = (index: number) => { const updatedVariables = variables.filter((_, i) => i !== index); onUpdate(updatedVariables); }; return ( <div className={styles.panel}> <div className={styles.header}> <h3>Переменные</h3> {!readOnly && ( <button className={styles.addBtn} onClick={handleAddVariable} title="Добавить переменную" > <Plus size={16} /> </button> )} </div> <div className={styles.variablesList}> {variables.map((variable, index) => ( editingIndex === index ? ( <div key={index} className={styles.editForm}> <input type="text" value={editForm.name} onChange={(e) => setEditForm({ ...editForm, name: e.target.value })} placeholder="variable_name" className={styles.input} autoFocus /> <input type="text" value={editForm.description} onChange={(e) => setEditForm({ ...editForm, description: e.target.value })} placeholder="Описание" className={styles.input} /> <select value={editForm.type} onChange={(e) => setEditForm({ ...editForm, type: e.target.value as FieldType })} className={styles.select} > <option value="Number">Number</option> <option value="Double">Double</option> <option value="String">String</option> <option value="Boolean">Boolean</option> </select> <input type="text" value={String(editForm.value)} onChange={(e) => setEditForm({ ...editForm, value: e.target.value })} placeholder="Значение" className={styles.input} /> {validationError && ( <div className={styles.error}> <AlertCircle size={14} /> <span>{validationError}</span> </div> )} <div className={styles.actions}> <button className={styles.saveBtn} onClick={handleSave}> <Check size={14} /> </button> <button className={styles.cancelBtn} onClick={handleCancel}> <X size={14} /> </button> </div> </div> ) : ( <div key={index} className={styles.variableItem}> <div className={styles.variableInfo}> <div className={styles.variableName}>{variable.name}</div> <div className={styles.variableDetails}> <span className={styles.variableType}>{variable.type}</span> {variable.description && ( <span className={styles.variableDescription}>{variable.description}</span> )} </div> <div className={styles.variableValue}> = {String(variable.value)} </div> </div> {!readOnly && ( <div className={styles.variableActions}> <button className={styles.iconBtn} onClick={() => handleEditVariable(index)} title="Редактировать" > <Edit2 size={14} /> </button> <button className={styles.iconBtn} onClick={() => handleDelete(index)} title="Удалить" > <Trash2 size={14} /> </button> </div> )} </div> ) ))} {editingIndex === variables.length && ( <div className={styles.editForm}> <input type="text" value={editForm.name} onChange={(e) => setEditForm({ ...editForm, name: e.target.value })} placeholder="variable_name" className={styles.input} autoFocus /> <input type="text" value={editForm.description} onChange={(e) => setEditForm({ ...editForm, description: e.target.value })} placeholder="Описание" className={styles.input} /> <select value={editForm.type} onChange={(e) => setEditForm({ ...editForm, type: e.target.value as FieldType })} className={styles.select} > <option value="Number">Number</option> <option value="Double">Double</option> <option value="String">String</option> <option value="Boolean">Boolean</option> </select> <input type="text" value={String(editForm.value)} onChange={(e) => setEditForm({ ...editForm, value: e.target.value })} placeholder="Значение" className={styles.input} /> {validationError && ( <div className={styles.error}> <AlertCircle size={14} /> <span>{validationError}</span> </div> )} <div className={styles.actions}> <button className={styles.saveBtn} onClick={handleSave}> <Check size={14} /> </button> <button className={styles.cancelBtn} onClick={handleCancel}> <X size={14} /> </button> </div> </div> )} {variables.length === 0 && editingIndex === null && ( <div className={styles.emptyState}> <p>Нет переменных</p> <p className={styles.hint}>Нажмите + чтобы добавить</p> </div> )} </div> <div className={styles.tips}> <h4>Правила именования:</h4> <ul> <li>Только английские буквы (a-z, A-Z)</li> <li>Цифры и подчеркивание (_)</li> <li>Не может начинаться с цифры</li> </ul> </div> </div> ); }