123
This commit is contained in:
@@ -387,21 +387,21 @@
|
||||
<select id="selectUserList" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
SABUN
|
||||
, USER_ID AS "userId"
|
||||
, USER_NAME AS "userName"
|
||||
, COALESCE(USER_NAME_ENG, '') AS "userNameEng"
|
||||
, COALESCE(DEPT_CODE, '') AS "deptCode"
|
||||
, COALESCE(DEPT_NAME, '') AS "deptName"
|
||||
, COALESCE(POSITION_CODE, '') AS "positionCode"
|
||||
, COALESCE(POSITION_NAME, '') AS "positionName"
|
||||
, USER_ID
|
||||
, USER_NAME
|
||||
, COALESCE(USER_NAME_ENG, '') AS USER_NAME_ENG
|
||||
, COALESCE(DEPT_CODE, '') AS DEPT_CODE
|
||||
, COALESCE(DEPT_NAME, '') AS DEPT_NAME
|
||||
, COALESCE(POSITION_CODE, '') AS POSITION_CODE
|
||||
, COALESCE(POSITION_NAME, '') AS POSITION_NAME
|
||||
, COALESCE(EMAIL, '') AS EMAIL
|
||||
, COALESCE(TEL, '') AS TEL
|
||||
, COALESCE(CELL_PHONE, '') AS "cellPhone"
|
||||
, COALESCE(USER_TYPE, '') AS "userType"
|
||||
, COALESCE(USER_TYPE_NAME, '') AS "userTypeName"
|
||||
, COALESCE(TO_CHAR(CREATED_DATE, 'YYYY-MM-DD'), '') AS "regDate"
|
||||
, COALESCE(CELL_PHONE, '') AS CELL_PHONE
|
||||
, COALESCE(USER_TYPE, '') AS USER_TYPE
|
||||
, COALESCE(USER_TYPE_NAME, '') AS USER_TYPE_NAME
|
||||
, COALESCE(TO_CHAR(CREATED_DATE, 'YYYY-MM-DD'), '') AS REG_DATE
|
||||
, STATUS
|
||||
, COMPANY_CODE AS "companyCode"
|
||||
, COMPANY_CODE
|
||||
, COALESCE(LOCALE, '') AS LOCALE
|
||||
FROM USER_INFO
|
||||
WHERE 1=1
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
컬럼: flow_id (PK, serial), flow_name, flow_description, flow_data (JSONB),
|
||||
company_code, CREATED_DATE, UPDATED_DATE
|
||||
|
||||
Node 코드와 동일한 camelCase 응답 키를 위해 AS "camelCase" 별칭 사용
|
||||
snake_case 컬럼명 그대로 반환 (alias 없음)
|
||||
-->
|
||||
|
||||
<!-- ────────────────────────────────────────────────────── -->
|
||||
@@ -16,12 +16,12 @@
|
||||
<!-- ────────────────────────────────────────────────────── -->
|
||||
<select id="getNodeFlowList" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
FLOW_ID AS "flowId"
|
||||
, FLOW_NAME AS "flowName"
|
||||
, FLOW_DESCRIPTION AS "flowDescription"
|
||||
, COMPANY_CODE AS "companyCode"
|
||||
, CREATED_DATE AS "createdAt"
|
||||
, UPDATED_DATE AS "updatedAt"
|
||||
FLOW_ID
|
||||
, FLOW_NAME
|
||||
, FLOW_DESCRIPTION
|
||||
, COMPANY_CODE
|
||||
, CREATED_DATE
|
||||
, UPDATED_DATE
|
||||
FROM NODE_FLOWS
|
||||
WHERE 1=1
|
||||
<if test="company_code != null and company_code != "*"">
|
||||
@@ -35,12 +35,12 @@
|
||||
<!-- ────────────────────────────────────────────────────── -->
|
||||
<select id="getNodeFlowInfo" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
FLOW_ID AS "flowId"
|
||||
, FLOW_NAME AS "flowName"
|
||||
, FLOW_DESCRIPTION AS "flowDescription"
|
||||
, FLOW_DATA AS "flowData"
|
||||
, CREATED_DATE AS "createdAt"
|
||||
, UPDATED_DATE AS "updatedAt"
|
||||
FLOW_ID
|
||||
, FLOW_NAME
|
||||
, FLOW_DESCRIPTION
|
||||
, FLOW_DATA
|
||||
, CREATED_DATE
|
||||
, UPDATED_DATE
|
||||
FROM NODE_FLOWS
|
||||
WHERE FLOW_ID = #{flow_id}
|
||||
<include refid="common.companyCodeFilter"/>
|
||||
@@ -51,9 +51,9 @@
|
||||
<!-- ────────────────────────────────────────────────────── -->
|
||||
<select id="getNodeFlowData" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
FLOW_ID AS "flowId"
|
||||
, FLOW_NAME AS "flowName"
|
||||
, FLOW_DATA AS "flowData"
|
||||
FLOW_ID
|
||||
, FLOW_NAME
|
||||
, FLOW_DATA
|
||||
FROM NODE_FLOWS
|
||||
WHERE FLOW_ID = #{flow_id}
|
||||
</select>
|
||||
@@ -63,9 +63,9 @@
|
||||
<!-- ────────────────────────────────────────────────────── -->
|
||||
<select id="getNodeFlowForAudit" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
FLOW_NAME AS "flowName"
|
||||
, FLOW_DESCRIPTION AS "flowDescription"
|
||||
, COMPANY_CODE AS "companyCode"
|
||||
FLOW_NAME
|
||||
, FLOW_DESCRIPTION
|
||||
, COMPANY_CODE
|
||||
FROM NODE_FLOWS
|
||||
WHERE FLOW_ID = #{flow_id}
|
||||
</select>
|
||||
@@ -74,7 +74,7 @@
|
||||
<!-- 생성 -->
|
||||
<!-- ────────────────────────────────────────────────────── -->
|
||||
<insert id="insertNodeFlow" parameterType="map"
|
||||
useGeneratedKeys="true" keyProperty="flowId" keyColumn="flow_id">
|
||||
useGeneratedKeys="true" keyProperty="flow_id" keyColumn="flow_id">
|
||||
INSERT INTO NODE_FLOWS (
|
||||
FLOW_NAME
|
||||
, FLOW_DESCRIPTION
|
||||
|
||||
@@ -122,30 +122,30 @@
|
||||
<select id="getScreenSplitPanelInfo" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
SSP.ID,
|
||||
SSP.SCREEN_ID AS "screenId",
|
||||
SSP.LEFT_EMBEDDING_ID AS "leftEmbeddingId",
|
||||
SSP.RIGHT_EMBEDDING_ID AS "rightEmbeddingId",
|
||||
SSP.DATA_TRANSFER_ID AS "dataTransferId",
|
||||
SSP.LAYOUT_CONFIG AS "layoutConfig",
|
||||
SSP.COMPANY_CODE AS "companyCode",
|
||||
SSP.CREATED_DATE AS "createdAt",
|
||||
SSP.UPDATED_DATE AS "updatedAt",
|
||||
LE.PARENT_SCREEN_ID AS "leParentScreenId",
|
||||
LE.CHILD_SCREEN_ID AS "leChildScreenId",
|
||||
LE.POSITION AS "lePosition",
|
||||
LE.MODE AS "leMode",
|
||||
LE.CONFIG AS "leConfig",
|
||||
RE.PARENT_SCREEN_ID AS "reParentScreenId",
|
||||
RE.CHILD_SCREEN_ID AS "reChildScreenId",
|
||||
RE.POSITION AS "rePosition",
|
||||
RE.MODE AS "reMode",
|
||||
RE.CONFIG AS "reConfig",
|
||||
SDT.SOURCE_SCREEN_ID AS "sourceScreenId",
|
||||
SDT.TARGET_SCREEN_ID AS "targetScreenId",
|
||||
SDT.SOURCE_COMPONENT_ID AS "sourceComponentId",
|
||||
SDT.SOURCE_COMPONENT_TYPE AS "sourceComponentType",
|
||||
SDT.DATA_RECEIVERS AS "dataReceivers",
|
||||
SDT.BUTTON_CONFIG AS "buttonConfig"
|
||||
SSP.SCREEN_ID,
|
||||
SSP.LEFT_EMBEDDING_ID,
|
||||
SSP.RIGHT_EMBEDDING_ID,
|
||||
SSP.DATA_TRANSFER_ID,
|
||||
SSP.LAYOUT_CONFIG,
|
||||
SSP.COMPANY_CODE,
|
||||
SSP.CREATED_DATE,
|
||||
SSP.UPDATED_DATE,
|
||||
LE.PARENT_SCREEN_ID AS LE_PARENT_SCREEN_ID,
|
||||
LE.CHILD_SCREEN_ID AS LE_CHILD_SCREEN_ID,
|
||||
LE.POSITION AS LE_POSITION,
|
||||
LE.MODE AS LE_MODE,
|
||||
LE.CONFIG AS LE_CONFIG,
|
||||
RE.PARENT_SCREEN_ID AS RE_PARENT_SCREEN_ID,
|
||||
RE.CHILD_SCREEN_ID AS RE_CHILD_SCREEN_ID,
|
||||
RE.POSITION AS RE_POSITION,
|
||||
RE.MODE AS RE_MODE,
|
||||
RE.CONFIG AS RE_CONFIG,
|
||||
SDT.SOURCE_SCREEN_ID,
|
||||
SDT.TARGET_SCREEN_ID,
|
||||
SDT.SOURCE_COMPONENT_ID,
|
||||
SDT.SOURCE_COMPONENT_TYPE,
|
||||
SDT.DATA_RECEIVERS,
|
||||
SDT.BUTTON_CONFIG
|
||||
FROM SCREEN_SPLIT_PANEL SSP
|
||||
LEFT JOIN SCREEN_EMBEDDING LE ON SSP.LEFT_EMBEDDING_ID = LE.ID
|
||||
LEFT JOIN SCREEN_EMBEDDING RE ON SSP.RIGHT_EMBEDDING_ID = RE.ID
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
<select id="selectGroupScreensByGroupIds" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
SGS.ID,
|
||||
SGS.GROUP_ID AS "groupId",
|
||||
SGS.SCREEN_ID AS "screen_id",
|
||||
SGS.GROUP_ID,
|
||||
SGS.SCREEN_ID,
|
||||
SD.SCREEN_NAME,
|
||||
SGS.SCREEN_ROLE,
|
||||
SGS.DISPLAY_ORDER,
|
||||
|
||||
@@ -9,56 +9,56 @@
|
||||
|
||||
<select id="getCategoryColumnList" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
tc.table_name AS "tableName",
|
||||
tc.column_name AS "columnName",
|
||||
tc.column_name AS "columnLabel",
|
||||
COUNT(cv.value_id) AS "valueCount"
|
||||
TC.TABLE_NAME
|
||||
, TC.COLUMN_NAME
|
||||
, TC.COLUMN_NAME AS column_label
|
||||
, COUNT(CV.VALUE_ID) AS value_count
|
||||
|
||||
FROM table_type_columns tc
|
||||
FROM TABLE_TYPE_COLUMNS TC
|
||||
|
||||
LEFT JOIN category_values cv
|
||||
ON tc.table_name = cv.table_name
|
||||
AND tc.column_name = cv.column_name
|
||||
AND cv.is_active = TRUE
|
||||
LEFT JOIN CATEGORY_VALUES CV
|
||||
ON TC.TABLE_NAME = CV.TABLE_NAME
|
||||
AND TC.COLUMN_NAME = CV.COLUMN_NAME
|
||||
AND CV.IS_ACTIVE = TRUE
|
||||
<if test='company_code != null and company_code != "*"'>
|
||||
AND (cv.company_code = #{company_code} OR cv.company_code = '*')
|
||||
AND (CV.COMPANY_CODE = #{company_code} OR CV.COMPANY_CODE = '*')
|
||||
</if>
|
||||
|
||||
WHERE tc.table_name = #{table_name}
|
||||
AND tc.input_type = 'category'
|
||||
WHERE TC.TABLE_NAME = #{table_name}
|
||||
AND TC.INPUT_TYPE = 'category'
|
||||
|
||||
GROUP BY tc.table_name, tc.column_name, tc.display_order
|
||||
GROUP BY TC.TABLE_NAME, TC.COLUMN_NAME, TC.DISPLAY_ORDER
|
||||
|
||||
ORDER BY tc.display_order, tc.column_name
|
||||
ORDER BY TC.DISPLAY_ORDER, TC.COLUMN_NAME
|
||||
</select>
|
||||
|
||||
<select id="getAllCategoryColumnList" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
tc.table_name AS "tableName",
|
||||
tc.column_name AS "columnName",
|
||||
tc.column_name AS "columnLabel",
|
||||
COALESCE(cv_count.cnt, 0) AS "valueCount"
|
||||
TC.TABLE_NAME
|
||||
, TC.COLUMN_NAME
|
||||
, TC.COLUMN_NAME AS column_label
|
||||
, COALESCE(CV_COUNT.cnt, 0) AS value_count
|
||||
|
||||
FROM (
|
||||
SELECT DISTINCT table_name, column_name, MIN(display_order) AS display_order
|
||||
FROM table_type_columns
|
||||
WHERE input_type = 'category'
|
||||
GROUP BY table_name, column_name
|
||||
) tc
|
||||
SELECT DISTINCT TABLE_NAME, COLUMN_NAME, MIN(DISPLAY_ORDER) AS display_order
|
||||
FROM TABLE_TYPE_COLUMNS
|
||||
WHERE INPUT_TYPE = 'category'
|
||||
GROUP BY TABLE_NAME, COLUMN_NAME
|
||||
) TC
|
||||
|
||||
LEFT JOIN (
|
||||
SELECT table_name, column_name, COUNT(*) AS cnt
|
||||
FROM category_values
|
||||
WHERE is_active = TRUE
|
||||
SELECT TABLE_NAME, COLUMN_NAME, COUNT(*) AS cnt
|
||||
FROM CATEGORY_VALUES
|
||||
WHERE IS_ACTIVE = TRUE
|
||||
<if test='company_code != null and company_code != "*"'>
|
||||
AND (company_code = #{company_code} OR company_code = '*')
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
GROUP BY table_name, column_name
|
||||
) cv_count
|
||||
ON tc.table_name = cv_count.table_name
|
||||
AND tc.column_name = cv_count.column_name
|
||||
GROUP BY TABLE_NAME, COLUMN_NAME
|
||||
) CV_COUNT
|
||||
ON TC.TABLE_NAME = CV_COUNT.TABLE_NAME
|
||||
AND TC.COLUMN_NAME = CV_COUNT.COLUMN_NAME
|
||||
|
||||
ORDER BY tc.table_name, tc.display_order, tc.column_name
|
||||
ORDER BY TC.TABLE_NAME, TC.DISPLAY_ORDER, TC.COLUMN_NAME
|
||||
</select>
|
||||
|
||||
<!-- ══════════════════════════════════════════════════════════════
|
||||
@@ -67,98 +67,98 @@
|
||||
|
||||
<select id="getCategoryValueList" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
value_id AS value_id,
|
||||
table_name AS "tableName",
|
||||
column_name AS "columnName",
|
||||
value_code AS "valueCode",
|
||||
value_label AS "valueLabel",
|
||||
value_order AS "valueOrder",
|
||||
parent_value_id AS parent_value_id,
|
||||
depth,
|
||||
description,
|
||||
color,
|
||||
icon,
|
||||
is_active AS "isActive",
|
||||
is_default AS "isDefault",
|
||||
company_code AS "companyCode",
|
||||
menu_objid AS "menuObjid",
|
||||
CREATED_DATE AS "createdAt",
|
||||
UPDATED_DATE AS "updatedAt",
|
||||
created_by AS "createdBy",
|
||||
updated_by AS "updatedBy"
|
||||
VALUE_ID
|
||||
, TABLE_NAME
|
||||
, COLUMN_NAME
|
||||
, VALUE_CODE
|
||||
, VALUE_LABEL
|
||||
, VALUE_ORDER
|
||||
, PARENT_VALUE_ID
|
||||
, DEPTH
|
||||
, DESCRIPTION
|
||||
, COLOR
|
||||
, ICON
|
||||
, IS_ACTIVE
|
||||
, IS_DEFAULT
|
||||
, COMPANY_CODE
|
||||
, MENU_OBJID
|
||||
, CREATED_DATE
|
||||
, UPDATED_DATE
|
||||
, CREATED_BY
|
||||
, UPDATED_BY
|
||||
|
||||
FROM category_values
|
||||
FROM CATEGORY_VALUES
|
||||
|
||||
WHERE table_name = #{table_name}
|
||||
AND column_name = #{column_name}
|
||||
WHERE TABLE_NAME = #{table_name}
|
||||
AND COLUMN_NAME = #{column_name}
|
||||
<choose>
|
||||
<when test='company_code != null and company_code != "*"'>
|
||||
AND (company_code = #{company_code} OR company_code = '*')
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</when>
|
||||
<otherwise>
|
||||
AND company_code = '*'
|
||||
AND COMPANY_CODE = '*'
|
||||
</otherwise>
|
||||
</choose>
|
||||
<if test="include_inactive == null or include_inactive == false">
|
||||
AND is_active = TRUE
|
||||
AND IS_ACTIVE = TRUE
|
||||
</if>
|
||||
|
||||
ORDER BY value_order, value_label
|
||||
ORDER BY VALUE_ORDER, VALUE_LABEL
|
||||
</select>
|
||||
|
||||
<select id="getCategoryValueInfo" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
value_id AS "valueId",
|
||||
table_name AS "tableName",
|
||||
column_name AS "columnName",
|
||||
value_code AS "valueCode",
|
||||
value_label AS "valueLabel",
|
||||
value_order AS "valueOrder",
|
||||
parent_value_id AS "parentValueId",
|
||||
depth,
|
||||
description,
|
||||
color,
|
||||
icon,
|
||||
is_active AS "isActive",
|
||||
is_default AS "isDefault",
|
||||
company_code AS "companyCode",
|
||||
menu_objid AS "menuObjid",
|
||||
CREATED_DATE AS "createdAt",
|
||||
UPDATED_DATE AS "updatedAt",
|
||||
created_by AS "createdBy",
|
||||
updated_by AS "updatedBy"
|
||||
VALUE_ID
|
||||
, TABLE_NAME
|
||||
, COLUMN_NAME
|
||||
, VALUE_CODE
|
||||
, VALUE_LABEL
|
||||
, VALUE_ORDER
|
||||
, PARENT_VALUE_ID
|
||||
, DEPTH
|
||||
, DESCRIPTION
|
||||
, COLOR
|
||||
, ICON
|
||||
, IS_ACTIVE
|
||||
, IS_DEFAULT
|
||||
, COMPANY_CODE
|
||||
, MENU_OBJID
|
||||
, CREATED_DATE
|
||||
, UPDATED_DATE
|
||||
, CREATED_BY
|
||||
, UPDATED_BY
|
||||
|
||||
FROM category_values
|
||||
FROM CATEGORY_VALUES
|
||||
|
||||
WHERE value_id = #{value_id}
|
||||
WHERE VALUE_ID = #{value_id}
|
||||
</select>
|
||||
|
||||
<!-- 사용 여부 확인용: table_name, column_name, value_code, value_label 반환 -->
|
||||
<select id="getCategoryValueUsageInfo" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
table_name AS table_name,
|
||||
column_name AS column_name,
|
||||
value_code AS value_code,
|
||||
value_label AS value_label
|
||||
TABLE_NAME
|
||||
, COLUMN_NAME
|
||||
, VALUE_CODE
|
||||
, VALUE_LABEL
|
||||
|
||||
FROM category_values
|
||||
FROM CATEGORY_VALUES
|
||||
|
||||
WHERE value_id = #{value_id}
|
||||
WHERE VALUE_ID = #{value_id}
|
||||
<if test='company_code != null and company_code != "*"'>
|
||||
AND (company_code = #{company_code} OR company_code = '*')
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<!-- 수정 시 라벨 중복 체크용: table_name, column_name, company_code 반환 -->
|
||||
<select id="getCategoryValueLabelInfo" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
table_name AS table_name,
|
||||
column_name AS column_name,
|
||||
company_code AS company_code
|
||||
TABLE_NAME
|
||||
, COLUMN_NAME
|
||||
, COMPANY_CODE
|
||||
|
||||
FROM category_values
|
||||
FROM CATEGORY_VALUES
|
||||
|
||||
WHERE value_id = #{value_id}
|
||||
WHERE VALUE_ID = #{value_id}
|
||||
</select>
|
||||
|
||||
<!-- ══════════════════════════════════════════════════════════════
|
||||
@@ -166,44 +166,44 @@
|
||||
══════════════════════════════════════════════════════════════ -->
|
||||
|
||||
<select id="countDuplicateCode" parameterType="map" resultType="int">
|
||||
SELECT COUNT(*) FROM category_values
|
||||
WHERE table_name = #{table_name}
|
||||
AND column_name = #{column_name}
|
||||
AND value_code = #{value_code}
|
||||
AND menu_objid = #{menu_objid}
|
||||
SELECT COUNT(*) FROM CATEGORY_VALUES
|
||||
WHERE TABLE_NAME = #{table_name}
|
||||
AND COLUMN_NAME = #{column_name}
|
||||
AND VALUE_CODE = #{value_code}
|
||||
AND MENU_OBJID = #{menu_objid}
|
||||
<if test='company_code != null and company_code != "*"'>
|
||||
AND (company_code = #{company_code} OR company_code = '*')
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="countDuplicateLabel" parameterType="map" resultType="int">
|
||||
SELECT COUNT(*) FROM category_values
|
||||
WHERE table_name = #{table_name}
|
||||
AND column_name = #{column_name}
|
||||
AND value_label = #{value_label}
|
||||
AND is_active = TRUE
|
||||
SELECT COUNT(*) FROM CATEGORY_VALUES
|
||||
WHERE TABLE_NAME = #{table_name}
|
||||
AND COLUMN_NAME = #{column_name}
|
||||
AND VALUE_LABEL = #{value_label}
|
||||
AND IS_ACTIVE = TRUE
|
||||
<if test='company_code != null and company_code != "*"'>
|
||||
AND (company_code = #{company_code} OR company_code = '*')
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<!-- 수정 시 자기 자신 제외 라벨 중복 체크 (항상 company_code 필터) -->
|
||||
<select id="countDuplicateLabelExcludeSelf" parameterType="map" resultType="int">
|
||||
SELECT COUNT(*) FROM category_values
|
||||
WHERE table_name = #{table_name}
|
||||
AND column_name = #{column_name}
|
||||
AND value_label = #{value_label}
|
||||
AND (company_code = #{company_code} OR company_code = '*')
|
||||
AND is_active = TRUE
|
||||
AND value_id != #{value_id}
|
||||
SELECT COUNT(*) FROM CATEGORY_VALUES
|
||||
WHERE TABLE_NAME = #{table_name}
|
||||
AND COLUMN_NAME = #{column_name}
|
||||
AND VALUE_LABEL = #{value_label}
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
AND IS_ACTIVE = TRUE
|
||||
AND VALUE_ID != #{value_id}
|
||||
</select>
|
||||
|
||||
<insert id="insertCategoryValue" parameterType="map"
|
||||
useGeneratedKeys="true" keyProperty="valueId" keyColumn="value_id">
|
||||
INSERT INTO category_values (
|
||||
table_name, column_name, value_code, value_label, value_order,
|
||||
parent_value_id, depth, description, color, icon,
|
||||
is_active, is_default, company_code, menu_objid, created_by
|
||||
INSERT INTO CATEGORY_VALUES (
|
||||
TABLE_NAME, COLUMN_NAME, VALUE_CODE, VALUE_LABEL, VALUE_ORDER,
|
||||
PARENT_VALUE_ID, DEPTH, DESCRIPTION, COLOR, ICON,
|
||||
IS_ACTIVE, IS_DEFAULT, COMPANY_CODE, MENU_OBJID, CREATED_BY
|
||||
) VALUES (
|
||||
#{table_name}, #{column_name}, #{value_code}, #{value_label}, #{value_order},
|
||||
#{parent_value_id}, #{depth}, #{description}, #{color}, #{icon},
|
||||
@@ -212,21 +212,21 @@
|
||||
</insert>
|
||||
|
||||
<update id="updateCategoryValue" parameterType="map">
|
||||
UPDATE category_values
|
||||
UPDATE CATEGORY_VALUES
|
||||
<set>
|
||||
<if test="value_label != null">value_label = #{value_label},</if>
|
||||
<if test="value_order != null">value_order = #{value_order},</if>
|
||||
<if test="description != null">description = #{description},</if>
|
||||
<if test="color != null">color = #{color},</if>
|
||||
<if test="icon != null">icon = #{icon},</if>
|
||||
<if test="is_active != null">is_active = #{is_active},</if>
|
||||
<if test="is_default != null">is_default = #{is_default},</if>
|
||||
<if test="value_label != null">VALUE_LABEL = #{value_label},</if>
|
||||
<if test="value_order != null">VALUE_ORDER = #{value_order},</if>
|
||||
<if test="description != null">DESCRIPTION = #{description},</if>
|
||||
<if test="color != null">COLOR = #{color},</if>
|
||||
<if test="icon != null">ICON = #{icon},</if>
|
||||
<if test="is_active != null">IS_ACTIVE = #{is_active},</if>
|
||||
<if test="is_default != null">IS_DEFAULT = #{is_default},</if>
|
||||
UPDATED_DATE = NOW(),
|
||||
updated_by = #{user_id}
|
||||
UPDATED_BY = #{user_id}
|
||||
</set>
|
||||
WHERE value_id = #{value_id}
|
||||
WHERE VALUE_ID = #{value_id}
|
||||
<if test='company_code != null and company_code != "*"'>
|
||||
AND (company_code = #{company_code} OR company_code = '*')
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
</update>
|
||||
|
||||
@@ -251,52 +251,52 @@
|
||||
|
||||
WHERE ${safeColumnName} = #{value_code}
|
||||
<if test='company_code != null and company_code != "*"'>
|
||||
AND (company_code = #{company_code} OR company_code = '*')
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="getMenuListUsingTable" parameterType="map" resultType="map">
|
||||
SELECT DISTINCT
|
||||
mi.objid AS "menuObjid",
|
||||
mi.menu_name_kor AS menu_name,
|
||||
mi.menu_url AS "menuUrl"
|
||||
MI.OBJID AS menu_objid
|
||||
, MI.MENU_NAME_KOR AS menu_name
|
||||
, MI.MENU_URL
|
||||
|
||||
FROM menu_info mi
|
||||
FROM MENU_INFO MI
|
||||
|
||||
INNER JOIN screen_menu_assignments sma ON sma.menu_objid = mi.objid
|
||||
INNER JOIN screen_definitions sd ON sd.screen_id = sma.screen_id
|
||||
INNER JOIN SCREEN_MENU_ASSIGNMENTS SMA ON SMA.MENU_OBJID = MI.OBJID
|
||||
INNER JOIN SCREEN_DEFINITIONS SD ON SD.SCREEN_ID = SMA.SCREEN_ID
|
||||
|
||||
WHERE sd.table_name = #{table_name}
|
||||
AND (mi.company_code = #{company_code} OR mi.company_code = '*')
|
||||
WHERE SD.TABLE_NAME = #{table_name}
|
||||
AND (MI.COMPANY_CODE = #{company_code} OR MI.COMPANY_CODE = '*')
|
||||
|
||||
ORDER BY mi.menu_name_kor
|
||||
ORDER BY MI.MENU_NAME_KOR
|
||||
</select>
|
||||
|
||||
<!-- 재귀 CTE 로 모든 하위 value_id 수집 -->
|
||||
<select id="getChildValueIdList" parameterType="map" resultType="map">
|
||||
WITH RECURSIVE category_tree AS (
|
||||
SELECT value_id
|
||||
FROM category_values
|
||||
WHERE parent_value_id = #{value_id}
|
||||
SELECT VALUE_ID
|
||||
FROM CATEGORY_VALUES
|
||||
WHERE PARENT_VALUE_ID = #{value_id}
|
||||
<if test='company_code != null and company_code != "*"'>
|
||||
AND (company_code = #{company_code} OR company_code = '*')
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
UNION ALL
|
||||
SELECT cv.value_id
|
||||
FROM category_values cv
|
||||
INNER JOIN category_tree ct ON cv.parent_value_id = ct.value_id
|
||||
SELECT CV.VALUE_ID
|
||||
FROM CATEGORY_VALUES CV
|
||||
INNER JOIN category_tree CT ON CV.PARENT_VALUE_ID = CT.VALUE_ID
|
||||
<if test='company_code != null and company_code != "*"'>
|
||||
WHERE (cv.company_code = #{company_code} OR cv.company_code = '*')
|
||||
WHERE (CV.COMPANY_CODE = #{company_code} OR CV.COMPANY_CODE = '*')
|
||||
</if>
|
||||
)
|
||||
SELECT value_id AS value_id FROM category_tree
|
||||
SELECT VALUE_ID FROM category_tree
|
||||
</select>
|
||||
|
||||
<delete id="deleteValueById" parameterType="map">
|
||||
DELETE FROM category_values
|
||||
WHERE value_id = #{value_id}
|
||||
DELETE FROM CATEGORY_VALUES
|
||||
WHERE VALUE_ID = #{value_id}
|
||||
<if test='company_code != null and company_code != "*"'>
|
||||
AND (company_code = #{company_code} OR company_code = '*')
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
</delete>
|
||||
|
||||
@@ -305,26 +305,26 @@
|
||||
══════════════════════════════════════════════════════════════ -->
|
||||
|
||||
<update id="bulkSoftDeleteValues" parameterType="map">
|
||||
UPDATE category_values
|
||||
SET is_active = FALSE,
|
||||
UPDATE CATEGORY_VALUES
|
||||
SET IS_ACTIVE = FALSE,
|
||||
UPDATED_DATE = NOW(),
|
||||
updated_by = #{user_id}
|
||||
WHERE value_id IN
|
||||
UPDATED_BY = #{user_id}
|
||||
WHERE VALUE_ID IN
|
||||
<foreach collection="valueIds" item="id" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
<if test='company_code != null and company_code != "*"'>
|
||||
AND (company_code = #{company_code} OR company_code = '*')
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
</update>
|
||||
|
||||
<update id="updateValueOrder" parameterType="map">
|
||||
UPDATE category_values
|
||||
SET value_order = #{value_order},
|
||||
UPDATE CATEGORY_VALUES
|
||||
SET VALUE_ORDER = #{value_order},
|
||||
UPDATED_DATE = NOW()
|
||||
WHERE value_id = #{value_id}
|
||||
WHERE VALUE_ID = #{value_id}
|
||||
<if test='company_code != null and company_code != "*"'>
|
||||
AND (company_code = #{company_code} OR company_code = '*')
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
</update>
|
||||
|
||||
@@ -334,34 +334,34 @@
|
||||
|
||||
<select id="getColumnMappingList" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
logical_column_name AS logical_column_name,
|
||||
physical_column_name AS physical_column_name
|
||||
LOGICAL_COLUMN_NAME
|
||||
, PHYSICAL_COLUMN_NAME
|
||||
|
||||
FROM category_column_mapping
|
||||
FROM CATEGORY_COLUMN_MAPPING
|
||||
|
||||
WHERE table_name = #{table_name}
|
||||
AND menu_objid = #{menu_objid}
|
||||
WHERE TABLE_NAME = #{table_name}
|
||||
AND MENU_OBJID = #{menu_objid}
|
||||
<if test='company_code != null and company_code != "*"'>
|
||||
AND (company_code = #{company_code} OR company_code = '*')
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="getLogicalColumnList" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
mapping_id AS "mappingId",
|
||||
logical_column_name AS "logicalColumnName",
|
||||
physical_column_name AS "physicalColumnName",
|
||||
description
|
||||
MAPPING_ID
|
||||
, LOGICAL_COLUMN_NAME
|
||||
, PHYSICAL_COLUMN_NAME
|
||||
, DESCRIPTION
|
||||
|
||||
FROM category_column_mapping
|
||||
FROM CATEGORY_COLUMN_MAPPING
|
||||
|
||||
WHERE table_name = #{table_name}
|
||||
AND menu_objid = #{menu_objid}
|
||||
WHERE TABLE_NAME = #{table_name}
|
||||
AND MENU_OBJID = #{menu_objid}
|
||||
<if test='company_code != null and company_code != "*"'>
|
||||
AND (company_code = #{company_code} OR company_code = '*')
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
|
||||
ORDER BY logical_column_name
|
||||
ORDER BY LOGICAL_COLUMN_NAME
|
||||
</select>
|
||||
|
||||
<select id="checkPhysicalColumnExists" parameterType="map" resultType="int">
|
||||
@@ -373,46 +373,46 @@
|
||||
|
||||
<!-- UPSERT: ON CONFLICT (table_name, logical_column_name, menu_objid, company_code) -->
|
||||
<insert id="upsertColumnMapping" parameterType="map">
|
||||
INSERT INTO category_column_mapping (
|
||||
table_name, logical_column_name, physical_column_name,
|
||||
menu_objid, company_code, description, created_by, updated_by
|
||||
INSERT INTO CATEGORY_COLUMN_MAPPING (
|
||||
TABLE_NAME, LOGICAL_COLUMN_NAME, PHYSICAL_COLUMN_NAME,
|
||||
MENU_OBJID, COMPANY_CODE, DESCRIPTION, CREATED_BY, UPDATED_BY
|
||||
) VALUES (
|
||||
#{table_name}, #{logical_column_name}, #{physical_column_name},
|
||||
#{menu_objid}, #{company_code}, #{description}, #{user_id}, #{user_id}
|
||||
)
|
||||
ON CONFLICT (table_name, logical_column_name, menu_objid, company_code)
|
||||
DO UPDATE SET
|
||||
physical_column_name = EXCLUDED.physical_column_name,
|
||||
description = EXCLUDED.description,
|
||||
PHYSICAL_COLUMN_NAME = EXCLUDED.PHYSICAL_COLUMN_NAME,
|
||||
DESCRIPTION = EXCLUDED.DESCRIPTION,
|
||||
UPDATED_DATE = NOW(),
|
||||
updated_by = EXCLUDED.updated_by
|
||||
UPDATED_BY = EXCLUDED.UPDATED_BY
|
||||
</insert>
|
||||
|
||||
<select id="getColumnMappingInfo" parameterType="map" resultType="map">
|
||||
SELECT *
|
||||
|
||||
FROM category_column_mapping
|
||||
FROM CATEGORY_COLUMN_MAPPING
|
||||
|
||||
WHERE table_name = #{table_name}
|
||||
AND logical_column_name = #{logical_column_name}
|
||||
AND menu_objid = #{menu_objid}
|
||||
AND (company_code = #{company_code} OR company_code = '*')
|
||||
WHERE TABLE_NAME = #{table_name}
|
||||
AND LOGICAL_COLUMN_NAME = #{logical_column_name}
|
||||
AND MENU_OBJID = #{menu_objid}
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</select>
|
||||
|
||||
<delete id="deleteColumnMappingById" parameterType="map">
|
||||
DELETE FROM category_column_mapping
|
||||
WHERE mapping_id = #{mapping_id}
|
||||
DELETE FROM CATEGORY_COLUMN_MAPPING
|
||||
WHERE MAPPING_ID = #{mapping_id}
|
||||
<if test='company_code != null and company_code != "*"'>
|
||||
AND (company_code = #{company_code} OR company_code = '*')
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
</delete>
|
||||
|
||||
<delete id="deleteColumnMappingsByColumn" parameterType="map">
|
||||
DELETE FROM category_column_mapping
|
||||
WHERE table_name = #{table_name}
|
||||
AND logical_column_name = #{column_name}
|
||||
DELETE FROM CATEGORY_COLUMN_MAPPING
|
||||
WHERE TABLE_NAME = #{table_name}
|
||||
AND LOGICAL_COLUMN_NAME = #{column_name}
|
||||
<if test='company_code != null and company_code != "*"'>
|
||||
AND (company_code = #{company_code} OR company_code = '*')
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
</delete>
|
||||
|
||||
@@ -422,17 +422,17 @@
|
||||
|
||||
<select id="getLabelListByCodes" parameterType="map" resultType="map">
|
||||
SELECT DISTINCT
|
||||
value_code AS value_code,
|
||||
value_label AS value_label
|
||||
VALUE_CODE
|
||||
, VALUE_LABEL
|
||||
|
||||
FROM category_values
|
||||
FROM CATEGORY_VALUES
|
||||
|
||||
WHERE value_code IN
|
||||
WHERE VALUE_CODE IN
|
||||
<foreach collection="valueCodes" item="code" open="(" separator="," close=")">
|
||||
#{code}
|
||||
</foreach>
|
||||
<if test='company_code != null and company_code != "*"'>
|
||||
AND (company_code = #{company_code} OR company_code = '*')
|
||||
AND (COMPANY_CODE = #{company_code} OR COMPANY_CODE = '*')
|
||||
</if>
|
||||
</select>
|
||||
|
||||
@@ -448,23 +448,23 @@
|
||||
|
||||
<select id="getSecondLevelMenuList" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
m1.objid AS "menuObjid",
|
||||
m1.menu_name_kor AS "menuName",
|
||||
m0.menu_name_kor AS "parentMenuName",
|
||||
m1.screen_code AS "screenCode"
|
||||
M1.OBJID AS menu_objid
|
||||
, M1.MENU_NAME_KOR AS menu_name
|
||||
, M0.MENU_NAME_KOR AS parent_menu_name
|
||||
, M1.SCREEN_CODE AS screen_code
|
||||
|
||||
FROM menu_info m1
|
||||
FROM MENU_INFO M1
|
||||
|
||||
INNER JOIN menu_info m0 ON m1.parent_obj_id = m0.objid
|
||||
INNER JOIN MENU_INFO M0 ON M1.PARENT_OBJ_ID = M0.OBJID
|
||||
|
||||
WHERE m1.menu_type = '1'
|
||||
AND m1.status = 'active'
|
||||
AND m0.parent_obj_id = '0'
|
||||
WHERE M1.MENU_TYPE = '1'
|
||||
AND M1.STATUS = 'active'
|
||||
AND M0.PARENT_OBJ_ID = '0'
|
||||
<if test='has_company_code and company_code != null and company_code != "*"'>
|
||||
AND (m1.company_code = #{company_code} OR m1.company_code = '*')
|
||||
AND (M1.COMPANY_CODE = #{company_code} OR M1.COMPANY_CODE = '*')
|
||||
</if>
|
||||
|
||||
ORDER BY m0.seq, m1.seq
|
||||
ORDER BY M0.SEQ, M1.SEQ
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
═══════════════════════════════════════════════════ -->
|
||||
<select id="getTableList" resultType="map">
|
||||
SELECT
|
||||
T.TABLE_NAME AS "tableName"
|
||||
, COALESCE(TL.TABLE_LABEL, T.TABLE_NAME) AS "displayName"
|
||||
, COALESCE(TL.DESCRIPTION, '') AS "description"
|
||||
T.TABLE_NAME
|
||||
, COALESCE(TL.TABLE_LABEL, T.TABLE_NAME) AS DISPLAY_NAME
|
||||
, COALESCE(TL.DESCRIPTION, '') AS DESCRIPTION
|
||||
, (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_NAME = T.TABLE_NAME AND TABLE_SCHEMA = 'public') AS "columnCount"
|
||||
WHERE TABLE_NAME = T.TABLE_NAME AND TABLE_SCHEMA = 'public') AS COLUMN_COUNT
|
||||
FROM INFORMATION_SCHEMA.TABLES T
|
||||
LEFT JOIN TABLE_LABELS TL
|
||||
ON T.TABLE_NAME = TL.TABLE_NAME
|
||||
@@ -38,33 +38,33 @@
|
||||
═══════════════════════════════════════════════════ -->
|
||||
<select id="getColumnList" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
C.COLUMN_NAME AS "columnName"
|
||||
, COALESCE(CL.COLUMN_LABEL, C.COLUMN_NAME) AS "displayName"
|
||||
, C.DATA_TYPE AS "dataType"
|
||||
, C.DATA_TYPE AS "dbType"
|
||||
, COALESCE(CL.INPUT_TYPE, 'text') AS "webType"
|
||||
, COALESCE(CL.INPUT_TYPE, 'direct') AS "inputType"
|
||||
, COALESCE(CL.DETAIL_SETTINGS::TEXT, '') AS "detailSettings"
|
||||
, COALESCE(CL.DESCRIPTION, '') AS "description"
|
||||
C.COLUMN_NAME
|
||||
, COALESCE(CL.COLUMN_LABEL, C.COLUMN_NAME) AS DISPLAY_NAME
|
||||
, C.DATA_TYPE
|
||||
, C.DATA_TYPE AS DB_TYPE
|
||||
, COALESCE(CL.INPUT_TYPE, 'text') AS WEB_TYPE
|
||||
, COALESCE(CL.INPUT_TYPE, 'direct') AS INPUT_TYPE
|
||||
, COALESCE(CL.DETAIL_SETTINGS::TEXT, '') AS DETAIL_SETTINGS
|
||||
, COALESCE(CL.DESCRIPTION, '') AS DESCRIPTION
|
||||
, CASE
|
||||
WHEN CL.IS_NULLABLE IS NOT NULL
|
||||
THEN CASE WHEN CL.IS_NULLABLE = 'N' THEN 'NO' ELSE 'YES' END
|
||||
ELSE C.IS_NULLABLE
|
||||
END AS "isNullable"
|
||||
, CASE WHEN CL.IS_UNIQUE = 'Y' THEN 'YES' ELSE 'NO' END AS "isUnique"
|
||||
, CASE WHEN PK.COLUMN_NAME IS NOT NULL THEN TRUE ELSE FALSE END AS "isPrimaryKey"
|
||||
, C.COLUMN_DEFAULT AS "defaultValue"
|
||||
, C.CHARACTER_MAXIMUM_LENGTH AS "maxLength"
|
||||
, C.NUMERIC_PRECISION AS "numericPrecision"
|
||||
, C.NUMERIC_SCALE AS "numericScale"
|
||||
, CL.CODE_CATEGORY AS "codeCategory"
|
||||
, CL.CODE_VALUE AS "codeValue"
|
||||
, CL.REFERENCE_TABLE AS "referenceTable"
|
||||
, CL.REFERENCE_COLUMN AS "referenceColumn"
|
||||
, CL.DISPLAY_COLUMN AS "displayColumn"
|
||||
, CL.DISPLAY_ORDER AS "displayOrder"
|
||||
, CL.IS_VISIBLE AS "isVisible"
|
||||
, DCL.COLUMN_LABEL AS "displayColumnLabel"
|
||||
END AS IS_NULLABLE
|
||||
, CASE WHEN CL.IS_UNIQUE = 'Y' THEN 'YES' ELSE 'NO' END AS IS_UNIQUE
|
||||
, CASE WHEN PK.COLUMN_NAME IS NOT NULL THEN TRUE ELSE FALSE END AS IS_PRIMARY_KEY
|
||||
, C.COLUMN_DEFAULT AS DEFAULT_VALUE
|
||||
, C.CHARACTER_MAXIMUM_LENGTH AS MAX_LENGTH
|
||||
, C.NUMERIC_PRECISION
|
||||
, C.NUMERIC_SCALE
|
||||
, CL.CODE_CATEGORY
|
||||
, CL.CODE_VALUE
|
||||
, CL.REFERENCE_TABLE
|
||||
, CL.REFERENCE_COLUMN
|
||||
, CL.DISPLAY_COLUMN
|
||||
, CL.DISPLAY_ORDER
|
||||
, CL.IS_VISIBLE
|
||||
, DCL.COLUMN_LABEL AS DISPLAY_COLUMN_LABEL
|
||||
FROM INFORMATION_SCHEMA.COLUMNS C
|
||||
LEFT JOIN TABLE_TYPE_COLUMNS CL
|
||||
ON C.TABLE_NAME = CL.TABLE_NAME AND C.COLUMN_NAME = CL.COLUMN_NAME AND CL.COMPANY_CODE = '*'
|
||||
@@ -91,33 +91,33 @@
|
||||
═══════════════════════════════════════════════════ -->
|
||||
<select id="getColumnListWithCompany" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
C.COLUMN_NAME AS "columnName"
|
||||
, COALESCE(NULLIF(TTC.COLUMN_LABEL, C.COLUMN_NAME), CL.COLUMN_LABEL, C.COLUMN_NAME) AS "displayName"
|
||||
, C.DATA_TYPE AS "dataType"
|
||||
, C.DATA_TYPE AS "dbType"
|
||||
, COALESCE(TTC.INPUT_TYPE, CL.INPUT_TYPE, 'text') AS "webType"
|
||||
, COALESCE(TTC.INPUT_TYPE, CL.INPUT_TYPE, 'direct') AS "inputType"
|
||||
, COALESCE(TTC.DETAIL_SETTINGS::TEXT, CL.DETAIL_SETTINGS::TEXT, '') AS "detailSettings"
|
||||
, COALESCE(TTC.DESCRIPTION, CL.DESCRIPTION, '') AS "description"
|
||||
C.COLUMN_NAME
|
||||
, COALESCE(NULLIF(TTC.COLUMN_LABEL, C.COLUMN_NAME), CL.COLUMN_LABEL, C.COLUMN_NAME) AS DISPLAY_NAME
|
||||
, C.DATA_TYPE
|
||||
, C.DATA_TYPE AS DB_TYPE
|
||||
, COALESCE(TTC.INPUT_TYPE, CL.INPUT_TYPE, 'text') AS WEB_TYPE
|
||||
, COALESCE(TTC.INPUT_TYPE, CL.INPUT_TYPE, 'direct') AS INPUT_TYPE
|
||||
, COALESCE(TTC.DETAIL_SETTINGS::TEXT, CL.DETAIL_SETTINGS::TEXT, '') AS DETAIL_SETTINGS
|
||||
, COALESCE(TTC.DESCRIPTION, CL.DESCRIPTION, '') AS DESCRIPTION
|
||||
, CASE
|
||||
WHEN COALESCE(TTC.IS_NULLABLE, CL.IS_NULLABLE) IS NOT NULL
|
||||
THEN CASE WHEN COALESCE(TTC.IS_NULLABLE, CL.IS_NULLABLE) = 'N' THEN 'NO' ELSE 'YES' END
|
||||
ELSE C.IS_NULLABLE
|
||||
END AS "isNullable"
|
||||
, CASE WHEN COALESCE(TTC.IS_UNIQUE, CL.IS_UNIQUE) = 'Y' THEN 'YES' ELSE 'NO' END AS "isUnique"
|
||||
, CASE WHEN PK.COLUMN_NAME IS NOT NULL THEN TRUE ELSE FALSE END AS "isPrimaryKey"
|
||||
, C.COLUMN_DEFAULT AS "defaultValue"
|
||||
, C.CHARACTER_MAXIMUM_LENGTH AS "maxLength"
|
||||
, C.NUMERIC_PRECISION AS "numericPrecision"
|
||||
, C.NUMERIC_SCALE AS "numericScale"
|
||||
, COALESCE(TTC.CODE_CATEGORY, CL.CODE_CATEGORY) AS "codeCategory"
|
||||
, COALESCE(TTC.CODE_VALUE, CL.CODE_VALUE) AS "codeValue"
|
||||
, COALESCE(TTC.REFERENCE_TABLE, CL.REFERENCE_TABLE) AS "referenceTable"
|
||||
, COALESCE(TTC.REFERENCE_COLUMN, CL.REFERENCE_COLUMN) AS "referenceColumn"
|
||||
, COALESCE(TTC.DISPLAY_COLUMN, CL.DISPLAY_COLUMN) AS "displayColumn"
|
||||
, COALESCE(TTC.DISPLAY_ORDER, CL.DISPLAY_ORDER) AS "displayOrder"
|
||||
, COALESCE(TTC.IS_VISIBLE, CL.IS_VISIBLE) AS "isVisible"
|
||||
, DCL.COLUMN_LABEL AS "displayColumnLabel"
|
||||
END AS IS_NULLABLE
|
||||
, CASE WHEN COALESCE(TTC.IS_UNIQUE, CL.IS_UNIQUE) = 'Y' THEN 'YES' ELSE 'NO' END AS IS_UNIQUE
|
||||
, CASE WHEN PK.COLUMN_NAME IS NOT NULL THEN TRUE ELSE FALSE END AS IS_PRIMARY_KEY
|
||||
, C.COLUMN_DEFAULT AS DEFAULT_VALUE
|
||||
, C.CHARACTER_MAXIMUM_LENGTH AS MAX_LENGTH
|
||||
, C.NUMERIC_PRECISION
|
||||
, C.NUMERIC_SCALE
|
||||
, COALESCE(TTC.CODE_CATEGORY, CL.CODE_CATEGORY) AS CODE_CATEGORY
|
||||
, COALESCE(TTC.CODE_VALUE, CL.CODE_VALUE) AS CODE_VALUE
|
||||
, COALESCE(TTC.REFERENCE_TABLE, CL.REFERENCE_TABLE) AS REFERENCE_TABLE
|
||||
, COALESCE(TTC.REFERENCE_COLUMN, CL.REFERENCE_COLUMN) AS REFERENCE_COLUMN
|
||||
, COALESCE(TTC.DISPLAY_COLUMN, CL.DISPLAY_COLUMN) AS DISPLAY_COLUMN
|
||||
, COALESCE(TTC.DISPLAY_ORDER, CL.DISPLAY_ORDER) AS DISPLAY_ORDER
|
||||
, COALESCE(TTC.IS_VISIBLE, CL.IS_VISIBLE) AS IS_VISIBLE
|
||||
, DCL.COLUMN_LABEL AS DISPLAY_COLUMN_LABEL
|
||||
FROM INFORMATION_SCHEMA.COLUMNS C
|
||||
LEFT JOIN TABLE_TYPE_COLUMNS CL
|
||||
ON C.TABLE_NAME = CL.TABLE_NAME AND C.COLUMN_NAME = CL.COLUMN_NAME AND CL.COMPANY_CODE = '*'
|
||||
@@ -148,27 +148,27 @@
|
||||
═══════════════════════════════════════════════════ -->
|
||||
<select id="getTableSchemaList" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
C.COLUMN_NAME AS "columnName"
|
||||
, C.COLUMN_NAME AS "displayName"
|
||||
, C.DATA_TYPE AS "dataType"
|
||||
, C.UDT_NAME AS "dbType"
|
||||
, C.IS_NULLABLE AS "isNullable"
|
||||
, C.COLUMN_DEFAULT AS "defaultValue"
|
||||
, C.CHARACTER_MAXIMUM_LENGTH AS "maxLength"
|
||||
, C.NUMERIC_PRECISION AS "numericPrecision"
|
||||
, C.NUMERIC_SCALE AS "numericScale"
|
||||
C.COLUMN_NAME
|
||||
, C.COLUMN_NAME AS DISPLAY_NAME
|
||||
, C.DATA_TYPE
|
||||
, C.UDT_NAME AS DB_TYPE
|
||||
, C.IS_NULLABLE
|
||||
, C.COLUMN_DEFAULT AS DEFAULT_VALUE
|
||||
, C.CHARACTER_MAXIMUM_LENGTH AS MAX_LENGTH
|
||||
, C.NUMERIC_PRECISION
|
||||
, C.NUMERIC_SCALE
|
||||
, CASE
|
||||
WHEN C.COLUMN_NAME IN (
|
||||
SELECT KCU.COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU
|
||||
WHERE KCU.TABLE_NAME = #{table_name} AND KCU.CONSTRAINT_NAME LIKE '%_pkey'
|
||||
) THEN TRUE ELSE FALSE
|
||||
END AS "isPrimaryKey"
|
||||
END AS IS_PRIMARY_KEY
|
||||
, COL_DESCRIPTION(
|
||||
(SELECT OID FROM PG_CLASS
|
||||
WHERE RELNAME = #{table_name}
|
||||
AND RELNAMESPACE = (SELECT OID FROM PG_NAMESPACE WHERE NSPNAME = 'public')),
|
||||
C.ORDINAL_POSITION
|
||||
) AS "columnComment"
|
||||
) AS COLUMN_COMMENT
|
||||
FROM INFORMATION_SCHEMA.COLUMNS C
|
||||
WHERE C.TABLE_NAME = #{table_name}
|
||||
AND C.TABLE_SCHEMA = 'public'
|
||||
@@ -184,7 +184,7 @@
|
||||
WHERE TABLE_NAME = #{table_name}
|
||||
AND TABLE_SCHEMA = 'public'
|
||||
AND TABLE_TYPE = 'BASE TABLE'
|
||||
) AS "exists"
|
||||
) AS EXISTS
|
||||
</select>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════
|
||||
@@ -192,11 +192,11 @@
|
||||
═══════════════════════════════════════════════════ -->
|
||||
<select id="getTableLabelInfo" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
TABLE_NAME AS "tableName"
|
||||
, TABLE_LABEL AS "tableLabel"
|
||||
TABLE_NAME
|
||||
, TABLE_LABEL
|
||||
, DESCRIPTION
|
||||
, CREATED_DATE AS "createdDate"
|
||||
, UPDATED_DATE AS "updatedDate"
|
||||
, CREATED_DATE
|
||||
, UPDATED_DATE
|
||||
FROM TABLE_LABELS
|
||||
WHERE TABLE_NAME = #{table_name}
|
||||
</select>
|
||||
@@ -245,20 +245,20 @@
|
||||
<select id="getColumnLabelInfo" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
ID
|
||||
, TABLE_NAME AS "tableName"
|
||||
, COLUMN_NAME AS "columnName"
|
||||
, COLUMN_LABEL AS "columnLabel"
|
||||
, INPUT_TYPE AS "webType"
|
||||
, DETAIL_SETTINGS::TEXT AS "detailSettings"
|
||||
, TABLE_NAME
|
||||
, COLUMN_NAME
|
||||
, COLUMN_LABEL
|
||||
, INPUT_TYPE AS WEB_TYPE
|
||||
, DETAIL_SETTINGS::TEXT AS DETAIL_SETTINGS
|
||||
, DESCRIPTION
|
||||
, DISPLAY_ORDER AS "displayOrder"
|
||||
, IS_VISIBLE AS "isVisible"
|
||||
, CODE_CATEGORY AS "codeCategory"
|
||||
, CODE_VALUE AS "codeValue"
|
||||
, REFERENCE_TABLE AS "referenceTable"
|
||||
, REFERENCE_COLUMN AS "referenceColumn"
|
||||
, CREATED_DATE AS "createdDate"
|
||||
, UPDATED_DATE AS "updatedDate"
|
||||
, DISPLAY_ORDER
|
||||
, IS_VISIBLE
|
||||
, CODE_CATEGORY
|
||||
, CODE_VALUE
|
||||
, REFERENCE_TABLE
|
||||
, REFERENCE_COLUMN
|
||||
, CREATED_DATE
|
||||
, UPDATED_DATE
|
||||
FROM TABLE_TYPE_COLUMNS
|
||||
WHERE TABLE_NAME = #{table_name}
|
||||
AND COLUMN_NAME = #{column_name}
|
||||
@@ -365,14 +365,14 @@
|
||||
═══════════════════════════════════════════════════ -->
|
||||
<select id="getColumnInputTypeList" parameterType="map" resultType="map">
|
||||
SELECT DISTINCT ON (TTC.COLUMN_NAME)
|
||||
TTC.COLUMN_NAME AS "columnName"
|
||||
, COALESCE(TTC.COLUMN_LABEL, TTC.COLUMN_NAME) AS "displayName"
|
||||
, TTC.INPUT_TYPE AS "inputType"
|
||||
, COALESCE(TTC.DETAIL_SETTINGS::TEXT, '{}') AS "detailSettings"
|
||||
, TTC.IS_NULLABLE AS "isNullable"
|
||||
, IC.DATA_TYPE AS "dataType"
|
||||
, TTC.COMPANY_CODE AS "companyCode"
|
||||
, TTC.CATEGORY_REF AS "categoryRef"
|
||||
TTC.COLUMN_NAME
|
||||
, COALESCE(TTC.COLUMN_LABEL, TTC.COLUMN_NAME) AS DISPLAY_NAME
|
||||
, TTC.INPUT_TYPE
|
||||
, COALESCE(TTC.DETAIL_SETTINGS::TEXT, '{}') AS DETAIL_SETTINGS
|
||||
, TTC.IS_NULLABLE
|
||||
, IC.DATA_TYPE
|
||||
, TTC.COMPANY_CODE
|
||||
, TTC.CATEGORY_REF
|
||||
FROM TABLE_TYPE_COLUMNS TTC
|
||||
LEFT JOIN INFORMATION_SCHEMA.COLUMNS IC
|
||||
ON TTC.TABLE_NAME = IC.TABLE_NAME AND TTC.COLUMN_NAME = IC.COLUMN_NAME
|
||||
@@ -521,12 +521,12 @@
|
||||
═══════════════════════════════════════════════════ -->
|
||||
<select id="getCategoryColumnListByCompany" parameterType="map" resultType="map">
|
||||
SELECT DISTINCT ON (TTC.TABLE_NAME, TTC.COLUMN_NAME)
|
||||
TTC.TABLE_NAME AS "tableName"
|
||||
, TTC.COLUMN_NAME AS "columnName"
|
||||
, COALESCE(TTC.COLUMN_LABEL, TTC.COLUMN_NAME) AS "columnLabel"
|
||||
, TTC.CATEGORY_REF AS "categoryRef"
|
||||
, TTC.COMPANY_CODE AS "companyCode"
|
||||
, TL.TABLE_LABEL AS "tableLabel"
|
||||
TTC.TABLE_NAME
|
||||
, TTC.COLUMN_NAME
|
||||
, COALESCE(TTC.COLUMN_LABEL, TTC.COLUMN_NAME) AS COLUMN_LABEL
|
||||
, TTC.CATEGORY_REF
|
||||
, TTC.COMPANY_CODE
|
||||
, TL.TABLE_LABEL
|
||||
FROM TABLE_TYPE_COLUMNS TTC
|
||||
LEFT JOIN TABLE_LABELS TL
|
||||
ON TTC.TABLE_NAME = TL.TABLE_NAME
|
||||
@@ -541,11 +541,11 @@
|
||||
═══════════════════════════════════════════════════ -->
|
||||
<select id="getNumberingColumnListByCompany" parameterType="map" resultType="map">
|
||||
SELECT DISTINCT ON (TTC.TABLE_NAME, TTC.COLUMN_NAME)
|
||||
TTC.TABLE_NAME AS "tableName"
|
||||
, TTC.COLUMN_NAME AS "columnName"
|
||||
, COALESCE(TTC.COLUMN_LABEL, TTC.COLUMN_NAME) AS "columnLabel"
|
||||
, TTC.COMPANY_CODE AS "companyCode"
|
||||
, TL.TABLE_LABEL AS "tableLabel"
|
||||
TTC.TABLE_NAME
|
||||
, TTC.COLUMN_NAME
|
||||
, COALESCE(TTC.COLUMN_LABEL, TTC.COLUMN_NAME) AS COLUMN_LABEL
|
||||
, TTC.COMPANY_CODE
|
||||
, TL.TABLE_LABEL
|
||||
FROM TABLE_TYPE_COLUMNS TTC
|
||||
LEFT JOIN TABLE_LABELS TL
|
||||
ON TTC.TABLE_NAME = TL.TABLE_NAME
|
||||
@@ -560,11 +560,11 @@
|
||||
═══════════════════════════════════════════════════ -->
|
||||
<select id="getCategoryColumnListByMenu" parameterType="map" resultType="map">
|
||||
SELECT DISTINCT ON (TTC.TABLE_NAME, TTC.COLUMN_NAME)
|
||||
TTC.TABLE_NAME AS "tableName"
|
||||
, TTC.COLUMN_NAME AS "columnName"
|
||||
, COALESCE(TTC.COLUMN_LABEL, TTC.COLUMN_NAME) AS "columnLabel"
|
||||
, TTC.CATEGORY_REF AS "categoryRef"
|
||||
, CCM.MENU_OBJID AS "menuObjid"
|
||||
TTC.TABLE_NAME
|
||||
, TTC.COLUMN_NAME
|
||||
, COALESCE(TTC.COLUMN_LABEL, TTC.COLUMN_NAME) AS COLUMN_LABEL
|
||||
, TTC.CATEGORY_REF
|
||||
, CCM.MENU_OBJID
|
||||
FROM TABLE_TYPE_COLUMNS TTC
|
||||
JOIN CATEGORY_COLUMN_MAPPING CCM
|
||||
ON TTC.TABLE_NAME = CCM.TABLE_NAME
|
||||
@@ -582,11 +582,11 @@
|
||||
═══════════════════════════════════════════════════ -->
|
||||
<select id="getEntityRelationList" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
LEFT_COL.COLUMN_NAME AS "leftColumn"
|
||||
, LEFT_COL.REFERENCE_TABLE AS "rightTable"
|
||||
, LEFT_COL.REFERENCE_COLUMN AS "rightColumn"
|
||||
, LEFT_COL.DISPLAY_COLUMN AS "displayColumn"
|
||||
, COALESCE(LEFT_COL.COLUMN_LABEL, LEFT_COL.COLUMN_NAME) AS "columnLabel"
|
||||
LEFT_COL.COLUMN_NAME AS LEFT_COLUMN
|
||||
, LEFT_COL.REFERENCE_TABLE AS RIGHT_TABLE
|
||||
, LEFT_COL.REFERENCE_COLUMN AS RIGHT_COLUMN
|
||||
, LEFT_COL.DISPLAY_COLUMN
|
||||
, COALESCE(LEFT_COL.COLUMN_LABEL, LEFT_COL.COLUMN_NAME) AS COLUMN_LABEL
|
||||
FROM TABLE_TYPE_COLUMNS LEFT_COL
|
||||
WHERE LEFT_COL.TABLE_NAME = #{left_table}
|
||||
AND LEFT_COL.REFERENCE_TABLE = #{right_table}
|
||||
@@ -600,12 +600,12 @@
|
||||
═══════════════════════════════════════════════════ -->
|
||||
<select id="getReferencedByTableList" parameterType="map" resultType="map">
|
||||
SELECT DISTINCT
|
||||
TTC.TABLE_NAME AS "tableName"
|
||||
, TTC.COLUMN_NAME AS "columnName"
|
||||
, COALESCE(TTC.COLUMN_LABEL, TTC.COLUMN_NAME) AS "columnLabel"
|
||||
, TTC.REFERENCE_COLUMN AS "referenceColumn"
|
||||
, TTC.DISPLAY_COLUMN AS "displayColumn"
|
||||
, COALESCE(TL.TABLE_LABEL, TTC.TABLE_NAME) AS "tableLabel"
|
||||
TTC.TABLE_NAME
|
||||
, TTC.COLUMN_NAME
|
||||
, COALESCE(TTC.COLUMN_LABEL, TTC.COLUMN_NAME) AS COLUMN_LABEL
|
||||
, TTC.REFERENCE_COLUMN
|
||||
, TTC.DISPLAY_COLUMN
|
||||
, COALESCE(TL.TABLE_LABEL, TTC.TABLE_NAME) AS TABLE_LABEL
|
||||
FROM TABLE_TYPE_COLUMNS TTC
|
||||
LEFT JOIN TABLE_LABELS TL
|
||||
ON TTC.TABLE_NAME = TL.TABLE_NAME
|
||||
@@ -620,11 +620,11 @@
|
||||
═══════════════════════════════════════════════════ -->
|
||||
<select id="getLogConfigInfo" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
TABLE_NAME AS "tableName"
|
||||
, IS_ACTIVE AS "isActive"
|
||||
, LOG_COLUMNS AS "logColumns"
|
||||
, CREATED_DATE AS "createdDate"
|
||||
, UPDATED_DATE AS "updatedDate"
|
||||
TABLE_NAME
|
||||
, IS_ACTIVE
|
||||
, LOG_COLUMNS
|
||||
, CREATED_DATE
|
||||
, UPDATED_DATE
|
||||
FROM LOG_TABLES
|
||||
WHERE TABLE_NAME = #{table_name}
|
||||
</select>
|
||||
@@ -655,8 +655,8 @@
|
||||
═══════════════════════════════════════════════════ -->
|
||||
<select id="checkDatabaseConnection" resultType="map">
|
||||
SELECT
|
||||
1 AS "result"
|
||||
, NOW() AS "timestamp"
|
||||
1 AS RESULT
|
||||
, NOW() AS TIMESTAMP
|
||||
</select>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════
|
||||
|
||||
@@ -118,7 +118,7 @@ export default function TableManagementPage() {
|
||||
// PK/인덱스 관리 상태
|
||||
const [constraints, setConstraints] = useState<{
|
||||
primaryKey: { name: string; columns: string[] };
|
||||
indexes: Array<{ name: string; columns: string[]; isUnique: boolean }>;
|
||||
indexes: Array<{ name: string; columns: string[]; is_unique: boolean }>;
|
||||
}>({ primaryKey: { name: "", columns: [] }, indexes: [] });
|
||||
const [pkDialogOpen, setPkDialogOpen] = useState(false);
|
||||
const [pendingPkColumns, setPendingPkColumns] = useState<string[]>([]);
|
||||
@@ -217,11 +217,11 @@ export default function TableManagementPage() {
|
||||
const referenceTableOptions = [
|
||||
{ value: "none", label: getTextFromUI(TABLE_MANAGEMENT_KEYS.LABEL_NONE, "선택 안함") },
|
||||
...tables.map((table) => ({
|
||||
value: table.tableName,
|
||||
value: table.table_name,
|
||||
label:
|
||||
table.displayName && table.displayName !== table.tableName
|
||||
? `${table.displayName} (${table.tableName})`
|
||||
: table.tableName,
|
||||
table.display_name && table.display_name !== table.table_name
|
||||
? `${table.display_name} (${table.table_name})`
|
||||
: table.table_name,
|
||||
})),
|
||||
];
|
||||
|
||||
@@ -317,21 +317,21 @@ export default function TableManagementPage() {
|
||||
console.log("📥 원본 API 응답:", {
|
||||
hasColumns: !!(data.columns || data),
|
||||
firstColumn: (data.columns || data)[0],
|
||||
statusColumn: (data.columns || data).find((col: any) => col.columnName === "status"),
|
||||
statusColumn: (data.columns || data).find((col: any) => col.column_name === "status"),
|
||||
});
|
||||
|
||||
// 컬럼 데이터에 기본값 설정
|
||||
const processedColumns = (data.columns || data).map((col: any) => {
|
||||
let hierarchyRole: "large" | "medium" | "small" | undefined = undefined;
|
||||
if (col.detailSettings && typeof col.detailSettings === "string") {
|
||||
if (col.detail_settings && typeof col.detail_settings === "string") {
|
||||
try {
|
||||
const parsed = JSON.parse(col.detailSettings);
|
||||
const parsed = JSON.parse(col.detail_settings);
|
||||
if (
|
||||
parsed.hierarchyRole === "large" ||
|
||||
parsed.hierarchyRole === "medium" ||
|
||||
parsed.hierarchyRole === "small"
|
||||
parsed.hierarchy_role === "large" ||
|
||||
parsed.hierarchy_role === "medium" ||
|
||||
parsed.hierarchy_role === "small"
|
||||
) {
|
||||
hierarchyRole = parsed.hierarchyRole;
|
||||
hierarchyRole = parsed.hierarchy_role;
|
||||
}
|
||||
} catch {
|
||||
// JSON 파싱 실패 시 무시
|
||||
@@ -340,11 +340,11 @@ export default function TableManagementPage() {
|
||||
|
||||
return {
|
||||
...col,
|
||||
inputType: col.inputType || "text",
|
||||
isUnique: col.isUnique || "NO",
|
||||
categoryMenus: col.categoryMenus || [],
|
||||
hierarchyRole,
|
||||
categoryRef: col.categoryRef || null,
|
||||
input_type: col.input_type || "text",
|
||||
is_unique: col.is_unique || "NO",
|
||||
category_menus: col.category_menus || [],
|
||||
hierarchy_role: hierarchyRole,
|
||||
category_ref: col.category_ref || null,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -395,8 +395,8 @@ export default function TableManagementPage() {
|
||||
setTypeFilter(null);
|
||||
|
||||
// 선택된 테이블 정보에서 라벨 설정
|
||||
const tableInfo = tables.find((table) => table.tableName === tableName);
|
||||
setTableLabel(tableInfo?.displayName || tableName);
|
||||
const tableInfo = tables.find((table) => table.table_name === tableName);
|
||||
setTableLabel(tableInfo?.display_name || tableName);
|
||||
setTableDescription(tableInfo?.description || "");
|
||||
|
||||
loadColumnTypes(tableName, 1, pageSize);
|
||||
@@ -410,31 +410,31 @@ export default function TableManagementPage() {
|
||||
(columnName: string, newInputType: string) => {
|
||||
setColumns((prev) =>
|
||||
prev.map((col) => {
|
||||
if (col.columnName === columnName) {
|
||||
if (col.column_name === columnName) {
|
||||
const inputTypeOption = memoizedInputTypeOptions.find((option) => option.value === newInputType);
|
||||
const updated: typeof col = {
|
||||
...col,
|
||||
inputType: newInputType,
|
||||
detailSettings: inputTypeOption?.description || col.detailSettings,
|
||||
input_type: newInputType,
|
||||
detail_settings: inputTypeOption?.description || col.detail_settings,
|
||||
};
|
||||
|
||||
// 엔티티가 아닌 타입으로 변경 시 참조 설정 초기화
|
||||
if (newInputType !== "entity") {
|
||||
updated.referenceTable = undefined;
|
||||
updated.referenceColumn = undefined;
|
||||
updated.displayColumn = undefined;
|
||||
updated.reference_table = undefined;
|
||||
updated.reference_column = undefined;
|
||||
updated.display_column = undefined;
|
||||
}
|
||||
|
||||
// 코드가 아닌 타입으로 변경 시 코드 설정 초기화
|
||||
if (newInputType !== "code") {
|
||||
updated.codeCategory = undefined;
|
||||
updated.codeValue = undefined;
|
||||
updated.hierarchyRole = undefined;
|
||||
updated.code_category = undefined;
|
||||
updated.code_value = undefined;
|
||||
updated.hierarchy_role = undefined;
|
||||
}
|
||||
|
||||
// 카테고리가 아닌 타입으로 변경 시 카테고리 참조 초기화
|
||||
if (newInputType !== "category") {
|
||||
updated.categoryRef = undefined;
|
||||
updated.category_ref = undefined;
|
||||
}
|
||||
|
||||
return updated;
|
||||
@@ -451,14 +451,14 @@ export default function TableManagementPage() {
|
||||
(columnName: string, settingType: string, value: string) => {
|
||||
setColumns((prev) =>
|
||||
prev.map((col) => {
|
||||
if (col.columnName === columnName) {
|
||||
let newDetailSettings = col.detailSettings;
|
||||
let codeCategory = col.codeCategory;
|
||||
let codeValue = col.codeValue;
|
||||
let referenceTable = col.referenceTable;
|
||||
let referenceColumn = col.referenceColumn;
|
||||
let displayColumn = col.displayColumn;
|
||||
let hierarchyRole = col.hierarchyRole;
|
||||
if (col.column_name === columnName) {
|
||||
let newDetailSettings = col.detail_settings;
|
||||
let codeCategory = col.code_category;
|
||||
let codeValue = col.code_value;
|
||||
let referenceTable = col.reference_table;
|
||||
let referenceColumn = col.reference_column;
|
||||
let displayColumn = col.display_column;
|
||||
let hierarchyRole = col.hierarchy_role;
|
||||
|
||||
if (settingType === "code") {
|
||||
if (value === "none") {
|
||||
@@ -470,8 +470,8 @@ export default function TableManagementPage() {
|
||||
// 기존 hierarchyRole 유지하면서 JSON 형식으로 저장
|
||||
const existingHierarchyRole = hierarchyRole;
|
||||
newDetailSettings = JSON.stringify({
|
||||
codeCategory: value,
|
||||
hierarchyRole: existingHierarchyRole,
|
||||
code_category: value,
|
||||
hierarchy_role: existingHierarchyRole,
|
||||
});
|
||||
codeCategory = value;
|
||||
codeValue = value;
|
||||
@@ -481,16 +481,16 @@ export default function TableManagementPage() {
|
||||
hierarchyRole = value === "none" ? undefined : (value as "large" | "medium" | "small");
|
||||
// detailSettings를 JSON으로 업데이트
|
||||
let existingSettings: Record<string, any> = {};
|
||||
if (typeof col.detailSettings === "string" && col.detailSettings.trim().startsWith("{")) {
|
||||
if (typeof col.detail_settings === "string" && col.detail_settings.trim().startsWith("{")) {
|
||||
try {
|
||||
existingSettings = JSON.parse(col.detailSettings);
|
||||
existingSettings = JSON.parse(col.detail_settings);
|
||||
} catch {
|
||||
existingSettings = {};
|
||||
}
|
||||
}
|
||||
newDetailSettings = JSON.stringify({
|
||||
...existingSettings,
|
||||
hierarchyRole: hierarchyRole,
|
||||
hierarchy_role: hierarchyRole,
|
||||
});
|
||||
} else if (settingType === "entity") {
|
||||
if (value === "none") {
|
||||
@@ -504,31 +504,31 @@ export default function TableManagementPage() {
|
||||
referenceTable = value;
|
||||
// 🎯 참조 컬럼을 소스 컬럼명과 동일하게 설정 (일반적인 경우)
|
||||
// 예: user_info.dept_code -> dept_info.dept_code
|
||||
referenceColumn = col.columnName;
|
||||
referenceColumn = col.column_name;
|
||||
// 참조 테이블의 컬럼 정보 로드
|
||||
loadReferenceTableColumns(value);
|
||||
}
|
||||
} else if (settingType === "entity_reference_column") {
|
||||
// 🎯 Entity 참조 컬럼 변경 (조인할 컬럼)
|
||||
referenceColumn = value;
|
||||
const tableOption = referenceTableOptions.find((option) => option.value === col.referenceTable);
|
||||
const tableOption = referenceTableOptions.find((option) => option.value === col.reference_table);
|
||||
newDetailSettings = tableOption ? `참조테이블: ${tableOption.label}` : "";
|
||||
} else if (settingType === "entity_display_column") {
|
||||
// 🎯 Entity 표시 컬럼 변경
|
||||
displayColumn = value;
|
||||
const tableOption = referenceTableOptions.find((option) => option.value === col.referenceTable);
|
||||
const tableOption = referenceTableOptions.find((option) => option.value === col.reference_table);
|
||||
newDetailSettings = tableOption ? `참조테이블: ${tableOption.label} (${value})` : "";
|
||||
}
|
||||
|
||||
return {
|
||||
...col,
|
||||
detailSettings: newDetailSettings,
|
||||
codeCategory,
|
||||
codeValue,
|
||||
referenceTable,
|
||||
referenceColumn,
|
||||
displayColumn,
|
||||
hierarchyRole,
|
||||
detail_settings: newDetailSettings,
|
||||
code_category: codeCategory,
|
||||
code_value: codeValue,
|
||||
reference_table: referenceTable,
|
||||
reference_column: referenceColumn,
|
||||
display_column: displayColumn,
|
||||
hierarchy_role: hierarchyRole,
|
||||
};
|
||||
}
|
||||
return col;
|
||||
@@ -542,10 +542,10 @@ export default function TableManagementPage() {
|
||||
const handleLabelChange = useCallback((columnName: string, newLabel: string) => {
|
||||
setColumns((prev) =>
|
||||
prev.map((col) => {
|
||||
if (col.columnName === columnName) {
|
||||
if (col.column_name === columnName) {
|
||||
return {
|
||||
...col,
|
||||
displayName: newLabel,
|
||||
display_name: newLabel,
|
||||
};
|
||||
}
|
||||
return col;
|
||||
@@ -574,14 +574,14 @@ export default function TableManagementPage() {
|
||||
|
||||
try {
|
||||
// 🎯 Entity 타입인 경우 detailSettings에 엔티티 설정을 JSON으로 포함
|
||||
let finalDetailSettings = column.detailSettings || "";
|
||||
let finalDetailSettings = column.detail_settings || "";
|
||||
|
||||
if (column.inputType === "entity" && column.referenceTable) {
|
||||
if (column.input_type === "entity" && column.reference_table) {
|
||||
// 기존 detailSettings를 파싱하거나 새로 생성
|
||||
let existingSettings: Record<string, unknown> = {};
|
||||
if (typeof column.detailSettings === "string" && column.detailSettings.trim().startsWith("{")) {
|
||||
if (typeof column.detail_settings === "string" && column.detail_settings.trim().startsWith("{")) {
|
||||
try {
|
||||
existingSettings = JSON.parse(column.detailSettings);
|
||||
existingSettings = JSON.parse(column.detail_settings);
|
||||
} catch {
|
||||
existingSettings = {};
|
||||
}
|
||||
@@ -590,9 +590,9 @@ export default function TableManagementPage() {
|
||||
// 엔티티 설정 추가
|
||||
const entitySettings = {
|
||||
...existingSettings,
|
||||
entityTable: column.referenceTable,
|
||||
entityCodeColumn: column.referenceColumn || "id",
|
||||
entityLabelColumn: column.displayColumn || "name",
|
||||
entityTable: column.reference_table,
|
||||
entityCodeColumn: column.reference_column || "id",
|
||||
entityLabelColumn: column.display_column || "name",
|
||||
placeholder: (existingSettings.placeholder as string) || "항목을 선택하세요",
|
||||
searchable: existingSettings.searchable ?? true,
|
||||
};
|
||||
@@ -602,7 +602,7 @@ export default function TableManagementPage() {
|
||||
}
|
||||
|
||||
// 🎯 Code 타입인 경우 hierarchyRole을 detailSettings에 포함
|
||||
if (column.inputType === "code" && column.hierarchyRole) {
|
||||
if (column.input_type === "code" && column.hierarchy_role) {
|
||||
let existingSettings: Record<string, unknown> = {};
|
||||
if (typeof finalDetailSettings === "string" && finalDetailSettings.trim().startsWith("{")) {
|
||||
try {
|
||||
@@ -614,7 +614,7 @@ export default function TableManagementPage() {
|
||||
|
||||
const codeSettings = {
|
||||
...existingSettings,
|
||||
hierarchyRole: column.hierarchyRole,
|
||||
hierarchy_role: column.hierarchy_role,
|
||||
};
|
||||
|
||||
finalDetailSettings = JSON.stringify(codeSettings);
|
||||
@@ -622,26 +622,26 @@ export default function TableManagementPage() {
|
||||
}
|
||||
|
||||
const columnSetting = {
|
||||
columnName: column.columnName,
|
||||
columnLabel: column.displayName,
|
||||
inputType: column.inputType || "text",
|
||||
detailSettings: finalDetailSettings,
|
||||
codeCategory: column.codeCategory || "",
|
||||
codeValue: column.codeValue || "",
|
||||
referenceTable: column.referenceTable || "",
|
||||
referenceColumn: column.referenceColumn || "",
|
||||
displayColumn: column.displayColumn || "",
|
||||
categoryRef: column.categoryRef || null,
|
||||
column_name: column.column_name,
|
||||
column_label: column.display_name,
|
||||
input_type: column.input_type || "text",
|
||||
detail_settings: finalDetailSettings,
|
||||
code_category: column.code_category || "",
|
||||
code_value: column.code_value || "",
|
||||
reference_table: column.reference_table || "",
|
||||
reference_column: column.reference_column || "",
|
||||
display_column: column.display_column || "",
|
||||
category_ref: column.category_ref || null,
|
||||
};
|
||||
|
||||
// console.log("저장할 컬럼 설정:", columnSetting);
|
||||
|
||||
console.log("💾 저장할 컬럼 정보:", {
|
||||
columnName: column.columnName,
|
||||
inputType: column.inputType,
|
||||
categoryMenus: column.categoryMenus,
|
||||
hasCategoryMenus: !!column.categoryMenus,
|
||||
categoryMenusLength: column.categoryMenus?.length || 0,
|
||||
columnName: column.column_name,
|
||||
inputType: column.input_type,
|
||||
categoryMenus: column.category_menus,
|
||||
hasCategoryMenus: !!column.category_menus,
|
||||
categoryMenusLength: column.category_menus?.length || 0,
|
||||
});
|
||||
|
||||
const response = await apiClient.post(`/table-management/tables/${selectedTable}/columns/settings`, [
|
||||
@@ -653,44 +653,44 @@ export default function TableManagementPage() {
|
||||
|
||||
// 🆕 Category 타입인 경우 컬럼 매핑 처리
|
||||
console.log("🔍 카테고리 조건 체크:", {
|
||||
isCategory: column.inputType === "category",
|
||||
hasCategoryMenus: !!column.categoryMenus,
|
||||
length: column.categoryMenus?.length || 0,
|
||||
isCategory: column.input_type === "category",
|
||||
hasCategoryMenus: !!column.category_menus,
|
||||
length: column.category_menus?.length || 0,
|
||||
});
|
||||
|
||||
if (column.inputType === "category" && !column.categoryRef) {
|
||||
if (column.input_type === "category" && !column.category_ref) {
|
||||
// 참조가 아닌 자체 카테고리만 메뉴 매핑 처리
|
||||
console.log("기존 카테고리 메뉴 매핑 삭제 시작:", {
|
||||
tableName: selectedTable,
|
||||
columnName: column.columnName,
|
||||
columnName: column.column_name,
|
||||
});
|
||||
|
||||
try {
|
||||
const deleteResponse = await deleteColumnMappingsByColumn(selectedTable, column.columnName);
|
||||
const deleteResponse = await deleteColumnMappingsByColumn(selectedTable, column.column_name);
|
||||
console.log("🗑️ 기존 매핑 삭제 결과:", deleteResponse);
|
||||
} catch (error) {
|
||||
console.error("❌ 기존 매핑 삭제 실패:", error);
|
||||
}
|
||||
|
||||
// 2. 새로운 매핑 추가 (선택된 메뉴가 있는 경우만)
|
||||
if (column.categoryMenus && column.categoryMenus.length > 0) {
|
||||
if (column.category_menus && column.category_menus.length > 0) {
|
||||
console.log("📥 카테고리 메뉴 매핑 시작:", {
|
||||
columnName: column.columnName,
|
||||
categoryMenus: column.categoryMenus,
|
||||
count: column.categoryMenus.length,
|
||||
columnName: column.column_name,
|
||||
categoryMenus: column.category_menus,
|
||||
count: column.category_menus.length,
|
||||
});
|
||||
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
|
||||
for (const menuObjid of column.categoryMenus) {
|
||||
for (const menuObjid of column.category_menus) {
|
||||
try {
|
||||
const mappingResponse = await createColumnMapping({
|
||||
tableName: selectedTable,
|
||||
logicalColumnName: column.columnName,
|
||||
physicalColumnName: column.columnName,
|
||||
logicalColumnName: column.column_name,
|
||||
physicalColumnName: column.column_name,
|
||||
menuObjid,
|
||||
description: `${column.displayName} (메뉴별 카테고리)`,
|
||||
description: `${column.display_name} (메뉴별 카테고리)`,
|
||||
});
|
||||
|
||||
if (mappingResponse.success) {
|
||||
@@ -720,7 +720,7 @@ export default function TableManagementPage() {
|
||||
}
|
||||
|
||||
// 원본 데이터 업데이트
|
||||
setOriginalColumns((prev) => prev.map((col) => (col.columnName === column.columnName ? column : col)));
|
||||
setOriginalColumns((prev) => prev.map((col) => (col.column_name === column.column_name ? column : col)));
|
||||
|
||||
// 저장 후 데이터 확인을 위해 다시 로드
|
||||
setTimeout(() => {
|
||||
@@ -762,10 +762,10 @@ export default function TableManagementPage() {
|
||||
if (columns.length > 0) {
|
||||
const columnSettings = columns.map((column) => {
|
||||
// detailSettings 계산
|
||||
let finalDetailSettings = column.detailSettings || "";
|
||||
let finalDetailSettings = column.detail_settings || "";
|
||||
|
||||
// 🆕 Entity 타입인 경우 detailSettings에 엔티티 설정 포함
|
||||
if (column.inputType === "entity" && column.referenceTable) {
|
||||
if (column.input_type === "entity" && column.reference_table) {
|
||||
let existingSettings: Record<string, unknown> = {};
|
||||
if (typeof finalDetailSettings === "string" && finalDetailSettings.trim().startsWith("{")) {
|
||||
try {
|
||||
@@ -776,15 +776,15 @@ export default function TableManagementPage() {
|
||||
}
|
||||
const entitySettings = {
|
||||
...existingSettings,
|
||||
entityTable: column.referenceTable,
|
||||
entityCodeColumn: column.referenceColumn || "id",
|
||||
entityLabelColumn: column.displayColumn || "name",
|
||||
entityTable: column.reference_table,
|
||||
entityCodeColumn: column.reference_column || "id",
|
||||
entityLabelColumn: column.display_column || "name",
|
||||
};
|
||||
finalDetailSettings = JSON.stringify(entitySettings);
|
||||
}
|
||||
|
||||
// 🆕 Code 타입인 경우 hierarchyRole을 detailSettings에 포함
|
||||
if (column.inputType === "code" && column.hierarchyRole) {
|
||||
if (column.input_type === "code" && column.hierarchy_role) {
|
||||
let existingSettings: Record<string, unknown> = {};
|
||||
if (typeof finalDetailSettings === "string" && finalDetailSettings.trim().startsWith("{")) {
|
||||
try {
|
||||
@@ -795,23 +795,23 @@ export default function TableManagementPage() {
|
||||
}
|
||||
const codeSettings = {
|
||||
...existingSettings,
|
||||
hierarchyRole: column.hierarchyRole,
|
||||
hierarchy_role: column.hierarchy_role,
|
||||
};
|
||||
finalDetailSettings = JSON.stringify(codeSettings);
|
||||
}
|
||||
|
||||
return {
|
||||
columnName: column.columnName,
|
||||
columnLabel: column.displayName,
|
||||
inputType: column.inputType || "text",
|
||||
detailSettings: finalDetailSettings,
|
||||
column_name: column.column_name,
|
||||
column_label: column.display_name,
|
||||
input_type: column.input_type || "text",
|
||||
detail_settings: finalDetailSettings,
|
||||
description: column.description || "",
|
||||
codeCategory: column.codeCategory || "",
|
||||
codeValue: column.codeValue || "",
|
||||
referenceTable: column.referenceTable || "",
|
||||
referenceColumn: column.referenceColumn || "",
|
||||
displayColumn: column.displayColumn || "",
|
||||
categoryRef: column.categoryRef || null,
|
||||
code_category: column.code_category || "",
|
||||
code_value: column.code_value || "",
|
||||
reference_table: column.reference_table || "",
|
||||
reference_column: column.reference_column || "",
|
||||
display_column: column.display_column || "",
|
||||
category_ref: column.category_ref || null,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -825,14 +825,14 @@ export default function TableManagementPage() {
|
||||
|
||||
if (response.data.success) {
|
||||
// 자체 카테고리 컬럼만 메뉴 매핑 처리 (참조 컬럼 제외)
|
||||
const categoryColumns = columns.filter((col) => col.inputType === "category" && !col.categoryRef);
|
||||
const categoryColumns = columns.filter((col) => col.input_type === "category" && !col.category_ref);
|
||||
|
||||
console.log("📥 전체 저장: 카테고리 컬럼 확인", {
|
||||
totalColumns: columns.length,
|
||||
categoryColumns: categoryColumns.length,
|
||||
categoryColumnsData: categoryColumns.map((col) => ({
|
||||
columnName: col.columnName,
|
||||
categoryMenus: col.categoryMenus,
|
||||
columnName: col.column_name,
|
||||
categoryMenus: col.category_menus,
|
||||
})),
|
||||
});
|
||||
|
||||
@@ -844,32 +844,32 @@ export default function TableManagementPage() {
|
||||
// 1. 먼저 기존 매핑 모두 삭제
|
||||
console.log("🗑️ 기존 카테고리 메뉴 매핑 삭제:", {
|
||||
tableName: selectedTable,
|
||||
columnName: column.columnName,
|
||||
columnName: column.column_name,
|
||||
});
|
||||
|
||||
try {
|
||||
const deleteResponse = await deleteColumnMappingsByColumn(selectedTable, column.columnName);
|
||||
const deleteResponse = await deleteColumnMappingsByColumn(selectedTable, column.column_name);
|
||||
console.log("🗑️ 기존 매핑 삭제 결과:", deleteResponse);
|
||||
} catch (error) {
|
||||
console.error("❌ 기존 매핑 삭제 실패:", error);
|
||||
}
|
||||
|
||||
// 2. 새로운 매핑 추가 (선택된 메뉴가 있는 경우만)
|
||||
if (column.categoryMenus && column.categoryMenus.length > 0) {
|
||||
for (const menuObjid of column.categoryMenus) {
|
||||
if (column.category_menus && column.category_menus.length > 0) {
|
||||
for (const menuObjid of column.category_menus) {
|
||||
try {
|
||||
console.log("🔄 매핑 API 호출:", {
|
||||
tableName: selectedTable,
|
||||
columnName: column.columnName,
|
||||
columnName: column.column_name,
|
||||
menuObjid,
|
||||
});
|
||||
|
||||
const mappingResponse = await createColumnMapping({
|
||||
tableName: selectedTable,
|
||||
logicalColumnName: column.columnName,
|
||||
physicalColumnName: column.columnName,
|
||||
logicalColumnName: column.column_name,
|
||||
physicalColumnName: column.column_name,
|
||||
menuObjid,
|
||||
description: `${column.displayName} (메뉴별 카테고리)`,
|
||||
description: `${column.display_name} (메뉴별 카테고리)`,
|
||||
});
|
||||
|
||||
console.log("✅ 매핑 API 응답:", mappingResponse);
|
||||
@@ -950,13 +950,13 @@ export default function TableManagementPage() {
|
||||
const filteredTables = useMemo(() => {
|
||||
const filtered = tables.filter(
|
||||
(table) =>
|
||||
table.tableName.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
table.displayName.toLowerCase().includes(searchTerm.toLowerCase()),
|
||||
table.table_name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
table.display_name.toLowerCase().includes(searchTerm.toLowerCase()),
|
||||
);
|
||||
const isKorean = (str: string) => /^[가-힣ㄱ-ㅎ]/.test(str);
|
||||
return filtered.sort((a, b) => {
|
||||
const nameA = a.displayName || a.tableName;
|
||||
const nameB = b.displayName || b.tableName;
|
||||
const nameA = a.display_name || a.table_name;
|
||||
const nameB = b.display_name || b.table_name;
|
||||
const aKo = isKorean(nameA);
|
||||
const bKo = isKorean(nameB);
|
||||
if (aKo && !bKo) return -1;
|
||||
@@ -966,7 +966,7 @@ export default function TableManagementPage() {
|
||||
}, [tables, searchTerm]);
|
||||
|
||||
// 선택된 테이블 정보
|
||||
const selectedTableInfo = tables.find((table) => table.tableName === selectedTable);
|
||||
const selectedTableInfo = tables.find((table) => table.table_name === selectedTable);
|
||||
|
||||
useEffect(() => {
|
||||
loadTables();
|
||||
@@ -978,13 +978,13 @@ export default function TableManagementPage() {
|
||||
useEffect(() => {
|
||||
if (columns.length > 0) {
|
||||
const entityColumns = columns.filter(
|
||||
(col) => col.inputType === "entity" && col.referenceTable && col.referenceTable !== "none",
|
||||
(col) => col.input_type === "entity" && col.reference_table && col.reference_table !== "none",
|
||||
);
|
||||
|
||||
entityColumns.forEach((col) => {
|
||||
if (col.referenceTable) {
|
||||
// console.log(`🎯 기존 Entity 컬럼 발견, 참조 테이블 컬럼 로드: ${col.columnName} -> ${col.referenceTable}`);
|
||||
loadReferenceTableColumns(col.referenceTable);
|
||||
if (col.reference_table) {
|
||||
// console.log(`🎯 기존 Entity 컬럼 발견, 참조 테이블 컬럼 로드: ${col.column_name} -> ${col.reference_table}`);
|
||||
loadReferenceTableColumns(col.reference_table);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1073,7 +1073,7 @@ export default function TableManagementPage() {
|
||||
(columnName: string) => {
|
||||
const isPk = constraints.primaryKey.columns.includes(columnName);
|
||||
const hasIndex = constraints.indexes.some(
|
||||
(idx) => !idx.isUnique && idx.columns.length === 1 && idx.columns[0] === columnName,
|
||||
(idx) => !idx.is_unique && idx.columns.length === 1 && idx.columns[0] === columnName,
|
||||
);
|
||||
return { isPk, hasIndex };
|
||||
},
|
||||
@@ -1095,8 +1095,8 @@ export default function TableManagementPage() {
|
||||
toast.success(response.data.message);
|
||||
setColumns((prev) =>
|
||||
prev.map((col) =>
|
||||
col.columnName === columnName
|
||||
? { ...col, isUnique: newUnique ? "YES" : "NO" }
|
||||
col.column_name === columnName
|
||||
? { ...col, is_unique: newUnique ? "YES" : "NO" }
|
||||
: col,
|
||||
),
|
||||
);
|
||||
@@ -1133,8 +1133,8 @@ export default function TableManagementPage() {
|
||||
// 컬럼 상태 로컬 업데이트
|
||||
setColumns((prev) =>
|
||||
prev.map((col) =>
|
||||
col.columnName === columnName
|
||||
? { ...col, isNullable: newNullable ? "YES" : "NO" }
|
||||
col.column_name === columnName
|
||||
? { ...col, is_nullable: newNullable ? "YES" : "NO" }
|
||||
: col,
|
||||
),
|
||||
);
|
||||
@@ -1211,10 +1211,10 @@ export default function TableManagementPage() {
|
||||
if (checked) {
|
||||
const filteredTables = tables.filter(
|
||||
(table) =>
|
||||
table.tableName.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
(table.displayName && table.displayName.toLowerCase().includes(searchTerm.toLowerCase())),
|
||||
table.table_name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
(table.display_name && table.display_name.toLowerCase().includes(searchTerm.toLowerCase())),
|
||||
);
|
||||
setSelectedTableIds(new Set(filteredTables.map((table) => table.tableName)));
|
||||
setSelectedTableIds(new Set(filteredTables.map((table) => table.table_name)));
|
||||
} else {
|
||||
setSelectedTableIds(new Set());
|
||||
}
|
||||
@@ -1377,7 +1377,7 @@ export default function TableManagementPage() {
|
||||
<Checkbox
|
||||
checked={
|
||||
filteredTables.length > 0 &&
|
||||
filteredTables.every((table) => selectedTableIds.has(table.tableName))
|
||||
filteredTables.every((table) => selectedTableIds.has(table.table_name))
|
||||
}
|
||||
onCheckedChange={handleSelectAll}
|
||||
aria-label="전체 선택"
|
||||
@@ -1414,14 +1414,14 @@ export default function TableManagementPage() {
|
||||
</div>
|
||||
) : (
|
||||
filteredTables.map((table, idx) => {
|
||||
const isActive = selectedTable === table.tableName;
|
||||
const isActive = selectedTable === table.table_name;
|
||||
const prevTable = idx > 0 ? filteredTables[idx - 1] : null;
|
||||
const isKo = /^[가-힣ㄱ-ㅎ]/.test(table.displayName || table.tableName);
|
||||
const prevIsKo = prevTable ? /^[가-힣ㄱ-ㅎ]/.test(prevTable.displayName || prevTable.tableName) : null;
|
||||
const isKo = /^[가-힣ㄱ-ㅎ]/.test(table.display_name || table.table_name);
|
||||
const prevIsKo = prevTable ? /^[가-힣ㄱ-ㅎ]/.test(prevTable.display_name || prevTable.table_name) : null;
|
||||
const showDivider = idx === 0 || (prevIsKo !== null && isKo !== prevIsKo);
|
||||
|
||||
return (
|
||||
<div key={table.tableName}>
|
||||
<div key={table.table_name}>
|
||||
{showDivider && (
|
||||
<div className="text-muted-foreground/60 mt-2 mb-1 px-2 text-[9px] font-bold uppercase tracking-widest">
|
||||
{isKo ? "한글" : "ENGLISH"}
|
||||
@@ -1434,13 +1434,13 @@ export default function TableManagementPage() {
|
||||
? "bg-accent text-foreground"
|
||||
: "text-foreground/80 hover:bg-accent/50",
|
||||
)}
|
||||
onClick={() => handleTableSelect(table.tableName)}
|
||||
onClick={() => handleTableSelect(table.table_name)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
e.preventDefault();
|
||||
handleTableSelect(table.tableName);
|
||||
handleTableSelect(table.table_name);
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -1449,9 +1449,9 @@ export default function TableManagementPage() {
|
||||
)}
|
||||
{isSuperAdmin && (
|
||||
<Checkbox
|
||||
checked={selectedTableIds.has(table.tableName)}
|
||||
onCheckedChange={(checked) => handleTableCheck(table.tableName, checked as boolean)}
|
||||
aria-label={`${table.displayName || table.tableName} 선택`}
|
||||
checked={selectedTableIds.has(table.table_name)}
|
||||
onCheckedChange={(checked) => handleTableCheck(table.table_name, checked as boolean)}
|
||||
aria-label={`${table.display_name || table.table_name} 선택`}
|
||||
className="h-3.5 w-3.5 flex-shrink-0"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
@@ -1462,11 +1462,11 @@ export default function TableManagementPage() {
|
||||
"truncate text-[16px] leading-tight",
|
||||
isActive ? "font-bold" : "font-medium",
|
||||
)}>
|
||||
{table.displayName || table.tableName}
|
||||
{table.display_name || table.table_name}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-muted-foreground truncate font-mono text-[12px] leading-tight tracking-tight">
|
||||
{table.tableName}
|
||||
{table.table_name}
|
||||
</div>
|
||||
</div>
|
||||
<span className={cn(
|
||||
@@ -1475,7 +1475,7 @@ export default function TableManagementPage() {
|
||||
? "bg-primary/15 text-primary"
|
||||
: "text-muted-foreground",
|
||||
)}>
|
||||
{table.columnCount}
|
||||
{table.column_count}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1563,21 +1563,21 @@ export default function TableManagementPage() {
|
||||
selectedColumn={selectedColumn}
|
||||
onSelectColumn={setSelectedColumn}
|
||||
onColumnChange={(columnName, field, value) => {
|
||||
if (field === "isUnique") {
|
||||
const currentColumn = columns.find((c) => c.columnName === columnName);
|
||||
if (field === "is_unique") {
|
||||
const currentColumn = columns.find((c) => c.column_name === columnName);
|
||||
if (currentColumn) {
|
||||
handleUniqueToggle(columnName, currentColumn.isUnique || "NO");
|
||||
handleUniqueToggle(columnName, currentColumn.is_unique || "NO");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (field === "isNullable") {
|
||||
const currentColumn = columns.find((c) => c.columnName === columnName);
|
||||
if (field === "is_nullable") {
|
||||
const currentColumn = columns.find((c) => c.column_name === columnName);
|
||||
if (currentColumn) {
|
||||
handleNullableToggle(columnName, currentColumn.isNullable || "YES");
|
||||
handleNullableToggle(columnName, currentColumn.is_nullable || "YES");
|
||||
}
|
||||
return;
|
||||
}
|
||||
const idx = columns.findIndex((c) => c.columnName === columnName);
|
||||
const idx = columns.findIndex((c) => c.column_name === columnName);
|
||||
if (idx >= 0) handleColumnChange(idx, field, value);
|
||||
}}
|
||||
constraints={constraints}
|
||||
@@ -1600,23 +1600,23 @@ export default function TableManagementPage() {
|
||||
{selectedColumn && (
|
||||
<div className="w-[380px] min-w-[380px] flex-shrink-0 overflow-hidden">
|
||||
<ColumnDetailPanel
|
||||
column={columns.find((c) => c.columnName === selectedColumn) ?? null}
|
||||
column={columns.find((c) => c.column_name === selectedColumn) ?? null}
|
||||
tables={tables}
|
||||
referenceTableColumns={referenceTableColumns}
|
||||
secondLevelMenus={secondLevelMenus}
|
||||
numberingRules={[]}
|
||||
onColumnChange={(field, value) => {
|
||||
if (!selectedColumn) return;
|
||||
if (field === "inputType") {
|
||||
if (field === "input_type") {
|
||||
handleInputTypeChange(selectedColumn, value as string);
|
||||
return;
|
||||
}
|
||||
if (field === "referenceTable" && value) {
|
||||
if (field === "reference_table" && value) {
|
||||
loadReferenceTableColumns(value as string);
|
||||
}
|
||||
setColumns((prev) =>
|
||||
prev.map((c) =>
|
||||
c.columnName === selectedColumn ? { ...c, [field]: value } : c,
|
||||
c.column_name === selectedColumn ? { ...c, [field]: value } : c,
|
||||
),
|
||||
);
|
||||
}}
|
||||
@@ -1648,11 +1648,11 @@ export default function TableManagementPage() {
|
||||
// 테이블 목록 새로고침
|
||||
await loadTables();
|
||||
// 새로 생성된 테이블 자동 선택 및 컬럼 로드
|
||||
if (result.data?.tableName) {
|
||||
setSelectedTable(result.data.tableName);
|
||||
if (result.data?.table_name) {
|
||||
setSelectedTable(result.data.table_name);
|
||||
setCurrentPage(1);
|
||||
setColumns([]);
|
||||
await loadColumnTypes(result.data.tableName, 1, pageSize);
|
||||
await loadColumnTypes(result.data.table_name, 1, pageSize);
|
||||
}
|
||||
// 선택 초기화
|
||||
setSelectedTableIds(new Set());
|
||||
@@ -1789,11 +1789,11 @@ export default function TableManagementPage() {
|
||||
{pendingPkColumns.length > 0 ? (
|
||||
<div className="mt-2 flex flex-wrap gap-2">
|
||||
{pendingPkColumns.map((col) => {
|
||||
const colInfo = columns.find((c) => c.columnName === col);
|
||||
const colInfo = columns.find((c) => c.column_name === col);
|
||||
return (
|
||||
<Badge key={col} variant="secondary" className="text-xs">
|
||||
{colInfo?.displayName && colInfo.displayName !== col
|
||||
? `${colInfo.displayName} (${col})`
|
||||
{colInfo?.display_name && colInfo.display_name !== col
|
||||
? `${colInfo.display_name} (${col})`
|
||||
: col}
|
||||
</Badge>
|
||||
);
|
||||
|
||||
@@ -53,22 +53,22 @@ export function ColumnDetailPanel({
|
||||
const [entityTableOpen, setEntityTableOpen] = React.useState(false);
|
||||
const [entityColumnOpen, setEntityColumnOpen] = React.useState(false);
|
||||
|
||||
const typeConf = column ? INPUT_TYPE_COLORS[column.inputType || "text"] : null;
|
||||
const refColumns = column?.referenceTable
|
||||
? referenceTableColumns[column.referenceTable] ?? []
|
||||
const typeConf = column ? INPUT_TYPE_COLORS[column.input_type || "text"] : null;
|
||||
const refColumns = column?.reference_table
|
||||
? referenceTableColumns[column.reference_table] ?? []
|
||||
: [];
|
||||
|
||||
React.useEffect(() => {
|
||||
if (column?.referenceTable && column.referenceTable !== "none") {
|
||||
onLoadReferenceColumns?.(column.referenceTable);
|
||||
if (column?.reference_table && column.reference_table !== "none") {
|
||||
onLoadReferenceColumns?.(column.reference_table);
|
||||
}
|
||||
}, [column?.referenceTable, onLoadReferenceColumns]);
|
||||
}, [column?.reference_table, onLoadReferenceColumns]);
|
||||
|
||||
const advancedCount = useMemo(() => {
|
||||
if (!column) return 0;
|
||||
let n = 0;
|
||||
if (column.defaultValue != null && column.defaultValue !== "") n++;
|
||||
if (column.maxLength != null && column.maxLength > 0) n++;
|
||||
if (column.default_value != null && column.default_value !== "") n++;
|
||||
if (column.max_length != null && column.max_length > 0) n++;
|
||||
return n;
|
||||
}, [column]);
|
||||
|
||||
@@ -81,11 +81,11 @@ export function ColumnDetailPanel({
|
||||
: [
|
||||
{ value: "none", label: "없음" },
|
||||
...tables.map((t) => ({
|
||||
value: t.tableName,
|
||||
value: t.table_name,
|
||||
label:
|
||||
t.displayName && t.displayName !== t.tableName
|
||||
? `${t.displayName} (${t.tableName})`
|
||||
: t.tableName,
|
||||
t.display_name && t.display_name !== t.table_name
|
||||
? `${t.display_name} (${t.table_name})`
|
||||
: t.table_name,
|
||||
})),
|
||||
];
|
||||
|
||||
@@ -114,9 +114,9 @@ export function ColumnDetailPanel({
|
||||
</span>
|
||||
)}
|
||||
<span className="truncate text-sm font-medium">
|
||||
{column.displayName && column.displayName !== column.columnName
|
||||
? `${column.displayName} (${column.columnName})`
|
||||
: column.columnName}
|
||||
{column.display_name && column.display_name !== column.column_name
|
||||
? `${column.display_name} (${column.column_name})`
|
||||
: column.column_name}
|
||||
</span>
|
||||
</div>
|
||||
<Button type="button" variant="ghost" size="icon" className="h-8 w-8 shrink-0" onClick={onClose} aria-label="닫기">
|
||||
@@ -133,12 +133,12 @@ export function ColumnDetailPanel({
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-1.5">
|
||||
{Object.entries(INPUT_TYPE_COLORS).map(([type, conf]) => {
|
||||
const isSelected = (column.inputType || "text") === type;
|
||||
const isSelected = (column.input_type || "text") === type;
|
||||
return (
|
||||
<button
|
||||
key={type}
|
||||
type="button"
|
||||
onClick={() => onColumnChange("inputType", type)}
|
||||
onClick={() => onColumnChange("input_type", type)}
|
||||
className={cn(
|
||||
"flex flex-col items-center gap-1 rounded-lg border px-1.5 py-2.5 text-center transition-all",
|
||||
isSelected
|
||||
@@ -168,7 +168,7 @@ export function ColumnDetailPanel({
|
||||
</section>
|
||||
|
||||
{/* [섹션 2] 타입별 상세 설정 */}
|
||||
{column.inputType === "entity" && (
|
||||
{column.input_type === "entity" && (
|
||||
<section className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Settings2 className="h-4 w-4 text-muted-foreground" />
|
||||
@@ -185,8 +185,8 @@ export function ColumnDetailPanel({
|
||||
role="combobox"
|
||||
className="h-9 w-full justify-between text-xs"
|
||||
>
|
||||
{column.referenceTable && column.referenceTable !== "none"
|
||||
? refTableOpts.find((o) => o.value === column.referenceTable)?.label ?? column.referenceTable
|
||||
{column.reference_table && column.reference_table !== "none"
|
||||
? refTableOpts.find((o) => o.value === column.reference_table)?.label ?? column.reference_table
|
||||
: "테이블 선택..."}
|
||||
<ChevronsUpDown className="ml-2 h-3 w-3 opacity-50" />
|
||||
</Button>
|
||||
@@ -204,14 +204,14 @@ export function ColumnDetailPanel({
|
||||
key={opt.value}
|
||||
value={`${opt.label} ${opt.value}`}
|
||||
onSelect={() => {
|
||||
onColumnChange("referenceTable", opt.value === "none" ? undefined : opt.value);
|
||||
onColumnChange("reference_table", opt.value === "none" ? undefined : opt.value);
|
||||
if (opt.value !== "none") onLoadReferenceColumns?.(opt.value);
|
||||
setEntityTableOpen(false);
|
||||
}}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check
|
||||
className={cn("mr-2 h-3 w-3", column.referenceTable === opt.value ? "opacity-100" : "opacity-0")}
|
||||
className={cn("mr-2 h-3 w-3", column.reference_table === opt.value ? "opacity-100" : "opacity-0")}
|
||||
/>
|
||||
{hasKorean ? (
|
||||
<div className="flex flex-col">
|
||||
@@ -232,7 +232,7 @@ export function ColumnDetailPanel({
|
||||
</div>
|
||||
|
||||
{/* 조인 컬럼 */}
|
||||
{column.referenceTable && column.referenceTable !== "none" && (
|
||||
{column.reference_table && column.reference_table !== "none" && (
|
||||
<div className="space-y-1">
|
||||
<Label className="text-[11px] font-medium text-muted-foreground">조인 컬럼(값)</Label>
|
||||
<Popover open={entityColumnOpen} onOpenChange={setEntityColumnOpen}>
|
||||
@@ -243,12 +243,12 @@ export function ColumnDetailPanel({
|
||||
disabled={refColumns.length === 0}
|
||||
className="h-9 w-full justify-between text-xs"
|
||||
>
|
||||
{column.referenceColumn && column.referenceColumn !== "none"
|
||||
{column.reference_column && column.reference_column !== "none"
|
||||
? (() => {
|
||||
const matched = refColumns.find((c) => c.columnName === column.referenceColumn);
|
||||
return matched?.displayName && matched.displayName !== column.referenceColumn
|
||||
? `${matched.displayName} (${column.referenceColumn})`
|
||||
: column.referenceColumn;
|
||||
const matched = refColumns.find((c) => c.column_name === column.reference_column);
|
||||
return matched?.display_name && matched.display_name !== column.reference_column
|
||||
? `${matched.display_name} (${column.reference_column})`
|
||||
: column.reference_column;
|
||||
})()
|
||||
: "컬럼 선택..."}
|
||||
<ChevronsUpDown className="ml-2 h-3 w-3 opacity-50" />
|
||||
@@ -263,20 +263,20 @@ export function ColumnDetailPanel({
|
||||
<CommandItem
|
||||
value="none"
|
||||
onSelect={() => {
|
||||
onColumnChange("referenceColumn", undefined);
|
||||
onColumnChange("reference_column", undefined);
|
||||
setEntityColumnOpen(false);
|
||||
}}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check className={cn("mr-2 h-3 w-3", !column.referenceColumn ? "opacity-100" : "opacity-0")} />
|
||||
<Check className={cn("mr-2 h-3 w-3", !column.reference_column ? "opacity-100" : "opacity-0")} />
|
||||
선택 안함
|
||||
</CommandItem>
|
||||
{refColumns.map((refCol) => (
|
||||
<CommandItem
|
||||
key={refCol.columnName}
|
||||
value={`${refCol.displayName ?? ""} ${refCol.columnName}`}
|
||||
key={refCol.column_name}
|
||||
value={`${refCol.display_name ?? ""} ${refCol.column_name}`}
|
||||
onSelect={() => {
|
||||
onColumnChange("referenceColumn", refCol.columnName);
|
||||
onColumnChange("reference_column", refCol.column_name);
|
||||
setEntityColumnOpen(false);
|
||||
}}
|
||||
className="text-xs"
|
||||
@@ -284,16 +284,16 @@ export function ColumnDetailPanel({
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-3 w-3",
|
||||
column.referenceColumn === refCol.columnName ? "opacity-100" : "opacity-0",
|
||||
column.reference_column === refCol.column_name ? "opacity-100" : "opacity-0",
|
||||
)}
|
||||
/>
|
||||
{refCol.displayName && refCol.displayName !== refCol.columnName ? (
|
||||
{refCol.display_name && refCol.display_name !== refCol.column_name ? (
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium">{refCol.displayName}</span>
|
||||
<span className="text-[10px] text-muted-foreground">{refCol.columnName}</span>
|
||||
<span className="font-medium">{refCol.display_name}</span>
|
||||
<span className="text-[10px] text-muted-foreground">{refCol.column_name}</span>
|
||||
</div>
|
||||
) : (
|
||||
<span>{refCol.columnName}</span>
|
||||
<span>{refCol.column_name}</span>
|
||||
)}
|
||||
</CommandItem>
|
||||
))}
|
||||
@@ -306,21 +306,21 @@ export function ColumnDetailPanel({
|
||||
)}
|
||||
|
||||
{/* 참조 요약 미니맵 */}
|
||||
{column.referenceTable && column.referenceTable !== "none" && column.referenceColumn && (
|
||||
{column.reference_table && column.reference_table !== "none" && column.reference_column && (
|
||||
<div className="flex items-center gap-2 rounded-md bg-violet-50 px-3 py-2">
|
||||
<span className="text-[11px] font-semibold text-violet-600">
|
||||
{(() => {
|
||||
const tbl = refTableOpts.find((o) => o.value === column.referenceTable);
|
||||
return tbl?.label ?? column.referenceTable;
|
||||
const tbl = refTableOpts.find((o) => o.value === column.reference_table);
|
||||
return tbl?.label ?? column.reference_table;
|
||||
})()}
|
||||
</span>
|
||||
<span className="text-muted-foreground text-[10px]">→</span>
|
||||
<span className="text-[11px] font-semibold text-violet-600">
|
||||
{(() => {
|
||||
const col = refColumns.find((c) => c.columnName === column.referenceColumn);
|
||||
return col?.displayName && col.displayName !== column.referenceColumn
|
||||
? `${col.displayName} (${column.referenceColumn})`
|
||||
: column.referenceColumn;
|
||||
const col = refColumns.find((c) => c.column_name === column.reference_column);
|
||||
return col?.display_name && col.display_name !== column.reference_column
|
||||
? `${col.display_name} (${column.reference_column})`
|
||||
: column.reference_column;
|
||||
})()}
|
||||
</span>
|
||||
</div>
|
||||
@@ -328,7 +328,7 @@ export function ColumnDetailPanel({
|
||||
</section>
|
||||
)}
|
||||
|
||||
{column.inputType === "code" && (
|
||||
{column.input_type === "code" && (
|
||||
<section className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Settings2 className="h-4 w-4 text-muted-foreground" />
|
||||
@@ -338,8 +338,8 @@ export function ColumnDetailPanel({
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-xs text-muted-foreground">공통코드 카테고리</Label>
|
||||
<Select
|
||||
value={column.codeCategory ?? "none"}
|
||||
onValueChange={(v) => onColumnChange("codeCategory", v === "none" ? undefined : v)}
|
||||
value={column.code_category ?? "none"}
|
||||
onValueChange={(v) => onColumnChange("code_category", v === "none" ? undefined : v)}
|
||||
>
|
||||
<SelectTrigger className="h-9 text-xs">
|
||||
<SelectValue placeholder="코드 선택" />
|
||||
@@ -353,13 +353,13 @@ export function ColumnDetailPanel({
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
{column.codeCategory && column.codeCategory !== "none" && (
|
||||
{column.code_category && column.code_category !== "none" && (
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-xs text-muted-foreground">계층 역할</Label>
|
||||
<Select
|
||||
value={column.hierarchyRole ?? "none"}
|
||||
value={column.hierarchy_role ?? "none"}
|
||||
onValueChange={(v) =>
|
||||
onColumnChange("hierarchyRole", v === "none" ? undefined : (v as "large" | "medium" | "small"))
|
||||
onColumnChange("hierarchy_role", v === "none" ? undefined : (v as "large" | "medium" | "small"))
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="h-9 text-xs">
|
||||
@@ -378,7 +378,7 @@ export function ColumnDetailPanel({
|
||||
</section>
|
||||
)}
|
||||
|
||||
{column.inputType === "category" && (
|
||||
{column.input_type === "category" && (
|
||||
<section className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Settings2 className="h-4 w-4 text-muted-foreground" />
|
||||
@@ -387,8 +387,8 @@ export function ColumnDetailPanel({
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-xs text-muted-foreground">참조 (테이블.컬럼)</Label>
|
||||
<Input
|
||||
value={column.categoryRef ?? ""}
|
||||
onChange={(e) => onColumnChange("categoryRef", e.target.value || null)}
|
||||
value={column.category_ref ?? ""}
|
||||
onChange={(e) => onColumnChange("category_ref", e.target.value || null)}
|
||||
placeholder="테이블명.컬럼명"
|
||||
className="h-9 text-xs"
|
||||
/>
|
||||
@@ -396,7 +396,7 @@ export function ColumnDetailPanel({
|
||||
</section>
|
||||
)}
|
||||
|
||||
{column.inputType === "numbering" && (
|
||||
{column.input_type === "numbering" && (
|
||||
<section className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Settings2 className="h-4 w-4 text-muted-foreground" />
|
||||
@@ -416,9 +416,9 @@ export function ColumnDetailPanel({
|
||||
<Label className="text-sm font-medium">표시 이름</Label>
|
||||
</div>
|
||||
<Input
|
||||
value={column.displayName ?? ""}
|
||||
onChange={(e) => onColumnChange("displayName", e.target.value)}
|
||||
placeholder={column.columnName}
|
||||
value={column.display_name ?? ""}
|
||||
onChange={(e) => onColumnChange("display_name", e.target.value)}
|
||||
placeholder={column.column_name}
|
||||
className="h-9 text-sm"
|
||||
/>
|
||||
</section>
|
||||
@@ -436,8 +436,8 @@ export function ColumnDetailPanel({
|
||||
<p className="text-xs text-muted-foreground">비워두면 저장할 수 없어요.</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={column.isNullable === "NO"}
|
||||
onCheckedChange={(checked) => onColumnChange("isNullable", checked ? "NO" : "YES")}
|
||||
checked={column.is_nullable === "NO"}
|
||||
onCheckedChange={(checked) => onColumnChange("is_nullable", checked ? "NO" : "YES")}
|
||||
aria-label="필수 입력"
|
||||
/>
|
||||
</div>
|
||||
@@ -484,8 +484,8 @@ export function ColumnDetailPanel({
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-xs text-muted-foreground">기본값</Label>
|
||||
<Input
|
||||
value={column.defaultValue ?? ""}
|
||||
onChange={(e) => onColumnChange("defaultValue", e.target.value)}
|
||||
value={column.default_value ?? ""}
|
||||
onChange={(e) => onColumnChange("default_value", e.target.value)}
|
||||
placeholder="기본값"
|
||||
className="h-9 text-xs"
|
||||
/>
|
||||
@@ -494,10 +494,10 @@ export function ColumnDetailPanel({
|
||||
<Label className="text-xs text-muted-foreground">최대 길이</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={column.maxLength ?? ""}
|
||||
value={column.max_length ?? ""}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value;
|
||||
onColumnChange("maxLength", v === "" ? undefined : Number(v));
|
||||
onColumnChange("max_length", v === "" ? undefined : Number(v));
|
||||
}}
|
||||
placeholder="숫자"
|
||||
className="h-9 text-xs"
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { ReferenceTableColumn } from "@/lib/api/entityJoin";
|
||||
|
||||
export interface ColumnGridConstraints {
|
||||
primaryKey: { columns: string[] };
|
||||
indexes: Array<{ columns: string[]; isUnique: boolean }>;
|
||||
indexes: Array<{ columns: string[]; is_unique: boolean }>;
|
||||
}
|
||||
|
||||
export interface ColumnGridProps {
|
||||
@@ -35,7 +35,7 @@ function getIndexState(
|
||||
): { isPk: boolean; hasIndex: boolean } {
|
||||
const isPk = constraints.primaryKey.columns.includes(columnName);
|
||||
const hasIndex = constraints.indexes.some(
|
||||
(idx) => !idx.isUnique && idx.columns.length === 1 && idx.columns[0] === columnName,
|
||||
(idx) => !idx.is_unique && idx.columns.length === 1 && idx.columns[0] === columnName,
|
||||
);
|
||||
return { isPk, hasIndex };
|
||||
}
|
||||
@@ -68,7 +68,7 @@ export function ColumnGrid({
|
||||
/** typeFilter 적용 후 그룹별로 정렬 */
|
||||
const filteredAndGrouped = useMemo(() => {
|
||||
const filtered =
|
||||
typeFilter != null ? columns.filter((c) => (c.inputType || "text") === typeFilter) : columns;
|
||||
typeFilter != null ? columns.filter((c) => (c.input_type || "text") === typeFilter) : columns;
|
||||
const groups = { basic: [] as ColumnTypeInfo[], reference: [] as ColumnTypeInfo[], meta: [] as ColumnTypeInfo[] };
|
||||
for (const col of filtered) {
|
||||
const group = getColumnGroup(col);
|
||||
@@ -116,20 +116,20 @@ export function ColumnGrid({
|
||||
</Badge>
|
||||
</div>
|
||||
{list.map((column) => {
|
||||
const typeConf = INPUT_TYPE_COLORS[column.inputType || "text"] || INPUT_TYPE_COLORS.text;
|
||||
const idxState = getIdxState(column.columnName);
|
||||
const isSelected = selectedColumn === column.columnName;
|
||||
const typeConf = INPUT_TYPE_COLORS[column.input_type || "text"] || INPUT_TYPE_COLORS.text;
|
||||
const idxState = getIdxState(column.column_name);
|
||||
const isSelected = selectedColumn === column.column_name;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={column.columnName}
|
||||
key={column.column_name}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => onSelectColumn(column.columnName)}
|
||||
onClick={() => onSelectColumn(column.column_name)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
e.preventDefault();
|
||||
onSelectColumn(column.columnName);
|
||||
onSelectColumn(column.column_name);
|
||||
}
|
||||
}}
|
||||
className={cn(
|
||||
@@ -145,15 +145,15 @@ export function ColumnGrid({
|
||||
{/* 라벨 + 컬럼명 (한글라벨 (영어명) 동시 표시) */}
|
||||
<div className="min-w-0">
|
||||
<div className="truncate text-sm font-medium">
|
||||
{column.displayName && column.displayName !== column.columnName
|
||||
? `${column.displayName} (${column.columnName})`
|
||||
: column.columnName}
|
||||
{column.display_name && column.display_name !== column.column_name
|
||||
? `${column.display_name} (${column.column_name})`
|
||||
: column.column_name}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 참조/설정 칩 */}
|
||||
<div className="flex min-w-0 flex-wrap gap-1">
|
||||
{column.inputType === "entity" && column.referenceTable && column.referenceTable !== "none" && (
|
||||
{column.input_type === "entity" && column.reference_table && column.reference_table !== "none" && (
|
||||
<>
|
||||
<Badge
|
||||
variant="outline"
|
||||
@@ -161,51 +161,51 @@ export function ColumnGrid({
|
||||
title={
|
||||
tables
|
||||
? (() => {
|
||||
const t = tables.find((tb) => tb.tableName === column.referenceTable);
|
||||
return t?.displayName && t.displayName !== t.tableName
|
||||
? `${t.displayName} (${column.referenceTable})`
|
||||
: column.referenceTable;
|
||||
const t = tables.find((tb) => tb.table_name === column.reference_table);
|
||||
return t?.display_name && t.display_name !== t.table_name
|
||||
? `${t.display_name} (${column.reference_table})`
|
||||
: column.reference_table;
|
||||
})()
|
||||
: column.referenceTable
|
||||
: column.reference_table
|
||||
}
|
||||
>
|
||||
{column.referenceTable}
|
||||
{column.reference_table}
|
||||
</Badge>
|
||||
<span className="text-muted-foreground text-xs">→</span>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-xs font-normal"
|
||||
title={
|
||||
referenceTableColumns?.[column.referenceTable]
|
||||
referenceTableColumns?.[column.reference_table]
|
||||
? (() => {
|
||||
const refCols = referenceTableColumns[column.referenceTable];
|
||||
const c = refCols.find((rc) => rc.columnName === (column.referenceColumn ?? ""));
|
||||
return c?.displayName && c.displayName !== c.columnName
|
||||
? `${c.displayName} (${column.referenceColumn})`
|
||||
: column.referenceColumn ?? "—";
|
||||
const refCols = referenceTableColumns[column.reference_table];
|
||||
const c = refCols.find((rc) => rc.column_name === (column.reference_column ?? ""));
|
||||
return c?.display_name && c.display_name !== c.column_name
|
||||
? `${c.display_name} (${column.reference_column})`
|
||||
: column.reference_column ?? "—";
|
||||
})()
|
||||
: column.referenceColumn ?? "—"
|
||||
: column.reference_column ?? "—"
|
||||
}
|
||||
>
|
||||
{column.referenceColumn || "—"}
|
||||
{column.reference_column || "—"}
|
||||
</Badge>
|
||||
</>
|
||||
)}
|
||||
{column.inputType === "code" && (
|
||||
{column.input_type === "code" && (
|
||||
<span className="text-muted-foreground truncate text-xs">
|
||||
{column.codeCategory ?? "—"} · {column.defaultValue ?? ""}
|
||||
{column.code_category ?? "—"} · {column.default_value ?? ""}
|
||||
</span>
|
||||
)}
|
||||
{column.inputType === "numbering" && column.numberingRuleId && (
|
||||
{column.input_type === "numbering" && column.numbering_rule_id && (
|
||||
<Badge variant="outline" className="text-xs font-normal">
|
||||
{column.numberingRuleId}
|
||||
{column.numbering_rule_id}
|
||||
</Badge>
|
||||
)}
|
||||
{column.inputType !== "entity" &&
|
||||
column.inputType !== "code" &&
|
||||
column.inputType !== "numbering" &&
|
||||
(column.defaultValue ? (
|
||||
<span className="text-muted-foreground truncate text-xs">{column.defaultValue}</span>
|
||||
{column.input_type !== "entity" &&
|
||||
column.input_type !== "code" &&
|
||||
column.input_type !== "numbering" &&
|
||||
(column.default_value ? (
|
||||
<span className="text-muted-foreground truncate text-xs">{column.default_value}</span>
|
||||
) : (
|
||||
<span className="text-muted-foreground/60 text-xs">—</span>
|
||||
))}
|
||||
@@ -229,7 +229,7 @@ export function ColumnGrid({
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onPkToggle?.(column.columnName, !idxState.isPk);
|
||||
onPkToggle?.(column.column_name, !idxState.isPk);
|
||||
}}
|
||||
title="Primary Key 토글"
|
||||
>
|
||||
@@ -239,13 +239,13 @@ export function ColumnGrid({
|
||||
type="button"
|
||||
className={cn(
|
||||
"rounded border px-1.5 py-0.5 text-[10px] font-bold transition-colors",
|
||||
column.isNullable === "NO"
|
||||
column.is_nullable === "NO"
|
||||
? "border-amber-200 bg-amber-50 text-amber-600"
|
||||
: "border-border text-muted-foreground/40 hover:border-amber-200 hover:text-amber-400",
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onColumnChange(column.columnName, "isNullable", column.isNullable === "NO" ? "YES" : "NO");
|
||||
onColumnChange(column.column_name, "is_nullable", column.is_nullable === "NO" ? "YES" : "NO");
|
||||
}}
|
||||
title="Not Null 토글"
|
||||
>
|
||||
@@ -261,7 +261,7 @@ export function ColumnGrid({
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onIndexToggle?.(column.columnName, !idxState.hasIndex);
|
||||
onIndexToggle?.(column.column_name, !idxState.hasIndex);
|
||||
}}
|
||||
title="Index 토글"
|
||||
>
|
||||
@@ -271,13 +271,13 @@ export function ColumnGrid({
|
||||
type="button"
|
||||
className={cn(
|
||||
"rounded border px-1.5 py-0.5 text-[10px] font-bold transition-colors",
|
||||
column.isUnique === "YES"
|
||||
column.is_unique === "YES"
|
||||
? "border-violet-200 bg-violet-50 text-violet-600"
|
||||
: "border-border text-muted-foreground/40 hover:border-violet-200 hover:text-violet-400",
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onColumnChange(column.columnName, "isUnique", column.isUnique === "YES" ? "NO" : "YES");
|
||||
onColumnChange(column.column_name, "is_unique", column.is_unique === "YES" ? "NO" : "YES");
|
||||
}}
|
||||
title="Unique 토글"
|
||||
>
|
||||
@@ -293,7 +293,7 @@ export function ColumnGrid({
|
||||
className="h-8 w-8"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onSelectColumn(column.columnName);
|
||||
onSelectColumn(column.column_name);
|
||||
}}
|
||||
aria-label="상세 설정"
|
||||
>
|
||||
|
||||
@@ -15,7 +15,7 @@ export interface TypeOverviewStripProps {
|
||||
function countByInputType(columns: ColumnTypeInfo[]): Record<string, number> {
|
||||
const counts: Record<string, number> = {};
|
||||
for (const col of columns) {
|
||||
const t = col.inputType || "text";
|
||||
const t = col.input_type || "text";
|
||||
counts[t] = (counts[t] || 0) + 1;
|
||||
}
|
||||
return counts;
|
||||
|
||||
@@ -4,40 +4,40 @@
|
||||
*/
|
||||
|
||||
export interface TableInfo {
|
||||
tableName: string;
|
||||
displayName: string;
|
||||
table_name: string;
|
||||
display_name: string;
|
||||
description: string;
|
||||
columnCount: number;
|
||||
column_count: number;
|
||||
}
|
||||
|
||||
export interface ColumnTypeInfo {
|
||||
columnName: string;
|
||||
displayName: string;
|
||||
inputType: string;
|
||||
detailSettings: string;
|
||||
column_name: string;
|
||||
display_name: string;
|
||||
input_type: string;
|
||||
detail_settings: string;
|
||||
description: string;
|
||||
isNullable: string;
|
||||
isUnique: string;
|
||||
defaultValue?: string;
|
||||
maxLength?: number;
|
||||
numericPrecision?: number;
|
||||
numericScale?: number;
|
||||
codeCategory?: string;
|
||||
codeValue?: string;
|
||||
referenceTable?: string;
|
||||
referenceColumn?: string;
|
||||
displayColumn?: string;
|
||||
categoryMenus?: number[];
|
||||
hierarchyRole?: "large" | "medium" | "small";
|
||||
numberingRuleId?: string;
|
||||
categoryRef?: string | null;
|
||||
is_nullable: string;
|
||||
is_unique: string;
|
||||
default_value?: string;
|
||||
max_length?: number;
|
||||
numeric_precision?: number;
|
||||
numeric_scale?: number;
|
||||
code_category?: string;
|
||||
code_value?: string;
|
||||
reference_table?: string;
|
||||
reference_column?: string;
|
||||
display_column?: string;
|
||||
category_menus?: number[];
|
||||
hierarchy_role?: "large" | "medium" | "small";
|
||||
numbering_rule_id?: string;
|
||||
category_ref?: string | null;
|
||||
}
|
||||
|
||||
export interface SecondLevelMenu {
|
||||
menuObjid: number;
|
||||
menuName: string;
|
||||
parentMenuName: string;
|
||||
screenCode?: string;
|
||||
menu_objid: number;
|
||||
menu_name: string;
|
||||
parent_menu_name: string;
|
||||
screen_code?: string;
|
||||
}
|
||||
|
||||
/** 컬럼 그룹 분류 */
|
||||
@@ -73,7 +73,7 @@ export const INPUT_TYPE_COLORS: Record<string, TypeColorConfig> = {
|
||||
/** 컬럼 그룹 판별 */
|
||||
export function getColumnGroup(col: ColumnTypeInfo): ColumnGroup {
|
||||
const metaCols = ["id", "created_date", "updated_date", "writer", "company_code"];
|
||||
if (metaCols.includes(col.columnName)) return "meta";
|
||||
if (["entity", "code", "category"].includes(col.inputType)) return "reference";
|
||||
if (metaCols.includes(col.column_name)) return "meta";
|
||||
if (["entity", "code", "category"].includes(col.input_type)) return "reference";
|
||||
return "basic";
|
||||
}
|
||||
|
||||
@@ -46,11 +46,11 @@ interface FlowSummary {
|
||||
}
|
||||
|
||||
interface NodeFlow {
|
||||
flowId: number;
|
||||
flowName: string;
|
||||
flowDescription: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
flow_id: number;
|
||||
flow_name: string;
|
||||
flow_description: string;
|
||||
created_date: string;
|
||||
updated_date: string;
|
||||
summary: FlowSummary;
|
||||
}
|
||||
|
||||
@@ -182,10 +182,10 @@ function FlowCard({
|
||||
{/* 카드 바디 */}
|
||||
<div className="px-4 pb-3 pt-3.5">
|
||||
<h3 className="mb-1 truncate text-sm font-semibold tracking-tight text-zinc-100">
|
||||
{flow.flowName}
|
||||
{flow.flow_name}
|
||||
</h3>
|
||||
<p className="mb-3 line-clamp-2 min-h-[2.5rem] text-[11px] leading-relaxed text-zinc-500">
|
||||
{flow.flowDescription || "설명이 아직 없어요"}
|
||||
{flow.flow_description || "설명이 아직 없어요"}
|
||||
</p>
|
||||
|
||||
{/* 노드 타입 칩 */}
|
||||
@@ -206,7 +206,7 @@ function FlowCard({
|
||||
{/* 카드 푸터 */}
|
||||
<div className="flex items-center justify-between border-t border-zinc-800/40 px-4 py-2.5">
|
||||
<span className="font-mono text-[11px] text-zinc-600">
|
||||
수정 {relativeTime(flow.updatedAt)}
|
||||
수정 {relativeTime(flow.updated_date)}
|
||||
</span>
|
||||
<div className="flex gap-0.5">
|
||||
<button
|
||||
@@ -284,13 +284,13 @@ export default function DataFlowList({ onLoadFlow }: DataFlowListProps) {
|
||||
const handleCopy = async (flow: NodeFlow) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await apiClient.get(`/dataflow/node-flows/${flow.flowId}`);
|
||||
const response = await apiClient.get(`/dataflow/node-flows/${flow.flow_id}`);
|
||||
if (!response.data.success) throw new Error(response.data.message || "플로우 조회 실패");
|
||||
|
||||
const copyResponse = await apiClient.post("/dataflow/node-flows", {
|
||||
flowName: `${flow.flowName} (복사본)`,
|
||||
flowDescription: flow.flowDescription,
|
||||
flowData: response.data.data.flowData,
|
||||
flow_name: `${flow.flow_name} (복사본)`,
|
||||
flow_description: flow.flow_description,
|
||||
flow_data: response.data.data.flow_data,
|
||||
});
|
||||
|
||||
if (copyResponse.data.success) {
|
||||
@@ -313,9 +313,9 @@ export default function DataFlowList({ onLoadFlow }: DataFlowListProps) {
|
||||
if (!selectedFlow) return;
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await apiClient.delete(`/dataflow/node-flows/${selectedFlow.flowId}`);
|
||||
const response = await apiClient.delete(`/dataflow/node-flows/${selectedFlow.flow_id}`);
|
||||
if (response.data.success) {
|
||||
toast.success(`"${selectedFlow.flowName}" 플로우를 삭제했어요`);
|
||||
toast.success(`"${selectedFlow.flow_name}" 플로우를 삭제했어요`);
|
||||
await loadFlows();
|
||||
} else {
|
||||
throw new Error(response.data.message || "플로우 삭제 실패");
|
||||
@@ -336,8 +336,8 @@ export default function DataFlowList({ onLoadFlow }: DataFlowListProps) {
|
||||
() =>
|
||||
flows.filter(
|
||||
(f) =>
|
||||
f.flowName.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
(f.flowDescription || "").toLowerCase().includes(searchTerm.toLowerCase()),
|
||||
f.flow_name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
(f.flow_description || "").toLowerCase().includes(searchTerm.toLowerCase()),
|
||||
),
|
||||
[flows, searchTerm],
|
||||
);
|
||||
@@ -478,9 +478,9 @@ export default function DataFlowList({ onLoadFlow }: DataFlowListProps) {
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
{filteredFlows.map((flow) => (
|
||||
<FlowCard
|
||||
key={flow.flowId}
|
||||
key={flow.flow_id}
|
||||
flow={flow}
|
||||
onOpen={() => onLoadFlow(flow.flowId)}
|
||||
onOpen={() => onLoadFlow(flow.flow_id)}
|
||||
onCopy={() => handleCopy(flow)}
|
||||
onDelete={() => handleDelete(flow)}
|
||||
/>
|
||||
@@ -503,19 +503,19 @@ export default function DataFlowList({ onLoadFlow }: DataFlowListProps) {
|
||||
<div className="space-y-2">
|
||||
{filteredFlows.map((flow) => (
|
||||
<div
|
||||
key={flow.flowId}
|
||||
key={flow.flow_id}
|
||||
className="group flex cursor-pointer items-center gap-4 rounded-lg border border-zinc-800 bg-zinc-900/80 px-4 py-3 transition-all hover:border-violet-500/40 hover:bg-zinc-900"
|
||||
onClick={() => onLoadFlow(flow.flowId)}
|
||||
onClick={() => onLoadFlow(flow.flow_id)}
|
||||
>
|
||||
<div className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-violet-500/10">
|
||||
<Network className="h-5 w-5 text-violet-400" />
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<h3 className="truncate text-sm font-semibold text-zinc-100">
|
||||
{flow.flowName}
|
||||
{flow.flow_name}
|
||||
</h3>
|
||||
<p className="truncate text-xs text-zinc-500">
|
||||
{flow.flowDescription || "설명이 아직 없어요"}
|
||||
{flow.flow_description || "설명이 아직 없어요"}
|
||||
</p>
|
||||
</div>
|
||||
<div className="hidden items-center gap-1.5 lg:flex">
|
||||
@@ -534,7 +534,7 @@ export default function DataFlowList({ onLoadFlow }: DataFlowListProps) {
|
||||
})}
|
||||
</div>
|
||||
<span className="hidden font-mono text-[11px] text-zinc-600 sm:block">
|
||||
{relativeTime(flow.updatedAt)}
|
||||
{relativeTime(flow.updated_date)}
|
||||
</span>
|
||||
<div className="flex gap-0.5" onClick={(e) => e.stopPropagation()}>
|
||||
<button
|
||||
@@ -563,7 +563,7 @@ export default function DataFlowList({ onLoadFlow }: DataFlowListProps) {
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-base sm:text-lg">플로우를 삭제할까요?</DialogTitle>
|
||||
<DialogDescription className="text-xs sm:text-sm">
|
||||
“{selectedFlow?.flowName}” 플로우가 완전히 삭제돼요.
|
||||
“{selectedFlow?.flow_name}” 플로우가 완전히 삭제돼요.
|
||||
<br />
|
||||
<span className="text-destructive font-medium">
|
||||
이 작업은 되돌릴 수 없으며, 모든 노드와 연결 정보가 함께 삭제돼요.
|
||||
|
||||
@@ -29,10 +29,10 @@ import { tableManagementApi } from "@/lib/api/tableManagement";
|
||||
import { apiClient } from "@/lib/api/client";
|
||||
|
||||
interface SecondLevelMenu {
|
||||
menuObjid: number;
|
||||
menuName: string;
|
||||
parentMenuName: string;
|
||||
screenCode?: string;
|
||||
menu_objid: number;
|
||||
menu_name: string;
|
||||
parent_menu_name: string;
|
||||
screen_code?: string;
|
||||
}
|
||||
|
||||
interface AddCategoryColumnDialogProps {
|
||||
@@ -56,8 +56,8 @@ export function AddCategoryColumnDialog({
|
||||
const [physicalColumns, setPhysicalColumns] = useState<string[]>([]);
|
||||
const [secondLevelMenus, setSecondLevelMenus] = useState<SecondLevelMenu[]>([]);
|
||||
const [selectedMenus, setSelectedMenus] = useState<number[]>([]);
|
||||
const [logicalColumnName, setLogicalColumnName] = useState("");
|
||||
const [physicalColumnName, setPhysicalColumnName] = useState("");
|
||||
const [logical_column_name, set_logical_column_name] = useState("");
|
||||
const [physical_column_name, set_physical_column_name] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
|
||||
// 다이얼로그 열릴 때 데이터 로드
|
||||
@@ -99,22 +99,22 @@ export function AddCategoryColumnDialog({
|
||||
};
|
||||
|
||||
// 메뉴 선택/해제
|
||||
const toggleMenu = (menuObjid: number) => {
|
||||
const toggleMenu = (menu_objid: number) => {
|
||||
setSelectedMenus((prev) =>
|
||||
prev.includes(menuObjid)
|
||||
? prev.filter((id) => id !== menuObjid)
|
||||
: [...prev, menuObjid]
|
||||
prev.includes(menu_objid)
|
||||
? prev.filter((id) => id !== menu_objid)
|
||||
: [...prev, menu_objid]
|
||||
);
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
// 입력 검증
|
||||
if (!logicalColumnName.trim()) {
|
||||
if (!logical_column_name.trim()) {
|
||||
toast.error("논리적 컬럼명을 입력해주세요");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!physicalColumnName) {
|
||||
if (!physical_column_name) {
|
||||
toast.error("실제 컬럼을 선택해주세요");
|
||||
return;
|
||||
}
|
||||
@@ -128,12 +128,12 @@ export function AddCategoryColumnDialog({
|
||||
|
||||
try {
|
||||
// 선택된 각 메뉴에 대해 매핑 생성
|
||||
const promises = selectedMenus.map((menuObjid) =>
|
||||
const promises = selectedMenus.map((menu_objid) =>
|
||||
createColumnMapping({
|
||||
tableName,
|
||||
logicalColumnName: logicalColumnName.trim(),
|
||||
physicalColumnName,
|
||||
menuObjid,
|
||||
logicalColumnName: logical_column_name.trim(),
|
||||
physicalColumnName: physical_column_name,
|
||||
menuObjid: menu_objid,
|
||||
description: description.trim() || undefined,
|
||||
})
|
||||
);
|
||||
@@ -165,8 +165,8 @@ export function AddCategoryColumnDialog({
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
setLogicalColumnName("");
|
||||
setPhysicalColumnName("");
|
||||
set_logical_column_name("");
|
||||
set_physical_column_name("");
|
||||
setDescription("");
|
||||
setSelectedMenus([]);
|
||||
};
|
||||
@@ -196,7 +196,7 @@ export function AddCategoryColumnDialog({
|
||||
<Label className="text-xs sm:text-sm">
|
||||
실제 컬럼 (물리적) *
|
||||
</Label>
|
||||
<Select value={physicalColumnName} onValueChange={setPhysicalColumnName}>
|
||||
<Select value={physical_column_name} onValueChange={set_physical_column_name}>
|
||||
<SelectTrigger className="h-8 text-xs sm:h-10 sm:text-sm">
|
||||
<SelectValue placeholder="컬럼 선택" />
|
||||
</SelectTrigger>
|
||||
@@ -219,8 +219,8 @@ export function AddCategoryColumnDialog({
|
||||
논리적 컬럼명 (메뉴별 식별용) *
|
||||
</Label>
|
||||
<Input
|
||||
value={logicalColumnName}
|
||||
onChange={(e) => setLogicalColumnName(e.target.value)}
|
||||
value={logical_column_name}
|
||||
onChange={(e) => set_logical_column_name(e.target.value)}
|
||||
placeholder="예: status_stock, status_sales"
|
||||
className="h-8 text-xs sm:h-10 sm:text-sm"
|
||||
/>
|
||||
@@ -239,18 +239,18 @@ export function AddCategoryColumnDialog({
|
||||
<p className="text-xs text-muted-foreground">로딩 중...</p>
|
||||
) : (
|
||||
secondLevelMenus.map((menu) => (
|
||||
<div key={menu.menuObjid} className="flex items-center gap-2">
|
||||
<div key={menu.menu_objid} className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
id={`menu-${menu.menuObjid}`}
|
||||
checked={selectedMenus.includes(menu.menuObjid)}
|
||||
onCheckedChange={() => toggleMenu(menu.menuObjid)}
|
||||
id={`menu-${menu.menu_objid}`}
|
||||
checked={selectedMenus.includes(menu.menu_objid)}
|
||||
onCheckedChange={() => toggleMenu(menu.menu_objid)}
|
||||
className="h-4 w-4"
|
||||
/>
|
||||
<label
|
||||
htmlFor={`menu-${menu.menuObjid}`}
|
||||
htmlFor={`menu-${menu.menu_objid}`}
|
||||
className="text-xs sm:text-sm cursor-pointer flex-1"
|
||||
>
|
||||
{menu.parentMenuName} → {menu.menuName}
|
||||
{menu.parent_menu_name} → {menu.menu_name}
|
||||
</label>
|
||||
</div>
|
||||
))
|
||||
@@ -290,7 +290,7 @@ export function AddCategoryColumnDialog({
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
disabled={!logicalColumnName || !physicalColumnName || selectedMenus.length === 0 || loading}
|
||||
disabled={!logical_column_name || !physical_column_name || selectedMenus.length === 0 || loading}
|
||||
className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm"
|
||||
>
|
||||
{loading ? "추가 중..." : "추가"}
|
||||
|
||||
@@ -8,12 +8,12 @@ import { Input } from "@/components/ui/input";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export interface CategoryColumn {
|
||||
tableName: string;
|
||||
tableLabel?: string;
|
||||
columnName: string;
|
||||
columnLabel: string;
|
||||
inputType: string;
|
||||
valueCount?: number;
|
||||
table_name: string;
|
||||
table_label?: string;
|
||||
column_name: string;
|
||||
column_label: string;
|
||||
input_type: string;
|
||||
value_count?: number;
|
||||
}
|
||||
|
||||
interface CategoryColumnListProps {
|
||||
@@ -49,18 +49,18 @@ export function CategoryColumnList({
|
||||
// 검색어로 필터링된 컬럼 목록
|
||||
const filteredColumns = useMemo(() => {
|
||||
if (!searchQuery.trim()) return columns;
|
||||
|
||||
|
||||
const query = searchQuery.toLowerCase();
|
||||
return columns.filter((col) => {
|
||||
const columnName = (col.columnName || "").toLowerCase();
|
||||
const columnLabel = (col.columnLabel || "").toLowerCase();
|
||||
const tableName = (col.tableName || "").toLowerCase();
|
||||
const tableLabel = (col.tableLabel || "").toLowerCase();
|
||||
|
||||
return columnName.includes(query) ||
|
||||
columnLabel.includes(query) ||
|
||||
tableName.includes(query) ||
|
||||
tableLabel.includes(query);
|
||||
const column_name = (col.column_name || "").toLowerCase();
|
||||
const column_label = (col.column_label || "").toLowerCase();
|
||||
const table_name = (col.table_name || "").toLowerCase();
|
||||
const table_label = (col.table_label || "").toLowerCase();
|
||||
|
||||
return column_name.includes(query) ||
|
||||
column_label.includes(query) ||
|
||||
table_name.includes(query) ||
|
||||
table_label.includes(query);
|
||||
});
|
||||
}, [columns, searchQuery]);
|
||||
|
||||
@@ -70,7 +70,7 @@ export function CategoryColumnList({
|
||||
const groupMap = new Map<string, CategoryColumn[]>();
|
||||
|
||||
for (const col of filteredColumns) {
|
||||
const key = col.tableName;
|
||||
const key = col.table_name;
|
||||
if (!groupMap.has(key)) {
|
||||
groupMap.set(key, []);
|
||||
}
|
||||
@@ -80,7 +80,7 @@ export function CategoryColumnList({
|
||||
for (const [tblName, cols] of groupMap) {
|
||||
groups.push({
|
||||
tableName: tblName,
|
||||
tableLabel: cols[0]?.tableLabel || tblName,
|
||||
tableLabel: cols[0]?.table_label || tblName,
|
||||
columns: cols,
|
||||
});
|
||||
}
|
||||
@@ -91,12 +91,12 @@ export function CategoryColumnList({
|
||||
// 선택된 컬럼이 있는 그룹을 자동 펼침
|
||||
useEffect(() => {
|
||||
if (!selectedColumn) return;
|
||||
const tableName = selectedColumn.split(".")[0];
|
||||
if (tableName) {
|
||||
const tblName = selectedColumn.split(".")[0];
|
||||
if (tblName) {
|
||||
setExpandedGroups((prev) => {
|
||||
if (prev.has(tableName)) return prev;
|
||||
if (prev.has(tblName)) return prev;
|
||||
const next = new Set(prev);
|
||||
next.add(tableName);
|
||||
next.add(tblName);
|
||||
return next;
|
||||
});
|
||||
}
|
||||
@@ -112,7 +112,7 @@ export function CategoryColumnList({
|
||||
setIsLoading(true);
|
||||
try {
|
||||
console.log("🔍 테이블 기반 카테고리 컬럼 조회 시작", { tableName });
|
||||
|
||||
|
||||
// table_type_columns에서 input_type='category'인 컬럼 조회
|
||||
const response = await apiClient.get(`/screen-management/tables/${tableName}/columns`);
|
||||
|
||||
@@ -127,8 +127,8 @@ export function CategoryColumnList({
|
||||
|
||||
// category 타입 중 자체 카테고리만 필터링 (참조 컬럼 제외)
|
||||
const categoryColumns = allColumns.filter(
|
||||
(col: any) => (col.inputType === "category" || col.input_type === "category")
|
||||
&& !col.categoryRef && !col.category_ref
|
||||
(col: any) => col.input_type === "category"
|
||||
&& !col.category_ref
|
||||
);
|
||||
|
||||
console.log("✅ 카테고리 컬럼 필터링 완료:", {
|
||||
@@ -139,27 +139,27 @@ export function CategoryColumnList({
|
||||
// 값 개수 조회 (테스트 테이블 사용)
|
||||
const columnsWithCount = await Promise.all(
|
||||
categoryColumns.map(async (col: any) => {
|
||||
const colName = col.columnName || col.column_name;
|
||||
const colLabel = col.columnLabel || col.column_label || colName;
|
||||
const colName = col.column_name;
|
||||
const colLabel = col.column_label || colName;
|
||||
|
||||
let valueCount = 0;
|
||||
let value_count = 0;
|
||||
try {
|
||||
// 테스트 테이블에서 조회
|
||||
const treeResponse = await apiClient.get(`/category-tree/test/${tableName}/${colName}`);
|
||||
if (treeResponse.data.success && treeResponse.data.data) {
|
||||
valueCount = countTreeNodes(treeResponse.data.data);
|
||||
value_count = countTreeNodes(treeResponse.data.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`항목 개수 조회 실패 (${tableName}.${colName}):`, error);
|
||||
}
|
||||
|
||||
return {
|
||||
tableName,
|
||||
tableLabel: tableName,
|
||||
columnName: colName,
|
||||
columnLabel: colLabel,
|
||||
inputType: "category",
|
||||
valueCount,
|
||||
table_name: tableName,
|
||||
table_label: tableName,
|
||||
column_name: colName,
|
||||
column_label: colLabel,
|
||||
input_type: "category",
|
||||
value_count,
|
||||
};
|
||||
}),
|
||||
);
|
||||
@@ -169,7 +169,7 @@ export function CategoryColumnList({
|
||||
|
||||
if (columnsWithCount.length > 0 && !selectedColumn) {
|
||||
const firstCol = columnsWithCount[0];
|
||||
onColumnSelect(`${firstCol.tableName}.${firstCol.columnName}`, firstCol.columnLabel, firstCol.tableName);
|
||||
onColumnSelect(`${firstCol.table_name}.${firstCol.column_name}`, firstCol.column_label, firstCol.table_name);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ 테이블 기반 카테고리 컬럼 조회 실패:", error);
|
||||
@@ -195,9 +195,9 @@ export function CategoryColumnList({
|
||||
setIsLoading(true);
|
||||
try {
|
||||
console.log("🔍 회사 기준 카테고리 컬럼 조회 시작", { menuObjid });
|
||||
|
||||
|
||||
// 회사 기준 카테고리 컬럼 조회 (menuObjid는 선택사항)
|
||||
const url = menuObjid
|
||||
const url = menuObjid
|
||||
? `/table-management/menu/${menuObjid}/category-columns`
|
||||
: `/table-management/category-columns`;
|
||||
const response = await apiClient.get(url);
|
||||
@@ -221,37 +221,37 @@ export function CategoryColumnList({
|
||||
console.log("✅ 카테고리 컬럼 파싱 완료:", {
|
||||
count: categoryColumns.length,
|
||||
columns: categoryColumns.map((c: any) => ({
|
||||
table: c.tableName,
|
||||
column: c.columnName,
|
||||
label: c.columnLabel,
|
||||
table: c.table_name,
|
||||
column: c.column_name,
|
||||
label: c.column_label,
|
||||
})),
|
||||
});
|
||||
|
||||
// 각 컬럼의 값 개수 가져오기
|
||||
const columnsWithCount = await Promise.all(
|
||||
categoryColumns.map(async (col: any) => {
|
||||
const colTable = col.tableName;
|
||||
const colName = col.columnName;
|
||||
const colLabel = col.columnLabel || colName;
|
||||
const colTable = col.table_name;
|
||||
const colName = col.column_name;
|
||||
const colLabel = col.column_label || colName;
|
||||
|
||||
let valueCount = 0;
|
||||
let value_count = 0;
|
||||
try {
|
||||
const valuesResult = await getCategoryValues(colTable, colName, false, menuObjid);
|
||||
const valuesData = (valuesResult as any).data;
|
||||
if (valuesResult.success && valuesData) {
|
||||
valueCount = valuesData.length;
|
||||
value_count = valuesData.length;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`항목 개수 조회 실패 (${colTable}.${colName}):`, error);
|
||||
}
|
||||
|
||||
return {
|
||||
tableName: colTable,
|
||||
tableLabel: col.tableLabel || colTable, // 테이블 라벨 추가
|
||||
columnName: colName,
|
||||
columnLabel: colLabel,
|
||||
inputType: col.inputType,
|
||||
valueCount,
|
||||
table_name: colTable,
|
||||
table_label: col.table_label || colTable,
|
||||
column_name: colName,
|
||||
column_label: colLabel,
|
||||
input_type: col.input_type,
|
||||
value_count,
|
||||
};
|
||||
}),
|
||||
);
|
||||
@@ -268,7 +268,7 @@ export function CategoryColumnList({
|
||||
|
||||
if (columnsWithCount.length > 0 && !selectedColumn) {
|
||||
const firstCol = columnsWithCount[0];
|
||||
onColumnSelect(`${firstCol.tableName}.${firstCol.columnName}`, firstCol.columnLabel, firstCol.tableName);
|
||||
onColumnSelect(`${firstCol.table_name}.${firstCol.column_name}`, firstCol.column_label, firstCol.table_name);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ 카테고리 컬럼 조회 실패:", error);
|
||||
@@ -338,7 +338,7 @@ export function CategoryColumnList({
|
||||
</div>
|
||||
) : null}
|
||||
{groupedColumns.map((group) => {
|
||||
const totalValues = group.columns.reduce((sum, c) => sum + (c.valueCount ?? 0), 0);
|
||||
const totalValues = group.columns.reduce((sum, c) => sum + (c.value_count ?? 0), 0);
|
||||
const isActive = selectedTable === group.tableName;
|
||||
return (
|
||||
<button
|
||||
@@ -407,9 +407,9 @@ export function CategoryColumnList({
|
||||
) : null}
|
||||
{groupedColumns.map((group) => {
|
||||
const isExpanded = expandedGroups.has(group.tableName);
|
||||
const totalValues = group.columns.reduce((sum, c) => sum + (c.valueCount ?? 0), 0);
|
||||
const totalValues = group.columns.reduce((sum, c) => sum + (c.value_count ?? 0), 0);
|
||||
const hasSelectedInGroup = group.columns.some(
|
||||
(c) => selectedColumn === `${c.tableName}.${c.columnName}`,
|
||||
(c) => selectedColumn === `${c.table_name}.${c.column_name}`,
|
||||
);
|
||||
|
||||
// 그룹이 1개뿐이면 드롭다운 없이 바로 표시
|
||||
@@ -417,12 +417,12 @@ export function CategoryColumnList({
|
||||
return (
|
||||
<div key={group.tableName} className="space-y-1.5">
|
||||
{group.columns.map((column) => {
|
||||
const uniqueKey = `${column.tableName}.${column.columnName}`;
|
||||
const uniqueKey = `${column.table_name}.${column.column_name}`;
|
||||
const isSelected = selectedColumn === uniqueKey;
|
||||
return (
|
||||
<div
|
||||
key={uniqueKey}
|
||||
onClick={() => onColumnSelect(uniqueKey, column.columnLabel || column.columnName, column.tableName)}
|
||||
onClick={() => onColumnSelect(uniqueKey, column.column_label || column.column_name, column.table_name)}
|
||||
className={`cursor-pointer rounded-lg border px-4 py-2 transition-all ${
|
||||
isSelected ? "border-primary bg-primary/10 shadow-sm" : "hover:bg-muted/50"
|
||||
}`}
|
||||
@@ -432,11 +432,11 @@ export function CategoryColumnList({
|
||||
className={`h-4 w-4 ${isSelected ? "text-primary" : "text-muted-foreground"}`}
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<h4 className="text-sm font-semibold">{column.columnLabel || column.columnName}</h4>
|
||||
<p className="text-muted-foreground text-xs">{column.tableLabel || column.tableName}</p>
|
||||
<h4 className="text-sm font-semibold">{column.column_label || column.column_name}</h4>
|
||||
<p className="text-muted-foreground text-xs">{column.table_label || column.table_name}</p>
|
||||
</div>
|
||||
<span className="text-muted-foreground text-xs font-medium">
|
||||
{column.valueCount !== undefined ? `${column.valueCount}개` : "..."}
|
||||
{column.value_count !== undefined ? `${column.value_count}개` : "..."}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -483,12 +483,12 @@ export function CategoryColumnList({
|
||||
{isExpanded && (
|
||||
<div className="space-y-1 border-t px-2 py-2">
|
||||
{group.columns.map((column) => {
|
||||
const uniqueKey = `${column.tableName}.${column.columnName}`;
|
||||
const uniqueKey = `${column.table_name}.${column.column_name}`;
|
||||
const isSelected = selectedColumn === uniqueKey;
|
||||
return (
|
||||
<div
|
||||
key={uniqueKey}
|
||||
onClick={() => onColumnSelect(uniqueKey, column.columnLabel || column.columnName, column.tableName)}
|
||||
onClick={() => onColumnSelect(uniqueKey, column.column_label || column.column_name, column.table_name)}
|
||||
className={`cursor-pointer rounded-md px-3 py-1.5 transition-all ${
|
||||
isSelected ? "bg-primary/10 font-semibold text-primary" : "hover:bg-muted/50"
|
||||
}`}
|
||||
@@ -497,9 +497,9 @@ export function CategoryColumnList({
|
||||
<FolderTree
|
||||
className={`h-3.5 w-3.5 ${isSelected ? "text-primary" : "text-muted-foreground"}`}
|
||||
/>
|
||||
<span className="flex-1 text-xs">{column.columnLabel || column.columnName}</span>
|
||||
<span className="flex-1 text-xs">{column.column_label || column.column_name}</span>
|
||||
<span className="text-muted-foreground text-[10px]">
|
||||
{column.valueCount !== undefined ? `${column.valueCount}개` : "..."}
|
||||
{column.value_count !== undefined ? `${column.value_count}개` : "..."}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -237,8 +237,8 @@ export const V2TableListConfigPanel: React.FC<V2TableListConfigPanelProps> = ({
|
||||
|
||||
const [entityDisplayConfigs, setEntityDisplayConfigs] = useState<
|
||||
Record<string, {
|
||||
sourceColumns: Array<{ columnName: string; displayName: string; dataType: string }>;
|
||||
joinColumns: Array<{ columnName: string; displayName: string; dataType: string }>;
|
||||
sourceColumns: Array<{ column_name: string; display_name: string; data_type: string }>;
|
||||
joinColumns: Array<{ column_name: string; display_name: string; data_type: string }>;
|
||||
selectedColumns: string[];
|
||||
separator: string;
|
||||
}>
|
||||
@@ -529,7 +529,7 @@ export const V2TableListConfigPanel: React.FC<V2TableListConfigPanelProps> = ({
|
||||
const sourceResult = await entityJoinApi.getReferenceTableColumns(sourceTable);
|
||||
const sourceColumns = sourceResult.columns || [];
|
||||
|
||||
let joinColumns: Array<{ columnName: string; displayName: string; dataType: string }> = [];
|
||||
let joinColumns: Array<{ column_name: string; display_name: string; data_type: string }> = [];
|
||||
if (joinTable) {
|
||||
try {
|
||||
const joinResult = await entityJoinApi.getReferenceTableColumns(joinTable);
|
||||
@@ -1165,17 +1165,17 @@ export const V2TableListConfigPanel: React.FC<V2TableListConfigPanelProps> = ({
|
||||
>
|
||||
{entityDisplayConfigs[column.columnName].sourceColumns.map((col) => (
|
||||
<CommandItem
|
||||
key={`source-${col.columnName}`}
|
||||
onSelect={() => toggleEntityDisplayColumn(column.columnName, col.columnName)}
|
||||
key={`source-${col.column_name}`}
|
||||
onSelect={() => toggleEntityDisplayColumn(column.columnName, col.column_name)}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
entityDisplayConfigs[column.columnName].selectedColumns.includes(col.columnName) ? "opacity-100" : "opacity-0",
|
||||
entityDisplayConfigs[column.columnName].selectedColumns.includes(col.column_name) ? "opacity-100" : "opacity-0",
|
||||
)}
|
||||
/>
|
||||
{col.displayName}
|
||||
{col.display_name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
@@ -1184,17 +1184,17 @@ export const V2TableListConfigPanel: React.FC<V2TableListConfigPanelProps> = ({
|
||||
<CommandGroup heading={`참조 테이블: ${column.entityDisplayConfig?.joinTable}`}>
|
||||
{entityDisplayConfigs[column.columnName].joinColumns.map((col) => (
|
||||
<CommandItem
|
||||
key={`join-${col.columnName}`}
|
||||
onSelect={() => toggleEntityDisplayColumn(column.columnName, col.columnName)}
|
||||
key={`join-${col.column_name}`}
|
||||
onSelect={() => toggleEntityDisplayColumn(column.columnName, col.column_name)}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
entityDisplayConfigs[column.columnName].selectedColumns.includes(col.columnName) ? "opacity-100" : "opacity-0",
|
||||
entityDisplayConfigs[column.columnName].selectedColumns.includes(col.column_name) ? "opacity-100" : "opacity-0",
|
||||
)}
|
||||
/>
|
||||
{col.displayName}
|
||||
{col.display_name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
|
||||
@@ -15,7 +15,7 @@ export const queryKeys = {
|
||||
tables: {
|
||||
all: ["tables"] as const,
|
||||
columns: (tableName: string) => ["tables", "columns", tableName] as const,
|
||||
codeCategory: (tableName: string, columnName: string) => ["tables", "codeCategory", tableName, columnName] as const,
|
||||
code_category: (tableName: string, columnName: string) => ["tables", "code_category", tableName, columnName] as const,
|
||||
},
|
||||
categories: {
|
||||
all: ["categories"] as const,
|
||||
@@ -26,15 +26,15 @@ export const queryKeys = {
|
||||
// 테이블 컬럼의 코드 카테고리 조회
|
||||
export function useTableCodeCategory(tableName?: string, columnName?: string) {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.tables.codeCategory(tableName || "", columnName || ""),
|
||||
queryKey: queryKeys.tables.code_category(tableName || "", columnName || ""),
|
||||
queryFn: async () => {
|
||||
if (!tableName || !columnName) return null;
|
||||
|
||||
const columns = await tableTypeApi.getColumns(tableName);
|
||||
const targetColumn = columns.find((col) => col.columnName === columnName);
|
||||
const targetColumn = columns.find((col) => col.column_name === columnName);
|
||||
|
||||
const codeCategory =
|
||||
targetColumn?.codeCategory && targetColumn.codeCategory !== "none" ? targetColumn.codeCategory : null;
|
||||
targetColumn?.code_category && targetColumn.code_category !== "none" ? targetColumn.code_category : null;
|
||||
|
||||
return codeCategory;
|
||||
},
|
||||
@@ -46,9 +46,9 @@ export function useTableCodeCategory(tableName?: string, columnName?: string) {
|
||||
|
||||
// 🆕 테이블 컬럼의 계층구조 설정 조회 (대분류/중분류/소분류)
|
||||
interface ColumnHierarchyInfo {
|
||||
hierarchyRole?: "large" | "medium" | "small";
|
||||
hierarchyParentField?: string;
|
||||
codeCategory?: string;
|
||||
hierarchy_role?: "large" | "medium" | "small";
|
||||
hierarchy_parent_field?: string;
|
||||
code_category?: string;
|
||||
}
|
||||
|
||||
export function useTableColumnHierarchy(tableName?: string, columnName?: string) {
|
||||
@@ -58,59 +58,59 @@ export function useTableColumnHierarchy(tableName?: string, columnName?: string)
|
||||
if (!tableName || !columnName) return null;
|
||||
|
||||
const columns = await tableTypeApi.getColumns(tableName);
|
||||
const targetColumn = columns.find((col) => col.columnName === columnName);
|
||||
const targetColumn = columns.find((col) => col.column_name === columnName);
|
||||
|
||||
if (!targetColumn) return null;
|
||||
|
||||
// detailSettings에서 hierarchyRole 추출
|
||||
let hierarchyRole: ColumnHierarchyInfo["hierarchyRole"];
|
||||
let hierarchyParentField: string | undefined;
|
||||
// detail_settings에서 hierarchy_role 추출
|
||||
let hierarchy_role: ColumnHierarchyInfo["hierarchy_role"];
|
||||
let hierarchy_parent_field: string | undefined;
|
||||
|
||||
if (targetColumn.detailSettings) {
|
||||
if (targetColumn.detail_settings) {
|
||||
try {
|
||||
const settings =
|
||||
typeof targetColumn.detailSettings === "string"
|
||||
? JSON.parse(targetColumn.detailSettings)
|
||||
: targetColumn.detailSettings;
|
||||
typeof targetColumn.detail_settings === "string"
|
||||
? JSON.parse(targetColumn.detail_settings)
|
||||
: targetColumn.detail_settings;
|
||||
|
||||
hierarchyRole = settings.hierarchyRole;
|
||||
hierarchyParentField = settings.hierarchyParentField;
|
||||
hierarchy_role = settings.hierarchy_role;
|
||||
hierarchy_parent_field = settings.hierarchy_parent_field;
|
||||
} catch {
|
||||
// JSON 파싱 실패 시 무시
|
||||
}
|
||||
}
|
||||
|
||||
// hierarchyParentField가 없으면 같은 codeCategory를 가진 상위 역할 컬럼을 자동으로 찾음
|
||||
if (hierarchyRole && !hierarchyParentField && targetColumn.codeCategory) {
|
||||
// hierarchy_parent_field가 없으면 같은 code_category를 가진 상위 역할 컬럼을 자동으로 찾음
|
||||
if (hierarchy_role && !hierarchy_parent_field && targetColumn.code_category) {
|
||||
const roleOrder = { large: 0, medium: 1, small: 2 };
|
||||
const currentOrder = roleOrder[hierarchyRole];
|
||||
const currentOrder = roleOrder[hierarchy_role];
|
||||
|
||||
if (currentOrder > 0) {
|
||||
// 같은 codeCategory를 가진 컬럼들 중에서 상위 역할을 찾음
|
||||
// 같은 code_category를 가진 컬럼들 중에서 상위 역할을 찾음
|
||||
const parentRole = currentOrder === 1 ? "large" : "medium";
|
||||
|
||||
const parentColumn = columns.find((col) => {
|
||||
if (col.codeCategory !== targetColumn.codeCategory) return false;
|
||||
if (col.code_category !== targetColumn.code_category) return false;
|
||||
|
||||
try {
|
||||
const colSettings =
|
||||
typeof col.detailSettings === "string" ? JSON.parse(col.detailSettings) : col.detailSettings;
|
||||
return colSettings?.hierarchyRole === parentRole;
|
||||
typeof col.detail_settings === "string" ? JSON.parse(col.detail_settings) : col.detail_settings;
|
||||
return colSettings?.hierarchy_role === parentRole;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (parentColumn) {
|
||||
hierarchyParentField = parentColumn.columnName;
|
||||
hierarchy_parent_field = parentColumn.column_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
hierarchyRole,
|
||||
hierarchyParentField,
|
||||
codeCategory: targetColumn.codeCategory,
|
||||
hierarchy_role,
|
||||
hierarchy_parent_field,
|
||||
code_category: targetColumn.code_category,
|
||||
};
|
||||
},
|
||||
enabled: !!(tableName && columnName),
|
||||
@@ -135,9 +135,8 @@ export function useCodeOptions(codeCategory?: string, enabled: boolean = true, m
|
||||
|
||||
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;
|
||||
const actualValue = code.code || code.CODE || code.value || code.code_value;
|
||||
const actualLabel =
|
||||
code.codeName ||
|
||||
code.code_name ||
|
||||
code.name ||
|
||||
code.CODE_NAME ||
|
||||
@@ -151,7 +150,7 @@ export function useCodeOptions(codeCategory?: string, enabled: boolean = true, m
|
||||
|
||||
// 계층구조 정보 포함
|
||||
const depth = code.depth ?? 1;
|
||||
const parentCodeValue = code.parentCodeValue || code.parent_code_value || null;
|
||||
const parentCodeValue = code.parent_code_value || null;
|
||||
|
||||
return {
|
||||
value: actualValue,
|
||||
@@ -207,10 +206,10 @@ export function useUpdateCode() {
|
||||
return useMutation({
|
||||
mutationFn: ({ categoryCode, codeValue, data }: { categoryCode: string; codeValue: string; data: any }) =>
|
||||
commonCodeApi.codes.update(categoryCode, codeValue, data),
|
||||
onSuccess: (_, variables) => {
|
||||
onSuccess: (_, { categoryCode, codeValue }) => {
|
||||
// 해당 코드 상세 쿼리 무효화
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: queryKeys.codes.detail(variables.categoryCode, variables.codeValue),
|
||||
queryKey: queryKeys.codes.detail(categoryCode, codeValue),
|
||||
});
|
||||
// 해당 카테고리의 모든 코드 관련 쿼리 무효화
|
||||
queryClient.invalidateQueries({
|
||||
@@ -218,7 +217,7 @@ export function useUpdateCode() {
|
||||
});
|
||||
// 무한 스크롤 쿼리도 명시적으로 무효화
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: queryKeys.codes.infiniteList(variables.categoryCode),
|
||||
queryKey: queryKeys.codes.infiniteList(categoryCode),
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -231,17 +230,17 @@ export function useDeleteCode() {
|
||||
return useMutation({
|
||||
mutationFn: ({ categoryCode, codeValue }: { categoryCode: string; codeValue: string }) =>
|
||||
commonCodeApi.codes.delete(categoryCode, codeValue),
|
||||
onSuccess: (_, variables) => {
|
||||
onSuccess: (_, { categoryCode, codeValue }) => {
|
||||
// 해당 코드 관련 쿼리 무효화 및 캐시 제거
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: queryKeys.codes.all,
|
||||
});
|
||||
// 무한 스크롤 쿼리도 명시적으로 무효화
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: queryKeys.codes.infiniteList(variables.categoryCode),
|
||||
queryKey: queryKeys.codes.infiniteList(categoryCode),
|
||||
});
|
||||
queryClient.removeQueries({
|
||||
queryKey: queryKeys.codes.detail(variables.categoryCode, variables.codeValue),
|
||||
queryKey: queryKeys.codes.detail(categoryCode, codeValue),
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -272,7 +271,7 @@ export function useReorderCodes() {
|
||||
|
||||
// 기존 데이터를 복사하고 sort_order만 업데이트
|
||||
const updatedCodes = [...previousCodesArray].map((code: any) => {
|
||||
const newCodeData = codes.find((c) => c.codeValue === code.code_value);
|
||||
const newCodeData = codes.find(({ codeValue }) => codeValue === code.code_value);
|
||||
return newCodeData ? { ...code, sort_order: newCodeData.sortOrder } : code;
|
||||
});
|
||||
|
||||
|
||||
@@ -26,9 +26,10 @@ export interface EntityJoinResponse {
|
||||
}
|
||||
|
||||
export interface ReferenceTableColumn {
|
||||
columnName: string;
|
||||
displayName: string;
|
||||
dataType: string;
|
||||
column_name: string;
|
||||
display_name: string;
|
||||
data_type: string;
|
||||
input_type?: string;
|
||||
}
|
||||
|
||||
export interface CacheStatus {
|
||||
|
||||
@@ -52,9 +52,9 @@ interface ColumnInfo {
|
||||
|
||||
// 참조 테이블 컬럼 정보
|
||||
interface ReferenceColumnInfo {
|
||||
columnName: string;
|
||||
displayName: string;
|
||||
dataType: string;
|
||||
column_name: string;
|
||||
display_name: string;
|
||||
data_type: string;
|
||||
}
|
||||
|
||||
interface ColumnConfigModalProps {
|
||||
@@ -736,26 +736,26 @@ export const ColumnConfigModal: React.FC<ColumnConfigModalProps> = ({
|
||||
<ScrollArea className="max-h-40">
|
||||
<div className="space-y-2 pr-4">
|
||||
{entityInfo.referenceColumns.map((col) => {
|
||||
const isSelected = (editingColumn.entityReference?.displayColumns || []).includes(col.columnName);
|
||||
const isSelected = (editingColumn.entityReference?.displayColumns || []).includes(col.column_name);
|
||||
return (
|
||||
<div
|
||||
key={col.columnName}
|
||||
<div
|
||||
key={col.column_name}
|
||||
className={cn(
|
||||
"flex items-center gap-2 p-2 rounded-md cursor-pointer hover:bg-muted/50 transition-colors",
|
||||
isSelected && "bg-muted"
|
||||
)}
|
||||
onClick={() => toggleEntityDisplayColumn(col.columnName)}
|
||||
onClick={() => toggleEntityDisplayColumn(col.column_name)}
|
||||
>
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
onCheckedChange={() => toggleEntityDisplayColumn(col.columnName)}
|
||||
onCheckedChange={() => toggleEntityDisplayColumn(col.column_name)}
|
||||
/>
|
||||
<div className="flex-1 min-w-0">
|
||||
<span className="text-sm font-medium truncate block">
|
||||
{col.displayName || col.columnName}
|
||||
{col.display_name || col.column_name}
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground truncate block">
|
||||
{col.columnName} ({col.dataType})
|
||||
{col.column_name} ({col.data_type})
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -772,13 +772,13 @@ export const ColumnConfigModal: React.FC<ColumnConfigModalProps> = ({
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{(editingColumn.entityReference?.displayColumns || []).map((colName) => {
|
||||
const colInfo = entityInfo.referenceColumns.find(c => c.columnName === colName);
|
||||
const colInfo = entityInfo.referenceColumns.find(c => c.column_name === colName);
|
||||
return (
|
||||
<span
|
||||
key={colName}
|
||||
className="inline-flex items-center px-2 py-0.5 rounded-full text-xs bg-primary/10 text-primary"
|
||||
>
|
||||
{colInfo?.displayName || colName}
|
||||
{colInfo?.display_name || colName}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -159,8 +159,8 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
Record<
|
||||
string,
|
||||
{
|
||||
sourceColumns: Array<{ columnName: string; displayName: string; dataType: string }>;
|
||||
joinColumns: Array<{ columnName: string; displayName: string; dataType: string }>;
|
||||
sourceColumns: Array<{ column_name: string; display_name: string; data_type: string }>;
|
||||
joinColumns: Array<{ column_name: string; display_name: string; data_type: string }>;
|
||||
selectedColumns: string[];
|
||||
separator: string;
|
||||
}
|
||||
@@ -691,7 +691,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
const sourceColumns = sourceResult.columns || [];
|
||||
|
||||
// joinTable이 있으면 조인 테이블 컬럼도 로드
|
||||
let joinColumns: Array<{ columnName: string; displayName: string; dataType: string }> = [];
|
||||
let joinColumns: Array<{ column_name: string; display_name: string; data_type: string }> = [];
|
||||
if (joinTable) {
|
||||
try {
|
||||
const joinResult = await entityJoinApi.getReferenceTableColumns(joinTable);
|
||||
@@ -1049,21 +1049,21 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
>
|
||||
{entityDisplayConfigs[column.columnName].sourceColumns.map((col) => (
|
||||
<CommandItem
|
||||
key={`source-${col.columnName}`}
|
||||
onSelect={() => toggleEntityDisplayColumn(column.columnName, col.columnName)}
|
||||
key={`source-${col.column_name}`}
|
||||
onSelect={() => toggleEntityDisplayColumn(column.columnName, col.column_name)}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
entityDisplayConfigs[column.columnName].selectedColumns.includes(
|
||||
col.columnName,
|
||||
col.column_name,
|
||||
)
|
||||
? "opacity-100"
|
||||
: "opacity-0",
|
||||
)}
|
||||
/>
|
||||
{col.displayName}
|
||||
{col.display_name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
@@ -1072,21 +1072,21 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
<CommandGroup heading={`참조 테이블: ${column.entityDisplayConfig?.joinTable}`}>
|
||||
{entityDisplayConfigs[column.columnName].joinColumns.map((col) => (
|
||||
<CommandItem
|
||||
key={`join-${col.columnName}`}
|
||||
onSelect={() => toggleEntityDisplayColumn(column.columnName, col.columnName)}
|
||||
key={`join-${col.column_name}`}
|
||||
onSelect={() => toggleEntityDisplayColumn(column.columnName, col.column_name)}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
entityDisplayConfigs[column.columnName].selectedColumns.includes(
|
||||
col.columnName,
|
||||
col.column_name,
|
||||
)
|
||||
? "opacity-100"
|
||||
: "opacity-0",
|
||||
)}
|
||||
/>
|
||||
{col.displayName}
|
||||
{col.display_name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
|
||||
@@ -58,7 +58,7 @@ export function V2CategoryManagerComponent({
|
||||
const handleColumnsLoaded = useCallback((loaded: CategoryColumn[]) => {
|
||||
setColumns(loaded);
|
||||
if (loaded.length > 0) {
|
||||
setSelectedTable((prev) => prev ?? loaded[0].tableName);
|
||||
setSelectedTable((prev) => prev ?? loaded[0].table_name);
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -77,14 +77,14 @@ export function V2CategoryManagerComponent({
|
||||
|
||||
const stats = useMemo(() => {
|
||||
const columnCount = columns.length;
|
||||
const totalValues = columns.reduce((sum, c) => sum + (c.valueCount ?? 0), 0);
|
||||
const tableCount = new Set(columns.map((c) => c.tableName)).size;
|
||||
const totalValues = columns.reduce((sum, c) => sum + (c.value_count ?? 0), 0);
|
||||
const tableCount = new Set(columns.map((c) => c.table_name)).size;
|
||||
const inactiveCount = 0;
|
||||
return { columnCount, totalValues, tableCount, inactiveCount };
|
||||
}, [columns]);
|
||||
|
||||
const columnsForSelectedTable = useMemo(
|
||||
() => (selectedTable ? columns.filter((c) => c.tableName === selectedTable) : []),
|
||||
() => (selectedTable ? columns.filter((c) => c.table_name === selectedTable) : []),
|
||||
[columns, selectedTable],
|
||||
);
|
||||
|
||||
@@ -219,14 +219,14 @@ export function V2CategoryManagerComponent({
|
||||
{/* 칩 바 */}
|
||||
<div className="flex flex-wrap gap-1.5 border-b bg-background px-4 py-3">
|
||||
{columnsForSelectedTable.map((col) => {
|
||||
const uniqueKey = `${col.tableName}.${col.columnName}`;
|
||||
const uniqueKey = `${col.table_name}.${col.column_name}`;
|
||||
const isActive = selectedColumn?.uniqueKey === uniqueKey;
|
||||
return (
|
||||
<button
|
||||
key={uniqueKey}
|
||||
type="button"
|
||||
onClick={() =>
|
||||
handleColumnSelect(uniqueKey, col.columnLabel || col.columnName, col.tableName)
|
||||
handleColumnSelect(uniqueKey, col.column_label || col.column_name, col.table_name)
|
||||
}
|
||||
className={cn(
|
||||
"inline-flex items-center gap-1.5 rounded-full border px-[10px] py-[5px] text-[11px] font-semibold transition-colors",
|
||||
@@ -235,7 +235,7 @@ export function V2CategoryManagerComponent({
|
||||
: "border-border bg-muted/50 hover:border-primary hover:bg-primary/5 hover:text-primary",
|
||||
)}
|
||||
>
|
||||
<span>{col.columnLabel || col.columnName}</span>
|
||||
<span>{col.column_label || col.column_name}</span>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={cn(
|
||||
@@ -243,7 +243,7 @@ export function V2CategoryManagerComponent({
|
||||
isActive ? "bg-primary/15 text-primary" : "bg-muted text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
{col.valueCount ?? 0}
|
||||
{col.value_count ?? 0}
|
||||
</Badge>
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -176,8 +176,8 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
Record<
|
||||
string,
|
||||
{
|
||||
sourceColumns: Array<{ columnName: string; displayName: string; dataType: string }>;
|
||||
joinColumns: Array<{ columnName: string; displayName: string; dataType: string }>;
|
||||
sourceColumns: Array<{ column_name: string; display_name: string; data_type: string }>;
|
||||
joinColumns: Array<{ column_name: string; display_name: string; data_type: string }>;
|
||||
selectedColumns: string[];
|
||||
separator: string;
|
||||
}
|
||||
@@ -711,7 +711,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
const sourceColumns = sourceResult.columns || [];
|
||||
|
||||
// joinTable이 있으면 조인 테이블 컬럼도 로드
|
||||
let joinColumns: Array<{ columnName: string; displayName: string; dataType: string }> = [];
|
||||
let joinColumns: Array<{ column_name: string; display_name: string; data_type: string }> = [];
|
||||
if (joinTable) {
|
||||
try {
|
||||
const joinResult = await entityJoinApi.getReferenceTableColumns(joinTable);
|
||||
@@ -1248,21 +1248,21 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
>
|
||||
{entityDisplayConfigs[column.columnName].sourceColumns.map((col) => (
|
||||
<CommandItem
|
||||
key={`source-${col.columnName}`}
|
||||
onSelect={() => toggleEntityDisplayColumn(column.columnName, col.columnName)}
|
||||
key={`source-${col.column_name}`}
|
||||
onSelect={() => toggleEntityDisplayColumn(column.columnName, col.column_name)}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
entityDisplayConfigs[column.columnName].selectedColumns.includes(
|
||||
col.columnName,
|
||||
col.column_name,
|
||||
)
|
||||
? "opacity-100"
|
||||
: "opacity-0",
|
||||
)}
|
||||
/>
|
||||
{col.displayName}
|
||||
{col.display_name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
@@ -1271,21 +1271,21 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
<CommandGroup heading={`참조 테이블: ${column.entityDisplayConfig?.joinTable}`}>
|
||||
{entityDisplayConfigs[column.columnName].joinColumns.map((col) => (
|
||||
<CommandItem
|
||||
key={`join-${col.columnName}`}
|
||||
onSelect={() => toggleEntityDisplayColumn(column.columnName, col.columnName)}
|
||||
key={`join-${col.column_name}`}
|
||||
onSelect={() => toggleEntityDisplayColumn(column.columnName, col.column_name)}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
entityDisplayConfigs[column.columnName].selectedColumns.includes(
|
||||
col.columnName,
|
||||
col.column_name,
|
||||
)
|
||||
? "opacity-100"
|
||||
: "opacity-0",
|
||||
)}
|
||||
/>
|
||||
{col.displayName}
|
||||
{col.display_name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
|
||||
Reference in New Issue
Block a user