diff --git a/TestMasterUI/src/components/api-tester/APITabs.jsx b/TestMasterUI/src/components/api-tester/APITabs.jsx index 5230d5d..b0a19bb 100644 --- a/TestMasterUI/src/components/api-tester/APITabs.jsx +++ b/TestMasterUI/src/components/api-tester/APITabs.jsx @@ -5,6 +5,7 @@ import { TCPClientView } from '../client-views/TCPClientView'; import { HTTPFlatClientView } from '../client-views/HTTPFlatClientView'; import { X } from 'lucide-react'; import {Button} from '@/components/ui/button.jsx'; +import {ScrollArea} from '@/components/ui/scroll-area.jsx'; const APITabs = ({ apiRequests, diff --git a/TestMasterUI/src/components/client-views/HTTPClientView.jsx b/TestMasterUI/src/components/client-views/HTTPClientView.jsx index a3c75f2..59c6f7a 100644 --- a/TestMasterUI/src/components/client-views/HTTPClientView.jsx +++ b/TestMasterUI/src/components/client-views/HTTPClientView.jsx @@ -12,6 +12,7 @@ import MonacoEditor from '@/components/shared/MonacoEditor'; import APIClient from '@/services/APIClient.js'; import {SidebarTrigger} from '@/components/ui/sidebar.jsx'; import {Separator} from '@/components/ui/separator.jsx'; +import {ScrollArea} from '@/components/ui/scroll-area.jsx'; const HTTP_METHODS = [ 'POST', 'GET', 'PUT', 'DELETE', 'CONNECT', @@ -96,16 +97,15 @@ export const HTTPClientView = ({model, index}) => { }); }; - const buildUrlFromPathAndParams = (path, queryParams) => { const baseUrl = path.split('?')[0]; const enabledParams = queryParams.filter(p => p.enabled && p.key); - if (!enabledParams.length) return baseUrl; + if (!enabledParams.length) { + return baseUrl; + } - const queryString = enabledParams - .map(p => `${p.key}=${p.value || ''}`) - .join('&'); + const queryString = enabledParams.map(p => `${p.key}=${p.value || ''}`).join('&'); return `${baseUrl}?${queryString}`; }; @@ -119,7 +119,6 @@ export const HTTPClientView = ({model, index}) => { })); }; - const parseQueryParams = (path) => { const queryParams = []; const [, queryString] = path.split('?'); @@ -176,7 +175,6 @@ export const HTTPClientView = ({model, index}) => { responseBody: response.body })); - } catch (error) { console.error(error); setEditorContents(prev => ({ @@ -228,8 +226,8 @@ export const HTTPClientView = ({model, index}) => {
{/* Request Header Section */}
- - + +
{currentModel.collectionName} {' > '} @@ -275,143 +273,143 @@ export const HTTPClientView = ({model, index}) => {
{/* Request/Response Container */} -
- {/* Request Section */} -
- - - Body - Params - Headers - Pre-Request - Post-Request - + +
+ {/* 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;