feat: 카테고리 컴포넌트 메뉴 스코프 전환 완료
✅ 구현 내용: 1. 백엔드 API 추가 - GET /api/table-management/menu/:menuObjid/category-columns - 형제 메뉴들의 테이블에서 카테고리 타입 컬럼 조회 - menuService.getSiblingMenuObjids() 재사용 2. 프론트엔드 CategoryWidget 수정 - menuObjid를 props로 받아 CategoryColumnList에 전달 - effectiveMenuObjid로 props.menuObjid도 처리 - 선택된 컬럼에 tableName 포함하여 상태 관리 3. CategoryColumnList 수정 - menuObjid 기반으로 형제 메뉴의 모든 카테고리 컬럼 조회 - 테이블명+컬럼명 함께 표시 - onColumnSelect에 tableName 전달 4. 메뉴 네비게이션 수정 - AppLayout.tsx: 화면 이동 시 menuObjid를 URL 쿼리 파라미터로 전달 - useMenu.ts: 동일하게 menuObjid 전달 - page.tsx: 자식 컴포넌트에도 menuObjid 전달 🎯 효과: - 이제 형제 메뉴들이 서로 다른 테이블을 사용해도 카테고리 공유 가능 - 메뉴 클릭 → 화면 이동 시 자동으로 menuObjid 전달 - 카테고리 위젯이 형제 메뉴의 모든 카테고리 컬럼 표시
This commit is contained in:
parent
668b45d4ea
commit
23911d3dd8
|
|
@ -1599,3 +1599,96 @@ export async function toggleLogTable(
|
||||||
res.status(500).json(response);
|
res.status(500).json(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 메뉴의 형제 메뉴들이 사용하는 모든 테이블의 카테고리 타입 컬럼 조회
|
||||||
|
*
|
||||||
|
* @route GET /api/table-management/menu/:menuObjid/category-columns
|
||||||
|
* @description 형제 메뉴들의 화면에서 사용하는 테이블의 input_type='category' 컬럼 조회
|
||||||
|
*/
|
||||||
|
export async function getCategoryColumnsByMenu(
|
||||||
|
req: AuthenticatedRequest,
|
||||||
|
res: Response
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
const { menuObjid } = req.params;
|
||||||
|
const companyCode = req.user?.companyCode;
|
||||||
|
|
||||||
|
logger.info("📥 메뉴별 카테고리 컬럼 조회 요청", { menuObjid, companyCode });
|
||||||
|
|
||||||
|
if (!menuObjid) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: "메뉴 OBJID가 필요합니다.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 형제 메뉴 조회
|
||||||
|
const { getSiblingMenuObjids } = await import("../services/menuService");
|
||||||
|
const siblingObjids = await getSiblingMenuObjids(parseInt(menuObjid));
|
||||||
|
|
||||||
|
logger.info("✅ 형제 메뉴 조회 완료", { siblingObjids });
|
||||||
|
|
||||||
|
// 2. 형제 메뉴들이 사용하는 테이블 조회
|
||||||
|
const { getPool } = await import("../database/db");
|
||||||
|
const pool = getPool();
|
||||||
|
|
||||||
|
const tablesQuery = `
|
||||||
|
SELECT DISTINCT table_name
|
||||||
|
FROM screen_definitions
|
||||||
|
WHERE menu_objid = ANY($1)
|
||||||
|
AND company_code = $2
|
||||||
|
AND table_name IS NOT NULL
|
||||||
|
`;
|
||||||
|
|
||||||
|
const tablesResult = await pool.query(tablesQuery, [siblingObjids, companyCode]);
|
||||||
|
const tableNames = tablesResult.rows.map((row: any) => row.table_name);
|
||||||
|
|
||||||
|
logger.info("✅ 형제 메뉴 테이블 조회 완료", { tableNames });
|
||||||
|
|
||||||
|
if (tableNames.length === 0) {
|
||||||
|
return res.json({
|
||||||
|
success: true,
|
||||||
|
data: [],
|
||||||
|
message: "형제 메뉴에 연결된 테이블이 없습니다.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 테이블들의 카테고리 타입 컬럼 조회
|
||||||
|
const columnsQuery = `
|
||||||
|
SELECT
|
||||||
|
table_name AS "tableName",
|
||||||
|
column_name AS "columnName",
|
||||||
|
column_label AS "columnLabel",
|
||||||
|
input_type AS "inputType"
|
||||||
|
FROM table_type_columns
|
||||||
|
WHERE table_name = ANY($1)
|
||||||
|
AND company_code = $2
|
||||||
|
AND input_type = 'category'
|
||||||
|
ORDER BY table_name, column_name
|
||||||
|
`;
|
||||||
|
|
||||||
|
const columnsResult = await pool.query(columnsQuery, [tableNames, companyCode]);
|
||||||
|
|
||||||
|
logger.info("✅ 카테고리 컬럼 조회 완료", {
|
||||||
|
columnCount: columnsResult.rows.length
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: columnsResult.rows,
|
||||||
|
message: "카테고리 컬럼 조회 성공",
|
||||||
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
logger.error("❌ 메뉴별 카테고리 컬럼 조회 실패", {
|
||||||
|
error: error.message,
|
||||||
|
errorStack: error.stack,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: "카테고리 컬럼 조회에 실패했습니다.",
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import {
|
||||||
getLogConfig,
|
getLogConfig,
|
||||||
getLogData,
|
getLogData,
|
||||||
toggleLogTable,
|
toggleLogTable,
|
||||||
|
getCategoryColumnsByMenu, // 🆕 메뉴별 카테고리 컬럼 조회
|
||||||
} from "../controllers/tableManagementController";
|
} from "../controllers/tableManagementController";
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
@ -187,4 +188,14 @@ router.get("/tables/:tableName/log", getLogData);
|
||||||
*/
|
*/
|
||||||
router.post("/tables/:tableName/log/toggle", toggleLogTable);
|
router.post("/tables/:tableName/log/toggle", toggleLogTable);
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// 메뉴 기반 카테고리 관리 API
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 메뉴의 형제 메뉴들이 사용하는 모든 테이블의 카테고리 타입 컬럼 조회
|
||||||
|
* GET /api/table-management/menu/:menuObjid/category-columns
|
||||||
|
*/
|
||||||
|
router.get("/menu/:menuObjid/category-columns", getCategoryColumnsByMenu);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
|
||||||
|
|
@ -473,6 +473,7 @@ export default function ScreenViewPage() {
|
||||||
userId={user?.userId}
|
userId={user?.userId}
|
||||||
userName={userName}
|
userName={userName}
|
||||||
companyCode={companyCode}
|
companyCode={companyCode}
|
||||||
|
menuObjid={menuObjid}
|
||||||
selectedRowsData={selectedRowsData}
|
selectedRowsData={selectedRowsData}
|
||||||
sortBy={tableSortBy}
|
sortBy={tableSortBy}
|
||||||
sortOrder={tableSortOrder}
|
sortOrder={tableSortOrder}
|
||||||
|
|
|
||||||
|
|
@ -274,10 +274,14 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
||||||
// 할당된 화면이 있으면 첫 번째 화면으로 이동
|
// 할당된 화면이 있으면 첫 번째 화면으로 이동
|
||||||
const firstScreen = assignedScreens[0];
|
const firstScreen = assignedScreens[0];
|
||||||
|
|
||||||
// 관리자 모드 상태를 쿼리 파라미터로 전달
|
// 관리자 모드 상태와 menuObjid를 쿼리 파라미터로 전달
|
||||||
const screenPath = isAdminMode
|
const params = new URLSearchParams();
|
||||||
? `/screens/${firstScreen.screenId}?mode=admin`
|
if (isAdminMode) {
|
||||||
: `/screens/${firstScreen.screenId}`;
|
params.set("mode", "admin");
|
||||||
|
}
|
||||||
|
params.set("menuObjid", menuObjid.toString());
|
||||||
|
|
||||||
|
const screenPath = `/screens/${firstScreen.screenId}?${params.toString()}`;
|
||||||
|
|
||||||
router.push(screenPath);
|
router.push(screenPath);
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
|
|
|
||||||
|
|
@ -6,20 +6,52 @@ import { CategoryValueManager } from "@/components/table-category/CategoryValueM
|
||||||
import { GripVertical } from "lucide-react";
|
import { GripVertical } from "lucide-react";
|
||||||
|
|
||||||
interface CategoryWidgetProps {
|
interface CategoryWidgetProps {
|
||||||
widgetId: string;
|
widgetId?: string;
|
||||||
tableName: string; // 현재 화면의 테이블
|
tableName?: string; // 현재 화면의 테이블 (옵션 - 형제 메뉴 전체 표시)
|
||||||
menuObjid?: number; // 현재 메뉴 OBJID (메뉴 스코프)
|
menuObjid?: number; // 현재 메뉴 OBJID (메뉴 스코프) - 필수
|
||||||
|
component?: any; // DynamicComponentRenderer에서 전달되는 컴포넌트 정보
|
||||||
|
[key: string]: any; // 추가 props 허용
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 카테고리 관리 위젯 (좌우 분할)
|
* 카테고리 관리 위젯 (좌우 분할)
|
||||||
* - 좌측: 현재 테이블의 카테고리 타입 컬럼 목록
|
* - 좌측: 형제 메뉴들의 모든 카테고리 타입 컬럼 목록 (메뉴 스코프)
|
||||||
* - 우측: 선택된 컬럼의 카테고리 값 관리 (메뉴 스코프)
|
* - 우측: 선택된 컬럼의 카테고리 값 관리 (메뉴 스코프)
|
||||||
*/
|
*/
|
||||||
export function CategoryWidget({ widgetId, tableName, menuObjid }: CategoryWidgetProps) {
|
export function CategoryWidget({ widgetId, tableName, menuObjid, component, ...props }: CategoryWidgetProps) {
|
||||||
|
// menuObjid가 없으면 경고 로그
|
||||||
|
React.useEffect(() => {
|
||||||
|
console.log("🔍 CategoryWidget 받은 props:", {
|
||||||
|
widgetId,
|
||||||
|
tableName,
|
||||||
|
menuObjid,
|
||||||
|
hasComponent: !!component,
|
||||||
|
propsKeys: Object.keys(props),
|
||||||
|
propsMenuObjid: props.menuObjid,
|
||||||
|
allProps: { widgetId, tableName, menuObjid, ...props },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!menuObjid && !props.menuObjid) {
|
||||||
|
console.warn("⚠️ CategoryWidget: menuObjid가 전달되지 않았습니다", {
|
||||||
|
component,
|
||||||
|
props,
|
||||||
|
allAvailableProps: { widgetId, tableName, menuObjid, ...props }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("✅ CategoryWidget 렌더링", {
|
||||||
|
widgetId,
|
||||||
|
tableName,
|
||||||
|
menuObjid: menuObjid || props.menuObjid
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [menuObjid, widgetId, tableName, component, props]);
|
||||||
|
// menuObjid 우선순위: 직접 전달된 값 > props에서 추출한 값
|
||||||
|
const effectiveMenuObjid = menuObjid || props.menuObjid;
|
||||||
|
|
||||||
const [selectedColumn, setSelectedColumn] = useState<{
|
const [selectedColumn, setSelectedColumn] = useState<{
|
||||||
columnName: string;
|
columnName: string;
|
||||||
columnLabel: string;
|
columnLabel: string;
|
||||||
|
tableName: string;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
||||||
const [leftWidth, setLeftWidth] = useState(15); // 초기값 15%
|
const [leftWidth, setLeftWidth] = useState(15); // 초기값 15%
|
||||||
|
|
@ -67,10 +99,10 @@ export function CategoryWidget({ widgetId, tableName, menuObjid }: CategoryWidge
|
||||||
<CategoryColumnList
|
<CategoryColumnList
|
||||||
tableName={tableName}
|
tableName={tableName}
|
||||||
selectedColumn={selectedColumn?.columnName || null}
|
selectedColumn={selectedColumn?.columnName || null}
|
||||||
onColumnSelect={(columnName, columnLabel) =>
|
onColumnSelect={(columnName, columnLabel, tableName) =>
|
||||||
setSelectedColumn({ columnName, columnLabel })
|
setSelectedColumn({ columnName, columnLabel, tableName })
|
||||||
}
|
}
|
||||||
menuObjid={menuObjid}
|
menuObjid={effectiveMenuObjid}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -86,10 +118,10 @@ export function CategoryWidget({ widgetId, tableName, menuObjid }: CategoryWidge
|
||||||
<div style={{ width: `${100 - leftWidth - 1}%` }} className="pl-3">
|
<div style={{ width: `${100 - leftWidth - 1}%` }} className="pl-3">
|
||||||
{selectedColumn ? (
|
{selectedColumn ? (
|
||||||
<CategoryValueManager
|
<CategoryValueManager
|
||||||
tableName={tableName}
|
tableName={selectedColumn.tableName}
|
||||||
columnName={selectedColumn.columnName}
|
columnName={selectedColumn.columnName}
|
||||||
columnLabel={selectedColumn.columnLabel}
|
columnLabel={selectedColumn.columnLabel}
|
||||||
menuObjid={menuObjid}
|
menuObjid={effectiveMenuObjid}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex h-64 flex-col items-center justify-center rounded-lg border bg-card shadow-sm">
|
<div className="flex h-64 flex-col items-center justify-center rounded-lg border bg-card shadow-sm">
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { getCategoryValues } from "@/lib/api/tableCategoryValue";
|
||||||
import { FolderTree, Loader2 } from "lucide-react";
|
import { FolderTree, Loader2 } from "lucide-react";
|
||||||
|
|
||||||
interface CategoryColumn {
|
interface CategoryColumn {
|
||||||
|
tableName: string;
|
||||||
columnName: string;
|
columnName: string;
|
||||||
columnLabel: string;
|
columnLabel: string;
|
||||||
inputType: string;
|
inputType: string;
|
||||||
|
|
@ -13,95 +14,84 @@ interface CategoryColumn {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CategoryColumnListProps {
|
interface CategoryColumnListProps {
|
||||||
tableName: string;
|
tableName: string; // 현재 화면의 테이블 (사용하지 않음 - 형제 메뉴 전체 표시)
|
||||||
selectedColumn: string | null;
|
selectedColumn: string | null;
|
||||||
onColumnSelect: (columnName: string, columnLabel: string) => void;
|
onColumnSelect: (columnName: string, columnLabel: string, tableName: string) => void;
|
||||||
menuObjid?: number; // 현재 메뉴 OBJID (메뉴 스코프)
|
menuObjid?: number; // 현재 메뉴 OBJID (필수)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 카테고리 컬럼 목록 (좌측 패널)
|
* 카테고리 컬럼 목록 (좌측 패널)
|
||||||
* - 현재 테이블에서 input_type='category'인 컬럼들을 표시 (메뉴 스코프)
|
* - 형제 메뉴들의 모든 카테고리 타입 컬럼을 표시 (메뉴 스코프)
|
||||||
*/
|
*/
|
||||||
export function CategoryColumnList({ tableName, selectedColumn, onColumnSelect, menuObjid }: CategoryColumnListProps) {
|
export function CategoryColumnList({ tableName, selectedColumn, onColumnSelect, menuObjid }: CategoryColumnListProps) {
|
||||||
const [columns, setColumns] = useState<CategoryColumn[]>([]);
|
const [columns, setColumns] = useState<CategoryColumn[]>([]);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadCategoryColumns();
|
if (menuObjid) {
|
||||||
}, [tableName]);
|
loadCategoryColumnsByMenu();
|
||||||
|
} else {
|
||||||
|
console.warn("⚠️ menuObjid가 없어서 카테고리 컬럼을 로드할 수 없습니다");
|
||||||
|
setColumns([]);
|
||||||
|
}
|
||||||
|
}, [menuObjid]);
|
||||||
|
|
||||||
const loadCategoryColumns = async () => {
|
const loadCategoryColumnsByMenu = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
// table_type_columns에서 input_type = 'category'인 컬럼 조회
|
console.log("🔍 형제 메뉴의 카테고리 컬럼 조회 시작", { menuObjid });
|
||||||
const response = await apiClient.get(`/table-management/tables/${tableName}/columns`);
|
|
||||||
|
// 새 API: 형제 메뉴들의 카테고리 컬럼 조회
|
||||||
|
const response = await apiClient.get(`/table-management/menu/${menuObjid}/category-columns`);
|
||||||
|
|
||||||
console.log("🔍 테이블 컬럼 API 응답:", {
|
console.log("✅ 메뉴별 카테고리 컬럼 API 응답:", {
|
||||||
tableName,
|
menuObjid,
|
||||||
response: response.data,
|
response: response.data,
|
||||||
type: typeof response.data,
|
|
||||||
isArray: Array.isArray(response.data),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// API 응답 구조 파싱 (여러 가능성 대응)
|
let categoryColumns: any[] = [];
|
||||||
let allColumns: any[] = [];
|
|
||||||
|
|
||||||
if (Array.isArray(response.data)) {
|
if (response.data.success && response.data.data) {
|
||||||
// response.data가 직접 배열인 경우
|
categoryColumns = response.data.data;
|
||||||
allColumns = response.data;
|
} else if (Array.isArray(response.data)) {
|
||||||
} else if (response.data.data && response.data.data.columns && Array.isArray(response.data.data.columns)) {
|
categoryColumns = response.data;
|
||||||
// response.data.data.columns가 배열인 경우 (table-management API)
|
|
||||||
allColumns = response.data.data.columns;
|
|
||||||
} else if (response.data.data && Array.isArray(response.data.data)) {
|
|
||||||
// response.data.data가 배열인 경우
|
|
||||||
allColumns = response.data.data;
|
|
||||||
} else if (response.data.columns && Array.isArray(response.data.columns)) {
|
|
||||||
// response.data.columns가 배열인 경우
|
|
||||||
allColumns = response.data.columns;
|
|
||||||
} else {
|
} else {
|
||||||
console.warn("⚠️ 예상하지 못한 API 응답 구조:", response.data);
|
console.warn("⚠️ 예상하지 못한 API 응답 구조:", response.data);
|
||||||
allColumns = [];
|
categoryColumns = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("🔍 파싱된 컬럼 목록:", {
|
console.log("✅ 카테고리 컬럼 파싱 완료:", {
|
||||||
totalColumns: allColumns.length,
|
|
||||||
sample: allColumns.slice(0, 3),
|
|
||||||
});
|
|
||||||
|
|
||||||
// category 타입만 필터링
|
|
||||||
const categoryColumns = allColumns.filter(
|
|
||||||
(col: any) => col.inputType === "category" || col.input_type === "category",
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log("✅ 카테고리 컬럼:", {
|
|
||||||
count: categoryColumns.length,
|
count: categoryColumns.length,
|
||||||
columns: categoryColumns.map((c: any) => ({
|
columns: categoryColumns.map((c: any) => ({
|
||||||
name: c.columnName || c.column_name,
|
table: c.tableName,
|
||||||
type: c.inputType || c.input_type,
|
column: c.columnName,
|
||||||
|
label: c.columnLabel,
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 각 컬럼의 값 개수 가져오기
|
||||||
const columnsWithCount = await Promise.all(
|
const columnsWithCount = await Promise.all(
|
||||||
categoryColumns.map(async (col: any) => {
|
categoryColumns.map(async (col: any) => {
|
||||||
const colName = col.columnName || col.column_name;
|
const colTable = col.tableName;
|
||||||
const colLabel = col.columnLabel || col.column_label || col.displayName || colName;
|
const colName = col.columnName;
|
||||||
|
const colLabel = col.columnLabel || colName;
|
||||||
|
|
||||||
// 각 컬럼의 값 개수 가져오기
|
|
||||||
let valueCount = 0;
|
let valueCount = 0;
|
||||||
try {
|
try {
|
||||||
const valuesResult = await getCategoryValues(tableName, colName, false, menuObjid);
|
const valuesResult = await getCategoryValues(colTable, colName, false, menuObjid);
|
||||||
if (valuesResult.success && valuesResult.data) {
|
if (valuesResult.success && valuesResult.data) {
|
||||||
valueCount = valuesResult.data.length;
|
valueCount = valuesResult.data.length;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`항목 개수 조회 실패 (${colName}):`, error);
|
console.error(`항목 개수 조회 실패 (${colTable}.${colName}):`, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
tableName: colTable,
|
||||||
columnName: colName,
|
columnName: colName,
|
||||||
columnLabel: colLabel,
|
columnLabel: colLabel,
|
||||||
inputType: col.inputType || col.input_type,
|
inputType: col.inputType,
|
||||||
valueCount,
|
valueCount,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
@ -112,7 +102,7 @@ export function CategoryColumnList({ tableName, selectedColumn, onColumnSelect,
|
||||||
// 첫 번째 컬럼 자동 선택
|
// 첫 번째 컬럼 자동 선택
|
||||||
if (columnsWithCount.length > 0 && !selectedColumn) {
|
if (columnsWithCount.length > 0 && !selectedColumn) {
|
||||||
const firstCol = columnsWithCount[0];
|
const firstCol = columnsWithCount[0];
|
||||||
onColumnSelect(firstCol.columnName, firstCol.columnLabel);
|
onColumnSelect(firstCol.columnName, firstCol.columnLabel, firstCol.tableName);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ 카테고리 컬럼 조회 실패:", error);
|
console.error("❌ 카테고리 컬럼 조회 실패:", error);
|
||||||
|
|
@ -153,27 +143,31 @@ export function CategoryColumnList({ tableName, selectedColumn, onColumnSelect,
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{columns.map((column) => (
|
{columns.map((column) => {
|
||||||
<div
|
const uniqueKey = `${column.tableName}.${column.columnName}`;
|
||||||
key={column.columnName}
|
return (
|
||||||
onClick={() => onColumnSelect(column.columnName, column.columnLabel || column.columnName)}
|
<div
|
||||||
className={`cursor-pointer rounded-lg border px-4 py-2 transition-all ${
|
key={uniqueKey}
|
||||||
selectedColumn === column.columnName ? "border-primary bg-primary/10 shadow-sm" : "hover:bg-muted/50"
|
onClick={() => onColumnSelect(column.columnName, column.columnLabel || column.columnName, column.tableName)}
|
||||||
}`}
|
className={`cursor-pointer rounded-lg border px-4 py-2 transition-all ${
|
||||||
>
|
selectedColumn === column.columnName ? "border-primary bg-primary/10 shadow-sm" : "hover:bg-muted/50"
|
||||||
<div className="flex items-center gap-2">
|
}`}
|
||||||
<FolderTree
|
>
|
||||||
className={`h-4 w-4 ${selectedColumn === column.columnName ? "text-primary" : "text-muted-foreground"}`}
|
<div className="flex items-center gap-2">
|
||||||
/>
|
<FolderTree
|
||||||
<div className="flex-1">
|
className={`h-4 w-4 ${selectedColumn === column.columnName ? "text-primary" : "text-muted-foreground"}`}
|
||||||
<h4 className="text-sm font-semibold">{column.columnLabel || column.columnName}</h4>
|
/>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h4 className="text-sm font-semibold">{column.columnLabel || column.columnName}</h4>
|
||||||
|
<p className="text-muted-foreground text-xs">{column.tableName}</p>
|
||||||
|
</div>
|
||||||
|
<span className="text-muted-foreground text-xs font-medium">
|
||||||
|
{column.valueCount !== undefined ? `${column.valueCount}개` : "..."}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-muted-foreground text-xs font-medium">
|
|
||||||
{column.valueCount !== undefined ? `${column.valueCount}개` : "..."}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
))}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,8 @@ export const useMenu = (user: any, authLoading: boolean) => {
|
||||||
if (assignedScreens.length > 0) {
|
if (assignedScreens.length > 0) {
|
||||||
// 할당된 화면이 있으면 첫 번째 화면으로 이동
|
// 할당된 화면이 있으면 첫 번째 화면으로 이동
|
||||||
const firstScreen = assignedScreens[0];
|
const firstScreen = assignedScreens[0];
|
||||||
router.push(`/screens/${firstScreen.screenId}`);
|
// menuObjid를 쿼리 파라미터로 전달
|
||||||
|
router.push(`/screens/${firstScreen.screenId}?menuObjid=${menuObjid}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue