# 선택 항목 상세입력 컴포넌트 - 완성 가이드

## 📦 구현 완료 사항

### ✅ 1. Zustand 스토어 생성 (modalDataStore)
- 파일: `frontend/stores/modalDataStore.ts`
- 기능:
  - 모달 간 데이터 전달 관리
  - `setData()`: 데이터 저장
  - `getData()`: 데이터 조회
  - `clearData()`: 데이터 정리
  - `updateItemData()`: 항목별 추가 데이터 업데이트

### ✅ 2. SelectedItemsDetailInput 컴포넌트 생성
- 디렉토리: `frontend/lib/registry/components/selected-items-detail-input/`
- 파일들:
  - `types.ts`: 타입 정의
  - `SelectedItemsDetailInputComponent.tsx`: 메인 컴포넌트
  - `SelectedItemsDetailInputConfigPanel.tsx`: 설정 패널
  - `SelectedItemsDetailInputRenderer.tsx`: 렌더러
  - `index.ts`: 컴포넌트 정의
  - `README.md`: 사용 가이드

### ✅ 3. 컴포넌트 기능
- 전달받은 원본 데이터 표시 (읽기 전용)
- 각 항목별 추가 입력 필드 제공
- Grid/Table 레이아웃 및 Card 레이아웃 지원
- 6가지 입력 타입 지원 (text, number, date, select, checkbox, textarea)
- 필수 입력 검증
- 항목 삭제 기능

### ✅ 4. 설정 패널 기능
- 데이터 소스 ID 설정
- 저장 대상 테이블 선택 (검색 가능한 Combobox)
- 표시할 원본 데이터 컬럼 선택
- 추가 입력 필드 정의 (필드명, 라벨, 타입, 필수 여부 등)
- 레이아웃 모드 선택 (Grid/Card)
- 옵션 설정 (번호 표시, 삭제 허용, 비활성화)

---

## 🚧 남은 작업 (구현 필요)

### 1. TableList에서 선택된 행 데이터를 스토어에 저장

**필요한 수정 파일:**
- `frontend/lib/registry/components/table-list/TableListComponent.tsx`

**구현 방법:**
```typescript
import { useModalDataStore } from "@/stores/modalDataStore";

// TableList 컴포넌트 내부
const setModalData = useModalDataStore((state) => state.setData);

// 선택된 행이 변경될 때마다 스토어에 저장
useEffect(() => {
  if (selectedRows.length > 0) {
    const modalDataItems = selectedRows.map((row) => ({
      id: row[primaryKeyColumn] || row.id,
      originalData: row,
      additionalData: {},
    }));
    
    // 컴포넌트 ID를 키로 사용하여 저장
    setModalData(component.id || "default", modalDataItems);
    
    console.log("📦 [TableList] 선택된 데이터 저장:", modalDataItems);
  }
}, [selectedRows, component.id, setModalData]);
```

**참고:**
- `selectedRows`: TableList의 체크박스로 선택된 행들
- `component.id`: 컴포넌트 고유 ID
- 이 ID가 SelectedItemsDetailInput의 `dataSourceId`와 일치해야 함

---

### 2. ButtonPrimary에 'openModalWithData' 액션 타입 추가

**필요한 수정 파일:**
- `frontend/lib/registry/components/button-primary/types.ts`
- `frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx`
- `frontend/lib/registry/components/button-primary/ButtonPrimaryConfigPanel.tsx`

#### A. types.ts 수정

```typescript
export interface ButtonPrimaryConfig extends ComponentConfig {
  action?: {
    type: 
      | "save" 
      | "delete" 
      | "popup" 
      | "navigate" 
      | "custom" 
      | "openModalWithData"; // 🆕 새 액션 타입
    
    // 기존 필드들...
    
    // 🆕 모달 데이터 전달용 필드
    targetScreenId?: number; // 열릴 모달 화면 ID
    dataSourceId?: string; // 데이터를 전달할 컴포넌트 ID
  };
}
```

#### B. ButtonPrimaryComponent.tsx 수정

```typescript
import { useModalDataStore } from "@/stores/modalDataStore";

// 컴포넌트 내부
const modalData = useModalDataStore((state) => state.getData);

// handleClick 함수 수정
const handleClick = async () => {
  // ... 기존 코드 ...
  
  // openModalWithData 액션 처리
  if (processedConfig.action?.type === "openModalWithData") {
    const { targetScreenId, dataSourceId } = processedConfig.action;
    
    if (!targetScreenId) {
      toast.error("대상 화면이 설정되지 않았습니다.");
      return;
    }
    
    if (!dataSourceId) {
      toast.error("데이터 소스가 설정되지 않았습니다.");
      return;
    }
    
    // 데이터 확인
    const data = modalData(dataSourceId);
    
    if (!data || data.length === 0) {
      toast.warning("전달할 데이터가 없습니다. 먼저 항목을 선택해주세요.");
      return;
    }
    
    console.log("📦 [ButtonPrimary] 데이터와 함께 모달 열기:", {
      targetScreenId,
      dataSourceId,
      dataCount: data.length,
    });
    
    // 모달 열기 (기존 popup 액션과 동일)
    toast.success(`${data.length}개 항목을 전달합니다.`);
    
    // TODO: 실제 모달 열기 로직 (popup 액션 참고)
    window.open(`/screens/${targetScreenId}`, "_blank");
    
    return;
  }
  
  // ... 기존 액션 처리 코드 ...
};
```

#### C. ButtonPrimaryConfigPanel.tsx 수정

설정 패널에 openModalWithData 액션 설정 UI 추가:

```typescript
{config.action?.type === "openModalWithData" && (
  <div className="mt-4 space-y-4 rounded-lg border bg-muted/50 p-4">
    <h4 className="text-sm font-medium">데이터 전달 설정</h4>
    
    {/* 대상 화면 선택 */}
    <div>
      <Label htmlFor="target-screen">열릴 모달 화면</Label>
      <Popover open={screenOpen} onOpenChange={setScreenOpen}>
        <PopoverTrigger asChild>
          <Button variant="outline" role="combobox" className="w-full justify-between">
            {config.action?.targetScreenId
              ? screens.find((s) => s.id === config.action?.targetScreenId)?.name || "화면 선택"
              : "화면 선택"}
            <ChevronsUpDown className="ml-2 h-4 w-4" />
          </Button>
        </PopoverTrigger>
        <PopoverContent>
          {/* 화면 목록 표시 */}
        </PopoverContent>
      </Popover>
    </div>
    
    {/* 데이터 소스 ID 입력 */}
    <div>
      <Label htmlFor="data-source-id">데이터 소스 ID</Label>
      <Input
        id="data-source-id"
        value={config.action?.dataSourceId || ""}
        onChange={(e) =>
          updateActionConfig("dataSourceId", e.target.value)
        }
        placeholder="table-list-123"
      />
      <p className="text-xs text-gray-500 mt-1">
        💡 데이터를 전달할 컴포넌트의 ID (예: TableList의 ID)
      </p>
    </div>
  </div>
)}
```

---

### 3. 저장 기능 구현

**방법 1: 기존 save 액션 활용**

SelectedItemsDetailInput의 데이터는 자동으로 `formData`에 포함되므로, 기존 save 액션을 그대로 사용할 수 있습니다:

```typescript
// formData 구조
{
  "selected-items-component-id": [
    {
      id: "SALE-003",
      originalData: { item_code: "SALE-003", ... },
      additionalData: { customer_item_code: "ABC-001", unit_price: 50, ... }
    },
    // ... 더 많은 항목들
  ]
}
```

백엔드에서 이 데이터를 받아서 각 항목을 개별 INSERT하면 됩니다.

**방법 2: 전용 save 로직 추가**

더 나은 UX를 위해 전용 저장 로직을 추가할 수 있습니다:

```typescript
// ButtonPrimary의 save 액션에서
if (config.action?.type === "save") {
  // formData에서 SelectedItemsDetailInput 데이터 찾기
  const selectedItemsKey = Object.keys(formData).find(
    (key) => Array.isArray(formData[key]) && formData[key][0]?.originalData
  );
  
  if (selectedItemsKey) {
    const items = formData[selectedItemsKey] as ModalDataItem[];
    
    // 저장할 데이터 변환
    const dataToSave = items.map((item) => ({
      ...item.originalData,
      ...item.additionalData,
    }));
    
    // 백엔드 API 호출
    const response = await apiClient.post(`/api/table-data/${targetTable}`, {
      data: dataToSave,
      batchInsert: true,
    });
    
    if (response.data.success) {
      toast.success(`${dataToSave.length}개 항목이 저장되었습니다.`);
      onClose?.();
    }
  }
}
```

---

## 🎯 통합 테스트 시나리오

### 시나리오: 수주 등록 - 품목 상세 입력

#### 1단계: 화면 구성

**[모달 1] 품목 선택 화면 (screen_id: 100)**

- TableList 컴포넌트
  - ID: `item-selection-table`
  - multiSelect: `true`
  - selectedTable: `item_info`
  - columns: 품목코드, 품목명, 규격, 단위, 단가

- ButtonPrimary 컴포넌트
  - text: "다음 (상세정보 입력)"
  - action.type: `openModalWithData`
  - action.targetScreenId: `101` (두 번째 모달)
  - action.dataSourceId: `item-selection-table`

**[모달 2] 상세 입력 화면 (screen_id: 101)**

- SelectedItemsDetailInput 컴포넌트
  - ID: `selected-items-detail`
  - dataSourceId: `item-selection-table`
  - displayColumns: `["item_code", "item_name", "spec", "unit"]`
  - additionalFields:
    ```json
    [
      { "name": "customer_item_code", "label": "거래처 품번", "type": "text" },
      { "name": "customer_item_name", "label": "거래처 품명", "type": "text" },
      { "name": "year", "label": "연도", "type": "select", "options": [...] },
      { "name": "currency", "label": "통화", "type": "select", "options": [...] },
      { "name": "unit_price", "label": "단가", "type": "number", "required": true },
      { "name": "quantity", "label": "수량", "type": "number", "required": true }
    ]
    ```
  - targetTable: `sales_detail`
  - layout: `grid`

- ButtonPrimary 컴포넌트 (저장)
  - text: "저장"
  - action.type: `save`
  - action.targetTable: `sales_detail`

#### 2단계: 테스트 절차

1. [모달 1] 품목 선택 화면 열기
2. TableList에서 3개 품목 체크박스 선택
3. "다음" 버튼 클릭
   - ✅ modalDataStore에 3개 항목 저장 확인 (콘솔 로그)
   - ✅ 모달 2가 열림
4. [모달 2] SelectedItemsDetailInput에 3개 항목 자동 표시 확인
   - ✅ 원본 데이터 (품목코드, 품목명, 규격, 단위) 표시
   - ✅ 추가 입력 필드 (거래처 품번, 단가, 수량 등) 빈 상태
5. 각 항목별로 추가 정보 입력
   - 거래처 품번: "ABC-001", "ABC-002", "ABC-003"
   - 단가: 50, 200, 3000
   - 수량: 100, 50, 200
6. "저장" 버튼 클릭
   - ✅ formData에 전체 데이터 포함 확인
   - ✅ 백엔드 API 호출
   - ✅ 저장 성공 토스트 메시지
   - ✅ 모달 닫힘

#### 3단계: 데이터 검증

데이터베이스에 다음과 같이 저장되어야 합니다:

```sql
SELECT * FROM sales_detail;
-- 결과:
-- item_code | item_name | spec | unit | customer_item_code | unit_price | quantity
-- SALE-003  | 와셔 M8   | M8   | EA   | ABC-001            | 50         | 100
-- SALE-005  | 육각 볼트 | M10  | EA   | ABC-002            | 200        | 50
-- SIL-003   | 실리콘    | 325  | kg   | ABC-003            | 3000       | 200
```

---

## 📚 추가 참고 자료

### 관련 파일 위치

- 스토어: `frontend/stores/modalDataStore.ts`
- 컴포넌트: `frontend/lib/registry/components/selected-items-detail-input/`
- TableList: `frontend/lib/registry/components/table-list/`
- ButtonPrimary: `frontend/lib/registry/components/button-primary/`

### 디버깅 팁

콘솔에서 다음 명령어로 상태 확인:

```javascript
// 모달 데이터 확인
__MODAL_DATA_STORE__.getState().dataRegistry

// 컴포넌트 등록 확인
__COMPONENT_REGISTRY__.get("selected-items-detail-input")

// TableList 선택 상태 확인
// (TableList 컴포넌트 내부에 로그 추가 필요)
```

### 예상 문제 및 해결

1. **데이터가 전달되지 않음**
   - dataSourceId가 정확히 일치하는지 확인
   - modalDataStore에 데이터가 저장되었는지 콘솔 로그 확인

2. **컴포넌트가 표시되지 않음**
   - `frontend/lib/registry/components/index.ts`에 import 추가되었는지 확인
   - 브라우저 새로고침 후 재시도

3. **저장이 안 됨**
   - formData에 데이터가 포함되어 있는지 확인
   - 백엔드 API 응답 확인
   - targetTable이 올바른지 확인

---

## ✅ 완료 체크리스트

- [x] Zustand 스토어 생성 (modalDataStore)
- [x] SelectedItemsDetailInput 컴포넌트 생성
- [x] 컴포넌트 렌더링 로직 구현
- [x] 설정 패널 구현
- [ ] TableList에서 선택된 데이터를 스토어에 저장
- [ ] ButtonPrimary에 openModalWithData 액션 추가
- [ ] 저장 기능 구현
- [ ] 통합 테스트
- [ ] 사용자 매뉴얼 작성

---

## 🚀 다음 단계

1. TableList 컴포넌트에 modalDataStore 연동 추가
2. ButtonPrimary에 openModalWithData 액션 구현
3. 수주 등록 화면에서 실제 테스트
4. 문제 발견 시 디버깅 및 수정
5. 문서 업데이트 및 배포

**예상 소요 시간**: 2~3시간

