/** * Activity Feed Component * Real-time activity feed with filtering and auto-refresh */ class ActivityFeed { constructor(containerId, options = {}) { this.container = document.getElementById(containerId); if (!this.container) { console.error(`Activity feed container not found: ${containerId}`); return; } this.options = { limit: options.limit || 50, autoRefresh: options.autoRefresh !== false, refreshInterval: options.refreshInterval || 30000, // 30 seconds filters: options.filters || {}, ...options }; this.activities = []; this.page = 1; this.hasMore = true; this.loading = false; this.refreshTimer = null; this.init(); } init() { this.render(); this.loadActivities(); this.setupAutoRefresh(); this.setupWebSocket(); } async loadActivities(page = 1, append = false) { if (this.loading) return; this.loading = true; this.showLoading(); try { const params = new URLSearchParams({ page: page.toString(), limit: this.options.limit.toString(), ...this.options.filters }); const response = await fetch(`/api/activity?${params}`); const data = await response.json(); if (append) { this.activities = [...this.activities, ...data.activities]; } else { this.activities = data.activities; } this.hasMore = data.pagination.has_next; this.page = data.pagination.page; this.render(); } catch (error) { console.error('Error loading activities:', error); this.showError('Failed to load activities'); } finally { this.loading = false; this.hideLoading(); } } render() { if (!this.container) return; if (this.activities.length === 0 && !this.loading) { this.container.innerHTML = `
No activities found