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: `
@if (loading) {
Loading APIs...
} @else if (filteredAPIs.length === 0) {
No APIs found.
} @else {
Name |
URL |
Method |
Timeout |
Auth |
Deleted |
Actions |
@for (api of filteredAPIs; track api.name) {
{{ 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);
}
}