ERP-node/frontend/lib/registry/components/selected-items-detail-input
kjs 28ef7e1226 fix: Enhance error handling and validation messages in form data operations
- Integrated `formatPgError` utility to provide user-friendly error messages based on PostgreSQL error codes during form data operations.
- Updated error responses in `saveFormData`, `saveFormDataEnhanced`, `updateFormData`, and `updateFormDataPartial` to include specific messages based on the company context.
- Improved error handling in the frontend components to display relevant error messages from the server response, ensuring users receive clear feedback on save operations.
- Enhanced the required field validation by incorporating NOT NULL metadata checks across various components, improving the accuracy of form submissions.

These changes improve the overall user experience by providing clearer error messages and ensuring that required fields are properly validated based on both manual settings and database constraints.
2026-03-10 14:47:05 +09:00
..
CalculationBuilder.tsx 화면 복사기능 수정 2025-11-19 10:03:38 +09:00
README.md feat: 선택항목 상세입력 컴포넌트 그룹별 독립 입력 구조로 개선 2025-11-18 09:56:49 +09:00
SelectedItemsDetailInputComponent.tsx fix: Enhance error handling and validation messages in form data operations 2026-03-10 14:47:05 +09:00
SelectedItemsDetailInputConfigPanel.tsx feat: Enhance SelectedItemsDetailInputComponent with sourceKeyField auto-detection and FK mapping 2026-02-26 16:39:06 +09:00
SelectedItemsDetailInputRenderer.tsx 선택항목 상게입력 컴포넌트 구현 2025-11-17 12:23:45 +09:00
index.ts 선택항목 상게입력 컴포넌트 구현 2025-11-17 12:23:45 +09:00
types.ts feat: Enhance SelectedItemsDetailInputComponent with sourceKeyField auto-detection and FK mapping 2026-02-26 16:39:06 +09:00

README.md

SelectedItemsDetailInput 컴포넌트

선택된 항목들의 상세 정보를 입력하는 컴포넌트입니다.

개요

이 컴포넌트는 다음과 같은 흐름에서 사용됩니다:

  1. 첫 번째 모달: TableList에서 여러 항목 선택 (체크박스)
  2. 버튼 클릭: "다음" 버튼 클릭 → 선택된 데이터를 modalDataStore에 저장
  3. 두 번째 모달: SelectedItemsDetailInput이 자동으로 데이터를 읽어와서 표시
  4. 추가 입력: 각 항목별로 추가 정보 입력 (거래처 품번, 단가 등)
  5. 저장: 모든 데이터를 백엔드로 일괄 전송

주요 기능

  • 전달받은 원본 데이터 표시 (읽기 전용)
  • 각 항목별 추가 입력 필드 제공
  • Grid/Table 레이아웃 또는 Card 레이아웃 지원
  • 필드별 타입 지정 (text, number, date, select, checkbox, textarea)
  • 필수 입력 검증
  • 항목 삭제 기능 (선택적)

사용 방법

1단계: 첫 번째 모달 (품목 선택)

// TableList 컴포넌트 설정
{
  type: "table-list",
  config: {
    selectedTable: "item_info",
    multiSelect: true, // 다중 선택 활성화
    columns: [
      { columnName: "item_code", label: "품목코드" },
      { columnName: "item_name", label: "품목명" },
      { columnName: "spec", label: "규격" },
      { columnName: "unit", label: "단위" },
      { columnName: "price", label: "단가" }
    ]
  }
}

// "다음" 버튼 설정
{
  type: "button-primary",
  config: {
    text: "다음 (상세정보 입력)",
    action: {
      type: "openModalWithData", // 새 액션 타입
      targetScreenId: "123", // 두 번째 모달 화면 ID
      dataSourceId: "table-list-456" // TableList 컴포넌트 ID
    }
  }
}

2단계: 두 번째 모달 (상세 입력)

// SelectedItemsDetailInput 컴포넌트 설정
{
  type: "selected-items-detail-input",
  config: {
    dataSourceId: "table-list-456", // 첫 번째 모달의 TableList ID
    targetTable: "sales_detail", // 최종 저장 테이블
    layout: "grid", // 테이블 형식
    
    // 전달받은 원본 데이터 중 표시할 컬럼
    displayColumns: ["item_code", "item_name", "spec", "unit"],
    
    // 추가 입력 필드 정의
    additionalFields: [
      {
        name: "customer_item_code",
        label: "거래처 품번",
        type: "text",
        required: false
      },
      {
        name: "customer_item_name",
        label: "거래처 품명",
        type: "text",
        required: false
      },
      {
        name: "year",
        label: "연도",
        type: "select",
        required: true,
        options: [
          { value: "2024", label: "2024년" },
          { value: "2025", label: "2025년" }
        ]
      },
      {
        name: "currency",
        label: "통화단위",
        type: "select",
        required: true,
        options: [
          { value: "KRW", label: "KRW (원)" },
          { value: "USD", label: "USD (달러)" }
        ]
      },
      {
        name: "unit_price",
        label: "단가",
        type: "number",
        required: true
      },
      {
        name: "quantity",
        label: "수량",
        type: "number",
        required: true
      }
    ],
    
    showIndex: true,
    allowRemove: true // 항목 삭제 허용
  }
}

3단계: 저장 버튼

{
  type: "button-primary",
    config: {
    text: "저장",
    action: {
      type: "save",
      targetTable: "sales_detail",
      // formData에 selected_items 데이터가 자동으로 포함됨
    }
  }
}

데이터 구조

전달되는 데이터 형식

const modalData: ModalDataItem[] = [
  {
    id: "SALE-003", // 항목 ID
    originalData: { // 원본 데이터 (TableList에서 선택한 행)
      item_code: "SALE-003",
      item_name: "와셔 M8",
      spec: "M8",
      unit: "EA",
      price: 50
    },
    additionalData: { // 사용자가 입력한 추가 데이터
      customer_item_code: "ABC-001",
      customer_item_name: "와셔",
      year: "2025",
      currency: "KRW",
      unit_price: 50,
      quantity: 100
    }
  },
  // ... 더 많은 항목들
];

설정 옵션

속성 타입 기본값 설명
dataSourceId string - 데이터를 전달하는 컴포넌트 ID (필수)
displayColumns string[] [] 표시할 원본 데이터 컬럼명
additionalFields AdditionalFieldDefinition[] [] 추가 입력 필드 정의
targetTable string - 최종 저장 대상 테이블
layout "grid" | "card" "grid" 레이아웃 모드
showIndex boolean true 항목 번호 표시 여부
allowRemove boolean false 항목 삭제 허용 여부
emptyMessage string "전달받은 데이터가 없습니다." 빈 상태 메시지
disabled boolean false 비활성화 여부
readonly boolean false 읽기 전용 여부

추가 필드 정의

interface AdditionalFieldDefinition {
  name: string;          // 필드명 (컬럼명)
  label: string;         // 필드 라벨
  type: "text" | "number" | "date" | "select" | "checkbox" | "textarea";
  required?: boolean;    // 필수 입력 여부
  placeholder?: string;  // 플레이스홀더
  defaultValue?: any;    // 기본값
  options?: Array<{ label: string; value: string }>; // 선택 옵션 (select 타입일 때)
  validation?: {         // 검증 규칙
    min?: number;
    max?: number;
    minLength?: number;
    maxLength?: number;
    pattern?: string;
  };
}

실전 예시: 수주 등록 화면

시나리오

  1. 품목 선택 모달에서 여러 품목 선택
  2. "다음" 버튼 클릭
  3. 각 품목별로 거래처 정보, 단가, 수량 입력
  4. "저장" 버튼으로 일괄 저장

구현

// [모달 1] 품목 선택
<TableList id="item-selection-table" multiSelect={true} />
<Button action="openModalWithData" targetScreenId="detail-input-modal" dataSourceId="item-selection-table" />

// [모달 2] 상세 입력
<SelectedItemsDetailInput 
  dataSourceId="item-selection-table"
  displayColumns={["item_code", "item_name", "spec"]}
  additionalFields={[
    { name: "customer_item_code", label: "거래처 품번", type: "text" },
    { name: "unit_price", label: "단가", type: "number", required: true },
    { name: "quantity", label: "수량", type: "number", required: true }
  ]}
  targetTable="sales_detail"
/>
<Button action="save" />

주의사항

  1. dataSourceId 일치: 첫 번째 모달의 TableList ID와 두 번째 모달의 dataSourceId가 정확히 일치해야 합니다.
  2. 컬럼명 정확성: displayColumns와 additionalFields의 name은 실제 데이터베이스 컬럼명과 일치해야 합니다.
  3. 필수 필드 검증: required=true인 필드는 반드시 입력해야 저장이 가능합니다.
  4. 데이터 정리: 모달이 닫힐 때 modalDataStore의 데이터가 자동으로 정리됩니다.

향후 개선 사항

  • 일괄 수정 기능 (모든 항목에 같은 값 적용)
  • 엑셀 업로드로 일괄 입력
  • 조건부 필드 표시 (특정 조건에서만 필드 활성화)
  • 커스텀 검증 규칙
  • 실시간 계산 필드 (단가 × 수량 = 금액)