File size: 4,703 Bytes
d7362f8
fa48cea
 
 
2ea23b2
03cc6ab
d7362f8
fa48cea
217a79c
 
 
fa48cea
217a79c
 
 
f4c3104
217a79c
f4c3104
 
217a79c
 
 
 
 
11a9ac3
217a79c
 
 
03cc6ab
217a79c
fa48cea
 
 
39a1836
fa48cea
f4c3104
 
fa48cea
f4c3104
fa48cea
 
 
 
11a9ac3
 
fa48cea
 
 
 
39a1836
fa48cea
39a1836
 
 
 
d7362f8
fa48cea
 
 
 
 
d7362f8
 
fa48cea
 
d7362f8
03cc6ab
4d5e0aa
 
 
fa48cea
4d5e0aa
fa48cea
4d5e0aa
f4c3104
d7362f8
39a1836
4d5e0aa
d7362f8
39a1836
 
 
4d5e0aa
d7362f8
fa48cea
217a79c
39a1836
2ea23b2
 
 
 
 
 
fa48cea
f4c3104
d7362f8
fa48cea
2ea23b2
 
 
 
 
 
fa48cea
 
 
39a1836
d7362f8
03cc6ab
39a1836
4d5e0aa
39a1836
fa48cea
 
 
 
 
 
86d9157
03cc6ab
 
6d16de8
 
 
11a9ac3
 
d7362f8
 
 
 
 
 
 
 
fa48cea
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import { useState, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import { updateEntity, updateRelation, checkEntityNameExists } from '@/api/lightrag'
import { useGraphStore } from '@/stores/graph'
import { PropertyName, EditIcon, PropertyValue } from './PropertyRowComponents'
import PropertyEditDialog from './PropertyEditDialog'

/**
 * Interface for the EditablePropertyRow component props
 */
interface EditablePropertyRowProps {
  name: string                  // Property name to display and edit
  value: any                    // Initial value of the property
  onClick?: () => void          // Optional click handler for the property value
  nodeId?: string               // ID of the node (for node type)
  entityId?: string             // ID of the entity (for node type)
  edgeId?: string               // ID of the edge (for edge type)
  dynamicId?: string
  entityType?: 'node' | 'edge'  // Type of graph entity
  sourceId?: string            // Source node ID (for edge type)
  targetId?: string            // Target node ID (for edge type)
  onValueChange?: (newValue: any) => void  // Optional callback when value changes
  isEditable?: boolean         // Whether this property can be edited
  tooltip?: string             // Optional tooltip to display on hover
}

/**
 * EditablePropertyRow component that supports editing property values
 * This component is used in the graph properties panel to display and edit entity properties
 */
const EditablePropertyRow = ({
  name,
  value: initialValue,
  onClick,
  nodeId,
  edgeId,
  entityId,
  dynamicId,
  entityType,
  sourceId,
  targetId,
  onValueChange,
  isEditable = false,
  tooltip
}: EditablePropertyRowProps) => {
  const { t } = useTranslation()
  const [isEditing, setIsEditing] = useState(false)
  const [isSubmitting, setIsSubmitting] = useState(false)
  const [currentValue, setCurrentValue] = useState(initialValue)

  useEffect(() => {
    setCurrentValue(initialValue)
  }, [initialValue])

  const handleEditClick = () => {
    if (isEditable && !isEditing) {
      setIsEditing(true)
    }
  }

  const handleCancel = () => {
    setIsEditing(false)
  }

  const handleSave = async (value: string) => {
    if (isSubmitting || value === String(currentValue)) {
      setIsEditing(false)
      return
    }

    setIsSubmitting(true)

    try {
      if (entityType === 'node' && entityId && nodeId) {
        let updatedData = { [name]: value }

        if (name === 'entity_id') {
          const exists = await checkEntityNameExists(value)
          if (exists) {
            toast.error(t('graphPanel.propertiesView.errors.duplicateName'))
            return
          }
          updatedData = { 'entity_name': value }
        }

        await updateEntity(entityId, updatedData, true)
        try {
          await useGraphStore.getState().updateNodeAndSelect(nodeId, entityId, name, value)
        } catch (error) {
          console.error('Error updating node in graph:', error)
          throw new Error('Failed to update node in graph')
        }
        toast.success(t('graphPanel.propertiesView.success.entityUpdated'))
      } else if (entityType === 'edge' && sourceId && targetId && edgeId && dynamicId) {
        const updatedData = { [name]: value }
        await updateRelation(sourceId, targetId, updatedData)
        try {
          await useGraphStore.getState().updateEdgeAndSelect(edgeId, dynamicId, sourceId, targetId, name, value)
        } catch (error) {
          console.error(`Error updating edge ${sourceId}->${targetId} in graph:`, error)
          throw new Error('Failed to update edge in graph')
        }
        toast.success(t('graphPanel.propertiesView.success.relationUpdated'))
      }

      setIsEditing(false)
      setCurrentValue(value)
      onValueChange?.(value)
    } catch (error) {
      console.error('Error updating property:', error)
      toast.error(t('graphPanel.propertiesView.errors.updateFailed'))
    } finally {
      setIsSubmitting(false)
    }
  }

  return (
    <div className="flex items-center gap-1 overflow-hidden">
      <PropertyName name={name} />
      <EditIcon onClick={handleEditClick} />:
      <PropertyValue
        value={currentValue}
        onClick={onClick}
        tooltip={tooltip || (typeof currentValue === 'string' ? currentValue : JSON.stringify(currentValue, null, 2))}
      />
      <PropertyEditDialog
        isOpen={isEditing}
        onClose={handleCancel}
        onSave={handleSave}
        propertyName={name}
        initialValue={String(currentValue)}
        isSubmitting={isSubmitting}
      />
    </div>
  )
}

export default EditablePropertyRow