= ({
);
}
- // 컨테이너 스타일 (원래 카드 레이아웃과 완전히 동일)
+ // 컨테이너 스타일 - 통일된 디자인 시스템 적용
const containerStyle: React.CSSProperties = {
display: "grid",
gridTemplateColumns: `repeat(${componentConfig.cardsPerRow || 3}, 1fr)`, // 기본값 3 (한 행당 카드 수)
gridAutoRows: "min-content", // 자동 행 생성으로 모든 데이터 표시
- gap: `${componentConfig.cardSpacing || 16}px`,
- padding: "16px",
+ gap: `${componentConfig.cardSpacing || 32}px`, // 간격 대폭 증가로 여유로운 느낌
+ padding: "32px", // 패딩 대폭 증가
width: "100%",
height: "100%",
- background: "transparent",
+ background: "linear-gradient(to br, #f8fafc, #f1f5f9)", // 부드러운 그라데이션 배경
overflow: "auto",
+ borderRadius: "12px", // 컨테이너 자체도 라운드 처리
};
- // 카드 스타일 (원래 카드 레이아웃과 완전히 동일)
+ // 카드 스타일 - 통일된 디자인 시스템 적용
const cardStyle: React.CSSProperties = {
backgroundColor: "white",
- border: "1px solid #e5e7eb",
- borderRadius: "8px",
- padding: "16px",
- boxShadow: "0 1px 3px 0 rgba(0, 0, 0, 0.1)",
- transition: "all 0.2s ease-in-out",
+ border: "1px solid #e2e8f0", // 더 부드러운 보더 색상
+ borderRadius: "12px", // 통일된 라운드 처리
+ padding: "24px", // 더 여유로운 패딩
+ boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)", // 더 깊은 그림자
+ transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)", // 부드러운 트랜지션
overflow: "hidden",
display: "flex",
flexDirection: "column",
position: "relative",
- minHeight: "200px",
+ minHeight: "240px", // 최소 높이 더 증가
cursor: isDesignMode ? "pointer" : "default",
+ // 호버 효과를 위한 추가 스타일
+ "&:hover": {
+ transform: "translateY(-2px)",
+ boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
+ }
};
// 텍스트 자르기 함수
@@ -386,53 +392,53 @@ export const CardDisplayComponent: React.FC
= ({
handleCardClick(data)}
>
- {/* 카드 이미지 */}
+ {/* 카드 이미지 - 통일된 디자인 */}
{componentConfig.cardStyle?.showImage && componentConfig.columnMapping?.imageColumn && (
-
-
-
👤
+
)}
- {/* 카드 타이틀 */}
+ {/* 카드 타이틀 - 통일된 디자인 */}
{componentConfig.cardStyle?.showTitle && (
-
-
{titleValue}
+
+
{titleValue}
)}
- {/* 카드 서브타이틀 */}
+ {/* 카드 서브타이틀 - 통일된 디자인 */}
{componentConfig.cardStyle?.showSubtitle && (
-
-
{subtitleValue}
+
)}
- {/* 카드 설명 */}
+ {/* 카드 설명 - 통일된 디자인 */}
{componentConfig.cardStyle?.showDescription && (
-
-
+
+
{truncateText(descriptionValue, componentConfig.cardStyle?.maxDescriptionLength || 100)}
)}
- {/* 추가 표시 컬럼들 */}
+ {/* 추가 표시 컬럼들 - 통일된 디자인 */}
{componentConfig.columnMapping?.displayColumns &&
componentConfig.columnMapping.displayColumns.length > 0 && (
-
+
{componentConfig.columnMapping.displayColumns.map((columnName, idx) => {
const value = getColumnValue(data, columnName);
if (!value) return null;
return (
-
-
{getColumnLabel(columnName)}:
-
{value}
+
+ {getColumnLabel(columnName)}:
+ {value}
);
})}
diff --git a/frontend/lib/registry/components/file-upload/FileUploadComponent.tsx b/frontend/lib/registry/components/file-upload/FileUploadComponent.tsx
index 999bc85b..86bb406d 100644
--- a/frontend/lib/registry/components/file-upload/FileUploadComponent.tsx
+++ b/frontend/lib/registry/components/file-upload/FileUploadComponent.tsx
@@ -131,15 +131,90 @@ const FileUploadComponent: React.FC
= ({
}
}, [component.id]); // component.id가 변경될 때만 실행
+ // 🎯 화면설계 모드에서 실제 화면으로의 실시간 동기화 이벤트 리스너
+ useEffect(() => {
+ const handleDesignModeFileChange = (event: CustomEvent) => {
+ console.log("🎯🎯🎯 FileUploadComponent 화면설계 모드 파일 변경 이벤트 수신:", {
+ eventComponentId: event.detail.componentId,
+ currentComponentId: component.id,
+ isMatch: event.detail.componentId === component.id,
+ filesCount: event.detail.files?.length || 0,
+ action: event.detail.action,
+ source: event.detail.source,
+ eventDetail: event.detail
+ });
+
+ // 현재 컴포넌트와 일치하고 화면설계 모드에서 온 이벤트인 경우
+ if (event.detail.componentId === component.id && event.detail.source === 'designMode') {
+ console.log("✅✅✅ 화면설계 모드 → 실제 화면 파일 동기화 시작:", {
+ componentId: component.id,
+ filesCount: event.detail.files?.length || 0,
+ action: event.detail.action
+ });
+
+ // 파일 상태 업데이트
+ const newFiles = event.detail.files || [];
+ setUploadedFiles(newFiles);
+
+ // localStorage 백업 업데이트
+ try {
+ const backupKey = `fileUpload_${component.id}`;
+ localStorage.setItem(backupKey, JSON.stringify(newFiles));
+ console.log("💾 화면설계 모드 동기화 후 localStorage 백업 업데이트:", {
+ componentId: component.id,
+ fileCount: newFiles.length
+ });
+ } catch (e) {
+ console.warn("localStorage 백업 업데이트 실패:", e);
+ }
+
+ // 전역 상태 업데이트
+ if (typeof window !== 'undefined') {
+ (window as any).globalFileState = {
+ ...(window as any).globalFileState,
+ [component.id]: newFiles
+ };
+ }
+
+ // onUpdate 콜백 호출 (부모 컴포넌트에 알림)
+ if (onUpdate) {
+ onUpdate({
+ uploadedFiles: newFiles,
+ lastFileUpdate: event.detail.timestamp
+ });
+ }
+
+ console.log("🎉🎉🎉 화면설계 모드 → 실제 화면 동기화 완료:", {
+ componentId: component.id,
+ finalFileCount: newFiles.length
+ });
+ }
+ };
+
+ if (typeof window !== 'undefined') {
+ window.addEventListener('globalFileStateChanged', handleDesignModeFileChange as EventListener);
+
+ return () => {
+ window.removeEventListener('globalFileStateChanged', handleDesignModeFileChange as EventListener);
+ };
+ }
+ }, [component.id, onUpdate]);
+
// 템플릿 파일과 데이터 파일을 조회하는 함수
const loadComponentFiles = useCallback(async () => {
if (!component?.id) return;
try {
- const screenId = formData?.screenId || (typeof window !== 'undefined' && window.location.pathname.includes('/screens/')
+ let screenId = formData?.screenId || (typeof window !== 'undefined' && window.location.pathname.includes('/screens/')
? parseInt(window.location.pathname.split('/screens/')[1])
: null);
+ // 디자인 모드인 경우 기본 화면 ID 사용
+ if (!screenId && isDesignMode) {
+ screenId = 40; // 기본 화면 ID
+ console.log("📂 디자인 모드: 기본 화면 ID 사용 (40)");
+ }
+
if (!screenId) {
console.log("📂 화면 ID 없음, 기존 파일 로직 사용");
return false; // 기존 로직 사용
@@ -474,14 +549,18 @@ const FileUploadComponent: React.FC = ({
}
const uploadData = {
- tableName: tableName,
- fieldName: columnName,
- recordId: recordId || `temp_${component.id}`,
+ // 🎯 formData에서 백엔드 API 설정 가져오기
+ autoLink: formData?.autoLink || true,
+ linkedTable: formData?.linkedTable || tableName,
+ recordId: formData?.recordId || recordId || `temp_${component.id}`,
+ columnName: formData?.columnName || columnName,
+ isVirtualFileColumn: formData?.isVirtualFileColumn || true,
docType: component.fileConfig?.docType || 'DOCUMENT',
docTypeName: component.fileConfig?.docTypeName || '일반 문서',
+ // 호환성을 위한 기존 필드들
+ tableName: tableName,
+ fieldName: columnName,
targetObjid: targetObjid, // InteractiveDataTable 호환을 위한 targetObjid 추가
- columnName: columnName, // 가상 파일 컬럼 지원
- isVirtualFileColumn: true, // 가상 파일 컬럼으로 처리
};
console.log("📤 파일 업로드 시작:", {
@@ -715,7 +794,9 @@ const FileUploadComponent: React.FC = ({
componentId: component.id,
files: updatedFiles,
fileCount: updatedFiles.length,
- timestamp: Date.now()
+ timestamp: Date.now(),
+ source: 'realScreen', // 🎯 실제 화면에서 온 이벤트임을 표시
+ action: 'delete'
}
});
window.dispatchEvent(syncEvent);
diff --git a/frontend/lib/registry/components/file-upload/FileViewerModal.tsx b/frontend/lib/registry/components/file-upload/FileViewerModal.tsx
index b3f02982..d563e065 100644
--- a/frontend/lib/registry/components/file-upload/FileViewerModal.tsx
+++ b/frontend/lib/registry/components/file-upload/FileViewerModal.tsx
@@ -516,7 +516,7 @@ export const FileViewerModal: React.FC = ({
return (