ciyidogan commited on
Commit
d9e8ba7
·
verified ·
1 Parent(s): 548c527

Update flare-ui/src/app/components/apis/apis.component.ts

Browse files
flare-ui/src/app/components/apis/apis.component.ts CHANGED
@@ -1,299 +1,333 @@
1
- import { Component, inject, OnInit } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
- import { FormsModule } from '@angular/forms';
4
- import { ApiService, API } from '../../services/api.service';
5
-
6
- @Component({
7
- selector: 'app-apis',
8
- standalone: true,
9
- imports: [CommonModule, FormsModule],
10
- template: `
11
- <div class="apis-container">
12
- <div class="toolbar">
13
- <h2>API Definitions</h2>
14
- <div class="toolbar-actions">
15
- <button class="btn btn-primary" (click)="createAPI()">
16
- New API
17
- </button>
18
- <button class="btn btn-secondary" disabled>
19
- Import
20
- </button>
21
- <button class="btn btn-secondary" disabled>
22
- Export
23
- </button>
24
- <input
25
- type="text"
26
- placeholder="Search APIs..."
27
- [(ngModel)]="searchTerm"
28
- (input)="filterAPIs()"
29
- class="search-input"
30
- >
31
- <label class="checkbox-label">
32
- <input
33
- type="checkbox"
34
- [(ngModel)]="showDeleted"
35
- (change)="loadAPIs()"
36
- >
37
- Display Deleted
38
- </label>
39
- </div>
40
- </div>
41
-
42
- @if (loading) {
43
- <div class="loading">
44
- <span class="spinner"></span> Loading APIs...
45
- </div>
46
- } @else if (filteredAPIs.length === 0) {
47
- <div class="empty-state">
48
- <p>No APIs found.</p>
49
- <button class="btn btn-primary" (click)="createAPI()">
50
- Create your first API
51
- </button>
52
- </div>
53
- } @else {
54
- <table class="table">
55
- <thead>
56
- <tr>
57
- <th>Name</th>
58
- <th>URL</th>
59
- <th>Method</th>
60
- <th>Timeout</th>
61
- <th>Auth</th>
62
- <th>Deleted</th>
63
- <th>Actions</th>
64
- </tr>
65
- </thead>
66
- <tbody>
67
- @for (api of filteredAPIs; track api.name) {
68
- <tr [class.deleted]="api.deleted">
69
- <td>{{ api.name }}</td>
70
- <td class="url-cell">{{ api.url }}</td>
71
- <td>
72
- <span class="method-badge" [class]="'method-' + api.method.toLowerCase()">
73
- {{ api.method }}
74
- </span>
75
- </td>
76
- <td>{{ api.timeout_seconds }}s</td>
77
- <td>
78
- @if (api.auth?.enabled) {
79
- <span class="status-badge enabled">✓</span>
80
- } @else {
81
- <span class="status-badge">✗</span>
82
- }
83
- </td>
84
- <td>
85
- @if (api.deleted) {
86
- <span class="status-badge deleted">✓</span>
87
- } @else {
88
- <span class="status-badge">✗</span>
89
- }
90
- </td>
91
- <td class="actions">
92
- <button class="action-btn" title="Edit" (click)="editAPI(api)">
93
- 🖊️
94
- </button>
95
- <button class="action-btn" title="Test" (click)="testAPI(api)">
96
- 🧪
97
- </button>
98
- <button class="action-btn" title="Duplicate" (click)="duplicateAPI(api)">
99
- 📋
100
- </button>
101
- @if (!api.deleted) {
102
- <button class="action-btn danger" title="Delete" (click)="deleteAPI(api)">
103
- 🗑️
104
- </button>
105
- }
106
- </td>
107
- </tr>
108
- }
109
- </tbody>
110
- </table>
111
- }
112
-
113
- @if (message) {
114
- <div class="alert" [class.alert-success]="!isError" [class.alert-danger]="isError">
115
- {{ message }}
116
- </div>
117
- }
118
- </div>
119
- `,
120
- styles: [`
121
- .apis-container {
122
- .toolbar {
123
- display: flex;
124
- justify-content: space-between;
125
- align-items: center;
126
- margin-bottom: 1.5rem;
127
-
128
- h2 {
129
- margin: 0;
130
- }
131
-
132
- .toolbar-actions {
133
- display: flex;
134
- gap: 0.5rem;
135
- align-items: center;
136
- }
137
- }
138
-
139
- .search-input {
140
- padding: 0.375rem 0.75rem;
141
- border: 1px solid #ced4da;
142
- border-radius: 0.25rem;
143
- width: 200px;
144
- }
145
-
146
- .checkbox-label {
147
- display: flex;
148
- align-items: center;
149
- gap: 0.25rem;
150
- cursor: pointer;
151
- }
152
-
153
- .loading, .empty-state {
154
- text-align: center;
155
- padding: 3rem;
156
- background-color: white;
157
- border-radius: 0.25rem;
158
-
159
- p {
160
- margin-bottom: 1rem;
161
- color: #6c757d;
162
- }
163
- }
164
-
165
- .url-cell {
166
- max-width: 300px;
167
- overflow: hidden;
168
- text-overflow: ellipsis;
169
- white-space: nowrap;
170
- }
171
-
172
- .method-badge {
173
- padding: 0.25rem 0.5rem;
174
- border-radius: 0.25rem;
175
- font-size: 0.75rem;
176
- font-weight: 600;
177
-
178
- &.method-get { background-color: #28a745; color: white; }
179
- &.method-post { background-color: #007bff; color: white; }
180
- &.method-put { background-color: #ffc107; color: #333; }
181
- &.method-patch { background-color: #17a2b8; color: white; }
182
- &.method-delete { background-color: #dc3545; color: white; }
183
- }
184
-
185
- .status-badge {
186
- &.enabled { color: #28a745; }
187
- &.deleted { color: #dc3545; }
188
- }
189
-
190
- .actions {
191
- display: flex;
192
- gap: 0.25rem;
193
- }
194
-
195
- .action-btn {
196
- background: none;
197
- border: none;
198
- cursor: pointer;
199
- font-size: 1.1rem;
200
- padding: 0.25rem;
201
- border-radius: 0.25rem;
202
-
203
- &:hover {
204
- background-color: #f8f9fa;
205
- }
206
-
207
- &.danger:hover {
208
- background-color: #f8d7da;
209
- }
210
- }
211
-
212
- tr.deleted {
213
- opacity: 0.6;
214
- background-color: #f8f9fa;
215
- }
216
- }
217
- `]
218
- })
219
- export class ApisComponent implements OnInit {
220
- private apiService = inject(ApiService);
221
-
222
- apis: API[] = [];
223
- filteredAPIs: API[] = [];
224
- loading = true;
225
- showDeleted = false;
226
- searchTerm = '';
227
- message = '';
228
- isError = false;
229
-
230
- ngOnInit() {
231
- this.loadAPIs();
232
- }
233
-
234
- loadAPIs() {
235
- this.loading = true;
236
- this.apiService.getAPIs(this.showDeleted).subscribe({
237
- next: (apis) => {
238
- this.apis = apis;
239
- this.filterAPIs();
240
- this.loading = false;
241
- },
242
- error: (err) => {
243
- this.showMessage('Failed to load APIs', true);
244
- this.loading = false;
245
- }
246
- });
247
- }
248
-
249
- filterAPIs() {
250
- const term = this.searchTerm.toLowerCase();
251
- this.filteredAPIs = this.apis.filter(api =>
252
- api.name.toLowerCase().includes(term) ||
253
- api.url.toLowerCase().includes(term)
254
- );
255
- }
256
-
257
- createAPI() {
258
- // TODO: Open create dialog
259
- console.log('Create API - not implemented yet');
260
- }
261
-
262
- editAPI(api: API) {
263
- // TODO: Open edit dialog
264
- console.log('Edit API:', api.name);
265
- }
266
-
267
- testAPI(api: API) {
268
- // TODO: Test API
269
- console.log('Test API:', api.name);
270
- }
271
-
272
- duplicateAPI(api: API) {
273
- // TODO: Duplicate API
274
- console.log('Duplicate API:', api.name);
275
- }
276
-
277
- deleteAPI(api: API) {
278
- if (confirm(`Are you sure you want to delete "${api.name}"?`)) {
279
- this.apiService.deleteAPI(api.name).subscribe({
280
- next: () => {
281
- this.showMessage(`API "${api.name}" deleted successfully`, false);
282
- this.loadAPIs();
283
- },
284
- error: (err) => {
285
- this.showMessage(err.error?.detail || 'Failed to delete API', true);
286
- }
287
- });
288
- }
289
- }
290
-
291
- private showMessage(message: string, isError: boolean) {
292
- this.message = message;
293
- this.isError = isError;
294
-
295
- setTimeout(() => {
296
- this.message = '';
297
- }, 5000);
298
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  }
 
1
+ import { Component, inject, OnInit } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormsModule } from '@angular/forms';
4
+ import { ApiService, API } from '../../services/api.service';
5
+
6
+ @Component({
7
+ selector: 'app-apis',
8
+ standalone: true,
9
+ imports: [CommonModule, FormsModule],
10
+ template: `
11
+ <div class="apis-container">
12
+ <div class="toolbar">
13
+ <h2>API Definitions</h2>
14
+ <div class="toolbar-actions">
15
+ <button class="btn btn-primary" (click)="createAPI()">
16
+ New API
17
+ </button>
18
+ <button class="btn btn-secondary" disabled>
19
+ Import
20
+ </button>
21
+ <button class="btn btn-secondary" disabled>
22
+ Export
23
+ </button>
24
+ <input
25
+ type="text"
26
+ placeholder="Search APIs..."
27
+ [(ngModel)]="searchTerm"
28
+ (input)="filterAPIs()"
29
+ class="search-input"
30
+ >
31
+ <label class="checkbox-label">
32
+ <input
33
+ type="checkbox"
34
+ [(ngModel)]="showDeleted"
35
+ (change)="loadAPIs()"
36
+ >
37
+ Display Deleted
38
+ </label>
39
+ </div>
40
+ </div>
41
+
42
+ @if (loading) {
43
+ <div class="loading">
44
+ <span class="spinner"></span> Loading APIs...
45
+ </div>
46
+ } @else if (filteredAPIs.length === 0) {
47
+ <div class="empty-state">
48
+ <p>No APIs found.</p>
49
+ <button class="btn btn-primary" (click)="createAPI()">
50
+ Create your first API
51
+ </button>
52
+ </div>
53
+ } @else {
54
+ <table class="table">
55
+ <thead>
56
+ <tr>
57
+ <th>Name</th>
58
+ <th>URL</th>
59
+ <th>Method</th>
60
+ <th>Timeout</th>
61
+ <th>Auth</th>
62
+ <th>Deleted</th>
63
+ <th>Actions</th>
64
+ </tr>
65
+ </thead>
66
+ <tbody>
67
+ @for (api of filteredAPIs; track api.name) {
68
+ <tr [class.deleted]="api.deleted">
69
+ <td>{{ api.name }}</td>
70
+ <td class="url-cell">{{ api.url }}</td>
71
+ <td>
72
+ <span class="method-badge" [class]="'method-' + api.method.toLowerCase()">
73
+ {{ api.method }}
74
+ </span>
75
+ </td>
76
+ <td>{{ api.timeout_seconds }}s</td>
77
+ <td>
78
+ @if (api.auth?.enabled) {
79
+ <span class="status-badge enabled">✓</span>
80
+ } @else {
81
+ <span class="status-badge">✗</span>
82
+ }
83
+ </td>
84
+ <td>
85
+ @if (api.deleted) {
86
+ <span class="status-badge deleted">✓</span>
87
+ } @else {
88
+ <span class="status-badge">✗</span>
89
+ }
90
+ </td>
91
+ <td class="actions">
92
+ <button class="action-btn" title="Edit" (click)="editAPI(api)">
93
+ 🖊️
94
+ </button>
95
+ <button class="action-btn" title="Test" (click)="testAPI(api)">
96
+ 🧪
97
+ </button>
98
+ <button class="action-btn" title="Duplicate" (click)="duplicateAPI(api)">
99
+ 📋
100
+ </button>
101
+ @if (!api.deleted) {
102
+ <button class="action-btn danger" title="Delete" (click)="deleteAPI(api)">
103
+ 🗑️
104
+ </button>
105
+ }
106
+ </td>
107
+ </tr>
108
+ }
109
+ </tbody>
110
+ </table>
111
+ }
112
+
113
+ @if (message) {
114
+ <div class="alert" [class.alert-success]="!isError" [class.alert-danger]="isError">
115
+ {{ message }}
116
+ </div>
117
+ }
118
+ </div>
119
+ `,
120
+ styles: [`
121
+ .apis-container {
122
+ .toolbar {
123
+ display: flex;
124
+ justify-content: space-between;
125
+ align-items: center;
126
+ margin-bottom: 1.5rem;
127
+
128
+ h2 {
129
+ margin: 0;
130
+ }
131
+
132
+ .toolbar-actions {
133
+ display: flex;
134
+ gap: 0.5rem;
135
+ align-items: center;
136
+ }
137
+ }
138
+
139
+ .search-input {
140
+ padding: 0.375rem 0.75rem;
141
+ border: 1px solid #ced4da;
142
+ border-radius: 0.25rem;
143
+ width: 200px;
144
+ }
145
+
146
+ .checkbox-label {
147
+ display: flex;
148
+ align-items: center;
149
+ gap: 0.25rem;
150
+ cursor: pointer;
151
+ }
152
+
153
+ .loading, .empty-state {
154
+ text-align: center;
155
+ padding: 3rem;
156
+ background-color: white;
157
+ border-radius: 0.25rem;
158
+
159
+ p {
160
+ margin-bottom: 1rem;
161
+ color: #6c757d;
162
+ }
163
+ }
164
+
165
+ .url-cell {
166
+ max-width: 300px;
167
+ overflow: hidden;
168
+ text-overflow: ellipsis;
169
+ white-space: nowrap;
170
+ }
171
+
172
+ .method-badge {
173
+ padding: 0.25rem 0.5rem;
174
+ border-radius: 0.25rem;
175
+ font-size: 0.75rem;
176
+ font-weight: 600;
177
+
178
+ &.method-get { background-color: #28a745; color: white; }
179
+ &.method-post { background-color: #007bff; color: white; }
180
+ &.method-put { background-color: #ffc107; color: #333; }
181
+ &.method-patch { background-color: #17a2b8; color: white; }
182
+ &.method-delete { background-color: #dc3545; color: white; }
183
+ }
184
+
185
+ .status-badge {
186
+ &.enabled { color: #28a745; }
187
+ &.deleted { color: #dc3545; }
188
+ }
189
+
190
+ .actions {
191
+ display: flex;
192
+ gap: 0.25rem;
193
+ }
194
+
195
+ .action-btn {
196
+ background: none;
197
+ border: none;
198
+ cursor: pointer;
199
+ font-size: 1.1rem;
200
+ padding: 0.25rem;
201
+ border-radius: 0.25rem;
202
+
203
+ &:hover {
204
+ background-color: #f8f9fa;
205
+ }
206
+
207
+ &.danger:hover {
208
+ background-color: #f8d7da;
209
+ }
210
+ }
211
+
212
+ tr.deleted {
213
+ opacity: 0.6;
214
+ background-color: #f8f9fa;
215
+ }
216
+ }
217
+ `]
218
+ })
219
+ export class ApisComponent implements OnInit {
220
+ private apiService = inject(ApiService);
221
+
222
+ apis: API[] = [];
223
+ filteredAPIs: API[] = [];
224
+ loading = true;
225
+ showDeleted = false;
226
+ searchTerm = '';
227
+ message = '';
228
+ isError = false;
229
+
230
+ ngOnInit() {
231
+ this.loadAPIs();
232
+ }
233
+
234
+ loadAPIs() {
235
+ this.loading = true;
236
+ this.apiService.getAPIs(this.showDeleted).subscribe({
237
+ next: (apis) => {
238
+ this.apis = apis;
239
+ this.filterAPIs();
240
+ this.loading = false;
241
+ },
242
+ error: (err) => {
243
+ this.showMessage('Failed to load APIs', true);
244
+ this.loading = false;
245
+ }
246
+ });
247
+ }
248
+
249
+ filterAPIs() {
250
+ const term = this.searchTerm.toLowerCase();
251
+ this.filteredAPIs = this.apis.filter(api =>
252
+ api.name.toLowerCase().includes(term) ||
253
+ api.url.toLowerCase().includes(term)
254
+ );
255
+ }
256
+
257
+ createAPI() {
258
+ const dialogRef = this.dialog.open(ApiEditDialogComponent, {
259
+ width: '800px',
260
+ data: { mode: 'create' }
261
+ });
262
+
263
+ dialogRef.afterClosed().subscribe(result => {
264
+ if (result) {
265
+ this.loadAPIs();
266
+ }
267
+ });
268
+ }
269
+
270
+ editAPI(api: API) {
271
+ const dialogRef = this.dialog.open(ApiEditDialogComponent, {
272
+ width: '800px',
273
+ data: { mode: 'edit', api: { ...api } }
274
+ });
275
+
276
+ dialogRef.afterClosed().subscribe(result => {
277
+ if (result) {
278
+ this.loadAPIs();
279
+ }
280
+ });
281
+ }
282
+
283
+ testAPI(api: API) {
284
+ this.apiService.testAPI(api).subscribe({
285
+ next: (result) => {
286
+ if (result.success) {
287
+ this.showMessage(`API test successful! Status: ${result.status_code}`, false);
288
+ } else {
289
+ this.showMessage(`API test failed: ${result.error}`, true);
290
+ }
291
+ },
292
+ error: (err) => {
293
+ this.showMessage('Failed to test API', true);
294
+ }
295
+ });
296
+ }
297
+
298
+ duplicateAPI(api: API) {
299
+ const dialogRef = this.dialog.open(ApiEditDialogComponent, {
300
+ width: '800px',
301
+ data: { mode: 'duplicate', api: { ...api } }
302
+ });
303
+
304
+ dialogRef.afterClosed().subscribe(result => {
305
+ if (result) {
306
+ this.loadAPIs();
307
+ }
308
+ });
309
+ }
310
+
311
+ deleteAPI(api: API) {
312
+ if (confirm(`Are you sure you want to delete "${api.name}"?`)) {
313
+ this.apiService.deleteAPI(api.name).subscribe({
314
+ next: () => {
315
+ this.showMessage(`API "${api.name}" deleted successfully`, false);
316
+ this.loadAPIs();
317
+ },
318
+ error: (err) => {
319
+ this.showMessage(err.error?.detail || 'Failed to delete API', true);
320
+ }
321
+ });
322
+ }
323
+ }
324
+
325
+ private showMessage(message: string, isError: boolean) {
326
+ this.message = message;
327
+ this.isError = isError;
328
+
329
+ setTimeout(() => {
330
+ this.message = '';
331
+ }, 5000);
332
+ }
333
  }