+ {/* Request Section */}
+
+
+
+ Body
+ Params
+ Headers
+ Pre-Request
+ Post-Request
+
-
- {/* Body Tab Content */}
-
-
-
-
-
{
- setCurrentModel(prev => ({
- ...prev,
- requestBody: newValue
- }));
- }}
- height="full"
- />
+
+ {/* Body Tab Content */}
+
+
+
+
+ {
+ setCurrentModel(prev => ({
+ ...prev,
+ requestBody: newValue
+ }));
+ }}
+ height="300px"
+ />
+
-
-
+
-
-
-
+
+
+
- {/* Headers Tab Content */}
-
-
-
-
- {
- setEditorContents(prev => ({...prev, preRequestScript: value}));
- setCurrentModel(prev => ({...prev, preRequestScript: value}));
- }}
- height="full"
- />
-
+ {/* Headers Tab Content */}
+
+
+
+
+ {
+ setEditorContents(prev => ({...prev, preRequestScript: value}));
+ setCurrentModel(prev => ({...prev, preRequestScript: value}));
+ }}
+ height="300px"
+ />
+
- {/* Post-Request Tab Content */}
-
- {
- setEditorContents(prev => ({...prev, postRequestScript: value}));
- setCurrentModel(prev => ({...prev, postRequestScript: value}));
- }}
- height="full"
- />
-
-
-
-
-
- {/* Response Section */}
-
-
- Response
- {responseData.status}
- {responseData.time}
- {responseData.size}
+ {/* Post-Request Tab Content */}
+
+ {
+ setEditorContents(prev => ({...prev, postRequestScript: value}));
+ setCurrentModel(prev => ({...prev, postRequestScript: value}));
+ }}
+ height="300px"
+ />
+
+
+
-
-
- Body
- Headers
- Console
-
-
-
-
-
-
-
-
- {renderResponseHeaders()}
-
-
-
-
-
+ {/* Response Section */}
+
+
+ Response
+ {responseData.status}
+ {responseData.time}
+ {responseData.size}
-
+
+
+
+ Body
+ Headers
+ Console
+
+
+
+
+
+
+
+
+ {renderResponseHeaders()}
+
+
+
+
+
+
+
+
-
-
-
+
);
};
diff --git a/TestMasterUI/src/components/client-views/TCPClientView.jsx b/TestMasterUI/src/components/client-views/TCPClientView.jsx
index 7fdb017..e9883d9 100644
--- a/TestMasterUI/src/components/client-views/TCPClientView.jsx
+++ b/TestMasterUI/src/components/client-views/TCPClientView.jsx
@@ -7,10 +7,9 @@ import MonacoEditor from '@/components/shared/MonacoEditor';
import APIClient from '@/services/APIClient';
import {SidebarTrigger} from '@/components/ui/sidebar.jsx';
import {Separator} from '@/components/ui/separator.jsx';
-import LayoutGrid from '@/components/layout/LayoutGrid.jsx';
-import LayoutTreeGrid from '@/components/layout/LayoutTreeGrid.jsx';
-import LayoutTreeDataGrid from '@/components/layout/LayoutTreeDataGrid.jsx';
import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card.jsx';
+import FlatLayoutManager from '@/components/layout/FlatLayoutManager.jsx';
+import {ScrollArea} from '@/components/ui/scroll-area.jsx';
export const TCPClientView = ({model, index}) => {
const apiClient = new APIClient();
@@ -19,7 +18,7 @@ export const TCPClientView = ({model, index}) => {
const [currentModel, setCurrentModel] = useState(model);
const [currentTab, setCurrentTab] = useState('body');
const [currentResponseTab, setCurrentResponseTab] = useState('response-body');
- const [layoutItems, setLayoutItems] = useState(model.layout?.layoutItems || []);
+ // const [layoutItems, setLayoutItems] = useState(model.layout?.layoutItems || []);
useEffect(() => {
setCurrentModel(prev => ({
@@ -48,7 +47,7 @@ export const TCPClientView = ({model, index}) => {
useEffect(() => {
if (currentModel.server) {
- const selectedServer = getServer(currentModel.server);
+ getServer(currentModel.server);
}
}, [currentModel.server]);
@@ -115,58 +114,35 @@ export const TCPClientView = ({model, index}) => {
};
const handleLayoutUpdate = useCallback((updatedLayout) => {
- setLayoutItems(updatedLayout.layoutItems); // Add this
- setCurrentModel(prev => ({ // Update this to use setState
+ setCurrentModel(prev => ({
...prev,
layout: updatedLayout
}));
-
- setEditorContents(prev => ({
- ...prev,
- layout: JSON.stringify(updatedLayout, null, 2)
- }));
-
}, []);
const handleLayoutDataUpdate = useCallback((updatedLayout) => {
- console.log(updatedLayout);
-
- // Update layout and request body based on the data tree
+ const requestBody = generateRequestBody(updatedLayout.computedLayoutItemRoot);
setCurrentModel(prev => ({
...prev,
layout: updatedLayout,
- requestBody: generateRequestBody(updatedLayout.computedLayoutItemRoot)
- }));
-
- // Update editor contents with new request body
- setEditorContents(prev => ({
- ...prev,
- requestBody: generateRequestBody(updatedLayout.computedLayoutItemRoot)
+ requestBody
}));
}, []);
const generateRequestBody = useCallback((layoutRoot) => {
- console.log(layoutRoot);
if (!layoutRoot) return '';
- // Helper function to pad field values with spaces
const padField = (value, length) => {
- // Convert to string and ensure it's not null/undefined
const stringValue = String(value || '');
- // Left pad with spaces to match the specified length
return stringValue.padStart(length, ' ');
};
- // Process nodes using recursive function
const processNodes = (node) => {
- // If it's a Field type node, pad the value
if (node.loutItemType === 'Field') {
return padField(node.value, node.loutItemLength);
}
- // If node has children
if (node.children && node.children.length > 0) {
- // For non-repeating nodes, just process children
return node.children
.map(child => processNodes(child))
.join('');
@@ -178,6 +154,7 @@ export const TCPClientView = ({model, index}) => {
return processNodes(layoutRoot);
}, []);
+
return (
{/* Request Header Section */}
@@ -202,158 +179,133 @@ export const TCPClientView = ({model, index}) => {
+
+
+ {/* 요청 영역 */}
+
+
+ Request
+
+
+
+
+ Layout
+ Pre-Request
+ Post-Request
+
-
- {/* 요청 영역 */}
-
-
- Request
-
-
-
-
- Layout
- Layout Tree
- Layout Data
- Layout JSON
- Pre-Request
- Post-Request
-
+
+
+ {
+ setCurrentModel(prev => ({
+ ...prev,
+ requestBody: body
+ }));
+ }}
+ />
+
+
+ {
+ setEditorContents(prev => ({...prev, preRequestScript: value}));
+ setCurrentModel(prev => ({...prev, preRequestScript: value}));
+ }}
+ height="300px"
+ />
+
-
- {/* Body Tab Content */}
-
-
-
-
-
-
-
-
-
-
- {
- setEditorContents(prev => ({...prev, layout: value}));
- setCurrentModel(prev => ({...prev, layout: JSON.parse(value)}));
- }}
- height="full"
- />
-
-
- {
- setEditorContents(prev => ({...prev, preRequestScript: value}));
- setCurrentModel(prev => ({...prev, preRequestScript: value}));
- }}
- height="full"
- />
-
+ {/* Post-Request Tab Content */}
+
+ {
+ setEditorContents(prev => ({...prev, postRequestScript: value}));
+ setCurrentModel(prev => ({...prev, postRequestScript: value}));
+ }}
+ height="300px"
+ />
+
+
+
- {/* Post-Request Tab Content */}
-
- {
- setEditorContents(prev => ({...prev, postRequestScript: value}));
- setCurrentModel(prev => ({...prev, postRequestScript: value}));
- }}
- height="full"
- />
-
+
+
+
+
Request Body
+
{
+ setCurrentModel(prev => ({
+ ...prev,
+ requestBody: newValue
+ }));
+ }}
+ height="150px"
+ />
+
+ 전체 메시지 길이: {currentModel.requestBody?.length || 0}
+
-
+
+
-
+ {/* 응답 영역 */}
+
+
+
+ Response
+ {responseData.status}
+ {responseData.time}
+ {responseData.size}
+
+
+
+
+
+ Body
+ Console
+
-
-
Request Body
-
{
- setCurrentModel(prev => ({
- ...prev,
- requestBody: newValue
- }));
- }}
- height="200px"
- />
-
- 전체 메시지 길이: {currentModel.requestBody?.length || 0}
-
-
-
-
-
- {/* 응답 영역 */}
-
-
-
- Response
- {responseData.status}
- {responseData.time}
- {responseData.size}
-
-
-
-
-
- Body
- Console
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
);
};
diff --git a/TestMasterUI/src/components/layout/FlatLayoutManager.jsx b/TestMasterUI/src/components/layout/FlatLayoutManager.jsx
new file mode 100644
index 0000000..6bae638
--- /dev/null
+++ b/TestMasterUI/src/components/layout/FlatLayoutManager.jsx
@@ -0,0 +1,102 @@
+import React, { useState, useCallback } from 'react';
+import LayoutGrid from '@/components/layout/LayoutGrid';
+import LayoutTreeGrid from '@/components/layout/LayoutTreeGrid';
+import LayoutTreeDataGrid from '@/components/layout/LayoutTreeDataGrid';
+import MonacoEditor from '@/components/shared/MonacoEditor';
+import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
+
+const FlatLayoutManager = ({
+ initialLayout,
+ onLayoutChange,
+ onLayoutDataChange,
+ onRequestBodyChange,
+ modelId
+}) => {
+ const [currentLayout, setCurrentLayout] = useState(initialLayout);
+ const [layoutItems, setLayoutItems] = useState(initialLayout?.layoutItems || []);
+ const [layoutJson, setLayoutJson] = useState(
+ JSON.stringify(initialLayout, null, 2)
+ );
+
+ // Shared layout update handler
+ const handleLayoutUpdate = useCallback((updatedLayout) => {
+ // Clear the computed tree when layout changes
+ const layoutToUpdate = {
+ ...updatedLayout,
+ computedLayoutItemRoot: null // Clear the computed tree
+ };
+ setCurrentLayout(layoutToUpdate);
+ setLayoutItems(layoutToUpdate.layoutItems);
+ setLayoutJson(JSON.stringify(layoutToUpdate, null, 2));
+ onLayoutChange?.(layoutToUpdate);
+ }, [onLayoutChange]);
+
+ // Layout data update handler (for LayoutTreeDataGrid)
+ const handleLayoutDataUpdate = useCallback((updatedLayout) => {
+ setCurrentLayout(updatedLayout);
+ setLayoutItems(updatedLayout.layoutItems);
+ setLayoutJson(JSON.stringify(updatedLayout, null, 2));
+ onLayoutDataChange?.(updatedLayout);
+ }, [onLayoutDataChange]);
+
+ // JSON editor change handler
+ const handleJsonChange = useCallback((value) => {
+ try {
+ const parsedLayout = JSON.parse(value);
+ setLayoutJson(value);
+ setCurrentLayout(parsedLayout);
+ setLayoutItems(parsedLayout.layoutItems || []);
+ onLayoutChange?.(parsedLayout);
+ } catch (error) {
+ console.error('Invalid JSON:', error);
+ }
+ }, [onLayoutChange]);
+
+ return (
+
+
+ Layout
+ Layout Tree
+ Layout Data
+ Layout JSON
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default FlatLayoutManager;