Files
elink-test-master/TestMasterUI/src/components/client-views/HTTPClientView.jsx
T
2025-01-25 11:10:57 +09:00

325 lines
12 KiB
React

import React, {useState} from 'react';
import {Button} from '@/components/ui/button';
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@/components/ui/select';
import {Input} from '@/components/ui/input';
import {Tabs, TabsContent, TabsList, TabsTrigger} from '@/components/ui/tabs';
import {Label} from '@/components/ui/label';
import EditableInput from '../shared/EditableInput';
import PropertyTable from '../shared/PropertyTable';
import ServerManager from '@/components/ServerManager.jsx';
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from '@/components/ui/table.jsx';
import {useAPI} from '@/providers/APIProvider';
import MonacoEditor from '@/components/shared/MonacoEditor';
// import MonacoVariableEditor from './MonacoVariableEditor'; // Import the new component
const HTTP_METHODS = [
'POST', 'GET', 'PUT', 'DELETE', 'CONNECT',
'HEAD', 'OPTIONS', 'TRACE', 'PATCH'
];
export const HTTPClientView = ({model, index}) => {
const {getServer} = useAPI();
const [currentModel, setCurrentModel] = useState(model);
const [basePath, setBasePath] = useState('');
const [currentTab, setCurrentTab] = useState('body');
const [currentResponseTab, setCurrentResponseTab] = useState('response-body');
const [responseData, setResponseData] = useState({
headers: [],
status: '',
time: '',
size: ''
});
const [editorContents, setEditorContents] = useState({
requestBody: currentModel.requestBody || '',
responseBody: currentModel.responseBody || '',
console: '',
preRequestScript: currentModel.preRequestScript || '',
postRequestScript: currentModel.postRequestScript || ''
});
const handleServerChange = (serverId) => {
const selectedServer = getServer(serverId);
setCurrentModel(prev => ({
...prev,
server: serverId
}));
if (selectedServer) {
setBasePath(selectedServer.basePath || '');
}
};
const handleHeadersChange = (headers) => {
setCurrentModel(prev => ({
...prev,
headers
}));
};
const handlePathChange = (path) => {
const queryParams = parseQueryParams(path);
setCurrentModel(prev => ({
...prev,
path,
queryParams
}));
};
const parseQueryParams = (path) => {
const queryParams = [];
const queryString = path.split('?')[1];
if (queryString) {
queryString.split('&').forEach(param => {
const [key, value] = param.split('=');
queryParams.push({
enabled: true,
key,
value: value || ''
});
});
}
return queryParams;
};
const renderRequestVariables = () => (
<div className="p-4">
<h3 className="text-sm font-medium mb-2">Available Variables</h3>
<div className="space-y-2">
<div>
<Label>request</Label>
<div className="text-sm text-gray-500">Request object containing headers, body, etc.</div>
</div>
<div>
<Label>environment</Label>
<div className="text-sm text-gray-500">Current environment variables</div>
</div>
</div>
</div>
);
const renderResponseHeaders = () => (
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Value</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{responseData.headers.map((header, index) => (
<TableRow key={index}>
<TableCell>{header.name}</TableCell>
<TableCell>{header.value}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
return (
<div className="flex flex-col h-full p-4 gap-4">
{/* Request Header Section */}
<div className="flex items-center gap-2">
<div>
<span>{currentModel.collectionName}</span>
{' > '}
<span>{currentModel.name}</span>
</div>
</div>
{/* Request Controls */}
<div className="flex items-center gap-2">
<Select
value={currentModel.method}
onValueChange={(value) => setCurrentModel(prev => ({...prev, method: value}))}
>
<SelectTrigger className="w-24">
<SelectValue placeholder="Method"/>
</SelectTrigger>
<SelectContent>
{HTTP_METHODS.map(method => (
<SelectItem key={method} value={method}>
{method}
</SelectItem>
))}
</SelectContent>
</Select>
<ServerManager
onServerChange={handleServerChange}
/>
<Input
value={basePath} // 선택된 서버의 basePath 표시
readOnly
className="w-40"
/>
<EditableInput
value={currentModel.path}
onChange={handlePathChange}
className="flex-1"
/>
<Button>Send</Button>
<Button variant="secondary">Save</Button>
</div>
{/* Request/Response Container */}
<div className="flex flex-col gap-4 flex-1 min-h-0">
{/* Request Section */}
<div className="flex-1 min-h-0">
<Tabs value={currentTab} onValueChange={setCurrentTab} className="flex flex-col h-full">
<TabsList>
<TabsTrigger value="body">Body</TabsTrigger>
<TabsTrigger value="params">Params</TabsTrigger>
<TabsTrigger value="headers">Headers</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">
<div className="flex flex-col h-full gap-2">
<Select
value={currentModel.contentType}
// onValueChange={(value) => setCurrentModel(prev => ({ ...prev, contentType: value }))}
onValueChange={(value) => {
// Update both content type and editor content
setCurrentModel(prev => ({
...prev,
contentType: value,
// Optionally format the content when switching to JSON
requestBody: value === 'json' && prev.requestBody ?
JSON.stringify(JSON.parse(prev.requestBody), null, 2) :
prev.requestBody
}));
}}
>
<SelectTrigger>
<SelectValue placeholder="Content Type"/>
</SelectTrigger>
<SelectContent>
<SelectItem value="json">JSON</SelectItem>
<SelectItem value="xml">XML</SelectItem>
<SelectItem value="plaintext">Text/Plain</SelectItem>
</SelectContent>
</Select>
<div className="h-full">
<MonacoEditor
id={`request-body-${model.id}`}
value={currentModel.requestBody}
language={
currentModel.contentType
}
onChange={(newValue) => {
setCurrentModel(prev => ({
...prev,
requestBody: newValue
}));
}}
height="full"
/>
</div>
</div>
</TabsContent>
<TabsContent value="params" className="h-full m-0">
<PropertyTable
properties={currentModel.queryParams}
onChange={handlePathChange}
/>
</TabsContent>
{/* Headers Tab Content */}
<TabsContent value="headers" className="h-full m-0">
<PropertyTable
properties={currentModel.headers}
onChange={handleHeadersChange}
/>
</TabsContent>
<TabsContent value="pre-request" className="h-full m-0">
<MonacoEditor
id={`pre-request-${model.id}`}
value={editorContents.preRequestScript}
language="javascript"
onChange={(value) => {
setEditorContents(prev => ({...prev, preRequestScript: value}));
setCurrentModel(prev => ({...prev, preRequestScript: value}));
}}
height="full"
/>
</TabsContent>
{/* Post-Request Tab Content */}
<TabsContent value="post-request" className="h-full m-0">
<MonacoEditor
id={`post-request-${model.id}`}
value={editorContents.postRequestScript}
language="javascript"
onChange={(value) => {
setEditorContents(prev => ({...prev, postRequestScript: value}));
setCurrentModel(prev => ({...prev, postRequestScript: value}));
}}
height="full"
/>
</TabsContent>
</div>
</Tabs>
</div>
{/* Response Section */}
<div className="flex-1 min-h-0">
<div className="flex items-center gap-2 mb-2">
<span>Response</span>
<span className="text-sm text-gray-500">{responseData.status}</span>
<span className="text-sm text-gray-500">{responseData.time}</span>
<span className="text-sm text-gray-500">{responseData.size}</span>
</div>
<Tabs value={currentResponseTab} onValueChange={setCurrentResponseTab} className="flex flex-col h-[calc(100%-2rem)]">
<TabsList>
<TabsTrigger value="response-body">Body</TabsTrigger>
<TabsTrigger value="response-headers">Headers</TabsTrigger>
<TabsTrigger value="console">Console</TabsTrigger>
</TabsList>
<div className="flex-1 min-h-0 mt-2">
<TabsContent value="response-body" className="h-full m-0">
<MonacoEditor
id={`response-body-${model.id}`}
value={editorContents.responseBody}
readOnly={true}
height="full"
/>
</TabsContent>
<TabsContent value="response-headers" className="h-full m-0">
{renderResponseHeaders()}
</TabsContent>
<TabsContent value="console" className="h-full m-0">
<MonacoEditor
id={`console-${model.id}`}
value={editorContents.console}
language="plaintext"
readOnly={true}
height="full"
/>
</TabsContent>
</div>
</Tabs>
</div>
</div>
</div>
);
};
export default HTTPClientView;