This commit is contained in:
@@ -17,6 +17,12 @@ class EntityUpdateRequest(BaseModel):
|
|||||||
allow_rename: bool = False
|
allow_rename: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
class RelationUpdateRequest(BaseModel):
|
||||||
|
source_id: str
|
||||||
|
target_id: str
|
||||||
|
updated_data: Dict[str, Any]
|
||||||
|
|
||||||
|
|
||||||
def create_graph_routes(rag, api_key: Optional[str] = None):
|
def create_graph_routes(rag, api_key: Optional[str] = None):
|
||||||
combined_auth = get_combined_auth_dependency(api_key)
|
combined_auth = get_combined_auth_dependency(api_key)
|
||||||
|
|
||||||
@@ -107,4 +113,32 @@ def create_graph_routes(rag, api_key: Optional[str] = None):
|
|||||||
status_code=500, detail=f"Error updating entity: {str(e)}"
|
status_code=500, detail=f"Error updating entity: {str(e)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@router.post("/graph/relation/edit", dependencies=[Depends(combined_auth)])
|
||||||
|
async def update_relation(request: RelationUpdateRequest):
|
||||||
|
"""Update a relation's properties in the knowledge graph
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (RelationUpdateRequest): Request containing source ID, target ID and updated data
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: Updated relation information
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
result = await rag.aedit_relation(
|
||||||
|
source_entity=request.source_id,
|
||||||
|
target_entity=request.target_id,
|
||||||
|
updated_data=request.updated_data,
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"status": "success",
|
||||||
|
"message": "Relation updated successfully",
|
||||||
|
"data": result,
|
||||||
|
}
|
||||||
|
except ValueError as ve:
|
||||||
|
raise HTTPException(status_code=400, detail=str(ve))
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=500, detail=f"Error updating relation: {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
return router
|
return router
|
||||||
|
@@ -540,8 +540,8 @@ export const updateRelation = async (
|
|||||||
updatedData: Record<string, any>
|
updatedData: Record<string, any>
|
||||||
): Promise<DocActionResponse> => {
|
): Promise<DocActionResponse> => {
|
||||||
const response = await axiosInstance.post('/graph/relation/edit', {
|
const response = await axiosInstance.post('/graph/relation/edit', {
|
||||||
source_entity: sourceEntity,
|
source_id: sourceEntity,
|
||||||
target_entity: targetEntity,
|
target_id: targetEntity,
|
||||||
updated_data: updatedData
|
updated_data: updatedData
|
||||||
})
|
})
|
||||||
return response.data
|
return response.data
|
||||||
|
@@ -6,6 +6,7 @@ import { toast } from 'sonner'
|
|||||||
import { updateEntity, updateRelation, checkEntityNameExists } from '@/api/lightrag'
|
import { updateEntity, updateRelation, checkEntityNameExists } from '@/api/lightrag'
|
||||||
import { useGraphStore } from '@/stores/graph'
|
import { useGraphStore } from '@/stores/graph'
|
||||||
import { PencilIcon } from 'lucide-react'
|
import { PencilIcon } from 'lucide-react'
|
||||||
|
import { tr } from '@faker-js/faker'
|
||||||
|
|
||||||
interface EditablePropertyRowProps {
|
interface EditablePropertyRowProps {
|
||||||
name: string
|
name: string
|
||||||
@@ -26,7 +27,7 @@ interface EditablePropertyRowProps {
|
|||||||
*/
|
*/
|
||||||
const EditablePropertyRow = ({
|
const EditablePropertyRow = ({
|
||||||
name,
|
name,
|
||||||
value,
|
value: initialValue,
|
||||||
onClick,
|
onClick,
|
||||||
tooltip,
|
tooltip,
|
||||||
entityId,
|
entityId,
|
||||||
@@ -40,12 +41,18 @@ const EditablePropertyRow = ({
|
|||||||
const [isEditing, setIsEditing] = useState(false)
|
const [isEditing, setIsEditing] = useState(false)
|
||||||
const [editValue, setEditValue] = useState('')
|
const [editValue, setEditValue] = useState('')
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||||
|
const [currentValue, setCurrentValue] = useState(initialValue)
|
||||||
const inputRef = useRef<HTMLInputElement>(null)
|
const inputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
|
// Update currentValue when initialValue changes
|
||||||
|
useEffect(() => {
|
||||||
|
setCurrentValue(initialValue)
|
||||||
|
}, [initialValue])
|
||||||
|
|
||||||
// Initialize edit value when entering edit mode
|
// Initialize edit value when entering edit mode
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
setEditValue(String(value))
|
setEditValue(String(currentValue))
|
||||||
// Focus the input element when entering edit mode
|
// Focus the input element when entering edit mode
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (inputRef.current) {
|
if (inputRef.current) {
|
||||||
@@ -54,7 +61,7 @@ const EditablePropertyRow = ({
|
|||||||
}
|
}
|
||||||
}, 50)
|
}, 50)
|
||||||
}
|
}
|
||||||
}, [isEditing, value])
|
}, [isEditing, currentValue])
|
||||||
|
|
||||||
const getPropertyNameTranslation = (propName: string) => {
|
const getPropertyNameTranslation = (propName: string) => {
|
||||||
const translationKey = `graphPanel.propertiesView.node.propertyNames.${propName}`
|
const translationKey = `graphPanel.propertiesView.node.propertyNames.${propName}`
|
||||||
@@ -91,10 +98,34 @@ const EditablePropertyRow = ({
|
|||||||
if (propertyName === 'entity_id') {
|
if (propertyName === 'entity_id') {
|
||||||
sigmaGraph.addNode(newValue, { ...nodeAttributes, label: newValue })
|
sigmaGraph.addNode(newValue, { ...nodeAttributes, label: newValue })
|
||||||
|
|
||||||
|
interface EdgeToUpdate {
|
||||||
|
originalDynamicId: string;
|
||||||
|
newEdgeId: string;
|
||||||
|
edgeIndex: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const edgesToUpdate: EdgeToUpdate[] = [];
|
||||||
|
|
||||||
sigmaGraph.forEachEdge(String(nodeId), (edge, attributes, source, target) => {
|
sigmaGraph.forEachEdge(String(nodeId), (edge, attributes, source, target) => {
|
||||||
const otherNode = source === String(nodeId) ? target : source
|
const otherNode = source === String(nodeId) ? target : source
|
||||||
const isOutgoing = source === String(nodeId)
|
const isOutgoing = source === String(nodeId)
|
||||||
sigmaGraph.addEdge(isOutgoing ? newValue : otherNode, isOutgoing ? otherNode : newValue, attributes)
|
|
||||||
|
// 获取原始边的dynamicId,以便后续更新edgeDynamicIdMap
|
||||||
|
const originalEdgeDynamicId = edge
|
||||||
|
const edgeIndexInRawGraph = rawGraph.edgeDynamicIdMap[originalEdgeDynamicId]
|
||||||
|
|
||||||
|
// 创建新边并获取新边的ID
|
||||||
|
const newEdgeId = sigmaGraph.addEdge(isOutgoing ? newValue : otherNode, isOutgoing ? otherNode : newValue, attributes)
|
||||||
|
|
||||||
|
// 存储需要更新的边信息
|
||||||
|
if (edgeIndexInRawGraph !== undefined) {
|
||||||
|
edgesToUpdate.push({
|
||||||
|
originalDynamicId: originalEdgeDynamicId,
|
||||||
|
newEdgeId: newEdgeId,
|
||||||
|
edgeIndex: edgeIndexInRawGraph
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
sigmaGraph.dropEdge(edge)
|
sigmaGraph.dropEdge(edge)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -107,42 +138,112 @@ const EditablePropertyRow = ({
|
|||||||
delete rawGraph.nodeIdMap[String(nodeId)]
|
delete rawGraph.nodeIdMap[String(nodeId)]
|
||||||
rawGraph.nodeIdMap[newValue] = nodeIndex
|
rawGraph.nodeIdMap[newValue] = nodeIndex
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
const updatedAttributes = { ...nodeAttributes }
|
// 更新边的引用关系
|
||||||
if (propertyName === 'description') {
|
edgesToUpdate.forEach(({ originalDynamicId, newEdgeId, edgeIndex }) => {
|
||||||
updatedAttributes.description = newValue
|
// 更新边的source和target
|
||||||
}
|
if (rawGraph.edges[edgeIndex]) {
|
||||||
Object.entries(updatedAttributes).forEach(([key, value]) => {
|
if (rawGraph.edges[edgeIndex].source === String(nodeId)) {
|
||||||
sigmaGraph.setNodeAttribute(String(nodeId), key, value)
|
rawGraph.edges[edgeIndex].source = newValue
|
||||||
|
}
|
||||||
|
if (rawGraph.edges[edgeIndex].target === String(nodeId)) {
|
||||||
|
rawGraph.edges[edgeIndex].target = newValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新dynamicId映射
|
||||||
|
rawGraph.edges[edgeIndex].dynamicId = newEdgeId
|
||||||
|
delete rawGraph.edgeDynamicIdMap[originalDynamicId]
|
||||||
|
rawGraph.edgeDynamicIdMap[newEdgeId] = edgeIndex
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useGraphStore.getState().setSelectedNode(editValue)
|
||||||
|
} else {
|
||||||
|
// const updatedAttributes = { ...nodeAttributes }
|
||||||
|
// if (propertyName === 'description') {
|
||||||
|
// updatedAttributes.description = newValue
|
||||||
|
// }
|
||||||
|
// Object.entries(updatedAttributes).forEach(([key, value]) => {
|
||||||
|
// sigmaGraph.setNodeAttribute(String(nodeId), key, value)
|
||||||
|
// })
|
||||||
|
|
||||||
const nodeIndex = rawGraph.nodeIdMap[String(nodeId)]
|
const nodeIndex = rawGraph.nodeIdMap[String(nodeId)]
|
||||||
if (nodeIndex !== undefined) {
|
if (nodeIndex !== undefined) {
|
||||||
rawGraph.nodes[nodeIndex].properties[propertyName] = newValue
|
rawGraph.nodes[nodeIndex].properties[propertyName] = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedNode = useGraphStore.getState().selectedNode
|
|
||||||
if (selectedNode === String(nodeId)) {
|
|
||||||
useGraphStore.getState().setSelectedNode(newValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
const focusedNode = useGraphStore.getState().focusedNode
|
|
||||||
if (focusedNode === String(nodeId)) {
|
|
||||||
useGraphStore.getState().setFocusedNode(newValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
sigmaInstance.refresh()
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating node in graph:', error)
|
console.error('Error updating node in graph:', error)
|
||||||
throw new Error('Failed to update node in graph')
|
throw new Error('Failed to update node in graph')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateGraphEdge = async (sourceId: string, targetId: string, propertyName: string, newValue: string) => {
|
||||||
|
const sigmaInstance = useGraphStore.getState().sigmaInstance
|
||||||
|
const sigmaGraph = useGraphStore.getState().sigmaGraph
|
||||||
|
const rawGraph = useGraphStore.getState().rawGraph
|
||||||
|
|
||||||
|
if (!sigmaInstance || !sigmaGraph || !rawGraph) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const allEdges = sigmaGraph.edges()
|
||||||
|
let keyToUse = null
|
||||||
|
|
||||||
|
for (const edge of allEdges) {
|
||||||
|
const edgeSource = sigmaGraph.source(edge)
|
||||||
|
const edgeTarget = sigmaGraph.target(edge)
|
||||||
|
|
||||||
|
if ((edgeSource === sourceId && edgeTarget === targetId) ||
|
||||||
|
(edgeSource === targetId && edgeTarget === sourceId)) {
|
||||||
|
keyToUse = edge
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyToUse !== null) {
|
||||||
|
if(propertyName === 'keywords') {
|
||||||
|
sigmaGraph.setEdgeAttribute(keyToUse, 'label', newValue);
|
||||||
|
} else {
|
||||||
|
sigmaGraph.setEdgeAttribute(keyToUse, propertyName, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyToUse && rawGraph.edgeDynamicIdMap[keyToUse] !== undefined) {
|
||||||
|
const edgeIndex = rawGraph.edgeDynamicIdMap[keyToUse];
|
||||||
|
if (rawGraph.edges[edgeIndex]) {
|
||||||
|
rawGraph.edges[edgeIndex].properties[propertyName] = newValue;
|
||||||
|
} else {
|
||||||
|
console.warn(`Edge index ${edgeIndex} found but edge data missing in rawGraph for dynamicId ${entityId}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn(`Could not find edge with dynamicId ${entityId} in rawGraph.edgeDynamicIdMap to update properties.`);
|
||||||
|
if (keyToUse !== null) {
|
||||||
|
const edgeIndexByKey = rawGraph.edgeIdMap[keyToUse];
|
||||||
|
if (edgeIndexByKey !== undefined && rawGraph.edges[edgeIndexByKey]) {
|
||||||
|
rawGraph.edges[edgeIndexByKey].properties[propertyName] = newValue;
|
||||||
|
console.log(`Updated rawGraph edge using constructed key ${keyToUse}`);
|
||||||
|
} else {
|
||||||
|
console.warn(`Could not find edge in rawGraph using key ${keyToUse} either.`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn('Cannot update edge properties: edge key is null');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn(`Edge not found in sigmaGraph with key ${keyToUse}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Log the specific edge key that caused the error if possible
|
||||||
|
console.error(`Error updating edge ${sourceId}->${targetId} in graph:`, error);
|
||||||
|
throw new Error('Failed to update edge in graph')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
if (isSubmitting) return
|
if (isSubmitting) return
|
||||||
|
|
||||||
if (editValue === String(value)) {
|
if (editValue === String(currentValue)) {
|
||||||
setIsEditing(false)
|
setIsEditing(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -150,112 +251,71 @@ const EditablePropertyRow = ({
|
|||||||
setIsSubmitting(true)
|
setIsSubmitting(true)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const updatedData: Record<string, any> = {}
|
|
||||||
|
|
||||||
if (entityType === 'node' && entityId) {
|
if (entityType === 'node' && entityId) {
|
||||||
|
let updatedData = { [name]: editValue }
|
||||||
|
|
||||||
if (name === 'entity_id') {
|
if (name === 'entity_id') {
|
||||||
if (editValue !== String(value)) {
|
const exists = await checkEntityNameExists(editValue)
|
||||||
const exists = await checkEntityNameExists(editValue)
|
if (exists) {
|
||||||
if (exists) {
|
toast.error(t('graphPanel.propertiesView.errors.duplicateName'))
|
||||||
toast.error(t('graphPanel.propertiesView.errors.duplicateName'))
|
setIsSubmitting(false)
|
||||||
return
|
return
|
||||||
}
|
|
||||||
}
|
|
||||||
updatedData['entity_name'] = editValue
|
|
||||||
await updateEntity(String(value), updatedData, true)
|
|
||||||
await updateGraphNode(String(value), 'entity_id', editValue)
|
|
||||||
} else {
|
|
||||||
updatedData[name] = editValue
|
|
||||||
await updateEntity(entityId, updatedData)
|
|
||||||
if (name === 'description') {
|
|
||||||
await updateGraphNode(entityId, name, editValue)
|
|
||||||
}
|
}
|
||||||
|
updatedData = { 'entity_name': editValue }
|
||||||
}
|
}
|
||||||
|
await updateEntity(entityId, updatedData, true)
|
||||||
|
await updateGraphNode(entityId, name, editValue)
|
||||||
toast.success(t('graphPanel.propertiesView.success.entityUpdated'))
|
toast.success(t('graphPanel.propertiesView.success.entityUpdated'))
|
||||||
} else if (entityType === 'edge' && sourceId && targetId) {
|
} else if (entityType === 'edge' && sourceId && targetId) {
|
||||||
updatedData[name] = editValue
|
const updatedData = { [name]: editValue }
|
||||||
await updateRelation(sourceId, targetId, updatedData)
|
await updateRelation(sourceId, targetId, updatedData)
|
||||||
|
await updateGraphEdge(sourceId, targetId, name, editValue)
|
||||||
toast.success(t('graphPanel.propertiesView.success.relationUpdated'))
|
toast.success(t('graphPanel.propertiesView.success.relationUpdated'))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onValueChange) {
|
setIsEditing(false)
|
||||||
onValueChange(editValue)
|
setCurrentValue(editValue)
|
||||||
}
|
} catch (error) {
|
||||||
|
|
||||||
useGraphStore.getState().setGraphDataFetchAttempted(false)
|
|
||||||
useGraphStore.getState().setLabelsFetchAttempted(false)
|
|
||||||
|
|
||||||
const currentNodeId = name === 'entity_id' ? editValue : (entityId || '')
|
|
||||||
useGraphStore.getState().setSelectedNode(null)
|
|
||||||
useGraphStore.getState().setSelectedNode(currentNodeId)
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error('Error updating property:', error)
|
console.error('Error updating property:', error)
|
||||||
let detailMessage = t('graphPanel.propertiesView.errors.updateFailed')
|
toast.error(t('graphPanel.propertiesView.errors.updateFailed'))
|
||||||
|
|
||||||
if (error.response?.data?.detail) {
|
|
||||||
detailMessage = error.response.data.detail
|
|
||||||
} else if (error.response?.data?.message) {
|
|
||||||
detailMessage = error.response.data.message
|
|
||||||
} else if (error.message) {
|
|
||||||
detailMessage = error.message
|
|
||||||
}
|
|
||||||
|
|
||||||
console.error('Update failed:', {
|
|
||||||
entityType,
|
|
||||||
entityId,
|
|
||||||
propertyName: name,
|
|
||||||
newValue: editValue,
|
|
||||||
error: error.response?.data || error.message
|
|
||||||
})
|
|
||||||
|
|
||||||
toast.error(detailMessage, {
|
|
||||||
description: t('graphPanel.propertiesView.errors.tryAgainLater')
|
|
||||||
})
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsSubmitting(false)
|
setIsSubmitting(false)
|
||||||
setIsEditing(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if this property should be editable
|
// Always render the property name label and edit icon, regardless of edit state
|
||||||
// Currently only 'description' and 'entity_id' fields are editable
|
|
||||||
const isEditableField = isEditable && (name === 'description' || name === 'entity_id')
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-1" onDoubleClick={handleDoubleClick}>
|
||||||
<div className="flex items-center gap-1 text-primary/60 tracking-wide whitespace-nowrap">
|
<span className="text-primary/60 tracking-wide whitespace-nowrap">{getPropertyNameTranslation(name)}</span>
|
||||||
{getPropertyNameTranslation(name)}
|
<div className="group relative">
|
||||||
{isEditableField && (
|
<PencilIcon
|
||||||
<div className="group relative">
|
className="h-3 w-3 text-gray-500 hover:text-gray-700 cursor-pointer"
|
||||||
<PencilIcon className="w-3 h-3 opacity-50 hover:opacity-100" />
|
onClick={() => setIsEditing(true)}
|
||||||
<div className="absolute left-5 transform -translate-y-full -top-2 bg-primary/90 text-white text-xs px-3 py-1.5 rounded shadow-lg border border-primary/20 opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap z-[100]">
|
/>
|
||||||
{t('graphPanel.propertiesView.doubleClickToEdit')}
|
<div className="absolute left-5 transform -translate-y-full -top-2 bg-primary/90 text-white text-xs px-3 py-1.5 rounded shadow-lg border border-primary/20 opacity-0 group-hover:opacity-100 transition-opacity duration-200 whitespace-nowrap z-[100]">
|
||||||
</div>
|
{t('graphPanel.propertiesView.doubleClickToEdit')}
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</div>:
|
</div>:
|
||||||
{isEditing ? (
|
{isEditing ? (
|
||||||
<div className="flex-1">
|
// Render input field when editing
|
||||||
<Input
|
<Input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
className="h-7 text-xs w-full"
|
type="text"
|
||||||
value={editValue}
|
value={editValue}
|
||||||
onChange={(e) => setEditValue(e.target.value)}
|
onChange={(e) => setEditValue(e.target.value)}
|
||||||
onBlur={handleSave}
|
onKeyDown={handleKeyDown}
|
||||||
onKeyDown={handleKeyDown}
|
onBlur={handleSave}
|
||||||
disabled={isSubmitting}
|
className="h-6 text-xs"
|
||||||
/>
|
disabled={isSubmitting}
|
||||||
</div>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div
|
// Render text component when not editing
|
||||||
className={`flex-1 overflow-hidden ${isEditableField ? 'cursor-text' : ''}`}
|
<div className="flex items-center gap-1">
|
||||||
onDoubleClick={isEditableField ? handleDoubleClick : undefined}
|
|
||||||
>
|
|
||||||
<Text
|
<Text
|
||||||
className="hover:bg-primary/20 rounded p-1 overflow-hidden text-ellipsis block w-full"
|
className="hover:bg-primary/20 rounded p-1 overflow-hidden text-ellipsis"
|
||||||
tooltipClassName="max-w-80"
|
tooltipClassName="max-w-80"
|
||||||
text={String(value)}
|
text={currentValue}
|
||||||
tooltip={tooltip || (typeof value === 'string' ? value : JSON.stringify(value, null, 2))}
|
tooltip={tooltip || (typeof currentValue === 'string' ? currentValue : JSON.stringify(currentValue, null, 2))}
|
||||||
side="left"
|
side="left"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
/>
|
/>
|
||||||
|
@@ -195,8 +195,8 @@ const PropertyRow = ({
|
|||||||
return translation === translationKey ? name : translation
|
return translation === translationKey ? name : translation
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use EditablePropertyRow for editable fields (description and entity_id)
|
// Use EditablePropertyRow for editable fields (description, entity_id and keywords)
|
||||||
if (isEditable && (name === 'description' || name === 'entity_id')) {
|
if (isEditable && (name === 'description' || name === 'entity_id' || name === 'keywords')) {
|
||||||
return (
|
return (
|
||||||
<EditablePropertyRow
|
<EditablePropertyRow
|
||||||
name={name}
|
name={name}
|
||||||
@@ -352,9 +352,9 @@ const EdgePropertiesView = ({ edge }: { edge: EdgeType }) => {
|
|||||||
value={edge.properties[name]}
|
value={edge.properties[name]}
|
||||||
entityId={edge.id}
|
entityId={edge.id}
|
||||||
entityType="edge"
|
entityType="edge"
|
||||||
sourceId={edge.sourceNode?.properties['entity_id'] || edge.source}
|
sourceId={edge.source}
|
||||||
targetId={edge.targetNode?.properties['entity_id'] || edge.target}
|
targetId={edge.target}
|
||||||
isEditable={name === 'description'}
|
isEditable={name === 'description' || name === 'keywords'}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
Reference in New Issue
Block a user