From 4b5992bcc121c784a90e053445ec3faa7e69b09a Mon Sep 17 00:00:00 2001 From: briannavo Date: Sun, 16 Nov 2025 13:05:46 -0500 Subject: [PATCH] added edit feature to nodes, with save/cancel ability --- src/features/modals/NodeEditModal/index.tsx | 210 ++++++++++++++++++ src/features/modals/NodeModal/index.tsx | 225 +++++++++++++++++--- 2 files changed, 410 insertions(+), 25 deletions(-) create mode 100644 src/features/modals/NodeEditModal/index.tsx diff --git a/src/features/modals/NodeEditModal/index.tsx b/src/features/modals/NodeEditModal/index.tsx new file mode 100644 index 00000000000..96e5d0ddabe --- /dev/null +++ b/src/features/modals/NodeEditModal/index.tsx @@ -0,0 +1,210 @@ +import React from "react"; +import type { ModalProps } from "@mantine/core"; +import { Modal, Stack, Text, Flex, CloseButton, Button, Group, TextInput, Textarea } from "@mantine/core"; +import { toast } from "react-hot-toast"; +import type { NodeData } from "../../../types/graph"; +import useGraph from "../../editor/views/GraphView/stores/useGraph"; +import useJson from "../../../store/useJson"; +import useFile from "../../../store/useFile"; + +export const NodeEditModal = ({ opened, onClose }: ModalProps) => { + const nodeData = useGraph(state => state.selectedNode); + const setJson = useJson(state => state.setJson); + const setContents = useFile(state => state.setContents); + const currentJson = useJson(state => state.json); + + const [isEditing, setIsEditing] = React.useState(false); + const [editValues, setEditValues] = React.useState>({}); + const [originalValues, setOriginalValues] = React.useState>({}); + + React.useEffect(() => { + if (opened && nodeData) { + const values: Record = {}; + nodeData.text.forEach(row => { + if (row.type !== "array" && row.type !== "object" && row.key) { + values[row.key] = row.value; + } + }); + setEditValues(values); + setOriginalValues(JSON.parse(JSON.stringify(values))); + } + }, [opened, nodeData]); + + const handleEdit = () => { + setIsEditing(true); + }; + + const handleCancel = () => { + setIsEditing(false); + setEditValues(JSON.parse(JSON.stringify(originalValues))); + }; + + const handleSave = () => { + try { + const jsonObj = JSON.parse(currentJson); + const path = nodeData?.path; + + if (!path) { + toast.error("Cannot edit root node"); + return; + } + + // Navigate through the path to find the target object + let current = jsonObj; + for (let i = 0; i < path.length; i++) { + if (i === path.length - 1) { + // At the last segment - update the properties + if (typeof current === "object" && current !== null) { + Object.entries(editValues).forEach(([key, value]) => { + current[key] = value; + }); + } + } else { + current = current[path[i]]; + } + } + + // Update the stores + const updatedJson = JSON.stringify(jsonObj, null, 2); + setJson(updatedJson); + setContents({ contents: updatedJson, hasChanges: true, skipUpdate: false }); + + setIsEditing(false); + setOriginalValues(JSON.parse(JSON.stringify(editValues))); + toast.success("Node updated successfully"); + } catch (error) { + console.error("Error saving changes:", error); + toast.error("Failed to save changes"); + } + }; + + const handleValueChange = (key: string, value: string) => { + try { + // Try to parse as JSON if it looks like JSON + let parsedValue: any = value; + if (value === "true") parsedValue = true; + else if (value === "false") parsedValue = false; + else if (value === "null") parsedValue = null; + else if (!isNaN(Number(value)) && value !== "") parsedValue = Number(value); + + setEditValues(prev => ({ + ...prev, + [key]: parsedValue, + })); + } catch { + setEditValues(prev => ({ + ...prev, + [key]: value, + })); + } + }; + + return ( + + + + + {isEditing ? "Edit the node values below:" : "View or edit node information:"} + + + + + {!isEditing && nodeData && ( + + {nodeData.text.map((row, index) => { + if (row.type === "object") { + return ( +
+ + {row.key} + + + {`{${row.childrenCount ?? 0} keys}`} + +
+ ); + } + if (row.type === "array") { + return ( +
+ + {row.key} + + + {`[${row.childrenCount ?? 0} items]`} + +
+ ); + } + return ( +
+ + {row.key} + + + {String(row.value)} + +
+ ); + })} +
+ )} + + {isEditing && ( + + {Object.entries(editValues).map(([key, value]) => ( +
+ + {key} + + {typeof value === "string" && value.length > 100 ? ( +