-
-
Response
-
-
+ {/* Request/Response Container */}
+
+ {/* Request Section */}
+
+
+
+ Layout
+ Layout JSON
+ Pre-Request
+ Post-Request
+
+
+
+ {/* Body Tab Content */}
+
+
+
+
+ {
+ setCurrentModel(prev => ({
+ ...prev,
+ requestBody: newValue
+ }));
+ }}
+ />
+
+
+
+
+ {
+ 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="full"
+ />
+
-
-
- Body
- Console
-
+
+
-
-
-
-
-
-
-
-
+ {/* Response Section */}
+
+
+ Response
+ {responseData.status}
+ {responseData.time}
+ {responseData.size}
-
-
+
+
+
+ Body
+ Console
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
};
-// export default TCPClientView;
+export default TCPClientView;
diff --git a/TestMasterUI/src/components/layout/LayoutGrid.jsx b/TestMasterUI/src/components/layout/LayoutGrid.jsx
index 761f7ae..97affa3 100644
--- a/TestMasterUI/src/components/layout/LayoutGrid.jsx
+++ b/TestMasterUI/src/components/layout/LayoutGrid.jsx
@@ -1,15 +1,32 @@
-import React, { useState, useCallback } from 'react';
+import React, { useState, useCallback } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
-import { Select } from "@/components/ui/select";
-import EditableInput from '../shared/EditableInput';
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+import { ArrowUp, ArrowDown, Trash2, Plus } from "lucide-react";
+import EditableInput from "../shared/EditableInput";
+
+// shadcn table imports
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
const LayoutGrid = ({ layout, onChange }) => {
const [items, setItems] = useState(layout?.layoutItems || []);
const getLastId = useCallback(() => {
let lastId = 0;
- items.forEach(item => {
+ items.forEach((item) => {
if (item.id > lastId) {
lastId = item.id;
}
@@ -22,26 +39,26 @@ const LayoutGrid = ({ layout, onChange }) => {
id: getLastId(),
parent: 0,
level: 1,
- loutName: '',
- loutItemName: '',
- loutItemDesc: '',
+ loutName: "",
+ loutItemName: "",
+ loutItemDesc: "",
loutItemDepth: 1,
- loutItemType: 'Field',
- loutItemOccCnt: '',
- loutItemOccRef: '',
- loutItemDataType: 'String',
+ loutItemType: "Field",
+ loutItemOccCnt: "",
+ loutItemOccRef: "",
+ loutItemDataType: "String",
loutItemLength: 1,
loutItemDecimal: 0,
- loutItemDefault: '',
- loutItemMaskYn: 'N',
+ loutItemDefault: "",
+ loutItemMaskYn: "N",
loutItemMaskOffset: 0,
loutItemMaskLength: 0,
parentLoutItemIndex: 0,
expanded: true,
isLeaf: true,
- LOUTITEMPATH: '',
- value: '',
- loutItemSerno: items.length + 1
+ LOUTITEMPATH: "",
+ value: "",
+ loutItemSerno: items.length + 1,
};
const newItems = [...items, newItem];
@@ -50,201 +67,257 @@ const LayoutGrid = ({ layout, onChange }) => {
onChange?.(layout);
}, [items, layout, onChange, getLastId]);
- const handleMove = useCallback((index, direction) => {
- if ((direction === 'up' && index > 0) ||
- (direction === 'down' && index < items.length - 1)) {
- const newItems = [...items];
- const swapIndex = direction === 'up' ? index - 1 : index + 1;
- [newItems[index], newItems[swapIndex]] = [newItems[swapIndex], newItems[index]];
+ const handleMove = useCallback(
+ (index, direction) => {
+ if (
+ (direction === "up" && index > 0) ||
+ (direction === "down" && index < items.length - 1)
+ ) {
+ const newItems = [...items];
+ const swapIndex = direction === "up" ? index - 1 : index + 1;
+ [newItems[index], newItems[swapIndex]] = [
+ newItems[swapIndex],
+ newItems[index],
+ ];
- // Update loutItemSerno values
- newItems.forEach((item, idx) => {
- item.loutItemSerno = idx + 1;
- });
+ // Update loutItemSerno values
+ newItems.forEach((item, idx) => {
+ item.loutItemSerno = idx + 1;
+ });
- setItems(newItems);
- layout.layoutItems = newItems;
- onChange?.(layout);
- }
- }, [items, layout, onChange]);
+ setItems(newItems);
+ layout.layoutItems = newItems;
+ onChange?.(layout);
+ }
+ },
+ [items, layout, onChange]
+ );
- const handleDelete = useCallback((index) => {
- const newItems = items.filter((_, idx) => idx !== index);
+ const handleDelete = useCallback(
+ (index) => {
+ const newItems = items.filter((_, idx) => idx !== index);
- // Update loutItemSerno values
- newItems.forEach((item, idx) => {
- item.loutItemSerno = idx + 1;
- });
+ // Update loutItemSerno values
+ newItems.forEach((item, idx) => {
+ item.loutItemSerno = idx + 1;
+ });
- setItems(newItems);
- layout.layoutItems = newItems;
- onChange?.(layout);
- }, [items, layout, onChange]);
+ setItems(newItems);
+ layout.layoutItems = newItems;
+ onChange?.(layout);
+ },
+ [items, layout, onChange]
+ );
- const handleFieldChange = useCallback((index, field, value) => {
- const newItems = [...items];
- newItems[index][field] = value;
+ const handleFieldChange = useCallback(
+ (index, field, value) => {
+ const newItems = [...items];
+ newItems[index][field] = value;
- if (field === 'value' && newItems[index].value === undefined) {
- newItems[index].value = '';
- }
+ if (field === "value" && newItems[index].value === undefined) {
+ newItems[index].value = "";
+ }
- setItems(newItems);
- layout.layoutItems = newItems;
- onChange?.(layout);
- }, [items, layout, onChange]);
+ setItems(newItems);
+ layout.layoutItems = newItems;
+ onChange?.(layout);
+ },
+ [items, layout, onChange]
+ );
return (
-
-
-
- | # |
- ID |
- Parent |
- 항목명(영문) |
- 항목설명 |
- 깊이 |
- 아이템 유형 |
- 반복 횟수 |
- 반복 참조 필드 |
- 데이터 길이 |
- 값 |
-
- 비고
-
- |
-
-
-
- {items.map((item, index) => (
-
- |
- {item.loutItemSerno}
- |
-
- handleFieldChange(index, 'id', e.target.value)}
- className="w-full"
- />
- |
-
- handleFieldChange(index, 'parent', e.target.value)}
- className="w-full"
- />
- |
-
- handleFieldChange(index, 'loutItemName', e.target.value)}
- className="w-full"
- />
- |
-
- handleFieldChange(index, 'loutItemDesc', e.target.value)}
- className="w-full"
- />
- |
-
- handleFieldChange(index, 'loutItemDepth', parseInt(e.target.value))}
- min={1}
- className="w-full"
- />
- |
-
-
- |
-
- handleFieldChange(index, 'loutItemOccCnt', e.target.value)}
- className="w-full"
- />
- |
-
- handleFieldChange(index, 'loutItemOccRef', e.target.value)}
- className="w-full"
- />
- |
-
- handleFieldChange(index, 'loutItemLength', parseInt(e.target.value))}
- min={1}
- className="w-full"
- />
- |
-
- {item.loutItemType === 'Field' && (
- handleFieldChange(index, 'value', value)}
- />
- )}
- |
-
-
- |
);
};
diff --git a/TestMasterUI/src/components/shared/MonacoEditor.jsx b/TestMasterUI/src/components/shared/MonacoEditor.jsx
index 88633fb..a210211 100644
--- a/TestMasterUI/src/components/shared/MonacoEditor.jsx
+++ b/TestMasterUI/src/components/shared/MonacoEditor.jsx
@@ -10,10 +10,10 @@ const MonacoEditor = memo(({
onChange = () => {}
}) => {
const editorRef = useRef(null);
+ const modelRef = useRef(null);
const containerRef = useRef(null);
const subscriptionRef = useRef(null);
- // Configure editor options based on language
const getEditorOptions = (lang) => {
const baseOptions = {
automaticLayout: true,
@@ -31,7 +31,6 @@ const MonacoEditor = memo(({
}
};
- // Language-specific options
switch (lang) {
case 'json':
return {
@@ -55,97 +54,59 @@ const MonacoEditor = memo(({
}
};
- // Initialize or update editor
- const setupEditor = () => {
+ useEffect(() => {
if (!containerRef.current) return;
- // Dispose existing editor if it exists
- if (editorRef.current) {
- editorRef.current.dispose();
- }
+ const modelUri = monaco.Uri.parse(`inmemory://model-${id}`);
+ modelRef.current = monaco.editor.createModel(value, language, modelUri);
- // Create new editor with language-specific options
const options = getEditorOptions(language);
editorRef.current = monaco.editor.create(containerRef.current, {
- value,
- language,
- ...options
+ ...options,
+ model: modelRef.current
});
- // Set up change event handler
- if (subscriptionRef.current) {
- subscriptionRef.current.dispose();
- }
-
subscriptionRef.current = editorRef.current.onDidChangeModelContent(() => {
- const newValue = editorRef.current.getValue();
- onChange(newValue);
+ onChange(editorRef.current.getValue());
});
- // Format document if it's JSON
if (language === 'json' && value) {
try {
- const modelUri = monaco.Uri.parse(`inmemory://model-${id}.json`);
- const model = monaco.editor.createModel(value, 'json', modelUri);
+ monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
+ validate: true,
+ allowComments: false,
+ schemas: []
+ });
setTimeout(() => {
- monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
- validate: true,
- allowComments: false,
- schemas: []
- });
- editorRef.current.setModel(model);
+ const action = editorRef.current?.getAction('editor.action.formatDocument');
+ if (action) action.run();
}, 100);
} catch (e) {
console.warn('Failed to format JSON:', e);
}
}
- };
- // Initial setup
- useEffect(() => {
- setupEditor();
-
- // Set up resize observer
const resizeObserver = new ResizeObserver(() => {
- if (editorRef.current) {
- editorRef.current.layout();
- }
+ editorRef.current?.layout();
});
- if (containerRef.current) {
- resizeObserver.observe(containerRef.current);
- }
+ resizeObserver.observe(containerRef.current);
- // Cleanup
return () => {
resizeObserver.disconnect();
- if (subscriptionRef.current) {
- subscriptionRef.current.dispose();
- }
- if (editorRef.current) {
- editorRef.current.dispose();
+ subscriptionRef.current?.dispose();
+ editorRef.current?.dispose();
+ if (modelRef.current && !modelRef.current.isDisposed()) {
+ modelRef.current.dispose();
}
};
- }, [language]); // Reinitialize when language changes
+ }, [language]);
- // Update value if it changes externally
useEffect(() => {
- if (editorRef.current && value !== editorRef.current.getValue()) {
- editorRef.current.setValue(value);
-
- // Add formatting for JSON
- if (language === 'json' && value) {
- try {
- const action = editorRef.current.getAction('editor.action.formatDocument');
- if (action) {
- action.run();
- }
- } catch (e) {
- console.warn('Failed to format JSON:', e);
- }
- }
+ if (modelRef.current && !modelRef.current.isDisposed()) {
+ modelRef.current.setValue(value);
}
- }, [value, language]);
+ }, [value]);
return (
{
const handleKeyDown = (event) => {
if (
- event.key === SIDEBAR_KEYBOARD_SHORTCUT &&
+ event.key === shortcut &&
(event.metaKey || event.ctrlKey)
) {
event.preventDefault()
diff --git a/TestMasterUI/src/services/APIClient.js b/TestMasterUI/src/services/APIClient.js
index 5cbf37d..e7ef9e4 100644
--- a/TestMasterUI/src/services/APIClient.js
+++ b/TestMasterUI/src/services/APIClient.js
@@ -73,7 +73,7 @@ class APIClient {
return {
body: data.body,
status: data.status,
- headers: Object.fromEntries(data.headers.map(h => [h.key, h.value])),
+ headers: data.headers ? Object.fromEntries(data.headers.map(h => [h.key, h.value])) : {},
size: data.size
};
}