feat: 테이블 테두리 및 라운드 제거, 검색 필터 제목 제거
- 모든 테이블 컴포넌트의 외곽 테두리(border) 제거 - 테이블 컨테이너의 라운드(rounded-lg) 제거 - 테이블 행 구분선(border-b)은 유지하여 데이터 구분 - FlowWidget과 TableListComponent에 동일한 스타일 적용 - 검색 필터 영역의 회색 배경(bg-muted/30) 제거 - 검색 필터 제목 제거 - AdvancedSearchFilters 컴포넌트의 '검색 필터' 제목 제거
This commit is contained in:
@@ -94,25 +94,27 @@ const ActionConfigStep: React.FC<ActionConfigStepProps> = ({
|
||||
|
||||
<CardContent className="flex h-full flex-col overflow-hidden p-0">
|
||||
<Tabs defaultValue="config" className="flex h-full flex-col">
|
||||
<div className="flex-shrink-0 border-b px-4 pt-4">
|
||||
<div className="flex-shrink-0 border-b px-3 pt-3 sm:px-4 sm:pt-4">
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
<TabsTrigger value="config" className="flex items-center gap-2">
|
||||
<Settings className="h-4 w-4" />
|
||||
액션 설정
|
||||
<TabsTrigger value="config" className="flex items-center gap-1 text-xs sm:gap-2 sm:text-sm">
|
||||
<Settings className="h-3 w-3 sm:h-4 sm:w-4" />
|
||||
<span className="hidden sm:inline">액션 설정</span>
|
||||
<span className="sm:hidden">설정</span>
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="visualization" className="flex items-center gap-2">
|
||||
<Eye className="h-4 w-4" />
|
||||
흐름 미리보기
|
||||
<TabsTrigger value="visualization" className="flex items-center gap-1 text-xs sm:gap-2 sm:text-sm">
|
||||
<Eye className="h-3 w-3 sm:h-4 sm:w-4" />
|
||||
<span className="hidden sm:inline">흐름 미리보기</span>
|
||||
<span className="sm:hidden">미리보기</span>
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
{/* 액션 설정 탭 */}
|
||||
<TabsContent value="config" className="mt-0 flex-1 overflow-y-auto p-4">
|
||||
<div className="space-y-6">
|
||||
<TabsContent value="config" className="mt-0 flex-1 overflow-y-auto p-3 sm:p-4">
|
||||
<div className="space-y-4 sm:space-y-6">
|
||||
{/* 액션 타입 선택 */}
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-lg font-semibold">액션 타입</h3>
|
||||
<div className="space-y-2 sm:space-y-3">
|
||||
<h3 className="text-base font-semibold sm:text-lg">액션 타입</h3>
|
||||
<Select value={actionType} onValueChange={actions.setActionType}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="액션 타입을 선택하세요" />
|
||||
@@ -168,9 +170,9 @@ const ActionConfigStep: React.FC<ActionConfigStepProps> = ({
|
||||
|
||||
{/* INSERT 액션 안내 */}
|
||||
{actionType === "insert" && (
|
||||
<div className="rounded-lg border border-green-200 bg-green-50 p-4">
|
||||
<h4 className="mb-2 text-sm font-medium text-green-800">INSERT 액션</h4>
|
||||
<p className="text-sm text-green-700">
|
||||
<div className="rounded-lg border border-success/20 bg-success/10 p-4">
|
||||
<h4 className="mb-2 text-sm font-medium text-success">INSERT 액션</h4>
|
||||
<p className="text-sm text-success/80">
|
||||
INSERT 액션은 별도의 실행 조건이 필요하지 않습니다. 매핑된 모든 데이터가 새로운 레코드로 삽입됩니다.
|
||||
</p>
|
||||
</div>
|
||||
@@ -222,11 +224,11 @@ const ActionConfigStep: React.FC<ActionConfigStepProps> = ({
|
||||
</Tabs>
|
||||
|
||||
{/* 하단 네비게이션 */}
|
||||
<div className="flex-shrink-0 border-t bg-white p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Button variant="outline" onClick={onBack} className="flex items-center gap-2">
|
||||
<div className="flex-shrink-0 border-t bg-background p-3 sm:p-4">
|
||||
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||||
<Button variant="outline" onClick={onBack} className="flex items-center gap-2 w-full sm:w-auto">
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
이전: 제어 조건
|
||||
<span className="text-xs sm:text-sm">이전: 제어 조건</span>
|
||||
</Button>
|
||||
|
||||
<div className="flex gap-2">
|
||||
|
||||
@@ -134,9 +134,9 @@ const ControlConditionStep: React.FC<ControlConditionStepProps> = ({ state, acti
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
{isCompleted ? (
|
||||
<CheckCircle className="h-5 w-5 text-green-600" />
|
||||
<CheckCircle className="h-5 w-5 text-success" />
|
||||
) : (
|
||||
<AlertCircle className="h-5 w-5 text-orange-500" />
|
||||
<AlertCircle className="h-5 w-5 text-warning" />
|
||||
)}
|
||||
4단계: 제어 실행 조건
|
||||
</CardTitle>
|
||||
@@ -146,11 +146,11 @@ const ControlConditionStep: React.FC<ControlConditionStepProps> = ({ state, acti
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="flex h-full flex-col overflow-hidden p-0">
|
||||
<div className="min-h-0 flex-1 space-y-6 overflow-y-auto p-4">
|
||||
<div className="min-h-0 flex-1 space-y-4 overflow-y-auto p-3 sm:space-y-6 sm:p-4">
|
||||
{/* 제어 실행 조건 안내 */}
|
||||
<div className="rounded-lg border border-primary/20 bg-accent p-4">
|
||||
<h4 className="mb-2 text-sm font-medium text-blue-800">제어 실행 조건이란?</h4>
|
||||
<div className="space-y-1 text-sm text-blue-700">
|
||||
<div className="rounded-lg border border-primary/20 bg-primary/5 p-4">
|
||||
<h4 className="mb-2 text-sm font-medium text-primary">제어 실행 조건이란?</h4>
|
||||
<div className="space-y-1 text-sm text-primary/80">
|
||||
<p>
|
||||
• <strong>전체 제어의 트리거 조건</strong>을 설정합니다
|
||||
</p>
|
||||
@@ -363,7 +363,7 @@ const ControlConditionStep: React.FC<ControlConditionStepProps> = ({ state, acti
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => actions.deleteControlCondition(index)}
|
||||
className="text-destructive hover:text-red-700"
|
||||
className="text-destructive hover:text-destructive/80"
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -406,9 +406,9 @@ const ControlConditionStep: React.FC<ControlConditionStepProps> = ({ state, acti
|
||||
|
||||
{/* 컬럼 정보 로드 실패 시 안내 */}
|
||||
{fromColumns.length === 0 && toColumns.length === 0 && controlConditions.length === 0 && (
|
||||
<div className="rounded-lg border border-yellow-200 bg-yellow-50 p-4">
|
||||
<h4 className="mb-2 text-sm font-medium text-yellow-800">컬럼 정보를 불러올 수 없습니다</h4>
|
||||
<div className="space-y-2 text-sm text-yellow-700">
|
||||
<div className="rounded-lg border border-warning/20 bg-warning/10 p-4">
|
||||
<h4 className="mb-2 text-sm font-medium text-warning">컬럼 정보를 불러올 수 없습니다</h4>
|
||||
<div className="space-y-2 text-sm text-warning/80">
|
||||
<p>• 외부 데이터베이스 연결에 문제가 있을 수 있습니다</p>
|
||||
<p>• 조건 없이 진행하면 항상 실행됩니다</p>
|
||||
<p>• 나중에 수동으로 조건을 추가할 수 있습니다</p>
|
||||
@@ -451,15 +451,15 @@ const ControlConditionStep: React.FC<ControlConditionStepProps> = ({ state, acti
|
||||
</div>
|
||||
|
||||
{/* 하단 네비게이션 */}
|
||||
<div className="flex-shrink-0 border-t bg-white p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Button variant="outline" onClick={onBack} className="flex items-center gap-2">
|
||||
<div className="flex-shrink-0 border-t bg-background p-3 sm:p-4">
|
||||
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||||
<Button variant="outline" onClick={onBack} className="flex items-center gap-2 w-full sm:w-auto">
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
이전
|
||||
<span className="text-xs sm:text-sm">이전</span>
|
||||
</Button>
|
||||
|
||||
<Button onClick={onNext} disabled={!canProceed} className="flex items-center gap-2">
|
||||
다음: 액션 설정
|
||||
<Button onClick={onNext} disabled={!canProceed} className="flex items-center gap-2 w-full sm:w-auto">
|
||||
<span className="text-xs sm:text-sm">다음: 액션 설정</span>
|
||||
<CheckCircle className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -63,17 +63,17 @@ export const DataflowVisualization: React.FC<DataflowVisualizationProps> = ({ st
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-6 p-6">
|
||||
<div className="space-y-4 p-4 sm:space-y-6 sm:p-6">
|
||||
{/* 헤더 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-sm text-gray-600">전체 데이터 흐름을 한눈에 확인</p>
|
||||
<Badge variant={isComplete ? "default" : "secondary"} className="text-sm">
|
||||
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||||
<p className="text-xs sm:text-sm text-muted-foreground">전체 데이터 흐름을 한눈에 확인</p>
|
||||
<Badge variant={isComplete ? "default" : "secondary"} className="text-xs sm:text-sm w-fit">
|
||||
{isComplete ? "✅ 설정 완료" : "⚠️ 설정 필요"}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
{/* Sankey 다이어그램 */}
|
||||
<div className="relative flex items-center justify-center py-12">
|
||||
<div className="relative flex flex-col items-center justify-center gap-6 py-8 sm:flex-row sm:gap-0 sm:py-12">
|
||||
{/* 연결선 레이어 */}
|
||||
<svg className="absolute inset-0 h-full w-full" style={{ zIndex: 0 }}>
|
||||
{/* 소스 → 조건 선 */}
|
||||
@@ -106,105 +106,109 @@ export const DataflowVisualization: React.FC<DataflowVisualizationProps> = ({ st
|
||||
})}
|
||||
</svg>
|
||||
|
||||
<div className="relative flex w-full items-center justify-around" style={{ zIndex: 1 }}>
|
||||
<div className="relative flex w-full flex-col items-center justify-around gap-4 sm:flex-row sm:gap-0" style={{ zIndex: 1 }}>
|
||||
{/* 1. 소스 노드 */}
|
||||
<div className="flex flex-col items-center" style={{ width: "28%" }}>
|
||||
<div className="flex flex-col items-center w-full sm:w-[28%]">
|
||||
<Card
|
||||
className={`w-full cursor-pointer border-2 transition-all hover:shadow-lg ${
|
||||
hasSource ? "border-blue-400 bg-blue-50" : "border-gray-300 bg-gray-50"
|
||||
hasSource ? "border-primary bg-primary/10" : "border-border bg-muted"
|
||||
}`}
|
||||
onClick={() => onEdit("source")}
|
||||
>
|
||||
<CardContent className="p-4">
|
||||
<CardContent>
|
||||
<div className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="mb-2 flex items-center gap-2">
|
||||
<Database className="h-5 w-5 text-blue-600" />
|
||||
<span className="text-sm font-semibold text-gray-900">데이터 소스</span>
|
||||
<Database className="h-5 w-5 text-primary" />
|
||||
<span className="text-sm font-semibold text-foreground">데이터 소스</span>
|
||||
</div>
|
||||
{hasSource ? (
|
||||
<div className="space-y-1">
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
<p className="text-sm font-medium text-foreground">
|
||||
{fromTable.tableLabel || fromTable.displayName || fromTable.tableName}
|
||||
</p>
|
||||
{(fromTable.tableLabel || fromTable.displayName) && (
|
||||
<p className="text-xs text-gray-500">({fromTable.tableName})</p>
|
||||
<p className="text-xs text-muted-foreground">({fromTable.tableName})</p>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-xs text-gray-500">미설정</p>
|
||||
<p className="text-xs text-muted-foreground">미설정</p>
|
||||
)}
|
||||
</div>
|
||||
<Button variant="ghost" size="sm" className="h-7 w-7 p-0">
|
||||
<Edit className="h-3 w-3" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 2. 조건 노드 (중앙) */}
|
||||
<div className="flex flex-col items-center" style={{ width: "28%" }}>
|
||||
<div className="flex flex-col items-center w-full sm:w-[28%]">
|
||||
<Card
|
||||
className={`w-full cursor-pointer border-2 transition-all hover:shadow-lg ${
|
||||
hasConditions ? "border-yellow-400 bg-yellow-50" : "border-gray-300 bg-gray-50"
|
||||
hasConditions ? "border-warning bg-warning/10" : "border-border bg-muted"
|
||||
}`}
|
||||
onClick={() => onEdit("conditions")}
|
||||
>
|
||||
<CardContent className="p-4">
|
||||
<CardContent>
|
||||
<div className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="mb-2 flex items-center gap-2">
|
||||
<Filter className="h-5 w-5 text-yellow-600" />
|
||||
<span className="text-sm font-semibold text-gray-900">조건 검증</span>
|
||||
<Filter className="h-5 w-5 text-warning" />
|
||||
<span className="text-sm font-semibold text-foreground">조건 검증</span>
|
||||
</div>
|
||||
{hasConditions ? (
|
||||
<div className="space-y-2">
|
||||
{/* 실제 조건들 표시 */}
|
||||
<div className="max-h-32 space-y-1 overflow-y-auto">
|
||||
{controlConditions.slice(0, 3).map((condition, index) => (
|
||||
<div key={index} className="rounded bg-white/50 px-2 py-1">
|
||||
<p className="text-xs font-medium text-gray-800">
|
||||
<div key={index} className="rounded bg-background/50 px-2 py-1">
|
||||
<p className="text-xs font-medium text-foreground">
|
||||
{index > 0 && (
|
||||
<span className="mr-1 font-bold text-blue-600">
|
||||
<span className="mr-1 font-bold text-primary">
|
||||
{condition.logicalOperator || "AND"}
|
||||
</span>
|
||||
)}
|
||||
<span className="text-blue-800">{getFieldLabel(condition.field)}</span>{" "}
|
||||
<span className="text-gray-600">{condition.operator}</span>{" "}
|
||||
<span className="text-green-700">{condition.value}</span>
|
||||
<span className="text-primary">{getFieldLabel(condition.field)}</span>{" "}
|
||||
<span className="text-muted-foreground">{condition.operator}</span>{" "}
|
||||
<span className="text-success">{condition.value}</span>
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
{controlConditions.length > 3 && (
|
||||
<p className="px-2 text-xs text-gray-500">외 {controlConditions.length - 3}개...</p>
|
||||
<p className="px-2 text-xs text-muted-foreground">외 {controlConditions.length - 3}개...</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-2 flex items-center justify-between border-t pt-2">
|
||||
<div className="flex items-center gap-1">
|
||||
<CheckCircle className="h-3 w-3 text-green-600" />
|
||||
<span className="text-xs text-gray-600">충족 → 실행</span>
|
||||
<CheckCircle className="h-3 w-3 text-success" />
|
||||
<span className="text-xs text-muted-foreground">충족 → 실행</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<XCircle className="h-3 w-3 text-red-600" />
|
||||
<span className="text-xs text-gray-600">불만족 → 중단</span>
|
||||
<XCircle className="h-3 w-3 text-destructive" />
|
||||
<span className="text-xs text-muted-foreground">불만족 → 중단</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-xs text-gray-500">조건 없음 (항상 실행)</p>
|
||||
<p className="text-xs text-muted-foreground">조건 없음 (항상 실행)</p>
|
||||
)}
|
||||
</div>
|
||||
<Button variant="ghost" size="sm" className="h-7 w-7 p-0">
|
||||
<Edit className="h-3 w-3" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 3. 액션 노드들 (우측) */}
|
||||
<div className="flex flex-col items-center gap-3" style={{ width: "28%" }}>
|
||||
<div className="flex flex-col items-center gap-3 w-full sm:w-[28%]">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
@@ -227,10 +231,12 @@ export const DataflowVisualization: React.FC<DataflowVisualizationProps> = ({ st
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<Card className="w-full border-2 border-dashed border-gray-300 bg-gray-50">
|
||||
<CardContent className="p-4 text-center">
|
||||
<Zap className="mx-auto mb-2 h-6 w-6 text-gray-400" />
|
||||
<p className="text-xs text-gray-500">액션 미설정</p>
|
||||
<Card className="w-full border-2 border-dashed border-border bg-muted">
|
||||
<CardContent>
|
||||
<div className="p-4 text-center">
|
||||
<Zap className="mx-auto mb-2 h-6 w-6 text-muted-foreground" />
|
||||
<p className="text-xs text-muted-foreground">액션 미설정</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
@@ -240,34 +246,36 @@ export const DataflowVisualization: React.FC<DataflowVisualizationProps> = ({ st
|
||||
{/* 조건 불만족 시 중단 표시 (하단) */}
|
||||
{hasConditions && (
|
||||
<div
|
||||
className="absolute bottom-0 flex items-center gap-2 rounded-lg border-2 border-red-300 bg-red-50 px-3 py-2"
|
||||
className="absolute bottom-0 flex items-center gap-2 rounded-lg border-2 border-destructive/30 bg-destructive/10 px-3 py-2"
|
||||
style={{ left: "50%", transform: "translateX(-50%)" }}
|
||||
>
|
||||
<XCircle className="h-4 w-4 text-red-600" />
|
||||
<span className="text-xs font-medium text-red-900">조건 불만족 → 실행 중단</span>
|
||||
<XCircle className="h-4 w-4 text-destructive" />
|
||||
<span className="text-xs font-medium text-destructive">조건 불만족 → 실행 중단</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 통계 요약 */}
|
||||
<Card className="border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-around text-center">
|
||||
<Card className="border-border bg-gradient-to-r from-muted to-muted/50">
|
||||
<CardContent>
|
||||
<div className="p-4">
|
||||
<div className="flex flex-col items-center justify-around gap-4 text-center sm:flex-row sm:gap-0">
|
||||
<div>
|
||||
<p className="text-xs text-gray-600">소스</p>
|
||||
<p className="text-lg font-bold text-blue-600">{hasSource ? 1 : 0}</p>
|
||||
<p className="text-xs text-muted-foreground">소스</p>
|
||||
<p className="text-base font-bold text-primary sm:text-lg">{hasSource ? 1 : 0}</p>
|
||||
</div>
|
||||
<div className="h-8 w-px bg-gray-300"></div>
|
||||
<div className="h-8 w-px bg-border hidden sm:block"></div>
|
||||
<div>
|
||||
<p className="text-xs text-gray-600">조건</p>
|
||||
<p className="text-lg font-bold text-yellow-600">{controlConditions.length}</p>
|
||||
<p className="text-xs text-muted-foreground">조건</p>
|
||||
<p className="text-base font-bold text-warning sm:text-lg">{controlConditions.length}</p>
|
||||
</div>
|
||||
<div className="h-8 w-px bg-gray-300"></div>
|
||||
<div className="h-8 w-px bg-border hidden sm:block"></div>
|
||||
<div>
|
||||
<p className="text-xs text-gray-600">액션</p>
|
||||
<p className="text-lg font-bold text-green-600">{dataflowActions.length}</p>
|
||||
<p className="text-xs text-muted-foreground">액션</p>
|
||||
<p className="text-base font-bold text-success sm:text-lg">{dataflowActions.length}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
@@ -288,10 +296,10 @@ interface ActionFlowCardProps {
|
||||
|
||||
const ActionFlowCard: React.FC<ActionFlowCardProps> = ({ type, actions, getTableLabel }) => {
|
||||
const actionColors = {
|
||||
insert: { bg: "bg-blue-50", border: "border-blue-300", text: "text-blue-900", icon: "text-blue-600" },
|
||||
update: { bg: "bg-green-50", border: "border-green-300", text: "text-green-900", icon: "text-green-600" },
|
||||
delete: { bg: "bg-red-50", border: "border-red-300", text: "text-red-900", icon: "text-red-600" },
|
||||
upsert: { bg: "bg-purple-50", border: "border-purple-300", text: "text-purple-900", icon: "text-purple-600" },
|
||||
insert: { bg: "bg-primary/10", border: "border-primary/30", text: "text-primary", icon: "text-primary" },
|
||||
update: { bg: "bg-success/10", border: "border-success/30", text: "text-success", icon: "text-success" },
|
||||
delete: { bg: "bg-destructive/10", border: "border-destructive/30", text: "text-destructive", icon: "text-destructive" },
|
||||
upsert: { bg: "bg-primary/10", border: "border-primary/30", text: "text-primary", icon: "text-primary" },
|
||||
};
|
||||
|
||||
const colors = actionColors[type as keyof typeof actionColors] || actionColors.insert;
|
||||
@@ -301,20 +309,22 @@ const ActionFlowCard: React.FC<ActionFlowCardProps> = ({ type, actions, getTable
|
||||
|
||||
return (
|
||||
<Card className={`border-2 ${colors.border} ${colors.bg}`}>
|
||||
<CardContent className="p-3">
|
||||
<CardContent>
|
||||
<div className="p-3">
|
||||
<div className="mb-2 flex items-center gap-2">
|
||||
<Zap className={`h-4 w-4 ${colors.icon}`} />
|
||||
<span className={`text-sm font-semibold ${colors.text}`}>{type.toUpperCase()}</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<Database className="h-3 w-3 text-gray-500" />
|
||||
<span className="truncate font-medium text-gray-900">{displayName}</span>
|
||||
<Database className="h-3 w-3 text-muted-foreground" />
|
||||
<span className="truncate font-medium text-foreground">{displayName}</span>
|
||||
</div>
|
||||
{isTableLabel && action.targetTable && (
|
||||
<span className="ml-5 truncate text-xs text-gray-500">({action.targetTable})</span>
|
||||
<span className="ml-5 truncate text-xs text-muted-foreground">({action.targetTable})</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@@ -154,11 +154,11 @@ const MultiActionConfigStep: React.FC<MultiActionConfigStepProps> = ({
|
||||
const getLogicalOperatorColor = (operator: string) => {
|
||||
switch (operator) {
|
||||
case "AND":
|
||||
return "bg-primary/20 text-blue-800";
|
||||
return "bg-primary/20 text-primary";
|
||||
case "OR":
|
||||
return "bg-orange-100 text-orange-800";
|
||||
return "bg-warning/20 text-warning";
|
||||
default:
|
||||
return "bg-gray-100 text-gray-800";
|
||||
return "bg-muted text-muted-foreground";
|
||||
}
|
||||
};
|
||||
|
||||
@@ -184,14 +184,14 @@ const MultiActionConfigStep: React.FC<MultiActionConfigStepProps> = ({
|
||||
<p className="text-muted-foreground text-sm">제어 조건, 액션 그룹, 필드 매핑을 설정하세요</p>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="flex h-full flex-col p-4">
|
||||
<CardContent className="flex h-full flex-col p-3 sm:p-4">
|
||||
{/* 탭 헤더 */}
|
||||
<div className="mb-4 flex border-b">
|
||||
<div className="mb-3 flex border-b sm:mb-4">
|
||||
{tabs.map((tab) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={`flex items-center gap-2 px-4 py-2 text-sm font-medium transition-colors ${
|
||||
className={`flex items-center gap-1 px-2 py-2 text-xs font-medium transition-colors sm:gap-2 sm:px-4 sm:text-sm ${
|
||||
activeTab === tab.id
|
||||
? "border-primary text-primary border-b-2"
|
||||
: "text-muted-foreground hover:text-foreground"
|
||||
@@ -221,7 +221,7 @@ const MultiActionConfigStep: React.FC<MultiActionConfigStepProps> = ({
|
||||
</div>
|
||||
|
||||
{controlConditions.length === 0 ? (
|
||||
<div className="rounded-lg border border-dashed border-gray-300 p-8 text-center">
|
||||
<div className="rounded-lg border border-dashed border-border p-8 text-center">
|
||||
<div className="text-muted-foreground">
|
||||
<AlertTriangle className="mx-auto mb-2 h-8 w-8" />
|
||||
<p className="mb-2">제어 조건이 없습니다</p>
|
||||
@@ -267,7 +267,7 @@ const MultiActionConfigStep: React.FC<MultiActionConfigStepProps> = ({
|
||||
{actionGroups.length > 1 && (
|
||||
<div className="rounded-md border bg-accent p-3">
|
||||
<div className="mb-2 flex items-center gap-2">
|
||||
<h4 className="text-sm font-medium text-blue-900">그룹 간 실행 조건</h4>
|
||||
<h4 className="text-sm font-medium text-primary">그룹 간 실행 조건</h4>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -279,7 +279,7 @@ const MultiActionConfigStep: React.FC<MultiActionConfigStepProps> = ({
|
||||
onChange={() => onSetGroupsLogicalOperator?.("AND")}
|
||||
className="h-4 w-4"
|
||||
/>
|
||||
<label htmlFor="groups-and" className="text-sm text-blue-800">
|
||||
<label htmlFor="groups-and" className="text-sm text-primary">
|
||||
<span className="font-medium">AND</span> - 모든 그룹의 조건이 참일 때 실행
|
||||
</label>
|
||||
</div>
|
||||
@@ -292,7 +292,7 @@ const MultiActionConfigStep: React.FC<MultiActionConfigStepProps> = ({
|
||||
onChange={() => onSetGroupsLogicalOperator?.("OR")}
|
||||
className="h-4 w-4"
|
||||
/>
|
||||
<label htmlFor="groups-or" className="text-sm text-blue-800">
|
||||
<label htmlFor="groups-or" className="text-sm text-primary">
|
||||
<span className="font-medium">OR</span> - 하나 이상의 그룹 조건이 참일 때 실행
|
||||
</label>
|
||||
</div>
|
||||
@@ -310,8 +310,8 @@ const MultiActionConfigStep: React.FC<MultiActionConfigStepProps> = ({
|
||||
<div
|
||||
className={`rounded px-2 py-1 text-xs font-medium ${
|
||||
groupsLogicalOperator === "AND"
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-orange-100 text-orange-800"
|
||||
? "bg-success/20 text-success"
|
||||
: "bg-warning/20 text-warning"
|
||||
}`}
|
||||
>
|
||||
{groupsLogicalOperator}
|
||||
@@ -409,7 +409,7 @@ const MultiActionConfigStep: React.FC<MultiActionConfigStepProps> = ({
|
||||
{/* 액션 목록 */}
|
||||
<div className="space-y-3">
|
||||
{group.actions.map((action, actionIndex) => (
|
||||
<div key={action.id} className="rounded-md border bg-white p-3">
|
||||
<div key={action.id} className="rounded-md border bg-background p-3">
|
||||
{/* 액션 헤더 */}
|
||||
<div className="mb-3 flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -533,7 +533,7 @@ const MultiActionConfigStep: React.FC<MultiActionConfigStepProps> = ({
|
||||
</div>
|
||||
|
||||
{/* 컬럼 매핑 캔버스 */}
|
||||
<div className="rounded-lg border bg-white p-3">
|
||||
<div className="rounded-lg border bg-background p-3">
|
||||
<FieldMappingCanvas
|
||||
fromFields={fromColumns}
|
||||
toFields={toColumns}
|
||||
@@ -594,8 +594,8 @@ const MultiActionConfigStep: React.FC<MultiActionConfigStepProps> = ({
|
||||
</div>
|
||||
|
||||
{/* 매핑되지 않은 필드 처리 옵션 */}
|
||||
<div className="rounded-md border bg-yellow-50 p-3">
|
||||
<h6 className="mb-2 flex items-center gap-1 text-xs font-medium text-yellow-800">
|
||||
<div className="rounded-md border bg-warning/10 p-3">
|
||||
<h6 className="mb-2 flex items-center gap-1 text-xs font-medium text-warning">
|
||||
<AlertTriangle className="h-3 w-3" />
|
||||
매핑되지 않은 필드 처리
|
||||
</h6>
|
||||
@@ -608,7 +608,7 @@ const MultiActionConfigStep: React.FC<MultiActionConfigStepProps> = ({
|
||||
defaultChecked
|
||||
className="h-3 w-3"
|
||||
/>
|
||||
<label htmlFor={`empty-${action.id}`} className="text-yellow-700">
|
||||
<label htmlFor={`empty-${action.id}`} className="text-warning/80">
|
||||
비워두기 (NULL 값)
|
||||
</label>
|
||||
</div>
|
||||
@@ -619,7 +619,7 @@ const MultiActionConfigStep: React.FC<MultiActionConfigStepProps> = ({
|
||||
name={`unmapped-${action.id}`}
|
||||
className="h-3 w-3"
|
||||
/>
|
||||
<label htmlFor={`default-${action.id}`} className="text-yellow-700">
|
||||
<label htmlFor={`default-${action.id}`} className="text-warning/80">
|
||||
기본값 사용
|
||||
</label>
|
||||
</div>
|
||||
@@ -630,7 +630,7 @@ const MultiActionConfigStep: React.FC<MultiActionConfigStepProps> = ({
|
||||
name={`unmapped-${action.id}`}
|
||||
className="h-3 w-3"
|
||||
/>
|
||||
<label htmlFor={`skip-${action.id}`} className="text-yellow-700">
|
||||
<label htmlFor={`skip-${action.id}`} className="text-warning/80">
|
||||
필드 제외
|
||||
</label>
|
||||
</div>
|
||||
@@ -647,8 +647,8 @@ const MultiActionConfigStep: React.FC<MultiActionConfigStepProps> = ({
|
||||
<div className="flex items-start gap-2">
|
||||
<AlertTriangle className="mt-0.5 h-4 w-4 text-primary" />
|
||||
<div className="text-sm">
|
||||
<div className="font-medium text-blue-900">{group.logicalOperator} 조건 그룹</div>
|
||||
<div className="text-blue-700">
|
||||
<div className="font-medium text-primary">{group.logicalOperator} 조건 그룹</div>
|
||||
<div className="text-primary/80">
|
||||
{group.logicalOperator === "AND"
|
||||
? "이 그룹의 모든 액션이 실행 가능한 조건일 때만 실행됩니다."
|
||||
: "이 그룹의 액션 중 하나라도 실행 가능한 조건이면 해당 액션만 실행됩니다."}
|
||||
@@ -698,21 +698,21 @@ const MultiActionConfigStep: React.FC<MultiActionConfigStepProps> = ({
|
||||
</div>
|
||||
|
||||
{/* 하단 네비게이션 */}
|
||||
<div className="flex-shrink-0 border-t pt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Button variant="outline" onClick={onBack} className="flex items-center gap-2">
|
||||
<div className="flex-shrink-0 border-t pt-3 sm:pt-4">
|
||||
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||||
<Button variant="outline" onClick={onBack} className="flex items-center gap-2 w-full sm:w-auto">
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
이전
|
||||
<span className="text-xs sm:text-sm">이전</span>
|
||||
</Button>
|
||||
|
||||
<div className="text-muted-foreground text-sm">
|
||||
<div className="text-muted-foreground text-center text-xs sm:text-sm">
|
||||
{actionGroups.filter((g) => g.isEnabled).length}개 그룹, 총{" "}
|
||||
{actionGroups.reduce((sum, g) => sum + g.actions.filter((a) => a.isEnabled).length, 0)}개 액션
|
||||
</div>
|
||||
|
||||
<Button onClick={onNext} className="flex items-center gap-2">
|
||||
<Button onClick={onNext} className="flex items-center gap-2 w-full sm:w-auto">
|
||||
<Save className="h-4 w-4" />
|
||||
저장
|
||||
<span className="text-xs sm:text-sm">저장</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user