feat: Enhance screen group deletion functionality with optional numbering rules deletion
- Added a new query parameter `deleteNumberingRules` to the `deleteScreenGroup` function, allowing users to specify if numbering rules should be deleted when a root screen group is removed. - Updated the `deleteScreenGroup` controller to handle the deletion of numbering rules conditionally based on the new parameter. - Enhanced the frontend `ScreenGroupTreeView` component to include a checkbox for users to confirm the deletion of numbering rules when deleting a root group, improving user control and clarity during deletion operations. - Implemented appropriate warnings and messages to inform users about the implications of deleting numbering rules, ensuring better user experience and data integrity awareness.
This commit is contained in:
parent
93d9df3e5a
commit
f97edad1ea
|
|
@ -308,6 +308,7 @@ export const deleteScreenGroup = async (req: AuthenticatedRequest, res: Response
|
||||||
const client = await pool.connect();
|
const client = await pool.connect();
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
const deleteNumberingRules = req.query.deleteNumberingRules === "true";
|
||||||
const companyCode = req.user?.companyCode || "*";
|
const companyCode = req.user?.companyCode || "*";
|
||||||
|
|
||||||
await client.query('BEGIN');
|
await client.query('BEGIN');
|
||||||
|
|
@ -380,31 +381,29 @@ export const deleteScreenGroup = async (req: AuthenticatedRequest, res: Response
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2-4. 해당 회사의 채번 규칙 삭제 (최상위 그룹 삭제 시)
|
// 2-4. 해당 회사의 채번 규칙 삭제 (최상위 그룹 삭제 + 사용자가 명시적으로 요청한 경우에만)
|
||||||
// 삭제되는 그룹이 최상위인지 확인
|
if (deleteNumberingRules) {
|
||||||
const isRootGroup = await client.query(
|
const isRootGroup = await client.query(
|
||||||
`SELECT 1 FROM screen_groups WHERE id = $1 AND parent_group_id IS NULL`,
|
`SELECT 1 FROM screen_groups WHERE id = $1 AND parent_group_id IS NULL`,
|
||||||
[id]
|
[id]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isRootGroup.rows.length > 0) {
|
if (isRootGroup.rows.length > 0) {
|
||||||
// 최상위 그룹 삭제 시 해당 회사의 채번 규칙도 삭제
|
await client.query(
|
||||||
// 먼저 파트 삭제
|
`DELETE FROM numbering_rule_parts
|
||||||
await client.query(
|
WHERE rule_id IN (SELECT rule_id FROM numbering_rules WHERE company_code = $1)`,
|
||||||
`DELETE FROM numbering_rule_parts
|
[targetCompanyCode]
|
||||||
WHERE rule_id IN (SELECT rule_id FROM numbering_rules WHERE company_code = $1)`,
|
);
|
||||||
[targetCompanyCode]
|
const deletedRules = await client.query(
|
||||||
);
|
`DELETE FROM numbering_rules WHERE company_code = $1 RETURNING rule_id`,
|
||||||
// 규칙 삭제
|
[targetCompanyCode]
|
||||||
const deletedRules = await client.query(
|
);
|
||||||
`DELETE FROM numbering_rules WHERE company_code = $1 RETURNING rule_id`,
|
if (deletedRules.rowCount && deletedRules.rowCount > 0) {
|
||||||
[targetCompanyCode]
|
logger.warn("최상위 그룹 삭제 시 채번 규칙 삭제 (사용자 명시 요청)", {
|
||||||
);
|
companyCode: targetCompanyCode,
|
||||||
if (deletedRules.rowCount && deletedRules.rowCount > 0) {
|
deletedCount: deletedRules.rowCount
|
||||||
logger.info("그룹 삭제 시 채번 규칙 삭제", {
|
});
|
||||||
companyCode: targetCompanyCode,
|
}
|
||||||
deletedCount: deletedRules.rowCount
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -513,6 +513,15 @@ export class TableManagementService {
|
||||||
detailSettingsStr = JSON.stringify(settings.detailSettings);
|
detailSettingsStr = JSON.stringify(settings.detailSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 입력타입에 해당하지 않는 설정값은 NULL로 강제 초기화
|
||||||
|
const inputType = settings.inputType;
|
||||||
|
const referenceTable = inputType === "entity" ? (settings.referenceTable || null) : null;
|
||||||
|
const referenceColumn = inputType === "entity" ? (settings.referenceColumn || null) : null;
|
||||||
|
const displayColumn = inputType === "entity" ? (settings.displayColumn || null) : null;
|
||||||
|
const codeCategory = inputType === "code" ? (settings.codeCategory || null) : null;
|
||||||
|
const codeValue = inputType === "code" ? (settings.codeValue || null) : null;
|
||||||
|
const categoryRef = inputType === "category" ? (settings.categoryRef || null) : null;
|
||||||
|
|
||||||
await query(
|
await query(
|
||||||
`INSERT INTO table_type_columns (
|
`INSERT INTO table_type_columns (
|
||||||
table_name, column_name, column_label, input_type, detail_settings,
|
table_name, column_name, column_label, input_type, detail_settings,
|
||||||
|
|
@ -525,11 +534,11 @@ export class TableManagementService {
|
||||||
column_label = COALESCE(EXCLUDED.column_label, table_type_columns.column_label),
|
column_label = COALESCE(EXCLUDED.column_label, table_type_columns.column_label),
|
||||||
input_type = COALESCE(EXCLUDED.input_type, table_type_columns.input_type),
|
input_type = COALESCE(EXCLUDED.input_type, table_type_columns.input_type),
|
||||||
detail_settings = COALESCE(EXCLUDED.detail_settings, table_type_columns.detail_settings),
|
detail_settings = COALESCE(EXCLUDED.detail_settings, table_type_columns.detail_settings),
|
||||||
code_category = COALESCE(EXCLUDED.code_category, table_type_columns.code_category),
|
code_category = EXCLUDED.code_category,
|
||||||
code_value = COALESCE(EXCLUDED.code_value, table_type_columns.code_value),
|
code_value = EXCLUDED.code_value,
|
||||||
reference_table = COALESCE(EXCLUDED.reference_table, table_type_columns.reference_table),
|
reference_table = EXCLUDED.reference_table,
|
||||||
reference_column = COALESCE(EXCLUDED.reference_column, table_type_columns.reference_column),
|
reference_column = EXCLUDED.reference_column,
|
||||||
display_column = COALESCE(EXCLUDED.display_column, table_type_columns.display_column),
|
display_column = EXCLUDED.display_column,
|
||||||
display_order = COALESCE(EXCLUDED.display_order, table_type_columns.display_order),
|
display_order = COALESCE(EXCLUDED.display_order, table_type_columns.display_order),
|
||||||
is_visible = COALESCE(EXCLUDED.is_visible, table_type_columns.is_visible),
|
is_visible = COALESCE(EXCLUDED.is_visible, table_type_columns.is_visible),
|
||||||
category_ref = EXCLUDED.category_ref,
|
category_ref = EXCLUDED.category_ref,
|
||||||
|
|
@ -538,17 +547,17 @@ export class TableManagementService {
|
||||||
tableName,
|
tableName,
|
||||||
columnName,
|
columnName,
|
||||||
settings.columnLabel,
|
settings.columnLabel,
|
||||||
settings.inputType,
|
inputType,
|
||||||
detailSettingsStr,
|
detailSettingsStr,
|
||||||
settings.codeCategory,
|
codeCategory,
|
||||||
settings.codeValue,
|
codeValue,
|
||||||
settings.referenceTable,
|
referenceTable,
|
||||||
settings.referenceColumn,
|
referenceColumn,
|
||||||
settings.displayColumn,
|
displayColumn,
|
||||||
settings.displayOrder || 0,
|
settings.displayOrder || 0,
|
||||||
settings.isVisible !== undefined ? settings.isVisible : true,
|
settings.isVisible !== undefined ? settings.isVisible : true,
|
||||||
companyCode,
|
companyCode,
|
||||||
settings.categoryRef || null,
|
categoryRef,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -849,7 +858,11 @@ export class TableManagementService {
|
||||||
...detailSettings,
|
...detailSettings,
|
||||||
};
|
};
|
||||||
|
|
||||||
// table_type_columns 테이블에서 업데이트 (company_code 추가)
|
// 입력타입 변경 시 이전 타입의 설정값 초기화
|
||||||
|
const clearEntity = finalInputType !== "entity";
|
||||||
|
const clearCode = finalInputType !== "code";
|
||||||
|
const clearCategory = finalInputType !== "category";
|
||||||
|
|
||||||
await query(
|
await query(
|
||||||
`INSERT INTO table_type_columns (
|
`INSERT INTO table_type_columns (
|
||||||
table_name, column_name, input_type, detail_settings,
|
table_name, column_name, input_type, detail_settings,
|
||||||
|
|
@ -859,6 +872,12 @@ export class TableManagementService {
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
input_type = EXCLUDED.input_type,
|
input_type = EXCLUDED.input_type,
|
||||||
detail_settings = EXCLUDED.detail_settings,
|
detail_settings = EXCLUDED.detail_settings,
|
||||||
|
reference_table = CASE WHEN $6 THEN NULL ELSE table_type_columns.reference_table END,
|
||||||
|
reference_column = CASE WHEN $6 THEN NULL ELSE table_type_columns.reference_column END,
|
||||||
|
display_column = CASE WHEN $6 THEN NULL ELSE table_type_columns.display_column END,
|
||||||
|
code_category = CASE WHEN $7 THEN NULL ELSE table_type_columns.code_category END,
|
||||||
|
code_value = CASE WHEN $7 THEN NULL ELSE table_type_columns.code_value END,
|
||||||
|
category_ref = CASE WHEN $8 THEN NULL ELSE table_type_columns.category_ref END,
|
||||||
updated_date = now()`,
|
updated_date = now()`,
|
||||||
[
|
[
|
||||||
tableName,
|
tableName,
|
||||||
|
|
@ -866,6 +885,9 @@ export class TableManagementService {
|
||||||
finalInputType,
|
finalInputType,
|
||||||
JSON.stringify(finalDetailSettings),
|
JSON.stringify(finalDetailSettings),
|
||||||
companyCode,
|
companyCode,
|
||||||
|
clearEntity,
|
||||||
|
clearCode,
|
||||||
|
clearCategory,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -453,18 +453,39 @@ export default function TableManagementPage() {
|
||||||
[loadColumnTypes, loadConstraints, pageSize, tables],
|
[loadColumnTypes, loadConstraints, pageSize, tables],
|
||||||
);
|
);
|
||||||
|
|
||||||
// 입력 타입 변경
|
// 입력 타입 변경 - 이전 타입의 설정값 초기화 포함
|
||||||
const handleInputTypeChange = useCallback(
|
const handleInputTypeChange = useCallback(
|
||||||
(columnName: string, newInputType: string) => {
|
(columnName: string, newInputType: string) => {
|
||||||
setColumns((prev) =>
|
setColumns((prev) =>
|
||||||
prev.map((col) => {
|
prev.map((col) => {
|
||||||
if (col.columnName === columnName) {
|
if (col.columnName === columnName) {
|
||||||
const inputTypeOption = memoizedInputTypeOptions.find((option) => option.value === newInputType);
|
const inputTypeOption = memoizedInputTypeOptions.find((option) => option.value === newInputType);
|
||||||
return {
|
const updated: typeof col = {
|
||||||
...col,
|
...col,
|
||||||
inputType: newInputType,
|
inputType: newInputType,
|
||||||
detailSettings: inputTypeOption?.description || col.detailSettings,
|
detailSettings: inputTypeOption?.description || col.detailSettings,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 엔티티가 아닌 타입으로 변경 시 참조 설정 초기화
|
||||||
|
if (newInputType !== "entity") {
|
||||||
|
updated.referenceTable = undefined;
|
||||||
|
updated.referenceColumn = undefined;
|
||||||
|
updated.displayColumn = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 코드가 아닌 타입으로 변경 시 코드 설정 초기화
|
||||||
|
if (newInputType !== "code") {
|
||||||
|
updated.codeCategory = undefined;
|
||||||
|
updated.codeValue = undefined;
|
||||||
|
updated.hierarchyRole = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 카테고리가 아닌 타입으로 변경 시 카테고리 참조 초기화
|
||||||
|
if (newInputType !== "category") {
|
||||||
|
updated.categoryRef = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return updated;
|
||||||
}
|
}
|
||||||
return col;
|
return col;
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,7 @@ export function ScreenGroupTreeView({
|
||||||
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
|
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
|
||||||
const [deletingGroup, setDeletingGroup] = useState<ScreenGroup | null>(null);
|
const [deletingGroup, setDeletingGroup] = useState<ScreenGroup | null>(null);
|
||||||
const [deleteScreensWithGroup, setDeleteScreensWithGroup] = useState(false); // 화면도 함께 삭제 체크박스
|
const [deleteScreensWithGroup, setDeleteScreensWithGroup] = useState(false); // 화면도 함께 삭제 체크박스
|
||||||
|
const [deleteNumberingRules, setDeleteNumberingRules] = useState(false); // 채번 규칙도 함께 삭제 체크박스
|
||||||
const [isDeleting, setIsDeleting] = useState(false); // 삭제 진행 중 상태
|
const [isDeleting, setIsDeleting] = useState(false); // 삭제 진행 중 상태
|
||||||
const [deleteProgress, setDeleteProgress] = useState({ current: 0, total: 0, message: "" }); // 삭제 진행 상태
|
const [deleteProgress, setDeleteProgress] = useState({ current: 0, total: 0, message: "" }); // 삭제 진행 상태
|
||||||
|
|
||||||
|
|
@ -439,7 +440,8 @@ export function ScreenGroupTreeView({
|
||||||
const handleDeleteGroup = (group: ScreenGroup, e?: React.MouseEvent) => {
|
const handleDeleteGroup = (group: ScreenGroup, e?: React.MouseEvent) => {
|
||||||
e?.stopPropagation();
|
e?.stopPropagation();
|
||||||
setDeletingGroup(group);
|
setDeletingGroup(group);
|
||||||
setDeleteScreensWithGroup(false); // 기본값: 화면 삭제 안함
|
setDeleteScreensWithGroup(false);
|
||||||
|
setDeleteNumberingRules(false);
|
||||||
setIsDeleteDialogOpen(true);
|
setIsDeleteDialogOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -572,11 +574,17 @@ export function ScreenGroupTreeView({
|
||||||
// 최종적으로 대상 그룹 삭제
|
// 최종적으로 대상 그룹 삭제
|
||||||
currentStep++;
|
currentStep++;
|
||||||
setDeleteProgress({ current: currentStep, total: totalSteps, message: "그룹 삭제 완료 중..." });
|
setDeleteProgress({ current: currentStep, total: totalSteps, message: "그룹 삭제 완료 중..." });
|
||||||
const response = await deleteScreenGroup(deletingGroup.id);
|
const isRootGroup = !deletingGroup.parent_group_id;
|
||||||
|
const response = await deleteScreenGroup(deletingGroup.id, {
|
||||||
|
deleteNumberingRules: isRootGroup && deleteNumberingRules,
|
||||||
|
});
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
|
const messages = [];
|
||||||
|
if (deleteScreensWithGroup) messages.push(`화면 ${totalScreensToDelete}개`);
|
||||||
|
if (isRootGroup && deleteNumberingRules) messages.push("채번 규칙");
|
||||||
toast.success(
|
toast.success(
|
||||||
deleteScreensWithGroup
|
messages.length > 0
|
||||||
? `그룹과 화면 ${totalScreensToDelete}개가 삭제되었습니다`
|
? `그룹과 ${messages.join(", ")}이(가) 삭제되었습니다`
|
||||||
: "그룹이 삭제되었습니다"
|
: "그룹이 삭제되었습니다"
|
||||||
);
|
);
|
||||||
await loadGroupsData();
|
await loadGroupsData();
|
||||||
|
|
@ -593,6 +601,7 @@ export function ScreenGroupTreeView({
|
||||||
setIsDeleteDialogOpen(false);
|
setIsDeleteDialogOpen(false);
|
||||||
setDeletingGroup(null);
|
setDeletingGroup(null);
|
||||||
setDeleteScreensWithGroup(false);
|
setDeleteScreensWithGroup(false);
|
||||||
|
setDeleteNumberingRules(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1479,7 +1488,7 @@ export function ScreenGroupTreeView({
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-2 text-destructive/80">
|
<p className="mt-2 text-destructive/80">
|
||||||
{deleteScreensWithGroup
|
{deleteScreensWithGroup
|
||||||
? "⚠️ 그룹에 속한 모든 화면, 플로우, 관련 데이터가 영구적으로 삭제됩니다. 이 작업은 되돌릴 수 없습니다."
|
? "그룹에 속한 모든 화면, 플로우, 관련 데이터가 영구적으로 삭제됩니다. 이 작업은 되돌릴 수 없습니다."
|
||||||
: "그룹에 속한 화면들은 미분류로 이동됩니다."
|
: "그룹에 속한 화면들은 미분류로 이동됩니다."
|
||||||
}
|
}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -1521,6 +1530,43 @@ export function ScreenGroupTreeView({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* 최상위 그룹일 때 채번 삭제 경고 */}
|
||||||
|
{deletingGroup && !deletingGroup.parent_group_id && (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="rounded-md border-2 border-destructive bg-destructive/5 p-4">
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<AlertTriangle className="mt-0.5 h-6 w-6 shrink-0 text-destructive" />
|
||||||
|
<div className="space-y-2">
|
||||||
|
<p className="text-sm font-bold text-destructive">
|
||||||
|
최상위 그룹 삭제 - 채번 규칙 경고
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-destructive/90 leading-relaxed">
|
||||||
|
이 그룹은 최상위 그룹입니다.
|
||||||
|
아래 체크박스를 선택하면 해당 회사의 <span className="font-bold underline">모든 채번 규칙과 채번 파트가 영구적으로 삭제</span>됩니다.
|
||||||
|
삭제된 채번 데이터는 복구할 수 없으며, 채번이 필요한 모든 기능이 중단됩니다.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center space-x-2 rounded-md border border-destructive/30 bg-destructive/5 p-3">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="deleteNumberingRules"
|
||||||
|
checked={deleteNumberingRules}
|
||||||
|
onChange={(e) => setDeleteNumberingRules(e.target.checked)}
|
||||||
|
className="h-4 w-4 rounded border-destructive text-destructive focus:ring-destructive"
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="deleteNumberingRules"
|
||||||
|
className="cursor-pointer text-sm font-semibold text-destructive"
|
||||||
|
>
|
||||||
|
채번 규칙도 함께 삭제 (위험)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 로딩 오버레이 */}
|
{/* 로딩 오버레이 */}
|
||||||
{isDeleting && (
|
{isDeleting && (
|
||||||
<div className="absolute inset-0 z-50 flex flex-col items-center justify-center rounded-lg bg-background/90 backdrop-blur-sm">
|
<div className="absolute inset-0 z-50 flex flex-col items-center justify-center rounded-lg bg-background/90 backdrop-blur-sm">
|
||||||
|
|
@ -1551,7 +1597,7 @@ export function ScreenGroupTreeView({
|
||||||
</AlertDialogCancel>
|
</AlertDialogCancel>
|
||||||
<AlertDialogAction
|
<AlertDialogAction
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault(); // 자동 닫힘 방지
|
e.preventDefault();
|
||||||
confirmDeleteGroup();
|
confirmDeleteGroup();
|
||||||
}}
|
}}
|
||||||
disabled={isDeleting}
|
disabled={isDeleting}
|
||||||
|
|
|
||||||
|
|
@ -156,9 +156,15 @@ export async function updateScreenGroup(id: number, data: Partial<ScreenGroup>):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteScreenGroup(id: number): Promise<ApiResponse<void>> {
|
export async function deleteScreenGroup(id: number, options?: { deleteNumberingRules?: boolean }): Promise<ApiResponse<void>> {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.delete(`/screen-groups/groups/${id}`);
|
const params = new URLSearchParams();
|
||||||
|
if (options?.deleteNumberingRules) {
|
||||||
|
params.set("deleteNumberingRules", "true");
|
||||||
|
}
|
||||||
|
const queryString = params.toString();
|
||||||
|
const url = `/screen-groups/groups/${id}${queryString ? `?${queryString}` : ""}`;
|
||||||
|
const response = await apiClient.delete(url);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
return { success: false, error: error.message };
|
return { success: false, error: error.message };
|
||||||
|
|
|
||||||
|
|
@ -649,11 +649,12 @@ export const SplitPanelLayout2Component: React.FC<SplitPanelLayout2ComponentProp
|
||||||
title: config.rightPanel?.addButtonLabel || "추가",
|
title: config.rightPanel?.addButtonLabel || "추가",
|
||||||
modalSize: "lg",
|
modalSize: "lg",
|
||||||
editData: initialData,
|
editData: initialData,
|
||||||
isCreateMode: true, // 생성 모드
|
isCreateMode: true,
|
||||||
onSave: () => {
|
onSave: () => {
|
||||||
if (selectedLeftItem) {
|
if (selectedLeftItem) {
|
||||||
loadRightData(selectedLeftItem);
|
loadRightData(selectedLeftItem);
|
||||||
}
|
}
|
||||||
|
loadLeftData();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -664,6 +665,7 @@ export const SplitPanelLayout2Component: React.FC<SplitPanelLayout2ComponentProp
|
||||||
config.dataTransferFields,
|
config.dataTransferFields,
|
||||||
selectedLeftItem,
|
selectedLeftItem,
|
||||||
loadRightData,
|
loadRightData,
|
||||||
|
loadLeftData,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 기본키 컬럼명 가져오기 (우측 패널)
|
// 기본키 컬럼명 가져오기 (우측 패널)
|
||||||
|
|
@ -722,19 +724,20 @@ export const SplitPanelLayout2Component: React.FC<SplitPanelLayout2ComponentProp
|
||||||
screenId: modalScreenId,
|
screenId: modalScreenId,
|
||||||
title: "수정",
|
title: "수정",
|
||||||
modalSize: "lg",
|
modalSize: "lg",
|
||||||
editData: editData, // 병합된 데이터 전달
|
editData: editData,
|
||||||
isCreateMode: false, // 수정 모드
|
isCreateMode: false,
|
||||||
onSave: () => {
|
onSave: () => {
|
||||||
if (selectedLeftItem) {
|
if (selectedLeftItem) {
|
||||||
loadRightData(selectedLeftItem);
|
loadRightData(selectedLeftItem);
|
||||||
}
|
}
|
||||||
|
loadLeftData();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
window.dispatchEvent(event);
|
window.dispatchEvent(event);
|
||||||
console.log("[SplitPanelLayout2] 우측 수정 모달 열기:", editData);
|
console.log("[SplitPanelLayout2] 우측 수정 모달 열기:", editData);
|
||||||
},
|
},
|
||||||
[config.rightPanel?.editModalScreenId, config.rightPanel?.addModalScreenId, config.rightPanel?.mainTableForEdit, selectedLeftItem, loadRightData],
|
[config.rightPanel?.editModalScreenId, config.rightPanel?.addModalScreenId, config.rightPanel?.mainTableForEdit, selectedLeftItem, loadRightData, loadLeftData],
|
||||||
);
|
);
|
||||||
|
|
||||||
// 좌측 패널 수정 버튼 클릭
|
// 좌측 패널 수정 버튼 클릭
|
||||||
|
|
@ -835,10 +838,11 @@ export const SplitPanelLayout2Component: React.FC<SplitPanelLayout2ComponentProp
|
||||||
// 데이터 새로고침
|
// 데이터 새로고침
|
||||||
if (deleteTargetPanel === "left") {
|
if (deleteTargetPanel === "left") {
|
||||||
loadLeftData();
|
loadLeftData();
|
||||||
setSelectedLeftItem(null); // 좌측 선택 초기화
|
setSelectedLeftItem(null);
|
||||||
setRightData([]); // 우측 데이터도 초기화
|
setRightData([]);
|
||||||
} else if (selectedLeftItem) {
|
} else if (selectedLeftItem) {
|
||||||
loadRightData(selectedLeftItem);
|
loadRightData(selectedLeftItem);
|
||||||
|
loadLeftData();
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("[SplitPanelLayout2] 삭제 실패:", error);
|
console.error("[SplitPanelLayout2] 삭제 실패:", error);
|
||||||
|
|
@ -903,6 +907,7 @@ export const SplitPanelLayout2Component: React.FC<SplitPanelLayout2ComponentProp
|
||||||
if (selectedLeftItem) {
|
if (selectedLeftItem) {
|
||||||
loadRightData(selectedLeftItem);
|
loadRightData(selectedLeftItem);
|
||||||
}
|
}
|
||||||
|
loadLeftData();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -917,7 +922,6 @@ export const SplitPanelLayout2Component: React.FC<SplitPanelLayout2ComponentProp
|
||||||
const selectedId = Array.from(selectedRightItems)[0];
|
const selectedId = Array.from(selectedRightItems)[0];
|
||||||
const item = rightData.find((d) => d[pkColumn] === selectedId);
|
const item = rightData.find((d) => d[pkColumn] === selectedId);
|
||||||
if (item) {
|
if (item) {
|
||||||
// 액션 버튼에 모달 화면이 설정되어 있으면 해당 화면 사용
|
|
||||||
const modalScreenId = btn.modalScreenId || config.rightPanel?.editModalScreenId || config.rightPanel?.addModalScreenId;
|
const modalScreenId = btn.modalScreenId || config.rightPanel?.editModalScreenId || config.rightPanel?.addModalScreenId;
|
||||||
|
|
||||||
if (!modalScreenId) {
|
if (!modalScreenId) {
|
||||||
|
|
@ -936,6 +940,7 @@ export const SplitPanelLayout2Component: React.FC<SplitPanelLayout2ComponentProp
|
||||||
if (selectedLeftItem) {
|
if (selectedLeftItem) {
|
||||||
loadRightData(selectedLeftItem);
|
loadRightData(selectedLeftItem);
|
||||||
}
|
}
|
||||||
|
loadLeftData();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -966,6 +971,7 @@ export const SplitPanelLayout2Component: React.FC<SplitPanelLayout2ComponentProp
|
||||||
selectedLeftItem,
|
selectedLeftItem,
|
||||||
config.dataTransferFields,
|
config.dataTransferFields,
|
||||||
loadRightData,
|
loadRightData,
|
||||||
|
loadLeftData,
|
||||||
selectedRightItems,
|
selectedRightItems,
|
||||||
getPrimaryKeyColumn,
|
getPrimaryKeyColumn,
|
||||||
rightData,
|
rightData,
|
||||||
|
|
|
||||||
|
|
@ -2449,7 +2449,7 @@ export function TableSectionRenderer({
|
||||||
multiSelect={multiSelect}
|
multiSelect={multiSelect}
|
||||||
filterCondition={conditionalFilterCondition}
|
filterCondition={conditionalFilterCondition}
|
||||||
modalTitle={`${effectiveOptions.find((o) => o.value === modalCondition)?.label || modalCondition} - ${modalTitle}`}
|
modalTitle={`${effectiveOptions.find((o) => o.value === modalCondition)?.label || modalCondition} - ${modalTitle}`}
|
||||||
alreadySelected={conditionalTableData[modalCondition] || []}
|
alreadySelected={Object.values(conditionalTableData).flat()}
|
||||||
uniqueField={tableConfig.saveConfig?.uniqueField}
|
uniqueField={tableConfig.saveConfig?.uniqueField}
|
||||||
onSelect={handleConditionalAddItems}
|
onSelect={handleConditionalAddItems}
|
||||||
columnLabels={columnLabels}
|
columnLabels={columnLabels}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue