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 { MatTableModule } from '@angular/material/table';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatChipsModule } from '@angular/material/chips';
import { MatMenuModule } from '@angular/material/menu';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
import { MatDividerModule } from '@angular/material/divider';
import { ApiService, API } from '../../services/api.service';
@Component({
selector: 'app-apis',
standalone: true,
imports: [
CommonModule,
FormsModule,
MatDialogModule,
MatTableModule,
MatButtonModule,
MatIconModule,
MatFormFieldModule,
MatInputModule,
MatCheckboxModule,
MatProgressBarModule,
MatChipsModule,
MatMenuModule,
MatTooltipModule,
MatSnackBarModule,
MatDividerModule
],
template: `
@if (!loading && filteredAPIs.length === 0) {
api
No APIs found.
} @else if (!loading) {
Name |
{{ api.name }} |
URL |
{{ api.url }}
|
Method |
{{ api.method }}
|
Timeout |
{{ api.timeout_seconds }}s |
Auth |
{{ api.auth?.enabled ? 'lock' : 'lock_open' }}
|
Deleted |
@if (api.deleted) {
delete
}
|
Actions |
@if (!api.deleted) {
}
|
}
`,
styles: [`
.apis-container {
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
flex-wrap: wrap;
gap: 16px;
h2 {
margin: 0;
font-size: 24px;
}
.toolbar-actions {
display: flex;
gap: 16px;
align-items: center;
flex-wrap: wrap;
.search-field {
width: 250px;
}
}
}
.empty-state {
text-align: center;
padding: 60px 20px;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
mat-icon {
font-size: 64px;
width: 64px;
height: 64px;
color: #e0e0e0;
margin-bottom: 16px;
}
p {
margin-bottom: 24px;
color: #666;
font-size: 16px;
}
}
.apis-table {
width: 100%;
background: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
.url-cell {
max-width: 300px;
span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: block;
}
}
mat-chip {
font-size: 12px;
min-height: 24px;
padding: 4px 12px;
&.method-get { background-color: #4caf50; color: white; }
&.method-post { background-color: #2196f3; color: white; }
&.method-put { background-color: #ff9800; color: white; }
&.method-patch { background-color: #9c27b0; color: white; }
&.method-delete { background-color: #f44336; color: white; }
}
tr.mat-mdc-row {
cursor: pointer;
transition: background-color 0.2s;
&:hover {
background-color: #f5f5f5;
}
&.deleted-row {
opacity: 0.6;
background-color: #fafafa;
}
}
}
}
::ng-deep {
.mat-mdc-form-field {
font-size: 14px;
}
.mat-mdc-checkbox {
.mdc-form-field {
font-size: 14px;
}
}
}
`]
})
export class ApisComponent implements OnInit {
private apiService = inject(ApiService);
private dialog = inject(MatDialog);
private snackBar = inject(MatSnackBar);
apis: API[] = [];
filteredAPIs: API[] = [];
loading = true;
showDeleted = false;
searchTerm = '';
displayedColumns: string[] = ['name', 'url', 'method', 'timeout', 'auth', 'deleted', 'actions'];
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.snackBar.open('Failed to load APIs', 'Close', {
duration: 5000,
panelClass: 'error-snackbar'
});
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();
}
});
}
async testAPI(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: 'test', // Test modunda açıyoruz
api: { ...api },
activeTab: 4 // Test tab'ını aktif yap (0-based index)
}
});
dialogRef.afterClosed().subscribe((result: any) => {
if (result) {
this.loadAPIs();
}
});
}
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();
}
});
}
async deleteAPI(api: API) {
const { default: ConfirmDialogComponent } = await import('../../dialogs/confirm-dialog/confirm-dialog.component');
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
width: '400px',
data: {
title: 'Delete API',
message: `Are you sure you want to delete "${api.name}"?`,
confirmText: 'Delete',
confirmColor: 'warn'
}
});
dialogRef.afterClosed().subscribe((confirmed) => {
if (confirmed) {
this.apiService.deleteAPI(api.name).subscribe({
next: () => {
this.snackBar.open(`API "${api.name}" deleted successfully`, 'Close', {
duration: 3000
});
this.loadAPIs();
},
error: (err) => {
this.snackBar.open(err.error?.detail || 'Failed to delete API', 'Close', {
duration: 5000,
panelClass: 'error-snackbar'
});
}
});
}
});
}
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.snackBar.open('Invalid file format. Expected an array of APIs.', 'Close', {
duration: 5000,
panelClass: 'error-snackbar'
});
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.snackBar.open(`Imported ${imported} APIs. ${failed} failed.`, 'Close', {
duration: 5000
});
this.loadAPIs();
} catch (error) {
this.snackBar.open('Failed to read file', 'Close', {
duration: 5000,
panelClass: 'error-snackbar'
});
}
};
input.click();
}
exportAPIs() {
const selectedAPIs = this.filteredAPIs.filter(api => !api.deleted);
if (selectedAPIs.length === 0) {
this.snackBar.open('No APIs to export', 'Close', {
duration: 3000
});
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.snackBar.open(`Exported ${selectedAPIs.length} APIs`, 'Close', {
duration: 3000
});
}
}