feat(pop-cart): 장바구니 저장 시스템 구현 + 선택적 컬럼 저장
장바구니 담기 -> DB 저장 전체 플로우 구현 및 검증 완료. - useCartSync 훅 신규: DB(cart_items) <-> 로컬 상태 동기화, dirty check, 일괄 저장 - pop-button cart 프리셋: 배지 표시, 저장 트리거, 확인 모달, 3색 데이터 흐름 시각화 - pop-card-list: 담기/취소 UI, cart_save_trigger 수신 시 saveToDb 실행 - 선택적 컬럼 저장: RowDataMode(all/selected) + 연결 기반 자동 컬럼 로딩 - ComponentEditorPanel: allComponents/connections/componentId를 ConfigPanel에 전달 - connectionMeta: cart_save_trigger/cart_updated/cart_save_completed 이벤트 정의 - ConnectionEditor: 이벤트 타입 연결 구분 (데이터 vs 이벤트) - types.ts: CartItemWithId, CartSyncStatus, CartButtonConfig 등 타입 추가 - 접근성: NumberInputModal/PackageUnitModal에 DialogTitle 추가 Made-with: Cursor
This commit is contained in:
@@ -272,6 +272,25 @@ function ConnectionForm({
|
||||
? PopComponentRegistry.getComponent(targetComp.type)?.connectionMeta
|
||||
: null;
|
||||
|
||||
// 보내는 값 + 받는 컴포넌트가 결정되면 받는 방식 자동 매칭
|
||||
React.useEffect(() => {
|
||||
if (!selectedOutput || !targetMeta?.receivable?.length) return;
|
||||
// 이미 선택된 값이 있으면 건드리지 않음
|
||||
if (selectedTargetInput) return;
|
||||
|
||||
const receivables = targetMeta.receivable;
|
||||
// 1) 같은 key가 있으면 자동 매칭
|
||||
const exactMatch = receivables.find((r) => r.key === selectedOutput);
|
||||
if (exactMatch) {
|
||||
setSelectedTargetInput(exactMatch.key);
|
||||
return;
|
||||
}
|
||||
// 2) receivable이 1개뿐이면 자동 선택
|
||||
if (receivables.length === 1) {
|
||||
setSelectedTargetInput(receivables[0].key);
|
||||
}
|
||||
}, [selectedOutput, targetMeta, selectedTargetInput]);
|
||||
|
||||
// 화면에 표시 중인 컬럼
|
||||
const displayColumns = React.useMemo(
|
||||
() => extractDisplayColumns(targetComp || undefined),
|
||||
@@ -322,6 +341,8 @@ function ConnectionForm({
|
||||
const handleSubmit = () => {
|
||||
if (!selectedOutput || !selectedTargetId || !selectedTargetInput) return;
|
||||
|
||||
const isEvent = isEventTypeConnection(meta, selectedOutput, targetMeta, selectedTargetInput);
|
||||
|
||||
onSubmit({
|
||||
sourceComponent: component.id,
|
||||
sourceField: "",
|
||||
@@ -330,7 +351,7 @@ function ConnectionForm({
|
||||
targetField: "",
|
||||
targetInput: selectedTargetInput,
|
||||
filterConfig:
|
||||
filterColumns.length > 0
|
||||
!isEvent && filterColumns.length > 0
|
||||
? {
|
||||
targetColumn: filterColumns[0],
|
||||
targetColumns: filterColumns,
|
||||
@@ -427,8 +448,8 @@ function ConnectionForm({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 필터 설정 */}
|
||||
{selectedTargetInput && (
|
||||
{/* 필터 설정: event 타입 연결이면 숨김 */}
|
||||
{selectedTargetInput && !isEventTypeConnection(meta, selectedOutput, targetMeta, selectedTargetInput) && (
|
||||
<div className="space-y-2 rounded bg-gray-50 p-2">
|
||||
<p className="text-[10px] font-medium text-muted-foreground">필터할 컬럼</p>
|
||||
|
||||
@@ -607,6 +628,17 @@ function ReceiveSection({
|
||||
// 유틸
|
||||
// ========================================
|
||||
|
||||
function isEventTypeConnection(
|
||||
sourceMeta: ComponentConnectionMeta | undefined,
|
||||
outputKey: string,
|
||||
targetMeta: ComponentConnectionMeta | null | undefined,
|
||||
inputKey: string,
|
||||
): boolean {
|
||||
const sourceItem = sourceMeta?.sendable?.find((s) => s.key === outputKey);
|
||||
const targetItem = targetMeta?.receivable?.find((r) => r.key === inputKey);
|
||||
return sourceItem?.type === "event" || targetItem?.type === "event";
|
||||
}
|
||||
|
||||
function buildConnectionLabel(
|
||||
source: PopComponentDefinitionV5,
|
||||
_outputKey: string,
|
||||
|
||||
Reference in New Issue
Block a user