diff --git a/src/components/orders/OrderRegistration.tsx b/src/components/orders/OrderRegistration.tsx index 40938e54..59a613af 100644 --- a/src/components/orders/OrderRegistration.tsx +++ b/src/components/orders/OrderRegistration.tsx @@ -508,7 +508,7 @@ export function OrderRegistration({ // 폼 콘텐츠 렌더링 const renderFormContent = useCallback( () => ( -
+
{/* Validation 에러 Alert */} {Object.keys(fieldErrors).length > 0 && ( diff --git a/src/components/templates/IntegratedListTemplateV2.tsx b/src/components/templates/IntegratedListTemplateV2.tsx index 4a369cbd..ab1859f2 100644 --- a/src/components/templates/IntegratedListTemplateV2.tsx +++ b/src/components/templates/IntegratedListTemplateV2.tsx @@ -296,6 +296,7 @@ export function IntegratedListTemplateV2({ const [accumulatedMobileData, setAccumulatedMobileData] = useState([]); const [lastAccumulatedPage, setLastAccumulatedPage] = useState(0); const mobileScrollSentinelRef = useRef(null); + const mobileCardAreaRef = useRef(null); // 클라이언트 사이드 인피니티용 (allData가 있는 경우) const [clientDisplayCount, setClientDisplayCount] = useState(mobileDisplayCount || 20); @@ -404,6 +405,45 @@ export function IntegratedListTemplateV2({ return () => observer.disconnect(); }, [isServerSidePagination, allData, clientDisplayCount, enableMobileInfinityScroll, isMobileLoading, pagination.currentPage, pagination.totalPages, handleLoadMoreClient, handleLoadMoreMobile]); + // ===== 모바일 카드 영역 내부 스크롤 컨테인먼트 ===== + // 카드 영역이 뷰포트 남은 높이만큼만 차지하고, 내부에서만 스크롤되도록 설정 + // → 헤더/검색/탭은 항상 보이고, 카드만 스크롤 + useEffect(() => { + const el = mobileCardAreaRef.current; + if (!el) return; + + const applyScrollContainment = () => { + // xl(1280px) 이상은 데스크톱 → 테이블+페이지네이션 사용, 컨테인먼트 해제 + if (window.innerWidth >= 1280) { + el.style.maxHeight = ''; + el.style.overflowY = ''; + el.style.overscrollBehavior = ''; + return; + } + + const rect = el.getBoundingClientRect(); + const available = window.innerHeight - rect.top - 16; // 16px 하단 여유 + + if (available > 200) { + el.style.maxHeight = `${available}px`; + el.style.overflowY = 'auto'; + el.style.overscrollBehavior = 'contain'; // 스크롤 누수 방지 + } + }; + + // 페이지 스크롤을 최상단으로 리셋 후 정확한 위치 측정 + window.scrollTo(0, 0); + el.scrollTop = 0; + + const raf = requestAnimationFrame(applyScrollContainment); + window.addEventListener('resize', applyScrollContainment); + + return () => { + cancelAnimationFrame(raf); + window.removeEventListener('resize', applyScrollContainment); + }; + }, [activeTab, isLoading]); + const startIndex = (pagination.currentPage - 1) * pagination.itemsPerPage; const allSelected = selectedItems.size === data.length && data.length > 0; @@ -773,7 +813,7 @@ export function IntegratedListTemplateV2({ )} {/* 모바일/태블릿/소형 노트북 (~1279px) 카드 뷰 */} -
+
{isLoading ? (