diff --git a/backend-node/src/controllers/fileController.ts b/backend-node/src/controllers/fileController.ts index 2feb6bfd..78193960 100644 --- a/backend-node/src/controllers/fileController.ts +++ b/backend-node/src/controllers/fileController.ts @@ -767,8 +767,9 @@ export const previewFile = async ( mimeType = "application/octet-stream"; } - // CORS 헤더 설정 (더 포괄적으로) - res.setHeader("Access-Control-Allow-Origin", "*"); + // CORS 헤더 설정 (credentials 모드에서는 구체적인 origin 필요) + const origin = req.headers.origin || "http://localhost:9771"; + res.setHeader("Access-Control-Allow-Origin", origin); res.setHeader( "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS" diff --git a/frontend/components/admin/dashboard/CanvasElement.tsx b/frontend/components/admin/dashboard/CanvasElement.tsx index 90eec9ca..09ddfe5c 100644 --- a/frontend/components/admin/dashboard/CanvasElement.tsx +++ b/frontend/components/admin/dashboard/CanvasElement.tsx @@ -26,80 +26,108 @@ import { // 위젯 동적 임포트 const WeatherWidget = dynamic(() => import("@/components/dashboard/widgets/WeatherWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); const ExchangeWidget = dynamic(() => import("@/components/dashboard/widgets/ExchangeWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); const CalculatorWidget = dynamic(() => import("@/components/dashboard/widgets/CalculatorWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); const VehicleStatusWidget = dynamic(() => import("@/components/dashboard/widgets/VehicleStatusWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); const VehicleListWidget = dynamic(() => import("@/components/dashboard/widgets/VehicleListWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); const VehicleMapOnlyWidget = dynamic(() => import("@/components/dashboard/widgets/VehicleMapOnlyWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); // 범용 지도 위젯 (차량, 창고, 고객 등 모든 위치 위젯 통합) const MapSummaryWidget = dynamic(() => import("@/components/dashboard/widgets/MapSummaryWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); // 🧪 테스트용 지도 위젯 (REST API 지원) const MapTestWidget = dynamic(() => import("@/components/dashboard/widgets/MapTestWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); // 🧪 테스트용 지도 위젯 V2 (다중 데이터 소스) const MapTestWidgetV2 = dynamic(() => import("@/components/dashboard/widgets/MapTestWidgetV2"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); // 🧪 테스트용 차트 위젯 (다중 데이터 소스) const ChartTestWidget = dynamic(() => import("@/components/dashboard/widgets/ChartTestWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); const ListTestWidget = dynamic( () => import("@/components/dashboard/widgets/ListTestWidget").then((mod) => ({ default: mod.ListTestWidget })), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }, ); const CustomMetricTestWidget = dynamic(() => import("@/components/dashboard/widgets/CustomMetricTestWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); const RiskAlertTestWidget = dynamic(() => import("@/components/dashboard/widgets/RiskAlertTestWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); // 범용 상태 요약 위젯 (차량, 배송 등 모든 상태 위젯 통합) const StatusSummaryWidget = dynamic(() => import("@/components/dashboard/widgets/StatusSummaryWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); // 범용 목록 위젯 (차량, 기사, 제품 등 모든 목록 위젯 통합) - 다른 분 작업 중, 임시 주석 @@ -128,22 +156,30 @@ const StatusSummaryWidget = dynamic(() => import("@/components/dashboard/widgets const RiskAlertWidget = dynamic(() => import("@/components/dashboard/widgets/RiskAlertWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); const TaskWidget = dynamic(() => import("@/components/dashboard/widgets/TaskWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); const BookingAlertWidget = dynamic(() => import("@/components/dashboard/widgets/BookingAlertWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); const DocumentWidget = dynamic(() => import("@/components/dashboard/widgets/DocumentWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); // 시계 위젯 임포트 @@ -160,25 +196,33 @@ import { Button } from "@/components/ui/button"; // 야드 관리 3D 위젯 const YardManagement3DWidget = dynamic(() => import("./widgets/YardManagement3DWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); // 작업 이력 위젯 const WorkHistoryWidget = dynamic(() => import("@/components/dashboard/widgets/WorkHistoryWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); // 커스텀 통계 카드 위젯 const CustomStatsWidget = dynamic(() => import("@/components/dashboard/widgets/CustomStatsWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); // 사용자 커스텀 카드 위젯 const CustomMetricWidget = dynamic(() => import("@/components/dashboard/widgets/CustomMetricWidget"), { ssr: false, - loading: () =>
로딩 중...
, + loading: () => ( +
로딩 중...
+ ), }); interface CanvasElementProps { @@ -758,7 +802,7 @@ export function CanvasElement({
{element.customTitle || element.title} + {element.customTitle || element.title} ) : null}
@@ -817,7 +861,7 @@ export function CanvasElement({ -
- {/* 파일 업로드 영역 */} +
+ {/* 파일 업로드 영역 - 높이 축소 */} {!isDesignMode && (
{ + if (!config.disabled && !isDesignMode) { + fileInputRef.current?.click(); + } + }} onDragOver={handleDragOver} onDragLeave={handleDragLeave} onDrop={handleDrop} @@ -186,47 +231,71 @@ export const FileManagerModal: React.FC = ({ /> {uploading ? ( -
-
- 업로드 중... +
+
+ 업로드 중...
) : ( -
- -

+

+ +

파일을 드래그하거나 클릭하여 업로드하세요

-

- {config.accept && `지원 형식: ${config.accept}`} - {config.maxSize && ` • 최대 ${formatFileSize(config.maxSize)}`} - {config.multiple && ' • 여러 파일 선택 가능'} -

)}
)} - {/* 파일 목록 */} -
-
-
-

- 업로드된 파일 -

- {uploadedFiles.length > 0 && ( - - 총 {formatFileSize(uploadedFiles.reduce((sum, file) => sum + file.fileSize, 0))} - - )} + {/* 좌우 분할 레이아웃 */} +
+ {/* 좌측: 이미지 미리보기 */} +
+ {selectedFile && previewImageUrl ? ( + {selectedFile.realFileName} + ) : selectedFile ? ( +
+ {getFileIcon(selectedFile.fileExt)} +

미리보기 불가능

+
+ ) : ( +
+ +

파일을 선택하면 미리보기가 표시됩니다

+
+ )} +
+ + {/* 우측: 파일 목록 */} +
+
+
+

+ 업로드된 파일 +

+ {uploadedFiles.length > 0 && ( + + 총 {formatFileSize(uploadedFiles.reduce((sum, file) => sum + file.fileSize, 0))} + + )} +
- {uploadedFiles.length > 0 ? ( -
- {uploadedFiles.map((file) => ( -
+
+ {uploadedFiles.length > 0 ? ( +
+ {uploadedFiles.map((file) => ( +
handleFileClick(file)} + >
{getFileIcon(file.fileExt)}
@@ -250,40 +319,52 @@ export const FileManagerModal: React.FC = ({ )} {!isDesignMode && ( )}
@@ -291,17 +372,18 @@ export const FileManagerModal: React.FC = ({ ))}
) : ( -
- -

업로드된 파일이 없습니다

-

- {isDesignMode ? '디자인 모드에서는 파일을 업로드할 수 없습니다' : '위의 영역에 파일을 드래그하거나 클릭하여 업로드하세요'} +

+ +

업로드된 파일이 없습니다

+

+ {isDesignMode ? '디자인 모드에서는 파일을 업로드할 수 없습니다' : '위의 영역에 파일을 업로드하세요'}

)}
+