Merge branch 'ksh-function-stabilization' into ksh-v2-work

ksh-function-stabilization의 9개 커밋을 ksh-v2-work에 병합한다.
[병합 내용]
- BLOCK O: pop-search 연결 탭 일관성 통합
- BLOCK P: 날짜 입력 타입 구현 + 셀 반응형 레이아웃
- pop-button 설정 패널 UX/UI 전면 개선
- 일괄 채번 + 모달 distinct + 선택 해제
- pop-scanner 바코드/QR 스캐너 컴포넌트
- pop-button 제어 실행 + 연결 데이터 UX
- BLOCK R: PC <-> POP 네비게이션 + Landing
- pop-profile 컴포넌트 (10번째 POP 컴포넌트)
- BLOCK S: 로그인 POP 모드 토글
[충돌 해결 3건 - 모두 양쪽 통합]
- UserDropdown.tsx: HEAD 결재함 + source POP 모드 메뉴 통합
- AppLayout.tsx: HEAD 결재함 + source POP 모드 메뉴 (모바일+사이드바 2곳)
- MenuFormModal.tsx: HEAD menuIcon 필드 + source 주석 제거 통합
This commit is contained in:
SeongHyun Kim
2026-03-09 15:36:53 +09:00
40 changed files with 3509 additions and 969 deletions

View File

@@ -621,6 +621,74 @@ export class AdminService {
}
}
/**
* POP 메뉴 목록 조회
* menu_name_kor에 'POP'이 포함되거나 menu_desc에 [POP] 태그가 있는 L1 메뉴의 하위 active 메뉴를 반환
* [POP_LANDING] 태그가 있는 하위 메뉴를 landingMenu로 별도 반환
*/
static async getPopMenuList(paramMap: any): Promise<{ parentMenu: any | null; childMenus: any[]; landingMenu: any | null }> {
try {
const { userCompanyCode, userType } = paramMap;
logger.info("AdminService.getPopMenuList 시작", { userCompanyCode, userType });
let queryParams: any[] = [];
let paramIndex = 1;
let companyFilter = "";
if (userType === "SUPER_ADMIN" && userCompanyCode === "*") {
companyFilter = `AND COMPANY_CODE = '*'`;
} else {
companyFilter = `AND COMPANY_CODE = $${paramIndex}`;
queryParams.push(userCompanyCode);
paramIndex++;
}
// POP L1 메뉴 조회
const parentMenus = await query<any>(
`SELECT OBJID, MENU_NAME_KOR, MENU_URL, MENU_DESC, SEQ, COMPANY_CODE, STATUS
FROM MENU_INFO
WHERE PARENT_OBJ_ID = 0
AND MENU_TYPE = 1
AND (
MENU_DESC LIKE '%[POP]%'
OR UPPER(MENU_NAME_KOR) LIKE '%POP%'
)
${companyFilter}
ORDER BY SEQ
LIMIT 1`,
queryParams
);
if (parentMenus.length === 0) {
logger.info("POP 메뉴 없음 (L1 POP 메뉴 미발견)");
return { parentMenu: null, childMenus: [], landingMenu: null };
}
const parentMenu = parentMenus[0];
// 하위 active 메뉴 조회 (부모와 같은 company_code로 필터링)
const childMenus = await query<any>(
`SELECT OBJID, MENU_NAME_KOR, MENU_URL, MENU_DESC, SEQ, COMPANY_CODE, STATUS
FROM MENU_INFO
WHERE PARENT_OBJ_ID = $1
AND STATUS = 'active'
AND COMPANY_CODE = $2
ORDER BY SEQ`,
[parentMenu.objid, parentMenu.company_code]
);
// [POP_LANDING] 태그가 있는 메뉴를 랜딩 화면으로 지정
const landingMenu = childMenus.find((m: any) => m.menu_desc?.includes("[POP_LANDING]")) || null;
logger.info(`POP 메뉴 조회 완료: 부모=${parentMenu.menu_name_kor}, 하위=${childMenus.length}개, 랜딩=${landingMenu?.menu_name_kor || '없음'}`);
return { parentMenu, childMenus, landingMenu };
} catch (error) {
logger.error("AdminService.getPopMenuList 오류:", error);
throw error;
}
}
/**
* 메뉴 정보 조회
*/