diff --git a/docs/화면관리_시스템_설계.md b/docs/화면관리_시스템_설계.md index 34fa3ccc..9003f341 100644 --- a/docs/화면관리_시스템_설계.md +++ b/docs/화면관리_시스템_설계.md @@ -42,6 +42,7 @@ - **웹타입 지원**: text, number, decimal, date, datetime, select, dropdown, textarea, boolean, checkbox, radio, code, entity, file - **데이터 테이블 컴포넌트**: 완전한 실시간 설정 시스템, 컬럼 관리, 필터링, 페이징 - **🆕 실시간 데이터 테이블**: 실제 PostgreSQL 데이터 조회, 웹타입별 검색 필터, 페이지네이션, 데이터 포맷팅 +- **🆕 화면 저장 후 메뉴 할당**: 저장 완료 시 자동 메뉴 할당 모달, 기존 화면 교체 확인, 시각적 피드백 및 자동 목록 복귀 #### 🔧 해결된 기술적 문제들 @@ -410,6 +411,9 @@ const removeItem = useCallback( - **회사별 메뉴 할당**: 각 회사의 메뉴에만 화면 할당 - **메뉴-화면 연결**: 메뉴와 화면의 1:1 또는 1:N 연결 - **권한 기반 메뉴 표시**: 사용자 권한에 따른 메뉴 표시 제어 +- **🆕 저장 후 자동 할당**: 화면 저장 완료 시 메뉴 할당 모달 자동 팝업 +- **🆕 기존 화면 교체**: 이미 할당된 화면이 있을 때 교체 확인 및 안전한 처리 +- **🆕 완전한 워크플로우**: 저장 → 할당 → 목록 복귀의 자연스러운 흐름 ## 🗄️ 데이터베이스 설계 @@ -1172,9 +1176,264 @@ function generateValidationRules(column: ColumnInfo): ValidationRule[] { } ``` +## 🎯 메뉴 할당 시스템 (신규 완성) + +### 1. 화면 저장 후 메뉴 할당 워크플로우 + +#### 전체 프로세스 + +``` +화면 설계 완료 → 저장 버튼 클릭 → 메뉴 할당 모달 자동 팝업 + ↓ +메뉴 선택 및 할당 OR "나중에 할당" 클릭 + ↓ +성공 화면 표시 (3초간 시각적 피드백) + ↓ +자동으로 화면 목록 페이지로 이동 +``` + +#### 메뉴 할당 모달 (MenuAssignmentModal) + +**주요 기능:** + +1. **관리자 메뉴만 표시**: 화면관리는 관리자 전용 기능이므로 관리자 메뉴(`menuType: "0"`)만 로드 +2. **셀렉트박스 내부 검색**: 메뉴명, URL, 설명으로 실시간 검색 가능 +3. **기존 화면 감지**: 선택한 메뉴에 이미 할당된 화면이 있는지 자동 확인 +4. **화면 교체 확인**: 기존 화면이 있을 때 교체 확인 대화상자 표시 + +```typescript +interface MenuAssignmentModalProps { + isOpen: boolean; + onClose: () => void; + screenInfo: ScreenDefinition | null; + onAssignmentComplete?: () => void; + onBackToList?: () => void; // 화면 목록으로 돌아가는 콜백 +} +``` + +### 2. 메뉴 검색 시스템 + +```typescript +// 셀렉트박스 내부 검색 구현 + + {/* 검색 입력 필드 */} +
+
+ + { + e.stopPropagation(); // 이벤트 전파 방지 + setSearchTerm(e.target.value); + }} + onKeyDown={(e) => e.stopPropagation()} + onClick={(e) => e.stopPropagation()} + className="h-8 pr-8 pl-10 text-sm" + /> + {searchTerm && ( + + )} +
+
+ {/* 메뉴 옵션들 */} +
{getMenuOptions()}
+
+``` + +### 3. 기존 화면 감지 및 교체 시스템 + +```typescript +// 메뉴 선택 시 기존 할당된 화면 확인 +const handleMenuSelect = async (menuId: string) => { + const menu = menus.find((m) => m.objid?.toString() === menuId); + setSelectedMenu(menu || null); + + if (menu) { + try { + const menuObjid = parseInt(menu.objid?.toString() || "0"); + const screens = await menuScreenApi.getScreensByMenu(menuObjid); + setExistingScreens(screens); + } catch (error) { + console.error("할당된 화면 조회 실패:", error); + } + } +}; + +// 할당 시 기존 화면 확인 +const handleAssignScreen = async () => { + if (existingScreens.length > 0) { + // 이미 같은 화면이 할당되어 있는지 확인 + const alreadyAssigned = existingScreens.some( + (screen) => screen.screenId === screenInfo.screenId + ); + if (alreadyAssigned) { + toast.info("이미 해당 메뉴에 할당된 화면입니다."); + return; + } + + // 다른 화면이 할당되어 있으면 교체 확인 + setShowReplaceDialog(true); + return; + } + + // 기존 화면이 없으면 바로 할당 + await performAssignment(); +}; +``` + +### 4. 화면 교체 확인 대화상자 + +**시각적 구분:** + +- 🔴 **제거될 화면**: 빨간색 배경으로 표시 +- 🟢 **새로 할당될 화면**: 초록색 배경으로 표시 +- 🟠 **주의 메시지**: 작업이 되돌릴 수 없음을 명확히 안내 + +**안전한 교체 프로세스:** + +1. 기존 화면들을 하나씩 제거 +2. 새 화면 할당 +3. 성공/실패 로그 출력 + +```typescript +// 기존 화면 교체인 경우 기존 화면들 먼저 제거 +if (replaceExisting && existingScreens.length > 0) { + for (const existingScreen of existingScreens) { + try { + await menuScreenApi.unassignScreenFromMenu( + existingScreen.screenId, + menuObjid + ); + console.log(`기존 화면 "${existingScreen.screenName}" 제거 완료`); + } catch (error) { + console.error( + `기존 화면 "${existingScreen.screenName}" 제거 실패:`, + error + ); + } + } +} + +// 새 화면 할당 +await menuScreenApi.assignScreenToMenu(screenInfo.screenId, menuObjid); +``` + +### 5. 성공 피드백 및 자동 이동 + +**성공 화면 구성:** + +- ✅ **체크마크 아이콘**: 성공을 나타내는 녹색 체크마크 +- 🎯 **성공 메시지**: 구체적인 할당 완료 메시지 +- ⏱️ **자동 이동 안내**: "3초 후 자동으로 화면 목록으로 이동합니다..." +- 🔵 **로딩 애니메이션**: 3개의 점이 순차적으로 바운스하는 애니메이션 + +```typescript +// 성공 상태 설정 +setAssignmentSuccess(true); +setAssignmentMessage(successMessage); + +// 3초 후 자동으로 화면 목록으로 이동 +setTimeout(() => { + if (onBackToList) { + onBackToList(); + } else { + onClose(); + } +}, 3000); +``` + +**성공 화면 UI:** + +```jsx +{assignmentSuccess ? ( + // 성공 화면 + <> + + +
+ + + +
+ 화면 할당 완료 +
+
+ +
+
+
+
+ +
+
+

{assignmentMessage}

+

+ 3초 후 자동으로 화면 목록으로 이동합니다... +

+
+
+
+ + {/* 로딩 애니메이션 */} +
+
+
+
+
+
+ +) : ( + // 기본 할당 화면 + // ... +)} +``` + +### 6. 사용자 경험 개선사항 + +1. **선택적 할당**: 필수가 아닌 선택적 기능으로 "나중에 할당" 가능 +2. **직관적 UI**: 저장된 화면 정보를 모달에서 바로 확인 가능 +3. **검색 기능**: 많은 메뉴 중에서 쉽게 찾을 수 있음 +4. **상태 표시**: 메뉴 활성/비활성 상태, 기존 할당된 화면 정보 표시 +5. **완전한 워크플로우**: 저장 → 할당 → 목록 복귀의 자연스러운 흐름 + ## 🌐 API 설계 -### 1. 화면 정의 API +### 1. 메뉴-화면 할당 API + +#### 화면을 메뉴에 할당 + +```typescript +POST /screen-management/screens/:screenId/assign-menu +Request: { + menuObjid: number; + displayOrder?: number; +} +``` + +#### 메뉴별 할당된 화면 목록 조회 + +```typescript +GET /screen-management/menus/:menuObjid/screens +Response: { + success: boolean; + data: ScreenDefinition[]; +} +``` + +#### 화면-메뉴 할당 해제 + +```typescript +DELETE /screen-management/screens/:screenId/menus/:menuObjid +Response: { + success: boolean; + message: string; +} +``` + +### 2. 화면 정의 API #### 화면 목록 조회 (회사별) @@ -2675,7 +2934,19 @@ export class TableTypeIntegrationService { 3. **커스터마이징**: 템플릿을 기반으로 필요한 부분 수정 4. **저장**: 커스터마이징된 화면 저장 -### 6. 메뉴 할당 및 관리 +### 6. 메뉴 할당 및 관리 (신규 완성) + +#### 🆕 저장 후 자동 메뉴 할당 + +1. **화면 저장 완료**: 화면 설계 완료 후 저장 버튼 클릭 +2. **메뉴 할당 모달 자동 팝업**: 저장 성공 시 즉시 메뉴 할당 모달 표시 +3. **관리자 메뉴 검색**: 메뉴명, URL, 설명으로 실시간 검색 +4. **기존 화면 확인**: 선택한 메뉴에 이미 할당된 화면 자동 감지 +5. **교체 확인**: 기존 화면이 있을 때 교체 여부 확인 대화상자 +6. **안전한 교체**: 기존 화면 제거 후 새 화면 할당 +7. **성공 피드백**: 3초간 성공 화면 표시 후 자동으로 화면 목록으로 이동 + +#### 기존 메뉴 할당 방식 1. **메뉴 선택**: 화면을 할당할 메뉴 선택 (회사별 메뉴만 표시) 2. **화면 할당**: 선택한 화면을 메뉴에 할당 @@ -2778,6 +3049,7 @@ export class TableTypeIntegrationService { - [x] 메뉴-화면 할당 기능 구현 - [x] 인터랙티브 화면 뷰어 구현 - [x] 사용자 피드백 반영 완료 +- [x] 🆕 화면 저장 후 메뉴 할당 워크플로우 구현 **구현 완료 사항:** @@ -2788,6 +3060,7 @@ export class TableTypeIntegrationService { - 메뉴 관리에서 화면 할당 기능 구현 - 할당된 화면을 실제 사용 가능한 인터랙티브 화면으로 렌더링 - 실제 사용자 입력 및 상호작용 가능한 완전 기능 화면 구현 +- 🆕 **완전한 메뉴 할당 워크플로우**: 저장 → 메뉴 할당 모달 → 기존 화면 교체 확인 → 성공 피드백 → 목록 복귀 ## 🎯 현재 구현된 핵심 기능 @@ -3559,3 +3832,4 @@ ComponentData = ContainerComponent | WidgetComponent | GroupComponent 등으로 - ✅ **메뉴 연동**: 설계한 화면을 실제 메뉴에 할당하여 즉시 사용 - ✅ **인터랙티브 화면**: 할당된 화면에서 실제 사용자 입력 및 상호작용 가능 - ✅ **13가지 웹 타입 지원**: 모든 업무 요구사항에 대응 가능한 다양한 위젯 +- ✅ **🆕 완전한 메뉴 할당 워크플로우**: 저장 → 자동 메뉴 할당 → 기존 화면 교체 확인 → 성공 피드백 → 목록 복귀의 완벽한 사용자 경험 diff --git a/frontend/components/screen/MenuAssignmentModal.tsx b/frontend/components/screen/MenuAssignmentModal.tsx index 00c74a6b..deceff6f 100644 --- a/frontend/components/screen/MenuAssignmentModal.tsx +++ b/frontend/components/screen/MenuAssignmentModal.tsx @@ -210,6 +210,29 @@ export const MenuAssignmentModal: React.FC = ({ } }; + // "나중에 할당" 처리 - 시각적 효과 포함 + const handleAssignLater = () => { + if (!screenInfo) return; + + // 성공 상태 설정 (나중에 할당 메시지) + setAssignmentSuccess(true); + setAssignmentMessage(`"${screenInfo.screenName}" 화면이 저장되었습니다. 나중에 메뉴에 할당할 수 있습니다.`); + + // 할당 완료 콜백 호출 + if (onAssignmentComplete) { + onAssignmentComplete(); + } + + // 3초 후 자동으로 화면 목록으로 이동 + setTimeout(() => { + if (onBackToList) { + onBackToList(); + } else { + onClose(); + } + }, 3000); + }; + // 필터된 메뉴 목록 const filteredMenus = menus.filter((menu) => { if (!searchTerm) return true; @@ -268,9 +291,13 @@ export const MenuAssignmentModal: React.FC = ({ - 화면 할당 완료 + {assignmentMessage.includes("나중에") ? "화면 저장 완료" : "화면 할당 완료"} - 화면이 성공적으로 메뉴에 할당되었습니다. + + {assignmentMessage.includes("나중에") + ? "화면이 성공적으로 저장되었습니다. 나중에 메뉴에 할당할 수 있습니다." + : "화면이 성공적으로 메뉴에 할당되었습니다."} +
@@ -427,17 +454,7 @@ export const MenuAssignmentModal: React.FC = ({
-