refactor: Update category value handling to make menuObjid optional

- Modified the addCategoryValue function to allow menuObjid to be optional, accommodating scenarios where it may not be provided, such as in global management screens.
- Adjusted related service and controller logic to handle the absence of menuObjid gracefully, ensuring that the application remains robust and user-friendly.
- Enhanced the frontend components to reflect these changes, improving the overall user experience when adding category values across multiple companies.
This commit is contained in:
kjs
2026-04-10 14:17:35 +09:00
parent 5842a91c7f
commit b8860e56e5
5 changed files with 47 additions and 53 deletions
@@ -116,12 +116,7 @@ export const addCategoryValue = async (req: AuthenticatedRequest, res: Response)
const userId = req.user!.userId;
const { menuObjid, ...value } = req.body;
if (!menuObjid) {
return res.status(400).json({
success: false,
message: "menuObjid는 필수입니다",
});
}
// menuObjid는 선택사항 — 옵션설정 등 전역 관리 화면에서는 없을 수 있음
logger.info("카테고리 값 추가 요청", {
tableName: value.tableName,
@@ -134,7 +129,7 @@ export const addCategoryValue = async (req: AuthenticatedRequest, res: Response)
value,
companyCode,
userId,
Number(menuObjid) // ← menuObjid 전달
menuObjid ? Number(menuObjid) : null
);
return res.status(201).json({
@@ -269,7 +269,7 @@ class TableCategoryValueService {
value: TableCategoryValue,
companyCode: string,
userId: string,
menuObjid: number
menuObjid: number | null
): Promise<TableCategoryValue> {
const pool = getPool();
@@ -286,29 +286,35 @@ class TableCategoryValueService {
let duplicateQuery: string;
let duplicateParams: any[];
const menuCondition = menuObjid
? "AND menu_objid = $4"
: "AND menu_objid IS NULL";
const baseParams = menuObjid
? [value.tableName, value.columnName, value.valueCode, menuObjid]
: [value.tableName, value.columnName, value.valueCode];
if (companyCode === "*") {
// 최고 관리자: 모든 회사에서 중복 체크
duplicateQuery = `
SELECT value_id
FROM category_values
WHERE table_name = $1
AND column_name = $2
AND value_code = $3
AND menu_objid = $4
${menuCondition}
`;
duplicateParams = [value.tableName, value.columnName, value.valueCode, menuObjid];
duplicateParams = baseParams;
} else {
// 일반 회사: 자신의 회사에서만 중복 체크
const companyIdx = menuObjid ? "$5" : "$4";
duplicateQuery = `
SELECT value_id
FROM category_values
WHERE table_name = $1
AND column_name = $2
AND value_code = $3
AND menu_objid = $4
AND company_code = $5
${menuCondition}
AND company_code = ${companyIdx}
`;
duplicateParams = [value.tableName, value.columnName, value.valueCode, menuObjid, companyCode];
duplicateParams = [...baseParams, companyCode];
}
const duplicateResult = await pool.query(duplicateQuery, duplicateParams);
@@ -352,10 +358,10 @@ class TableCategoryValueService {
const insertQuery = `
INSERT INTO category_values (
table_name, column_name, value_code, value_label, value_order,
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_by
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
) VALUES ((SELECT COALESCE(MAX(value_id), 0) + 1 FROM category_values), $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
RETURNING
value_id AS "valueId",
table_name AS "tableName",
@@ -372,33 +372,35 @@ export function CategoryColumnList({
}
return (
<div className="space-y-3">
<div className="space-y-1">
<div className="flex h-full flex-col">
<div className="shrink-0 space-y-1 px-3 pt-3">
<h3 className="text-lg font-semibold"> </h3>
<p className="text-muted-foreground text-xs"> </p>
</div>
<div className="relative">
<Search className="text-muted-foreground absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2" />
<Input
type="text"
placeholder="컬럼 검색..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="h-8 pl-8 pr-8 text-xs"
/>
{searchQuery && (
<button
type="button"
onClick={() => setSearchQuery("")}
className="text-muted-foreground hover:text-foreground absolute right-2 top-1/2 -translate-y-1/2"
>
<X className="h-4 w-4" />
</button>
)}
<div className="shrink-0 px-3 pt-3">
<div className="relative">
<Search className="text-muted-foreground absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2" />
<Input
type="text"
placeholder="컬럼 검색..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="h-8 pl-8 pr-8 text-xs"
/>
{searchQuery && (
<button
type="button"
onClick={() => setSearchQuery("")}
className="text-muted-foreground hover:text-foreground absolute right-2 top-1/2 -translate-y-1/2"
>
<X className="h-4 w-4" />
</button>
)}
</div>
</div>
<div className="space-y-1">
<div className="flex-1 min-h-0 overflow-y-auto space-y-1 px-3 py-3">
{filteredColumns.length === 0 && searchQuery ? (
<div className="text-muted-foreground py-4 text-center text-xs">
&apos;{searchQuery}&apos;
@@ -106,22 +106,13 @@ export const CategoryValueManager: React.FC<CategoryValueManagerProps> = ({
const handleAddValue = async (newValue: TableCategoryValue) => {
try {
if (!menuObjid) {
toast({
title: "오류",
description: "메뉴 정보가 없습니다. 카테고리 값을 추가할 수 없습니다.",
variant: "destructive",
});
return;
}
const response = await addCategoryValue(
{
...newValue,
tableName,
columnName,
},
menuObjid
menuObjid || 0
);
if (response.success && response.data) {
+2 -2
View File
@@ -80,7 +80,7 @@ export async function getCategoryValues(
*/
export async function addCategoryValue(
value: TableCategoryValue,
menuObjid: number
menuObjid?: number
) {
try {
const response = await apiClient.post<{
@@ -88,7 +88,7 @@ export async function addCategoryValue(
data: TableCategoryValue;
}>("/table-categories/values", {
...value,
menuObjid, // ← menuObjid 포함
menuObjid: menuObjid || undefined,
});
return response.data;
} catch (error: any) {