diff --git a/frontend/lib/registry/pop-components/pop-card-list-v2/PopCardListV2Component.tsx b/frontend/lib/registry/pop-components/pop-card-list-v2/PopCardListV2Component.tsx index 56296c87..075e436b 100644 --- a/frontend/lib/registry/pop-components/pop-card-list-v2/PopCardListV2Component.tsx +++ b/frontend/lib/registry/pop-components/pop-card-list-v2/PopCardListV2Component.tsx @@ -61,6 +61,14 @@ const LazyPopWorkDetail = dynamic( type RowData = Record; +const MES_STATUS_TABS = [ + { value: "", label: "전체" }, + { value: "waiting", label: "대기", color: "#94a3b8", bg: "#f8fafc" }, + { value: "acceptable", label: "접수가능", color: "#2563eb", bg: "#eff6ff" }, + { value: "in_progress", label: "진행중", color: "#d97706", bg: "#fffbeb" }, + { value: "completed", label: "완료", color: "#059669", bg: "#ecfdf5" }, +] as const; + function calculateMaxQty( row: RowData, processId: string | number | undefined, @@ -357,6 +365,9 @@ export function PopCardListV2Component({ return true; }, [selectMode, selectModeStatus]); + // 내장 상태 탭 + const [selectedStatusTab, setSelectedStatusTab] = useState(""); + // 확장/페이지네이션 const [isExpanded, setIsExpanded] = useState(false); const [currentPage, setCurrentPage] = useState(1); @@ -585,23 +596,57 @@ export function PopCardListV2Component({ return col ? subTableKeys.has(col) : false; }, [subTableKeys]); + // showStatusTabs일 때 외부 status-bar 필터 무시 (내장 탭으로 대체) + const effectiveExternalFilters = useMemo(() => { + if (!config?.showStatusTabs) return externalFilters; + const filtered = new Map( + [...externalFilters.entries()].filter(([, f]) => f._source !== "status-bar") + ); + return filtered; + }, [externalFilters, config?.showStatusTabs]); + // 외부 필터 (자동 분류: 컬럼이 processFlow에 있으면 subFilter) const filteredRows = useMemo(() => { - if (externalFilters.size === 0) return duplicateAcceptableCards(rows); + if (effectiveExternalFilters.size === 0) return duplicateAcceptableCards(rows); - const allFilters = [...externalFilters.values()]; + const allFilters = [...effectiveExternalFilters.values()]; const mainFilters = allFilters.filter((f) => !isSubTableColumn(f)); const subFilters = allFilters.filter((f) => isSubTableColumn(f)); const afterDuplicate = applySubFilterAndDuplicate(rows, subFilters); return applyMainFilters(afterDuplicate, mainFilters, subFilters.length > 0); - }, [rows, externalFilters, duplicateAcceptableCards, applySubFilterAndDuplicate, applyMainFilters, isSubTableColumn]); + }, [rows, effectiveExternalFilters, duplicateAcceptableCards, applySubFilterAndDuplicate, applyMainFilters, isSubTableColumn]); // 하위 필터 활성 여부 const hasActiveSubFilter = useMemo(() => { - if (externalFilters.size === 0) return false; - return [...externalFilters.values()].some((f) => isSubTableColumn(f)); - }, [externalFilters, isSubTableColumn]); + if (effectiveExternalFilters.size === 0) return false; + return [...effectiveExternalFilters.values()].some((f) => isSubTableColumn(f)); + }, [effectiveExternalFilters, isSubTableColumn]); + + // 내장 상태 탭: 카운트 계산 (filteredRows 기준, 상태 필터 적용 전) + const hasProcessFlow = useMemo( + () => rows.some((r) => r[VIRTUAL_SUB_STATUS] !== undefined), + [rows], + ); + const statusCounts = useMemo(() => { + if (!config?.showStatusTabs || !hasProcessFlow) return null; + const map = new Map(); + let originalTotal = 0; + for (const row of filteredRows) { + const v = String(row[VIRTUAL_SUB_STATUS] ?? ""); + if (v) map.set(v, (map.get(v) || 0) + 1); + if (!row.__isAcceptClone) originalTotal++; + } + return { counts: map, total: originalTotal }; + }, [filteredRows, config?.showStatusTabs, hasProcessFlow]); + + // 내장 상태 탭: 필터 적용 + const statusFilteredRows = useMemo(() => { + if (!config?.showStatusTabs || !selectedStatusTab) return filteredRows; + return filteredRows.filter((row) => + String(row[VIRTUAL_SUB_STATUS] ?? "") === selectedStatusTab + ); + }, [filteredRows, selectedStatusTab, config?.showStatusTabs]); // 선택 모드 일괄 처리 const handleSelectModeAction = useCallback(async (btnConfig: SelectModeButtonConfig) => { @@ -680,8 +725,9 @@ export function PopCardListV2Component({ } }, [selectedRowIds, filteredRows, exitSelectMode]); - // status-bar 필터를 제외한 rows (카운트 집계용) + // status-bar 필터를 제외한 rows (외부 status-bar 카운트 집계용, 하위 호환) const rowsForStatusCount = useMemo(() => { + if (config?.showStatusTabs) return filteredRows; const hasStatusBarFilter = [...externalFilters.values()].some((f) => f._source === "status-bar"); if (!hasStatusBarFilter) return filteredRows; @@ -696,7 +742,7 @@ export function PopCardListV2Component({ const afterDuplicate = applySubFilterAndDuplicate(rows, subFilters); return applyMainFilters(afterDuplicate, mainFilters, subFilters.length > 0); - }, [rows, filteredRows, externalFilters, duplicateAcceptableCards, applySubFilterAndDuplicate, applyMainFilters, isSubTableColumn]); + }, [rows, filteredRows, externalFilters, config?.showStatusTabs, duplicateAcceptableCards, applySubFilterAndDuplicate, applyMainFilters, isSubTableColumn]); // 카운트 집계용 rows 발행 (status-bar 필터 제외) // originalCount: 복제 카드를 제외한 원본 카드 수 @@ -713,7 +759,7 @@ export function PopCardListV2Component({ const overflowCfg = effectiveConfig?.overflow; const baseVisibleCount = overflowCfg?.visibleCount ?? gridColumns * gridRows; const visibleCardCount = useMemo(() => Math.max(1, baseVisibleCount), [baseVisibleCount]); - const hasMoreCards = filteredRows.length > visibleCardCount; + const hasMoreCards = statusFilteredRows.length > visibleCardCount; const expandedCardsPerPage = useMemo(() => { if (overflowCfg?.mode === "pagination" && overflowCfg.pageSize) return overflowCfg.pageSize; if (overflowCfg?.mode === "loadMore" && overflowCfg.loadMoreCount) return overflowCfg.loadMoreCount + visibleCardCount; @@ -726,7 +772,7 @@ export function PopCardListV2Component({ const ownerFilterMode = config?.ownerFilterMode || "priority"; const displayCards = useMemo(() => { - let source = filteredRows; + let source = statusFilteredRows; if (ownerSortColumn && currentUserId) { const mine: RowData[] = []; @@ -744,9 +790,9 @@ export function PopCardListV2Component({ if (!isExpanded) return source.slice(0, visibleCardCount); const start = (currentPage - 1) * expandedCardsPerPage; return source.slice(start, start + expandedCardsPerPage); - }, [filteredRows, isExpanded, visibleCardCount, currentPage, expandedCardsPerPage, ownerSortColumn, ownerFilterMode, currentUserId]); + }, [statusFilteredRows, isExpanded, visibleCardCount, currentPage, expandedCardsPerPage, ownerSortColumn, ownerFilterMode, currentUserId]); - const totalPages = isExpanded ? Math.ceil(filteredRows.length / expandedCardsPerPage) : 1; + const totalPages = isExpanded ? Math.ceil(statusFilteredRows.length / expandedCardsPerPage) : 1; const needsPagination = isExpanded && totalPages > 1; const toggleExpand = () => { @@ -1244,7 +1290,7 @@ export function PopCardListV2Component({

데이터 소스를 설정해주세요.

- ) : effectiveConfig?.hideUntilFiltered && externalFilters.size === 0 ? ( + ) : effectiveConfig?.hideUntilFiltered && effectiveExternalFilters.size === 0 ? (

필터를 선택하면 데이터가 표시됩니다.

@@ -1290,10 +1336,10 @@ export function PopCardListV2Component({
0} + checked={selectedKeys.size === statusFilteredRows.length && statusFilteredRows.length > 0} onChange={(e) => { if (e.target.checked) { - setSelectedKeys(new Set(filteredRows.map((r) => String(r.__cart_id ?? "")))); + setSelectedKeys(new Set(statusFilteredRows.map((r) => String(r.__cart_id ?? "")))); } else { setSelectedKeys(new Set()); } @@ -1306,6 +1352,41 @@ export function PopCardListV2Component({
)} + {/* 내장 MES 상태 탭 */} + {config?.showStatusTabs && statusCounts && hasProcessFlow && !selectMode && ( +
+ {MES_STATUS_TABS.map((tab) => { + const isActive = selectedStatusTab === tab.value; + const count = tab.value === "" + ? statusCounts.total + : (statusCounts.counts.get(tab.value) || 0); + return ( + + ); + })} +
+ )} +
{advancedOpen && (
+ {/* 내장 상태 탭 */} +
+ + onUpdate({ showStatusTabs: checked })} + /> +
+ {cfg.showStatusTabs && ( +

+ 카드 상단에 MES 상태 탭(전체/대기/접수가능/진행/완료)이 표시됩니다. + 별도 상태 바 컴포넌트가 필요 없습니다. +

+ )} + {/* 필터 전 비표시 */}
diff --git a/frontend/lib/registry/pop-components/types.ts b/frontend/lib/registry/pop-components/types.ts index e89165fe..8a1341df 100644 --- a/frontend/lib/registry/pop-components/types.ts +++ b/frontend/lib/registry/pop-components/types.ts @@ -1006,6 +1006,7 @@ export interface PopCardListV2Config { ownerSortColumn?: string; ownerFilterMode?: "priority" | "only"; workDetailConfig?: PopWorkDetailConfig; + showStatusTabs?: boolean; } /** 카드 컴포넌트가 하위 필터 적용 시 주입하는 가상 컬럼 키 */