feat: Enhance form validation and modal handling in various components

- Added `isInModal` prop to `ScreenModal` and `InteractiveScreenViewerDynamic` for improved modal context awareness.
- Implemented `isFieldEmpty` and `checkAllRequiredFieldsFilled` utility functions to validate required fields in forms.
- Updated `SaveModal` and `ButtonPrimaryComponent` to disable save actions when required fields are missing, enhancing user feedback.
- Introduced error messages for required fields in modals to guide users in completing necessary inputs.

Made-with: Cursor
This commit is contained in:
2026-02-27 18:11:59 +09:00
parent dc04bd162a
commit 83437e76dd
6 changed files with 151 additions and 16 deletions

View File

@@ -27,6 +27,7 @@ import { useScreenContextOptional } from "@/contexts/ScreenContext";
import { useSplitPanelContext, SplitPanelPosition } from "@/contexts/SplitPanelContext";
import { applyMappingRules } from "@/lib/utils/dataMapping";
import { apiClient } from "@/lib/api/client";
import { checkAllRequiredFieldsFilled } from "@/lib/utils/formValidation";
export interface ButtonPrimaryComponentProps extends ComponentRendererProps {
config?: ButtonPrimaryConfig;
@@ -1258,9 +1259,16 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
}
}
// 🆕 최종 비활성화 상태 (설정 + 조건부 비활성화 + 행 선택 필수)
// 모달 내 저장 버튼: 필수 필드 미입력 시 비활성화
const isInModalContext = (props as any).isInModal === true;
const isSaveAction = processedConfig.action?.type === "save";
const isRequiredFieldsMissing = isSaveAction && isInModalContext && allComponents
? !checkAllRequiredFieldsFilled(allComponents, formData || {})
: false;
// 🆕 최종 비활성화 상태 (설정 + 조건부 비활성화 + 행 선택 필수 + 필수 필드 미입력)
const finalDisabled =
componentConfig.disabled || isOperationButtonDisabled || isRowSelectionDisabled || statusLoading;
componentConfig.disabled || isOperationButtonDisabled || isRowSelectionDisabled || statusLoading || isRequiredFieldsMissing;
// 공통 버튼 스타일
// 🔧 component.style에서 background/backgroundColor 충돌 방지
@@ -1317,6 +1325,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
<button
type={componentConfig.actionType || "button"}
disabled={finalDisabled}
title={isRequiredFieldsMissing ? "필수 입력 항목을 모두 채워주세요" : undefined}
className="transition-colors transition-transform duration-150 hover:opacity-90 active:scale-95"
style={buttonElementStyle}
onClick={handleClick}

View File

@@ -28,6 +28,7 @@ import { useSplitPanelContext, SplitPanelPosition } from "@/contexts/SplitPanelC
import { applyMappingRules } from "@/lib/utils/dataMapping";
import { apiClient } from "@/lib/api/client";
import { V2ErrorBoundary, v2EventBus, V2_EVENTS } from "@/lib/v2-core";
import { checkAllRequiredFieldsFilled } from "@/lib/utils/formValidation";
export interface ButtonPrimaryComponentProps extends ComponentRendererProps {
config?: ButtonPrimaryConfig;
@@ -1216,15 +1217,6 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
effectiveFormData = { ...splitPanelParentData };
}
console.log("🔴 [ButtonPrimary] 저장 시 formData 디버그:", {
propsFormDataKeys: Object.keys(propsFormData),
screenContextFormDataKeys: Object.keys(screenContextFormData),
effectiveFormDataKeys: Object.keys(effectiveFormData),
process_code: effectiveFormData.process_code,
equipment_code: effectiveFormData.equipment_code,
fullData: JSON.stringify(effectiveFormData),
});
const context: ButtonActionContext = {
formData: effectiveFormData,
originalData: originalData, // 🔧 빈 객체 대신 undefined 유지 (UPDATE 판단에 사용)
@@ -1381,9 +1373,16 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
}
}
// 🆕 최종 비활성화 상태 (설정 + 조건부 비활성화 + 행 선택 필수)
// 모달 내 저장 버튼: 필수 필드 미입력 시 비활성화
const isInModalContext = (props as any).isInModal === true;
const isSaveAction = processedConfig.action?.type === "save";
const isRequiredFieldsMissing = isSaveAction && isInModalContext && allComponents
? !checkAllRequiredFieldsFilled(allComponents, formData || {})
: false;
// 최종 비활성화 상태 (설정 + 조건부 비활성화 + 행 선택 필수 + 필수 필드 미입력)
const finalDisabled =
componentConfig.disabled || isOperationButtonDisabled || isRowSelectionDisabled || statusLoading;
componentConfig.disabled || isOperationButtonDisabled || isRowSelectionDisabled || statusLoading || isRequiredFieldsMissing;
// 공통 버튼 스타일
// 🔧 component.style에서 background/backgroundColor 충돌 방지 (width/height는 허용)
@@ -1470,6 +1469,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
<button
type={componentConfig.actionType || "button"}
disabled={finalDisabled}
title={isRequiredFieldsMissing ? "필수 입력 항목을 모두 채워주세요" : undefined}
className="transition-colors transition-transform duration-150 hover:opacity-90 active:scale-95"
style={buttonElementStyle}
onClick={handleClick}