fix: Improve number and slider input handling in V2Input and SplitPanelLayoutComponent

- Enhanced V2Input to convert string values from the database to numbers for number and slider inputs, ensuring correct display and functionality.
- Updated primary key retrieval logic in SplitPanelLayoutComponent to prioritize actual DB id values, improving data integrity.
- Simplified primary key handling by removing unnecessary checks and ensuring consistent usage of id fields.
- Improved user feedback in the SplitPanelLayoutComponent with clearer console logs for save operations and item selections.
This commit is contained in:
kjs 2026-02-12 16:07:36 +09:00
parent 505930b3ec
commit d0ebb82f90
2 changed files with 47 additions and 20 deletions

View File

@ -771,9 +771,15 @@ export const V2Input = forwardRef<HTMLDivElement, V2InputProps>((props, ref) =>
);
case "number":
// DB에서 문자열("325")로 반환되는 경우도 숫자로 변환하여 표시
const numValue = typeof displayValue === "number"
? displayValue
: (displayValue !== undefined && displayValue !== null && displayValue !== "" && !isNaN(Number(displayValue)))
? Number(displayValue)
: undefined;
return (
<NumberInput
value={typeof displayValue === "number" ? displayValue : undefined}
value={numValue}
onChange={(v) => {
setAutoGeneratedValue(null);
onChange?.(v ?? 0);
@ -802,9 +808,15 @@ export const V2Input = forwardRef<HTMLDivElement, V2InputProps>((props, ref) =>
);
case "slider":
// DB에서 문자열로 반환되는 경우도 숫자로 변환
const sliderValue = typeof displayValue === "number"
? displayValue
: (displayValue !== undefined && displayValue !== null && displayValue !== "" && !isNaN(Number(displayValue)))
? Number(displayValue)
: (config.min ?? 0);
return (
<SliderInput
value={typeof displayValue === "number" ? displayValue : (config.min ?? 0)}
value={sliderValue}
onChange={(v) => {
setAutoGeneratedValue(null);
onChange?.(v);

View File

@ -2105,22 +2105,16 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
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];
// Primary Key 찾기 - 실제 DB의 id 컬럼 값을 우선 사용
let primaryKeyValue = item.id || item.ID;
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 {
// id가 없으면 sourceColumn 시도, 마지막으로 첫 번째 키
const sourceColumn = componentConfig.leftPanel?.itemAddConfig?.sourceColumn || "id";
primaryKeyValue = item[sourceColumn];
if (primaryKeyValue === undefined || primaryKeyValue === null) {
const firstKey = Object.keys(item)[0];
primaryKeyName = firstKey;
primaryKeyValue = item[firstKey];
}
}
@ -2147,7 +2141,6 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
console.log("✅ [SplitPanel] 좌측 수정 모달 화면 열기:", {
screenId: editButtonConfig.modalScreenId,
tableName: leftTableName,
primaryKeyName,
primaryKeyValue,
});
return;
@ -2282,9 +2275,8 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
return;
}
// Primary Key 찾기
const sourceColumn = componentConfig.leftPanel?.itemAddConfig?.sourceColumn || "id";
const primaryKey = selectedLeftItem[sourceColumn] || selectedLeftItem.id || selectedLeftItem.ID;
// Primary Key 찾기 - 실제 DB의 id 컬럼 값을 사용 (sourceColumn은 관계 연결용이므로 PK로 사용하지 않음)
const primaryKey = selectedLeftItem.id || selectedLeftItem.ID;
if (!primaryKey) {
toast({
@ -2307,7 +2299,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
cleanData.company_code = companyCode;
}
console.log("📝 [SplitPanel] 커스텀 우측 패널 저장:", { tableName, sourceColumn, primaryKey, data: cleanData });
console.log("📝 [SplitPanel] 커스텀 우측 패널 저장:", { tableName, primaryKey, data: cleanData });
const response = await dataApi.updateRecord(tableName, primaryKey, cleanData);
@ -2529,7 +2521,8 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
if (deleteModalPanel === "left") {
loadLeftData();
// 삭제된 항목이 선택되어 있었으면 선택 해제
if (selectedLeftItem && selectedLeftItem[sourceColumn] === primaryKey) {
const deletedId = deleteModalItem?.id || deleteModalItem?.ID;
if (selectedLeftItem && (selectedLeftItem.id === deletedId || selectedLeftItem.ID === deletedId)) {
setSelectedLeftItem(null);
setRightData(null);
}
@ -2968,6 +2961,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
const displayHeight = isResizingComp && resizeSize ? resizeSize.height : (comp.size?.height || 100);
// 컴포넌트 데이터를 DynamicComponentRenderer 형식으로 변환
// componentConfig의 주요 속성을 최상위로 펼침 (일반 화면의 overrides 플래트닝과 동일)
const componentData = {
id: comp.id,
type: "component" as const,
@ -2977,6 +2971,11 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
size: { width: displayWidth, height: displayHeight },
componentConfig: comp.componentConfig || {},
style: comp.style || {},
// 파일 업로드/미디어 등이 component.tableName, component.columnName을 직접 참조하므로 펼침
tableName: comp.componentConfig?.tableName,
columnName: comp.componentConfig?.columnName,
webType: comp.componentConfig?.webType,
inputType: comp.inputType || comp.componentConfig?.inputType,
};
if (isDesignMode) {
@ -3981,6 +3980,15 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
})()
) : componentConfig.rightPanel?.displayMode === "custom" ? (
// 🆕 커스텀 모드: 패널 안에 자유롭게 컴포넌트 배치
// 실행 모드에서 좌측 미선택 시 안내 메시지 표시
!isDesignMode && !selectedLeftItem ? (
<div className="flex h-full items-center justify-center">
<div className="text-muted-foreground text-center text-sm">
<p className="mb-2"> </p>
<p className="text-xs"> </p>
</div>
</div>
) : (
<div
className="relative h-full w-full"
data-split-panel-container="true"
@ -4002,6 +4010,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
const displayHeight = isResizingComp && resizeSize ? resizeSize.height : (comp.size?.height || 100);
// 컴포넌트 데이터를 DynamicComponentRenderer 형식으로 변환
// componentConfig의 주요 속성을 최상위로 펼침 (일반 화면의 overrides 플래트닝과 동일)
const componentData = {
id: comp.id,
type: "component" as const,
@ -4011,6 +4020,11 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
size: { width: displayWidth, height: displayHeight },
componentConfig: comp.componentConfig || {},
style: comp.style || {},
// 파일 업로드/미디어 등이 component.tableName, component.columnName을 직접 참조하므로 펼침
tableName: comp.componentConfig?.tableName,
columnName: comp.componentConfig?.columnName,
webType: comp.componentConfig?.webType,
inputType: comp.inputType || comp.componentConfig?.inputType,
};
if (isDesignMode) {
@ -4201,6 +4215,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
</div>
)}
</div>
)
) : isLoadingRight ? (
// 로딩 중
<div className="flex h-full items-center justify-center">