import { Component, EventEmitter, Output, inject, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; import { HttpClient } from '@angular/common/http'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatPaginatorModule, PageEvent } from '@angular/material/paginator'; import { MatListModule } from '@angular/material/list'; import { MatCardModule } from '@angular/material/card'; import { MatDividerModule } from '@angular/material/divider'; interface ActivityLog { id: number; timestamp: string; user: string; action: string; entity_type: string; entity_id: any; entity_name: string; details?: string; } interface ActivityLogResponse { items: ActivityLog[]; total: number; page: number; limit: number; pages: number; } @Component({ selector: 'app-activity-log', standalone: true, imports: [ CommonModule, MatProgressSpinnerModule, MatButtonModule, MatIconModule, MatPaginatorModule, MatListModule, MatCardModule, MatDividerModule ], template: ` notifications Recent Activities close @if (loading && activities.length === 0) { } @else if (activities.length === 0) { inbox No activities found } @else { @for (activity of activities; track activity.id) { {{ getActivityIcon(activity.action) }} {{ getRelativeTime(activity.timestamp) }} {{ activity.user }} {{ getActionText(activity) }} {{ activity.entity_name }} @if (activity.details) { • {{ activity.details }} } @if (!$last) { } } } pageSize"> 0"> open_in_new View All Activities `, styles: [` .activity-log-dropdown { width: 450px; max-height: 600px; display: flex; flex-direction: column; overflow: hidden; } mat-card-header { display: flex; justify-content: space-between; align-items: center; padding: 16px; background-color: #424242; // Koyu gri (önceden #f5f5f5 idi) color: white; // Yazıları beyaz yap mat-card-title { margin: 0; display: flex; align-items: center; gap: 8px; font-size: 18px; color: white; // Title'ı da beyaz yap mat-icon { font-size: 24px; width: 24px; height: 24px; color: white; // Icon'u da beyaz yap } } button { color: white; // Close button'u beyaz yap } } mat-card-content { flex: 1; overflow-y: auto; padding: 0; min-height: 200px; max-height: 400px; } .activity-list { padding: 0; mat-list-item { height: auto; min-height: 72px; padding: 12px 16px; &:hover { background-color: #f5f5f5; } .activity-time { font-size: 12px; color: #666; } strong { color: #1976d2; margin-right: 4px; } em { color: #673ab7; font-style: normal; font-weight: 500; margin: 0 4px; } .details { color: #666; font-size: 12px; margin-left: 4px; } } } mat-card-actions { padding: 0; margin: 0; mat-paginator { background: transparent; } .full-width { width: 100%; margin: 0; } } .loading, .empty { padding: 60px 20px; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #666; mat-icon { font-size: 48px; width: 48px; height: 48px; color: #e0e0e0; margin-bottom: 16px; } p { margin: 0; font-size: 14px; } } ::ng-deep { .mat-mdc-list-item-unscoped-content { display: block; } .mat-mdc-paginator { .mat-mdc-paginator-container { padding: 8px; justify-content: center; } } } `] }) export class ActivityLogComponent implements OnInit { @Output() close = new EventEmitter(); private http = inject(HttpClient); activities: ActivityLog[] = []; loading = false; currentPage = 1; pageSize = 10; totalItems = 0; totalPages = 0; ngOnInit() { this.loadActivities(); } loadActivities(page: number = 1) { this.loading = true; this.currentPage = page; // Backend sadece limit parametresi alıyor, page almıyor const limit = this.pageSize * page; // Toplam kaç kayıt istediğimizi hesapla this.http.get( `/api/activity-log?limit=${limit}` ).subscribe({ next: (response) => { // Response direkt array olarak geliyor const allActivities = response || []; // Manuel pagination yap const startIndex = (page - 1) * this.pageSize; const endIndex = startIndex + this.pageSize; this.activities = allActivities.slice(startIndex, endIndex); this.totalItems = allActivities.length; this.totalPages = Math.ceil(allActivities.length / this.pageSize); this.loading = false; }, error: (error) => { console.error('Failed to load activities:', error); this.activities = []; // Hata durumunda boş array this.loading = false; } }); } onPageChange(event: PageEvent) { this.pageSize = event.pageSize; this.loadActivities(event.pageIndex + 1); } openFullView() { // TODO: Implement full activity log view console.log('Open full activity log view'); this.close.emit(); } getRelativeTime(timestamp: string): string { const date = new Date(timestamp); const now = new Date(); const diffMs = now.getTime() - date.getTime(); const diffMins = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMs / 3600000); const diffDays = Math.floor(diffMs / 86400000); if (diffMins < 1) return 'just now'; if (diffMins < 60) return `${diffMins} min ago`; if (diffHours < 24) return `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`; if (diffDays < 7) return `${diffDays} day${diffDays > 1 ? 's' : ''} ago`; return date.toLocaleDateString(); } getActionText(activity: ActivityLog): string { const actions: Record = { 'CREATE_PROJECT': 'created project', 'UPDATE_PROJECT': 'updated project', 'DELETE_PROJECT': 'deleted project', 'ENABLE_PROJECT': 'enabled project', 'DISABLE_PROJECT': 'disabled project', 'PUBLISH_VERSION': 'published version of', 'CREATE_VERSION': 'created version for', 'UPDATE_VERSION': 'updated version of', 'DELETE_VERSION': 'deleted version from', 'CREATE_API': 'created API', 'UPDATE_API': 'updated API', 'DELETE_API': 'deleted API', 'UPDATE_ENVIRONMENT': 'updated environment', 'IMPORT_PROJECT': 'imported project', 'CHANGE_PASSWORD': 'changed password' }; return actions[activity.action] || activity.action.toLowerCase().replace(/_/g, ' '); } getActivityIcon(action: string): string { if (action.includes('CREATE')) return 'add_circle'; if (action.includes('UPDATE')) return 'edit'; if (action.includes('DELETE')) return 'delete'; if (action.includes('ENABLE')) return 'check_circle'; if (action.includes('DISABLE')) return 'cancel'; if (action.includes('PUBLISH')) return 'publish'; if (action.includes('IMPORT')) return 'cloud_upload'; if (action.includes('PASSWORD')) return 'lock'; return 'info'; } trackByActivityId(index: number, activity: ActivityLog): number { return activity.id; } isLast(activity: ActivityLog): boolean { return this.activities.indexOf(activity) === this.activities.length - 1; } }
No activities found