style(batch): FROM 카드 행 그룹화 + 컴팩트 폰트로 sparse 레이아웃 정리
이전: API 서버 URL / 인증 토큰 / 엔드포인트 / 메서드 / 데이터 배열 경로가 각각 단독 행 신규: 의미 단위로 그룹화 + 컴팩트화 - API 서버 URL (3fr) + HTTP 메서드 (1fr) 한 행 - 엔드포인트 + 데이터 배열 경로 50:50 한 행 - 인증 토큰: 라디오 → 세그먼티드 토글 버튼 그룹 + 입력을 한 행에 압축 - Label text-xs / Input h-9 text-sm 로 컴팩트 통일 vexplor_rps batch-management-new (L1417 부근) 의 컴팩트 레이아웃 패턴 참고. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -806,7 +806,7 @@ export default function BatchManagementNewPage() {
|
||||
<div className="space-y-4">
|
||||
{/* 등록된 연결 선택 — 외부 커넥션 관리에 등록한 REST API 연결을 골라 자동 호출 */}
|
||||
<div>
|
||||
<Label htmlFor="registeredRestApi" className="flex items-center gap-1.5">
|
||||
<Label htmlFor="registeredRestApi" className="flex items-center gap-1.5 text-xs">
|
||||
<span>🔗 등록된 연결</span>
|
||||
{rawResponseLoading && (
|
||||
<RefreshCw className="h-3 w-3 animate-spin text-muted-foreground" />
|
||||
@@ -816,7 +816,7 @@ export default function BatchManagementNewPage() {
|
||||
value={selectedRestApiId}
|
||||
onValueChange={applyRegisteredRestApi}
|
||||
>
|
||||
<SelectTrigger id="registeredRestApi">
|
||||
<SelectTrigger id="registeredRestApi" className="h-9 text-sm">
|
||||
<SelectValue placeholder="직접 입력 (등록된 연결 사용 안 함)" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -838,125 +838,96 @@ export default function BatchManagementNewPage() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* API 서버 URL */}
|
||||
<div>
|
||||
<Label htmlFor="fromApiUrl">API 서버 URL *</Label>
|
||||
<Input
|
||||
id="fromApiUrl"
|
||||
value={fromApiUrl}
|
||||
onChange={(e) => setFromApiUrl(e.target.value)}
|
||||
placeholder="https://api.example.com"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 인증 토큰 설정 */}
|
||||
<div>
|
||||
<Label>인증 토큰 (Authorization)</Label>
|
||||
{/* 토큰 설정 방식 선택 */}
|
||||
<div className="mt-2 flex gap-4">
|
||||
<label className="flex cursor-pointer items-center gap-1.5">
|
||||
<input
|
||||
type="radio"
|
||||
name="authTokenMode"
|
||||
value="direct"
|
||||
checked={authTokenMode === "direct"}
|
||||
onChange={() => {
|
||||
setAuthTokenMode("direct");
|
||||
setAuthServiceName("");
|
||||
}}
|
||||
className="h-3.5 w-3.5"
|
||||
/>
|
||||
<span className="text-xs">직접 입력</span>
|
||||
</label>
|
||||
<label className="flex cursor-pointer items-center gap-1.5">
|
||||
<input
|
||||
type="radio"
|
||||
name="authTokenMode"
|
||||
value="db"
|
||||
checked={authTokenMode === "db"}
|
||||
onChange={() => setAuthTokenMode("db")}
|
||||
className="h-3.5 w-3.5"
|
||||
/>
|
||||
<span className="text-xs">DB에서 선택</span>
|
||||
</label>
|
||||
</div>
|
||||
{/* 직접 입력 모드 */}
|
||||
{authTokenMode === "direct" && (
|
||||
{/* API 서버 URL + HTTP 메서드 — 한 행, URL 이 길어 7:3 비율 */}
|
||||
<div className="grid grid-cols-[3fr_1fr] gap-3">
|
||||
<div>
|
||||
<Label htmlFor="fromApiUrl" className="text-xs">API 서버 URL *</Label>
|
||||
<Input
|
||||
id="fromApiKey"
|
||||
value={fromApiKey}
|
||||
onChange={(e) => setFromApiKey(e.target.value)}
|
||||
placeholder="Bearer eyJhbGciOiJIUzI1NiIs..."
|
||||
className="mt-2"
|
||||
id="fromApiUrl"
|
||||
value={fromApiUrl}
|
||||
onChange={(e) => setFromApiUrl(e.target.value)}
|
||||
placeholder="https://api.example.com"
|
||||
className="h-9 text-sm"
|
||||
/>
|
||||
)}
|
||||
{/* DB 선택 모드 */}
|
||||
{authTokenMode === "db" && (
|
||||
<Select
|
||||
value={authServiceName || "none"}
|
||||
onValueChange={(value) => setAuthServiceName(value === "none" ? "" : value)}
|
||||
>
|
||||
<SelectTrigger className="mt-2">
|
||||
<SelectValue placeholder="서비스명 선택" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-xs">메서드</Label>
|
||||
<Select value={fromApiMethod} onValueChange={(value: any) => setFromApiMethod(value)}>
|
||||
<SelectTrigger className="h-9 text-sm">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">선택 안 함</SelectItem>
|
||||
{authServiceNames.map((name) => (
|
||||
<SelectItem key={name} value={name}>
|
||||
{name}
|
||||
</SelectItem>
|
||||
))}
|
||||
<SelectItem value="GET">GET</SelectItem>
|
||||
<SelectItem value="POST">POST</SelectItem>
|
||||
<SelectItem value="PUT">PUT</SelectItem>
|
||||
<SelectItem value="DELETE">DELETE</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
{authTokenMode === "direct"
|
||||
? "API 호출 시 Authorization 헤더에 사용할 토큰을 입력하세요."
|
||||
: "auth_tokens 테이블에서 선택한 서비스의 최신 토큰을 사용합니다."}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 엔드포인트 */}
|
||||
<div>
|
||||
<Label htmlFor="fromEndpoint">엔드포인트 *</Label>
|
||||
<Input
|
||||
id="fromEndpoint"
|
||||
value={fromEndpoint}
|
||||
onChange={(e) => setFromEndpoint(e.target.value)}
|
||||
placeholder="/api/users"
|
||||
/>
|
||||
{/* 엔드포인트 + 데이터 배열 경로 — 한 행, 50:50 */}
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<Label htmlFor="fromEndpoint" className="text-xs">엔드포인트 *</Label>
|
||||
<Input
|
||||
id="fromEndpoint"
|
||||
value={fromEndpoint}
|
||||
onChange={(e) => setFromEndpoint(e.target.value)}
|
||||
placeholder="/api/users"
|
||||
className="h-9 text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="dataArrayPath" className="text-xs">데이터 배열 경로</Label>
|
||||
<Input
|
||||
id="dataArrayPath"
|
||||
value={dataArrayPath}
|
||||
onChange={(e) => setDataArrayPath(e.target.value)}
|
||||
placeholder="resultData (비우면 자동 탐색)"
|
||||
className="h-9 text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* HTTP 메서드 */}
|
||||
{/* 인증 토큰 — 라디오 + 입력을 한 행으로 압축 */}
|
||||
<div>
|
||||
<Label>HTTP 메서드</Label>
|
||||
<Select value={fromApiMethod} onValueChange={(value: any) => setFromApiMethod(value)}>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="GET">GET (데이터 조회)</SelectItem>
|
||||
<SelectItem value="POST">POST (데이터 조회/전송)</SelectItem>
|
||||
<SelectItem value="PUT">PUT</SelectItem>
|
||||
<SelectItem value="DELETE">DELETE</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* 데이터 배열 경로 */}
|
||||
<div>
|
||||
<Label htmlFor="dataArrayPath">데이터 배열 경로</Label>
|
||||
<Input
|
||||
id="dataArrayPath"
|
||||
value={dataArrayPath}
|
||||
onChange={(e) => setDataArrayPath(e.target.value)}
|
||||
placeholder="response (예: data.items, results)"
|
||||
/>
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
API 응답에서 배열 데이터가 있는 경로를 입력하세요. 비워두면 응답 전체를 사용합니다.
|
||||
<br />
|
||||
예시: response, data.items, result.list
|
||||
</p>
|
||||
<Label className="text-xs">인증 토큰</Label>
|
||||
<div className="mt-1.5 flex items-center gap-2">
|
||||
<div className="flex shrink-0 overflow-hidden rounded-md border">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => { setAuthTokenMode("direct"); setAuthServiceName(""); }}
|
||||
className={`px-2.5 py-1.5 text-xs ${authTokenMode === "direct" ? "bg-primary text-primary-foreground" : "bg-background hover:bg-muted"}`}
|
||||
>직접 입력</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setAuthTokenMode("db")}
|
||||
className={`px-2.5 py-1.5 text-xs ${authTokenMode === "db" ? "bg-primary text-primary-foreground" : "bg-background hover:bg-muted"}`}
|
||||
>DB에서 선택</button>
|
||||
</div>
|
||||
{authTokenMode === "direct" ? (
|
||||
<Input
|
||||
id="fromApiKey"
|
||||
value={fromApiKey}
|
||||
onChange={(e) => setFromApiKey(e.target.value)}
|
||||
placeholder="Bearer eyJhbGciOiJIUzI1NiIs..."
|
||||
className="h-9 flex-1 text-sm"
|
||||
/>
|
||||
) : (
|
||||
<Select value={authServiceName || "none"} onValueChange={(value) => setAuthServiceName(value === "none" ? "" : value)}>
|
||||
<SelectTrigger className="h-9 flex-1 text-sm">
|
||||
<SelectValue placeholder="서비스명 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">선택 안 함</SelectItem>
|
||||
{authServiceNames.map((name) => (
|
||||
<SelectItem key={name} value={name}>{name}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Request Body (POST/PUT/DELETE용) */}
|
||||
|
||||
Reference in New Issue
Block a user