Merge pull request 'feature/screen-management' (#324) from feature/screen-management into main

Reviewed-on: http://39.117.244.52:3000/kjs/ERP-node/pulls/324
This commit is contained in:
kjs 2026-01-06 10:28:30 +09:00
commit 9ea0f1b84f
16 changed files with 80 additions and 37 deletions

View File

@ -55,3 +55,4 @@ export default router;

View File

@ -51,3 +51,4 @@ export default router;

View File

@ -67,3 +67,4 @@ export default router;

View File

@ -55,3 +55,4 @@ export default router;

View File

@ -65,6 +65,13 @@ export class AdminService {
}
);
// [임시 비활성화] 메뉴 권한 그룹 체크 - 모든 사용자에게 전체 메뉴 표시
// TODO: 권한 체크 다시 활성화 필요
logger.info(
`⚠️ [임시 비활성화] 권한 그룹 체크 스킵 - 사용자 ${userId}(${userType})에게 전체 메뉴 표시`
);
/* [ - ]
if (userType === "COMPANY_ADMIN") {
// 회사 관리자: 권한 그룹 기반 필터링 적용
if (userRoleGroups.length > 0) {
@ -141,6 +148,7 @@ export class AdminService {
return [];
}
}
*/
} else if (
menuType !== undefined &&
userType === "SUPER_ADMIN" &&
@ -412,6 +420,15 @@ export class AdminService {
let queryParams: any[] = [userLang];
let paramIndex = 2;
// [임시 비활성화] 메뉴 권한 그룹 체크 - 모든 사용자에게 전체 메뉴 표시
// TODO: 권한 체크 다시 활성화 필요
logger.info(
`⚠️ [임시 비활성화] getUserMenuList 권한 그룹 체크 스킵 - 사용자 ${userId}(${userType})에게 전체 메뉴 표시`
);
authFilter = "";
unionFilter = "";
/* [ - getUserMenuList ]
if (userType === "SUPER_ADMIN") {
// SUPER_ADMIN: 권한 그룹 체크 없이 해당 회사의 모든 메뉴 표시
logger.info(`✅ 좌측 사이드바 (SUPER_ADMIN): 회사 ${userCompanyCode}의 모든 메뉴 표시`);
@ -471,6 +488,7 @@ export class AdminService {
return [];
}
}
*/
// 2. 회사별 필터링 조건 생성
let companyFilter = "";

View File

@ -587,3 +587,4 @@ const result = await executeNodeFlow(flowId, {

View File

@ -360,3 +360,4 @@

View File

@ -346,3 +346,4 @@ const getComponentValue = (componentId: string) => {

View File

@ -127,3 +127,4 @@ export default function ScreenManagementPage() {
</div>
);
}

View File

@ -140,3 +140,4 @@ export const useActiveTabOptional = () => {

View File

@ -197,3 +197,4 @@ export function applyAutoFillToFormData(

View File

@ -299,6 +299,20 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
// 🆕 modalDataStore에서 선택된 데이터 확인 (분할 패널 등에서 저장됨)
const [modalStoreData, setModalStoreData] = useState<Record<string, any[]>>({});
// 🆕 splitPanelContext?.selectedLeftData를 로컬 상태로 추적 (리렌더링 보장)
const [trackedSelectedLeftData, setTrackedSelectedLeftData] = useState<Record<string, any> | null>(null);
// splitPanelContext?.selectedLeftData 변경 감지 및 로컬 상태 동기화
useEffect(() => {
const newData = splitPanelContext?.selectedLeftData ?? null;
setTrackedSelectedLeftData(newData);
console.log("🔄 [ButtonPrimary] selectedLeftData 변경 감지:", {
label: component.label,
hasData: !!newData,
dataKeys: newData ? Object.keys(newData) : [],
});
}, [splitPanelContext?.selectedLeftData, component.label]);
// modalDataStore 상태 구독 (실시간 업데이트)
useEffect(() => {
const actionConfig = component.componentConfig?.action;
@ -357,8 +371,8 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
// 2. 분할 패널 좌측 선택 데이터 확인
if (rowSelectionSource === "auto" || rowSelectionSource === "splitPanelLeft") {
// SplitPanelContext에서 확인
if (splitPanelContext?.selectedLeftData && Object.keys(splitPanelContext.selectedLeftData).length > 0) {
// SplitPanelContext에서 확인 (trackedSelectedLeftData 사용으로 리렌더링 보장)
if (trackedSelectedLeftData && Object.keys(trackedSelectedLeftData).length > 0) {
if (!hasSelection) {
hasSelection = true;
selectionCount = 1;
@ -397,7 +411,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
selectionCount,
selectionSource,
hasSplitPanelContext: !!splitPanelContext,
selectedLeftData: splitPanelContext?.selectedLeftData,
trackedSelectedLeftData: trackedSelectedLeftData,
selectedRowsData: selectedRowsData?.length,
selectedRows: selectedRows?.length,
flowSelectedData: flowSelectedData?.length,
@ -429,7 +443,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
component.label,
selectedRows,
selectedRowsData,
splitPanelContext?.selectedLeftData,
trackedSelectedLeftData,
flowSelectedData,
splitPanelContext,
modalStoreData,

View File

@ -2043,7 +2043,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
return row.id || row.uuid || `row-${index}`;
};
const handleRowSelection = (rowKey: string, checked: boolean) => {
const handleRowSelection = (rowKey: string, checked: boolean, rowData?: any) => {
const newSelectedRows = new Set(selectedRows);
if (checked) {
newSelectedRows.add(rowKey);
@ -2086,6 +2086,31 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
});
}
// 🆕 분할 패널 컨텍스트에 선택된 데이터 저장/해제 (체크박스 선택 시에도 작동)
const effectiveSplitPosition = splitPanelPosition || currentSplitPosition;
if (splitPanelContext && effectiveSplitPosition === "left" && !splitPanelContext.disableAutoDataTransfer) {
if (checked && selectedRowsData.length > 0) {
// 선택된 경우: 첫 번째 선택된 데이터 저장 (또는 전달된 rowData)
const dataToStore = rowData || selectedRowsData[selectedRowsData.length - 1];
splitPanelContext.setSelectedLeftData(dataToStore);
console.log("🔗 [TableList] handleRowSelection - 분할 패널 좌측 데이터 저장:", {
rowKey,
dataToStore,
});
} else if (!checked && selectedRowsData.length === 0) {
// 모든 선택이 해제된 경우: 데이터 초기화
splitPanelContext.setSelectedLeftData(null);
console.log("🔗 [TableList] handleRowSelection - 분할 패널 좌측 데이터 초기화");
} else if (selectedRowsData.length > 0) {
// 일부 선택 해제된 경우: 남은 첫 번째 데이터로 업데이트
splitPanelContext.setSelectedLeftData(selectedRowsData[0]);
console.log("🔗 [TableList] handleRowSelection - 분할 패널 좌측 데이터 업데이트:", {
remainingCount: selectedRowsData.length,
firstData: selectedRowsData[0],
});
}
}
const allRowsSelected = filteredData.every((row, index) => newSelectedRows.has(getRowKey(row, index)));
setIsAllSelected(allRowsSelected && filteredData.length > 0);
};
@ -2155,35 +2180,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
const rowKey = getRowKey(row, index);
const isCurrentlySelected = selectedRows.has(rowKey);
handleRowSelection(rowKey, !isCurrentlySelected);
// 🆕 분할 패널 컨텍스트에 선택된 데이터 저장 (좌측 화면인 경우)
// disableAutoDataTransfer가 true이면 자동 전달 비활성화 (버튼 클릭으로만 전달)
// currentSplitPosition을 사용하여 정확한 위치 확인 (splitPanelPosition이 없을 수 있음)
const effectiveSplitPosition = splitPanelPosition || currentSplitPosition;
console.log("🔗 [TableList] 행 클릭 - 분할 패널 위치 확인:", {
splitPanelPosition,
currentSplitPosition,
effectiveSplitPosition,
hasSplitPanelContext: !!splitPanelContext,
disableAutoDataTransfer: splitPanelContext?.disableAutoDataTransfer,
});
if (splitPanelContext && effectiveSplitPosition === "left" && !splitPanelContext.disableAutoDataTransfer) {
if (!isCurrentlySelected) {
// 선택된 경우: 데이터 저장
splitPanelContext.setSelectedLeftData(row);
console.log("🔗 [TableList] 분할 패널 좌측 데이터 저장:", {
row,
parentDataMapping: splitPanelContext.parentDataMapping,
});
} else {
// 선택 해제된 경우: 데이터 초기화
splitPanelContext.setSelectedLeftData(null);
console.log("🔗 [TableList] 분할 패널 좌측 데이터 초기화");
}
}
// handleRowSelection에서 분할 패널 데이터 처리도 함께 수행됨
handleRowSelection(rowKey, !isCurrentlySelected, row);
console.log("행 클릭:", { row, index, isSelected: !isCurrentlySelected });
};
@ -3918,7 +3916,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
if (enterRow) {
const rowKey = getRowKey(enterRow, rowIndex);
const isCurrentlySelected = selectedRows.has(rowKey);
handleRowSelection(rowKey, !isCurrentlySelected);
handleRowSelection(rowKey, !isCurrentlySelected, enterRow);
}
break;
case " ": // Space
@ -3928,7 +3926,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
if (spaceRow) {
const currentRowKey = getRowKey(spaceRow, rowIndex);
const isChecked = selectedRows.has(currentRowKey);
handleRowSelection(currentRowKey, !isChecked);
handleRowSelection(currentRowKey, !isChecked, spaceRow);
}
break;
case "F2":
@ -4142,7 +4140,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
return (
<Checkbox
checked={isChecked}
onCheckedChange={(checked) => handleRowSelection(rowKey, checked as boolean)}
onCheckedChange={(checked) => handleRowSelection(rowKey, checked as boolean, row)}
aria-label={`${index + 1} 선택`}
/>
);

View File

@ -1689,3 +1689,4 @@ const 출고등록_설정: ScreenSplitPanel = {

View File

@ -536,3 +536,4 @@ const { data: config } = await getScreenSplitPanel(screenId);

View File

@ -523,3 +523,4 @@ function ScreenViewPage() {