From 1e1bc0b2c68c9131781ceb812f855aac39c08c44 Mon Sep 17 00:00:00 2001
From: dohyeons
Date: Fri, 21 Nov 2025 12:22:27 +0900
Subject: [PATCH 1/9] =?UTF-8?q?=EB=8C=80=EC=8B=9C=EB=B3=B4=EB=93=9C=20?=
=?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=A0=80=EC=9E=A5=20=EB=B0=8F=20=EB=94=94?=
=?UTF-8?q?=EC=A7=80=ED=84=B8=20=ED=8A=B8=EC=9C=88=20UX=20=EA=B0=9C?=
=?UTF-8?q?=EC=84=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../widgets/yard-3d/DigitalTwinEditor.tsx | 116 +++++++++---------
.../widgets/yard-3d/DigitalTwinViewer.tsx | 21 ++--
.../widgets/yard-3d/HierarchyConfigPanel.tsx | 14 +--
.../dashboard/widgets/yard-3d/constants.ts | 30 +++++
4 files changed, 103 insertions(+), 78 deletions(-)
create mode 100644 frontend/components/admin/dashboard/widgets/yard-3d/constants.ts
diff --git a/frontend/components/admin/dashboard/widgets/yard-3d/DigitalTwinEditor.tsx b/frontend/components/admin/dashboard/widgets/yard-3d/DigitalTwinEditor.tsx
index 88e844d3..ac9aac19 100644
--- a/frontend/components/admin/dashboard/widgets/yard-3d/DigitalTwinEditor.tsx
+++ b/frontend/components/admin/dashboard/widgets/yard-3d/DigitalTwinEditor.tsx
@@ -25,6 +25,7 @@ import {
import type { MaterialData } from "@/types/digitalTwin";
import { ExternalDbConnectionAPI } from "@/lib/api/externalDbConnection";
import HierarchyConfigPanel, { HierarchyConfig } from "./HierarchyConfigPanel";
+import { OBJECT_COLORS, DEFAULT_COLOR } from "./constants";
import { validateSpatialContainment, updateChildrenPositions, getAllDescendants } from "./spatialContainment";
// 백엔드 DB 객체 타입 (snake_case)
@@ -702,7 +703,7 @@ export default function DigitalTwinEditor({ layoutId, layoutName, onBack }: Digi
name: objectName,
position: { x, y: yPosition, z },
size: defaults.size || { x: 5, y: 5, z: 5 },
- color: defaults.color || "#9ca3af",
+ color: OBJECT_COLORS[draggedTool] || DEFAULT_COLOR, // 타입별 기본 색상
areaKey,
locaKey,
locType,
@@ -1169,8 +1170,8 @@ export default function DigitalTwinEditor({ layoutId, layoutName, onBack }: Digi
도구:
{[
{ type: "area" as ToolType, label: "영역", icon: Grid3x3, color: "text-blue-500" },
- { type: "location-bed" as ToolType, label: "베드", icon: Package, color: "text-emerald-500" },
- { type: "location-stp" as ToolType, label: "정차", icon: Move, color: "text-orange-500" },
+ { type: "location-bed" as ToolType, label: "베드", icon: Package, color: "text-blue-600" },
+ { type: "location-stp" as ToolType, label: "정차", icon: Move, color: "text-gray-500" },
// { type: "crane-gantry" as ToolType, label: "겐트리", icon: Combine, color: "text-green-500" },
{ type: "crane-mobile" as ToolType, label: "크레인", icon: Truck, color: "text-yellow-500" },
{ type: "rack" as ToolType, label: "랙", icon: Box, color: "text-purple-500" },
@@ -1221,54 +1222,6 @@ export default function DigitalTwinEditor({ layoutId, layoutName, onBack }: Digi
- {/* 창고 테이블 및 컬럼 매핑 */}
- {selectedDbConnection && (
-
-
-
- {/* 이 레이아웃의 창고 선택 */}
- {hierarchyConfig?.warehouse?.tableName && hierarchyConfig?.warehouse?.keyColumn && (
-
-
- {loadingWarehouses ? (
-
-
-
- ) : (
-
- )}
-
- )}
-
- )}
-
{/* 계층 설정 패널 (신규) */}
{selectedDbConnection && (
)}
+ {/* 창고 선택 (HierarchyConfigPanel 아래로 이동) */}
+ {selectedDbConnection && hierarchyConfig?.warehouse?.tableName && hierarchyConfig?.warehouse?.keyColumn && (
+
+
+
+
+
+ {loadingWarehouses ? (
+
+
+
+ ) : (
+
+ )}
+
+
+ )}
+
{/* Area 목록 */}
{selectedDbConnection && selectedWarehouse && (
@@ -1605,7 +1605,7 @@ export default function DigitalTwinEditor({ layoutId, layoutName, onBack }: Digi
handleObjectUpdate({ name: e.target.value })}
className="mt-1.5 h-9 text-sm"
/>
@@ -1622,7 +1622,7 @@ export default function DigitalTwinEditor({ layoutId, layoutName, onBack }: Digi
handleObjectUpdate({
position: {
@@ -1641,7 +1641,7 @@ export default function DigitalTwinEditor({ layoutId, layoutName, onBack }: Digi
handleObjectUpdate({
position: {
@@ -1669,7 +1669,7 @@ export default function DigitalTwinEditor({ layoutId, layoutName, onBack }: Digi
type="number"
step="5"
min="5"
- value={selectedObject.size.x}
+ value={selectedObject.size?.x || 5}
onChange={(e) =>
handleObjectUpdate({
size: {
@@ -1688,7 +1688,7 @@ export default function DigitalTwinEditor({ layoutId, layoutName, onBack }: Digi
handleObjectUpdate({
size: {
@@ -1709,7 +1709,7 @@ export default function DigitalTwinEditor({ layoutId, layoutName, onBack }: Digi
type="number"
step="5"
min="5"
- value={selectedObject.size.z}
+ value={selectedObject.size?.z || 5}
onChange={(e) =>
handleObjectUpdate({
size: {
@@ -1732,7 +1732,7 @@ export default function DigitalTwinEditor({ layoutId, layoutName, onBack }: Digi
handleObjectUpdate({ color: e.target.value })}
className="mt-1.5 h-9"
/>
diff --git a/frontend/components/admin/dashboard/widgets/yard-3d/DigitalTwinViewer.tsx b/frontend/components/admin/dashboard/widgets/yard-3d/DigitalTwinViewer.tsx
index 3945a692..94ef98fb 100644
--- a/frontend/components/admin/dashboard/widgets/yard-3d/DigitalTwinViewer.tsx
+++ b/frontend/components/admin/dashboard/widgets/yard-3d/DigitalTwinViewer.tsx
@@ -10,6 +10,7 @@ import dynamic from "next/dynamic";
import { useToast } from "@/hooks/use-toast";
import type { PlacedObject, MaterialData } from "@/types/digitalTwin";
import { getLayoutById, getMaterials } from "@/lib/api/digitalTwin";
+import { OBJECT_COLORS, DEFAULT_COLOR } from "./constants";
const Yard3DCanvas = dynamic(() => import("./Yard3DCanvas"), {
ssr: false,
@@ -81,7 +82,7 @@ export default function DigitalTwinViewer({ layoutId }: DigitalTwinViewerProps)
z: parseFloat(obj.size_z),
},
rotation: obj.rotation ? parseFloat(obj.rotation) : 0,
- color: getObjectColor(objectType), // 타입별 기본 색상 사용
+ color: getObjectColor(objectType, obj.color), // 저장된 색상 우선, 없으면 타입별 기본 색상
areaKey: obj.area_key,
locaKey: obj.loca_key,
locType: obj.loc_type,
@@ -225,17 +226,11 @@ export default function DigitalTwinViewer({ layoutId }: DigitalTwinViewerProps)
// 객체 타입별 기본 색상 (useMemo로 최적화)
const getObjectColor = useMemo(() => {
- return (type: string): string => {
- const colorMap: Record
= {
- area: "#3b82f6", // 파란색
- "location-bed": "#2563eb", // 진한 파란색
- "location-stp": "#6b7280", // 회색
- "location-temp": "#f59e0b", // 주황색
- "location-dest": "#10b981", // 초록색
- "crane-mobile": "#8b5cf6", // 보라색
- rack: "#ef4444", // 빨간색
- };
- return colorMap[type] || "#3b82f6";
+ return (type: string, savedColor?: string): string => {
+ // 저장된 색상이 있으면 우선 사용
+ if (savedColor) return savedColor;
+ // 없으면 타입별 기본 색상 사용
+ return OBJECT_COLORS[type] || DEFAULT_COLOR;
};
}, []);
@@ -383,7 +378,7 @@ export default function DigitalTwinViewer({ layoutId }: DigitalTwinViewerProps)
{typeLabel}
diff --git a/frontend/components/admin/dashboard/widgets/yard-3d/HierarchyConfigPanel.tsx b/frontend/components/admin/dashboard/widgets/yard-3d/HierarchyConfigPanel.tsx
index 8a6f4bfd..d309c92f 100644
--- a/frontend/components/admin/dashboard/widgets/yard-3d/HierarchyConfigPanel.tsx
+++ b/frontend/components/admin/dashboard/widgets/yard-3d/HierarchyConfigPanel.tsx
@@ -257,7 +257,7 @@ export default function HierarchyConfigPanel({
handleLevelChange(level.level, "name", e.target.value)}
className="h-7 w-32 text-xs"
placeholder="레벨명"
@@ -276,7 +276,7 @@ export default function HierarchyConfigPanel({
@@ -310,6 +393,15 @@ export default function HierarchyConfigPanel({
)}
+
+ {localConfig.warehouse?.tableName &&
+ !columnsCache[localConfig.warehouse.tableName] &&
+ loadingColumns && (
+
+
+ 컬럼 정보를 불러오는 중입니다...
+
+ )}
@@ -385,16 +477,22 @@ export default function HierarchyConfigPanel({
- {columnsCache[level.tableName].map((col) => (
-
-
- {col.column_name}
- {col.description && (
- {col.description}
- )}
-
-
- ))}
+ {columnsCache[level.tableName].map((col) => {
+ const pk = isPrimaryKey(col);
+ return (
+
+
+
+ {col.column_name}
+ {pk && PK}
+
+ {col.description && (
+ {col.description}
+ )}
+
+
+ );
+ })}
@@ -475,6 +573,13 @@ export default function HierarchyConfigPanel({
>
)}
+
+ {level.tableName && !columnsCache[level.tableName] && loadingColumns && (
+
+
+ 컬럼 정보를 불러오는 중입니다...
+
+ )}
))}
@@ -528,21 +633,27 @@ export default function HierarchyConfigPanel({
value={localConfig.material.keyColumn || ""}
onValueChange={(val) => handleMaterialChange("keyColumn", val)}
>
-
-
-
-
- {columnsCache[localConfig.material.tableName].map((col) => (
-
-
- {col.column_name}
- {col.description && (
- {col.description}
- )}
-
-
- ))}
-
+
+
+
+
+ {columnsCache[localConfig.material.tableName].map((col) => {
+ const pk = isPrimaryKey(col);
+ return (
+
+
+
+ {col.column_name}
+ {pk && PK}
+
+ {col.description && (
+ {col.description}
+ )}
+
+
+ );
+ })}
+
@@ -673,6 +784,15 @@ export default function HierarchyConfigPanel({
>
)}
+
+ {localConfig.material?.tableName &&
+ !columnsCache[localConfig.material.tableName] &&
+ loadingColumns && (
+
+
+ 컬럼 정보를 불러오는 중입니다...
+
+ )}
diff --git a/frontend/components/admin/dashboard/widgets/yard-3d/spatialContainment.ts b/frontend/components/admin/dashboard/widgets/yard-3d/spatialContainment.ts
index ebedb9f2..f2df7e70 100644
--- a/frontend/components/admin/dashboard/widgets/yard-3d/spatialContainment.ts
+++ b/frontend/components/admin/dashboard/widgets/yard-3d/spatialContainment.ts
@@ -163,3 +163,4 @@ export function getAllDescendants(
}
+
diff --git a/frontend/lib/api/digitalTwin.ts b/frontend/lib/api/digitalTwin.ts
index c20525b4..7a67ff39 100644
--- a/frontend/lib/api/digitalTwin.ts
+++ b/frontend/lib/api/digitalTwin.ts
@@ -19,6 +19,21 @@ interface ApiResponse {
error?: string;
}
+// 매핑 템플릿 타입
+export interface DigitalTwinMappingTemplate {
+ id: string;
+ company_code: string;
+ name: string;
+ description?: string;
+ external_db_connection_id: number;
+ layout_type: string;
+ config: any;
+ created_by: string;
+ created_at: string;
+ updated_by: string;
+ updated_at: string;
+}
+
// ========== 레이아웃 관리 API ==========
// 레이아웃 목록 조회
@@ -281,3 +296,60 @@ export const getChildrenData = async (
};
}
};
+
+// ========== 매핑 템플릿 API ==========
+
+// 템플릿 목록 조회 (회사 단위, 현재 사용자 기준)
+export const getMappingTemplates = async (params?: {
+ externalDbConnectionId?: number;
+ layoutType?: string;
+}): Promise> => {
+ try {
+ const response = await apiClient.get("/digital-twin/mapping-templates", {
+ params: {
+ externalDbConnectionId: params?.externalDbConnectionId,
+ layoutType: params?.layoutType,
+ },
+ });
+ return response.data;
+ } catch (error: any) {
+ return {
+ success: false,
+ error: error.response?.data?.message || error.message,
+ };
+ }
+};
+
+// 템플릿 생성
+export const createMappingTemplate = async (data: {
+ name: string;
+ description?: string;
+ externalDbConnectionId: number;
+ layoutType?: string;
+ config: any;
+}): Promise> => {
+ try {
+ const response = await apiClient.post("/digital-twin/mapping-templates", data);
+ return response.data;
+ } catch (error: any) {
+ return {
+ success: false,
+ error: error.response?.data?.message || error.message,
+ };
+ }
+};
+
+// 템플릿 단건 조회
+export const getMappingTemplateById = async (
+ id: string,
+): Promise> => {
+ try {
+ const response = await apiClient.get(`/digital-twin/mapping-templates/${id}`);
+ return response.data;
+ } catch (error: any) {
+ return {
+ success: false,
+ error: error.response?.data?.message || error.message,
+ };
+ }
+};
--
2.43.0
From 080188b419a534894e3985d3cbfbfab71a4d2a90 Mon Sep 17 00:00:00 2001
From: dohyeons
Date: Tue, 25 Nov 2025 14:57:48 +0900
Subject: [PATCH 7/9] =?UTF-8?q?=EC=99=B8=EB=B6=80=20DB=20=EC=97=B0?=
=?UTF-8?q?=EA=B2=B0=20=EC=84=A4=EC=A0=95=20=EB=B0=8F=20=EC=BF=BC=EB=A6=AC?=
=?UTF-8?q?=20=EC=B2=98=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20=EB=B3=B4=EC=99=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../widgets/yard-3d/HierarchyConfigPanel.tsx | 33 ++++++++++++-------
frontend/lib/api/externalDbConnection.ts | 5 +++
2 files changed, 27 insertions(+), 11 deletions(-)
diff --git a/frontend/components/admin/dashboard/widgets/yard-3d/HierarchyConfigPanel.tsx b/frontend/components/admin/dashboard/widgets/yard-3d/HierarchyConfigPanel.tsx
index 186ac63f..0dffb0de 100644
--- a/frontend/components/admin/dashboard/widgets/yard-3d/HierarchyConfigPanel.tsx
+++ b/frontend/components/admin/dashboard/widgets/yard-3d/HierarchyConfigPanel.tsx
@@ -119,18 +119,29 @@ export default function HierarchyConfigPanel({
tablesToLoad.push(hierarchyConfig.material.tableName);
}
- // 중복 제거 후 로드
+ // 중복 제거 후, 아직 캐시에 없는 테이블만 병렬로 로드
const uniqueTables = [...new Set(tablesToLoad)];
- for (const tableName of uniqueTables) {
- if (!columnsCache[tableName]) {
- try {
- const columns = await onLoadColumns(tableName);
- const normalized = normalizeColumns(columns);
- setColumnsCache((prev) => ({ ...prev, [tableName]: normalized }));
- } catch (error) {
- console.error(`컬럼 로드 실패 (${tableName}):`, error);
- }
- }
+ const tablesToFetch = uniqueTables.filter((tableName) => !columnsCache[tableName]);
+
+ if (tablesToFetch.length === 0) {
+ return;
+ }
+
+ setLoadingColumns(true);
+ try {
+ await Promise.all(
+ tablesToFetch.map(async (tableName) => {
+ try {
+ const columns = await onLoadColumns(tableName);
+ const normalized = normalizeColumns(columns);
+ setColumnsCache((prev) => ({ ...prev, [tableName]: normalized }));
+ } catch (error) {
+ console.error(`컬럼 로드 실패 (${tableName}):`, error);
+ }
+ }),
+ );
+ } finally {
+ setLoadingColumns(false);
}
};
diff --git a/frontend/lib/api/externalDbConnection.ts b/frontend/lib/api/externalDbConnection.ts
index 034a60ef..6d211b3d 100644
--- a/frontend/lib/api/externalDbConnection.ts
+++ b/frontend/lib/api/externalDbConnection.ts
@@ -290,8 +290,13 @@ export class ExternalDbConnectionAPI {
static async getTableColumns(connectionId: number, tableName: string): Promise> {
try {
console.log("컬럼 정보 API 요청:", `${this.BASE_PATH}/${connectionId}/tables/${tableName}/columns`);
+ // 컬럼 메타데이터 조회는 외부 DB 성능/네트워크 영향으로 오래 걸릴 수 있으므로
+ // 기본 30초보다 넉넉한 타임아웃을 사용
const response = await apiClient.get>(
`${this.BASE_PATH}/${connectionId}/tables/${tableName}/columns`,
+ {
+ timeout: 120000, // 120초
+ },
);
console.log("컬럼 정보 API 응답:", response.data);
return response.data;
--
2.43.0
From 60832e88ff2e762d633fba43ba0333e84ac65f86 Mon Sep 17 00:00:00 2001
From: dohyeons
Date: Tue, 25 Nov 2025 15:01:47 +0900
Subject: [PATCH 8/9] =?UTF-8?q?3d=ED=95=84=EB=93=9C=20=EC=83=9D=EC=84=B1?=
=?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../admin/dashboard/widgets/YardManagement3DWidget.tsx | 2 +-
.../dashboard/widgets/yard-3d/YardLayoutCreateModal.tsx | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/frontend/components/admin/dashboard/widgets/YardManagement3DWidget.tsx b/frontend/components/admin/dashboard/widgets/YardManagement3DWidget.tsx
index 815ef07c..8bfcadb4 100644
--- a/frontend/components/admin/dashboard/widgets/YardManagement3DWidget.tsx
+++ b/frontend/components/admin/dashboard/widgets/YardManagement3DWidget.tsx
@@ -173,7 +173,7 @@ export default function YardManagement3DWidget({
diff --git a/frontend/components/admin/dashboard/widgets/yard-3d/YardLayoutCreateModal.tsx b/frontend/components/admin/dashboard/widgets/yard-3d/YardLayoutCreateModal.tsx
index 6fcaca8e..d554dac3 100644
--- a/frontend/components/admin/dashboard/widgets/yard-3d/YardLayoutCreateModal.tsx
+++ b/frontend/components/admin/dashboard/widgets/yard-3d/YardLayoutCreateModal.tsx
@@ -68,15 +68,15 @@ export default function YardLayoutCreateModal({ isOpen, onClose, onCreate }: Yar
e.stopPropagation()}>
- 새 야드 생성
- 야드 이름을 입력하세요
+ 새로운 3d필드 생성
+ 필드 이름을 입력하세요
--
2.43.0
From f59218aa4365f4518d3ff8ba8b411a3bbfd86b31 Mon Sep 17 00:00:00 2001
From: dohyeons
Date: Tue, 25 Nov 2025 15:06:55 +0900
Subject: [PATCH 9/9] =?UTF-8?q?3d=ED=95=84=EB=93=9C=EB=A1=9C=20=ED=85=8D?=
=?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
backend-node/src/app.ts | 4 +-
.../admin/dashboard/CanvasElement.tsx | 4 +-
.../admin/dashboard/DashboardDesigner.tsx | 2 +-
.../admin/dashboard/DashboardTopMenu.tsx | 2 +-
.../admin/dashboard/WidgetConfigSidebar.tsx | 4 +-
frontend/components/admin/dashboard/types.ts | 6 +-
.../widgets/YardManagement3DWidget.tsx | 63 +++++++++----------
.../widgets/yard-3d/YardLayoutCreateModal.tsx | 2 +-
.../admin/dashboard/widgets/yard-3d/types.ts | 2 +-
9 files changed, 43 insertions(+), 46 deletions(-)
diff --git a/backend-node/src/app.ts b/backend-node/src/app.ts
index be51e70e..fc69cdb1 100644
--- a/backend-node/src/app.ts
+++ b/backend-node/src/app.ts
@@ -57,7 +57,7 @@ import riskAlertRoutes from "./routes/riskAlertRoutes"; // 리스크/알림 관
import todoRoutes from "./routes/todoRoutes"; // To-Do 관리
import bookingRoutes from "./routes/bookingRoutes"; // 예약 요청 관리
import mapDataRoutes from "./routes/mapDataRoutes"; // 지도 데이터 관리
-import yardLayoutRoutes from "./routes/yardLayoutRoutes"; // 야드 관리 3D
+import yardLayoutRoutes from "./routes/yardLayoutRoutes"; // 3D 필드
//import materialRoutes from "./routes/materialRoutes"; // 자재 관리
import digitalTwinRoutes from "./routes/digitalTwinRoutes"; // 디지털 트윈 (야드 관제)
import flowRoutes from "./routes/flowRoutes"; // 플로우 관리
@@ -222,7 +222,7 @@ app.use("/api/risk-alerts", riskAlertRoutes); // 리스크/알림 관리
app.use("/api/todos", todoRoutes); // To-Do 관리
app.use("/api/bookings", bookingRoutes); // 예약 요청 관리
app.use("/api/map-data", mapDataRoutes); // 지도 데이터 조회
-app.use("/api/yard-layouts", yardLayoutRoutes); // 야드 관리 3D
+app.use("/api/yard-layouts", yardLayoutRoutes); // 3D 필드
// app.use("/api/materials", materialRoutes); // 자재 관리 (임시 주석)
app.use("/api/digital-twin", digitalTwinRoutes); // 디지털 트윈 (야드 관제)
app.use("/api/flow-external-db", flowExternalDbConnectionRoutes); // 플로우 전용 외부 DB 연결
diff --git a/frontend/components/admin/dashboard/CanvasElement.tsx b/frontend/components/admin/dashboard/CanvasElement.tsx
index beb1e483..090985ba 100644
--- a/frontend/components/admin/dashboard/CanvasElement.tsx
+++ b/frontend/components/admin/dashboard/CanvasElement.tsx
@@ -193,7 +193,7 @@ import { ListWidget } from "./widgets/ListWidget";
import { X } from "lucide-react";
import { Button } from "@/components/ui/button";
-// 야드 관리 3D 위젯
+// 3D 필드 위젯
const YardManagement3DWidget = dynamic(() => import("./widgets/YardManagement3DWidget"), {
ssr: false,
loading: () => (
@@ -1085,7 +1085,7 @@ export function CanvasElement({
) : element.type === "widget" && element.subtype === "yard-management-3d" ? (
- // 야드 관리 3D 위젯 렌더링
+ // 3D 필드 위젯 렌더링
리스트
통계 카드
리스크/알림
- 야드 관리 3D
+ 3D 필드
{/* 커스텀 통계 카드 */}
{/* 커스텀 상태 카드 */}
diff --git a/frontend/components/admin/dashboard/WidgetConfigSidebar.tsx b/frontend/components/admin/dashboard/WidgetConfigSidebar.tsx
index db608645..10af48e8 100644
--- a/frontend/components/admin/dashboard/WidgetConfigSidebar.tsx
+++ b/frontend/components/admin/dashboard/WidgetConfigSidebar.tsx
@@ -93,7 +93,7 @@ const getWidgetTitle = (subtype: ElementSubtype): string => {
chart: "차트",
"map-summary-v2": "지도",
"risk-alert-v2": "리스크 알림",
- "yard-management-3d": "야드 관리 3D",
+ "yard-management-3d": "3D 필드",
weather: "날씨 위젯",
exchange: "환율 위젯",
calculator: "계산기",
@@ -449,7 +449,7 @@ export function WidgetConfigSidebar({ element, isOpen, onClose, onApply }: Widge
- {/* 레이아웃 선택 (야드 관리 3D 위젯 전용) */}
+ {/* 레이아웃 선택 (3D 필드 위젯 전용) */}
{element.subtype === "yard-management-3d" && (
);
}
@@ -164,30 +162,31 @@ export default function YardManagement3DWidget({
// 편집 모드: 레이아웃 선택 UI
if (isEditMode) {
return (
-
+
-
야드 레이아웃 선택
-
- {config?.layoutName ? `선택됨: ${config.layoutName}` : "표시할 야드 레이아웃을 선택하세요"}
+
3D 필드 선택
+
+ {config?.layoutName ? `선택됨: ${config.layoutName}` : "표시할 3D필드를 선택하세요"}
{isLoading ? (
) : layouts.length === 0 ? (
🏗️
-
생성된 야드 레이아웃이 없습니다
-
먼저 야드 레이아웃을 생성하세요
+
생성된 3D필드가 없습니다
+
먼저 3D필드가 생성하세요
) : (
@@ -202,11 +201,11 @@ export default function YardManagement3DWidget({