import { Component, inject, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { ApiService, API } from '../../services/api.service'; @Component({ selector: 'app-apis', standalone: true, imports: [CommonModule, FormsModule, MatDialogModule], template: `

API Definitions

@if (loading) {
Loading APIs...
} @else if (filteredAPIs.length === 0) {

No APIs found.

} @else { @for (api of filteredAPIs; track api.name) { }
Name URL Method Timeout Auth Deleted Actions
{{ api.name }} {{ api.url }} {{ api.method }} {{ api.timeout_seconds }}s @if (api.auth?.enabled) { โœ“ } @else { โœ— } @if (api.deleted) { โœ“ } @else { โœ— } @if (!api.deleted) { }
} @if (message) {
{{ message }}
}
`, styles: [` .apis-container { .toolbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; h2 { margin: 0; } .toolbar-actions { display: flex; gap: 0.5rem; align-items: center; } } .search-input { padding: 0.375rem 0.75rem; border: 1px solid #ced4da; border-radius: 0.25rem; width: 200px; } .checkbox-label { display: flex; align-items: center; gap: 0.25rem; cursor: pointer; } .loading, .empty-state { text-align: center; padding: 3rem; background-color: white; border-radius: 0.25rem; p { margin-bottom: 1rem; color: #6c757d; } } .url-cell { max-width: 300px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .method-badge { padding: 0.25rem 0.5rem; border-radius: 0.25rem; font-size: 0.75rem; font-weight: 600; &.method-get { background-color: #28a745; color: white; } &.method-post { background-color: #007bff; color: white; } &.method-put { background-color: #ffc107; color: #333; } &.method-patch { background-color: #17a2b8; color: white; } &.method-delete { background-color: #dc3545; color: white; } } .status-badge { &.enabled { color: #28a745; } &.deleted { color: #dc3545; } } .actions { display: flex; gap: 0.25rem; } .action-btn { background: none; border: none; cursor: pointer; font-size: 1.1rem; padding: 0.25rem; border-radius: 0.25rem; &:hover { background-color: #f8f9fa; } &.danger:hover { background-color: #f8d7da; } } tr.deleted { opacity: 0.6; background-color: #f8f9fa; } } `] }) export class ApisComponent implements OnInit { private apiService = inject(ApiService); private dialog = inject(MatDialog); apis: API[] = []; filteredAPIs: API[] = []; loading = true; showDeleted = false; searchTerm = ''; message = ''; isError = false; ngOnInit() { this.loadAPIs(); } loadAPIs() { this.loading = true; this.apiService.getAPIs(this.showDeleted).subscribe({ next: (apis) => { this.apis = apis; this.filterAPIs(); this.loading = false; }, error: (err) => { this.showMessage('Failed to load APIs', true); this.loading = false; } }); } filterAPIs() { const term = this.searchTerm.toLowerCase(); this.filteredAPIs = this.apis.filter(api => api.name.toLowerCase().includes(term) || api.url.toLowerCase().includes(term) ); } async createAPI() { const { default: ApiEditDialogComponent } = await import('../../dialogs/api-edit-dialog/api-edit-dialog.component'); const dialogRef = this.dialog.open(ApiEditDialogComponent, { width: '800px', data: { mode: 'create' } }); dialogRef.afterClosed().subscribe((result: any) => { if (result) { this.loadAPIs(); } }); } async editAPI(api: API) { const { default: ApiEditDialogComponent } = await import('../../dialogs/api-edit-dialog/api-edit-dialog.component'); const dialogRef = this.dialog.open(ApiEditDialogComponent, { width: '800px', data: { mode: 'edit', api: { ...api } } }); dialogRef.afterClosed().subscribe((result: any) => { if (result) { this.loadAPIs(); } }); } testAPI(api: API) { this.apiService.testAPI(api).subscribe({ next: (result) => { if (result.success) { this.showMessage(`API test successful! Status: ${result.status_code}`, false); } else { this.showMessage(`API test failed: ${result.error}`, true); } }, error: (err) => { this.showMessage('Failed to test API', true); } }); } async duplicateAPI(api: API) { const { default: ApiEditDialogComponent } = await import('../../dialogs/api-edit-dialog/api-edit-dialog.component'); const dialogRef = this.dialog.open(ApiEditDialogComponent, { width: '800px', data: { mode: 'duplicate', api: { ...api } } }); dialogRef.afterClosed().subscribe((result: any) => { if (result) { this.loadAPIs(); } }); } deleteAPI(api: API) { if (confirm(`Are you sure you want to delete "${api.name}"?`)) { this.apiService.deleteAPI(api.name).subscribe({ next: () => { this.showMessage(`API "${api.name}" deleted successfully`, false); this.loadAPIs(); }, error: (err) => { this.showMessage(err.error?.detail || 'Failed to delete API', true); } }); } } importAPIs() { const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.onchange = async (event: any) => { const file = event.target.files[0]; if (!file) return; try { const text = await file.text(); const apis = JSON.parse(text); if (!Array.isArray(apis)) { this.showMessage('Invalid file format. Expected an array of APIs.', true); return; } let imported = 0; let failed = 0; for (const api of apis) { try { await this.apiService.createAPI(api).toPromise(); imported++; } catch (err: any) { failed++; console.error(`Failed to import API ${api.name}:`, err); } } this.showMessage(`Imported ${imported} APIs. ${failed} failed.`, failed > 0); this.loadAPIs(); } catch (error) { this.showMessage('Failed to read file', true); } }; input.click(); } exportAPIs() { const selectedAPIs = this.filteredAPIs.filter(api => !api.deleted); if (selectedAPIs.length === 0) { this.showMessage('No APIs to export', true); return; } const data = JSON.stringify(selectedAPIs, null, 2); const blob = new Blob([data], { type: 'application/json' }); const url = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `apis_export_${new Date().getTime()}.json`; link.click(); window.URL.revokeObjectURL(url); this.showMessage(`Exported ${selectedAPIs.length} APIs`, false); } private showMessage(message: string, isError: boolean) { this.message = message; this.isError = isError; setTimeout(() => { this.message = ''; }, 5000); } }