탭 정리

This commit is contained in:
현성필
2025-02-02 22:22:37 +09:00
parent b02596ed04
commit 40e6aac336
4 changed files with 367 additions and 314 deletions
@@ -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,
@@ -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 => ({
@@ -275,9 +273,10 @@ export const HTTPClientView = ({model, index}) => {
</div>
{/* Request/Response Container */}
<div className="flex flex-col gap-4 flex-1 min-h-0">
<ScrollArea className="flex-1">
<div className="grid flex-1 gap-4 min-h-0">
{/* Request Section */}
<div className="flex-1 min-h-0">
<div className="flex flex-col">
<Tabs value={currentTab} onValueChange={setCurrentTab} className="flex flex-col h-full">
<TabsList>
<TabsTrigger value="body">Body</TabsTrigger>
@@ -317,7 +316,7 @@ export const HTTPClientView = ({model, index}) => {
requestBody: newValue
}));
}}
height="full"
height="300px"
/>
</div>
</div>
@@ -346,7 +345,7 @@ export const HTTPClientView = ({model, index}) => {
setEditorContents(prev => ({...prev, preRequestScript: value}));
setCurrentModel(prev => ({...prev, preRequestScript: value}));
}}
height="full"
height="300px"
/>
</TabsContent>
@@ -360,7 +359,7 @@ export const HTTPClientView = ({model, index}) => {
setEditorContents(prev => ({...prev, postRequestScript: value}));
setCurrentModel(prev => ({...prev, postRequestScript: value}));
}}
height="full"
height="300px"
/>
</TabsContent>
</div>
@@ -368,7 +367,7 @@ export const HTTPClientView = ({model, index}) => {
</div>
{/* Response Section */}
<div className="flex-1 min-h-0">
<div className="flex flex-col">
<div className="flex items-center gap-2 mb-2">
<span>Response</span>
<span className="text-sm text-gray-500">{responseData.status}</span>
@@ -389,7 +388,7 @@ export const HTTPClientView = ({model, index}) => {
id={`response-body-${model.id}`}
value={editorContents.responseBody}
readOnly={true}
height="full"
height="300px"
/>
</TabsContent>
@@ -403,15 +402,14 @@ export const HTTPClientView = ({model, index}) => {
value={editorContents.consoleLog}
language="plaintext"
readOnly={true}
height="full"
height="300px"
/>
</TabsContent>
</div>
</Tabs>
</div>
</div>
</ScrollArea>
</div>
);
};
@@ -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 (
<div className="flex flex-col h-full p-4 gap-4">
{/* Request Header Section */}
@@ -202,7 +179,7 @@ export const TCPClientView = ({model, index}) => {
</Button>
<Button variant="secondary" onClick={handleSave}>Save</Button>
</div>
<ScrollArea className="flex-1">
<div className="grid flex-1 gap-4 min-h-0">
{/* 요청 영역 */}
<Card className="flex flex-col">
@@ -213,49 +190,23 @@ export const TCPClientView = ({model, index}) => {
<Tabs value={currentTab} onValueChange={setCurrentTab} className="flex flex-col flex-1">
<TabsList>
<TabsTrigger value="body">Layout</TabsTrigger>
<TabsTrigger value="layout-tree">Layout Tree</TabsTrigger>
<TabsTrigger value="layout-data">Layout Data</TabsTrigger>
<TabsTrigger value="layout-json">Layout JSON</TabsTrigger>
<TabsTrigger value="pre-request">Pre-Request</TabsTrigger>
<TabsTrigger value="post-request">Post-Request</TabsTrigger>
</TabsList>
<div className="flex-1 min-h-0 mt-2">
{/* Body Tab Content */}
<TabsContent value="body" className="h-full m-0">
<LayoutGrid
layout={currentModel.layout}
layoutItems={layoutItems} // Add this
setLayoutItems={setLayoutItems} // Add this
onChange={handleLayoutUpdate}
/>
</TabsContent>
<TabsContent value="layout-tree" className="h-full m-0">
<LayoutTreeGrid
layout={currentModel.layout}
layoutItems={layoutItems} // Add this
setLayoutItems={setLayoutItems} // Add this
onChange={handleLayoutUpdate}
/>
</TabsContent>
<TabsContent value="layout-data" className="h-full m-0">
<LayoutTreeDataGrid
layout={currentModel.layout}
layoutItems={layoutItems} // Add this
setLayoutItems={setLayoutItems} // Add this
onChange={handleLayoutDataUpdate}
/>
</TabsContent>
<TabsContent value="layout-json" className="h-full m-0">
<MonacoEditor
id={`layout-json-${model.id}`}
value={editorContents.layout}
language="json"
onChange={(value) => {
setEditorContents(prev => ({...prev, layout: value}));
setCurrentModel(prev => ({...prev, layout: JSON.parse(value)}));
<FlatLayoutManager
initialLayout={currentModel.layout}
modelId={model.id}
onLayoutChange={handleLayoutUpdate}
onLayoutDataChange={handleLayoutDataUpdate}
onRequestBodyChange={(body) => {
setCurrentModel(prev => ({
...prev,
requestBody: body
}));
}}
height="full"
/>
</TabsContent>
<TabsContent value="pre-request" className="h-full m-0">
@@ -267,7 +218,7 @@ export const TCPClientView = ({model, index}) => {
setEditorContents(prev => ({...prev, preRequestScript: value}));
setCurrentModel(prev => ({...prev, preRequestScript: value}));
}}
height="full"
height="300px"
/>
</TabsContent>
@@ -281,7 +232,7 @@ export const TCPClientView = ({model, index}) => {
setEditorContents(prev => ({...prev, postRequestScript: value}));
setCurrentModel(prev => ({...prev, postRequestScript: value}));
}}
height="full"
height="300px"
/>
</TabsContent>
</div>
@@ -301,7 +252,7 @@ export const TCPClientView = ({model, index}) => {
requestBody: newValue
}));
}}
height="200px"
height="150px"
/>
<div className="mt-2 text-sm text-gray-600">
<strong>전체 메시지 길이:</strong> {currentModel.requestBody?.length || 0}
@@ -337,7 +288,7 @@ export const TCPClientView = ({model, index}) => {
id={`response-body-${model.id}`}
value={editorContents.responseBody}
readOnly={true}
height="full"
height="250px"
/>
</TabsContent>
<TabsContent value="console" className="h-full m-0">
@@ -346,7 +297,7 @@ export const TCPClientView = ({model, index}) => {
value={editorContents.consoleLog}
language="plaintext"
readOnly={true}
height="full"
height="250px"
/>
</TabsContent>
</div>
@@ -354,6 +305,7 @@ export const TCPClientView = ({model, index}) => {
</CardContent>
</Card>
</div>
</ScrollArea>
</div>
);
};
@@ -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 (
<Tabs defaultValue="grid">
<TabsList>
<TabsTrigger value="grid">Layout</TabsTrigger>
<TabsTrigger value="tree">Layout Tree</TabsTrigger>
<TabsTrigger value="data">Layout Data</TabsTrigger>
<TabsTrigger value="json">Layout JSON</TabsTrigger>
</TabsList>
<div className="flex-1 min-h-0 mt-2">
<TabsContent value="grid" className="h-full m-0">
<LayoutGrid
layout={currentLayout}
layoutItems={layoutItems}
setLayoutItems={setLayoutItems}
onChange={handleLayoutUpdate}
/>
</TabsContent>
<TabsContent value="tree" className="h-full m-0">
<LayoutTreeGrid
layout={currentLayout}
layoutItems={layoutItems}
/>
</TabsContent>
<TabsContent value="data" className="h-full m-0">
<LayoutTreeDataGrid
layout={currentLayout}
onChange={handleLayoutDataUpdate}
/>
</TabsContent>
<TabsContent value="json" className="h-full m-0">
<MonacoEditor
id={`layout-json-${modelId}`}
value={layoutJson}
language="json"
onChange={handleJsonChange}
height="300px"
/>
</TabsContent>
</div>
</Tabs>
);
};
export default FlatLayoutManager;