import { Component, EventEmitter, Output, inject, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; import { HttpClient } from '@angular/common/http'; interface ActivityLog { id: number; timestamp: string; user: string; action: string; entity_type: string; entity_id: any; entity_name: string; details?: string; } @Component({ selector: 'app-activity-log', standalone: true, imports: [CommonModule], template: `

🔔 Recent Activities

@if (loading) {
Loading...
} @else if (activities.length === 0) {
No recent activities
} @else { @for (activity of activities; track activity.id) {
{{ getRelativeTime(activity.timestamp) }}
{{ activity.user }} {{ getActionText(activity) }} {{ activity.entity_name }}
} }
`, styles: [` .activity-log-dropdown { position: absolute; top: 100%; right: 0; width: 350px; background: white; border: 1px solid #dee2e6; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); z-index: 1000; margin-top: 0.5rem; } .activity-header { padding: 1rem; border-bottom: 1px solid #dee2e6; display: flex; justify-content: space-between; align-items: center; h3 { margin: 0; font-size: 1.1rem; } .close-btn { background: none; border: none; font-size: 1.5rem; cursor: pointer; color: #6c757d; &:hover { color: #333; } } } .activity-list { max-height: 300px; overflow-y: auto; } .activity-item { padding: 0.75rem 1rem; border-bottom: 1px solid #f0f0f0; &:hover { background-color: #f8f9fa; } .activity-time { font-size: 0.85rem; color: #6c757d; margin-bottom: 0.25rem; } .activity-content { font-size: 0.9rem; em { color: #007bff; font-style: normal; } } } .activity-footer { padding: 0.75rem; border-top: 1px solid #dee2e6; text-align: center; } .loading, .empty { padding: 2rem; text-align: center; color: #6c757d; } `] }) export class ActivityLogComponent implements OnInit { @Output() close = new EventEmitter(); private http = inject(HttpClient); activities: ActivityLog[] = []; loading = true; ngOnInit() { this.loadActivities(); } loadActivities() { this.loading = true; this.http.get('/api/activity-log?limit=10') .subscribe({ next: (data) => { this.activities = data; this.loading = false; }, error: () => { this.loading = false; } }); } loadMore() { // TODO: Implement full activity log view console.log('Load more activities'); } getRelativeTime(timestamp: string): string { const date = new Date(timestamp); const now = new Date(); const diff = now.getTime() - date.getTime(); const minutes = Math.floor(diff / 60000); const hours = Math.floor(diff / 3600000); const days = Math.floor(diff / 86400000); if (minutes < 1) return 'just now'; if (minutes < 60) return `${minutes} min ago`; if (hours < 24) return `${hours} hour${hours > 1 ? 's' : ''} ago`; return `${days} day${days > 1 ? 's' : ''} ago`; } getActionText(activity: ActivityLog): string { const actions: Record = { 'CREATE_PROJECT': 'created project', 'UPDATE_PROJECT': 'updated project', 'DELETE_PROJECT': 'deleted project', 'PUBLISH_VERSION': 'published version', 'CREATE_VERSION': 'created version', 'UPDATE_VERSION': 'updated version', 'CREATE_API': 'created API', 'UPDATE_API': 'updated API', 'DELETE_API': 'deleted API', 'UPDATE_ENVIRONMENT': 'updated environment', 'IMPORT_PROJECT': 'imported project' }; return actions[activity.action] || activity.action.toLowerCase().replace('_', ' '); } }