From b844953fe0cd41e07fd12c58da08bfd03ed1f9de Mon Sep 17 00:00:00 2001 From: SeongHyun Kim Date: Tue, 18 Nov 2025 16:19:56 +0900 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20ItemSelectionModal=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=20=EB=A1=9C=EC=A7=81=20=EB=B0=8F=20=EB=94=94=EB=B2=84?= =?UTF-8?q?=EA=B9=85=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - uniqueField 값이 undefined일 때 객체 참조 비교로 폴백 - 멀티셀렉트 모드에서 선택/해제 로직 안정화 - 체크박스 클릭 이벤트 전파 개선 - 유효한 컬럼만 렌더링하도록 필터링 추가 - 디버깅을 위한 콘솔 로그 추가 - 선택된 항목의 uniqueField 값 표시 --- .../ItemSelectionModal.tsx | 154 ++++++++++++++---- 1 file changed, 126 insertions(+), 28 deletions(-) diff --git a/frontend/lib/registry/components/modal-repeater-table/ItemSelectionModal.tsx b/frontend/lib/registry/components/modal-repeater-table/ItemSelectionModal.tsx index d8c8c8d9..6463f1b1 100644 --- a/frontend/lib/registry/components/modal-repeater-table/ItemSelectionModal.tsx +++ b/frontend/lib/registry/components/modal-repeater-table/ItemSelectionModal.tsx @@ -42,6 +42,7 @@ export function ItemSelectionModal({ // 모달 열릴 때 초기 검색 useEffect(() => { if (open) { + console.log("🚪 모달 열림 - uniqueField:", uniqueField, "multiSelect:", multiSelect); search("", 1); // 빈 검색어로 전체 목록 조회 setSelectedItems([]); } else { @@ -56,26 +57,67 @@ export function ItemSelectionModal({ }; const handleToggleItem = (item: any) => { + const itemValue = uniqueField ? item[uniqueField] : undefined; + + console.log("🖱️ 행 클릭:", { + item, + uniqueField, + itemValue, + currentSelected: selectedItems.length, + selectedValues: uniqueField ? selectedItems.map(s => s[uniqueField]) : [] + }); + if (!multiSelect) { setSelectedItems([item]); return; } - const isSelected = selectedItems.some((selected) => - uniqueField - ? selected[uniqueField] === item[uniqueField] - : selected === item - ); + // uniqueField 값이 undefined인 경우 객체 참조로 비교 + if (uniqueField && (itemValue === undefined || itemValue === null)) { + console.warn(`⚠️ uniqueField "${uniqueField}"의 값이 undefined입니다. 객체 참조로 비교합니다.`); + const itemIsSelected = selectedItems.includes(item); + + console.log("📊 선택 상태 (객체 참조):", itemIsSelected); + + if (itemIsSelected) { + const newSelected = selectedItems.filter((selected) => selected !== item); + console.log("➖ 제거 후:", newSelected.length); + setSelectedItems(newSelected); + } else { + console.log("➕ 추가"); + setSelectedItems([...selectedItems, item]); + } + return; + } - if (isSelected) { - setSelectedItems( - selectedItems.filter((selected) => - uniqueField - ? selected[uniqueField] !== item[uniqueField] - : selected !== item - ) - ); + const itemIsSelected = selectedItems.some((selected) => { + if (!uniqueField) { + return selected === item; + } + const selectedValue = selected[uniqueField]; + if (selectedValue === undefined || selectedValue === null) { + return false; + } + return selectedValue === itemValue; + }); + + console.log("📊 선택 상태:", itemIsSelected); + + if (itemIsSelected) { + const newSelected = selectedItems.filter((selected) => { + if (!uniqueField) { + return selected !== item; + } + const selectedValue = selected[uniqueField]; + if (selectedValue === undefined || selectedValue === null) { + return true; + } + return selectedValue !== itemValue; + }); + console.log("➖ 제거 후:", newSelected.length); + setSelectedItems(newSelected); } else { + console.log("➕ 추가"); setSelectedItems([...selectedItems, item]); } }; @@ -98,13 +140,46 @@ export function ItemSelectionModal({ // 선택된 항목인지 확인 const isSelected = (item: any): boolean => { - return selectedItems.some((selected) => - uniqueField - ? selected[uniqueField] === item[uniqueField] - : selected === item - ); + if (!uniqueField) { + return selectedItems.includes(item); + } + + const itemValue = item[uniqueField]; + + // uniqueField 값이 undefined인 경우 객체 참조로 비교 + if (itemValue === undefined || itemValue === null) { + console.warn(`⚠️ uniqueField "${uniqueField}"의 값이 undefined입니다. 객체 참조로 비교합니다.`); + return selectedItems.includes(item); + } + + const result = selectedItems.some((selected) => { + const selectedValue = selected[uniqueField]; + + // selectedValue도 undefined면 안전하게 처리 + if (selectedValue === undefined || selectedValue === null) { + return false; + } + + const isMatch = selectedValue === itemValue; + + if (isMatch) { + console.log("✅ 매칭 발견:", { + selectedValue, + itemValue, + uniqueField + }); + } + + return isMatch; + }); + + return result; }; + // 유효한 컬럼만 필터링 + const validColumns = sourceColumns.filter(col => col != null && col !== ""); + const totalColumns = validColumns.length + (multiSelect ? 1 : 0); + return ( @@ -147,6 +222,11 @@ export function ItemSelectionModal({ {selectedItems.length > 0 && (
{selectedItems.length}개 항목 선택됨 + {uniqueField && ( + + ({selectedItems.map(item => item[uniqueField]).join(", ")}) + + )}
)} @@ -168,7 +248,7 @@ export function ItemSelectionModal({ 선택 )} - {sourceColumns.map((col) => ( + {validColumns.map((col) => ( @@ -192,7 +272,7 @@ export function ItemSelectionModal({ ) : filteredResults.length === 0 ? ( {results.length > 0 @@ -203,10 +283,22 @@ export function ItemSelectionModal({ ) : ( filteredResults.map((item, index) => { const selected = isSelected(item); + const uniqueFieldValue = uniqueField ? item[uniqueField] : undefined; + const itemKey = (uniqueFieldValue !== undefined && uniqueFieldValue !== null) + ? uniqueFieldValue + : `item-${index}`; + + console.log("🔍 행 렌더링:", { + index, + itemKey, + selected, + uniqueFieldValue, + selectedCount: selectedItems.length + }); return ( handleToggleItem(item)} > {multiSelect && ( - - handleToggleItem(item)} - /> + { + // 체크박스 영역 클릭을 행 클릭으로 전파 + e.stopPropagation(); + handleToggleItem(item); + }} + > +
+ +
)} - {sourceColumns.map((col) => ( + {validColumns.map((col) => ( {item[col] || "-"} From 0bedd8bc0b97a0ecc704597b48f9560d2a5f0c95 Mon Sep 17 00:00:00 2001 From: SeongHyun Kim Date: Tue, 18 Nov 2025 18:40:25 +0900 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20Dialog=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?=EB=82=B4=EB=B6=80=20input=20=ED=95=84=EB=93=9C=20=ED=85=8D?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=9E=85=EB=A0=A5=20=EB=B6=88=EA=B0=80=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ResizableDialog 콘텐츠 영역에 pointer-events 및 z-index 설정 추가 - TextInputComponent를 제어 컴포넌트에서 비제어 컴포넌트로 변경 (value → defaultValue) - ItemSelectionModal 및 TextInputComponent 디버그 로그 제거 수정 파일: - frontend/components/ui/resizable-dialog.tsx - frontend/lib/registry/components/text-input/TextInputComponent.tsx - frontend/lib/registry/components/modal-repeater-table/ItemSelectionModal.tsx --- .../order/OrderRegistrationModal.tsx | 46 +++++++++++-------- frontend/components/ui/resizable-dialog.tsx | 12 ++++- .../ItemSelectionModal.tsx | 9 ---- .../text-input/TextInputComponent.tsx | 9 +--- 4 files changed, 37 insertions(+), 39 deletions(-) diff --git a/frontend/components/order/OrderRegistrationModal.tsx b/frontend/components/order/OrderRegistrationModal.tsx index adfee1b2..bd780038 100644 --- a/frontend/components/order/OrderRegistrationModal.tsx +++ b/frontend/components/order/OrderRegistrationModal.tsx @@ -11,7 +11,6 @@ import { } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; -import { Input } from "@/components/ui/input"; import { Select, SelectContent, @@ -19,7 +18,6 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { Textarea } from "@/components/ui/textarea"; import { OrderCustomerSearch } from "./OrderCustomerSearch"; import { OrderItemRepeaterTable } from "./OrderItemRepeaterTable"; import { toast } from "sonner"; @@ -260,14 +258,15 @@ export function OrderRegistrationModal({ - setFormData({ ...formData, contactPerson: e.target.value }) } - className="h-8 text-xs sm:h-10 sm:text-sm" + className="flex h-8 w-full rounded-md border border-input bg-background px-3 py-2 text-xs ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 sm:h-10 sm:text-sm" /> @@ -276,14 +275,15 @@ export function OrderRegistrationModal({ - setFormData({ ...formData, deliveryDestination: e.target.value }) } - className="h-8 text-xs sm:h-10 sm:text-sm" + className="flex h-8 w-full rounded-md border border-input bg-background px-3 py-2 text-xs ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 sm:h-10 sm:text-sm" /> @@ -292,14 +292,15 @@ export function OrderRegistrationModal({ - setFormData({ ...formData, deliveryAddress: e.target.value }) } - className="h-8 text-xs sm:h-10 sm:text-sm" + className="flex h-8 w-full rounded-md border border-input bg-background px-3 py-2 text-xs ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 sm:h-10 sm:text-sm" /> @@ -310,9 +311,10 @@ export function OrderRegistrationModal({
-
@@ -322,9 +324,10 @@ export function OrderRegistrationModal({
-
@@ -436,14 +439,15 @@ export function OrderRegistrationModal({ - setFormData({ ...formData, portOfLoading: e.target.value }) } - className="h-8 text-xs sm:h-10 sm:text-sm" + className="flex h-8 w-full rounded-md border border-input bg-background px-3 py-2 text-xs ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 sm:h-10 sm:text-sm" /> @@ -452,14 +456,15 @@ export function OrderRegistrationModal({ - setFormData({ ...formData, portOfDischarge: e.target.value }) } - className="h-8 text-xs sm:h-10 sm:text-sm" + className="flex h-8 w-full rounded-md border border-input bg-background px-3 py-2 text-xs ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 sm:h-10 sm:text-sm" /> @@ -468,14 +473,15 @@ export function OrderRegistrationModal({ - setFormData({ ...formData, hsCode: e.target.value }) } - className="h-8 text-xs sm:h-10 sm:text-sm" + className="flex h-8 w-full rounded-md border border-input bg-background px-3 py-2 text-xs ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 sm:h-10 sm:text-sm" /> @@ -487,14 +493,14 @@ export function OrderRegistrationModal({ -