Merge branch 'main' of http://39.117.244.52:3000/kjs/ERP-node into jskim-node
This commit is contained in:
@@ -12,6 +12,7 @@ import { getFilePreviewUrl } from "@/lib/api/file";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { v2EventBus, V2_EVENTS, V2ErrorBoundary } from "@/lib/v2-core";
|
||||
import { getAdaptiveLabelColor } from "@/lib/utils/darkModeColor";
|
||||
import { useTabId } from "@/contexts/TabIdContext";
|
||||
|
||||
// 🖼️ 테이블 셀 이미지 썸네일 컴포넌트
|
||||
// objid인 경우 인증된 API로 blob URL 생성, 경로인 경우 직접 URL 사용
|
||||
@@ -405,6 +406,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
|
||||
// 디버그 로그 제거 (성능 최적화)
|
||||
|
||||
const currentTabId = useTabId();
|
||||
|
||||
const buttonColor = getAdaptiveLabelColor(component.style?.labelColor);
|
||||
const buttonTextColor = component.config?.buttonTextColor || "#ffffff";
|
||||
|
||||
@@ -433,13 +436,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
minHeight: isDesignMode ? "300px" : "100%",
|
||||
...style,
|
||||
// 런타임에서는 DB의 고정 px 크기를 무시하고 부모에 맞춤
|
||||
...(!isDesignMode && {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
minWidth: 0,
|
||||
}),
|
||||
...style, // style prop이 위의 기본값들을 덮어씀
|
||||
};
|
||||
|
||||
// ========================================
|
||||
@@ -695,13 +692,32 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [totalPages, setTotalPages] = useState(0);
|
||||
const [totalItems, setTotalItems] = useState(0);
|
||||
const [pageInputValue, setPageInputValue] = useState<string>("1");
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [sortColumn, setSortColumn] = useState<string | null>(null);
|
||||
const [sortDirection, setSortDirection] = useState<"asc" | "desc">("asc");
|
||||
const hasInitializedSort = useRef(false);
|
||||
const [columnLabels, setColumnLabels] = useState<Record<string, string>>({});
|
||||
const [tableLabel, setTableLabel] = useState<string>("");
|
||||
const [localPageSize, setLocalPageSize] = useState<number>(tableConfig.pagination?.pageSize || 20);
|
||||
const pageSizeKey = useMemo(() => {
|
||||
if (!tableConfig.selectedTable) return null;
|
||||
if (currentTabId) return `pageSize_${currentTabId}_${tableConfig.selectedTable}`;
|
||||
return `pageSize_${tableConfig.selectedTable}`;
|
||||
}, [tableConfig.selectedTable, currentTabId]);
|
||||
|
||||
const [localPageSize, setLocalPageSize] = useState<number>(() => {
|
||||
const key =
|
||||
currentTabId && tableConfig.selectedTable
|
||||
? `pageSize_${currentTabId}_${tableConfig.selectedTable}`
|
||||
: tableConfig.selectedTable
|
||||
? `pageSize_${tableConfig.selectedTable}`
|
||||
: null;
|
||||
if (key) {
|
||||
const val = sessionStorage.getItem(key);
|
||||
if (val) return Number(val);
|
||||
}
|
||||
return tableConfig.pagination?.pageSize || 20;
|
||||
});
|
||||
const [displayColumns, setDisplayColumns] = useState<ColumnConfig[]>([]);
|
||||
const [columnMeta, setColumnMeta] = useState<
|
||||
Record<string, { webType?: string; codeCategory?: string; inputType?: string; categoryRef?: string }>
|
||||
@@ -811,9 +827,10 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
const [dropTargetColumnIndex, setDropTargetColumnIndex] = useState<number | null>(null);
|
||||
const [isColumnDragEnabled] = useState<boolean>((tableConfig as any).enableColumnDrag ?? true);
|
||||
|
||||
// 🆕 State Persistence: 통합 상태 키
|
||||
// 🆕 State Persistence: 통합 상태 키 (탭 ID 스코프 + sessionStorage)
|
||||
const tableStateKey = useMemo(() => {
|
||||
if (!tableConfig.selectedTable) return null;
|
||||
if (currentTabId) return `tableState_${currentTabId}_${tableConfig.selectedTable}`;
|
||||
return `tableState_${tableConfig.selectedTable}`;
|
||||
}, [tableConfig.selectedTable]);
|
||||
|
||||
@@ -1623,7 +1640,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const page = tableConfig.pagination?.currentPage || currentPage;
|
||||
const page = currentPage;
|
||||
const pageSize = localPageSize;
|
||||
// 🆕 sortColumn이 없으면 defaultSort 설정을 fallback으로 사용
|
||||
const sortBy = sortColumn || tableConfig.defaultSort?.columnName || undefined;
|
||||
@@ -1886,7 +1903,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
}
|
||||
}, [
|
||||
tableConfig.selectedTable,
|
||||
tableConfig.pagination?.currentPage,
|
||||
tableConfig.columns,
|
||||
currentPage,
|
||||
localPageSize,
|
||||
@@ -1929,6 +1945,23 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setPageInputValue(String(currentPage));
|
||||
}, [currentPage]);
|
||||
|
||||
const commitPageInput = () => {
|
||||
const parsed = parseInt(pageInputValue, 10);
|
||||
if (isNaN(parsed) || pageInputValue.trim() === "") {
|
||||
setPageInputValue(String(currentPage));
|
||||
return;
|
||||
}
|
||||
const clamped = Math.max(1, Math.min(parsed, totalPages || 1));
|
||||
if (clamped !== currentPage) {
|
||||
handlePageChange(clamped);
|
||||
}
|
||||
setPageInputValue(String(clamped));
|
||||
};
|
||||
|
||||
const handleSort = (column: string) => {
|
||||
let newSortColumn = column;
|
||||
let newSortDirection: "asc" | "desc" = "asc";
|
||||
@@ -2989,7 +3022,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
};
|
||||
|
||||
try {
|
||||
localStorage.setItem(tableStateKey, JSON.stringify(state));
|
||||
sessionStorage.setItem(tableStateKey, JSON.stringify(state));
|
||||
} catch (error) {
|
||||
console.error("❌ 테이블 상태 저장 실패:", error);
|
||||
}
|
||||
@@ -3012,7 +3045,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
if (!tableStateKey) return;
|
||||
|
||||
try {
|
||||
const saved = localStorage.getItem(tableStateKey);
|
||||
const saved = sessionStorage.getItem(tableStateKey);
|
||||
if (!saved) return;
|
||||
|
||||
const state = JSON.parse(saved);
|
||||
@@ -3050,7 +3083,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
if (!tableStateKey) return;
|
||||
|
||||
try {
|
||||
localStorage.removeItem(tableStateKey);
|
||||
sessionStorage.removeItem(tableStateKey);
|
||||
setColumnWidths({});
|
||||
setColumnOrder([]);
|
||||
setSortColumn(null);
|
||||
@@ -4285,8 +4318,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
|
||||
return (
|
||||
<div className="flex max-w-full items-center gap-1.5 text-sm">
|
||||
<Paperclip className="h-4 w-4 flex-shrink-0 text-muted-foreground" />
|
||||
<span className="truncate text-primary" title={fileNames}>
|
||||
<Paperclip className="h-4 w-4 flex-shrink-0 text-gray-500" />
|
||||
<span className="truncate text-blue-600" title={fileNames}>
|
||||
{fileNames}
|
||||
</span>
|
||||
{files.length > 1 && <span className="text-muted-foreground flex-shrink-0 text-xs">({files.length})</span>}
|
||||
@@ -4475,28 +4508,32 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
// useEffect 훅
|
||||
// ========================================
|
||||
|
||||
// 필터 설정 localStorage 키 생성 (화면별로 독립적)
|
||||
// 필터 설정 sessionStorage 키 생성 (탭 ID 스코프)
|
||||
const filterSettingKey = useMemo(() => {
|
||||
if (!tableConfig.selectedTable) return null;
|
||||
return screenId
|
||||
? `tableList_filterSettings_${tableConfig.selectedTable}_screen_${screenId}`
|
||||
: `tableList_filterSettings_${tableConfig.selectedTable}`;
|
||||
}, [tableConfig.selectedTable, screenId]);
|
||||
const base = screenId
|
||||
? `${tableConfig.selectedTable}_screen_${screenId}`
|
||||
: tableConfig.selectedTable;
|
||||
if (currentTabId) return `filterSettings_${currentTabId}_${base}`;
|
||||
return `filterSettings_${base}`;
|
||||
}, [tableConfig.selectedTable, screenId, currentTabId]);
|
||||
|
||||
// 그룹 설정 localStorage 키 생성 (화면별로 독립적)
|
||||
// 그룹 설정 sessionStorage 키 생성 (탭 ID 스코프)
|
||||
const groupSettingKey = useMemo(() => {
|
||||
if (!tableConfig.selectedTable) return null;
|
||||
return screenId
|
||||
? `tableList_groupSettings_${tableConfig.selectedTable}_screen_${screenId}`
|
||||
: `tableList_groupSettings_${tableConfig.selectedTable}`;
|
||||
}, [tableConfig.selectedTable, screenId]);
|
||||
const base = screenId
|
||||
? `${tableConfig.selectedTable}_screen_${screenId}`
|
||||
: tableConfig.selectedTable;
|
||||
if (currentTabId) return `groupSettings_${currentTabId}_${base}`;
|
||||
return `groupSettings_${base}`;
|
||||
}, [tableConfig.selectedTable, screenId, currentTabId]);
|
||||
|
||||
// 저장된 필터 설정 불러오기
|
||||
useEffect(() => {
|
||||
if (!filterSettingKey || visibleColumns.length === 0) return;
|
||||
|
||||
try {
|
||||
const saved = localStorage.getItem(filterSettingKey);
|
||||
const saved = sessionStorage.getItem(filterSettingKey);
|
||||
if (saved) {
|
||||
const savedFilters = JSON.parse(saved);
|
||||
setVisibleFilterColumns(new Set(savedFilters));
|
||||
@@ -4515,7 +4552,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
if (!filterSettingKey) return;
|
||||
|
||||
try {
|
||||
localStorage.setItem(filterSettingKey, JSON.stringify(Array.from(visibleFilterColumns)));
|
||||
sessionStorage.setItem(filterSettingKey, JSON.stringify(Array.from(visibleFilterColumns)));
|
||||
setIsFilterSettingOpen(false);
|
||||
toast.success("검색 필터 설정이 저장되었습니다");
|
||||
|
||||
@@ -4570,7 +4607,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
if (!groupSettingKey) return;
|
||||
|
||||
try {
|
||||
localStorage.setItem(groupSettingKey, JSON.stringify(groupByColumns));
|
||||
sessionStorage.setItem(groupSettingKey, JSON.stringify(groupByColumns));
|
||||
} catch (error) {
|
||||
console.error("그룹 설정 저장 실패:", error);
|
||||
}
|
||||
@@ -4650,7 +4687,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
setGroupByColumns([]);
|
||||
setCollapsedGroups(new Set());
|
||||
if (groupSettingKey) {
|
||||
localStorage.removeItem(groupSettingKey);
|
||||
sessionStorage.removeItem(groupSettingKey);
|
||||
}
|
||||
toast.success("그룹이 해제되었습니다");
|
||||
}, [groupSettingKey]);
|
||||
@@ -4834,7 +4871,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
if (!groupSettingKey || visibleColumns.length === 0) return;
|
||||
|
||||
try {
|
||||
const saved = localStorage.getItem(groupSettingKey);
|
||||
const saved = sessionStorage.getItem(groupSettingKey);
|
||||
if (saved) {
|
||||
const savedGroups = JSON.parse(saved);
|
||||
setGroupByColumns(savedGroups);
|
||||
@@ -5134,7 +5171,10 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
// 페이지 크기 변경 핸들러
|
||||
const handlePageSizeChange = (newSize: number) => {
|
||||
setLocalPageSize(newSize);
|
||||
setCurrentPage(1); // 페이지 크기 변경 시 첫 페이지로 이동
|
||||
setCurrentPage(1);
|
||||
if (pageSizeKey) {
|
||||
sessionStorage.setItem(pageSizeKey, String(newSize));
|
||||
}
|
||||
if (onConfigChange) {
|
||||
onConfigChange({
|
||||
...tableConfig,
|
||||
@@ -5143,8 +5183,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const pageSizeOptions = tableConfig.pagination?.pageSizeOptions || [5, 10, 20, 50, 100];
|
||||
|
||||
return (
|
||||
<div className="border-border bg-background relative flex h-14 w-full flex-shrink-0 items-center justify-center border-t-2 px-4 sm:h-[60px] sm:px-6">
|
||||
{/* 좌측: 페이지 크기 입력 */}
|
||||
@@ -5190,9 +5228,28 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
<ChevronLeft className="h-3 w-3 sm:h-4 sm:w-4" />
|
||||
</Button>
|
||||
|
||||
<span className="text-foreground min-w-[60px] text-center text-xs font-medium sm:min-w-[80px] sm:text-sm">
|
||||
{currentPage} / {totalPages || 1}
|
||||
</span>
|
||||
<div className="flex items-center gap-1 sm:gap-2">
|
||||
<input
|
||||
type="text"
|
||||
inputMode="numeric"
|
||||
value={pageInputValue}
|
||||
onChange={(e) => setPageInputValue(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
commitPageInput();
|
||||
(e.target as HTMLInputElement).blur();
|
||||
}
|
||||
}}
|
||||
onBlur={commitPageInput}
|
||||
onFocus={(e) => e.target.select()}
|
||||
disabled={loading}
|
||||
className="border-input bg-background focus:ring-ring h-7 w-10 rounded-md border px-1 text-center text-xs font-medium focus:ring-1 focus:outline-none sm:h-8 sm:w-12 sm:text-sm"
|
||||
/>
|
||||
<span className="text-muted-foreground text-xs sm:text-sm">/</span>
|
||||
<span className="text-foreground text-xs font-medium sm:text-sm">
|
||||
{totalPages || 1}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -5227,7 +5284,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="text-muted-foreground px-2 py-1 text-xs font-medium">Excel</div>
|
||||
<Button variant="ghost" size="sm" className="justify-start text-xs" onClick={() => exportToExcel(true)}>
|
||||
<FileSpreadsheet className="mr-2 h-3 w-3 text-emerald-600" />
|
||||
<FileSpreadsheet className="mr-2 h-3 w-3 text-green-600" />
|
||||
전체 Excel 내보내기
|
||||
</Button>
|
||||
<Button
|
||||
@@ -5237,13 +5294,13 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
onClick={() => exportToExcel(false)}
|
||||
disabled={selectedRows.size === 0}
|
||||
>
|
||||
<FileSpreadsheet className="mr-2 h-3 w-3 text-emerald-600" />
|
||||
<FileSpreadsheet className="mr-2 h-3 w-3 text-green-600" />
|
||||
선택 항목만 ({selectedRows.size}개)
|
||||
</Button>
|
||||
<div className="border-border my-1 border-t" />
|
||||
<div className="text-muted-foreground px-2 py-1 text-xs font-medium">PDF/인쇄</div>
|
||||
<Button variant="ghost" size="sm" className="justify-start text-xs" onClick={() => exportToPdf(true)}>
|
||||
<FileText className="mr-2 h-3 w-3 text-destructive" />
|
||||
<FileText className="mr-2 h-3 w-3 text-red-600" />
|
||||
전체 PDF 내보내기
|
||||
</Button>
|
||||
<Button
|
||||
@@ -5253,7 +5310,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
onClick={() => exportToPdf(false)}
|
||||
disabled={selectedRows.size === 0}
|
||||
>
|
||||
<FileText className="mr-2 h-3 w-3 text-destructive" />
|
||||
<FileText className="mr-2 h-3 w-3 text-red-600" />
|
||||
선택 항목만 ({selectedRows.size}개)
|
||||
</Button>
|
||||
</div>
|
||||
@@ -5289,6 +5346,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
localPageSize,
|
||||
onConfigChange,
|
||||
tableConfig,
|
||||
pageInputValue,
|
||||
]);
|
||||
|
||||
// ========================================
|
||||
@@ -5300,7 +5358,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
onDragStart: isDesignMode ? onDragStart : undefined,
|
||||
onDragEnd: isDesignMode ? onDragEnd : undefined,
|
||||
draggable: isDesignMode,
|
||||
className: cn("w-full h-full overflow-hidden", className, isDesignMode && "cursor-move"),
|
||||
className: cn("w-full h-full", className, isDesignMode && "cursor-move"), // customer-item-mapping과 동일
|
||||
style: componentStyle,
|
||||
};
|
||||
|
||||
@@ -5370,7 +5428,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div style={{ flex: 1, overflow: "auto", WebkitOverflowScrolling: "touch" }}>
|
||||
<div style={{ flex: 1, overflow: "hidden" }}>
|
||||
<SingleTableWithSticky
|
||||
data={data}
|
||||
columns={visibleColumns}
|
||||
@@ -5436,7 +5494,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
className="h-7 text-xs"
|
||||
title="Excel 내보내기"
|
||||
>
|
||||
<FileSpreadsheet className="mr-1 h-3 w-3 text-emerald-600" />
|
||||
<FileSpreadsheet className="mr-1 h-3 w-3 text-green-600" />
|
||||
Excel
|
||||
</Button>
|
||||
)}
|
||||
@@ -5448,7 +5506,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
className="h-7 text-xs"
|
||||
title="PDF 내보내기"
|
||||
>
|
||||
<FileText className="mr-1 h-3 w-3 text-destructive" />
|
||||
<FileText className="mr-1 h-3 w-3 text-red-600" />
|
||||
PDF
|
||||
</Button>
|
||||
)}
|
||||
@@ -5680,7 +5738,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
overflow: "auto",
|
||||
WebkitOverflowScrolling: "touch",
|
||||
}}
|
||||
onScroll={handleVirtualScroll}
|
||||
>
|
||||
@@ -5691,7 +5748,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
borderCollapse: "collapse",
|
||||
width: "100%",
|
||||
tableLayout: "fixed",
|
||||
minWidth: "400px",
|
||||
}}
|
||||
>
|
||||
{/* 헤더 (sticky) */}
|
||||
@@ -5909,7 +5965,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
{/* 리사이즈 핸들 (체크박스 제외) */}
|
||||
{columnIndex < visibleColumns.length - 1 && column.columnName !== "__checkbox__" && (
|
||||
<div
|
||||
className="absolute top-0 right-0 z-20 h-full w-2 cursor-col-resize hover:bg-primary"
|
||||
className="absolute top-0 right-0 z-20 h-full w-2 cursor-col-resize hover:bg-blue-500"
|
||||
style={{ marginRight: "-4px", paddingLeft: "4px", paddingRight: "4px" }}
|
||||
onClick={(e) => e.stopPropagation()} // 정렬 클릭 방지
|
||||
onMouseDown={(e) => {
|
||||
@@ -6262,11 +6318,11 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
// 🆕 배치 편집: 수정된 셀 스타일 (노란 배경)
|
||||
isModified && !cellValidationError && "bg-amber-100 dark:bg-amber-900/40",
|
||||
// 🆕 유효성 에러: 빨간 테두리 및 배경
|
||||
cellValidationError && "bg-destructive/10 ring-2 ring-destructive ring-inset dark:bg-destructive/15",
|
||||
cellValidationError && "bg-red-50 ring-2 ring-red-500 ring-inset dark:bg-red-950/40",
|
||||
// 🆕 검색 하이라이트 스타일 (노란 배경)
|
||||
isSearchHighlighted && !isCellFocused && "bg-yellow-200 dark:bg-yellow-700/50",
|
||||
// 🆕 편집 불가 컬럼 스타일 (연한 회색 배경)
|
||||
column.editable === false && "bg-muted dark:bg-foreground/30",
|
||||
column.editable === false && "bg-gray-50 dark:bg-gray-900/30",
|
||||
)}
|
||||
// 🆕 유효성 에러 툴팁
|
||||
title={cellValidationError || undefined}
|
||||
@@ -6659,7 +6715,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
|
||||
{/* 행 삭제 */}
|
||||
<button
|
||||
className="hover:bg-destructive hover:text-destructive-foreground flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm text-destructive"
|
||||
className="hover:bg-destructive hover:text-destructive-foreground flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm text-red-600"
|
||||
onClick={async () => {
|
||||
if (confirm("이 행을 삭제하시겠습니까?")) {
|
||||
try {
|
||||
@@ -6726,7 +6782,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => removeFilterGroup(group.id)}
|
||||
className="h-6 w-6 p-0 text-destructive hover:text-destructive"
|
||||
className="h-6 w-6 p-0 text-red-500 hover:text-red-700"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -6786,7 +6842,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => removeFilterCondition(group.id, condition.id)}
|
||||
className="h-6 w-6 p-0 text-destructive hover:text-destructive"
|
||||
className="h-6 w-6 p-0 text-red-500 hover:text-red-700"
|
||||
disabled={group.conditions.length === 1}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
|
||||
Reference in New Issue
Block a user