전체적인 스타일 수정
This commit is contained in:
@@ -260,45 +260,55 @@ export function ExternalCallConfigModal({ isOpen, onClose, onSave, editingConfig
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={onClose}>
|
||||
<DialogContent className="max-h-[90vh] max-w-2xl overflow-y-auto">
|
||||
<DialogContent className="max-w-[95vw] sm:max-w-2xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editingConfig ? "외부 호출 설정 편집" : "새 외부 호출 설정"}</DialogTitle>
|
||||
<DialogTitle className="text-base sm:text-lg">
|
||||
{editingConfig ? "외부 호출 설정 편집" : "새 외부 호출 설정"}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="max-h-[60vh] space-y-4 overflow-y-auto sm:space-y-6">
|
||||
{/* 기본 정보 */}
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-3 sm:space-y-4">
|
||||
<div>
|
||||
<Label htmlFor="config_name">설정 이름 *</Label>
|
||||
<Label htmlFor="config_name" className="text-xs sm:text-sm">
|
||||
설정 이름 *
|
||||
</Label>
|
||||
<Input
|
||||
id="config_name"
|
||||
value={formData.config_name}
|
||||
onChange={(e) => setFormData((prev) => ({ ...prev, config_name: e.target.value }))}
|
||||
placeholder="예: 개발팀 Discord"
|
||||
className="h-8 text-xs sm:h-10 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="description">설명</Label>
|
||||
<Label htmlFor="description" className="text-xs sm:text-sm">
|
||||
설명
|
||||
</Label>
|
||||
<Textarea
|
||||
id="description"
|
||||
value={formData.description}
|
||||
onChange={(e) => setFormData((prev) => ({ ...prev, description: e.target.value }))}
|
||||
placeholder="이 외부 호출 설정에 대한 설명을 입력하세요."
|
||||
rows={2}
|
||||
className="text-xs sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 sm:gap-4">
|
||||
<div>
|
||||
<Label htmlFor="call_type">호출 타입 *</Label>
|
||||
<Label htmlFor="call_type" className="text-xs sm:text-sm">
|
||||
호출 타입 *
|
||||
</Label>
|
||||
<Select value={formData.call_type} onValueChange={handleCallTypeChange}>
|
||||
<SelectTrigger>
|
||||
<SelectTrigger className="h-8 text-xs sm:h-10 sm:text-sm">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{CALL_TYPE_OPTIONS.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<SelectItem key={option.value} value={option.value} className="text-xs sm:text-sm">
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
@@ -307,17 +317,19 @@ export function ExternalCallConfigModal({ isOpen, onClose, onSave, editingConfig
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="is_active">상태</Label>
|
||||
<Label htmlFor="is_active" className="text-xs sm:text-sm">
|
||||
상태
|
||||
</Label>
|
||||
<Select
|
||||
value={formData.is_active}
|
||||
onValueChange={(value) => setFormData((prev) => ({ ...prev, is_active: value }))}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectTrigger className="h-8 text-xs sm:h-10 sm:text-sm">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{ACTIVE_STATUS_OPTIONS.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<SelectItem key={option.value} value={option.value} className="text-xs sm:text-sm">
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
@@ -329,19 +341,21 @@ export function ExternalCallConfigModal({ isOpen, onClose, onSave, editingConfig
|
||||
|
||||
{/* REST API 설정 */}
|
||||
{formData.call_type === "rest-api" && (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-3 sm:space-y-4">
|
||||
<div>
|
||||
<Label htmlFor="api_type">API 타입 *</Label>
|
||||
<Label htmlFor="api_type" className="text-xs sm:text-sm">
|
||||
API 타입 *
|
||||
</Label>
|
||||
<Select
|
||||
value={formData.api_type}
|
||||
onValueChange={(value) => setFormData((prev) => ({ ...prev, api_type: value }))}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectTrigger className="h-8 text-xs sm:h-10 sm:text-sm">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{API_TYPE_OPTIONS.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<SelectItem key={option.value} value={option.value} className="text-xs sm:text-sm">
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
@@ -351,33 +365,42 @@ export function ExternalCallConfigModal({ isOpen, onClose, onSave, editingConfig
|
||||
|
||||
{/* Discord 설정 */}
|
||||
{formData.api_type === "discord" && (
|
||||
<div className="space-y-3 rounded-lg border p-4">
|
||||
<h4 className="font-medium">Discord 설정</h4>
|
||||
<div className="space-y-3 rounded-lg border bg-muted/20 p-3 sm:p-4">
|
||||
<h4 className="text-xs font-semibold sm:text-sm">Discord 설정</h4>
|
||||
<div>
|
||||
<Label htmlFor="discord_webhook">웹훅 URL *</Label>
|
||||
<Label htmlFor="discord_webhook" className="text-xs sm:text-sm">
|
||||
웹훅 URL *
|
||||
</Label>
|
||||
<Input
|
||||
id="discord_webhook"
|
||||
value={discordSettings.webhookUrl}
|
||||
onChange={(e) => setDiscordSettings((prev) => ({ ...prev, webhookUrl: e.target.value }))}
|
||||
placeholder="https://discord.com/api/webhooks/..."
|
||||
className="h-8 text-xs sm:h-10 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="discord_username">사용자명</Label>
|
||||
<Label htmlFor="discord_username" className="text-xs sm:text-sm">
|
||||
사용자명
|
||||
</Label>
|
||||
<Input
|
||||
id="discord_username"
|
||||
value={discordSettings.username}
|
||||
onChange={(e) => setDiscordSettings((prev) => ({ ...prev, username: e.target.value }))}
|
||||
placeholder="ERP 시스템"
|
||||
className="h-8 text-xs sm:h-10 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="discord_avatar">아바타 URL</Label>
|
||||
<Label htmlFor="discord_avatar" className="text-xs sm:text-sm">
|
||||
아바타 URL
|
||||
</Label>
|
||||
<Input
|
||||
id="discord_avatar"
|
||||
value={discordSettings.avatarUrl}
|
||||
onChange={(e) => setDiscordSettings((prev) => ({ ...prev, avatarUrl: e.target.value }))}
|
||||
placeholder="https://example.com/avatar.png"
|
||||
className="h-8 text-xs sm:h-10 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -385,33 +408,42 @@ export function ExternalCallConfigModal({ isOpen, onClose, onSave, editingConfig
|
||||
|
||||
{/* Slack 설정 */}
|
||||
{formData.api_type === "slack" && (
|
||||
<div className="space-y-3 rounded-lg border p-4">
|
||||
<h4 className="font-medium">Slack 설정</h4>
|
||||
<div className="space-y-3 rounded-lg border bg-muted/20 p-3 sm:p-4">
|
||||
<h4 className="text-xs font-semibold sm:text-sm">Slack 설정</h4>
|
||||
<div>
|
||||
<Label htmlFor="slack_webhook">웹훅 URL *</Label>
|
||||
<Label htmlFor="slack_webhook" className="text-xs sm:text-sm">
|
||||
웹훅 URL *
|
||||
</Label>
|
||||
<Input
|
||||
id="slack_webhook"
|
||||
value={slackSettings.webhookUrl}
|
||||
onChange={(e) => setSlackSettings((prev) => ({ ...prev, webhookUrl: e.target.value }))}
|
||||
placeholder="https://hooks.slack.com/services/..."
|
||||
className="h-8 text-xs sm:h-10 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="slack_channel">채널</Label>
|
||||
<Label htmlFor="slack_channel" className="text-xs sm:text-sm">
|
||||
채널
|
||||
</Label>
|
||||
<Input
|
||||
id="slack_channel"
|
||||
value={slackSettings.channel}
|
||||
onChange={(e) => setSlackSettings((prev) => ({ ...prev, channel: e.target.value }))}
|
||||
placeholder="#general"
|
||||
className="h-8 text-xs sm:h-10 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="slack_username">사용자명</Label>
|
||||
<Label htmlFor="slack_username" className="text-xs sm:text-sm">
|
||||
사용자명
|
||||
</Label>
|
||||
<Input
|
||||
id="slack_username"
|
||||
value={slackSettings.username}
|
||||
onChange={(e) => setSlackSettings((prev) => ({ ...prev, username: e.target.value }))}
|
||||
placeholder="ERP Bot"
|
||||
className="h-8 text-xs sm:h-10 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -419,25 +451,31 @@ export function ExternalCallConfigModal({ isOpen, onClose, onSave, editingConfig
|
||||
|
||||
{/* 카카오톡 설정 */}
|
||||
{formData.api_type === "kakao-talk" && (
|
||||
<div className="space-y-3 rounded-lg border p-4">
|
||||
<h4 className="font-medium">카카오톡 설정</h4>
|
||||
<div className="space-y-3 rounded-lg border bg-muted/20 p-3 sm:p-4">
|
||||
<h4 className="text-xs font-semibold sm:text-sm">카카오톡 설정</h4>
|
||||
<div>
|
||||
<Label htmlFor="kakao_token">액세스 토큰 *</Label>
|
||||
<Label htmlFor="kakao_token" className="text-xs sm:text-sm">
|
||||
액세스 토큰 *
|
||||
</Label>
|
||||
<Input
|
||||
id="kakao_token"
|
||||
type="password"
|
||||
value={kakaoSettings.accessToken}
|
||||
onChange={(e) => setKakaoSettings((prev) => ({ ...prev, accessToken: e.target.value }))}
|
||||
placeholder="카카오 API 액세스 토큰"
|
||||
className="h-8 text-xs sm:h-10 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="kakao_template">템플릿 ID</Label>
|
||||
<Label htmlFor="kakao_template" className="text-xs sm:text-sm">
|
||||
템플릿 ID
|
||||
</Label>
|
||||
<Input
|
||||
id="kakao_template"
|
||||
value={kakaoSettings.templateId}
|
||||
onChange={(e) => setKakaoSettings((prev) => ({ ...prev, templateId: e.target.value }))}
|
||||
placeholder="template_001"
|
||||
className="h-8 text-xs sm:h-10 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -445,54 +483,65 @@ export function ExternalCallConfigModal({ isOpen, onClose, onSave, editingConfig
|
||||
|
||||
{/* 일반 API 설정 */}
|
||||
{formData.api_type === "generic" && (
|
||||
<div className="space-y-3 rounded-lg border p-4">
|
||||
<h4 className="font-medium">일반 API 설정</h4>
|
||||
<div className="space-y-3 rounded-lg border bg-muted/20 p-3 sm:p-4">
|
||||
<h4 className="text-xs font-semibold sm:text-sm">일반 API 설정</h4>
|
||||
<div>
|
||||
<Label htmlFor="generic_url">API URL *</Label>
|
||||
<Label htmlFor="generic_url" className="text-xs sm:text-sm">
|
||||
API URL *
|
||||
</Label>
|
||||
<Input
|
||||
id="generic_url"
|
||||
value={genericSettings.url}
|
||||
onChange={(e) => setGenericSettings((prev) => ({ ...prev, url: e.target.value }))}
|
||||
placeholder="https://api.example.com/webhook"
|
||||
className="h-8 text-xs sm:h-10 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 sm:gap-4">
|
||||
<div>
|
||||
<Label htmlFor="generic_method">HTTP 메서드</Label>
|
||||
<Label htmlFor="generic_method" className="text-xs sm:text-sm">
|
||||
HTTP 메서드
|
||||
</Label>
|
||||
<Select
|
||||
value={genericSettings.method}
|
||||
onValueChange={(value) => setGenericSettings((prev) => ({ ...prev, method: value }))}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectTrigger className="h-8 text-xs sm:h-10 sm:text-sm">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="GET">GET</SelectItem>
|
||||
<SelectItem value="POST">POST</SelectItem>
|
||||
<SelectItem value="PUT">PUT</SelectItem>
|
||||
<SelectItem value="DELETE">DELETE</SelectItem>
|
||||
<SelectItem value="GET" className="text-xs sm:text-sm">GET</SelectItem>
|
||||
<SelectItem value="POST" className="text-xs sm:text-sm">POST</SelectItem>
|
||||
<SelectItem value="PUT" className="text-xs sm:text-sm">PUT</SelectItem>
|
||||
<SelectItem value="DELETE" className="text-xs sm:text-sm">DELETE</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="generic_timeout">타임아웃 (ms)</Label>
|
||||
<Label htmlFor="generic_timeout" className="text-xs sm:text-sm">
|
||||
타임아웃 (ms)
|
||||
</Label>
|
||||
<Input
|
||||
id="generic_timeout"
|
||||
type="number"
|
||||
value={genericSettings.timeout}
|
||||
onChange={(e) => setGenericSettings((prev) => ({ ...prev, timeout: e.target.value }))}
|
||||
placeholder="30000"
|
||||
className="h-8 text-xs sm:h-10 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="generic_headers">헤더 (JSON)</Label>
|
||||
<Label htmlFor="generic_headers" className="text-xs sm:text-sm">
|
||||
헤더 (JSON)
|
||||
</Label>
|
||||
<Textarea
|
||||
id="generic_headers"
|
||||
value={genericSettings.headers}
|
||||
onChange={(e) => setGenericSettings((prev) => ({ ...prev, headers: e.target.value }))}
|
||||
placeholder='{"Content-Type": "application/json"}'
|
||||
rows={3}
|
||||
className="text-xs sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -502,17 +551,26 @@ export function ExternalCallConfigModal({ isOpen, onClose, onSave, editingConfig
|
||||
|
||||
{/* 다른 호출 타입들 (이메일, FTP, 큐) */}
|
||||
{formData.call_type !== "rest-api" && (
|
||||
<div className="text-muted-foreground rounded-lg border p-4 text-center">
|
||||
<div className="rounded-lg border bg-muted/20 p-3 text-center text-xs text-muted-foreground sm:p-4 sm:text-sm">
|
||||
{formData.call_type} 타입의 설정은 아직 구현되지 않았습니다.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={onClose} disabled={loading}>
|
||||
<DialogFooter className="gap-2 sm:gap-0">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={onClose}
|
||||
disabled={loading}
|
||||
className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm"
|
||||
>
|
||||
취소
|
||||
</Button>
|
||||
<Button onClick={handleSave} disabled={loading}>
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
disabled={loading}
|
||||
className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm"
|
||||
>
|
||||
{loading ? "저장 중..." : editingConfig ? "수정" : "생성"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
||||
Reference in New Issue
Block a user