feat(WEB): UniversalListPage 날짜 범위 필터 자동 적용 기능 추가

UniversalListPage:
- dateRangeSelector.dateField 설정 시 클라이언트 사이드 날짜 필터 자동 적용
- 종료일 23:59:59까지 포함하도록 처리

AttendanceManagement:
- 사유 등록 버튼을 extraFilters에서 headerActions로 이동

IntegratedListTemplateV2:
- 날짜 범위 관련 타입 및 처리 개선

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-01-28 09:57:12 +09:00
parent 3157fb9401
commit 805063c686
6 changed files with 40 additions and 15 deletions

View File

@@ -544,7 +544,7 @@ export function IntegratedListTemplateV2<T = any>({
{/* 레이아웃: [달력] [프리셋버튼] [검색창] -------------- [추가버튼들] [등록버튼] (오른쪽 끝) */}
{(dateRangeSelector?.enabled || createButton || headerActions || (hideSearch && onSearchChange)) && (
isLoading ? renderHeaderActionSkeleton() : (
<div className="flex flex-col xl:flex-row xl:items-center gap-2 w-full">
<div className="flex flex-col xl:flex-row xl:flex-wrap xl:items-center xl:justify-between gap-2 w-full">
{/* 날짜 범위 선택기 + 검색창 (왼쪽) */}
{dateRangeSelector?.enabled ? (
<DateRangeSelector
@@ -589,9 +589,9 @@ export function IntegratedListTemplateV2<T = any>({
</div>
)
)}
{/* 버튼 영역 (오른쪽 끝으로 통합) */}
{/* 버튼 영역 (오른쪽 배치, 공간 부족시 자연스럽게 줄바꿈) */}
{(headerActions || createButton) && (
<div className="flex items-center gap-2 ml-auto shrink-0">
<div className="flex items-center gap-2 shrink-0">
{/* 헤더 액션 (엑셀 다운로드 등 추가 버튼들) */}
{headerActions}
{/* 등록 버튼 */}

View File

@@ -122,6 +122,22 @@ export function UniversalListPage<T>({
);
}
// 날짜 범위 필터 (dateRangeSelector.dateField 설정 시 자동 적용)
const { dateRangeSelector } = config;
if (dateRangeSelector?.enabled && dateRangeSelector.dateField && dateRangeSelector.startDate && dateRangeSelector.endDate) {
const dateField = dateRangeSelector.dateField;
const start = new Date(dateRangeSelector.startDate);
const end = new Date(dateRangeSelector.endDate);
end.setHours(23, 59, 59, 999); // 종료일 끝까지 포함
filtered = filtered.filter((item) => {
const itemDate = (item as Record<string, unknown>)[dateField];
if (!itemDate) return false;
const date = new Date(String(itemDate));
return date >= start && date <= end;
});
}
// 커스텀 정렬 함수
if (config.customSortFn) {
filtered = config.customSortFn(filtered, filters);
@@ -152,7 +168,7 @@ export function UniversalListPage<T>({
}
return filtered;
}, [rawData, activeTab, searchValue, filters, sortBy, sortOrder, config.clientSideFiltering, config.tabFilter, config.searchFilter, config.customFilterFn, config.customSortFn]);
}, [rawData, activeTab, searchValue, filters, sortBy, sortOrder, config.clientSideFiltering, config.tabFilter, config.searchFilter, config.customFilterFn, config.customSortFn, config.dateRangeSelector]);
// 클라이언트 사이드 페이지네이션
const paginatedData = useMemo(() => {

View File

@@ -277,6 +277,12 @@ export interface UniversalListConfig<T> {
onEndDateChange?: (date: string) => void;
/** 추가 액션 (검색창 등) - presetsPosition이 'below'일 때 달력 옆에 배치됨 */
extraActions?: ReactNode;
/**
* 날짜 필터링에 사용할 필드명 (clientSideFiltering: true 시 자동 필터링)
* - 예: 'hireDate', 'createdAt', 'orderDate' 등
* - 설정 시 startDate ~ endDate 범위로 해당 필드 자동 필터링
*/
dateField?: string;
};
/**
* 등록 버튼 (오른쪽 끝 배치)