레이아웃 정리

This commit is contained in:
현성필
2025-01-25 20:20:58 +09:00
parent 73b4bb96f6
commit 05f00337df
8 changed files with 147 additions and 78 deletions
+33 -28
View File
@@ -4,6 +4,7 @@ import APITabs from '@/components/api-tester/APITabs';
import {useAPI} from '@/providers/APIProvider.jsx';
import ScenarioNavigation from '@/components/api-tester/ScenarioNavigation.jsx';
import ScenarioEditor from '@/components/api-tester/ScenarioEditor.jsx';
import {SidebarProvider} from '@/components/ui/sidebar.jsx';
const MainContentArea = ({isApiMode, toggleMode}) => {
// Lift tab state up
@@ -117,9 +118,9 @@ const APIMode = ({
);
return (
<div className="api-mode flex h-full w-full">
<div className="navigation w-[250px] flex-shrink-0 border-r bg-background">
{/* APINavigation 고정 */}
<SidebarProvider>
<div className="api-mode flex flex-1 h-[calc(100vh-57px)]">
<div className="navigation-sidebar min-w-[0px] border-r bg-gray-100">
<APINavigation
contextPath="/"
collections={collections} // Pass collections to APINavigation
@@ -132,7 +133,7 @@ const APIMode = ({
onToggleMode={onToggleMode}
/>
</div>
<div className="flex-1 h-full w-full min-w-0 overflow-hidden">
<div className="editor flex-1 h-[calc(100vh-57px)] overflow-hidden">
{apiRequests.length === 0 ? (
renderFallback() // Render fallback when no tabs exist
) : (
@@ -145,40 +146,44 @@ const APIMode = ({
)}
</div>
</div>
</SidebarProvider>
);
};
// 시나리오 모드
const ScenarioMode = ({
isApiMode,
onToggleMode,
onToggleMode
}) => {
let handleApiSelect = () => {};
let handleApiSelect = () => {
};
return (
<div className="scenario-mode flex flex-1 h-[calc(100vh-57px)]">
<div className="navigation-sidebar min-w-[200px] border-r bg-gray-100">
<ScenarioNavigation
contextPath="/"
side="left"
isApiMode={isApiMode}
onToggleMode={onToggleMode}
/>
<SidebarProvider>
<div className="scenario-mode flex flex-1 h-[calc(100vh-57px)]">
<div className="navigation-sidebar min-w-[0px] border-r bg-gray-100">
<ScenarioNavigation
contextPath="/"
side="left"
isApiMode={isApiMode}
onToggleMode={onToggleMode}
/>
</div>
<div className="editor flex-1 h-[calc(100vh-57px)] overflow-hidden">
<ScenarioEditor/>
</div>
<div className="navigation-sidebar min-w-[200px] border-l bg-gray-100">
<APINavigation
contextPath="/"
side="right"
onApiSelect={handleApiSelect}
isApiMode={isApiMode}
onToggleMode={onToggleMode}
/>
</div>
</div>
<div className="editor flex-1 h-[calc(100vh-57px)] overflow-hidden">
<ScenarioEditor />
</div>
<div className="navigation-sidebar min-w-[200px] border-l bg-gray-100">
<APINavigation
contextPath="/"
side="right"
onApiSelect={handleApiSelect}
isApiMode={isApiMode}
onToggleMode={onToggleMode}
/>
</div>
</div>
</SidebarProvider>
);
};
export default MainContentArea;
export default MainContentArea;
@@ -250,7 +250,7 @@ const APINavigation = ({
};
return (
<SidebarProvider>
<>
<Sidebar side={side} className="py-16">
{side === 'left' && (
<SidebarHeader>
@@ -300,7 +300,7 @@ const APINavigation = ({
onSubmit={handleRenameApi}
api={apiToRename}
/>
</SidebarProvider>
</>
);
};
@@ -69,28 +69,58 @@ const DraggableApiItem = ({ api, collectionId, onApiAction }) => {
draggable
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
className={`cursor-move transition-colors duration-200 ${
isDragging ? 'opacity-50 bg-accent' : ''
}`}
className={`cursor-move transition-colors duration-200 ${isDragging ? 'opacity-50 bg-accent' : ''}`}
>
<SidebarMenuSubButton
onClick={(e) => {
// Prevent click when dragging ends
if (!isDragging) {
onApiAction.open(collectionId, api.id);
}
}}
className="w-full justify-start gap-2"
asChild
>
<div className="w-full flex items-center gap-2 min-w-0 pr-2">
{/*<SidebarMenuSubButton*/}
{/* onClick={(e) => {*/}
{/* // Prevent click when dragging ends*/}
{/* if (!isDragging) {*/}
{/* onApiAction.open(collectionId, api.id);*/}
{/* }*/}
{/* }}*/}
{/* className="w-full justify-start gap-2"*/}
{/* asChild*/}
{/*>*/}
{/* <div className="w-full flex items-center gap-2 min-w-0 pr-2">*/}
{/* <FileText className="h-4 w-4 flex-shrink-0"/>*/}
{/* <Tooltip>*/}
{/* <TooltipTrigger asChild>*/}
{/* <span className="truncate flex-1">*/}
{/* {api.method && <span className="font-medium text-primary">{api.method} </span>}*/}
{/* {api.name}*/}
{/* </span>*/}
{/* </TooltipTrigger>*/}
{/* <TooltipContent>*/}
{/* <div className="flex flex-col gap-1">*/}
{/* <p className="font-medium">{api.name}</p>*/}
{/* {api.path && <p className="text-xs text-muted-foreground">{api.path}</p>}*/}
{/* </div>*/}
{/* </TooltipContent>*/}
{/* </Tooltip>*/}
{/* <SidebarMenuAction*/}
{/* className="p-0 m-0 flex-shrink-0"*/}
{/* onClick={(e) => e.stopPropagation()}*/}
{/* >*/}
{/* <ApiDropdownMenu*/}
{/* onOpen={() => onApiAction.open(collectionId, api.id)}*/}
{/* onRename={() => onApiAction.rename(collectionId, api.id)}*/}
{/* onDelete={() => onApiAction.delete(collectionId, api.id)}*/}
{/* />*/}
{/* </SidebarMenuAction>*/}
{/* </div>*/}
{/*</SidebarMenuSubButton>*/}
<div className="w-full flex items-center gap-2 min-w-0 pr-2">
<div
onClick={() => !isDragging && onApiAction.open(collectionId, api.id)}
className="flex items-center gap-2 flex-1 min-w-0 cursor-pointer"
>
<FileText className="h-4 w-4 flex-shrink-0"/>
<Tooltip>
<TooltipTrigger asChild>
<span className="truncate flex-1">
{api.method && <span className="font-medium text-primary">{api.method} </span>}
{api.name}
</span>
<span className="truncate">
{api.method && <span className="font-medium text-primary">{api.method} </span>}
{api.name}
</span>
</TooltipTrigger>
<TooltipContent>
<div className="flex flex-col gap-1">
@@ -99,18 +129,28 @@ const DraggableApiItem = ({ api, collectionId, onApiAction }) => {
</div>
</TooltipContent>
</Tooltip>
<SidebarMenuAction
className="p-0 m-0 flex-shrink-0"
onClick={(e) => e.stopPropagation()}
>
<ApiDropdownMenu
onOpen={() => onApiAction.open(collectionId, api.id)}
onRename={() => onApiAction.rename(collectionId, api.id)}
onDelete={() => onApiAction.delete(collectionId, api.id)}
/>
</SidebarMenuAction>
</div>
</SidebarMenuSubButton>
<DropdownMenu>
<DropdownMenuTrigger data-dropdown-trigger="true" className="p-0 h-auto">
<div className="hover:bg-accent rounded-sm">
<MoreHorizontal className="h-4 w-4"/>
</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => onApiAction.open(collectionId, api.id)}>
Open
</DropdownMenuItem>
<DropdownMenuSeparator/>
<DropdownMenuItem onClick={() => onApiAction.rename(collectionId, api.id)}>
Rename
</DropdownMenuItem>
<DropdownMenuItem className="text-destructive" onClick={() => onApiAction.delete(collectionId, api.id)}>
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</SidebarMenuSubItem>
);
};
@@ -1,10 +1,14 @@
import {Button} from '@/components/ui/button.jsx';
import {Pause, Play, RotateCcw, Save, SkipForward} from 'lucide-react';
import React from 'react';
import {SidebarTrigger} from '@/components/ui/sidebar.jsx';
import {Separator} from '@/components/ui/separator.jsx';
const ScenarioMenuBar = ({ onSave, onStepRun, onFullRun, onPause, onReset }) => {
return (
<div className="flex items-center gap-2 p-4 border-b">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-4" />
<Button
variant="outline"
size="sm"
@@ -8,7 +8,7 @@ import {
SidebarMenu,
SidebarMenuItem,
SidebarMenuButton,
SidebarMenuAction, SidebarHeader
SidebarMenuAction, SidebarHeader,
} from '@/components/ui/sidebar';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Alert, AlertDescription } from '@/components/ui/alert';
@@ -167,7 +167,6 @@ const ScenarioNavigation = ({
};
return (
<SidebarProvider>
<Sidebar side={side} className="py-16">
<SidebarHeader>
<Button
@@ -186,7 +185,6 @@ const ScenarioNavigation = ({
scenarioName={scenarioToDelete?.name || ''}
/>
</Sidebar>
</SidebarProvider>
);
};
@@ -1,4 +1,4 @@
import React, {useState} from 'react';
import React, {useEffect, 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';
@@ -11,6 +11,8 @@ import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from '@/c
import {useAPI} from '@/providers/APIProvider';
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';
const HTTP_METHODS = [
'POST', 'GET', 'PUT', 'DELETE', 'CONNECT',
@@ -40,17 +42,26 @@ export const HTTPClientView = ({model, index}) => {
postRequestScript: currentModel.postRequestScript || ''
});
useEffect(() => {
if (currentModel.server) {
const selectedServer = getServer(currentModel.server);
if (selectedServer) {
setBasePath(selectedServer.basePath || '');
}
}
}, [currentModel.server]);
const handleServerChange = (serverId) => {
const selectedServer = getServer(serverId);
// const selectedServer = getServer(serverId);
setCurrentModel(prev => ({
...prev,
server: serverId
}));
if (selectedServer) {
setBasePath(selectedServer.basePath || '');
}
// if (selectedServer) {
// setBasePath(selectedServer.basePath || '');
// }
};
const handleHeadersChange = (headers) => {
@@ -191,6 +202,8 @@ export const HTTPClientView = ({model, index}) => {
<div className="flex flex-col h-full p-4 gap-4">
{/* Request Header Section */}
<div className="flex items-center gap-2">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-4" />
<div>
<span>{currentModel.collectionName}</span>
{' > '}
@@ -212,17 +225,26 @@ export const HTTPClientView = ({model, index}) => {
))}
</SelectContent>
</Select>
<ServerManager
onServerChange={handleServerChange}
defaultValue={currentModel.server}
/>
<Input value={basePath} readOnly className="w-40"/>
<EditableInput value={currentModel.path} onChange={handlePathChange} className="flex-1" />
<Button disabled={isLoading} onClick={handleSendRequest}>
{isLoading ? 'Sending...' : 'Send'}
</Button>
<Button variant="secondary" onClick={handleSave}>Save</Button>
<div className="shrink-0">
<ServerManager
onServerChange={handleServerChange}
defaultValue={currentModel.server}
/>
</div>
<Input value={basePath} readOnly className="w-40 shrink-0"/>
<div className="min-w-0 flex-1">
<EditableInput
value={currentModel.path}
onChange={handlePathChange}
className="w-full"
/>
</div>
<div className="flex gap-2 shrink-0">
<Button disabled={isLoading} onClick={handleSendRequest}>
{isLoading ? 'Sending...' : 'Send'}
</Button>
<Button variant="secondary" onClick={handleSave}>Save</Button>
</div>
</div>
{/* Request/Response Container */}