diff --git a/backend-node/src/controllers/numberingRuleController.ts b/backend-node/src/controllers/numberingRuleController.ts index 1b2e2197..d00db2c4 100644 --- a/backend-node/src/controllers/numberingRuleController.ts +++ b/backend-node/src/controllers/numberingRuleController.ts @@ -112,6 +112,17 @@ router.post("/", authenticateToken, async (req: AuthenticatedRequest, res: Respo const userId = req.user!.userId; const ruleConfig = req.body; + logger.info("πŸ” [POST /numbering-rules] μ±„λ²ˆ κ·œμΉ™ 생성 μš”μ²­:", { + companyCode, + userId, + ruleId: ruleConfig.ruleId, + ruleName: ruleConfig.ruleName, + scopeType: ruleConfig.scopeType, + menuObjid: ruleConfig.menuObjid, + tableName: ruleConfig.tableName, + partsCount: ruleConfig.parts?.length, + }); + try { if (!ruleConfig.ruleId || !ruleConfig.ruleName) { return res.status(400).json({ success: false, error: "κ·œμΉ™ ID와 κ·œμΉ™λͺ…은 ν•„μˆ˜μž…λ‹ˆλ‹€" }); @@ -122,12 +133,22 @@ router.post("/", authenticateToken, async (req: AuthenticatedRequest, res: Respo } const newRule = await numberingRuleService.createRule(ruleConfig, companyCode, userId); + + logger.info("βœ… [POST /numbering-rules] μ±„λ²ˆ κ·œμΉ™ 생성 성곡:", { + ruleId: newRule.ruleId, + menuObjid: newRule.menuObjid, + }); + return res.status(201).json({ success: true, data: newRule }); } catch (error: any) { if (error.code === "23505") { return res.status(409).json({ success: false, error: "이미 μ‘΄μž¬ν•˜λŠ” κ·œμΉ™ IDμž…λ‹ˆλ‹€" }); } - logger.error("κ·œμΉ™ 생성 μ‹€νŒ¨", { error: error.message }); + logger.error("❌ [POST /numbering-rules] κ·œμΉ™ 생성 μ‹€νŒ¨:", { + error: error.message, + stack: error.stack, + code: error.code, + }); return res.status(500).json({ success: false, error: error.message }); } }); diff --git a/backend-node/src/controllers/screenManagementController.ts b/backend-node/src/controllers/screenManagementController.ts index f7900b94..95277664 100644 --- a/backend-node/src/controllers/screenManagementController.ts +++ b/backend-node/src/controllers/screenManagementController.ts @@ -60,6 +60,29 @@ export const getScreen = async ( } }; +// 화면에 ν• λ‹Ήλœ 메뉴 쑰회 +export const getScreenMenu = async ( + req: AuthenticatedRequest, + res: Response +): Promise => { + try { + const { id } = req.params; + const { companyCode } = req.user as any; + + const menuInfo = await screenManagementService.getMenuByScreen( + parseInt(id), + companyCode + ); + + res.json({ success: true, data: menuInfo }); + } catch (error) { + console.error("ν™”λ©΄ 메뉴 쑰회 μ‹€νŒ¨:", error); + res + .status(500) + .json({ success: false, message: "ν™”λ©΄ 메뉴 μ‘°νšŒμ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€." }); + } +}; + // ν™”λ©΄ 생성 export const createScreen = async ( req: AuthenticatedRequest, diff --git a/backend-node/src/routes/screenManagementRoutes.ts b/backend-node/src/routes/screenManagementRoutes.ts index 3fed9129..e307ccc5 100644 --- a/backend-node/src/routes/screenManagementRoutes.ts +++ b/backend-node/src/routes/screenManagementRoutes.ts @@ -3,6 +3,7 @@ import { authenticateToken } from "../middleware/authMiddleware"; import { getScreens, getScreen, + getScreenMenu, createScreen, updateScreen, updateScreenInfo, @@ -33,6 +34,7 @@ router.use(authenticateToken); // ν™”λ©΄ 관리 router.get("/screens", getScreens); router.get("/screens/:id", getScreen); +router.get("/screens/:id/menu", getScreenMenu); // 화면에 ν• λ‹Ήλœ 메뉴 쑰회 router.post("/screens", createScreen); router.put("/screens/:id", updateScreen); router.put("/screens/:id/info", updateScreenInfo); // ν™”λ©΄ μ •λ³΄λ§Œ μˆ˜μ • diff --git a/backend-node/src/services/commonCodeService.ts b/backend-node/src/services/commonCodeService.ts index 7bf40b7d..40c05861 100644 --- a/backend-node/src/services/commonCodeService.ts +++ b/backend-node/src/services/commonCodeService.ts @@ -158,6 +158,17 @@ export class CommonCodeService { try { const { search, isActive, page = 1, size = 20 } = params; + logger.info(`πŸ” [getCodes] μ½”λ“œ 쑰회 μ‹œμž‘:`, { + categoryCode, + menuObjid, + hasMenuObjid: !!menuObjid, + userCompanyCode, + search, + isActive, + page, + size, + }); + const whereConditions: string[] = ["code_category = $1"]; const values: any[] = [categoryCode]; let paramIndex = 2; @@ -169,7 +180,13 @@ export class CommonCodeService { whereConditions.push(`menu_objid = ANY($${paramIndex})`); values.push(siblingMenuObjids); paramIndex++; - logger.info(`메뉴별 μ½”λ“œ 필터링: ${menuObjid}, ν˜•μ œ 메뉴: ${siblingMenuObjids.join(', ')}`); + logger.info(`πŸ“‹ [getCodes] 메뉴별 μ½”λ“œ 필터링:`, { + menuObjid, + siblingMenuObjids, + siblingCount: siblingMenuObjids.length, + }); + } else { + logger.warn(`⚠️ [getCodes] menuObjid μ—†μŒ - μ „μ—­ μ½”λ“œ 쑰회`); } // νšŒμ‚¬λ³„ 필터링 (졜고 κ΄€λ¦¬μžκ°€ μ•„λ‹Œ 경우) @@ -199,6 +216,13 @@ export class CommonCodeService { const offset = (page - 1) * size; + logger.info(`πŸ“ [getCodes] μ‹€ν–‰ν•  쿼리:`, { + whereClause, + values, + whereConditions, + paramIndex, + }); + // μ½”λ“œ 쑰회 const codes = await query( `SELECT * FROM code_info @@ -217,9 +241,20 @@ export class CommonCodeService { const total = parseInt(countResult?.count || "0"); logger.info( - `μ½”λ“œ 쑰회 μ™„λ£Œ: ${categoryCode} - ${codes.length}개, 전체: ${total}개 (νšŒμ‚¬: ${userCompanyCode || "전체"})` + `βœ… [getCodes] μ½”λ“œ 쑰회 μ™„λ£Œ: ${categoryCode} - ${codes.length}개, 전체: ${total}개 (νšŒμ‚¬: ${userCompanyCode || "전체"}, menuObjid: ${menuObjid || "μ—†μŒ"})` ); + logger.info(`πŸ“Š [getCodes] 쑰회된 μ½”λ“œ 상세:`, { + categoryCode, + menuObjid, + codes: codes.map((c) => ({ + code_value: c.code_value, + code_name: c.code_name, + menu_objid: c.menu_objid, + company_code: c.company_code, + })), + }); + return { data: codes, total }; } catch (error) { logger.error(`μ½”λ“œ 쑰회 쀑 였λ₯˜ (${categoryCode}):`, error); diff --git a/backend-node/src/services/numberingRuleService.ts b/backend-node/src/services/numberingRuleService.ts index 2c89f188..4fb27e52 100644 --- a/backend-node/src/services/numberingRuleService.ts +++ b/backend-node/src/services/numberingRuleService.ts @@ -301,16 +301,18 @@ class NumberingRuleService { scope_type = 'global' OR scope_type = 'table' OR (scope_type = 'menu' AND menu_objid = ANY($1)) + OR (scope_type = 'table' AND menu_objid = ANY($1)) -- ⚠️ μž„μ‹œ: table μŠ€μ½”ν”„λ„ menu_objid둜 필터링 + OR (scope_type = 'table' AND menu_objid IS NULL) -- ⚠️ μž„μ‹œ: κΈ°μ‘΄ κ·œμΉ™(menu_objid NULL) 포함 ORDER BY - CASE scope_type - WHEN 'menu' THEN 1 - WHEN 'table' THEN 2 - WHEN 'global' THEN 3 + CASE + WHEN scope_type = 'menu' OR (scope_type = 'table' AND menu_objid = ANY($1)) THEN 1 + WHEN scope_type = 'table' THEN 2 + WHEN scope_type = 'global' THEN 3 END, created_at DESC `; params = [siblingObjids]; - logger.info("졜고 κ΄€λ¦¬μž: ν˜•μ œ 메뉴 포함 μ±„λ²ˆ κ·œμΉ™ 쑰회", { siblingObjids }); + logger.info("졜고 κ΄€λ¦¬μž: ν˜•μ œ 메뉴 포함 μ±„λ²ˆ κ·œμΉ™ 쑰회 (κΈ°μ‘΄ κ·œμΉ™ 포함)", { siblingObjids }); } else { // 일반 νšŒμ‚¬: μžμ‹ μ˜ κ·œμΉ™λ§Œ 쑰회 (ν˜•μ œ 메뉴 포함) query = ` @@ -335,17 +337,19 @@ class NumberingRuleService { scope_type = 'global' OR scope_type = 'table' OR (scope_type = 'menu' AND menu_objid = ANY($2)) + OR (scope_type = 'table' AND menu_objid = ANY($2)) -- ⚠️ μž„μ‹œ: table μŠ€μ½”ν”„λ„ menu_objid둜 필터링 + OR (scope_type = 'table' AND menu_objid IS NULL) -- ⚠️ μž„μ‹œ: κΈ°μ‘΄ κ·œμΉ™(menu_objid NULL) 포함 ) ORDER BY - CASE scope_type - WHEN 'menu' THEN 1 - WHEN 'table' THEN 2 - WHEN 'global' THEN 3 + CASE + WHEN scope_type = 'menu' OR (scope_type = 'table' AND menu_objid = ANY($2)) THEN 1 + WHEN scope_type = 'table' THEN 2 + WHEN scope_type = 'global' THEN 3 END, created_at DESC `; params = [companyCode, siblingObjids]; - logger.info("νšŒμ‚¬λ³„: ν˜•μ œ 메뉴 포함 μ±„λ²ˆ κ·œμΉ™ 쑰회", { companyCode, siblingObjids }); + logger.info("νšŒμ‚¬λ³„: ν˜•μ œ 메뉴 포함 μ±„λ²ˆ κ·œμΉ™ 쑰회 (κΈ°μ‘΄ κ·œμΉ™ 포함)", { companyCode, siblingObjids }); } logger.info("πŸ” μ±„λ²ˆ κ·œμΉ™ 쿼리 μ‹€ν–‰", { diff --git a/backend-node/src/services/screenManagementService.ts b/backend-node/src/services/screenManagementService.ts index f3c3d133..9c125578 100644 --- a/backend-node/src/services/screenManagementService.ts +++ b/backend-node/src/services/screenManagementService.ts @@ -1547,6 +1547,39 @@ export class ScreenManagementService { return screens.map((screen) => this.mapToScreenDefinition(screen)); } + /** + * 화면에 ν• λ‹Ήλœ 메뉴 쑰회 (첫 번째 ν• λ‹Ήλ§Œ λ°˜ν™˜) + * ν™”λ©΄ νŽΈμ§‘κΈ°μ—μ„œ menuObjidλ₯Ό κ°€μ Έμ˜€κΈ° μœ„ν•΄ μ‚¬μš© + */ + async getMenuByScreen( + screenId: number, + companyCode: string + ): Promise<{ menuObjid: number; menuName?: string } | null> { + const result = await queryOne<{ + menu_objid: string; + menu_name_kor?: string; + }>( + `SELECT sma.menu_objid, mi.menu_name_kor + FROM screen_menu_assignments sma + LEFT JOIN menu_info mi ON sma.menu_objid = mi.objid + WHERE sma.screen_id = $1 + AND sma.company_code = $2 + AND sma.is_active = 'Y' + ORDER BY sma.created_at ASC + LIMIT 1`, + [screenId, companyCode] + ); + + if (!result) { + return null; + } + + return { + menuObjid: parseInt(result.menu_objid), + menuName: result.menu_name_kor, + }; + } + /** * ν™”λ©΄-메뉴 ν• λ‹Ή ν•΄μ œ (βœ… Raw Query μ „ν™˜ μ™„λ£Œ) */ diff --git a/frontend/components/numbering-rule/NumberingRuleDesigner.tsx b/frontend/components/numbering-rule/NumberingRuleDesigner.tsx index 252f5403..0bd49982 100644 --- a/frontend/components/numbering-rule/NumberingRuleDesigner.tsx +++ b/frontend/components/numbering-rule/NumberingRuleDesigner.tsx @@ -12,7 +12,7 @@ import { NumberingRuleConfig, NumberingRulePart } from "@/types/numbering-rule"; import { NumberingRuleCard } from "./NumberingRuleCard"; import { NumberingRulePreview } from "./NumberingRulePreview"; import { - getNumberingRules, + getAvailableNumberingRules, createNumberingRule, updateNumberingRule, deleteNumberingRule, @@ -55,7 +55,20 @@ export const NumberingRuleDesigner: React.FC = ({ const loadRules = useCallback(async () => { setLoading(true); try { - const response = await getNumberingRules(menuObjid); + console.log("πŸ” [NumberingRuleDesigner] μ±„λ²ˆ κ·œμΉ™ λͺ©λ‘ λ‘œλ“œ μ‹œμž‘:", { + menuObjid, + hasMenuObjid: !!menuObjid, + }); + + const response = await getAvailableNumberingRules(menuObjid); + + console.log("πŸ“¦ [NumberingRuleDesigner] μ±„λ²ˆ κ·œμΉ™ API 응닡:", { + menuObjid, + success: response.success, + rulesCount: response.data?.length || 0, + rules: response.data, + }); + if (response.success && response.data) { setSavedRules(response.data); } else { @@ -135,17 +148,21 @@ export const NumberingRuleDesigner: React.FC = ({ try { const existing = savedRules.find((r) => r.ruleId === currentRule.ruleId); - // μ €μž₯ 전에 ν˜„μž¬ ν™”λ©΄μ˜ ν…Œμ΄λΈ”λͺ… μžλ™ μ„€μ • + // μ €μž₯ 전에 ν˜„μž¬ ν™”λ©΄μ˜ ν…Œμ΄λΈ”λͺ…κ³Ό menuObjid μžλ™ μ„€μ • const ruleToSave = { ...currentRule, - scopeType: "table" as const, // 항상 table둜 κ³ μ • + scopeType: "table" as const, // ⚠️ μž„μ‹œ: DB μ œμ•½ 쑰건 λ•Œλ¬Έμ— table μœ μ§€ tableName: currentTableName || currentRule.tableName || "", // ν˜„μž¬ ν…Œμ΄λΈ”λͺ… μžλ™ μ„€μ • + menuObjid: menuObjid || currentRule.menuObjid || null, // πŸ†• 메뉴 OBJID μ„€μ • (ν•„ν„°λ§μš©) }; console.log("πŸ’Ύ μ±„λ²ˆ κ·œμΉ™ μ €μž₯:", { currentTableName, + menuObjid, "currentRule.tableName": currentRule.tableName, + "currentRule.menuObjid": currentRule.menuObjid, "ruleToSave.tableName": ruleToSave.tableName, + "ruleToSave.menuObjid": ruleToSave.menuObjid, "ruleToSave.scopeType": ruleToSave.scopeType, ruleToSave, }); @@ -215,7 +232,7 @@ export const NumberingRuleDesigner: React.FC = ({ ); const handleNewRule = useCallback(() => { - console.log("πŸ“‹ μƒˆ κ·œμΉ™ 생성 - currentTableName:", currentTableName); + console.log("πŸ“‹ μƒˆ κ·œμΉ™ 생성:", { currentTableName, menuObjid }); const newRule: NumberingRuleConfig = { ruleId: `rule-${Date.now()}`, @@ -224,8 +241,9 @@ export const NumberingRuleDesigner: React.FC = ({ separator: "-", resetPeriod: "none", currentSequence: 1, - scopeType: "table", // 기본값을 table둜 μ„€μ • + scopeType: "table", // ⚠️ μž„μ‹œ: DB μ œμ•½ 쑰건 λ•Œλ¬Έμ— table μœ μ§€ tableName: currentTableName || "", // ν˜„μž¬ ν™”λ©΄μ˜ ν…Œμ΄λΈ”λͺ… μžλ™ μ„€μ • + menuObjid: menuObjid || null, // πŸ†• 메뉴 OBJID μ„€μ • (ν•„ν„°λ§μš©) }; console.log("πŸ“‹ μƒμ„±λœ κ·œμΉ™ 정보:", newRule); @@ -234,7 +252,7 @@ export const NumberingRuleDesigner: React.FC = ({ setCurrentRule(newRule); toast.success("μƒˆ κ·œμΉ™μ΄ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€"); - }, [currentTableName]); + }, [currentTableName, menuObjid]); return (
diff --git a/frontend/components/screen/InteractiveScreenViewer.tsx b/frontend/components/screen/InteractiveScreenViewer.tsx index 9eab7004..d408fc93 100644 --- a/frontend/components/screen/InteractiveScreenViewer.tsx +++ b/frontend/components/screen/InteractiveScreenViewer.tsx @@ -1092,15 +1092,12 @@ export const InteractiveScreenViewer: React.FC = ( const widget = comp as WidgetComponent; const config = widget.webTypeConfig as CodeTypeConfig | undefined; - console.log("πŸ” InteractiveScreenViewer - Code μœ„μ ― (κ³΅ν†΅μ½”λ“œ 선택):", { + console.log(`πŸ” [InteractiveScreenViewer] Code μœ„μ ― λ Œλ”λ§:`, { componentId: widget.id, - widgetType: widget.widgetType, columnName: widget.columnName, - fieldName, - currentValue, - formData, - config, codeCategory: config?.codeCategory, + menuObjid, + hasMenuObjid: !!menuObjid, }); // code νƒ€μž…μ€ κ³΅ν†΅μ½”λ“œ μ„ νƒλ°•μŠ€λ‘œ 처리 diff --git a/frontend/components/screen/ScreenDesigner.tsx b/frontend/components/screen/ScreenDesigner.tsx index 54f26a8d..fc412291 100644 --- a/frontend/components/screen/ScreenDesigner.tsx +++ b/frontend/components/screen/ScreenDesigner.tsx @@ -143,6 +143,9 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD }); const [isSaving, setIsSaving] = useState(false); + // πŸ†• 화면에 ν• λ‹Ήλœ 메뉴 OBJID + const [menuObjid, setMenuObjid] = useState(undefined); + // 메뉴 ν• λ‹Ή λͺ¨λ‹¬ μƒνƒœ const [showMenuAssignmentModal, setShowMenuAssignmentModal] = useState(false); @@ -880,6 +883,15 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD const loadLayout = async () => { try { + // πŸ†• 화면에 ν• λ‹Ήλœ 메뉴 쑰회 + const menuInfo = await screenApi.getScreenMenu(selectedScreen.screenId); + if (menuInfo) { + setMenuObjid(menuInfo.menuObjid); + console.log("πŸ”— 화면에 ν• λ‹Ήλœ 메뉴:", menuInfo); + } else { + console.warn("⚠️ 화면에 ν• λ‹Ήλœ 메뉴가 μ—†μŠ΅λ‹ˆλ‹€"); + } + const response = await screenApi.getLayout(selectedScreen.screenId); if (response) { // πŸ”„ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ ν•„μš” μ—¬λΆ€ 확인 @@ -4205,6 +4217,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD currentResolution={screenResolution} onResolutionChange={handleResolutionChange} allComponents={layout.components} // πŸ†• ν”Œλ‘œμš° μœ„μ ― κ°μ§€μš© + menuObjid={menuObjid} // πŸ†• 메뉴 OBJID 전달 /> @@ -4497,6 +4510,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD onDragStart={(e) => startComponentDrag(component, e)} onDragEnd={endDrag} selectedScreen={selectedScreen} + menuObjid={menuObjid} // πŸ†• 메뉴 OBJID 전달 // onZoneComponentDrop 제거 onZoneClick={handleZoneClick} // μ„€μ • λ³€κ²½ ν•Έλ“€λŸ¬ (ν…Œμ΄λΈ” νŽ˜μ΄μ§€ 크기 λ“± 섀정을 상세섀정에 반영) diff --git a/frontend/components/screen/panels/UnifiedPropertiesPanel.tsx b/frontend/components/screen/panels/UnifiedPropertiesPanel.tsx index 84297aa7..aa63e451 100644 --- a/frontend/components/screen/panels/UnifiedPropertiesPanel.tsx +++ b/frontend/components/screen/panels/UnifiedPropertiesPanel.tsx @@ -84,6 +84,8 @@ interface UnifiedPropertiesPanelProps { onResolutionChange?: (resolution: { name: string; width: number; height: number }) => void; // πŸ†• ν”Œλ‘œμš° μœ„μ ― κ°μ§€μš© allComponents?: ComponentData[]; + // πŸ†• 메뉴 OBJID (μ½”λ“œ/μΉ΄ν…Œκ³ λ¦¬ μŠ€μ½”ν”„μš©) + menuObjid?: number; } export const UnifiedPropertiesPanel: React.FC = ({ @@ -98,6 +100,7 @@ export const UnifiedPropertiesPanel: React.FC = ({ currentTableName, dragState, onStyleChange, + menuObjid, currentResolution, onResolutionChange, allComponents = [], // πŸ†• κΈ°λ³Έκ°’ 빈 λ°°μ—΄ @@ -685,6 +688,7 @@ export const UnifiedPropertiesPanel: React.FC = ({ screenTableName={selectedComponent.tableName || currentTable?.tableName || currentTableName} tableColumns={currentTable?.columns || []} tables={tables} + menuObjid={menuObjid} // πŸ†• 메뉴 OBJID 전달 onChange={(newConfig) => { console.log("πŸ”„ DynamicComponentConfigPanel onChange:", newConfig); // κ°œλ³„ μ†μ„±λ³„λ‘œ μ—…λ°μ΄νŠΈν•˜μ—¬ λ‹€λ₯Έ μ†μ„±κ³Όμ˜ 좩돌 λ°©μ§€ @@ -848,6 +852,7 @@ export const UnifiedPropertiesPanel: React.FC = ({ screenTableName={widget.tableName || currentTable?.tableName || currentTableName} tableColumns={currentTable?.columns || []} tables={tables} + menuObjid={menuObjid} // πŸ†• 메뉴 OBJID 전달 onChange={(newConfig) => { console.log("πŸ”„ DynamicComponentConfigPanel onChange (widget):", newConfig); // 전체 componentConfigλ₯Ό μ—…λ°μ΄νŠΈ diff --git a/frontend/hooks/queries/useCodes.ts b/frontend/hooks/queries/useCodes.ts index 0b95f907..470a4a60 100644 --- a/frontend/hooks/queries/useCodes.ts +++ b/frontend/hooks/queries/useCodes.ts @@ -33,7 +33,6 @@ export function useTableCodeCategory(tableName?: string, columnName?: string) { queryFn: async () => { if (!tableName || !columnName) return null; - console.log(`πŸ” [React Query] ν…Œμ΄λΈ” μ½”λ“œ μΉ΄ν…Œκ³ λ¦¬ 쑰회: ${tableName}.${columnName}`); const columns = await tableTypeApi.getColumns(tableName); const targetColumn = columns.find((col) => col.columnName === columnName); @@ -41,7 +40,6 @@ export function useTableCodeCategory(tableName?: string, columnName?: string) { ? targetColumn.codeCategory : null; - console.log(`βœ… [React Query] ν…Œμ΄λΈ” μ½”λ“œ μΉ΄ν…Œκ³ λ¦¬ κ²°κ³Ό: ${tableName}.${columnName} -> ${codeCategory}`); return codeCategory; }, enabled: !!(tableName && columnName), @@ -59,12 +57,25 @@ export function useCodeOptions(codeCategory?: string, enabled: boolean = true, m queryFn: async () => { if (!codeCategory || codeCategory === "none") return []; - console.log(`πŸ” [React Query] μ½”λ“œ μ˜΅μ…˜ 쑰회: ${codeCategory} (menuObjid: ${menuObjid})`); + console.log(`πŸ” [useCodeOptions] μ½”λ“œ μ˜΅μ…˜ 쑰회 μ‹œμž‘:`, { + codeCategory, + menuObjid, + hasMenuObjid: !!menuObjid, + }); + const response = await commonCodeApi.codes.getList(codeCategory, { isActive: true, menuObjid }); + console.log(`πŸ“¦ [useCodeOptions] API 응닡:`, { + codeCategory, + menuObjid, + success: response.success, + dataCount: response.data?.length || 0, + rawData: response.data, + }); + if (response.success && response.data) { const options = response.data.map((code: any) => { const actualValue = code.code || code.CODE || code.value || code.code_value || code.codeValue; @@ -78,7 +89,13 @@ export function useCodeOptions(codeCategory?: string, enabled: boolean = true, m }; }); - console.log(`βœ… [React Query] μ½”λ“œ μ˜΅μ…˜ κ²°κ³Ό: ${codeCategory} (${options.length}개, menuObjid: ${menuObjid})`); + console.log(`βœ… [useCodeOptions] μ˜΅μ…˜ λ³€ν™˜ μ™„λ£Œ:`, { + codeCategory, + menuObjid, + optionsCount: options.length, + options, + }); + return options; } diff --git a/frontend/lib/api/screen.ts b/frontend/lib/api/screen.ts index d1d07d96..736e61b0 100644 --- a/frontend/lib/api/screen.ts +++ b/frontend/lib/api/screen.ts @@ -46,6 +46,12 @@ export const screenApi = { } as ScreenDefinition; }, + // 화면에 ν• λ‹Ήλœ 메뉴 쑰회 + getScreenMenu: async (screenId: number): Promise<{ menuObjid: number; menuName?: string } | null> => { + const response = await apiClient.get(`/screen-management/screens/${screenId}/menu`); + return response.data?.data || null; + }, + // ν™”λ©΄ 생성 createScreen: async (screenData: CreateScreenRequest): Promise => { const response = await apiClient.post("/screen-management/screens", screenData); diff --git a/frontend/lib/registry/components/numbering-rule/NumberingRuleComponent.tsx b/frontend/lib/registry/components/numbering-rule/NumberingRuleComponent.tsx index 0c2e795c..6f1048f9 100644 --- a/frontend/lib/registry/components/numbering-rule/NumberingRuleComponent.tsx +++ b/frontend/lib/registry/components/numbering-rule/NumberingRuleComponent.tsx @@ -9,6 +9,7 @@ interface NumberingRuleWrapperProps { onChange?: (config: NumberingRuleComponentConfig) => void; isPreview?: boolean; tableName?: string; // ν˜„μž¬ ν™”λ©΄μ˜ ν…Œμ΄λΈ”λͺ… + menuObjid?: number; // πŸ†• 메뉴 OBJID } export const NumberingRuleWrapper: React.FC = ({ @@ -16,8 +17,14 @@ export const NumberingRuleWrapper: React.FC = ({ onChange, isPreview = false, tableName, + menuObjid, }) => { - console.log("πŸ“‹ NumberingRuleWrapper: ν…Œμ΄λΈ”λͺ… 전달", { tableName, config }); + console.log("πŸ“‹ NumberingRuleWrapper: ν…Œμ΄λΈ”λͺ… + menuObjid 전달", { + tableName, + menuObjid, + hasMenuObjid: !!menuObjid, + config + }); return (
@@ -26,6 +33,7 @@ export const NumberingRuleWrapper: React.FC = ({ isPreview={isPreview} className="h-full" currentTableName={tableName} // ν…Œμ΄λΈ”λͺ… 전달 + menuObjid={menuObjid} // πŸ†• 메뉴 OBJID 전달 />
); diff --git a/frontend/lib/registry/components/select-basic/SelectBasicComponent.tsx b/frontend/lib/registry/components/select-basic/SelectBasicComponent.tsx index 14580ce8..2597a143 100644 --- a/frontend/lib/registry/components/select-basic/SelectBasicComponent.tsx +++ b/frontend/lib/registry/components/select-basic/SelectBasicComponent.tsx @@ -50,17 +50,6 @@ const SelectBasicComponent: React.FC = ({ menuObjid, // πŸ†• 메뉴 OBJID ...props }) => { - // 🚨 μ΅œμš°μ„  디버깅: μ»΄ν¬λ„ŒνŠΈκ°€ μ‹€ν–‰λ˜λŠ”μ§€ 확인 - console.log("🚨🚨🚨 SelectBasicComponent 싀행됨!!!", { - componentId: component?.id, - componentType: component?.type, - webType: component?.webType, - tableName: component?.tableName, - columnName: component?.columnName, - screenId, - timestamp: new Date().toISOString(), - }); - const [isOpen, setIsOpen] = useState(false); // webTypeConfig λ˜λŠ” componentConfig μ‚¬μš© (DynamicWebTypeRenderer ν˜Έν™˜μ„±) @@ -79,30 +68,6 @@ const SelectBasicComponent: React.FC = ({ // autocomplete의 경우 검색어 관리 const [searchQuery, setSearchQuery] = useState(""); - console.log("πŸ” SelectBasicComponent μ΄ˆκΈ°ν™” (React Query):", { - componentId: component.id, - externalValue, - componentConfigValue: componentConfig?.value, - webTypeConfigValue: (props as any).webTypeConfig?.value, - configValue: config?.value, - finalSelectedValue: externalValue || config?.value || "", - tableName: component.tableName, - columnName: component.columnName, - staticCodeCategory: config?.codeCategory, - // React Query 디버깅 정보 - timestamp: new Date().toISOString(), - mountCount: ++(window as any).selectMountCount || ((window as any).selectMountCount = 1), - }); - - // μ–Έλ§ˆμš΄νŠΈ μ‹œ λ‘œκΉ… - useEffect(() => { - const componentId = component.id; - console.log(`πŸ” [${componentId}] SelectBasicComponent 마운트됨`); - - return () => { - console.log(`πŸ” [${componentId}] SelectBasicComponent μ–Έλ§ˆμš΄νŠΈλ¨`); - }; - }, [component.id]); const selectRef = useRef(null); @@ -117,11 +82,6 @@ const SelectBasicComponent: React.FC = ({ // μ½”λ“œ μΉ΄ν…Œκ³ λ¦¬ κ²°μ •: 동적 μΉ΄ν…Œκ³ λ¦¬ > μ„€μ • μΉ΄ν…Œκ³ λ¦¬ (λ©”λͺ¨μ΄μ œμ΄μ…˜) const codeCategory = useMemo(() => { const category = dynamicCodeCategory || staticCodeCategory; - console.log(`πŸ”‘ [${component.id}] μ½”λ“œ μΉ΄ν…Œκ³ λ¦¬ κ²°μ •:`, { - dynamicCodeCategory, - staticCodeCategory, - finalCategory: category, - }); return category; }, [dynamicCodeCategory, staticCodeCategory, component.id]); @@ -136,32 +96,25 @@ const SelectBasicComponent: React.FC = ({ isFetching, } = useCodeOptions(codeCategory, isCodeCategoryValid, menuObjid); - // React Query μƒνƒœ 디버깅 + // 디버깅: menuObjidκ°€ μ œλŒ€λ‘œ μ „λ‹¬λ˜λŠ”μ§€ 확인 useEffect(() => { - console.log(`🎯 [${component.id}] React Query μƒνƒœ:`, { - codeCategory, - isCodeCategoryValid, - codeOptionsLength: codeOptions.length, - isLoadingCodes, - isFetching, - cacheStatus: isFetching ? "FETCHING" : "FROM_CACHE", - }); - }, [component.id, codeCategory, isCodeCategoryValid, codeOptions.length, isLoadingCodes, isFetching]); + if (codeCategory && codeCategory !== "none") { + console.log(`🎯 [SelectBasicComponent ${component.id}] μ½”λ“œ μ˜΅μ…˜ λ‘œλ“œ:`, { + codeCategory, + menuObjid, + hasMenuObjid: !!menuObjid, + isCodeCategoryValid, + codeOptionsCount: codeOptions.length, + isLoading: isLoadingCodes, + }); + } + }, [component.id, codeCategory, menuObjid, codeOptions.length, isLoadingCodes, isCodeCategoryValid]); // μ™ΈλΆ€ value prop λ³€κ²½ μ‹œ selectedValue μ—…λ°μ΄νŠΈ useEffect(() => { const newValue = externalValue || config?.value || ""; // 값이 μ‹€μ œλ‘œ λ‹€λ₯Έ κ²½μš°μ—λ§Œ μ—…λ°μ΄νŠΈ (빈 λ¬Έμžμ—΄λ„ μœ νš¨ν•œ κ°’μœΌλ‘œ 처리) if (newValue !== selectedValue) { - console.log(`πŸ”„ SelectBasicComponent value μ—…λ°μ΄νŠΈ: "${selectedValue}" β†’ "${newValue}"`); - console.log("πŸ” μ—…λ°μ΄νŠΈ 쑰건 뢄석:", { - externalValue, - componentConfigValue: componentConfig?.value, - configValue: config?.value, - newValue, - selectedValue, - shouldUpdate: newValue !== selectedValue, - }); setSelectedValue(newValue); } }, [externalValue, config?.value]); @@ -190,23 +143,12 @@ const SelectBasicComponent: React.FC = ({ const labelMatch = options.find((option) => option.label === selectedValue); if (labelMatch) { newLabel = labelMatch.label; - console.log(`πŸ” [${component.id}] μ½”λ“œλͺ…μœΌλ‘œ 맀치 발견: "${selectedValue}" β†’ "${newLabel}"`); } else { // 2) selectedValueκ°€ μ½”λ“œκ°’μΈ 경우라면 μ›λž˜ λ‘œμ§λŒ€λ‘œ 라벨을 찾되, μ—†μœΌλ©΄ 원값 ν‘œμ‹œ newLabel = selectedValue; // μ½”λ“œκ°’ κ·ΈλŒ€λ‘œ ν‘œμ‹œ (예: "555") - console.log(`πŸ” [${component.id}] μ½”λ“œκ°’ 원본 μœ μ§€: "${selectedValue}"`); } } - console.log(`🏷️ [${component.id}] 라벨 μ—…λ°μ΄νŠΈ:`, { - selectedValue, - selectedOption: selectedOption ? { value: selectedOption.value, label: selectedOption.label } : null, - newLabel, - optionsCount: options.length, - allOptionsValues: options.map((o) => o.value), - allOptionsLabels: options.map((o) => o.label), - }); - if (newLabel !== selectedLabel) { setSelectedLabel(newLabel); } @@ -216,15 +158,6 @@ const SelectBasicComponent: React.FC = ({ const handleToggle = () => { if (isDesignMode) return; - console.log(`πŸ–±οΈ [${component.id}] λ“œλ‘­λ‹€μš΄ ν† κΈ€ (React Query): ${isOpen} β†’ ${!isOpen}`); - console.log(`πŸ“Š [${component.id}] ν˜„μž¬ μƒνƒœ:`, { - codeCategory, - isLoadingCodes, - codeOptionsLength: codeOptions.length, - tableName: component.tableName, - columnName: component.columnName, - }); - // React Queryκ°€ μžλ™μœΌλ‘œ μΊμ‹œ κ΄€λ¦¬ν•˜λ―€λ‘œ μˆ˜λ™ μƒˆλ‘œκ³ μΉ¨ λΆˆν•„μš” setIsOpen(!isOpen); }; @@ -242,17 +175,8 @@ const SelectBasicComponent: React.FC = ({ // μΈν„°λž™ν‹°λΈŒ λͺ¨λ“œμ—μ„œ 폼 데이터 μ—…λ°μ΄νŠΈ (TextInputComponent와 λ™μΌν•œ 둜직) if (isInteractive && onFormDataChange && component.columnName) { - console.log(`πŸ“€ SelectBasicComponent -> onFormDataChange 호좜: ${component.columnName} = "${value}"`); onFormDataChange(component.columnName, value); - } else { - console.log("❌ SelectBasicComponent onFormDataChange 쑰건 λ―ΈμΆ©μ‘±:", { - isInteractive, - hasOnFormDataChange: !!onFormDataChange, - hasColumnName: !!component.columnName, - }); } - - console.log(`βœ… [${component.id}] μ˜΅μ…˜ 선택:`, { value, label }); }; // μ™ΈλΆ€ 클릭 μ‹œ λ“œλ‘­λ‹€μš΄ λ‹«κΈ° @@ -280,12 +204,6 @@ const SelectBasicComponent: React.FC = ({ // λͺ¨λ“  μ˜΅μ…˜ κ°€μ Έμ˜€κΈ° const getAllOptions = () => { const configOptions = config.options || []; - console.log(`πŸ”§ [${component.id}] μ˜΅μ…˜ 병합:`, { - codeOptionsLength: codeOptions.length, - codeOptions: codeOptions.map((o: Option) => ({ value: o.value, label: o.label })), - configOptionsLength: configOptions.length, - configOptions: configOptions.map((o: Option) => ({ value: o.value, label: o.label })), - }); return [...codeOptions, ...configOptions]; };