diff --git a/db/migrations/RUN_063_064_MIGRATION.md b/db/migrations/RUN_063_064_MIGRATION.md new file mode 100644 index 00000000..98ca3b90 --- /dev/null +++ b/db/migrations/RUN_063_064_MIGRATION.md @@ -0,0 +1,238 @@ +# 마이그레이션 063-064: 재고 관리 테이블 생성 + +## 목적 + +재고 현황 관리 및 입출고 이력 추적을 위한 테이블 생성 + +**테이블 타입관리 UI와 동일한 방식으로 생성됩니다.** + +### 생성되는 테이블 + +| 테이블명 | 설명 | 용도 | +|----------|------|------| +| `inventory_stock` | 재고 현황 | 품목+로트별 현재 재고 상태 | +| `inventory_history` | 재고 이력 | 입출고 트랜잭션 기록 | + +--- + +## 테이블 타입관리 UI 방식 특징 + +1. **기본 컬럼 자동 포함**: `id`, `created_date`, `updated_date`, `writer`, `company_code` +2. **데이터 타입 통일**: 날짜는 `TIMESTAMP`, 나머지는 `VARCHAR(500)` +3. **메타데이터 등록**: + - `table_labels`: 테이블 정보 + - `column_labels`: 컬럼 정보 (라벨, input_type, detail_settings) + - `table_type_columns`: 회사별 컬럼 타입 정보 + +--- + +## 테이블 구조 + +### 1. inventory_stock (재고 현황) + +| 컬럼명 | 타입 | input_type | 설명 | +|--------|------|------------|------| +| id | VARCHAR(500) | text | PK (자동생성) | +| created_date | TIMESTAMP | date | 생성일시 | +| updated_date | TIMESTAMP | date | 수정일시 | +| writer | VARCHAR(500) | text | 작성자 | +| company_code | VARCHAR(500) | text | 회사코드 | +| item_code | VARCHAR(500) | text | 품목코드 | +| lot_number | VARCHAR(500) | text | 로트번호 | +| warehouse_id | VARCHAR(500) | entity | 창고 (FK → warehouse_info) | +| location_code | VARCHAR(500) | text | 위치코드 | +| current_qty | VARCHAR(500) | number | 현재고량 | +| safety_qty | VARCHAR(500) | number | 안전재고 | +| last_in_date | TIMESTAMP | date | 최종입고일 | +| last_out_date | TIMESTAMP | date | 최종출고일 | + +### 2. inventory_history (재고 이력) + +| 컬럼명 | 타입 | input_type | 설명 | +|--------|------|------------|------| +| id | VARCHAR(500) | text | PK (자동생성) | +| created_date | TIMESTAMP | date | 생성일시 | +| updated_date | TIMESTAMP | date | 수정일시 | +| writer | VARCHAR(500) | text | 작성자 | +| company_code | VARCHAR(500) | text | 회사코드 | +| stock_id | VARCHAR(500) | text | 재고ID (FK) | +| item_code | VARCHAR(500) | text | 품목코드 | +| lot_number | VARCHAR(500) | text | 로트번호 | +| transaction_type | VARCHAR(500) | code | 구분 (IN/OUT) | +| transaction_date | TIMESTAMP | date | 일자 | +| quantity | VARCHAR(500) | number | 수량 | +| balance_qty | VARCHAR(500) | number | 재고량 | +| manager_id | VARCHAR(500) | text | 담당자ID | +| manager_name | VARCHAR(500) | text | 담당자명 | +| remark | VARCHAR(500) | text | 비고 | +| reference_type | VARCHAR(500) | text | 참조문서유형 | +| reference_id | VARCHAR(500) | text | 참조문서ID | +| reference_number | VARCHAR(500) | text | 참조문서번호 | + +--- + +## 실행 방법 + +### Docker 환경 (권장) + +```bash +# 재고 현황 테이블 +docker exec -i erp-node-db-1 psql -U postgres -d ilshin < db/migrations/063_create_inventory_stock.sql + +# 재고 이력 테이블 +docker exec -i erp-node-db-1 psql -U postgres -d ilshin < db/migrations/064_create_inventory_history.sql +``` + +### 로컬 PostgreSQL + +```bash +psql -U postgres -d ilshin -f db/migrations/063_create_inventory_stock.sql +psql -U postgres -d ilshin -f db/migrations/064_create_inventory_history.sql +``` + +### pgAdmin / DBeaver + +1. 각 SQL 파일 열기 +2. 전체 내용 복사 +3. SQL 쿼리 창에 붙여넣기 +4. 실행 (F5 또는 Execute) + +--- + +## 검증 방법 + +### 1. 테이블 생성 확인 + +```sql +SELECT table_name +FROM information_schema.tables +WHERE table_name IN ('inventory_stock', 'inventory_history'); +``` + +### 2. 메타데이터 등록 확인 + +```sql +-- table_labels +SELECT * FROM table_labels WHERE table_name IN ('inventory_stock', 'inventory_history'); + +-- column_labels +SELECT table_name, column_name, column_label, input_type, display_order +FROM column_labels +WHERE table_name IN ('inventory_stock', 'inventory_history') +ORDER BY table_name, display_order; + +-- table_type_columns +SELECT table_name, column_name, company_code, input_type, display_order +FROM table_type_columns +WHERE table_name IN ('inventory_stock', 'inventory_history') +ORDER BY table_name, display_order; +``` + +### 3. 샘플 데이터 확인 + +```sql +-- 재고 현황 +SELECT * FROM inventory_stock WHERE company_code = 'WACE'; + +-- 재고 이력 +SELECT * FROM inventory_history WHERE company_code = 'WACE' ORDER BY transaction_date; +``` + +--- + +## 화면에서 사용할 조회 쿼리 예시 + +### 재고 현황 그리드 (좌측) + +```sql +SELECT + s.item_code, + i.item_name, + i.size as specification, + i.unit, + s.lot_number, + w.warehouse_name, + s.location_code, + s.current_qty::numeric as current_qty, + s.safety_qty::numeric as safety_qty, + CASE + WHEN s.current_qty::numeric < s.safety_qty::numeric THEN '부족' + WHEN s.current_qty::numeric > s.safety_qty::numeric * 2 THEN '과다' + ELSE '정상' + END AS stock_status, + s.last_in_date, + s.last_out_date +FROM inventory_stock s +LEFT JOIN item_info i ON s.item_code = i.item_number AND s.company_code = i.company_code +LEFT JOIN warehouse_info w ON s.warehouse_id = w.id +WHERE s.company_code = 'WACE' +ORDER BY s.item_code, s.lot_number; +``` + +### 재고 이력 패널 (우측) + +```sql +SELECT + h.transaction_type, + h.transaction_date, + h.quantity, + h.balance_qty, + h.manager_name, + h.remark +FROM inventory_history h +WHERE h.item_code = 'A001' + AND h.lot_number = 'LOT-2024-001' + AND h.company_code = 'WACE' +ORDER BY h.transaction_date DESC, h.created_date DESC; +``` + +--- + +## 데이터 흐름 + +``` +[입고 발생] + │ + ├─→ inventory_history에 INSERT (+수량, 잔량) + │ + └─→ inventory_stock에 UPDATE (current_qty 증가, last_in_date 갱신) + +[출고 발생] + │ + ├─→ inventory_history에 INSERT (-수량, 잔량) + │ + └─→ inventory_stock에 UPDATE (current_qty 감소, last_out_date 갱신) +``` + +--- + +## 롤백 방법 (문제 발생 시) + +```sql +-- 테이블 삭제 +DROP TABLE IF EXISTS inventory_history; +DROP TABLE IF EXISTS inventory_stock; + +-- 메타데이터 삭제 +DELETE FROM column_labels WHERE table_name IN ('inventory_stock', 'inventory_history'); +DELETE FROM table_labels WHERE table_name IN ('inventory_stock', 'inventory_history'); +DELETE FROM table_type_columns WHERE table_name IN ('inventory_stock', 'inventory_history'); +``` + +--- + +## 관련 테이블 (마스터 데이터) + +| 테이블 | 역할 | 연결 컬럼 | +|--------|------|-----------| +| item_info | 품목 마스터 | item_number | +| warehouse_info | 창고 마스터 | id | +| warehouse_location | 위치 마스터 | location_code | + +--- + +**작성일**: 2025-12-09 +**영향 범위**: 재고 관리 시스템 +**생성 방식**: 테이블 타입관리 UI와 동일 + + diff --git a/docs/노드플로우_개선사항.md b/docs/노드플로우_개선사항.md index 85ae186b..e80a1a61 100644 --- a/docs/노드플로우_개선사항.md +++ b/docs/노드플로우_개선사항.md @@ -581,3 +581,4 @@ const result = await executeNodeFlow(flowId, { - 프론트엔드 플로우 API: `frontend/lib/api/nodeFlows.ts` + diff --git a/docs/메일발송_기능_사용_가이드.md b/docs/메일발송_기능_사용_가이드.md new file mode 100644 index 00000000..2ef68524 --- /dev/null +++ b/docs/메일발송_기능_사용_가이드.md @@ -0,0 +1,357 @@ +# 메일 발송 기능 사용 가이드 + +## 개요 + +노드 기반 제어관리 시스템을 통해 메일을 발송하는 방법을 설명합니다. +화면에서 데이터를 선택하고, 수신자를 지정하여 템플릿 기반의 메일을 발송할 수 있습니다. + +--- + +## 1. 사전 준비 + +### 1.1 메일 계정 등록 + +메일 발송을 위해 먼저 SMTP 계정을 등록해야 합니다. + +1. **관리자** > **메일관리** > **계정관리** 이동 +2. **새 계정 추가** 클릭 +3. SMTP 정보 입력: + - 계정명: 식별용 이름 (예: "회사 공식 메일") + - 이메일: 발신자 이메일 주소 + - SMTP 호스트: 메일 서버 주소 (예: smtp.gmail.com) + - SMTP 포트: 포트 번호 (예: 587) + - 보안: TLS/SSL 선택 + - 사용자명/비밀번호: SMTP 인증 정보 +4. **저장** 후 **테스트 발송**으로 동작 확인 + +--- + +## 2. 제어관리 설정 + +### 2.1 메일 발송 플로우 생성 + +**관리자** > **제어관리** > **플로우 관리**에서 새 플로우를 생성합니다. + +#### 기본 구조 + +``` +[테이블 소스] → [메일 발송] +``` + +#### 노드 구성 + +1. **테이블 소스 노드** 추가 + + - 데이터 소스: **컨텍스트 데이터** (화면에서 선택한 데이터 사용) + - 또는 **테이블 전체 데이터** (주의: 전체 데이터 건수만큼 메일 발송) + +2. **메일 발송 노드** 추가 + + - 노드 팔레트 > 외부 실행 > **메일 발송** 드래그 + +3. 두 노드 연결 (테이블 소스 → 메일 발송) + +--- + +### 2.2 메일 발송 노드 설정 + +메일 발송 노드를 클릭하면 우측에 속성 패널이 표시됩니다. + +#### 계정 탭 + +| 설정 | 설명 | +| -------------- | ----------------------------------- | +| 발송 계정 선택 | 사전에 등록한 메일 계정 선택 (필수) | + +#### 메일 탭 + +| 설정 | 설명 | +| -------------------- | ------------------------------------------------ | +| 수신자 컴포넌트 사용 | 체크 시 화면의 수신자 선택 컴포넌트 값 자동 사용 | +| 수신자 필드명 | 수신자 변수명 (기본: mailTo) | +| 참조 필드명 | 참조 변수명 (기본: mailCc) | +| 수신자 (To) | 직접 입력 또는 변수 사용 (예: `{{email}}`) | +| 참조 (CC) | 참조 수신자 | +| 숨은 참조 (BCC) | 숨은 참조 수신자 | +| 우선순위 | 높음 / 보통 / 낮음 | + +#### 본문 탭 + +| 설정 | 설명 | +| --------- | -------------------------------- | +| 제목 | 메일 제목 (변수 사용 가능) | +| 본문 형식 | 텍스트 (변수 태그 에디터) / HTML | +| 본문 내용 | 메일 본문 (변수 사용 가능) | + +#### 옵션 탭 + +| 설정 | 설명 | +| ----------- | ------------------- | +| 타임아웃 | 발송 제한 시간 (ms) | +| 재시도 횟수 | 실패 시 재시도 횟수 | + +--- + +### 2.3 변수 사용 방법 + +메일 제목과 본문에서 `{{변수명}}` 형식으로 데이터 필드를 참조할 수 있습니다. + +#### 텍스트 모드 (변수 태그 에디터) + +1. 본문 형식을 **텍스트 (변수 태그 에디터)** 선택 +2. 에디터에서 `@` 또는 `/` 키 입력 +3. 변수 목록에서 원하는 변수 선택 +4. 선택된 변수는 파란색 태그로 표시 + +#### HTML 모드 (직접 입력) + +```html +

주문 확인

+

안녕하세요 {{customerName}}님,

+

주문번호 {{orderNo}}의 주문이 완료되었습니다.

+

금액: {{totalAmount}}원

+``` + +#### 사용 가능한 변수 + +| 변수 | 설명 | +| ---------------- | ------------------------ | +| `{{timestamp}}` | 메일 발송 시점 | +| `{{sourceData}}` | 전체 소스 데이터 (JSON) | +| `{{필드명}}` | 테이블 소스의 각 컬럼 값 | + +--- + +## 3. 화면 구성 + +### 3.1 기본 구조 + +메일 발송 화면은 보통 다음과 같이 구성합니다: + +``` +[부모 화면: 데이터 목록] + ↓ (모달 열기 버튼) +[모달: 수신자 입력 + 발송 버튼] +``` + +### 3.2 수신자 선택 컴포넌트 배치 + +1. **화면관리**에서 모달 화면 편집 +2. 컴포넌트 팔레트 > **메일 수신자 선택** 드래그 +3. 컴포넌트 설정: + - 수신자 필드명: `mailTo` (메일 발송 노드와 일치) + - 참조 필드명: `mailCc` (메일 발송 노드와 일치) + +#### 수신자 선택 기능 + +- **내부 사용자**: 회사 직원 목록에서 검색/선택 +- **외부 이메일**: 직접 이메일 주소 입력 +- 여러 명 선택 가능 (쉼표로 구분) + +### 3.3 발송 버튼 설정 + +1. **버튼** 컴포넌트 추가 +2. 버튼 설정: + - 액션 타입: **제어 실행** + - 플로우 선택: 생성한 메일 발송 플로우 + - 데이터 소스: **자동** 또는 **폼 + 테이블 선택** + +--- + +## 4. 전체 흐름 예시 + +### 4.1 시나리오: 선택한 주문 건에 대해 고객에게 메일 발송 + +#### Step 1: 제어관리 플로우 생성 + +``` +[테이블 소스: 컨텍스트 데이터] + ↓ +[메일 발송] + - 계정: 회사 공식 메일 + - 수신자 컴포넌트 사용: 체크 + - 제목: [주문확인] {{orderNo}} 주문이 완료되었습니다 + - 본문: + 안녕하세요 {{customerName}}님, + + 주문번호 {{orderNo}}의 주문이 정상 처리되었습니다. + + - 상품명: {{productName}} + - 수량: {{quantity}} + - 금액: {{totalAmount}}원 + + 감사합니다. +``` + +#### Step 2: 부모 화면 (주문 목록) + +- 주문 데이터 테이블 +- "메일 발송" 버튼 + - 액션: 모달 열기 + - 모달 화면: 메일 발송 모달 + - 선택된 데이터 전달: 체크 + +#### Step 3: 모달 화면 (메일 발송) + +- 메일 수신자 선택 컴포넌트 + - 수신자 (To) 입력 + - 참조 (CC) 입력 +- "발송" 버튼 + - 액션: 제어 실행 + - 플로우: 메일 발송 플로우 + +#### Step 4: 실행 흐름 + +1. 사용자가 주문 목록에서 주문 선택 +2. "메일 발송" 버튼 클릭 → 모달 열림 +3. 수신자/참조 입력 +4. "발송" 버튼 클릭 +5. 제어 실행: + - 부모 화면 데이터 (orderNo, customerName 등) + 모달 폼 데이터 (mailTo, mailCc) 병합 + - 변수 치환 후 메일 발송 + +--- + +## 5. 데이터 소스별 동작 + +### 5.1 컨텍스트 데이터 (권장) + +- 화면에서 **선택한 데이터**만 사용 +- 선택한 건수만큼 메일 발송 + +| 선택 건수 | 메일 발송 수 | +| --------- | ------------ | +| 1건 | 1통 | +| 5건 | 5통 | +| 10건 | 10통 | + +### 5.2 테이블 전체 데이터 (주의) + +- 테이블의 **모든 데이터** 사용 +- 전체 건수만큼 메일 발송 + +| 테이블 데이터 | 메일 발송 수 | +| ------------- | ------------ | +| 100건 | 100통 | +| 1000건 | 1000통 | + +**주의사항:** + +- 대량 발송 시 SMTP 서버 rate limit 주의 +- 테스트 시 반드시 데이터 건수 확인 + +--- + +## 6. 문제 해결 + +### 6.1 메일이 발송되지 않음 + +1. **계정 설정 확인**: 메일관리 > 계정관리에서 테스트 발송 확인 +2. **수신자 확인**: 수신자 이메일 주소가 올바른지 확인 +3. **플로우 연결 확인**: 테이블 소스 → 메일 발송 노드가 연결되어 있는지 확인 + +### 6.2 변수가 치환되지 않음 + +1. **변수명 확인**: `{{변수명}}`에서 변수명이 테이블 컬럼명과 일치하는지 확인 +2. **데이터 소스 확인**: 테이블 소스 노드가 올바른 데이터를 가져오는지 확인 +3. **데이터 전달 확인**: 부모 화면 → 모달로 데이터가 전달되는지 확인 + +### 6.3 수신자 컴포넌트 값이 전달되지 않음 + +1. **필드명 일치 확인**: + - 수신자 컴포넌트의 필드명과 메일 발송 노드의 필드명이 일치해야 함 + - 기본값: `mailTo`, `mailCc` +2. **수신자 컴포넌트 사용 체크**: 메일 발송 노드에서 "수신자 컴포넌트 사용" 활성화 + +### 6.4 부모 화면 데이터가 메일에 포함되지 않음 + +1. **모달 열기 설정 확인**: "선택된 데이터 전달" 옵션 활성화 +2. **데이터 소스 설정 확인**: 발송 버튼의 데이터 소스가 "자동" 또는 "폼 + 테이블 선택"인지 확인 + +--- + +## 7. 고급 기능 + +### 7.1 조건부 메일 발송 + +조건 분기 노드를 사용하여 특정 조건에서만 메일을 발송할 수 있습니다. + +``` +[테이블 소스] + ↓ +[조건 분기: status === 'approved'] + ↓ (true) +[메일 발송: 승인 알림] +``` + +### 7.2 다중 수신자 처리 + +수신자 필드에 쉼표로 구분하여 여러 명에게 동시 발송: + +``` +{{managerEmail}}, {{teamLeadEmail}}, external@example.com +``` + +### 7.3 HTML 템플릿 활용 + +본문 형식을 HTML로 설정하면 풍부한 형식의 메일을 보낼 수 있습니다: + +```html + + + + + + +
+

주문 확인

+
+
+

안녕하세요 {{customerName}}님,

+

주문번호 {{orderNo}}의 주문이 완료되었습니다.

+ + + + + + + + + +
상품명{{productName}}
금액{{totalAmount}}원
+
+ + + +``` + +--- + +## 8. 체크리스트 + +메일 발송 기능 구현 시 확인 사항: + +- [ ] 메일 계정이 등록되어 있는가? +- [ ] 메일 계정 테스트 발송이 성공하는가? +- [ ] 제어관리에 메일 발송 플로우가 생성되어 있는가? +- [ ] 테이블 소스 노드의 데이터 소스가 올바르게 설정되어 있는가? +- [ ] 메일 발송 노드에서 계정이 선택되어 있는가? +- [ ] 수신자 컴포넌트 사용 시 필드명이 일치하는가? +- [ ] 변수명이 테이블 컬럼명과 일치하는가? +- [ ] 부모 화면에서 모달로 데이터가 전달되는가? +- [ ] 발송 버튼의 데이터 소스가 올바르게 설정되어 있는가? + diff --git a/frontend/components/screen/StyleEditor.tsx b/frontend/components/screen/StyleEditor.tsx index 2e5dec7e..2fe737c3 100644 --- a/frontend/components/screen/StyleEditor.tsx +++ b/frontend/components/screen/StyleEditor.tsx @@ -5,14 +5,9 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Separator } from "@/components/ui/separator"; -import { Palette, Type, Square, Box } from "lucide-react"; +import { Palette, Type, Square } from "lucide-react"; import { ComponentStyle } from "@/types/screen"; - -interface StyleEditorProps { - style: ComponentStyle; - onStyleChange: (style: ComponentStyle) => void; - className?: string; -} +import { ColorPickerWithTransparent } from "./common/ColorPickerWithTransparent"; export default function StyleEditor({ style, onStyleChange, className }: StyleEditorProps) { const [localStyle, setLocalStyle] = useState(style || {}); @@ -80,28 +75,18 @@ export default function StyleEditor({ style, onStyleChange, className }: StyleEd -
+
-
- handleStyleChange("borderColor", e.target.value)} - className="h-6 w-12 p-1" - className="text-xs" - /> - handleStyleChange("borderColor", e.target.value)} - placeholder="#000000" - className="h-6 flex-1 text-xs" - /> -
+ handleStyleChange("borderColor", value)} + defaultColor="#e5e7eb" + placeholder="#e5e7eb" + />
@@ -178,28 +153,18 @@ export default function StyleEditor({ style, onStyleChange, className }: StyleEd
-
+
-
- handleStyleChange("color", e.target.value)} - className="h-6 w-12 p-1" - className="text-xs" - /> - handleStyleChange("color", e.target.value)} - placeholder="#000000" - className="h-6 flex-1 text-xs" - /> -
+ handleStyleChange("color", value)} + defaultColor="#000000" + placeholder="#000000" + />
diff --git a/frontend/components/screen/panels/PropertiesPanel.tsx b/frontend/components/screen/panels/PropertiesPanel.tsx index bb663c74..31025238 100644 --- a/frontend/components/screen/panels/PropertiesPanel.tsx +++ b/frontend/components/screen/panels/PropertiesPanel.tsx @@ -31,6 +31,7 @@ import { getBaseInputType, getDefaultDetailType, } from "@/types/input-type-mapping"; +import { ColorPickerWithTransparent } from "../common/ColorPickerWithTransparent"; // DataTableConfigPanel을 위한 안정화된 래퍼 컴포넌트 const DataTableConfigPanelWrapper: React.FC<{ @@ -1092,17 +1093,18 @@ const PropertiesPanelComponent: React.FC = ({ - { - const newValue = e.target.value; - setLocalInputs((prev) => ({ ...prev, labelColor: newValue })); - onUpdateProperty("style.labelColor", newValue); - }} - className="mt-1 h-8" - /> +
+ { + setLocalInputs((prev) => ({ ...prev, labelColor: value || "" })); + onUpdateProperty("style.labelColor", value); + }} + defaultColor="#212121" + placeholder="#212121" + /> +
diff --git a/frontend/components/screen/panels/RowSettingsPanel.tsx b/frontend/components/screen/panels/RowSettingsPanel.tsx index c88eda92..49a3d05f 100644 --- a/frontend/components/screen/panels/RowSettingsPanel.tsx +++ b/frontend/components/screen/panels/RowSettingsPanel.tsx @@ -9,6 +9,7 @@ import { Separator } from "@/components/ui/separator"; import { LayoutRow } from "@/types/grid-system"; import { GapPreset, GAP_PRESETS } from "@/lib/constants/columnSpans"; import { Rows, AlignHorizontalJustifyCenter, AlignVerticalJustifyCenter } from "lucide-react"; +import { ColorPickerWithTransparent } from "../common/ColorPickerWithTransparent"; interface RowSettingsPanelProps { row: LayoutRow; @@ -224,26 +225,12 @@ export const RowSettingsPanel: React.FC = ({ row, onUpdat {/* 배경색 */}
-
- onUpdateRow({ backgroundColor: e.target.value })} - className="h-10 w-20 cursor-pointer p-1" - /> - onUpdateRow({ backgroundColor: e.target.value })} - placeholder="#ffffff" - className="flex-1" - /> - {row.backgroundColor && ( - - )} -
+ onUpdateRow({ backgroundColor: value })} + defaultColor="#ffffff" + placeholder="#ffffff" + />
); diff --git a/frontend/components/screen/panels/UnifiedPropertiesPanel.tsx b/frontend/components/screen/panels/UnifiedPropertiesPanel.tsx index 2264c99f..c2e9a562 100644 --- a/frontend/components/screen/panels/UnifiedPropertiesPanel.tsx +++ b/frontend/components/screen/panels/UnifiedPropertiesPanel.tsx @@ -48,6 +48,7 @@ import { import { ButtonConfigPanel } from "../config-panels/ButtonConfigPanel"; import { CardConfigPanel } from "../config-panels/CardConfigPanel"; import { DashboardConfigPanel } from "../config-panels/DashboardConfigPanel"; +import { ColorPickerWithTransparent } from "../common/ColorPickerWithTransparent"; import { StatsCardConfigPanel } from "../config-panels/StatsCardConfigPanel"; // ComponentRegistry import (동적 ConfigPanel 가져오기용) @@ -603,13 +604,13 @@ export const UnifiedPropertiesPanel: React.FC = ({ {selectedComponent.componentConfig?.backgroundColor === "custom" && (
- { - handleUpdateProperty(selectedComponent.id, "componentConfig.customColor", e.target.value); + { + handleUpdateProperty(selectedComponent.id, "componentConfig.customColor", value); }} - className="h-9" + defaultColor="#f0f0f0" + placeholder="#f0f0f0" />
)} @@ -882,12 +883,11 @@ export const UnifiedPropertiesPanel: React.FC = ({
- handleUpdate("style.labelColor", e.target.value)} - className="h-6 w-full px-2 py-0 text-xs" - className="text-xs" + handleUpdate("style.labelColor", value)} + defaultColor="#212121" + placeholder="#212121" />
diff --git a/화면_임베딩_및_데이터_전달_시스템_구현_계획서.md b/화면_임베딩_및_데이터_전달_시스템_구현_계획서.md index 29da36f1..9d3236af 100644 --- a/화면_임베딩_및_데이터_전달_시스템_구현_계획서.md +++ b/화면_임베딩_및_데이터_전달_시스템_구현_계획서.md @@ -1683,3 +1683,4 @@ const 출고등록_설정: ScreenSplitPanel = { + diff --git a/화면_임베딩_시스템_Phase1-4_구현_완료.md b/화면_임베딩_시스템_Phase1-4_구현_완료.md index 2f382cb3..1ef61a7d 100644 --- a/화면_임베딩_시스템_Phase1-4_구현_완료.md +++ b/화면_임베딩_시스템_Phase1-4_구현_완료.md @@ -530,3 +530,4 @@ const { data: config } = await getScreenSplitPanel(screenId); + diff --git a/화면_임베딩_시스템_충돌_분석_보고서.md b/화면_임베딩_시스템_충돌_분석_보고서.md index 8e4cdbd2..013b85bc 100644 --- a/화면_임베딩_시스템_충돌_분석_보고서.md +++ b/화면_임베딩_시스템_충돌_분석_보고서.md @@ -517,3 +517,4 @@ function ScreenViewPage() { +