diff --git a/backend-node/src/controllers/tableCategoryValueController.ts b/backend-node/src/controllers/tableCategoryValueController.ts index 75837300..e489bbf2 100644 --- a/backend-node/src/controllers/tableCategoryValueController.ts +++ b/backend-node/src/controllers/tableCategoryValueController.ts @@ -36,20 +36,11 @@ export const getCategoryValues = async (req: Request, res: Response) => { try { const companyCode = req.user!.companyCode; const { tableName, columnName } = req.params; - const menuId = parseInt(req.query.menuId as string, 10); const includeInactive = req.query.includeInactive === "true"; - if (!menuId || isNaN(menuId)) { - return res.status(400).json({ - success: false, - message: "menuId 파라미터가 필요합니다", - }); - } - const values = await tableCategoryValueService.getCategoryValues( tableName, columnName, - menuId, companyCode, includeInactive ); diff --git a/backend-node/src/services/tableCategoryValueService.ts b/backend-node/src/services/tableCategoryValueService.ts index 81be6361..5e91d332 100644 --- a/backend-node/src/services/tableCategoryValueService.ts +++ b/backend-node/src/services/tableCategoryValueService.ts @@ -6,54 +6,6 @@ import { } from "../types/tableCategoryValue"; class TableCategoryValueService { - /** - * 메뉴의 형제 메뉴 ID 목록 조회 - * (같은 부모를 가진 메뉴들) - */ - async getSiblingMenuIds(menuId: number): Promise { - try { - const pool = getPool(); - - // 1. 현재 메뉴의 부모 ID 조회 (menu_info는 objid와 parent_obj_id 사용) - const parentQuery = ` - SELECT parent_obj_id FROM menu_info WHERE objid = $1 - `; - const parentResult = await pool.query(parentQuery, [menuId]); - - if (parentResult.rows.length === 0) { - logger.warn(`메뉴 ID ${menuId}를 찾을 수 없습니다`); - return [menuId]; - } - - const parentId = parentResult.rows[0].parent_obj_id; - - // 최상위 메뉴인 경우 (parent_obj_id가 null 또는 0) - if (!parentId || parentId === 0) { - logger.info(`메뉴 ${menuId}는 최상위 메뉴입니다`); - return [menuId]; - } - - // 2. 같은 부모를 가진 형제 메뉴들 조회 - const siblingsQuery = ` - SELECT objid FROM menu_info WHERE parent_obj_id = $1 - `; - const siblingsResult = await pool.query(siblingsQuery, [parentId]); - - const siblingIds = siblingsResult.rows.map((row) => Number(row.objid)); - - logger.info(`메뉴 ${menuId}의 형제 메뉴 ${siblingIds.length}개 조회`, { - menuId, - parentId, - siblings: siblingIds, - }); - - return siblingIds; - } catch (error: any) { - logger.error(`형제 메뉴 조회 실패: ${error.message}`); - // 에러 시 현재 메뉴만 반환 - return [menuId]; - } - } /** * 테이블의 카테고리 타입 컬럼 목록 조회 */ @@ -98,12 +50,11 @@ class TableCategoryValueService { } /** - * 특정 컬럼의 카테고리 값 목록 조회 (메뉴 스코프 적용) + * 특정 컬럼의 카테고리 값 목록 조회 (테이블 스코프) */ async getCategoryValues( tableName: string, columnName: string, - menuId: number, companyCode: string, includeInactive: boolean = false ): Promise { @@ -111,14 +62,10 @@ class TableCategoryValueService { logger.info("카테고리 값 목록 조회", { tableName, columnName, - menuId, companyCode, includeInactive, }); - // 1. 메뉴 스코프 확인: 형제 메뉴들의 카테고리도 포함 - const siblingMenuIds = await this.getSiblingMenuIds(menuId); - const pool = getPool(); let query = ` SELECT @@ -135,7 +82,6 @@ class TableCategoryValueService { icon, is_active AS "isActive", is_default AS "isDefault", - menu_objid AS "menuId", company_code AS "companyCode", created_at AS "createdAt", updated_at AS "updatedAt", @@ -144,16 +90,10 @@ class TableCategoryValueService { FROM table_column_category_values WHERE table_name = $1 AND column_name = $2 - AND menu_objid = ANY($3) - AND (company_code = $4 OR company_code = '*') + AND (company_code = $3 OR company_code = '*') `; - const params: any[] = [ - tableName, - columnName, - siblingMenuIds, - companyCode, - ]; + const params: any[] = [tableName, columnName, companyCode]; if (!includeInactive) { query += ` AND is_active = true`; @@ -169,8 +109,6 @@ class TableCategoryValueService { logger.info(`카테고리 값 ${result.rows.length}개 조회 완료`, { tableName, columnName, - menuId, - siblingMenuIds, }); return values; @@ -216,8 +154,8 @@ class TableCategoryValueService { INSERT INTO table_column_category_values ( table_name, column_name, value_code, value_label, value_order, parent_value_id, depth, description, color, icon, - is_active, is_default, menu_objid, company_code, created_by - ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) + is_active, is_default, company_code, created_by + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) RETURNING value_id AS "valueId", table_name AS "tableName", @@ -232,7 +170,6 @@ class TableCategoryValueService { icon, is_active AS "isActive", is_default AS "isDefault", - menu_objid AS "menuId", company_code AS "companyCode", created_at AS "createdAt", created_by AS "createdBy" @@ -251,7 +188,6 @@ class TableCategoryValueService { value.icon || null, value.isActive !== false, value.isDefault || false, - value.menuId, // menuId 추가 companyCode, userId, ]); @@ -343,7 +279,6 @@ class TableCategoryValueService { icon, is_active AS "isActive", is_default AS "isDefault", - menu_objid AS "menuId", updated_at AS "updatedAt", updated_by AS "updatedBy" `; diff --git a/backend-node/src/types/tableCategoryValue.ts b/backend-node/src/types/tableCategoryValue.ts index ee1c4c2f..6f0055e7 100644 --- a/backend-node/src/types/tableCategoryValue.ts +++ b/backend-node/src/types/tableCategoryValue.ts @@ -26,9 +26,6 @@ export interface TableCategoryValue { // 하위 항목 (조회 시) children?: TableCategoryValue[]; - // 메뉴 스코프 - menuId: number; - // 멀티테넌시 companyCode?: string; diff --git a/frontend/components/admin/CreateTableModal.tsx b/frontend/components/admin/CreateTableModal.tsx index 85e846c7..b0e6a6e8 100644 --- a/frontend/components/admin/CreateTableModal.tsx +++ b/frontend/components/admin/CreateTableModal.tsx @@ -10,10 +10,10 @@ import { Dialog, DialogContent, DialogHeader, - - - -} from "@/components/ui/resizable-dialog"; + DialogTitle, + DialogDescription, + DialogFooter, +} from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; @@ -321,20 +321,20 @@ export function CreateTableModal({ const isFormValid = !tableNameError && tableName && columns.some((col) => col.name && col.inputType); return ( - - - - + + + + {isDuplicateMode ? "테이블 복제" : "새 테이블 생성"} - - + + {isDuplicateMode ? `${sourceTableName} 테이블을 복제하여 새 테이블을 생성합니다. 테이블명을 입력하고 필요시 컬럼을 수정하세요.` : "최고 관리자만 새로운 테이블을 생성할 수 있습니다. 테이블명과 컬럼 정의를 입력하고 검증 후 생성하세요." } - - + +
{/* 테이블 기본 정보 */} @@ -482,8 +482,8 @@ export function CreateTableModal({ isDuplicateMode ? "복제 생성" : "테이블 생성" )} - - - + + + ); } diff --git a/frontend/components/admin/MenuFormModal.tsx b/frontend/components/admin/MenuFormModal.tsx index b3c14d5f..7c6f6aa5 100644 --- a/frontend/components/admin/MenuFormModal.tsx +++ b/frontend/components/admin/MenuFormModal.tsx @@ -8,7 +8,12 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; -import { ResizableDialog, ResizableDialogContent, ResizableDialogHeader, DialogTitle } from "@/components/ui/resizable-dialog"; +import { + ResizableDialog, + ResizableDialogContent, + ResizableDialogHeader, + ResizableDialogTitle +} from "@/components/ui/resizable-dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { toast } from "sonner"; diff --git a/frontend/components/admin/UserAuthEditModal.tsx b/frontend/components/admin/UserAuthEditModal.tsx index 3fe771bf..34379b64 100644 --- a/frontend/components/admin/UserAuthEditModal.tsx +++ b/frontend/components/admin/UserAuthEditModal.tsx @@ -2,13 +2,13 @@ import React, { useState, useEffect } from "react"; import { - ResizableDialog, - ResizableDialogContent, - ResizableDialogHeader, - ResizableDialogTitle, - ResizableDialogDescription, - ResizableDialogFooter, -} from "@/components/ui/resizable-dialog"; + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter, +} from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; @@ -124,11 +124,11 @@ export function UserAuthEditModal({ isOpen, onClose, onSuccess, user }: UserAuth if (!user) return null; return ( - - - - 사용자 권한 변경 - + + + + 사용자 권한 변경 +
{/* 사용자 정보 */} @@ -211,8 +211,8 @@ export function UserAuthEditModal({ isOpen, onClose, onSuccess, user }: UserAuth > {isLoading ? "처리중..." : showConfirmation ? "확인 및 저장" : "저장"} - - - + + +
); } diff --git a/frontend/components/screen/CopyScreenModal.tsx b/frontend/components/screen/CopyScreenModal.tsx index e04e6a62..add283a9 100644 --- a/frontend/components/screen/CopyScreenModal.tsx +++ b/frontend/components/screen/CopyScreenModal.tsx @@ -4,11 +4,11 @@ import React, { useState, useEffect } from "react"; import { Dialog, DialogContent, - - + DialogTitle, + DialogDescription, DialogHeader, - -} from "@/components/ui/resizable-dialog"; + DialogFooter, +} from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; @@ -104,13 +104,13 @@ export default function CopyScreenModal({ isOpen, onClose, sourceScreen, onCopyS - + 화면 복사 - + {sourceScreen?.screenName} 화면을 복사합니다. 화면 구성도 함께 복사됩니다. - +
@@ -168,7 +168,7 @@ export default function CopyScreenModal({ isOpen, onClose, sourceScreen, onCopyS
- + @@ -185,7 +185,7 @@ export default function CopyScreenModal({ isOpen, onClose, sourceScreen, onCopyS )} - + ); diff --git a/frontend/components/screen/InteractiveDataTable.tsx b/frontend/components/screen/InteractiveDataTable.tsx index f4123169..14d1f5a9 100644 --- a/frontend/components/screen/InteractiveDataTable.tsx +++ b/frontend/components/screen/InteractiveDataTable.tsx @@ -368,18 +368,30 @@ export const InteractiveDataTable: React.FC = ({ // 검색 가능한 컬럼만 필터링 const visibleColumns = component.columns?.filter((col: DataTableColumn) => col.visible) || []; - // 컬럼의 실제 웹 타입 정보 찾기 + // 컬럼의 실제 웹 타입 정보 찾기 (webType 또는 input_type) const getColumnWebType = useCallback( (columnName: string) => { // 먼저 컴포넌트에 설정된 컬럼에서 찾기 (화면 관리에서 설정한 값 우선) const componentColumn = component.columns?.find((col) => col.columnName === columnName); if (componentColumn?.widgetType && componentColumn.widgetType !== "text") { + console.log(`🔍 [${columnName}] componentColumn.widgetType 사용:`, componentColumn.widgetType); return componentColumn.widgetType; } // 없으면 테이블 타입 관리에서 설정된 값 찾기 const tableColumn = tableColumns.find((col) => col.columnName === columnName); - return tableColumn?.webType || "text"; + + // input_type 우선 사용 (category 등) + const inputType = (tableColumn as any)?.input_type || (tableColumn as any)?.inputType; + if (inputType) { + console.log(`✅ [${columnName}] input_type 사용:`, inputType); + return inputType; + } + + // 없으면 webType 사용 + const result = tableColumn?.webType || "text"; + console.log(`✅ [${columnName}] webType 사용:`, result); + return result; }, [component.columns, tableColumns], ); @@ -1414,6 +1426,31 @@ export const InteractiveDataTable: React.FC = ({ ); + case "category": { + // 카테고리 셀렉트 (동적 import) + const { CategorySelectComponent } = require("@/lib/registry/components/category-select/CategorySelectComponent"); + console.log("🎯 카테고리 렌더링 (편집 폼):", { + tableName: component.tableName, + columnName: column.columnName, + columnLabel: column.label, + value, + }); + return ( +
+ handleEditFormChange(column.columnName, newValue)} + placeholder={advancedConfig?.placeholder || `${column.label} 선택...`} + required={isRequired} + className={commonProps.className} + /> + {advancedConfig?.helpText &&

{advancedConfig.helpText}

} +
+ ); + } + default: return (
@@ -1676,6 +1713,31 @@ export const InteractiveDataTable: React.FC = ({
); + case "category": { + // 카테고리 셀렉트 (동적 import) + const { CategorySelectComponent } = require("@/lib/registry/components/category-select/CategorySelectComponent"); + console.log("🎯 카테고리 렌더링 (추가 폼):", { + tableName: component.tableName, + columnName: column.columnName, + columnLabel: column.label, + value, + }); + return ( +
+ handleAddFormChange(column.columnName, newValue)} + placeholder={advancedConfig?.placeholder || `${column.label} 선택...`} + required={isRequired} + className={commonProps.className} + /> + {advancedConfig?.helpText &&

{advancedConfig.helpText}

} +
+ ); + } + default: return (
diff --git a/frontend/components/screen/MenuAssignmentModal.tsx b/frontend/components/screen/MenuAssignmentModal.tsx index 945a4e73..786d6bfe 100644 --- a/frontend/components/screen/MenuAssignmentModal.tsx +++ b/frontend/components/screen/MenuAssignmentModal.tsx @@ -4,11 +4,11 @@ import React, { useState, useEffect, useRef } from "react"; import { Dialog, DialogContent, - - + DialogTitle, + DialogDescription, DialogHeader, - -} from "@/components/ui/resizable-dialog"; + DialogFooter, +} from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; @@ -345,26 +345,26 @@ export const MenuAssignmentModal: React.FC = ({ return ( <> - - + + {assignmentSuccess ? ( // 성공 화면 <> - - + +
{assignmentMessage.includes("나중에") ? "화면 저장 완료" : "화면 할당 완료"} -
- + + {assignmentMessage.includes("나중에") ? "화면이 성공적으로 저장되었습니다. 나중에 메뉴에 할당할 수 있습니다." : "화면이 성공적으로 메뉴에 할당되었습니다."} - -
+ +
@@ -386,7 +386,7 @@ export const MenuAssignmentModal: React.FC = ({
- + - + ) : ( // 기본 할당 화면 <> - - + + 메뉴에 화면 할당 - - + + 저장된 화면을 메뉴에 할당하여 사용자가 접근할 수 있도록 설정합니다. - + {screenInfo && (
@@ -432,7 +432,7 @@ export const MenuAssignmentModal: React.FC = ({ {screenInfo.description &&

{screenInfo.description}

}
)} - +
{/* 메뉴 선택 (검색 기능 포함) */} @@ -572,22 +572,22 @@ export const MenuAssignmentModal: React.FC = ({ )} - + )} - - + +
{/* 화면 교체 확인 대화상자 */} - - - - + + + + 화면 교체 확인 - - 선택한 메뉴에 이미 할당된 화면이 있습니다. - + + 선택한 메뉴에 이미 할당된 화면이 있습니다. +
{/* 기존 화면 목록 */} @@ -652,9 +652,9 @@ export const MenuAssignmentModal: React.FC = ({ )} - - - + + + ); }; diff --git a/frontend/components/screen/ScreenDesigner.tsx b/frontend/components/screen/ScreenDesigner.tsx index d057930f..561d4e9d 100644 --- a/frontend/components/screen/ScreenDesigner.tsx +++ b/frontend/components/screen/ScreenDesigner.tsx @@ -2555,6 +2555,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD componentConfig: { type: componentId, // text-input, number-input 등 webType: column.widgetType, // 원본 웹타입 보존 + inputType: column.inputType, // ✅ input_type 추가 (category 등) ...getDefaultWebTypeConfig(column.widgetType), // 코드 타입인 경우 코드 카테고리 정보 추가 ...(column.widgetType === "code" && @@ -2618,6 +2619,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD componentConfig: { type: componentId, // text-input, number-input 등 webType: column.widgetType, // 원본 웹타입 보존 + inputType: column.inputType, // ✅ input_type 추가 (category 등) ...getDefaultWebTypeConfig(column.widgetType), // 코드 타입인 경우 코드 카테고리 정보 추가 ...(column.widgetType === "code" && diff --git a/frontend/components/screen/ScreenList.tsx b/frontend/components/screen/ScreenList.tsx index 7ce4160d..d5777d3a 100644 --- a/frontend/components/screen/ScreenList.tsx +++ b/frontend/components/screen/ScreenList.tsx @@ -17,15 +17,24 @@ import { AlertDialog, AlertDialogAction, AlertDialogCancel, - AlertResizableDialogContent, - AlertResizableDialogDescription, + AlertDialogContent, + AlertDialogDescription, AlertDialogFooter, - AlertResizableDialogHeader, + AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { Textarea } from "@/components/ui/textarea"; import { Label } from "@/components/ui/label"; -import { ResizableDialog, ResizableDialogContent, ResizableDialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogFooter, + ResizableDialog, + ResizableDialogContent, + ResizableDialogHeader +} from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { MoreHorizontal, Edit, Trash2, Copy, Eye, Plus, Search, Palette, RotateCcw, Trash } from "lucide-react"; import { ScreenDefinition } from "@/types/screen"; diff --git a/frontend/components/screen/widgets/CategoryWidget.tsx b/frontend/components/screen/widgets/CategoryWidget.tsx index 685340b5..099a6558 100644 --- a/frontend/components/screen/widgets/CategoryWidget.tsx +++ b/frontend/components/screen/widgets/CategoryWidget.tsx @@ -6,49 +6,26 @@ import { CategoryValueManager } from "@/components/table-category/CategoryValueM interface CategoryWidgetProps { widgetId: string; - menuId?: number; // 현재 화면의 menuId (선택사항) tableName: string; // 현재 화면의 테이블 - selectedScreen?: any; // 화면 정보 전체 (menuId 추출용) } /** * 카테고리 관리 위젯 (좌우 분할) * - 좌측: 현재 테이블의 카테고리 타입 컬럼 목록 - * - 우측: 선택된 컬럼의 카테고리 값 관리 + * - 우측: 선택된 컬럼의 카테고리 값 관리 (테이블 스코프) */ -export function CategoryWidget({ - widgetId, - menuId: propMenuId, - tableName, - selectedScreen, -}: CategoryWidgetProps) { +export function CategoryWidget({ widgetId, tableName }: CategoryWidgetProps) { const [selectedColumn, setSelectedColumn] = useState<{ columnName: string; columnLabel: string; } | null>(null); - // menuId 추출: props > selectedScreen > 기본값(1) - const menuId = - propMenuId || - selectedScreen?.menuId || - selectedScreen?.menu_id || - 1; // 기본값 - - // menuId가 없으면 경고 메시지 표시 - if (!menuId || menuId === 1) { - console.warn("⚠️ CategoryWidget: menuId가 제공되지 않아 기본값(1)을 사용합니다", { - propMenuId, - selectedScreen, - }); - } - return (
{/* 좌측: 카테고리 컬럼 리스트 (30%) */}
setSelectedColumn({ columnName, columnLabel }) @@ -63,7 +40,6 @@ export function CategoryWidget({ tableName={tableName} columnName={selectedColumn.columnName} columnLabel={selectedColumn.columnLabel} - menuId={menuId} /> ) : (
diff --git a/frontend/components/table-category/CategoryColumnList.tsx b/frontend/components/table-category/CategoryColumnList.tsx index 3cc8cb11..8207479a 100644 --- a/frontend/components/table-category/CategoryColumnList.tsx +++ b/frontend/components/table-category/CategoryColumnList.tsx @@ -12,18 +12,16 @@ interface CategoryColumn { interface CategoryColumnListProps { tableName: string; - menuId: number; selectedColumn: string | null; onColumnSelect: (columnName: string, columnLabel: string) => void; } /** * 카테고리 컬럼 목록 (좌측 패널) - * - 현재 테이블에서 input_type='category'인 컬럼들을 표시 + * - 현재 테이블에서 input_type='category'인 컬럼들을 표시 (테이블 스코프) */ export function CategoryColumnList({ tableName, - menuId, selectedColumn, onColumnSelect, }: CategoryColumnListProps) { @@ -32,7 +30,7 @@ export function CategoryColumnList({ useEffect(() => { loadCategoryColumns(); - }, [tableName, menuId]); + }, [tableName]); const loadCategoryColumns = async () => { setIsLoading(true); diff --git a/frontend/components/table-category/CategoryValueAddDialog.tsx b/frontend/components/table-category/CategoryValueAddDialog.tsx index b511ae7a..99aa02b1 100644 --- a/frontend/components/table-category/CategoryValueAddDialog.tsx +++ b/frontend/components/table-category/CategoryValueAddDialog.tsx @@ -11,9 +11,7 @@ import { } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; -import { Checkbox } from "@/components/ui/checkbox"; import { TableCategoryValue } from "@/types/tableCategoryValue"; interface CategoryValueAddDialogProps { @@ -26,33 +24,48 @@ interface CategoryValueAddDialogProps { export const CategoryValueAddDialog: React.FC< CategoryValueAddDialogProps > = ({ open, onOpenChange, onAdd, columnLabel }) => { - const [valueCode, setValueCode] = useState(""); const [valueLabel, setValueLabel] = useState(""); const [description, setDescription] = useState(""); - const [color, setColor] = useState("#3b82f6"); - const [isDefault, setIsDefault] = useState(false); + + // 라벨에서 코드 자동 생성 + const generateCode = (label: string): string => { + // 한글을 영문으로 변환하거나, 영문/숫자만 추출하여 대문자로 + const cleaned = label + .replace(/[^a-zA-Z0-9가-힣\s]/g, "") // 특수문자 제거 + .trim() + .toUpperCase(); + + // 영문이 있으면 영문만, 없으면 타임스탬프 기반 + const englishOnly = cleaned.replace(/[^A-Z0-9\s]/g, "").replace(/\s+/g, "_"); + + if (englishOnly.length > 0) { + return englishOnly.substring(0, 20); // 최대 20자 + } + + // 영문이 없으면 CATEGORY_TIMESTAMP 형식 + return `CATEGORY_${Date.now().toString().slice(-6)}`; + }; const handleSubmit = () => { - if (!valueCode || !valueLabel) { + if (!valueLabel.trim()) { return; } + const valueCode = generateCode(valueLabel); + onAdd({ tableName: "", columnName: "", - valueCode: valueCode.toUpperCase(), - valueLabel, - description, - color, - isDefault, + valueCode, + valueLabel: valueLabel.trim(), + description: description.trim(), + color: "#3b82f6", + isDefault: false, }); // 초기화 - setValueCode(""); setValueLabel(""); setDescription(""); - setColor("#3b82f6"); - setIsDefault(false); }; return ( @@ -68,83 +81,23 @@ export const CategoryValueAddDialog: React.FC<
-
- - setValueCode(e.target.value.toUpperCase())} - className="h-8 text-xs sm:h-10 sm:text-sm" - /> -

- 영문 대문자와 언더스코어만 사용 (DB 저장값) -

-
+ setValueLabel(e.target.value)} + className="h-8 text-xs sm:h-10 sm:text-sm" + autoFocus + /> -
- - setValueLabel(e.target.value)} - className="h-8 text-xs sm:h-10 sm:text-sm" - /> -

- 사용자에게 표시될 이름 -

-
- -
- -