()
| 255 | } |
| 256 | |
| 257 | export function AuditLogs() { |
| 258 | const [selectedTypes, setSelectedTypes] = useState<string[]>([]) |
| 259 | const [timeRange, setTimeRange] = useState<TimeRange>('Past 30 days') |
| 260 | const [customStartDate, setCustomStartDate] = useState('') |
| 261 | const [customEndDate, setCustomEndDate] = useState('') |
| 262 | const [datePickerOpen, setDatePickerOpen] = useState(false) |
| 263 | const previousTimeRangeRef = useRef<TimeRange>('Past 30 days') |
| 264 | const dateRangeAppliedRef = useRef(false) |
| 265 | const [searchTerm, setSearchTerm] = useState('') |
| 266 | const [debouncedSearch, setDebouncedSearch] = useState('') |
| 267 | const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null) |
| 268 | const [isVisuallyRefreshing, setIsVisuallyRefreshing] = useState(false) |
| 269 | const refreshTimersRef = useRef(new Set<number>()) |
| 270 | |
| 271 | useEffect(() => { |
| 272 | const trimmed = searchTerm.trim() |
| 273 | if (trimmed === debouncedSearch) return |
| 274 | debounceRef.current = setTimeout(() => { |
| 275 | setDebouncedSearch(trimmed) |
| 276 | }, 300) |
| 277 | return () => { |
| 278 | if (debounceRef.current) clearTimeout(debounceRef.current) |
| 279 | } |
| 280 | }, [searchTerm, debouncedSearch]) |
| 281 | |
| 282 | useEffect(() => { |
| 283 | const timers = refreshTimersRef.current |
| 284 | return () => { |
| 285 | for (const timerId of timers) window.clearTimeout(timerId) |
| 286 | } |
| 287 | }, []) |
| 288 | |
| 289 | const filters = useMemo<AuditLogFilters>(() => { |
| 290 | return { |
| 291 | search: debouncedSearch || undefined, |
| 292 | resourceType: selectedTypes.length > 0 ? selectedTypes.join(',') : undefined, |
| 293 | startDate: getStartDateFromTimeRange(timeRange, customStartDate)?.toISOString(), |
| 294 | endDate: getEndDateFromTimeRange(timeRange, customEndDate)?.toISOString(), |
| 295 | } |
| 296 | }, [debouncedSearch, selectedTypes, timeRange, customStartDate, customEndDate]) |
| 297 | |
| 298 | const { data, isLoading, isFetchingNextPage, hasNextPage, fetchNextPage, refetch } = |
| 299 | useAuditLogs(filters) |
| 300 | |
| 301 | const allEntries = useMemo(() => { |
| 302 | if (!data?.pages) return [] |
| 303 | return data.pages.flatMap((page) => page.data) |
| 304 | }, [data]) |
| 305 | |
| 306 | const typeDisplayLabel = |
| 307 | selectedTypes.length === 0 |
| 308 | ? 'All types' |
| 309 | : selectedTypes.length === 1 |
| 310 | ? RESOURCE_TYPE_OPTIONS.find((t) => t.value === selectedTypes[0])?.label || '1 selected' |
| 311 | : `${selectedTypes.length} types` |
| 312 | |
| 313 | const timeDisplayLabel = |
| 314 | timeRange === 'Custom range' && customStartDate && customEndDate |
nothing calls this directly
no test coverage detected