Repository: api-ghost/api-ghost-ui
Pull request #68: feat(request-header): support editable request headers and improve state management
Author: login: haerim-kweon
Created: 2025-05-14T14:59:24Z | Merged: 2025-05-15T07:24:23Z


### Related Issue

- #67 

### Description

This PR adds support for editing the `header` field in request nodes and includes several improvements to state management and UI styling.

**Key Changes:**
- Enabled adding and editing the `header` field for request nodes
- Improved Redux and React Flow state handling to reduce crash scenarios
- Updated styles for request node fields for better UI consistency
- Refined styles for mock API fields to improve readability
- Fixed deep clone issues and refactored related logic for stability

### Testing

- Manually tested request nodes to ensure headers can be added and updated
- Confirmed that updated styles render correctly in the UI
- Ensured consistent behavior after deep cloning and state updates

### Additional Notes

![image](https://github.com/user-attachments/assets/d746c59e-0a0b-4b81-a195-1e833540c167)
![image](https://github.com/user-attachments/assets/30dea366-b99f-4b44-8f53-fc94ba6a4b3a)


- Please review the logic for managing request headers and node updates
- Refactoring focused on clarity and preventing redundant state calls

### Pre-Submission Checklist

- [x] Have you completed code style (convention) checks locally?
- [x] Have you passed the CI tests in your local environment?



Diff:
diff --git a/src/common/utils/scenarioToReactFlow.ts b/src/common/utils/scenarioToReactFlow.ts
index 4ac39d9..7332d0f 100644
--- a/src/common/utils/scenarioToReactFlow.ts
+++ b/src/common/utils/scenarioToReactFlow.ts
@@ -33,7 +33,6 @@ export const scenarioToFlowElements = (
         id: `e-${stepId}-${route.then.step}-${idx}`,
         source: stepId,
         target: route.then.step,
-        label: route.expected.status,
         animated: true,
         data: {
           expected: {
diff --git a/src/pages/flow-canvas/FlowCanvas.tsx b/src/pages/flow-canvas/FlowCanvas.tsx
index 5b6e1ac..2df029e 100644
--- a/src/pages/flow-canvas/FlowCanvas.tsx
+++ b/src/pages/flow-canvas/FlowCanvas.tsx
@@ -6,10 +6,9 @@ import CustomNode from '@/pages/flow-canvas/components/custom-node/CustomNode';
 import { MappingModal } from '@/pages/flow-canvas/components/mapping-modal/MappingModal';
 import styles from './styles/FlowCanvas.module.scss';
 import { COLORS } from '@/pages/flow-canvas/constants/color';
-import { useMockApiModal } from '@/pages/flow-canvas/hooks/useMockApiModal';
 import { MockApiModal } from '@/pages/flow-canvas/components/mock-api-modal/MockApiModal';
-
 import { useAppSelector } from '@/store/hooks';
+
 import { NodeEndPoint } from '@/pages/flow-canvas/types';
 import {
   getScenarioList,
@@ -22,8 +21,7 @@ import { useScenario } from './hooks/useScenario';
 import SaveButton from '@/common/components/SaveButton';
 import PlayButton from '@/common/components/PlayButton';
 import CustomEdge from '@/pages/flow-canvas/components/custom-node/CustomEdge';
-
-const nodeTypes = { endpointNode: CustomNode };
+const nodeTypes = { endpointNode: CustomNode, mockNode: CustomNode };
 
 type NodeType = Node<NodeEndPoint>;
 
@@ -48,27 +46,6 @@ const FlowCanvas: React.FC = () => {
   } = useFlowCanvas();
 
   const [showMappingModal, setShowMappingModal] = useState<boolean>(false);
-  const {
-    isVisible: isMockVisible, //TODO
-    formValues,
-    baseUrl,
-    path,
-    method,
-    isSchemaValid,
-    openMockApiModal,
-    saveMockApi,
-    closeMockApiModal,
-    reqSchemaText,
-    resSchemaText,
-    setBaseUrl,
-    setMethod,
-    setPath,
-    setIsSchemaValid,
-    setReqSchemaText,
-    setResSchemaText,
-    validateSchemas,
-  } = useMockApiModal();
-
   const { saveScenario } = useScenario();
 
   const onChangeLabel = useCallback(
@@ -80,7 +57,6 @@ const FlowCanvas: React.FC = () => {
                 ...edge,
                 data: {
                   ...edge.data,
-                  label: newLabel,
                   expected: { ...edge.data?.expected, status: newLabel },
                 },
               }
@@ -91,10 +67,6 @@ const FlowCanvas: React.FC = () => {
     [setEdges],
   );
 
-  useEffect(() => {
-    console.log('[FlowCanvas] edges state →', edges);
-  }, [edges]);
-
   const edgeTypes = useMemo(
     () => ({
       flowCanvasEdge: (edgeProps: any) => (
@@ -106,6 +78,8 @@ const FlowCanvas: React.FC = () => {
 
   const [currentEdge, setCurrentEdge] = useState<Edge | null>(null);
 
+  const [showMockApiModal, setShowMockApiModal] = useState<boolean>(false);
+
   const handleEdgeDoubleClick = (_: React.MouseEvent, edge: Edge) => {
     setCurrentEdge(edge);
     setShowMappingModal(true);
@@ -115,8 +89,7 @@ const FlowCanvas: React.FC = () => {
     e.preventDefault();
     e.stopPropagation();
     if (!wrapperRef.current) return;
-    const bounds = wrapperRef.current.getBoundingClientRect();
-    openMockApiModal(e.clientX - bounds.left, e.clientY - bounds.top);
+    setShowMockApiModal(true);
   };
 
   const selectedScenario = useAppSelector(state => state.scenario.selected);
@@ -217,26 +190,9 @@ const FlowCanvas: React.FC = () => {
             setEdges={setEdges}
           />
         )}
-        <MockApiModal
-          isVisible={isMockVisible}
-          formValues={formValues}
-          baseUrl={baseUrl}
-          path={path}
-          method={method}
-          isSchemaValid={isSchemaValid}
-          reqSchemaText={reqSchemaText}
-          resSchemaText={resSchemaText}
-          setBaseUrl={setBaseUrl}
-          setMethod={setMethod}
-          setPath={setPath}
-          setIsSchemaValid={setIsSchemaValid}
-          setReqSchemaText={setReqSchemaText}
-          setResSchemaText={setResSchemaText}
-          saveMockApi={saveMockApi}
-          onConfirm={addNode}
-          onCancel={closeMockApiModal}
-          validateSchemas={validateSchemas}
-        />
+        {showMockApiModal && (
+          <MockApiModal onConfirm={addNode} closeModal={() => setShowMockApiModal(false)} />
+        )}
       </div>
     </>
   );
@@ -244,8 +200,10 @@ const FlowCanvas: React.FC = () => {
 
 const nodeColor = (node: any) => {
   switch (node.type) {
-    case 'endpointNode':
+    case 'mockNode':
       return '#6ede87';
+    case 'endpointNode':
+      return '#6865A5';
     default:
       return '#ff0072';
   }
diff --git a/src/pages/flow-canvas/components/custom-node/CustomEdge.tsx b/src/pages/flow-canvas/components/custom-node/CustomEdge.tsx
index 249357a..b80e865 100644
--- a/src/pages/flow-canvas/components/custom-node/CustomEdge.tsx
+++ b/src/pages/flow-canvas/components/custom-node/CustomEdge.tsx
@@ -35,7 +35,7 @@ const CustomEdge: React.FC<CustomEdgeProps> = ({
   return (
     <>
       <path id={id} d={path} stroke="#222" strokeWidth={1.5} fill="none" markerEnd={markerEnd} />
-      {data?.expected?.status !== undefined && (
+      {data?.expected?.status && (
         <foreignObject
           width={150}
           height={40}
diff --git a/src/pages/flow-canvas/components/custom-node/CustomNode.tsx b/src/pages/flow-canvas/components/custom-node/CustomNode.tsx
index 9c4e6b4..2b65221 100644
--- a/src/pages/flow-canvas/components/custom-node/CustomNode.tsx
+++ b/src/pages/flow-canvas/components/custom-node/CustomNode.tsx
@@ -8,9 +8,8 @@ import { useAppSelector, useAppDispatch } from '@/store/hooks';
 import { setNodeTab } from '@/store/slices/nodeTabSlice';
 import { useFlowCanvas } from '@/pages/flow-canvas/hooks/useFlowCanvas';
 
-const CustomNode: React.FC<NodeProps<NodeEndPoint>> = ({ id, data, xPos, yPos }) => {
-  const { setNodes } = useReactFlow();
-  const { updateNode } = useFlowCanvas();
+const CustomNode: React.FC<NodeProps<NodeEndPoint>> = ({ id, data, xPos, yPos, type }) => {
+  const { updateNode, setNodes } = useFlowCanvas();
   const dispatch = useAppDispatch();
 
   const {
@@ -20,6 +19,7 @@ const CustomNode: React.FC<NodeProps<NodeEndPoint>> = ({ id, data, xPos, yPos })
     showBody,
     requestSchema: dataReq = [],
     responseSchema: dataRes = [],
+    header: dataHeader,
   } = data;
 
   const { requestSchema, responseSchema, save } = useSchemaEditor(id, dataReq, dataRes);
@@ -36,8 +36,13 @@ const CustomNode: React.FC<NodeProps<NodeEndPoint>> = ({ id, data, xPos, yPos })
     );
   };
 
-  const handleSave = (type: MainTabType, newSchema: Field[]) => {
-    save(type, newSchema);
+  const handleSave = (
+    request: Field[],
+    response: Field[],
+    updatedHeader?: Record<string, string>,
+  ) => {
+    save(MainTabType.REQUEST, request);
+    save(MainTabType.REQUEST, response);
 
     const updatedNode = {
       id,
@@ -45,21 +50,21 @@ const CustomNode: React.FC<NodeProps<NodeEndPoint>> = ({ id, data, xPos, yPos })
       position: { x: xPos, y: yPos },
       data: {
         ...data,
-        requestSchema: type === MainTabType.REQUEST ? newSchema : data.requestSchema,
-        responseSchema: type === MainTabType.RESPONSE ? newSchema : data.responseSchema,
+        requestSchema: request,
+        responseSchema: response,
+        header: updatedHeader,
         showBody: false,
       },
     };
+
     updateNode(updatedNode);
   };
 
-  const handleSaveRequest = (schema: Field[]) => handleSave(MainTabType.REQUEST, schema);
-  const handleSaveResponse = (schema: Field[]) => handleSave(MainTabType.RESPONSE, schema);
-
+  const isMockNode = type === 'mockNode';
   return (
     <div className={styles.nodeWrapper}>
       <div className={`${styles.node} ${styles[method]}`}>
-        <div className={styles.header}>{baseUrl}</div>
+        <div className={`${styles.header} ${isMockNode && styles.mockHeader}`}>{baseUrl}</div>
 
         <div
           className={styles.actions}
@@ -86,9 +91,9 @@ const CustomNode: React.FC<NodeProps<NodeEndPoint>> = ({ id, data, xPos, yPos })
             onTabChange={(main, sub) =>
               dispatch(setNodeTab({ nodeId: id, mainTab: main, subTab: sub }))
             }
-            onSaveRequestSchema={handleSaveRequest}
-            onSaveResponseSchema={handleSaveResponse}
+            onSaveData={handleSave}
             onClose={handleToggleBody}
+            header={dataHeader}
           />
         )}
       </div>
diff --git a/src/pages/flow-canvas/components/custom-node/NodeBody.tsx b/src/pages/flow-canvas/components/custom-node/NodeBody.tsx
index f49a495..188f21c 100644
--- a/src/pages/flow-canvas/components/custom-node/NodeBody.tsx
+++ b/src/pages/flow-canvas/components/custom-node/NodeBody.tsx
@@ -1,10 +1,10 @@
 import React, { MouseEvent, useState, useEffect } from 'react';
 import styles from '@/pages/flow-canvas/styles/BodyEditor.module.scss';
 import { useBodyEditorController } from '@/pages/flow-canvas/hooks/useBodyEditorController';
-import { BODY_EDITOR_TABS, Path, Tab } from '@/pages/flow-canvas/types/index';
+import { BODY_EDITOR_TABS, Tab } from '@/pages/flow-canvas/types/index';
 import { Field, MainTabType, SubTabType } from '@/pages/flow-canvas/types';
-import { useBodyEditor } from '@/pages/flow-canvas/hooks/useBodyEditor';
 import RenderJsonSchema from '@/pages/flow-canvas/components/custom-node/RenderJsonSchema';
+import RenderHeaderInputTable from './RenderHeaderInputTable';
 
 interface NodeBodyProps {
   requestSchema: Field[];
@@ -12,9 +12,9 @@ interface NodeBodyProps {
   initialMainTabLabel?: MainTabType;
   initialSubTabLabel?: SubTabType;
   onTabChange?: (main: MainTabType, sub: SubTabType) => void;
-  onSaveRequestSchema: (newSchema: Field[]) => void;
-  onSaveResponseSchema: (newSchema: Field[]) => void;
+  onSaveData: (request: Field[], response: Field[], newHeader?: Record<string, string>) => void;
   onClose: () => void;
+  header?: Record<string, string>;
 }
 
 const NodeBody: React.FC<NodeBodyProps> = ({
@@ -23,15 +23,16 @@ const NodeBody: React.FC<NodeBodyProps> = ({
   initialMainTabLabel,
   initialSubTabLabel,
   onTabChange,
-  onSaveRequestSchema,
-  onSaveResponseSchema,
   onClose,
+  onSaveData,
+  header,
 }) => {
   const { mainTab, subTab, availableSubTabs, selectMainTab, selectSubTab } =
     useBodyEditorController(BODY_EDITOR_TABS, initialMainTabLabel, initialSubTabLabel);
 
-  const { currentSchema: reqSchema, updateSchema: setReqSchema } = useBodyEditor(requestSchema);
-  const { currentSchema: resSchema, updateSchema: setResSchema } = useBodyEditor(responseSchema);
+  const [reqSchema, setReqSchema] = useState<Field[]>(requestSchema);
+  const [resSchema, setResSchema] = useState<Field[]>(responseSchema);
+  const [requestHeader, setRequestHeader] = useState<Record<string, string>>({ ...header });
 
   useEffect(() => {
     setReqSchema(requestSchema);
@@ -56,10 +57,7 @@ const NodeBody: React.FC<NodeBodyProps> = ({
   const stopPropagation = (e: MouseEvent) => e.stopPropagation();
 
   const handleSave = () => {
-    if (isRequestTab) onSaveRequestSchema(reqSchema);
-    else {
-      onSaveResponseSchema(resSchema);
-    }
+    onSaveData(reqSchema, resSchema, requestHeader);
     onClose();
   };
 
@@ -91,22 +89,26 @@ const NodeBody: React.FC<NodeBodyProps> = ({
         ))}
       </nav>
 
-      <section className={styles.contentArea}>
-        {subTab.showSchema && isRequestTab && (
-          <RenderJsonSchema
-            data={reqSchema}
-            indent={0}
-            onChange={updated => setReqSchema(updated)}
-          />
-        )}
-        {subTab.showSchema && !isRequestTab && (
-          <RenderJsonSchema
-            data={resSchema}
-            indent={0}
-            onChange={updated => setResSchema(updated)}
-          />
-        )}
-      </section>
+      {subTab.label === SubTabType.HEADER && (
+        <RenderHeaderInputTable header={requestHeader} onChange={setRequestHeader} />
+      )}
+      {subTab.showSchema && (
+        <section className={styles.contentArea}>
+          {isRequestTab ? (
+            <RenderJsonSchema
+              data={reqSchema}
+              indent={0}
+              onChange={updated => setReqSchema(updated)}
+            />
+          ) : (
+            <RenderJsonSchema
+              data={resSchema}
+              indent={0}
+              onChange={updated => setResSchema(updated)}
+            />
+          )}
+        </section>
+      )}
 
       <footer className={styles.buttonRow}>
         {
diff --git a/src/pages/flow-canvas/components/custom-node/RenderHeaderInputTable.tsx b/src/pages/flow-canvas/components/custom-node/RenderHeaderInputTable.tsx
new file mode 100644
index 0000000..16b3e4d
--- /dev/null
+++ b/src/pages/flow-canvas/components/custom-node/RenderHeaderInputTable.tsx
@@ -0,0 +1,94 @@
+import React, { useEffect, useState } from 'react';
+import styles from '@/pages/flow-canvas/styles/BodyEditor.module.scss';
+
+interface HeaderInputTableProps {
+  header?: Record<string, string>;
+  onChange: (updated: Record<string, string>) => void;
+}
+
+interface HeaderRow {
+  key: string;
+  value: string;
+}
+
+const RenderHeaderInputTable: React.FC<HeaderInputTableProps> = ({ header, onChange }) => {
+  const [rows, setRows] = useState<HeaderRow[]>([]);
+
+  useEffect(() => {
+    const initialRows = header
+      ? Object.entries(header).map(([key, value]) => ({ key, value }))
+      : [];
+    setRows([...initialRows, { key: '', value: '' }]);
+  }, [JSON.stringify(header)]);
+
+  const handleChange = (index: number, field: 'key' | 'value', value: string) => {
+    const newRows = [...rows];
+    newRows[index][field] = value;
+
+    if (
+      index === rows.length - 1 &&
+      newRows[index].key.trim() !== '' &&
+      newRows[index].value.trim() !== ''
+    ) {
+      newRows.push({ key: '', value: '' });
+    }
+
+    setRows(newRows);
+    const filtered = newRows.filter(row => row.key.trim() !== '');
+    const updatedHeaders = Object.fromEntries(filtered.map(row => [row.key, row.value]));
+
+    onChange(updatedHeaders);
+  };
+
+  const handleDelete = (index: number) => {
+    const newRows = rows.filter((_, i) => i !== index);
+    setRows(newRows);
+
+    const filtered = newRows.filter(row => row.key.trim() !== '');
+    const updatedHeaders = Object.fromEntries(filtered.map(row => [row.key, row.value]));
+    onChange(updatedHeaders);
+  };
+
+  return (
+    <table className={styles.headerTable}>
+      <thead>
+        <tr>
+          <th>key</th>
+          <th>value</th>
+          <th></th>
+        </tr>
+      </thead>
+      <tbody>
+        {rows.map((row, idx) => (
+          <tr key={idx}>
+            <td>
+              <input
+                type="text"
+                value={row.key}
+                onChange={e => handleChange(idx, 'key', e.target.value)}
+                placeholder="key"
+              />
+            </td>
+            <td>
+              <input
+                type="text"
+                value={row.value}
+                onChange={e => handleChange(idx, 'value', e.target.value)}
+                placeholder="value"
+              />
+            </td>
+            <td className={styles.deleteSection}>
+              {row.key || row.value ? (
+                <button className={styles.deleteButton} onClick={() => handleDelete(idx)}>
+                  x
+                </button>
+              ) : null}
+            </td>
+          </tr>
+        ))}
+      </tbody>
+    </table>
+  );
+};
+
+export default RenderHeaderInputTable;
diff --git a/src/pages/flow-canvas/components/custom-node/RenderJsonSchema.tsx b/src/pages/flow-canvas/components/custom-node/RenderJsonSchema.tsx
index 6aa0b9a..d1e9c66 100644
--- a/src/pages/flow-canvas/components/custom-node/RenderJsonSchema.tsx
+++ b/src/pages/flow-canvas/components/custom-node/RenderJsonSchema.tsx
@@ -3,7 +3,7 @@ import { Field } from '@/pages/flow-canvas/types/index.ts';
 import styles from '@/pages/flow-canvas/styles/BodyEditor.module.scss';
 
 interface RenderJsonSchemaProps {
-  data: Field[] | Field | undefined;
+  data: Field[];
   indent: number;
   onChange?: (updated: Field[]) => void;
 }
@@ -20,6 +20,8 @@ const RenderJsonSchema: React.FC<RenderJsonSchemaProps> = ({ data, indent, onCha
     onChange?.(updated);
   };
 
+  if (!localData) return <></>;
+
   return (
     <>
       <div className={styles.jsonSchema} style={{ marginLeft: indent * 10 }}>
@@ -28,7 +30,7 @@ const RenderJsonSchema: React.FC<RenderJsonSchemaProps> = ({ data, indent, onCha
       {localData.map((field, index) => (
         <div key={`${field.name}-${index}`} style={{ marginLeft: indent * 10 + 10 }}>
           <div>
-            <span title={field.type}>"{field.name}"</span>
+            <span title={field.type ? field.type : ''}>"{field.name}"</span>
             {' : '}
 
             {editingIndex === index ? (
@@ -46,7 +48,7 @@ const RenderJsonSchema: React.FC<RenderJsonSchemaProps> = ({ data, indent, onCha
                 indent={indent + 1}
                 onChange={updatedNested => {
                   const updated = [...localData];
-                  updated[index].nestedFields = updatedNested;
+                  updated[index].nestedFields = [...updatedNested];
                   setLocalData(updated);
                   onChange?.(updated);
                 }}
diff --git a/src/pages/flow-canvas/components/mock-api-modal/MockApiModal.tsx b/src/pages/flow-canvas/components/mock-api-modal/MockApiModal.tsx
index 8b9e382..ca90618 100644
--- a/src/pages/flow-canvas/components/mock-api-modal/MockApiModal.tsx
+++ b/src/pages/flow-canvas/components/mock-api-modal/MockApiModal.tsx
@@ -3,49 +3,30 @@ import styles from '@/pages/flow-canvas/styles/MockApiModal.module.scss';
 import { MockApiFormValues } from '@/pages/flow-canvas/types/index';
 import { CommonButton } from '@/common/components/CommonButton';
 import { HttpMethod } from '@/common/types/index';
+import { useMockApiModal } from '@/pages/flow-canvas/hooks/useMockApiModal';
 
 interface MockApiModalProps {
-  isVisible: boolean;
-  formValues: MockApiFormValues;
-  resSchemaText: string;
-  reqSchemaText: string;
-  baseUrl: string;
-  path: string;
-  method: HttpMethod;
-  isSchemaValid: boolean;
-  setBaseUrl: (v: string) => void;
-  setMethod: (v: HttpMethod) => void;
-  setPath: (v: string) => void;
-  setIsSchemaValid: (v: boolean) => void;
-  setReqSchemaText: (v: string) => void;
-  setResSchemaText: (v: string) => void;
-  saveMockApi: () => MockApiFormValues;
-  onConfirm: (values: MockApiFormValues) => void;
-  onCancel: () => void;
-  validateSchemas: (reqText: string, resText: string) => void;
+  closeModal: () => void;
+  onConfirm: (saved: MockApiFormValues) => void;
 }
 
 const methods = Object.values(HttpMethod);
 
-export const MockApiModal: React.FC<MockApiModalProps> = ({
-  isVisible,
-  formValues,
-  resSchemaText,
-  reqSchemaText,
-  baseUrl,
-  path,
-  method,
-  isSchemaValid,
-  setResSchemaText,
-  setReqSchemaText,
-  setPath,
-  setMethod,
-  setBaseUrl,
-  onConfirm,
-  onCancel,
-  saveMockApi,
-}) => {
+export const MockApiModal: React.FC<MockApiModalProps> = ({ onConfirm, closeModal }) => {
   const stopPropagation = (e: MouseEvent) => e.stopPropagation();
+  const {
+    method,
+    path,
+    baseUrl,
+    reqSchemaText,
+    resSchemaText,
+    setReqSchemaText,
+    setResSchemaText,
+    setPath,
+    saveMockApi,
+    setBaseUrl,
+    setMethod,
+  } = useMockApiModal();
 
   const handleReqChange = (text: string) => {
     setReqSchemaText(text);
@@ -58,52 +39,54 @@ export const MockApiModal: React.FC<MockApiModalProps> = ({
   const handleConfirm = () => {
     const saved: MockApiFormValues = saveMockApi();
     onConfirm(saved);
-    onCancel();
+    closeModal();
   };
 
-  if (!isVisible) return null;
-
   return (
     <div className={styles.overlay} onClick={stopPropagation}>
       <div className={styles.modal} onClick={stopPropagation}>
-        <h2>Create Mock API Node</h2>
+        <div className={styles.header}>
+          <h2>Create Mock API Node</h2>
+        </div>
 
-        <div className={styles.inputRow}>
-          <label>
-            Method:
-            <select value={method} onChange={e => setMethod(e.target.value as HttpMethod)}>
-              {methods.map(m => (
-                <option key={m} value={m}>
-                  {m}
-                </option>
-              ))}
-            </select>
-          </label>
+        <div className={styles.modalContent}>
+          <div className={styles.inputRow}>
+            <label>
+              Method:
+              <select value={method} onChange={e => setMethod(e.target.value as HttpMethod)}>
+                {methods.map(m => (
+                  <option key={m} value={m}>
+                    {m}
+                  </option>
+                ))}
+              </select>
+            </label>
 
-          <label>
-            Base URL:
-            <input value={baseUrl} onChange={e => setBaseUrl(e.target.value)} />
-          </label>
+            <label>
+              Base URL:
+              <input value={baseUrl} onChange={e => setBaseUrl(e.target.value)} />
+            </label>
 
-          <label>
-            Path:
-            <input value={path} onChange={e => setPath(e.target.value)} />
-          </label>
-        </div>
-
-        <div className={styles.schemaRow}>
-          <div className={styles.schemaSection}>
-            <h3>Request Schema</h3>
-            <textarea value={reqSchemaText} onChange={e => handleReqChange(e.target.value)} />
+            <label>
+              Path:
+              <input value={path} onChange={e => setPath(e.target.value)} />
+            </label>
           </div>
 
-          <div className={styles.schemaSection}>
-            <h3>Response Schema</h3>
-            <textarea value={resSchemaText} onChange={e => handleResChange(e.target.value)} />
+          <div className={styles.schemaRow}>
+            <div className={styles.schemaSection}>
+              <h3>Request Schema</h3>
+              <textarea value={reqSchemaText} onChange={e => handleReqChange(e.target.value)} />
+            </div>
+
+            <div className={styles.schemaSection}>
+              <h3>Response Schema</h3>
+              <textarea value={resSchemaText} onChange={e => handleResChange(e.target.value)} />
+            </div>
           </div>
         </div>
 
-        <CommonButton onConfirm={handleConfirm} onCancel={onCancel} />
+        <CommonButton onConfirm={handleConfirm} onCancel={closeModal} />
       </div>
     </div>
   );
diff --git a/src/pages/flow-canvas/hooks/useBodyEditor.ts b/src/pages/flow-canvas/hooks/useBodyEditor.ts
index 3736db3..c968a14 100644
--- a/src/pages/flow-canvas/hooks/useBodyEditor.ts
+++ b/src/pages/flow-canvas/hooks/useBodyEditor.ts
@@ -1,30 +1,30 @@
-import { useState, useCallback } from 'react';
-import { Field } from '@/pages/flow-canvas/types/index';
+// import { useState, useCallback } from 'react';
+// import { Field } from '@/pages/flow-canvas/types/index';
 
-function deepCloneWithValue(fields: Field[]): Field[] {
-  if (!Array.isArray(fields)) {
-    console.error('Expected fields to be an array, but received:', fields);
-    return [];
-  }
-  return fields.map(f => ({
-    type: f.type,
-    name: f.name,
-    nestedFields: f.nestedFields ? deepCloneWithValue(f.nestedFields) : undefined,
-    value: (f as any).value ?? '',
-  }));
-}
+// function deepCloneWithValue(fields: Field[]): Field[] {
+//   if (!Array.isArray(fields)) {
+//     console.error('Expected fields to be an array, but received:', fields);
+//     return [];
+//   }
+//   return fields.map(f => ({
+//     type: f.type,
+//     name: f.name,
+//     nestedFields: f.nestedFields ? deepCloneWithValue(f.nestedFields) : undefined,
+//     value: (f as any).value ?? '',
+//   }));
+// }
 
-/**
- * Hook for managing schema editing state.
- * @param initialSchema - initial Field[] array
- */
-export const useBodyEditor = (initialSchema: Field[]) => {
-  const [currentSchema, setCurrentSchema] = useState<Field[]>(() =>
-    deepCloneWithValue(initialSchema),
-  );
+// /**
+//  * Hook for managing schema editing state.
+//  * @param initialSchema - initial Field[] array
+//  */
+// export const useBodyEditor = (initialSchema: Field[]) => {
+//   const [currentSchema, setCurrentSchema] = useState<Field[]>(() =>
+//     deepCloneWithValue(initialSchema),
+//   );
 
-  const updateSchema = useCallback((newSchema: Field[]) => {
-    setCurrentSchema(newSchema);
-  }, []);
-  return { currentSchema, updateSchema };
-};
+//   const updateSchema = useCallback((newSchema: Field[]) => {
+//     setCurrentSchema(newSchema);
+//   }, []);
+//   return { currentSchema, updateSchema };
+// };
diff --git a/src/pages/flow-canvas/hooks/useCustomNode.ts b/src/pages/flow-canvas/hooks/useCustomNode.ts
index ba4f2cd..e546c01 100644
--- a/src/pages/flow-canvas/hooks/useCustomNode.ts
+++ b/src/pages/flow-canvas/hooks/useCustomNode.ts
@@ -1,6 +1,6 @@
 import { useCallback, MouseEvent } from 'react';
-import { useReactFlow } from 'reactflow';
-import { Field, NodeEndPoint } from '@/pages/flow-canvas/types/index';
+import { Field } from '@/pages/flow-canvas/types/index';
+import { useFlowCanvas } from '@/pages/flow-canvas/hooks/useFlowCanvas';
 
 /**
  * Hook providing toggle and save handlers for a CustomNode.
@@ -8,7 +8,7 @@ import { Field, NodeEndPoint } from '@/pages/flow-canvas/types/index';
  * @param endpointId API spac ID
  */
 export const useNodeControls = (nodeId: string, endpointId?: string) => {
-  const { setNodes } = useReactFlow<NodeEndPoint>();
+  const { setNodes } = useFlowCanvas();
   /**
    * Toggle the showBody flag on the node.
    * If an event is passed, stop its propagation.
diff --git a/src/pages/flow-canvas/hooks/useFlowCanvas.ts b/src/pages/flow-canvas/hooks/useFlowCanvas.ts
index 63278a3..77fb3d9 100644
--- a/src/pages/flow-canvas/hooks/useFlowCanvas.ts
+++ b/src/pages/flow-canvas/hooks/useFlowCanvas.ts
@@ -4,7 +4,7 @@
  * A custom hook that manages ReactFlow state and drag-and-drop logic
  * for the scenario flow canvas.
  */
-import { useCallback, useRef, useEffect, ReactNode } from 'react';
+import { useCallback, useRef, useEffect, useState } from 'react';
 import {
   addEdge,
   applyNodeChanges,
@@ -14,19 +14,13 @@ import {
   NodeChange,
   reconnectEdge,
   useReactFlow,
-  useNodesState,
-  useEdgesState,
   Node,
   EdgeChange,
   Edge as ReactEdge,
 } from 'reactflow';
 import { NodeEndPoint, Field } from '@/pages/flow-canvas/types/index';
 import { useAppDispatch, useAppSelector } from '@/store/hooks';
-import {
-  setNodes as setNodesInStore,
-  setEdges as setEdgesInStore,
-  updateNode as updateNodeInStore,
-} from '@/store/slices/flowSlice';
+import { setNodes as setNodesInStore, setEdges as setEdgesInStore } from '@/store/slices/flowSlice';
 
 export const useFlowCanvas = () => {
   const wrapperRef = useRef<HTMLDivElement>(null);
@@ -36,93 +30,96 @@ export const useFlowCanvas = () => {
   const storedEdges = useAppSelector(state => state.flow.edges);
   const viewport = useAppSelector(state => state.flow.viewport);
 
-  const [nodes, _setNodes, internalOnNodesChange] = useNodesState<NodeEndPoint>(storedNodes);
-  const [edges, _setEdges, internalOnEdgesChange] = useEdgesState<ReactEdge>(storedEdges);
-  const { screenToFlowPosition } = useReactFlow();
-  const pendingEdgeRef = useRef<Edge | null>(null);
+  // Use local state instead of useNodesState and useEdgesState
+  const [nodes, setNodesLocal] = useState<Node<NodeEndPoint>[]>(storedNodes);
+  const [edges, setEdgesLocal] = useState<ReactEdge[]>(storedEdges);
 
-  const setNodes = useCallback(
-    (updater: (ns: Node<NodeEndPoint>[]) => Node<NodeEndPoint>[]) => {
-      _setNodes(updater);
-    },
-    [_setNodes],
-  );
+  // Sync local state with Redux store (only when store changes)
+  useEffect(() => {
+    setNodesLocal(storedNodes);
+  }, [storedNodes]);
 
   useEffect(() => {
-    dispatch(setNodesInStore(nodes));
-  }, [dispatch, nodes]);
+    setEdgesLocal(storedEdges);
+  }, [storedEdges]);
 
-  const setEdges = useCallback(
-    (updater: (es: ReactEdge[]) => ReactEdge[]) => {
-      _setEdges(updater);
-    },
-    [_setEdges],
-  );
+  // Batch updates to Redux store
+  const syncToRedux = useCallback(() => {
+    dispatch(setNodesInStore(nodes));
+    dispatch(setEdgesInStore(edges));
+  }, [dispatch, nodes, edges]);
 
+  // Debounced sync to Redux (prevents too many dispatches)
   useEffect(() => {
-    dispatch(setEdgesInStore(edges));
-  }, [dispatch, edges]);
+    const timeoutId = setTimeout(() => {
+      syncToRedux();
+    }, 50);
+    return () => clearTimeout(timeoutId);
+  }, [nodes, edges, syncToRedux]);
 
-  const onNodesChange = useCallback(
-    (changes: NodeChange[]) => {
-      setNodes(ns => applyNodeChanges(changes, ns));
-    },
-    [setNodes],
-  );
+  const { screenToFlowPosition } = useReactFlow();
+  const pendingEdgeRef = useRef<Edge | null>(null);
 
-  const onEdgesChange = useCallback(
-    (changes: EdgeChange[]) => {
-      setEdges(es => applyEdgeChanges(changes, es));
-    },
-    [setEdges],
-  );
+  const setNodes = useCallback((updater: (ns: Node<NodeEndPoint>[]) => Node<NodeEndPoint>[]) => {
+    setNodesLocal(updater);
+  }, []);
 
-  const updateNode = useCallback(
-    (node: Node<NodeEndPoint>) => {
-      setNodes(ns => {
-        const idx = ns.findIndex(n => n.id === node.id);
-        if (idx !== -1) {
-          const next = [...ns];
-          next[idx] = node;
-          return next;
-        }
-        return ns;
-      });
-      dispatch(updateNodeInStore(node));
-    },
-    [dispatch, setNodes],
-  );
+  const setEdges = useCallback((updater: (es: ReactEdge[]) => ReactEdge[]) => {
+    setEdgesLocal(updater);
+  }, []);
 
-  /**
-   * Adds a new edge when two nodes are connected.
-   *
-   * @param params - edge or connection parameters
-   */
-  const onConnect = useCallback(
-    (params: Connection) => {
-      const newEdge: Edge = {
-        ...params,
-        id: `${params.source}-${params.target}`,
-        animated: true,
-        type: 'flowCanvasEdge',
+  const onNodesChange = useCallback((changes: NodeChange[]) => {
+    setNodesLocal(prev => applyNodeChanges(changes, prev));
+  }, []);
+
+  const onEdgesChange = useCallback((changes: EdgeChange[]) => {
+    setEdgesLocal(prev => applyEdgeChanges(changes, prev));
+  }, []);
+
+  const updateNode = useCallback((node: Node<NodeEndPoint>) => {
+    setNodesLocal(prev => {
+      const idx = prev.findIndex(n => n.id === node.id);
+      if (idx === -1) return prev;
+
+      const updated = [...prev];
+      updated[idx] = {
+        ...prev[idx],
+        ...node,
+        position: {
+          ...prev[idx].position,
+          ...node.position,
+        },
         data: {
-          expected: {
-            status: '200',
-            value: {},
+          ...prev[idx].data,
+          ...node.data,
+          header: {
+            ...prev[idx].data?.header,
+            ...node.data?.header,
           },
-          label: '200',
         },
-        source: params.source!,
-        target: params.target!,
-        sourceHandle: params.sourceHandle!,
-        targetHandle: params.targetHandle!,
       };
-      console.log('[useFlowCanvas] onConnect', newEdge);
 
-      setEdges(es => addEdge(newEdge, es));
-    },
-    [setEdges],
-  );
+      return updated;
+    });
+  }, []);
+
+  const onConnect = useCallback((params: Connection) => {
+    const newEdge: Edge = {
+      ...params,
+      id: `${params.source}-${params.target}`,
+      animated: true,
+      type: 'flowCanvasEdge',
+      data: {
+        expected: { status: '200', value: {} },
+        label: '200',
+      },
+      source: params.source!,
+      target: params.target!,
+      sourceHandle: params.sourceHandle!,
+      targetHandle: params.targetHandle!,
+    };
+    setEdgesLocal(prev => addEdge(newEdge, prev));
+  }, []);
 
   /**
    * Prevents default behavior and sets move effect during drag over.
@@ -154,7 +151,8 @@ export const useFlowCanvas = () => {
       });
       const req = endpoint.requestSchema ?? [];
       const res = endpoint.responseSchema ?? [];
-      setNodes(ns => [
+
+      setNodesLocal(ns => [
         ...ns,
         {
           id: `${endpoint.endpointId}_${Date.now()}`,
@@ -169,39 +167,33 @@ export const useFlowCanvas = () => {
         },
       ]);
     },
-    [screenToFlowPosition, setNodes],
+    [screenToFlowPosition],
   );
 
   const onEdgeUpdateStart = useCallback((_: any, edge: Edge) => {
     pendingEdgeRef.current = edge;
   }, []);
 
-  const onEdgeUpdate = useCallback(
-    (oldEdge: Edge, newConn: Connection) => {
-      if (newConn.target) {
-        setEdges(es => reconnectEdge(oldEdge, newConn, es));
-        pendingEdgeRef.current = null;
-      }
-    },
-    [setEdges],
-  );
+  const onEdgeUpdate = useCallback((oldEdge: Edge, newConn: Connection) => {
+    if (newConn.target) {
+      setEdgesLocal(es => reconnectEdge(oldEdge, newConn, es));
+      pendingEdgeRef.current = null;
+    }
+  }, []);
 
   const onEdgeUpdateEnd = useCallback(() => {
     const edge = pendingEdgeRef.current;
     if (edge) {
-      setEdges(es => es.filter(e => e.id !== edge.id));
+      setEdgesLocal(es => es.filter(e => e.id !== edge.id));
       pendingEdgeRef.current = null;
     }
-  }, [setEdges]);
+  }, []);
 
-  const onEdgeContextMenu = useCallback(
-    (event: React.MouseEvent, edge: Edge) => {
-      event.preventDefault();
-      event.stopPropagation();
-      setEdges(es => es.filter(e => e.id !== edge.id));
-    },
-    [setEdges],
-  );
+  const onEdgeContextMenu = useCallback((event: React.MouseEvent, edge: Edge) => {
+    event.preventDefault();
+    event.stopPropagation();
+    setEdgesLocal(es => es.filter(e => e.id !== edge.id));
+  }, []);
 
   const addNode = useCallback(
     (vals: {
@@ -212,11 +204,12 @@ export const useFlowCanvas = () => {
       responseSchema: Field[];
       x: number;
       y: number;
+      header?: Record<string, string>;
     }) => {
       const id = `mock-${Date.now()}`;
       const newNode = {
         id,
-        type: 'endpointNode',
+        type: 'mockNode',
         position: { x: vals.x, y: vals.y },
         data: {
           endpointId: id,
@@ -226,19 +219,17 @@ export const useFlowCanvas = () => {
           requestSchema: vals.requestSchema,
           responseSchema: vals.responseSchema,
           showBody: false,
+          header: vals.header,
         } as NodeEndPoint,
       };
-      setNodes(ns => ns.concat(newNode));
+      setNodesLocal(ns => ns.concat(newNode));
     },
-    [setNodes],
+    [],
   );
 
-  const removeNode = useCallback(
-    (nodeId: String) => {
-      setNodes(ns => ns.filter(n => n.id !== nodeId));
-    },
-    [setNodes],
-  );
+  const removeNode = useCallback((nodeId: String) => {
+    setNodesLocal(prev => prev.filter(n => n.id !== nodeId));
+  }, []);
 
   return {
     wrapperRef,
diff --git a/src/pages/flow-canvas/hooks/useMockApiModal.ts b/src/pages/flow-canvas/hooks/useMockApiModal.ts
index 5b335c5..5f3d7c5 100644
--- a/src/pages/flow-canvas/hooks/useMockApiModal.ts
+++ b/src/pages/flow-canvas/hooks/useMockApiModal.ts
@@ -38,17 +38,7 @@ export const useMockApiModal = () => {
   };
 
   const openMockApiModal = (x: number, y: number) => {
-    setFormValues(init);
-    setMethod(init.method);
-    setBaseUrl(init.baseUrl);
-    setPath(init.path);
-
-    const reqText = JSON.stringify(fieldsToJson(init.requestSchema), null, 2);
-    const resText = JSON.stringify(fieldsToJson(init.responseSchema), null, 2);
-    setReqSchemaText(reqText);
-    setResSchemaText(resText);
-    validateSchemas(reqText, resText);
-
+    setFormValues({ ...init, x, y });
     setVisible(true);
   };
 
@@ -62,18 +52,14 @@ export const useMockApiModal = () => {
       const responseSchema: Field[] = parseJsonToFields(resSchemaText, 'value')[0].nestedFields!;
 
       const values: MockApiFormValues = {
+        ...formValues,
         baseUrl,
         method,
         path,
         requestSchema,
         responseSchema,
-        x: formValues.x,
-        y: formValues.y,
       };
 
-      console.log('[ saveMockApi ] : ' + values);
-      setFormValues(values);
-      setVisible(false);
       return values;
     } catch (e) {
       console.error('Failed to save mock API due to invalid schema', e);
diff --git a/src/pages/flow-canvas/styles/BodyEditor.module.scss b/src/pages/flow-canvas/styles/BodyEditor.module.scss
index 5fb3d6b..2e3acf2 100644
--- a/src/pages/flow-canvas/styles/BodyEditor.module.scss
+++ b/src/pages/flow-canvas/styles/BodyEditor.module.scss
@@ -126,15 +126,10 @@
   word-break: break-all;
   overflow-wrap: break-word;
   box-sizing: border-box;
-
-  &:hover {
-    background-color: $white;
-  }
 }
 
 .editInput {
   font-size: $font-size-2xs;
-  // width: 100%;
   max-width: 100%;
   padding: 0;
   border: 1px solid $primary-700;
@@ -146,3 +141,48 @@
 .jsonSchema {
   width: max-content;
 }
+
+.headerTable {
+  background-color: $white;
+  width: 100%;
+  margin: 1rem 0;
+
+  th,
+  td {
+    background-color: $white;
+    font-size: $font-size-2xs;
+    padding: 0.3rem;
+    border: 0.1px solid $outline-color;
+  }
+
+  input {
+    width: 100%;
+    outline: none;
+    border: none;
+    display: block;
+    font-size: $font-size-2xs;
+  }
+
+  :hover {
+    background-color: $white;
+  }
+
+  .deleteSection {
+    width: 25px;
+  }
+
+  .deleteButton {
+    border: none;
+    padding: 0 5px;
+    font-size: $font-size-2xs;
+    border-radius: $radius-md;
+    background-color: $gray-100;
+    border: 0.1px solid $gray-500;
+    color: $gray-500;
+
+    &:hover {
+      border: 0.1px solid $gray-800;
+      color: $gray-800;
+    }
+  }
+}
diff --git a/src/pages/flow-canvas/styles/CustomNode.module.scss b/src/pages/flow-canvas/styles/CustomNode.module.scss
index 65a02ce..cddde93 100644
--- a/src/pages/flow-canvas/styles/CustomNode.module.scss
+++ b/src/pages/flow-canvas/styles/CustomNode.module.scss
@@ -58,6 +58,10 @@
   border-radius: $radius-lg $radius-lg 0px 0px;
 }
 
+.mockHeader {
+  background-color: $green-700;
+}
+
 .actions {
   display: flex;
   align-items: center;
diff --git a/src/pages/flow-canvas/styles/MockApiModal.module.scss b/src/pages/flow-canvas/styles/MockApiModal.module.scss
index 70fc8c8..7847531 100644
--- a/src/pages/flow-canvas/styles/MockApiModal.module.scss
+++ b/src/pages/flow-canvas/styles/MockApiModal.module.scss
@@ -1,26 +1,5 @@
 @use '@/styles/mixins/httpMethod';
 
-.DELETEMethod {
-  @include httpMethod.method-button($delete-bg, $delete-font);
-}
-
-.GETMethod {
-  @include httpMethod.method-button($get-bg, $get-font);
-}
-
-.POSTMethod {
-  @include httpMethod.method-button($post-bg, $post-font);
-}
-
-.PUTMethod {
-  @include httpMethod.method-button($put-bg, $put-font);
-}
-
-.PATCHMethod,
-.WEBSOCKETMethod {
-  @include httpMethod.method-button($patch-bg, $patch-font);
-}
-
 .overlay {
   position: fixed;
   top: 0;
@@ -37,18 +16,31 @@
 .modal {
   background-color: $white;
   border-radius: $radius-md;
-  width: 600px;
+  width: 90%;
+  max-width: 700px;
   box-shadow: 0 4px 12px rgba($black, 0.2);
   display: flex;
   flex-direction: column;
-
-  padding: 1.5rem;
+  overflow: hidden;
+  // padding: 1.5rem;
   gap: 1rem;
 }
 
+.header {
+  padding: 1rem 1.5rem;
+  background-color: $primary-800;
+  border-bottom: 1px solid $gray-200;
+
+  h2 {
+    font-size: $font-size-lg;
+    margin: 0;
+    color: $white;
+  }
+}
+
 .inputRow {
   display: flex;
-  gap: 0.5rem;
+  gap: 1rem;
   width: 100%;
 
   label {
@@ -64,34 +56,70 @@
       padding: 0.5rem;
       border: 1px solid $gray-300;
       border-radius: $radius-sm;
+      font-size: $font-size-sm;
     }
   }
 }
 
 .schemaRow {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 1rem;
+}
+
+.modalContent {
+  padding: 1.5rem;
   display: flex;
+  flex-direction: column;
   gap: 1rem;
+}
+.schemaSection {
+  display: flex;
+  flex-direction: column;
+  flex: 1;
 
-  .schemaSection {
+  h3 {
+    margin: 0 0 0.5rem;
+    font-size: $font-size-md;
+    font-weight: $font-weight-semibold;
+  }
+
+  textarea {
     flex: 1;
-    display: flex;
-    flex-direction: column;
+    height: 300px;
+    resize: vertical;
+    padding: 0.75rem;
+    border: 1px solid $gray-300;
+    border-radius: $radius-sm;
+    font-family: inherit;
+    font-size: $font-size-xs;
+  }
+}
 
-    h3 {
-      margin-bottom: 0.5rem;
-      font-size: $font-size-md;
-      font-weight: $font-weight-semibold;
-    }
+.methodButton {
+  font-size: $font-size-2xs;
+  padding: 0px 6px;
+  border: none;
+  border-radius: $radius-2xl;
+}
 
-    textarea {
-      flex: 1;
-      height: 300px;
-      resize: vertical;
-      padding: 0.75rem;
-      border: 1px solid $gray-300;
-      border-radius: $radius-sm;
-      font-family: inherit;
-      font-size: $font-size-xs;
-    }
-  }
+.DELETEMethod {
+  @include httpMethod.method-button($delete-bg, $delete-font);
+}
+
+.GETMethod {
+  @include httpMethod.method-button($get-bg, $get-font);
+}
+
+.POSTMethod {
+  @include httpMethod.method-button($post-bg, $post-font);
+}
+
+.PUTMethod {
+  @include httpMethod.method-button($put-bg, $put-font);
+}
+
+.PATCHMethod,
+.WEBSOCKETMethod {
+  @include httpMethod.method-button($patch-bg, $patch-font);
 }
diff --git a/src/pages/flow-canvas/types/endpointTypes.ts b/src/pages/flow-canvas/types/endpointTypes.ts
index dbad7a2..13c7c6b 100644
--- a/src/pages/flow-canvas/types/endpointTypes.ts
+++ b/src/pages/flow-canvas/types/endpointTypes.ts
@@ -31,7 +31,7 @@ export interface ApiEndpoint {
 
 export interface NodeEndPoint {
   endpointId: string;
-  header?: string;
+  header?: Record<string, string>;
   method: HttpMethod | string;
   path: string;
   baseUrl: string;
diff --git a/src/styles/_fonts.scss b/src/styles/_fonts.scss
index eef7340..4ef1c1f 100644
--- a/src/styles/_fonts.scss
+++ b/src/styles/_fonts.scss
@@ -4,6 +4,7 @@
  */
 
 /** Font Sizes (xs to 5xl)*/
+$font-size-3xs: 8px;
 $font-size-2xs: 10px;
 $font-size-xs: 12px;
 $font-size-sm: 14px;


Reviews:
user: login: haazz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T06:56:41Z
user: login: haazz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T07:01:07Z
user: login: haazz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T07:06:53Z
user: login: haerim-kweon
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-15T07:25:05Z
user: login: haerim-kweon
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-15T07:25:52Z

Review comments:
diff_hunk: @@ -35,7 +35,7 @@ const CustomEdge: React.FC<CustomEdgeProps> = ({
   return (
     <>
       <path id={id} d={path} stroke="#222" strokeWidth={1.5} fill="none" markerEnd={markerEnd} />
-      {data?.expected?.status !== undefined && (
+      {data?.expected?.status && (
path: src/pages/flow-canvas/components/custom-node/CustomEdge.tsx
user: login: haazz
body: This looks like a great change!
created_at: 2025-05-15T06:56:41Z
author_association: CONTRIBUTOR
path: src/pages/flow-canvas/hooks/useBodyEditor.ts
user: login: haazz
body: Is there a reason this code is left as a comment?
created_at: 2025-05-15T07:01:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -37,18 +16,31 @@
 .modal {
   background-color: $white;
   border-radius: $radius-md;
-  width: 600px;
+  width: 90%;
+  max-width: 700px;
   box-shadow: 0 4px 12px rgba($black, 0.2);
   display: flex;
   flex-direction: column;
-
-  padding: 1.5rem;
+  overflow: hidden;
+  // padding: 1.5rem;
path: src/pages/flow-canvas/styles/MockApiModal.module.scss
user: login: haazz
body: Is there a reason this style is left as a comment?
created_at: 2025-05-15T07:06:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -35,7 +35,7 @@ const CustomEdge: React.FC<CustomEdgeProps> = ({
   return (
     <>
       <path id={id} d={path} stroke="#222" strokeWidth={1.5} fill="none" markerEnd={markerEnd} />
-      {data?.expected?.status !== undefined && (
+      {data?.expected?.status && (
path: src/pages/flow-canvas/components/custom-node/CustomEdge.tsx
user: login: haerim-kweon
body: Thanks a lot.
created_at: 2025-05-15T07:25:05Z
author_association: COLLABORATOR
diff_hunk: @@ -37,18 +16,31 @@
 .modal {
   background-color: $white;
   border-radius: $radius-md;
-  width: 600px;
+  width: 90%;
+  max-width: 700px;
   box-shadow: 0 4px 12px rgba($black, 0.2);
   display: flex;
   flex-direction: column;
-
-  padding: 1.5rem;
+  overflow: hidden;
+  // padding: 1.5rem;
path: src/pages/flow-canvas/styles/MockApiModal.module.scss
user: login: haerim-kweon
body: Thank you for your feedback. I will take it into consideration.
created_at: 2025-05-15T07:25:52Z
author_association: COLLABORATOR

PR comments:
user: login: sung-jun98
created_at: 2025-05-15T02:04:53Z
body: It truly looks more professional and impressive than other API testing tools like Postman. You're amazing!
author_association: COLLABORATOR
user: login: cod0216
created_at: 2025-05-15T02:08:23Z
body: Great work! The code is gradually getting more organized!👍👍
author_association: MEMBER
user: login: haazz
created_at: 2025-05-15T07:08:00Z
body: Thank you for your hard work!! 🙌🙌
author_association: CONTRIBUTOR
user: login: haerim-kweon
created_at: 2025-05-15T07:25:29Z
body: > Great work! The code is gradually getting more organized!👍👍

I want to write good code. Thank you.
author_association: COLLABORATOR