From 5b1938e5b384c5efb3f98bb6defeca5bf2cc5ad2 Mon Sep 17 00:00:00 2001 From: choizhang Date: Sun, 13 Apr 2025 23:32:35 +0800 Subject: [PATCH 1/2] feat(webui): Add attribute editing dialog box and optimize editable attribute row component --- lightrag/api/routers/graph_routes.py | 1 - .../components/graph/EditablePropertyRow.tsx | 121 +++++++----------- .../components/graph/PropertyEditDialog.tsx | 105 +++++++++++++++ lightrag_webui/src/locales/ar.json | 7 +- lightrag_webui/src/locales/en.json | 7 +- lightrag_webui/src/locales/fr.json | 5 +- lightrag_webui/src/locales/zh.json | 7 +- 7 files changed, 173 insertions(+), 80 deletions(-) create mode 100644 lightrag_webui/src/components/graph/PropertyEditDialog.tsx diff --git a/lightrag/api/routers/graph_routes.py b/lightrag/api/routers/graph_routes.py index e77e959a..107a7952 100644 --- a/lightrag/api/routers/graph_routes.py +++ b/lightrag/api/routers/graph_routes.py @@ -95,7 +95,6 @@ def create_graph_routes(rag, api_key: Optional[str] = None): Dict: Updated entity information """ try: - print(request.entity_name, request.updated_data, request.allow_rename) result = await rag.aedit_entity( entity_name=request.entity_name, updated_data=request.updated_data, diff --git a/lightrag_webui/src/components/graph/EditablePropertyRow.tsx b/lightrag_webui/src/components/graph/EditablePropertyRow.tsx index bfd88802..bca482b8 100644 --- a/lightrag_webui/src/components/graph/EditablePropertyRow.tsx +++ b/lightrag_webui/src/components/graph/EditablePropertyRow.tsx @@ -1,12 +1,12 @@ -import { useState, useEffect, useRef } from 'react' +import { useState, useEffect } from 'react' +import { useRef } from 'react' import { useTranslation } from 'react-i18next' import Text from '@/components/ui/Text' -import Input from '@/components/ui/Input' import { toast } from 'sonner' import { updateEntity, updateRelation, checkEntityNameExists } from '@/api/lightrag' import { useGraphStore } from '@/stores/graph' import { PencilIcon } from 'lucide-react' -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/Tooltip' +import PropertyEditDialog from './PropertyEditDialog' /** * Interface for the EditablePropertyRow component props @@ -44,7 +44,6 @@ const EditablePropertyRow = ({ name, value: initialValue, onClick, - tooltip, entityId, entityType, sourceId, @@ -55,11 +54,11 @@ const EditablePropertyRow = ({ // Component state const { t } = useTranslation() const [isEditing, setIsEditing] = useState(false) - const [editValue, setEditValue] = useState('') const [isSubmitting, setIsSubmitting] = useState(false) const [currentValue, setCurrentValue] = useState(initialValue) const inputRef = useRef(null) + /** * Update currentValue when initialValue changes from parent */ @@ -72,7 +71,6 @@ const EditablePropertyRow = ({ */ useEffect(() => { if (isEditing) { - setEditValue(String(currentValue)) // Focus the input element when entering edit mode with a small delay // to ensure the input is rendered before focusing setTimeout(() => { @@ -82,7 +80,7 @@ const EditablePropertyRow = ({ } }, 50) } - }, [isEditing, currentValue]) + }, [isEditing]) /** * Get translated property name from i18n @@ -95,25 +93,19 @@ const EditablePropertyRow = ({ } /** - * Handle double-click event to enter edit mode + * Handle edit icon click to open dialog */ - const handleDoubleClick = () => { + const handleEditClick = () => { if (isEditable && !isEditing) { setIsEditing(true) } } /** - * Handle keyboard events in the input field - * - Enter: Save changes - * - Escape: Cancel editing + * Handle dialog close without saving */ - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Enter') { - handleSave() - } else if (e.key === 'Escape') { - setIsEditing(false) - } + const handleCancel = () => { + setIsEditing(false) } /** @@ -300,12 +292,12 @@ const EditablePropertyRow = ({ * Save changes to the property value * Updates both the API and the graph visualization */ - const handleSave = async () => { + const handleSave = async (value: string) => { // Prevent duplicate submissions if (isSubmitting) return // Skip if value hasn't changed - if (editValue === String(currentValue)) { + if (value === String(currentValue)) { setIsEditing(false) return } @@ -315,44 +307,44 @@ const EditablePropertyRow = ({ try { // Handle node property updates if (entityType === 'node' && entityId) { - let updatedData = { [name]: editValue } + let updatedData = { [name]: value } // Special handling for entity_id (name) changes if (name === 'entity_id') { // Check if the new name already exists - const exists = await checkEntityNameExists(editValue) + const exists = await checkEntityNameExists(value) if (exists) { toast.error(t('graphPanel.propertiesView.errors.duplicateName')) setIsSubmitting(false) return } // For entity_id, we update entity_name in the API - updatedData = { 'entity_name': editValue } + updatedData = { 'entity_name': value } } // Update entity in API await updateEntity(entityId, updatedData, true) // Update graph visualization - await updateGraphNode(entityId, name, editValue) + await updateGraphNode(entityId, name, value) toast.success(t('graphPanel.propertiesView.success.entityUpdated')) } // Handle edge property updates else if (entityType === 'edge' && sourceId && targetId) { - const updatedData = { [name]: editValue } + const updatedData = { [name]: value } // Update relation in API await updateRelation(sourceId, targetId, updatedData) // Update graph visualization - await updateGraphEdge(sourceId, targetId, name, editValue) + await updateGraphEdge(sourceId, targetId, name, value) toast.success(t('graphPanel.propertiesView.success.relationUpdated')) } // Update local state setIsEditing(false) - setCurrentValue(editValue) + setCurrentValue(value) // Notify parent component if callback provided if (onValueChange) { - onValueChange(editValue) + onValueChange(value) } } catch (error) { console.error('Error updating property:', error) @@ -364,58 +356,43 @@ const EditablePropertyRow = ({ /** * Render the property row with edit functionality - * Shows property name, edit icon, and either the editable input or the current value + * Shows property name, edit icon, and the current value */ return ( -
+
{/* Property name with translation */} {getPropertyNameTranslation(name)} - {/* Edit icon with tooltip */} - - - -
- setIsEditing(true)} - /> -
-
- - {t('graphPanel.propertiesView.doubleClickToEdit')} - -
-
: - - {/* Conditional rendering based on edit state */} - {isEditing ? ( - // Input field for editing - setEditValue(e.target.value)} - onKeyDown={handleKeyDown} - onBlur={handleSave} - className="h-6 text-xs" - disabled={isSubmitting} + {/* Edit icon without tooltip */} +
+ - ) : ( - // Text display when not editing -
- -
- )} +
: + + {/* Text display */} +
+ +
+ + {/* Edit dialog */} +
) } diff --git a/lightrag_webui/src/components/graph/PropertyEditDialog.tsx b/lightrag_webui/src/components/graph/PropertyEditDialog.tsx new file mode 100644 index 00000000..612ffaf9 --- /dev/null +++ b/lightrag_webui/src/components/graph/PropertyEditDialog.tsx @@ -0,0 +1,105 @@ +import { useState, useEffect } from 'react' +import { useTranslation } from 'react-i18next' +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogFooter +} from '@/components/ui/Dialog' +import Button from '@/components/ui/Button' +import Input from '@/components/ui/Input' + +interface PropertyEditDialogProps { + isOpen: boolean + onClose: () => void + onSave: (value: string) => void + propertyName: string + initialValue: string + isSubmitting?: boolean +} + +/** + * Dialog component for editing property values + * Provides a modal with a title, multi-line text input, and save/cancel buttons + */ +const PropertyEditDialog = ({ + isOpen, + onClose, + onSave, + propertyName, + initialValue, + isSubmitting = false +}: PropertyEditDialogProps) => { + const { t } = useTranslation() + const [value, setValue] = useState('') + + // Initialize value when dialog opens + useEffect(() => { + if (isOpen) { + setValue(initialValue) + } + }, [isOpen, initialValue]) + + // Get translated property name + const getPropertyNameTranslation = (name: string) => { + const translationKey = `graphPanel.propertiesView.node.propertyNames.${name}` + const translation = t(translationKey) + return translation === translationKey ? name : translation + } + + const handleSave = () => { + if (value.trim() !== '') { + onSave(value) + onClose() + } +} + + return ( + !open && onClose()}> + + + + {t('graphPanel.propertiesView.editProperty', { + property: getPropertyNameTranslation(propertyName) + })} + +

+ {t('graphPanel.propertiesView.editPropertyDescription')} +

+
+ + {/* Multi-line text input using textarea */} +
+