diff --git a/frontend/components/layout/TabBar.tsx b/frontend/components/layout/TabBar.tsx index 8a850d20..e86ada2e 100644 --- a/frontend/components/layout/TabBar.tsx +++ b/frontend/components/layout/TabBar.tsx @@ -501,7 +501,7 @@ export function TabBar() { touchAction: "none", ...animStyle, ...(hiddenByGhost ? { opacity: 0 } : {}), - ...(isActive ? { boxShadow: "0 -1px 28px rgba(0,0,0,0.9)" } : {}), + ...(isActive ? {} : {}), }} title={tab.title} > @@ -542,13 +542,13 @@ export function TabBar() { <>
-
+
{displayVisible.map((tab, i) => renderTab(tab, i))} {hasOverflow && ( @@ -587,7 +587,7 @@ export function TabBar() { {ghostStyle && draggedTab && (
{draggedTab.title} diff --git a/frontend/components/screen/EditModal.tsx b/frontend/components/screen/EditModal.tsx index 1300886b..372132d4 100644 --- a/frontend/components/screen/EditModal.tsx +++ b/frontend/components/screen/EditModal.tsx @@ -335,92 +335,6 @@ export const EditModal: React.FC = ({ className }) => { } }; - /** - * 디테일 행의 FK를 통해 마스터 테이블 데이터를 자동 조회 - * - entity join 설정에서 FK 관계 탐지 - * - FK 값으로 마스터 테이블 전체 row 조회 - * - editData에 없는 필드만 병합 (디테일 데이터를 덮어쓰지 않음) - */ - const loadMasterDataForDetailRow = async ( - editData: Record, - targetScreenId: number, - eventTableName?: string, - ): Promise> => { - try { - let detailTableName = eventTableName; - if (!detailTableName) { - const screenInfo = await screenApi.getScreen(targetScreenId); - detailTableName = screenInfo?.tableName; - } - - if (!detailTableName) { - console.log("[EditModal:MasterLoad] 테이블명을 알 수 없음 - 스킵"); - return {}; - } - - console.log("[EditModal:MasterLoad] 시작:", { detailTableName, editDataKeys: Object.keys(editData) }); - - const entityJoinRes = await entityJoinApi.getEntityJoinConfigs(detailTableName); - const joinConfigs = entityJoinRes?.joinConfigs || []; - - if (joinConfigs.length === 0) { - console.log("[EditModal:MasterLoad] entity join 없음 - 스킵"); - return {}; - } - - console.log( - "[EditModal:MasterLoad] entity join:", - joinConfigs.map((c) => `${c.sourceColumn} → ${c.referenceTable}`), - ); - - const masterDataResult: Record = {}; - const processedTables = new Set(); - const { apiClient } = await import("@/lib/api/client"); - - for (const joinConfig of joinConfigs) { - const { sourceColumn, referenceTable, referenceColumn } = joinConfig; - if (processedTables.has(referenceTable)) continue; - - const fkValue = editData[sourceColumn]; - if (!fkValue) continue; - - try { - const response = await apiClient.post(`/table-management/tables/${referenceTable}/data`, { - search: { [referenceColumn || "id"]: fkValue }, - size: 1, - page: 1, - autoFilter: true, - }); - - const rows = response.data?.data?.data || response.data?.data?.rows || []; - if (rows.length > 0) { - const masterRow = rows[0]; - for (const [col, val] of Object.entries(masterRow)) { - if (val !== undefined && val !== null && editData[col] === undefined) { - masterDataResult[col] = val; - } - } - console.log("[EditModal:MasterLoad] 조회 성공:", { - table: referenceTable, - fk: `${sourceColumn}=${fkValue}`, - loadedFields: Object.keys(masterDataResult), - }); - } - } catch (queryError) { - console.warn("[EditModal:MasterLoad] 조회 실패:", referenceTable, queryError); - } - - processedTables.add(referenceTable); - } - - console.log("[EditModal:MasterLoad] 최종 결과:", Object.keys(masterDataResult)); - return masterDataResult; - } catch (error) { - console.warn("[EditModal:MasterLoad] 전체 오류:", error); - return {}; - } - }; - // 전역 모달 이벤트 리스너 (활성 탭에서만 처리) useEffect(() => { const handleOpenEditModal = async (event: CustomEvent) => { diff --git a/frontend/components/screen/SaveModal.tsx b/frontend/components/screen/SaveModal.tsx index 2b6a9c78..e8868cfb 100644 --- a/frontend/components/screen/SaveModal.tsx +++ b/frontend/components/screen/SaveModal.tsx @@ -173,6 +173,13 @@ export const SaveModal: React.FC = ({ return; } + // ✅ 필수 항목 검증 + const validation = validateRequiredFields(); + if (!validation.isValid) { + toast.error(`필수 항목을 입력해주세요: ${validation.missingFields.join(", ")}`); + return; + } + try { setIsSaving(true);