Merge branch 'main' into barcode
This commit is contained in:
@@ -99,10 +99,8 @@ function dbRowToCartItem(dbRow: Record<string, unknown>): CartItemWithId {
|
||||
function cartItemToDbRecord(
|
||||
item: CartItemWithId,
|
||||
screenId: string,
|
||||
cartType: string = "pop",
|
||||
selectedColumns?: string[],
|
||||
): Record<string, unknown> {
|
||||
// selectedColumns가 있으면 해당 컬럼만 추출, 없으면 전체 저장
|
||||
const rowData =
|
||||
selectedColumns && selectedColumns.length > 0
|
||||
? Object.fromEntries(
|
||||
@@ -111,7 +109,7 @@ function cartItemToDbRecord(
|
||||
: item.row;
|
||||
|
||||
return {
|
||||
cart_type: cartType,
|
||||
cart_type: "pop",
|
||||
screen_id: screenId,
|
||||
source_table: item.sourceTable,
|
||||
row_key: item.rowKey,
|
||||
@@ -144,7 +142,6 @@ function areItemsEqual(a: CartItemWithId[], b: CartItemWithId[]): boolean {
|
||||
export function useCartSync(
|
||||
screenId: string,
|
||||
sourceTable: string,
|
||||
cartType?: string,
|
||||
): UseCartSyncReturn {
|
||||
const [cartItems, setCartItems] = useState<CartItemWithId[]>([]);
|
||||
const [savedItems, setSavedItems] = useState<CartItemWithId[]>([]);
|
||||
@@ -153,21 +150,19 @@ export function useCartSync(
|
||||
|
||||
const screenIdRef = useRef(screenId);
|
||||
const sourceTableRef = useRef(sourceTable);
|
||||
const cartTypeRef = useRef(cartType || "pop");
|
||||
screenIdRef.current = screenId;
|
||||
sourceTableRef.current = sourceTable;
|
||||
cartTypeRef.current = cartType || "pop";
|
||||
|
||||
// ----- DB에서 장바구니 로드 -----
|
||||
const loadFromDb = useCallback(async () => {
|
||||
if (!screenId) return;
|
||||
if (!screenId || !sourceTable) return;
|
||||
setLoading(true);
|
||||
try {
|
||||
const result = await dataApi.getTableData("cart_items", {
|
||||
size: 500,
|
||||
filters: {
|
||||
screen_id: screenId,
|
||||
cart_type: cartTypeRef.current,
|
||||
cart_type: "pop",
|
||||
status: "in_cart",
|
||||
},
|
||||
});
|
||||
@@ -181,7 +176,7 @@ export function useCartSync(
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [screenId]);
|
||||
}, [screenId, sourceTable]);
|
||||
|
||||
// 마운트 시 자동 로드
|
||||
useEffect(() => {
|
||||
@@ -286,18 +281,16 @@ export function useCartSync(
|
||||
const promises: Promise<unknown>[] = [];
|
||||
|
||||
for (const item of toDelete) {
|
||||
promises.push(dataApi.deleteRecord("cart_items", item.cartId!));
|
||||
promises.push(dataApi.updateRecord("cart_items", item.cartId!, { status: "cancelled" }));
|
||||
}
|
||||
|
||||
const currentCartType = cartTypeRef.current;
|
||||
|
||||
for (const item of toCreate) {
|
||||
const record = cartItemToDbRecord(item, currentScreenId, currentCartType, selectedColumns);
|
||||
const record = cartItemToDbRecord(item, currentScreenId, selectedColumns);
|
||||
promises.push(dataApi.createRecord("cart_items", record));
|
||||
}
|
||||
|
||||
for (const item of toUpdate) {
|
||||
const record = cartItemToDbRecord(item, currentScreenId, currentCartType, selectedColumns);
|
||||
const record = cartItemToDbRecord(item, currentScreenId, selectedColumns);
|
||||
promises.push(dataApi.updateRecord("cart_items", item.cartId!, record));
|
||||
}
|
||||
|
||||
|
||||
@@ -8,47 +8,119 @@
|
||||
* 이벤트 규칙:
|
||||
* 소스: __comp_output__${sourceComponentId}__${outputKey}
|
||||
* 타겟: __comp_input__${targetComponentId}__${inputKey}
|
||||
*
|
||||
* _auto 모드:
|
||||
* sourceOutput="_auto"인 연결은 소스/타겟의 connectionMeta를 비교하여
|
||||
* key가 같고 category="event"인 쌍을 양방향으로 자동 라우팅한다.
|
||||
* (정방향: 소스->타겟, 역방향: 타겟->소스)
|
||||
*/
|
||||
|
||||
import { useEffect, useRef } from "react";
|
||||
import { usePopEvent } from "./usePopEvent";
|
||||
import type { PopDataConnection } from "@/components/pop/designer/types/pop-layout";
|
||||
import {
|
||||
PopComponentRegistry,
|
||||
type ConnectionMetaItem,
|
||||
} from "@/lib/registry/PopComponentRegistry";
|
||||
|
||||
interface UseConnectionResolverOptions {
|
||||
screenId: string;
|
||||
connections: PopDataConnection[];
|
||||
componentTypes?: Map<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 소스/타겟의 connectionMeta에서 자동 매칭 가능한 이벤트 쌍을 찾는다.
|
||||
* 규칙: category="event"이고 key가 동일한 쌍
|
||||
*/
|
||||
function getAutoMatchPairs(
|
||||
sourceType: string,
|
||||
targetType: string
|
||||
): { sourceKey: string; targetKey: string }[] {
|
||||
const sourceDef = PopComponentRegistry.getComponent(sourceType);
|
||||
const targetDef = PopComponentRegistry.getComponent(targetType);
|
||||
|
||||
if (!sourceDef?.connectionMeta?.sendable || !targetDef?.connectionMeta?.receivable) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const pairs: { sourceKey: string; targetKey: string }[] = [];
|
||||
|
||||
for (const s of sourceDef.connectionMeta.sendable) {
|
||||
if (s.category !== "event") continue;
|
||||
for (const r of targetDef.connectionMeta.receivable) {
|
||||
if (r.category !== "event") continue;
|
||||
if (s.key === r.key) {
|
||||
pairs.push({ sourceKey: s.key, targetKey: r.key });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pairs;
|
||||
}
|
||||
|
||||
export function useConnectionResolver({
|
||||
screenId,
|
||||
connections,
|
||||
componentTypes,
|
||||
}: UseConnectionResolverOptions): void {
|
||||
const { publish, subscribe } = usePopEvent(screenId);
|
||||
|
||||
// 연결 목록을 ref로 저장하여 콜백 안정성 확보
|
||||
const connectionsRef = useRef(connections);
|
||||
connectionsRef.current = connections;
|
||||
|
||||
const componentTypesRef = useRef(componentTypes);
|
||||
componentTypesRef.current = componentTypes;
|
||||
|
||||
useEffect(() => {
|
||||
if (!connections || connections.length === 0) return;
|
||||
|
||||
const unsubscribers: (() => void)[] = [];
|
||||
|
||||
// 소스별로 그룹핑하여 구독 생성
|
||||
const sourceGroups = new Map<string, PopDataConnection[]>();
|
||||
for (const conn of connections) {
|
||||
const sourceEvent = `__comp_output__${conn.sourceComponent}__${conn.sourceOutput || conn.sourceField}`;
|
||||
const existing = sourceGroups.get(sourceEvent) || [];
|
||||
existing.push(conn);
|
||||
sourceGroups.set(sourceEvent, existing);
|
||||
}
|
||||
const isAutoMode = conn.sourceOutput === "_auto" || !conn.sourceOutput;
|
||||
|
||||
for (const [sourceEvent, conns] of sourceGroups) {
|
||||
const unsub = subscribe(sourceEvent, (payload: unknown) => {
|
||||
for (const conn of conns) {
|
||||
if (isAutoMode && componentTypesRef.current) {
|
||||
const sourceType = componentTypesRef.current.get(conn.sourceComponent);
|
||||
const targetType = componentTypesRef.current.get(conn.targetComponent);
|
||||
|
||||
if (!sourceType || !targetType) continue;
|
||||
|
||||
// 정방향: 소스 sendable -> 타겟 receivable
|
||||
const forwardPairs = getAutoMatchPairs(sourceType, targetType);
|
||||
for (const pair of forwardPairs) {
|
||||
const sourceEvent = `__comp_output__${conn.sourceComponent}__${pair.sourceKey}`;
|
||||
const targetEvent = `__comp_input__${conn.targetComponent}__${pair.targetKey}`;
|
||||
|
||||
const unsub = subscribe(sourceEvent, (payload: unknown) => {
|
||||
publish(targetEvent, {
|
||||
value: payload,
|
||||
_connectionId: conn.id,
|
||||
});
|
||||
});
|
||||
unsubscribers.push(unsub);
|
||||
}
|
||||
|
||||
// 역방향: 타겟 sendable -> 소스 receivable
|
||||
const reversePairs = getAutoMatchPairs(targetType, sourceType);
|
||||
for (const pair of reversePairs) {
|
||||
const sourceEvent = `__comp_output__${conn.targetComponent}__${pair.sourceKey}`;
|
||||
const targetEvent = `__comp_input__${conn.sourceComponent}__${pair.targetKey}`;
|
||||
|
||||
const unsub = subscribe(sourceEvent, (payload: unknown) => {
|
||||
publish(targetEvent, {
|
||||
value: payload,
|
||||
_connectionId: conn.id,
|
||||
});
|
||||
});
|
||||
unsubscribers.push(unsub);
|
||||
}
|
||||
} else {
|
||||
const sourceEvent = `__comp_output__${conn.sourceComponent}__${conn.sourceOutput || conn.sourceField}`;
|
||||
|
||||
const unsub = subscribe(sourceEvent, (payload: unknown) => {
|
||||
const targetEvent = `__comp_input__${conn.targetComponent}__${conn.targetInput || conn.targetField}`;
|
||||
|
||||
// 항상 통일된 구조로 감싸서 전달: { value, filterConfig?, _connectionId }
|
||||
const enrichedPayload = {
|
||||
value: payload,
|
||||
filterConfig: conn.filterConfig,
|
||||
@@ -56,9 +128,9 @@ export function useConnectionResolver({
|
||||
};
|
||||
|
||||
publish(targetEvent, enrichedPayload);
|
||||
}
|
||||
});
|
||||
unsubscribers.push(unsub);
|
||||
});
|
||||
unsubscribers.push(unsub);
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
|
||||
Reference in New Issue
Block a user