diff --git a/frontend/components/screen-embedding/EmbeddedScreen.tsx b/frontend/components/screen-embedding/EmbeddedScreen.tsx
index d8e62c00..17cd240f 100644
--- a/frontend/components/screen-embedding/EmbeddedScreen.tsx
+++ b/frontend/components/screen-embedding/EmbeddedScreen.tsx
@@ -40,32 +40,33 @@ export const EmbeddedScreen = forwardRef(null);
const [screenInfo, setScreenInfo] = useState(null);
const [formData, setFormData] = useState>(initialFormData || {}); // ๐ ์ด๊ธฐ ๋ฐ์ดํฐ๋ก ์์
+ const [formDataVersion, setFormDataVersion] = useState(0); // ๐ ํผ ๋ฐ์ดํฐ ๋ฒ์ (๊ฐ์ ๋ฆฌ๋ ๋๋ง์ฉ)
// ์ปดํฌ๋ํธ ์ฐธ์กฐ ๋งต
const componentRefs = useRef
) : (
-
{layout.map((component) => {
const { position: compPosition = { x: 0, y: 0, z: 1 }, size = { width: 200, height: 40 } } = component;
-
+
// ์ปดํฌ๋ํธ๊ฐ ์ปจํ
์ด๋ ๋๋น๋ฅผ ์ด๊ณผํ์ง ์๋๋ก ๋๋น ์กฐ์
// ๋ถ๋ชจ ์ปจํ
์ด๋์ 100%๋ฅผ ๊ธฐ์ค์ผ๋ก ๊ณ์ฐ
const componentStyle: React.CSSProperties = {
@@ -397,13 +421,9 @@ export const EmbeddedScreen = forwardRef
+
= ({
const loadModalMappingColumns = async () => {
// ์์ค ํ
์ด๋ธ: ํ์ฌ ํ๋ฉด์ ๋ถํ ํจ๋ ๋๋ ํ
์ด๋ธ์์ ๊ฐ์ง
- // allComponents์์ split-panel-layout ๋๋ table-list ์ฐพ๊ธฐ
let sourceTableName: string | null = null;
+ console.log("[openModalWithData] ์ปฌ๋ผ ๋ก๋ ์์:", {
+ allComponentsCount: allComponents.length,
+ currentTableName,
+ targetScreenId: config.action?.targetScreenId,
+ });
+
+ // ๋ชจ๋ ์ปดํฌ๋ํธ ํ์
๋ก๊ทธ
+ allComponents.forEach((comp, idx) => {
+ const compType = comp.componentType || (comp as any).componentConfig?.type;
+ console.log(` [${idx}] componentType: ${compType}, tableName: ${(comp as any).componentConfig?.tableName || (comp as any).componentConfig?.leftPanel?.tableName || 'N/A'}`);
+ });
+
for (const comp of allComponents) {
const compType = comp.componentType || (comp as any).componentConfig?.type;
+ const compConfig = (comp as any).componentConfig || {};
+
+ // ๋ถํ ํจ๋ ํ์
๋ค (๋ค์ํ ๊ฒฝ๋ก์์ ํ
์ด๋ธ๋ช
์ถ์ถ)
if (compType === "split-panel-layout" || compType === "screen-split-panel") {
- // ๋ถํ ํจ๋์ ์ข์ธก ํ
์ด๋ธ๋ช
- sourceTableName = (comp as any).componentConfig?.leftPanel?.tableName ||
- (comp as any).componentConfig?.leftTableName;
- break;
+ sourceTableName = compConfig?.leftPanel?.tableName ||
+ compConfig?.leftTableName ||
+ compConfig?.tableName;
+ if (sourceTableName) {
+ console.log(`โ
[openModalWithData] split-panel-layout์์ ์์ค ํ
์ด๋ธ ๊ฐ์ง: ${sourceTableName}`);
+ break;
+ }
}
+
+ // split-panel-layout2 ํ์
(์๋ก์ด ๋ถํ ํจ๋)
+ if (compType === "split-panel-layout2") {
+ sourceTableName = compConfig?.leftPanel?.tableName ||
+ compConfig?.tableName ||
+ compConfig?.leftTableName;
+ if (sourceTableName) {
+ console.log(`โ
[openModalWithData] split-panel-layout2์์ ์์ค ํ
์ด๋ธ ๊ฐ์ง: ${sourceTableName}`);
+ break;
+ }
+ }
+
+ // ํ
์ด๋ธ ๋ฆฌ์คํธ ํ์
if (compType === "table-list") {
- sourceTableName = (comp as any).componentConfig?.tableName;
+ sourceTableName = compConfig?.tableName;
+ if (sourceTableName) {
+ console.log(`โ
[openModalWithData] table-list์์ ์์ค ํ
์ด๋ธ ๊ฐ์ง: ${sourceTableName}`);
+ break;
+ }
+ }
+
+ // ๐ ๋ชจ๋ ์ปดํฌ๋ํธ์์ tableName ์ฐพ๊ธฐ (ํด๋ฐฑ)
+ if (!sourceTableName && compConfig?.tableName) {
+ sourceTableName = compConfig.tableName;
+ console.log(`โ
[openModalWithData] ${compType}์์ ์์ค ํ
์ด๋ธ ๊ฐ์ง (ํด๋ฐฑ): ${sourceTableName}`);
break;
}
}
+
+ // ์ฌ์ ํ ์์ผ๋ฉด currentTableName ์ฌ์ฉ (ํ๋ฉด ๋ ๋ฒจ ํ
์ด๋ธ๋ช
)
+ if (!sourceTableName && currentTableName) {
+ sourceTableName = currentTableName;
+ console.log(`โ
[openModalWithData] currentTableName์์ ์์ค ํ
์ด๋ธ ์ฌ์ฉ: ${sourceTableName}`);
+ }
+
+ if (!sourceTableName) {
+ console.warn("[openModalWithData] ์์ค ํ
์ด๋ธ์ ์ฐพ์ ์ ์์ต๋๋ค.");
+ }
// ์์ค ํ
์ด๋ธ ์ปฌ๋ผ ๋ก๋
if (sourceTableName) {
@@ -361,11 +411,11 @@ export const ButtonConfigPanel: React.FC = ({
if (Array.isArray(columnData)) {
const columns = columnData.map((col: any) => ({
- name: col.name || col.columnName,
- label: col.displayName || col.label || col.columnLabel || col.name || col.columnName,
+ name: col.name || col.columnName || col.column_name,
+ label: col.displayName || col.label || col.columnLabel || col.display_name || col.name || col.columnName || col.column_name,
}));
setModalSourceColumns(columns);
- console.log(`โ
[openModalWithData] ์์ค ํ
์ด๋ธ(${sourceTableName}) ์ปฌ๋ผ ๋ก๋:`, columns.length);
+ console.log(`โ
[openModalWithData] ์์ค ํ
์ด๋ธ(${sourceTableName}) ์ปฌ๋ผ ๋ก๋ ์๋ฃ:`, columns.length);
}
}
} catch (error) {
@@ -379,8 +429,12 @@ export const ButtonConfigPanel: React.FC = ({
try {
// ํ๊ฒ ํ๋ฉด ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
const screenResponse = await apiClient.get(`/screen-management/screens/${targetScreenId}`);
+ console.log("[openModalWithData] ํ๊ฒ ํ๋ฉด ์๋ต:", screenResponse.data);
+
if (screenResponse.data.success && screenResponse.data.data) {
const targetTableName = screenResponse.data.data.tableName;
+ console.log("[openModalWithData] ํ๊ฒ ํ๋ฉด ํ
์ด๋ธ๋ช
:", targetTableName);
+
if (targetTableName) {
const columnResponse = await apiClient.get(`/table-management/tables/${targetTableName}/columns`);
if (columnResponse.data.success) {
@@ -390,23 +444,27 @@ export const ButtonConfigPanel: React.FC = ({
if (Array.isArray(columnData)) {
const columns = columnData.map((col: any) => ({
- name: col.name || col.columnName,
- label: col.displayName || col.label || col.columnLabel || col.name || col.columnName,
+ name: col.name || col.columnName || col.column_name,
+ label: col.displayName || col.label || col.columnLabel || col.display_name || col.name || col.columnName || col.column_name,
}));
setModalTargetColumns(columns);
- console.log(`โ
[openModalWithData] ํ๊ฒ ํ
์ด๋ธ(${targetTableName}) ์ปฌ๋ผ ๋ก๋:`, columns.length);
+ console.log(`โ
[openModalWithData] ํ๊ฒ ํ
์ด๋ธ(${targetTableName}) ์ปฌ๋ผ ๋ก๋ ์๋ฃ:`, columns.length);
}
}
+ } else {
+ console.warn("[openModalWithData] ํ๊ฒ ํ๋ฉด์ ํ
์ด๋ธ๋ช
์ด ์์ต๋๋ค.");
}
}
} catch (error) {
console.error("ํ๊ฒ ํ๋ฉด ํ
์ด๋ธ ์ปฌ๋ผ ๋ก๋ ์คํจ:", error);
}
+ } else {
+ console.warn("[openModalWithData] ํ๊ฒ ํ๋ฉด ID๊ฐ ์์ต๋๋ค.");
}
};
loadModalMappingColumns();
- }, [config.action?.type, config.action?.targetScreenId, allComponents]);
+ }, [config.action?.type, config.action?.targetScreenId, allComponents, currentTableName]);
// ํ๋ฉด ๋ชฉ๋ก ๊ฐ์ ธ์ค๊ธฐ (ํ์ฌ ํธ์ง ์ค์ธ ํ๋ฉด์ ํ์ฌ ์ฝ๋ ๊ธฐ์ค)
useEffect(() => {
@@ -1158,11 +1216,12 @@ export const ButtonConfigPanel: React.FC = ({
) : (
-
+
{(config.action?.fieldMappings || []).map((mapping: any, index: number) => (
-
- {/* ์์ค ํ๋ ์ ํ (Combobox) */}
-
+
+ {/* ์์ค ํ๋ ์ ํ (Combobox) - ์ธ๋ก ๋ฐฐ์น */}
+
+
setModalSourcePopoverOpen((prev) => ({ ...prev, [index]: open }))}
@@ -1171,15 +1230,17 @@ export const ButtonConfigPanel: React.FC = ({
-
+
= ({
value={modalSourceSearch[index] || ""}
onValueChange={(value) => setModalSourceSearch((prev) => ({ ...prev, [index]: value }))}
/>
-
+
์ปฌ๋ผ์ ์ฐพ์ ์ ์์ต๋๋ค
{modalSourceColumns.map((col) => (
@@ -1208,9 +1269,9 @@ export const ButtonConfigPanel: React.FC = ({
mapping.sourceField === col.name ? "opacity-100" : "opacity-0"
)}
/>
- {col.label}
+ {col.label}
{col.label !== col.name && (
- ({col.name})
+ ({col.name})
)}
))}
@@ -1221,10 +1282,14 @@ export const ButtonConfigPanel: React.FC = ({
-
โ
+ {/* ํ์ดํ ํ์ */}
+
+ โ
+
- {/* ํ๊ฒ ํ๋ ์ ํ (Combobox) */}
-
+ {/* ํ๊ฒ ํ๋ ์ ํ (Combobox) - ์ธ๋ก ๋ฐฐ์น */}
+
+
setModalTargetPopoverOpen((prev) => ({ ...prev, [index]: open }))}
@@ -1233,15 +1298,17 @@ export const ButtonConfigPanel: React.FC = ({
-
+
= ({
value={modalTargetSearch[index] || ""}
onValueChange={(value) => setModalTargetSearch((prev) => ({ ...prev, [index]: value }))}
/>
-
+
์ปฌ๋ผ์ ์ฐพ์ ์ ์์ต๋๋ค
{modalTargetColumns.map((col) => (
@@ -1270,9 +1337,9 @@ export const ButtonConfigPanel: React.FC = ({
mapping.targetField === col.name ? "opacity-100" : "opacity-0"
)}
/>
- {col.label}
+ {col.label}
{col.label !== col.name && (
- ({col.name})
+ ({col.name})
)}
))}
@@ -1284,19 +1351,22 @@ export const ButtonConfigPanel: React.FC = ({
{/* ์ญ์ ๋ฒํผ */}
-
+
+
+
))}
diff --git a/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx b/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx
index 7ba822b2..160591c6 100644
--- a/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx
+++ b/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx
@@ -880,6 +880,44 @@ export const ButtonPrimaryComponent: React.FC
= ({
return;
}
+ // ๋ชจ๋ฌ ์ก์
์ธ๋ฐ ์ ํ๋ ๋ฐ์ดํฐ๊ฐ ์์ผ๋ฉด ๊ฒฝ๊ณ ๋ฉ์์ง ํ์ํ๊ณ ์ค๋จ
+ // (์ ๊ท ๋ฑ๋ก ๋ชจ๋ฌ์์ ์ ํ๋ ๋ฐ์ดํฐ๊ฐ ์ด๊ธฐ๊ฐ์ผ๋ก ์ ๋ฌ๋๋ ๊ฒ์ ๋ฐฉ์ง)
+ if (processedConfig.action.type === "modal" && effectiveSelectedRowsData && effectiveSelectedRowsData.length > 0) {
+ toast.warning("์ ๊ท ๋ฑ๋ก ์์๋ ํ
์ด๋ธ์์ ์ ํ๋ ํญ๋ชฉ์ ํด์ ํด์ฃผ์ธ์.");
+ return;
+ }
+
+ // ์์ (edit) ์ก์
๊ฒ์ฆ
+ if (processedConfig.action.type === "edit") {
+ // ์ ํ๋ ๋ฐ์ดํฐ๊ฐ ์์ผ๋ฉด ๊ฒฝ๊ณ
+ if (!effectiveSelectedRowsData || effectiveSelectedRowsData.length === 0) {
+ toast.warning("์์ ํ ํญ๋ชฉ์ ์ ํํด์ฃผ์ธ์.");
+ return;
+ }
+
+ // groupByColumns ์ค์ ์ด ์์ผ๋ฉด ํด๋น ์ปฌ๋ผ ๊ฐ์ด ์ ์ผํ์ง ํ์ธ
+ const groupByColumns = processedConfig.action.groupByColumns;
+ if (groupByColumns && groupByColumns.length > 0 && effectiveSelectedRowsData.length > 1) {
+ // ์ฒซ ๋ฒ์งธ ๊ทธ๋ฃนํ ์ปฌ๋ผ ๊ธฐ์ค์ผ๋ก ์ค๋ณต ์ฒดํฌ (์: order_no)
+ const groupByColumn = groupByColumns[0];
+ const uniqueValues = new Set(
+ effectiveSelectedRowsData.map((row: any) => row[groupByColumn]).filter(Boolean)
+ );
+
+ if (uniqueValues.size > 1) {
+ // ์ปฌ๋ผ๋ช
์ ํ๊ธ๋ก ๋ณํ (order_no -> ์์ฃผ๋ฒํธ)
+ const columnLabels: Record = {
+ order_no: "์์ฃผ๋ฒํธ",
+ shipment_no: "์ถํ๋ฒํธ",
+ purchase_no: "๊ตฌ๋งค๋ฒํธ",
+ };
+ const columnLabel = columnLabels[groupByColumn] || groupByColumn;
+ toast.warning(`${columnLabel} ํ๋๋ง ์ ํํด์ฃผ์ธ์. (ํ์ฌ ${uniqueValues.size}๊ฐ ์ ํ๋จ)`);
+ return;
+ }
+ }
+ }
+
// ๐ ๋ชจ๋ ์ปดํฌ๋ํธ์ ์ค์ ์์ง (parentDataMapping ๋ฑ)
const componentConfigs: Record = {};
if (allComponents && Array.isArray(allComponents)) {
@@ -919,21 +957,27 @@ export const ButtonPrimaryComponent: React.FC = ({
}
}
- // ๐ ๋ถํ ํจ๋ ์ฐ์ธก์ด๋ฉด screenContext.formData์ props.formData๋ฅผ ๋ณํฉ
- // screenContext.formData: RepeaterFieldGroup ๋ฑ ์ปดํฌ๋ํธ๊ฐ ์ง์ ์
๋ฐ์ดํธํ ๋ฐ์ดํฐ
- // props.formData: ๋ถ๋ชจ์์ ์ ๋ฌ๋ ํผ ๋ฐ์ดํฐ
+ // ๐ ๋ถํ ํจ๋ ์ฐ์ธก์ด๋ฉด ์ฌ๋ฌ ์์ค์์ formData๋ฅผ ๋ณํฉ
+ // ์ฐ์ ์์: props.formData > screenContext.formData > splitPanelParentData
const screenContextFormData = screenContext?.formData || {};
const propsFormData = formData || {};
- // ๋ณํฉ: props.formData๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ํ๊ณ , screenContext.formData๋ก ์ค๋ฒ๋ผ์ด๋
- // (RepeaterFieldGroup ๋ฐ์ดํฐ๋ screenContext์๋ง ์์)
- const effectiveFormData = { ...propsFormData, ...screenContextFormData };
+ // ๋ณํฉ: splitPanelParentData๋ฅผ ๊ธฐ๋ณธ์ผ๋ก, props.formData, screenContext.formData ์์ผ๋ก ์ค๋ฒ๋ผ์ด๋
+ // (์ผ๋ฐ ํผ ํ๋๋ props.formData, RepeaterFieldGroup์ screenContext.formData์ ์์)
+ let effectiveFormData = { ...propsFormData, ...screenContextFormData };
+
+ // ๐ ๋ถํ ํจ๋ ์ฐ์ธก์ด๊ณ formData๊ฐ ๋น์ด์์ผ๋ฉด splitPanelParentData ์ฌ์ฉ
+ if (splitPanelPosition === "right" && Object.keys(effectiveFormData).length === 0 && splitPanelParentData) {
+ effectiveFormData = { ...splitPanelParentData };
+ console.log("๐ [ButtonPrimary] ๋ถํ ํจ๋ ์ฐ์ธก - splitPanelParentData ์ฌ์ฉ:", Object.keys(effectiveFormData));
+ }
console.log("๐ [ButtonPrimary] formData ์ ํ:", {
hasScreenContextFormData: Object.keys(screenContextFormData).length > 0,
screenContextKeys: Object.keys(screenContextFormData),
hasPropsFormData: Object.keys(propsFormData).length > 0,
propsFormDataKeys: Object.keys(propsFormData),
+ hasSplitPanelParentData: !!splitPanelParentData && Object.keys(splitPanelParentData).length > 0,
splitPanelPosition,
effectiveFormDataKeys: Object.keys(effectiveFormData),
});
diff --git a/frontend/lib/registry/components/text-input/TextInputComponent.tsx b/frontend/lib/registry/components/text-input/TextInputComponent.tsx
index ad37f19f..8ffa8afe 100644
--- a/frontend/lib/registry/components/text-input/TextInputComponent.tsx
+++ b/frontend/lib/registry/components/text-input/TextInputComponent.tsx
@@ -53,7 +53,7 @@ export const TextInputComponent: React.FC = ({
// ์๋์์ฑ๋ ๊ฐ ์ํ
const [autoGeneratedValue, setAutoGeneratedValue] = useState("");
-
+
// API ํธ์ถ ์ค๋ณต ๋ฐฉ์ง๋ฅผ ์ํ ref
const isGeneratingRef = React.useRef(false);
const hasGeneratedRef = React.useRef(false);
@@ -104,7 +104,6 @@ export const TextInputComponent: React.FC = ({
const currentFormValue = formData?.[component.columnName];
const currentComponentValue = component.value;
-
// ์๋์์ฑ๋ ๊ฐ์ด ์๊ณ , ํ์ฌ ๊ฐ๋ ์์ ๋๋ง ์์ฑ
if (!autoGeneratedValue && !currentFormValue && !currentComponentValue) {
isGeneratingRef.current = true; // ์์ฑ ์์ ํ๋๊ทธ
@@ -145,7 +144,7 @@ export const TextInputComponent: React.FC = ({
if (isInteractive && onFormDataChange && component.columnName) {
console.log("๐ formData ์
๋ฐ์ดํธ:", component.columnName, generatedValue);
onFormDataChange(component.columnName, generatedValue);
-
+
// ์ฑ๋ฒ ๊ท์น ID๋ ํจ๊ป ์ ์ฅ (์ ์ฅ ์์ ์ ์ค์ ํ ๋นํ๊ธฐ ์ํจ)
if (testAutoGeneration.type === "numbering_rule" && testAutoGeneration.options?.numberingRuleId) {
const ruleIdKey = `${component.columnName}_numberingRuleId`;
@@ -181,12 +180,12 @@ export const TextInputComponent: React.FC = ({
// width๋ ํญ์ 100%๋ก ๊ณ ์ (๋ถ๋ชจ ์ปจํ
์ด๋๊ฐ gridColumns๋ก ํฌ๊ธฐ ์ ์ด)
width: "100%",
// ์จ๊น ๊ธฐ๋ฅ: ํธ์ง ๋ชจ๋์์๋ง ์ฐํ๊ฒ ํ์
- ...(isHidden &&
- isDesignMode && {
- opacity: 0.4,
- backgroundColor: "hsl(var(--muted))",
- pointerEvents: "auto",
- }),
+ ...(isHidden &&
+ isDesignMode && {
+ opacity: 0.4,
+ backgroundColor: "hsl(var(--muted))",
+ pointerEvents: "auto",
+ }),
};
// ๋์์ธ ๋ชจ๋ ์คํ์ผ
@@ -361,7 +360,7 @@ export const TextInputComponent: React.FC = ({
{/* ๋ผ๋ฒจ ๋ ๋๋ง */}
{component.label && component.style?.labelDisplay !== false && (
-