Merge branch 'ksh'

This commit is contained in:
SeongHyun Kim 2025-11-18 16:20:38 +09:00
commit 9c8ec879d9
1 changed files with 126 additions and 28 deletions

View File

@ -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 (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-[95vw] sm:max-w-[900px] max-h-[90vh] overflow-y-auto">
@ -147,6 +222,11 @@ export function ItemSelectionModal({
{selectedItems.length > 0 && (
<div className="text-sm text-primary">
{selectedItems.length}
{uniqueField && (
<span className="ml-2 text-xs text-muted-foreground">
({selectedItems.map(item => item[uniqueField]).join(", ")})
</span>
)}
</div>
)}
@ -168,7 +248,7 @@ export function ItemSelectionModal({
</th>
)}
{sourceColumns.map((col) => (
{validColumns.map((col) => (
<th
key={col}
className="px-4 py-2 text-left font-medium text-muted-foreground"
@ -182,7 +262,7 @@ export function ItemSelectionModal({
{loading && filteredResults.length === 0 ? (
<tr>
<td
colSpan={sourceColumns.length + (multiSelect ? 1 : 0)}
colSpan={totalColumns}
className="px-4 py-8 text-center"
>
<Loader2 className="h-6 w-6 animate-spin mx-auto" />
@ -192,7 +272,7 @@ export function ItemSelectionModal({
) : filteredResults.length === 0 ? (
<tr>
<td
colSpan={sourceColumns.length + (multiSelect ? 1 : 0)}
colSpan={totalColumns}
className="px-4 py-8 text-center text-muted-foreground"
>
{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 (
<tr
key={index}
key={itemKey}
className={`border-t transition-colors ${
selected
? "bg-primary/10"
@ -215,14 +307,20 @@ export function ItemSelectionModal({
onClick={() => handleToggleItem(item)}
>
{multiSelect && (
<td className="px-4 py-2">
<Checkbox
checked={selected}
onCheckedChange={() => handleToggleItem(item)}
/>
<td
className="px-4 py-2"
onClick={(e) => {
// 체크박스 영역 클릭을 행 클릭으로 전파
e.stopPropagation();
handleToggleItem(item);
}}
>
<div className="pointer-events-none">
<Checkbox checked={selected} readOnly />
</div>
</td>
)}
{sourceColumns.map((col) => (
{validColumns.map((col) => (
<td key={col} className="px-4 py-2">
{item[col] || "-"}
</td>