feat: Enhance SplitPanelLayout with modal support for add and edit buttons
- Implemented modal configuration for add and edit buttons in the SplitPanelLayoutComponent, allowing for custom modal screens based on user interactions. - Added settings for button visibility and modes (auto or modal) in the SplitPanelLayoutConfigPanel, improving flexibility in UI configuration. - Enhanced data handling by storing selected left panel items in modalDataStore for use in modal screens, ensuring seamless data flow. - Updated types to include new properties for add and edit button configurations, facilitating better type safety and clarity in component usage.
This commit is contained in:
parent
5d391f0cee
commit
fb02e5b389
|
|
@ -1045,7 +1045,6 @@
|
||||||
"integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
|
"integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@babel/generator": "^7.28.3",
|
"@babel/generator": "^7.28.3",
|
||||||
|
|
@ -2373,7 +2372,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz",
|
||||||
"integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==",
|
"integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cluster-key-slot": "1.1.2",
|
"cluster-key-slot": "1.1.2",
|
||||||
"generic-pool": "3.9.0",
|
"generic-pool": "3.9.0",
|
||||||
|
|
@ -3477,7 +3475,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.17.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.17.tgz",
|
||||||
"integrity": "sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ==",
|
"integrity": "sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.21.0"
|
"undici-types": "~6.21.0"
|
||||||
}
|
}
|
||||||
|
|
@ -3714,7 +3711,6 @@
|
||||||
"integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==",
|
"integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "6.21.0",
|
"@typescript-eslint/scope-manager": "6.21.0",
|
||||||
"@typescript-eslint/types": "6.21.0",
|
"@typescript-eslint/types": "6.21.0",
|
||||||
|
|
@ -3932,7 +3928,6 @@
|
||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
|
|
@ -4459,7 +4454,6 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.8.3",
|
"baseline-browser-mapping": "^2.8.3",
|
||||||
"caniuse-lite": "^1.0.30001741",
|
"caniuse-lite": "^1.0.30001741",
|
||||||
|
|
@ -5670,7 +5664,6 @@
|
||||||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.6.1",
|
"@eslint-community/regexpp": "^4.6.1",
|
||||||
|
|
@ -5949,7 +5942,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
|
||||||
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
|
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "~1.3.8",
|
"accepts": "~1.3.8",
|
||||||
"array-flatten": "1.1.1",
|
"array-flatten": "1.1.1",
|
||||||
|
|
@ -7443,7 +7435,6 @@
|
||||||
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
|
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/core": "^29.7.0",
|
"@jest/core": "^29.7.0",
|
||||||
"@jest/types": "^29.6.3",
|
"@jest/types": "^29.6.3",
|
||||||
|
|
@ -8413,6 +8404,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||||
},
|
},
|
||||||
|
|
@ -9301,7 +9293,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
|
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
|
||||||
"integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
|
"integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pg-connection-string": "^2.9.1",
|
"pg-connection-string": "^2.9.1",
|
||||||
"pg-pool": "^3.10.1",
|
"pg-pool": "^3.10.1",
|
||||||
|
|
@ -10152,6 +10143,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
||||||
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
|
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0"
|
"loose-envify": "^1.1.0"
|
||||||
}
|
}
|
||||||
|
|
@ -10960,7 +10952,6 @@
|
||||||
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cspotcode/source-map-support": "^0.8.0",
|
"@cspotcode/source-map-support": "^0.8.0",
|
||||||
"@tsconfig/node10": "^1.0.7",
|
"@tsconfig/node10": "^1.0.7",
|
||||||
|
|
@ -11066,7 +11057,6 @@
|
||||||
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
|
|
||||||
|
|
@ -1993,6 +1993,88 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||||
// 추가 버튼 핸들러
|
// 추가 버튼 핸들러
|
||||||
const handleAddClick = useCallback(
|
const handleAddClick = useCallback(
|
||||||
(panel: "left" | "right") => {
|
(panel: "left" | "right") => {
|
||||||
|
// 좌측 패널 추가 시, addButton 모달 모드 확인
|
||||||
|
if (panel === "left") {
|
||||||
|
const addButtonConfig = componentConfig.leftPanel?.addButton;
|
||||||
|
if (addButtonConfig?.mode === "modal" && addButtonConfig?.modalScreenId) {
|
||||||
|
const leftTableName = componentConfig.leftPanel?.tableName || "";
|
||||||
|
|
||||||
|
// ScreenModal 열기 이벤트 발생
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent("openScreenModal", {
|
||||||
|
detail: {
|
||||||
|
screenId: addButtonConfig.modalScreenId,
|
||||||
|
urlParams: {
|
||||||
|
mode: "add",
|
||||||
|
tableName: leftTableName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("✅ [SplitPanel] 좌측 추가 모달 화면 열기:", {
|
||||||
|
screenId: addButtonConfig.modalScreenId,
|
||||||
|
tableName: leftTableName,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 우측 패널 추가 시, addButton 모달 모드 확인
|
||||||
|
if (panel === "right") {
|
||||||
|
const addButtonConfig =
|
||||||
|
activeTabIndex === 0
|
||||||
|
? componentConfig.rightPanel?.addButton
|
||||||
|
: (componentConfig.rightPanel?.additionalTabs?.[activeTabIndex - 1] as any)?.addButton;
|
||||||
|
|
||||||
|
if (addButtonConfig?.mode === "modal" && addButtonConfig?.modalScreenId) {
|
||||||
|
// 커스텀 모달 화면 열기
|
||||||
|
const currentTableName =
|
||||||
|
activeTabIndex === 0
|
||||||
|
? componentConfig.rightPanel?.tableName || ""
|
||||||
|
: (componentConfig.rightPanel?.additionalTabs?.[activeTabIndex - 1] as any)?.tableName || "";
|
||||||
|
|
||||||
|
// 좌측 선택 데이터를 modalDataStore에 저장 (추가 화면에서 참조 가능)
|
||||||
|
if (selectedLeftItem && componentConfig.leftPanel?.tableName) {
|
||||||
|
import("@/stores/modalDataStore").then(({ useModalDataStore }) => {
|
||||||
|
useModalDataStore.getState().setData(componentConfig.leftPanel!.tableName!, [selectedLeftItem]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScreenModal 열기 이벤트 발생
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent("openScreenModal", {
|
||||||
|
detail: {
|
||||||
|
screenId: addButtonConfig.modalScreenId,
|
||||||
|
urlParams: {
|
||||||
|
mode: "add",
|
||||||
|
tableName: currentTableName,
|
||||||
|
// 좌측 선택 항목의 연결 키 값 전달
|
||||||
|
...(selectedLeftItem && (() => {
|
||||||
|
const relation = activeTabIndex === 0
|
||||||
|
? componentConfig.rightPanel?.relation
|
||||||
|
: (componentConfig.rightPanel?.additionalTabs?.[activeTabIndex - 1] as any)?.relation;
|
||||||
|
const leftColumn = relation?.keys?.[0]?.leftColumn || relation?.leftColumn;
|
||||||
|
const rightColumn = relation?.keys?.[0]?.rightColumn || relation?.foreignKey;
|
||||||
|
if (leftColumn && rightColumn && selectedLeftItem[leftColumn] !== undefined) {
|
||||||
|
return { [rightColumn]: selectedLeftItem[leftColumn] };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
})()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("✅ [SplitPanel] 추가 모달 화면 열기:", {
|
||||||
|
screenId: addButtonConfig.modalScreenId,
|
||||||
|
tableName: currentTableName,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 기존 내장 추가 모달 로직
|
||||||
setAddModalPanel(panel);
|
setAddModalPanel(panel);
|
||||||
|
|
||||||
// 우측 패널 추가 시, 좌측에서 선택된 항목의 조인 컬럼 값을 자동으로 채움
|
// 우측 패널 추가 시, 좌측에서 선택된 항목의 조인 컬럼 값을 자동으로 채움
|
||||||
|
|
@ -2012,12 +2094,66 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||||
|
|
||||||
setShowAddModal(true);
|
setShowAddModal(true);
|
||||||
},
|
},
|
||||||
[selectedLeftItem, componentConfig],
|
[selectedLeftItem, componentConfig, activeTabIndex],
|
||||||
);
|
);
|
||||||
|
|
||||||
// 수정 버튼 핸들러
|
// 수정 버튼 핸들러
|
||||||
const handleEditClick = useCallback(
|
const handleEditClick = useCallback(
|
||||||
(panel: "left" | "right", item: any) => {
|
(panel: "left" | "right", item: any) => {
|
||||||
|
// 좌측 패널 수정 버튼 설정 확인 (모달 모드)
|
||||||
|
if (panel === "left") {
|
||||||
|
const editButtonConfig = componentConfig.leftPanel?.editButton;
|
||||||
|
if (editButtonConfig?.mode === "modal" && editButtonConfig?.modalScreenId) {
|
||||||
|
const leftTableName = componentConfig.leftPanel?.tableName || "";
|
||||||
|
const sourceColumn = componentConfig.leftPanel?.itemAddConfig?.sourceColumn || "id";
|
||||||
|
|
||||||
|
// Primary Key 찾기
|
||||||
|
let primaryKeyName = sourceColumn;
|
||||||
|
let primaryKeyValue = item[sourceColumn];
|
||||||
|
|
||||||
|
if (primaryKeyValue === undefined || primaryKeyValue === null) {
|
||||||
|
if (item.id !== undefined && item.id !== null) {
|
||||||
|
primaryKeyName = "id";
|
||||||
|
primaryKeyValue = item.id;
|
||||||
|
} else if (item.ID !== undefined && item.ID !== null) {
|
||||||
|
primaryKeyName = "ID";
|
||||||
|
primaryKeyValue = item.ID;
|
||||||
|
} else {
|
||||||
|
const firstKey = Object.keys(item)[0];
|
||||||
|
primaryKeyName = firstKey;
|
||||||
|
primaryKeyValue = item[firstKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// modalDataStore에 저장
|
||||||
|
import("@/stores/modalDataStore").then(({ useModalDataStore }) => {
|
||||||
|
useModalDataStore.getState().setData(leftTableName, [item]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ScreenModal 열기 이벤트 발생
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent("openScreenModal", {
|
||||||
|
detail: {
|
||||||
|
screenId: editButtonConfig.modalScreenId,
|
||||||
|
urlParams: {
|
||||||
|
mode: "edit",
|
||||||
|
editId: primaryKeyValue,
|
||||||
|
tableName: leftTableName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("✅ [SplitPanel] 좌측 수정 모달 화면 열기:", {
|
||||||
|
screenId: editButtonConfig.modalScreenId,
|
||||||
|
tableName: leftTableName,
|
||||||
|
primaryKeyName,
|
||||||
|
primaryKeyValue,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 우측 패널 수정 버튼 설정 확인 (탭별 설정 지원)
|
// 우측 패널 수정 버튼 설정 확인 (탭별 설정 지원)
|
||||||
if (panel === "right") {
|
if (panel === "right") {
|
||||||
const editButtonConfig =
|
const editButtonConfig =
|
||||||
|
|
@ -3339,29 +3475,33 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||||
{/* 항목별 버튼들 */}
|
{/* 항목별 버튼들 */}
|
||||||
{!isDesignMode && (
|
{!isDesignMode && (
|
||||||
<div className="flex flex-shrink-0 items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100">
|
<div className="flex flex-shrink-0 items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100">
|
||||||
{/* 수정 버튼 */}
|
{/* 수정 버튼 (showEdit 활성화 시에만 표시) */}
|
||||||
<button
|
{(componentConfig.leftPanel?.showEdit !== false) && (
|
||||||
onClick={(e) => {
|
<button
|
||||||
e.stopPropagation();
|
onClick={(e) => {
|
||||||
handleEditClick("left", item);
|
e.stopPropagation();
|
||||||
}}
|
handleEditClick("left", item);
|
||||||
className="rounded p-1 transition-colors hover:bg-gray-200"
|
}}
|
||||||
title="수정"
|
className="rounded p-1 transition-colors hover:bg-gray-200"
|
||||||
>
|
title="수정"
|
||||||
<Pencil className="h-4 w-4 text-gray-600" />
|
>
|
||||||
</button>
|
<Pencil className="h-4 w-4 text-gray-600" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 삭제 버튼 */}
|
{/* 삭제 버튼 (showDelete 활성화 시에만 표시) */}
|
||||||
<button
|
{(componentConfig.leftPanel?.showDelete !== false) && (
|
||||||
onClick={(e) => {
|
<button
|
||||||
e.stopPropagation();
|
onClick={(e) => {
|
||||||
handleDeleteClick("left", item);
|
e.stopPropagation();
|
||||||
}}
|
handleDeleteClick("left", item);
|
||||||
className="rounded p-1 transition-colors hover:bg-red-100"
|
}}
|
||||||
title="삭제"
|
className="rounded p-1 transition-colors hover:bg-red-100"
|
||||||
>
|
title="삭제"
|
||||||
<Trash2 className="h-4 w-4 text-red-600" />
|
>
|
||||||
</button>
|
<Trash2 className="h-4 w-4 text-red-600" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 항목별 추가 버튼 */}
|
{/* 항목별 추가 버튼 */}
|
||||||
{componentConfig.leftPanel?.showItemAddButton && (
|
{componentConfig.leftPanel?.showItemAddButton && (
|
||||||
|
|
|
||||||
|
|
@ -1066,6 +1066,62 @@ const AdditionalTabConfigPanel: React.FC<AdditionalTabConfigPanelProps> = ({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* ===== 10-1. 추가 버튼 설정 ===== */}
|
||||||
|
{tab.showAdd && (
|
||||||
|
<div className="space-y-3 rounded-lg border border-border/50 bg-muted/40 p-3">
|
||||||
|
<h3 className="border-l-2 border-l-primary/40 pl-2 text-xs font-semibold">추가 버튼 설정</h3>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label className="text-[10px]">추가 모드</Label>
|
||||||
|
<Select
|
||||||
|
value={tab.addButton?.mode || "auto"}
|
||||||
|
onValueChange={(value: "auto" | "modal") => {
|
||||||
|
updateTab({
|
||||||
|
addButton: { ...tab.addButton, enabled: true, mode: value },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="h-7 text-xs">
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="auto">자동 (내장 폼)</SelectItem>
|
||||||
|
<SelectItem value="modal">모달 화면</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{tab.addButton?.mode === "modal" && (
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label className="text-[10px]">추가 모달 화면</Label>
|
||||||
|
<ScreenSelector
|
||||||
|
value={tab.addButton?.modalScreenId}
|
||||||
|
onChange={(screenId) => {
|
||||||
|
updateTab({
|
||||||
|
addButton: { ...tab.addButton, enabled: true, mode: "modal", modalScreenId: screenId },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label className="text-[10px]">버튼 라벨</Label>
|
||||||
|
<Input
|
||||||
|
value={tab.addButton?.buttonLabel || ""}
|
||||||
|
onChange={(e) => {
|
||||||
|
updateTab({
|
||||||
|
addButton: { ...tab.addButton, enabled: true, buttonLabel: e.target.value || undefined },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
placeholder="추가"
|
||||||
|
className="h-7 text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* ===== 11. 삭제 버튼 설정 ===== */}
|
{/* ===== 11. 삭제 버튼 설정 ===== */}
|
||||||
{tab.showDelete && (
|
{tab.showDelete && (
|
||||||
<div className="space-y-3 rounded-lg border border-border/50 bg-muted/40 p-3">
|
<div className="space-y-3 rounded-lg border border-border/50 bg-muted/40 p-3">
|
||||||
|
|
@ -2071,6 +2127,169 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||||
menuObjid={menuObjid} // 🆕 메뉴 OBJID 전달
|
menuObjid={menuObjid} // 🆕 메뉴 OBJID 전달
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 좌측 패널 버튼 설정 */}
|
||||||
|
<div className="space-y-4 rounded-lg border border-border/50 bg-muted/40 p-4">
|
||||||
|
<h3 className="border-l-2 border-l-primary/40 pl-2 text-sm font-semibold">좌측 패널 버튼 설정</h3>
|
||||||
|
|
||||||
|
{/* 버튼 표시 체크박스 */}
|
||||||
|
<div className="grid grid-cols-4 gap-2">
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<Checkbox
|
||||||
|
id="left-showSearch"
|
||||||
|
checked={config.leftPanel?.showSearch ?? false}
|
||||||
|
onCheckedChange={(checked) => updateLeftPanel({ showSearch: !!checked })}
|
||||||
|
/>
|
||||||
|
<label htmlFor="left-showSearch" className="text-xs">검색</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<Checkbox
|
||||||
|
id="left-showAdd"
|
||||||
|
checked={config.leftPanel?.showAdd ?? false}
|
||||||
|
onCheckedChange={(checked) => updateLeftPanel({ showAdd: !!checked })}
|
||||||
|
/>
|
||||||
|
<label htmlFor="left-showAdd" className="text-xs">추가</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<Checkbox
|
||||||
|
id="left-showEdit"
|
||||||
|
checked={config.leftPanel?.showEdit ?? true}
|
||||||
|
onCheckedChange={(checked) => updateLeftPanel({ showEdit: !!checked })}
|
||||||
|
/>
|
||||||
|
<label htmlFor="left-showEdit" className="text-xs">수정</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<Checkbox
|
||||||
|
id="left-showDelete"
|
||||||
|
checked={config.leftPanel?.showDelete ?? true}
|
||||||
|
onCheckedChange={(checked) => updateLeftPanel({ showDelete: !!checked })}
|
||||||
|
/>
|
||||||
|
<label htmlFor="left-showDelete" className="text-xs">삭제</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 추가 버튼 상세 설정 */}
|
||||||
|
{config.leftPanel?.showAdd && (
|
||||||
|
<div className="space-y-3 rounded-lg border border-border/50 bg-muted/40 p-3">
|
||||||
|
<h3 className="border-l-2 border-l-primary/40 pl-2 text-xs font-semibold">추가 버튼 설정</h3>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label className="text-[10px]">추가 모드</Label>
|
||||||
|
<Select
|
||||||
|
value={config.leftPanel?.addButton?.mode || "auto"}
|
||||||
|
onValueChange={(value: "auto" | "modal") =>
|
||||||
|
updateLeftPanel({
|
||||||
|
addButton: { ...config.leftPanel?.addButton, enabled: true, mode: value },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="h-7 text-xs">
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="auto">자동 (내장 폼)</SelectItem>
|
||||||
|
<SelectItem value="modal">모달 화면</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{config.leftPanel?.addButton?.mode === "modal" && (
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label className="text-[10px]">추가 모달 화면</Label>
|
||||||
|
<ScreenSelector
|
||||||
|
value={config.leftPanel?.addButton?.modalScreenId}
|
||||||
|
onChange={(screenId) =>
|
||||||
|
updateLeftPanel({
|
||||||
|
addButton: { ...config.leftPanel?.addButton, enabled: true, mode: "modal", modalScreenId: screenId },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label className="text-[10px]">버튼 라벨</Label>
|
||||||
|
<Input
|
||||||
|
value={config.leftPanel?.addButton?.buttonLabel || ""}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateLeftPanel({
|
||||||
|
addButton: {
|
||||||
|
...config.leftPanel?.addButton,
|
||||||
|
enabled: true,
|
||||||
|
mode: config.leftPanel?.addButton?.mode || "auto",
|
||||||
|
buttonLabel: e.target.value || undefined,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="추가"
|
||||||
|
className="h-7 text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 수정 버튼 상세 설정 */}
|
||||||
|
{(config.leftPanel?.showEdit ?? true) && (
|
||||||
|
<div className="space-y-3 rounded-lg border border-border/50 bg-muted/40 p-3">
|
||||||
|
<h3 className="border-l-2 border-l-primary/40 pl-2 text-xs font-semibold">수정 버튼 설정</h3>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label className="text-[10px]">수정 모드</Label>
|
||||||
|
<Select
|
||||||
|
value={config.leftPanel?.editButton?.mode || "auto"}
|
||||||
|
onValueChange={(value: "auto" | "modal") =>
|
||||||
|
updateLeftPanel({
|
||||||
|
editButton: { ...config.leftPanel?.editButton, enabled: true, mode: value },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="h-7 text-xs">
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="auto">자동 (인라인)</SelectItem>
|
||||||
|
<SelectItem value="modal">모달 화면</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{config.leftPanel?.editButton?.mode === "modal" && (
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label className="text-[10px]">수정 모달 화면</Label>
|
||||||
|
<ScreenSelector
|
||||||
|
value={config.leftPanel?.editButton?.modalScreenId}
|
||||||
|
onChange={(screenId) =>
|
||||||
|
updateLeftPanel({
|
||||||
|
editButton: { ...config.leftPanel?.editButton, enabled: true, mode: "modal", modalScreenId: screenId },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label className="text-[10px]">버튼 라벨</Label>
|
||||||
|
<Input
|
||||||
|
value={config.leftPanel?.editButton?.buttonLabel || ""}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateLeftPanel({
|
||||||
|
editButton: {
|
||||||
|
...config.leftPanel?.editButton,
|
||||||
|
enabled: true,
|
||||||
|
mode: config.leftPanel?.editButton?.mode || "auto",
|
||||||
|
buttonLabel: e.target.value || undefined,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="수정"
|
||||||
|
className="h-7 text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
@ -2775,6 +2994,85 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 🆕 우측 패널 추가 버튼 설정 */}
|
||||||
|
{config.rightPanel?.showAdd && (
|
||||||
|
<div className="space-y-3 rounded-lg border border-border/50 bg-muted/40 p-4">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h3 className="border-l-2 border-l-primary/40 pl-2 text-sm font-semibold">추가 버튼 설정</h3>
|
||||||
|
<p className="text-muted-foreground text-xs">우측 리스트의 추가 버튼 동작 방식 설정</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-3 border-l-2 pl-4">
|
||||||
|
<div>
|
||||||
|
<Label className="text-xs">추가 모드</Label>
|
||||||
|
<Select
|
||||||
|
value={config.rightPanel?.addButton?.mode || "auto"}
|
||||||
|
onValueChange={(value: "auto" | "modal") =>
|
||||||
|
updateRightPanel({
|
||||||
|
addButton: {
|
||||||
|
...config.rightPanel?.addButton,
|
||||||
|
mode: value,
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="h-8 text-xs">
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="auto">자동 (내장 폼)</SelectItem>
|
||||||
|
<SelectItem value="modal">모달 화면</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<p className="text-muted-foreground mt-1 text-[10px]">
|
||||||
|
{config.rightPanel?.addButton?.mode === "modal"
|
||||||
|
? "지정한 화면을 모달로 열어 데이터를 추가합니다"
|
||||||
|
: "내장 폼으로 데이터를 추가합니다"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{config.rightPanel?.addButton?.mode === "modal" && (
|
||||||
|
<div>
|
||||||
|
<Label className="text-xs">모달 화면</Label>
|
||||||
|
<ScreenSelector
|
||||||
|
value={config.rightPanel?.addButton?.modalScreenId}
|
||||||
|
onChange={(screenId) =>
|
||||||
|
updateRightPanel({
|
||||||
|
addButton: {
|
||||||
|
...config.rightPanel?.addButton!,
|
||||||
|
modalScreenId: screenId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Label className="text-xs">버튼 라벨</Label>
|
||||||
|
<Input
|
||||||
|
value={config.rightPanel?.addButton?.buttonLabel || "추가"}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateRightPanel({
|
||||||
|
addButton: {
|
||||||
|
...config.rightPanel?.addButton!,
|
||||||
|
buttonLabel: e.target.value,
|
||||||
|
enabled: true,
|
||||||
|
mode: config.rightPanel?.addButton?.mode || "auto",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="추가"
|
||||||
|
className="h-8 text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 🆕 우측 패널 삭제 버튼 설정 */}
|
{/* 🆕 우측 패널 삭제 버튼 설정 */}
|
||||||
<div className="space-y-3 rounded-lg border border-border/50 bg-muted/40 p-4">
|
<div className="space-y-3 rounded-lg border border-border/50 bg-muted/40 p-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,14 @@ export interface AdditionalTabConfig {
|
||||||
groupByColumns?: string[];
|
groupByColumns?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 추가 버튼 설정 (모달 화면 연결 지원)
|
||||||
|
addButton?: {
|
||||||
|
enabled: boolean;
|
||||||
|
mode: "auto" | "modal"; // auto: 내장 폼, modal: 커스텀 모달 화면
|
||||||
|
modalScreenId?: number; // 모달로 열 화면 ID (mode: "modal"일 때)
|
||||||
|
buttonLabel?: string; // 버튼 라벨 (기본: "추가")
|
||||||
|
};
|
||||||
|
|
||||||
deleteButton?: {
|
deleteButton?: {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
buttonLabel?: string;
|
buttonLabel?: string;
|
||||||
|
|
@ -141,6 +149,23 @@ export interface SplitPanelLayoutConfig {
|
||||||
showAdd?: boolean;
|
showAdd?: boolean;
|
||||||
showEdit?: boolean; // 수정 버튼
|
showEdit?: boolean; // 수정 버튼
|
||||||
showDelete?: boolean; // 삭제 버튼
|
showDelete?: boolean; // 삭제 버튼
|
||||||
|
|
||||||
|
// 수정 버튼 설정 (모달 화면 연결 지원)
|
||||||
|
editButton?: {
|
||||||
|
enabled: boolean;
|
||||||
|
mode: "auto" | "modal"; // auto: 내장 편집, modal: 커스텀 모달 화면
|
||||||
|
modalScreenId?: number; // 모달로 열 화면 ID (mode: "modal"일 때)
|
||||||
|
buttonLabel?: string; // 버튼 라벨 (기본: "수정")
|
||||||
|
};
|
||||||
|
|
||||||
|
// 추가 버튼 설정 (모달 화면 연결 지원)
|
||||||
|
addButton?: {
|
||||||
|
enabled: boolean;
|
||||||
|
mode: "auto" | "modal"; // auto: 내장 폼, modal: 커스텀 모달 화면
|
||||||
|
modalScreenId?: number; // 모달로 열 화면 ID (mode: "modal"일 때)
|
||||||
|
buttonLabel?: string; // 버튼 라벨 (기본: "추가")
|
||||||
|
};
|
||||||
|
|
||||||
columns?: Array<{
|
columns?: Array<{
|
||||||
name: string;
|
name: string;
|
||||||
label: string;
|
label: string;
|
||||||
|
|
@ -307,6 +332,14 @@ export interface SplitPanelLayoutConfig {
|
||||||
groupByColumns?: string[]; // 🆕 그룹핑 기준 컬럼들 (예: ["customer_id", "item_id"])
|
groupByColumns?: string[]; // 🆕 그룹핑 기준 컬럼들 (예: ["customer_id", "item_id"])
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 🆕 추가 버튼 설정 (모달 화면 연결 지원)
|
||||||
|
addButton?: {
|
||||||
|
enabled: boolean; // 추가 버튼 표시 여부 (기본: true)
|
||||||
|
mode: "auto" | "modal"; // auto: 내장 폼, modal: 커스텀 모달 화면
|
||||||
|
modalScreenId?: number; // 모달로 열 화면 ID (mode: "modal"일 때)
|
||||||
|
buttonLabel?: string; // 버튼 라벨 (기본: "추가")
|
||||||
|
};
|
||||||
|
|
||||||
// 🆕 삭제 버튼 설정
|
// 🆕 삭제 버튼 설정
|
||||||
deleteButton?: {
|
deleteButton?: {
|
||||||
enabled: boolean; // 삭제 버튼 표시 여부 (기본: true)
|
enabled: boolean; // 삭제 버튼 표시 여부 (기본: true)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue