Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 210 additions & 0 deletions src/features/modals/NodeEditModal/index.tsx
Original file line number Diff line number Diff line change
@@ -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<Record<string, any>>({});
const [originalValues, setOriginalValues] = React.useState<Record<string, any>>({});

React.useEffect(() => {
if (opened && nodeData) {
const values: Record<string, any> = {};
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 (
<Modal
size="auto"
opened={opened}
onClose={onClose}
centered
withCloseButton={false}
title={isEditing ? "Edit Node" : "Node Details"}
>
<Stack pb="sm" gap="md">
<Flex justify="space-between" align="center">
<Text fz="sm" fw={500}>
{isEditing ? "Edit the node values below:" : "View or edit node information:"}
</Text>
<CloseButton onClick={onClose} />
</Flex>

{!isEditing && nodeData && (
<Stack gap="sm">
{nodeData.text.map((row, index) => {
if (row.type === "object") {
return (
<div key={index}>
<Text fz="xs" c="dimmed">
{row.key}
</Text>
<Text fz="sm" fw={500}>
{`{${row.childrenCount ?? 0} keys}`}
</Text>
</div>
);
}
if (row.type === "array") {
return (
<div key={index}>
<Text fz="xs" c="dimmed">
{row.key}
</Text>
<Text fz="sm" fw={500}>
{`[${row.childrenCount ?? 0} items]`}
</Text>
</div>
);
}
return (
<div key={index}>
<Text fz="xs" c="dimmed">
{row.key}
</Text>
<Text fz="sm" fw={500}>
{String(row.value)}
</Text>
</div>
);
})}
</Stack>
)}

{isEditing && (
<Stack gap="sm">
{Object.entries(editValues).map(([key, value]) => (
<div key={key}>
<Text fz="xs" fw={500} mb="4">
{key}
</Text>
{typeof value === "string" && value.length > 100 ? (
<Textarea
value={String(value)}
onChange={e => handleValueChange(key, e.currentTarget.value)}
placeholder="Enter value"
minRows={3}
/>
) : (
<TextInput
value={String(value)}
onChange={e => handleValueChange(key, e.currentTarget.value)}
placeholder="Enter value"
/>
)}
</div>
))}
</Stack>
)}

<Group justify="flex-end" mt="md">
{!isEditing ? (
<>
<Button variant="default" onClick={onClose}>
Close
</Button>
<Button onClick={handleEdit}>
Edit
</Button>
</>
) : (
<>
<Button variant="default" onClick={handleCancel}>
Cancel
</Button>
<Button onClick={handleSave} color="green">
Save
</Button>
</>
)}
</Group>
</Stack>
</Modal>
);
};
Loading