diff --git a/frontend/components/dataflow/node-editor/panels/properties/TableSourceProperties.tsx b/frontend/components/dataflow/node-editor/panels/properties/TableSourceProperties.tsx
index a342a213..9342e583 100644
--- a/frontend/components/dataflow/node-editor/panels/properties/TableSourceProperties.tsx
+++ b/frontend/components/dataflow/node-editor/panels/properties/TableSourceProperties.tsx
@@ -12,6 +12,7 @@ import { Button } from "@/components/ui/button";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
+import { ScrollArea } from "@/components/ui/scroll-area";
import { cn } from "@/lib/utils";
import { useFlowEditorStore } from "@/lib/stores/flowEditorStore";
import { tableTypeApi } from "@/lib/api/screen";
@@ -34,10 +35,10 @@ export function TableSourceProperties({ nodeId, data }: TableSourcePropertiesPro
const [displayName, setDisplayName] = useState(data.displayName || data.tableName);
const [tableName, setTableName] = useState(data.tableName);
-
+
// π λ°μ΄ν° μμ€ νμ
(κΈ°λ³Έκ°: context-data)
const [dataSourceType, setDataSourceType] = useState<"context-data" | "table-all">(
- (data as any).dataSourceType || "context-data"
+ (data as any).dataSourceType || "context-data",
);
// ν
μ΄λΈ μ ν κ΄λ ¨ μν
@@ -167,171 +168,168 @@ export function TableSourceProperties({ nodeId, data }: TableSourcePropertiesPro
return (
- {/* κΈ°λ³Έ μ 보 */}
-
-
κΈ°λ³Έ μ 보
+ {/* κΈ°λ³Έ μ 보 */}
+
+
κΈ°λ³Έ μ 보
-
-
-
- handleDisplayNameChange(e.target.value)}
- className="mt-1"
- placeholder="λ
Έλ νμ μ΄λ¦"
- />
-
+
+
+
+ handleDisplayNameChange(e.target.value)}
+ className="mt-1"
+ placeholder="λ
Έλ νμ μ΄λ¦"
+ />
+
- {/* ν
μ΄λΈ μ ν Combobox */}
-
-
-
-
-
-
-
-
-
-
- κ²μ κ²°κ³Όκ° μμ΅λλ€.
-
-
- {tables.map((table) => (
- handleTableSelect(table.tableName)}
- className="cursor-pointer"
- >
-
-
- {table.label}
- {table.label !== table.tableName && (
- {table.tableName}
- )}
- {table.description && (
- {table.description}
- )}
-
-
- ))}
-
-
-
-
-
-
- {tableName && selectedTableLabel !== tableName && (
-
- μ€μ ν
μ΄λΈλͺ
: {tableName}
-
+ {/* ν
μ΄λΈ μ ν Combobox */}
+
+
+
+
+
+
+
+
+
+
+ κ²μ κ²°κ³Όκ° μμ΅λλ€.
+
+
+ {tables.map((table) => (
+ handleTableSelect(table.tableName)}
+ className="cursor-pointer"
+ >
+
+
+ {table.label}
+ {table.label !== table.tableName && (
+ {table.tableName}
+ )}
+ {table.description && (
+ {table.description}
+ )}
+
+
+ ))}
+
+
+
+
+
+
+ {tableName && selectedTableLabel !== tableName && (
+
+ μ€μ ν
μ΄λΈλͺ
: {tableName}
+
+ )}
+
+
+
+
+ {/* π λ°μ΄ν° μμ€ μ€μ */}
+
+
λ°μ΄ν° μμ€ μ€μ
+
+
+
+
+
+
+ {/* μ€λͺ
ν
μ€νΈ */}
+
+ {dataSourceType === "context-data" ? (
+ <>
+
π‘ 컨ν
μ€νΈ λ°μ΄ν° λͺ¨λ
+
λ²νΌ μ€ν μ μ λ¬λ λ°μ΄ν°(νΌ λ°μ΄ν°, ν
μ΄λΈ μ ν νλͺ© λ±)λ₯Ό μ¬μ©ν©λλ€.
+
β’ νΌ λ°μ΄ν°: 1κ° λ μ½λ
+
β’ ν
μ΄λΈ μ ν: Nκ° λ μ½λ
+ >
+ ) : (
+ <>
+
π ν
μ΄λΈ μ 체 λ°μ΄ν° λͺ¨λ
+
μ νν ν
μ΄λΈμ **λͺ¨λ ν**μ μ§μ μ‘°νν©λλ€.
+
β οΈ λλ λ°μ΄ν° μ μ±λ₯ μ£Όμ
+ >
)}
+
- {/* π λ°μ΄ν° μμ€ μ€μ */}
-
-
λ°μ΄ν° μμ€ μ€μ
-
-
-
-
-
-
- {/* μ€λͺ
ν
μ€νΈ */}
-
- {dataSourceType === "context-data" ? (
- <>
-
π‘ 컨ν
μ€νΈ λ°μ΄ν° λͺ¨λ
-
λ²νΌ μ€ν μ μ λ¬λ λ°μ΄ν°(νΌ λ°μ΄ν°, ν
μ΄λΈ μ ν νλͺ© λ±)λ₯Ό μ¬μ©ν©λλ€.
-
β’ νΌ λ°μ΄ν°: 1κ° λ μ½λ
-
β’ ν
μ΄λΈ μ ν: Nκ° λ μ½λ
- >
- ) : (
- <>
-
π ν
μ΄λΈ μ 체 λ°μ΄ν° λͺ¨λ
-
μ νν ν
μ΄λΈμ **λͺ¨λ ν**μ μ§μ μ‘°νν©λλ€.
-
β οΈ λλ λ°μ΄ν° μ μ±λ₯ μ£Όμ
- >
- )}
+ {/* νλ μ 보 */}
+
+
+ μΆλ ₯ νλ {data.fields && data.fields.length > 0 && `(${data.fields.length}κ°)`}
+
+ {data.fields && data.fields.length > 0 ? (
+
+ {data.fields.map((field) => (
+
+
+ {field.name}
+
+ {field.type}
-
+ ))}
-
-
- {/* νλ μ 보 */}
-
-
- μΆλ ₯ νλ {data.fields && data.fields.length > 0 && `(${data.fields.length}κ°)`}
-
- {data.fields && data.fields.length > 0 ? (
-
- {data.fields.map((field) => (
-
-
- {field.name}
-
- {field.type}
-
- ))}
-
- ) : (
-
νλ μ λ³΄κ° μμ΅λλ€
- )}
-
-
+ ) : (
+
νλ μ λ³΄κ° μμ΅λλ€
+ )}
+
);
}
diff --git a/frontend/components/screen/config-panels/FlowVisibilityConfigPanel.tsx b/frontend/components/screen/config-panels/FlowVisibilityConfigPanel.tsx
index 9d0f859f..d820f2ca 100644
--- a/frontend/components/screen/config-panels/FlowVisibilityConfigPanel.tsx
+++ b/frontend/components/screen/config-panels/FlowVisibilityConfigPanel.tsx
@@ -13,7 +13,7 @@ import { Input } from "@/components/ui/input";
import { Workflow, Info, CheckCircle, XCircle, Loader2, ArrowRight, ArrowDown } from "lucide-react";
import { ComponentData } from "@/types/screen";
import { FlowVisibilityConfig } from "@/types/control-management";
-import { getFlowById } from "@/lib/api/flow";
+import { getFlowById, getFlowSteps } from "@/lib/api/flow";
import type { FlowDefinition, FlowStep } from "@/types/flow";
import { toast } from "sonner";
@@ -25,7 +25,7 @@ interface FlowVisibilityConfigPanelProps {
/**
* νλ‘μ° λ¨κ³λ³ λ²νΌ νμ μ€μ ν¨λ
- *
+ *
* νλ‘μ° μμ ―μ΄ νλ©΄μ μμ λ, λ²νΌμ΄ νΉμ νλ‘μ° λ¨κ³μμλ§ νμλλλ‘ μ€μ ν μ μμ΅λλ€.
*/
export const FlowVisibilityConfigPanel: React.FC
= ({
@@ -40,8 +40,7 @@ export const FlowVisibilityConfigPanel: React.FC
const flowWidgets = useMemo(() => {
return allComponents.filter((comp) => {
const isFlowWidget =
- comp.type === "flow" ||
- (comp.type === "component" && (comp as any).componentConfig?.type === "flow-widget");
+ comp.type === "flow" || (comp.type === "component" && (comp as any).componentConfig?.type === "flow-widget");
return isFlowWidget;
});
}, [allComponents]);
@@ -49,23 +48,23 @@ export const FlowVisibilityConfigPanel: React.FC
// State
const [enabled, setEnabled] = useState(currentConfig?.enabled || false);
const [selectedFlowComponentId, setSelectedFlowComponentId] = useState(
- currentConfig?.targetFlowComponentId || null
+ currentConfig?.targetFlowComponentId || null,
);
const [mode, setMode] = useState<"whitelist" | "blacklist" | "all">(currentConfig?.mode || "whitelist");
const [visibleSteps, setVisibleSteps] = useState(currentConfig?.visibleSteps || []);
const [hiddenSteps, setHiddenSteps] = useState(currentConfig?.hiddenSteps || []);
const [layoutBehavior, setLayoutBehavior] = useState<"preserve-position" | "auto-compact">(
- currentConfig?.layoutBehavior || "auto-compact"
+ currentConfig?.layoutBehavior || "auto-compact",
);
// π κ·Έλ£Ή μ€μ (auto-compact λͺ¨λμμλ§ μ¬μ©)
const [groupId, setGroupId] = useState(currentConfig?.groupId || `group-${Date.now()}`);
const [groupDirection, setGroupDirection] = useState<"horizontal" | "vertical">(
- currentConfig?.groupDirection || "horizontal"
+ currentConfig?.groupDirection || "horizontal",
);
const [groupGap, setGroupGap] = useState(currentConfig?.groupGap ?? 8);
const [groupAlign, setGroupAlign] = useState<"start" | "center" | "end" | "space-between" | "space-around">(
- currentConfig?.groupAlign || "start"
+ currentConfig?.groupAlign || "start",
);
// μ νλ νλ‘μ°μ μ€ν
λͺ©λ‘
@@ -127,13 +126,12 @@ export const FlowVisibilityConfigPanel: React.FC
setFlowInfo(flowResponse.data);
// μ€ν
λͺ©λ‘ μ‘°ν
- const stepsResponse = await fetch(`/api/flow/definitions/${flowId}/steps`);
- if (!stepsResponse.ok) {
+ const stepsResponse = await getFlowSteps(flowId);
+ if (!stepsResponse.success) {
throw new Error("μ€ν
λͺ©λ‘μ λΆλ¬μ¬ μ μμ΅λλ€");
}
- const stepsData = await stepsResponse.json();
- if (stepsData.success && stepsData.data) {
- const sortedSteps = stepsData.data.sort((a: FlowStep, b: FlowStep) => a.stepOrder - b.stepOrder);
+ if (stepsResponse.data) {
+ const sortedSteps = stepsResponse.data.sort((a: FlowStep, b: FlowStep) => a.stepOrder - b.stepOrder);
setFlowSteps(sortedSteps);
}
} catch (error: any) {
@@ -346,12 +344,10 @@ export const FlowVisibilityConfigPanel: React.FC
{/* μ€ν
체ν¬λ°μ€ λͺ©λ‘ */}
-
+
{flowSteps.map((step) => {
const isChecked =
- mode === "whitelist"
- ? visibleSteps.includes(step.id)
- : hiddenSteps.includes(step.id);
+ mode === "whitelist" ? visibleSteps.includes(step.id) : hiddenSteps.includes(step.id);
return (
@@ -366,7 +362,9 @@ export const FlowVisibilityConfigPanel: React.FC
{step.stepName}
{isChecked && (
-
+
)}
@@ -403,14 +401,12 @@ export const FlowVisibilityConfigPanel: React.FC
{/* π κ·Έλ£Ή μ€μ (auto-compact λͺ¨λμΌ λλ§ νμ) */}
{layoutBehavior === "auto-compact" && (
-
+
κ·Έλ£Ή μ€μ
-
- κ°μ κ·Έλ£Ή IDλ₯Ό κ°μ§ λ²νΌλ€μ΄ μλμΌλ‘ μ λ ¬λ©λλ€
-
+
κ°μ κ·Έλ£Ή IDλ₯Ό κ°μ§ λ²νΌλ€μ΄ μλμΌλ‘ μ λ ¬λ©λλ€
{/* κ·Έλ£Ή ID */}
@@ -425,7 +421,7 @@ export const FlowVisibilityConfigPanel: React.FC
placeholder="group-1"
className="h-8 text-xs sm:h-9 sm:text-sm"
/>
-
+
κ°μ κ·Έλ£Ή IDλ₯Ό κ°μ§ λ²νΌλ€μ΄ νλμ κ·Έλ£ΉμΌλ‘ λ¬Άμ
λλ€
@@ -577,4 +573,3 @@ export const FlowVisibilityConfigPanel: React.FC
);
};
-
diff --git a/frontend/components/screen/widgets/FlowWidget.tsx b/frontend/components/screen/widgets/FlowWidget.tsx
index 78156945..1204ccdb 100644
--- a/frontend/components/screen/widgets/FlowWidget.tsx
+++ b/frontend/components/screen/widgets/FlowWidget.tsx
@@ -5,7 +5,14 @@ import { FlowComponent } from "@/types/screen-management";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { AlertCircle, Loader2, ChevronUp, History } from "lucide-react";
-import { getFlowById, getAllStepCounts, getStepDataList, getFlowAuditLogs } from "@/lib/api/flow";
+import {
+ getFlowById,
+ getAllStepCounts,
+ getStepDataList,
+ getFlowAuditLogs,
+ getFlowSteps,
+ getFlowConnections,
+} from "@/lib/api/flow";
import type { FlowDefinition, FlowStep, FlowAuditLog } from "@/types/flow";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Checkbox } from "@/components/ui/checkbox";
@@ -162,22 +169,18 @@ export function FlowWidget({
setFlowData(flowResponse.data);
// μ€ν
λͺ©λ‘ μ‘°ν
- const stepsResponse = await fetch(`/api/flow/definitions/${flowId}/steps`);
- if (!stepsResponse.ok) {
+ const stepsResponse = await getFlowSteps(flowId);
+ if (!stepsResponse.success) {
throw new Error("μ€ν
λͺ©λ‘μ λΆλ¬μ¬ μ μμ΅λλ€");
}
- const stepsData = await stepsResponse.json();
- if (stepsData.success && stepsData.data) {
- const sortedSteps = stepsData.data.sort((a: FlowStep, b: FlowStep) => a.stepOrder - b.stepOrder);
+ if (stepsResponse.data) {
+ const sortedSteps = stepsResponse.data.sort((a: FlowStep, b: FlowStep) => a.stepOrder - b.stepOrder);
setSteps(sortedSteps);
// μ°κ²° μ 보 μ‘°ν
- const connectionsResponse = await fetch(`/api/flow/connections/${flowId}`);
- if (connectionsResponse.ok) {
- const connectionsData = await connectionsResponse.json();
- if (connectionsData.success && connectionsData.data) {
- setConnections(connectionsData.data);
- }
+ const connectionsResponse = await getFlowConnections(flowId);
+ if (connectionsResponse.success && connectionsResponse.data) {
+ setConnections(connectionsResponse.data);
}
// μ€ν
λ³ λ°μ΄ν° 건μ μ‘°ν
diff --git a/frontend/lib/api/flow.ts b/frontend/lib/api/flow.ts
index 7d61293a..d94455ff 100644
--- a/frontend/lib/api/flow.ts
+++ b/frontend/lib/api/flow.ts
@@ -19,7 +19,33 @@ import {
ApiResponse,
} from "@/types/flow";
-const API_BASE = process.env.NEXT_PUBLIC_API_URL || "/api";
+// API URL λμ μ€μ
+const getApiBaseUrl = (): string => {
+ // 1. νκ²½λ³μκ° μμΌλ©΄ μ°μ μ¬μ©
+ if (process.env.NEXT_PUBLIC_API_URL) {
+ return process.env.NEXT_PUBLIC_API_URL;
+ }
+
+ // 2. ν΄λΌμ΄μΈνΈ μ¬μ΄λμμ λμ μ€μ
+ if (typeof window !== "undefined") {
+ const currentHost = window.location.hostname;
+
+ // νλ‘λμ
νκ²½: v1.vexplor.com β api.vexplor.com
+ if (currentHost === "v1.vexplor.com") {
+ return "https://api.vexplor.com/api";
+ }
+
+ // λ‘컬 κ°λ°νκ²½
+ if (currentHost === "localhost" || currentHost === "127.0.0.1") {
+ return "http://localhost:8080/api";
+ }
+ }
+
+ // 3. κΈ°λ³Έκ°
+ return "/api";
+};
+
+const API_BASE = getApiBaseUrl();
// ν ν° κ°μ Έμ€κΈ°
function getAuthToken(): string | null {