ciyidogan commited on
Commit
ec47b19
·
verified ·
1 Parent(s): ac0ed62

Update flare-ui/src/app/dialogs/api-edit-dialog/api-edit-dialog.component.html

Browse files
flare-ui/src/app/dialogs/api-edit-dialog/api-edit-dialog.component.html CHANGED
@@ -1,469 +1,502 @@
1
- <h2 mat-dialog-title>
2
- @if (data.mode === 'create') {
3
- Create New API
4
- } @else if (data.mode === 'duplicate') {
5
- Duplicate API
6
- } @else if (data.mode === 'test') {
7
- Test API: {{ data.api.name }}
8
- } @else {
9
- Edit API: {{ data.api.name }}
10
- }
11
- </h2>
12
-
13
- <mat-dialog-content>
14
- <mat-tab-group [(selectedIndex)]="activeTabIndex">
15
- <!-- General Tab -->
16
- <mat-tab label="General">
17
- <div class="tab-content">
18
- <mat-form-field appearance="outline">
19
- <mat-label>Name</mat-label>
20
- <input matInput [formControl]="$any(form.get('name'))" placeholder="e.g., get_flights">
21
- <mat-hint>Unique identifier for this API</mat-hint>
22
- @if (form.get('name')?.hasError('required') && form.get('name')?.touched) {
23
- <mat-error>Name is required</mat-error>
24
- }
25
- @if (form.get('name')?.hasError('pattern') && form.get('name')?.touched) {
26
- <mat-error>Only alphanumeric and underscore allowed</mat-error>
27
- }
28
- </mat-form-field>
29
-
30
- <mat-form-field appearance="outline">
31
- <mat-label>URL</mat-label>
32
- <input matInput [formControl]="$any(form.get('url'))" placeholder="https://api.example.com/endpoint">
33
- <mat-hint>Full URL including protocol</mat-hint>
34
- @if (form.get('url')?.hasError('required') && form.get('url')?.touched) {
35
- <mat-error>URL is required</mat-error>
36
- }
37
- @if (form.get('url')?.hasError('pattern') && form.get('url')?.touched) {
38
- <mat-error>Invalid URL format</mat-error>
39
- }
40
- </mat-form-field>
41
-
42
- <div class="row">
43
- <mat-form-field appearance="outline" class="method-field">
44
- <mat-label>Method</mat-label>
45
- <mat-select [formControl]="$any(form.get('method'))">
46
- @for (method of httpMethods; track method) {
47
- <mat-option [value]="method">{{ method }}</mat-option>
48
- }
49
- </mat-select>
50
- </mat-form-field>
51
-
52
- <mat-form-field appearance="outline" class="timeout-field">
53
- <mat-label>Timeout (seconds)</mat-label>
54
- <input matInput type="number" [formControl]="$any(form.get('timeout_seconds'))">
55
- <mat-hint>Request timeout in seconds</mat-hint>
56
- @if (form.get('timeout_seconds')?.hasError('min')) {
57
- <mat-error>Minimum 1 second</mat-error>
58
- }
59
- @if (form.get('timeout_seconds')?.hasError('max')) {
60
- <mat-error>Maximum 300 seconds</mat-error>
61
- }
62
- </mat-form-field>
63
- </div>
64
-
65
- <mat-form-field appearance="outline" class="full-width">
66
- <mat-label>Body Template</mat-label>
67
- <textarea matInput
68
- [formControl]="$any(form.get('body_template'))"
69
- rows="8"
70
- placeholder='{"key": "value"}'
71
- (click)="saveCursorPosition('body_template', $event)"
72
- (keyup)="saveCursorPosition('body_template', $event)"></textarea>
73
- <mat-hint>JSON template with template variable support</mat-hint>
74
- @if (!validateJSON('body_template')) {
75
- <mat-error>Invalid JSON format</mat-error>
76
- }
77
- </mat-form-field>
78
-
79
- <div class="template-variables">
80
- <strong>Available Variables:</strong>
81
- <mat-chip-set>
82
- @for (variable of getTemplateVariables(false); track variable) {
83
- <mat-chip (click)="insertTemplateVariable('body_template', variable)">
84
- {{ variable }}
85
- </mat-chip>
86
- }
87
- </mat-chip-set>
88
- </div>
89
-
90
- <mat-form-field appearance="outline">
91
- <mat-label>Proxy URL (Optional)</mat-label>
92
- <input matInput [formControl]="$any(form.get('proxy'))" placeholder="http://proxy.example.com:8080">
93
- <mat-hint>HTTP proxy for this API call</mat-hint>
94
- </mat-form-field>
95
- </div>
96
- </mat-tab>
97
-
98
- <!-- Headers Tab -->
99
- <mat-tab label="Headers">
100
- <div class="tab-content">
101
- <div class="array-section">
102
- <div class="section-header">
103
- <h3>Request Headers</h3>
104
- <button mat-button color="primary" (click)="addHeader()">
105
- <mat-icon>add</mat-icon>
106
- Add Header
107
- </button>
108
- </div>
109
-
110
- @if (headers.length === 0) {
111
- <p class="empty-message">No headers configured. Click "Add Header" to add one.</p>
112
- }
113
-
114
- @for (header of headers.controls; track header; let i = $index) {
115
- <div class="array-item" [formGroup]="$any(header)">
116
- <mat-form-field appearance="outline" class="key-field">
117
- <mat-label>Header Name</mat-label>
118
- <input matInput formControlName="key" placeholder="Content-Type">
119
- </mat-form-field>
120
-
121
- <mat-form-field appearance="outline" class="value-field">
122
- <mat-label>Header Value</mat-label>
123
- <input matInput formControlName="value" placeholder="application/json">
124
- <button mat-icon-button matSuffix [matMenuTriggerFor]="headerMenu">
125
- <mat-icon>code</mat-icon>
126
- </button>
127
- <mat-menu #headerMenu="matMenu">
128
- @for (variable of getTemplateVariables(); track variable) {
129
- <button mat-menu-item (click)="insertHeaderValue(i, variable)">
130
- {{ variable }}
131
- </button>
132
- }
133
- </mat-menu>
134
- </mat-form-field>
135
-
136
- <button mat-icon-button color="warn" (click)="removeHeader(i)">
137
- <mat-icon>delete</mat-icon>
138
- </button>
139
- </div>
140
- }
141
- </div>
142
- </div>
143
- </mat-tab>
144
-
145
- <!-- Response Tab -->
146
- <mat-tab label="Response">
147
- <div class="tab-content">
148
- <mat-form-field appearance="outline" class="full-width">
149
- <mat-label>Response Prompt</mat-label>
150
- <textarea matInput
151
- [formControl]="$any(form.get('response_prompt'))"
152
- rows="4"
153
- placeholder="Optional instructions for processing the response"></textarea>
154
- <mat-hint>Instructions for AI to process the response (optional)</mat-hint>
155
- </mat-form-field>
156
-
157
- <mat-divider></mat-divider>
158
-
159
- <div class="array-section">
160
- <div class="section-header">
161
- <h3>Response Mappings</h3>
162
- <button mat-button color="primary" (click)="addResponseMapping()">
163
- <mat-icon>add</mat-icon>
164
- Add Mapping
165
- </button>
166
- </div>
167
-
168
- <p class="info-text">
169
- Extract values from API response and save them as variables for use in subsequent intents.
170
- </p>
171
-
172
- @if (responseMappings.length === 0) {
173
- <p class="empty-message">No response mappings configured.</p>
174
- }
175
-
176
- @for (mapping of responseMappings.controls; track mapping; let i = $index) {
177
- <mat-expansion-panel [formGroup]="$any(mapping)">
178
- <mat-expansion-panel-header>
179
- <mat-panel-title>
180
- {{ mapping.get('variable_name')?.value || 'New Mapping' }}
181
- </mat-panel-title>
182
- <mat-panel-description>
183
- {{ mapping.get('json_path')?.value || 'Configure mapping' }}
184
- </mat-panel-description>
185
- </mat-expansion-panel-header>
186
-
187
- <div class="mapping-content">
188
- <mat-form-field appearance="outline">
189
- <mat-label>Variable Name</mat-label>
190
- <input matInput formControlName="variable_name" placeholder="booking_ref">
191
- <mat-hint>Name to store the extracted value</mat-hint>
192
- @if (mapping.get('variable_name')?.hasError('pattern')) {
193
- <mat-error>Lowercase letters, numbers and underscore only</mat-error>
194
- }
195
- </mat-form-field>
196
-
197
- <mat-form-field appearance="outline">
198
- <mat-label>Caption</mat-label>
199
- <input matInput formControlName="caption" placeholder="Booking Reference">
200
- <mat-hint>Human-readable description</mat-hint>
201
- </mat-form-field>
202
-
203
- <div class="row">
204
- <mat-form-field appearance="outline" class="type-field">
205
- <mat-label>Type</mat-label>
206
- <mat-select formControlName="type">
207
- @for (type of variableTypes; track type) {
208
- <mat-option [value]="type">{{ type }}</mat-option>
209
- }
210
- </mat-select>
211
- </mat-form-field>
212
-
213
- <mat-form-field appearance="outline" class="path-field">
214
- <mat-label>JSON Path</mat-label>
215
- <input matInput formControlName="json_path" placeholder="$.data.bookingReference">
216
- <mat-hint>JSONPath expression to extract value</mat-hint>
217
- </mat-form-field>
218
- </div>
219
-
220
- <button mat-button color="warn" (click)="removeResponseMapping(i)">
221
- <mat-icon>delete</mat-icon>
222
- Remove Mapping
223
- </button>
224
- </div>
225
- </mat-expansion-panel>
226
- }
227
- </div>
228
-
229
- <!-- Retry Settings -->
230
- <mat-divider></mat-divider>
231
-
232
- <div class="retry-section" [formGroup]="$any(form.get('retry'))">
233
- <h3>Retry Settings</h3>
234
-
235
- <div class="row">
236
- <mat-form-field appearance="outline">
237
- <mat-label>Retry Count</mat-label>
238
- <input matInput type="number" formControlName="retry_count">
239
- <mat-hint>Number of retry attempts</mat-hint>
240
- </mat-form-field>
241
-
242
- <mat-form-field appearance="outline">
243
- <mat-label>Backoff (seconds)</mat-label>
244
- <input matInput type="number" formControlName="backoff_seconds">
245
- <mat-hint>Delay between retries</mat-hint>
246
- </mat-form-field>
247
-
248
- <mat-form-field appearance="outline">
249
- <mat-label>Strategy</mat-label>
250
- <mat-select formControlName="strategy">
251
- @for (strategy of retryStrategies; track strategy) {
252
- <mat-option [value]="strategy">{{ strategy }}</mat-option>
253
- }
254
- </mat-select>
255
- </mat-form-field>
256
- </div>
257
- </div>
258
- </div>
259
- </mat-tab>
260
-
261
- <!-- Test Tab -->
262
- <mat-tab label="Test">
263
- <div class="tab-content">
264
- <div class="test-section">
265
- <h3>Test API Call</h3>
266
-
267
- <div class="test-controls">
268
- <button mat-raised-button color="primary"
269
- (click)="testAPI()"
270
- [disabled]="testing || !form.get('url')?.valid || !form.get('method')?.valid">
271
- @if (testing) {
272
- <ng-container>
273
- <mat-icon class="spin">sync</mat-icon>
274
- Testing...
275
- </ng-container>
276
- } @else {
277
- <ng-container>
278
- <mat-icon>play_arrow</mat-icon>
279
- Test API
280
- </ng-container>
281
- }
282
- </button>
283
-
284
- <button mat-button (click)="updateTestRequestJson()">
285
- <mat-icon>refresh</mat-icon>
286
- Generate Test Data
287
- </button>
288
- </div>
289
-
290
- <mat-form-field appearance="outline" class="full-width">
291
- <mat-label>Test Request Body</mat-label>
292
- <textarea matInput
293
- [(ngModel)]="testRequestJson"
294
- rows="10"
295
- placeholder="Enter test request JSON here"></textarea>
296
- <mat-hint>Variables will be replaced with test values</mat-hint>
297
- </mat-form-field>
298
-
299
- @if (testResult) {
300
- <mat-divider></mat-divider>
301
-
302
- <div class="test-result" [class.success]="testResult.success" [class.error]="!testResult.success">
303
- <h4>Test Result</h4>
304
-
305
- @if (testResult.success) {
306
- <div class="result-status">
307
- <mat-icon>check_circle</mat-icon>
308
- <span>Success ({{ testResult.status_code }})</span>
309
- </div>
310
- } @else {
311
- <div class="result-status">
312
- <mat-icon>error</mat-icon>
313
- <span>Failed: {{ testResult.error }}</span>
314
- </div>
315
- }
316
-
317
- @if (testResult.response_time) {
318
- <p><strong>Response Time:</strong> {{ testResult.response_time }}ms</p>
319
- }
320
-
321
- @if (testResult.response_body) {
322
- <mat-form-field appearance="outline" class="full-width">
323
- <mat-label>Response Body</mat-label>
324
- <textarea matInput
325
- [value]="testResult.response_body | json"
326
- rows="10"
327
- readonly></textarea>
328
- </mat-form-field>
329
- }
330
-
331
- @if (testResult.request_body) {
332
- <mat-form-field appearance="outline" class="full-width">
333
- <mat-label>Actual Request Sent</mat-label>
334
- <textarea matInput
335
- [value]="testResult.request_body | json"
336
- rows="8"
337
- readonly></textarea>
338
- </mat-form-field>
339
- }
340
- </div>
341
- }
342
- </div>
343
- </div>
344
- </mat-tab>
345
-
346
- <!-- Auth Tab -->
347
- <mat-tab label="Authentication">
348
- <div class="tab-content" [formGroup]="$any(form.get('auth'))">
349
- <mat-checkbox formControlName="enabled">
350
- Enable Authentication
351
- </mat-checkbox>
352
-
353
- @if (form.get('auth.enabled')?.value) {
354
- <mat-divider></mat-divider>
355
-
356
- <div class="auth-section">
357
- <h3>Token Configuration</h3>
358
-
359
- <mat-form-field appearance="outline">
360
- <mat-label>Token Endpoint</mat-label>
361
- <input matInput formControlName="token_endpoint" placeholder="https://api.example.com/auth/token">
362
- <mat-hint>URL to obtain authentication token</mat-hint>
363
- @if (form.get('auth.token_endpoint')?.hasError('required') && form.get('auth.token_endpoint')?.touched) {
364
- <mat-error>Token endpoint is required when auth is enabled</mat-error>
365
- }
366
- </mat-form-field>
367
-
368
- <mat-form-field appearance="outline">
369
- <mat-label>Token Response Path</mat-label>
370
- <input matInput formControlName="response_token_path" placeholder="token">
371
- <mat-hint>JSON path to extract token from response</mat-hint>
372
- @if (form.get('auth.response_token_path')?.hasError('required') && form.get('auth.response_token_path')?.touched) {
373
- <mat-error>Token path is required when auth is enabled</mat-error>
374
- }
375
- </mat-form-field>
376
-
377
- <mat-form-field appearance="outline" class="full-width">
378
- <mat-label>Token Request Body</mat-label>
379
- <textarea matInput
380
- formControlName="token_request_body"
381
- rows="6"
382
- (click)="saveCursorPosition('auth.token_request_body', $event)"
383
- (keyup)="saveCursorPosition('auth.token_request_body', $event)"
384
- placeholder='{"username": "api_user", "password": "api_pass"}'></textarea>
385
- <mat-hint>JSON body for token request</mat-hint>
386
- @if (!validateJSON('auth.token_request_body')) {
387
- <mat-error>Invalid JSON format</mat-error>
388
- }
389
- </mat-form-field>
390
-
391
- <div class="template-variables">
392
- <strong>Available Variables:</strong>
393
- <mat-chip-set>
394
- @for (variable of getTemplateVariables(); track variable) {
395
- <mat-chip (click)="insertTemplateVariable('auth.token_request_body', variable)">
396
- {{ variable }}
397
- </mat-chip>
398
- }
399
- </mat-chip-set>
400
- </div>
401
-
402
- <mat-divider></mat-divider>
403
-
404
- <h3>Token Refresh (Optional)</h3>
405
-
406
- <mat-form-field appearance="outline">
407
- <mat-label>Refresh Endpoint</mat-label>
408
- <input matInput formControlName="token_refresh_endpoint" placeholder="https://api.example.com/auth/refresh">
409
- <mat-hint>URL to refresh expired token</mat-hint>
410
- </mat-form-field>
411
-
412
- <mat-form-field appearance="outline" class="full-width">
413
- <mat-label>Refresh Request Body</mat-label>
414
- <textarea matInput
415
- formControlName="token_refresh_body"
416
- rows="4"
417
- (click)="saveCursorPosition('auth.token_refresh_body', $event)"
418
- (keyup)="saveCursorPosition('auth.token_refresh_body', $event)"
419
- placeholder='{"refresh_token": "your_refresh_token"}'></textarea>
420
- <mat-hint>JSON body for refresh request</mat-hint>
421
- @if (!validateJSON('auth.token_refresh_body')) {
422
- <mat-error>Invalid JSON format</mat-error>
423
- }
424
- </mat-form-field>
425
-
426
- <div class="template-variables">
427
- <strong>Available Variables:</strong>
428
- <mat-chip-set>
429
- @for (variable of getTemplateVariables(); track variable) {
430
- <mat-chip (click)="insertTemplateVariable('auth.token_refresh_body', variable)">
431
- {{ variable }}
432
- </mat-chip>
433
- }
434
- </mat-chip-set>
435
- </div>
436
- </div>
437
- }
438
- </div>
439
- </mat-tab>
440
- </mat-tab-group>
441
- </mat-dialog-content>
442
-
443
- <mat-dialog-actions align="end">
444
- <button mat-button (click)="cancel()">
445
- @if (data.mode === 'test') {
446
- Close
447
- } @else {
448
- Cancel
449
- }
450
- </button>
451
- @if (data.mode !== 'test') {
452
- <button mat-raised-button color="primary"
453
- (click)="save()"
454
- [disabled]="saving || form.invalid">
455
- @if (saving) {
456
- <ng-container>
457
- <mat-icon class="spin">sync</mat-icon>
458
- Saving...
459
- </ng-container>
460
- } @else {
461
- @if (data.mode === 'create' || data.mode === 'duplicate') {
462
- Create
463
- } @else {
464
- Update
465
- }
466
- }
467
- </button>
468
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
469
  </mat-dialog-actions>
 
1
+ <h2 mat-dialog-title>
2
+ @if (data.mode === 'create') {
3
+ Create New API
4
+ } @else if (data.mode === 'duplicate') {
5
+ Duplicate API
6
+ } @else if (data.mode === 'test') {
7
+ Test API: {{ data.api.name }}
8
+ } @else {
9
+ Edit API: {{ data.api.name }}
10
+ }
11
+ </h2>
12
+
13
+ <mat-dialog-content>
14
+ <mat-tab-group [(selectedIndex)]="activeTabIndex">
15
+ <!-- General Tab -->
16
+ <mat-tab label="General">
17
+ <div class="tab-content">
18
+ <mat-form-field appearance="outline">
19
+ <mat-label>Name</mat-label>
20
+ <input matInput [formControl]="$any(form.get('name'))" placeholder="e.g., get_flights">
21
+ <mat-hint>Unique identifier for this API</mat-hint>
22
+ @if (form.get('name')?.hasError('required') && form.get('name')?.touched) {
23
+ <mat-error>Name is required</mat-error>
24
+ }
25
+ @if (form.get('name')?.hasError('pattern') && form.get('name')?.touched) {
26
+ <mat-error>Only alphanumeric and underscore allowed</mat-error>
27
+ }
28
+ </mat-form-field>
29
+
30
+ <mat-form-field appearance="outline">
31
+ <mat-label>URL</mat-label>
32
+ <input matInput [formControl]="$any(form.get('url'))" placeholder="https://api.example.com/endpoint">
33
+ <mat-hint>Full URL including protocol</mat-hint>
34
+ @if (form.get('url')?.hasError('required') && form.get('url')?.touched) {
35
+ <mat-error>URL is required</mat-error>
36
+ }
37
+ @if (form.get('url')?.hasError('pattern') && form.get('url')?.touched) {
38
+ <mat-error>Invalid URL format</mat-error>
39
+ }
40
+ </mat-form-field>
41
+
42
+ <div class="row">
43
+ <mat-form-field appearance="outline" class="method-field">
44
+ <mat-label>Method</mat-label>
45
+ <mat-select [formControl]="$any(form.get('method'))">
46
+ @for (method of httpMethods; track method) {
47
+ <mat-option [value]="method">{{ method }}</mat-option>
48
+ }
49
+ </mat-select>
50
+ </mat-form-field>
51
+
52
+ <mat-form-field appearance="outline" class="timeout-field">
53
+ <mat-label>Timeout (seconds)</mat-label>
54
+ <input matInput type="number" [formControl]="$any(form.get('timeout_seconds'))">
55
+ <mat-hint>Request timeout in seconds</mat-hint>
56
+ @if (form.get('timeout_seconds')?.hasError('min')) {
57
+ <mat-error>Minimum 1 second</mat-error>
58
+ }
59
+ @if (form.get('timeout_seconds')?.hasError('max')) {
60
+ <mat-error>Maximum 300 seconds</mat-error>
61
+ }
62
+ </mat-form-field>
63
+ </div>
64
+
65
+ <mat-form-field appearance="outline" class="full-width">
66
+ <mat-label>Body Template</mat-label>
67
+ <textarea matInput
68
+ [formControl]="$any(form.get('body_template'))"
69
+ rows="8"
70
+ placeholder='{"key": "value"}'
71
+ (click)="saveCursorPosition('body_template', $event)"
72
+ (keyup)="saveCursorPosition('body_template', $event)"></textarea>
73
+ <mat-hint>JSON template with template variable support</mat-hint>
74
+ @if (!validateJSON('body_template')) {
75
+ <mat-error>Invalid JSON format</mat-error>
76
+ }
77
+ </mat-form-field>
78
+
79
+ <!-- JSON Validation Indicator -->
80
+ <div class="json-validation-status">
81
+ @if (validateJSON('body_template')) {
82
+ <mat-icon class="valid">check_circle</mat-icon>
83
+ <span class="valid">Valid JSON</span>
84
+ } @else {
85
+ <mat-icon class="invalid">error</mat-icon>
86
+ <span class="invalid">Invalid JSON</span>
87
+ }
88
+ </div>
89
+
90
+ <div class="template-variables">
91
+ <strong>Available Variables:</strong>
92
+ <mat-chip-set>
93
+ @for (variable of getTemplateVariables(false); track variable) {
94
+ <mat-chip (click)="insertTemplateVariable('body_template', variable)">
95
+ {{ variable }}
96
+ </mat-chip>
97
+ }
98
+ </mat-chip-set>
99
+ </div>
100
+
101
+ <mat-form-field appearance="outline">
102
+ <mat-label>Proxy URL (Optional)</mat-label>
103
+ <input matInput [formControl]="$any(form.get('proxy'))" placeholder="http://proxy.example.com:8080">
104
+ <mat-hint>HTTP proxy for this API call</mat-hint>
105
+ </mat-form-field>
106
+ </div>
107
+ </mat-tab>
108
+
109
+ <!-- Headers Tab -->
110
+ <mat-tab label="Headers">
111
+ <div class="tab-content">
112
+ <div class="array-section">
113
+ <div class="section-header">
114
+ <h3>Request Headers</h3>
115
+ <button mat-button color="primary" (click)="addHeader()">
116
+ <mat-icon>add</mat-icon>
117
+ Add Header
118
+ </button>
119
+ </div>
120
+
121
+ @if (headers.length === 0) {
122
+ <p class="empty-message">No headers configured. Click "Add Header" to add one.</p>
123
+ }
124
+
125
+ @for (header of headers.controls; track header; let i = $index) {
126
+ <div class="array-item" [formGroup]="$any(header)">
127
+ <mat-form-field appearance="outline" class="key-field">
128
+ <mat-label>Header Name</mat-label>
129
+ <input matInput formControlName="key" placeholder="Content-Type">
130
+ </mat-form-field>
131
+
132
+ <mat-form-field appearance="outline" class="value-field">
133
+ <mat-label>Header Value</mat-label>
134
+ <input matInput formControlName="value" placeholder="application/json">
135
+ <button mat-icon-button matSuffix [matMenuTriggerFor]="headerMenu">
136
+ <mat-icon>code</mat-icon>
137
+ </button>
138
+ <mat-menu #headerMenu="matMenu">
139
+ @for (variable of getTemplateVariables(); track variable) {
140
+ <button mat-menu-item (click)="insertHeaderValue(i, variable)">
141
+ {{ variable }}
142
+ </button>
143
+ }
144
+ </mat-menu>
145
+ </mat-form-field>
146
+
147
+ <button mat-icon-button color="warn" (click)="removeHeader(i)">
148
+ <mat-icon>delete</mat-icon>
149
+ </button>
150
+ </div>
151
+ }
152
+ </div>
153
+ </div>
154
+ </mat-tab>
155
+
156
+ <!-- Response Tab -->
157
+ <mat-tab label="Response">
158
+ <div class="tab-content">
159
+ <mat-form-field appearance="outline" class="full-width">
160
+ <mat-label>Response Prompt</mat-label>
161
+ <textarea matInput
162
+ [formControl]="$any(form.get('response_prompt'))"
163
+ rows="4"
164
+ placeholder="Optional instructions for processing the response"></textarea>
165
+ <mat-hint>Instructions for AI to process the response (optional)</mat-hint>
166
+ </mat-form-field>
167
+
168
+ <mat-divider></mat-divider>
169
+
170
+ <div class="array-section">
171
+ <div class="section-header">
172
+ <h3>Response Mappings</h3>
173
+ <button mat-button color="primary" (click)="addResponseMapping()">
174
+ <mat-icon>add</mat-icon>
175
+ Add Mapping
176
+ </button>
177
+ </div>
178
+
179
+ <p class="info-text">
180
+ Extract values from API response and save them as variables for use in subsequent intents.
181
+ </p>
182
+
183
+ @if (responseMappings.length === 0) {
184
+ <p class="empty-message">No response mappings configured.</p>
185
+ }
186
+
187
+ @for (mapping of responseMappings.controls; track mapping; let i = $index) {
188
+ <mat-expansion-panel [formGroup]="$any(mapping)">
189
+ <mat-expansion-panel-header>
190
+ <mat-panel-title>
191
+ {{ mapping.get('variable_name')?.value || 'New Mapping' }}
192
+ </mat-panel-title>
193
+ <mat-panel-description>
194
+ {{ mapping.get('json_path')?.value || 'Configure mapping' }}
195
+ </mat-panel-description>
196
+ </mat-expansion-panel-header>
197
+
198
+ <div class="mapping-content">
199
+ <mat-form-field appearance="outline">
200
+ <mat-label>Variable Name</mat-label>
201
+ <input matInput formControlName="variable_name" placeholder="booking_ref">
202
+ <mat-hint>Name to store the extracted value</mat-hint>
203
+ @if (mapping.get('variable_name')?.hasError('pattern')) {
204
+ <mat-error>Lowercase letters, numbers and underscore only</mat-error>
205
+ }
206
+ </mat-form-field>
207
+
208
+ <mat-form-field appearance="outline">
209
+ <mat-label>Caption</mat-label>
210
+ <input matInput formControlName="caption" placeholder="Booking Reference">
211
+ <mat-hint>Human-readable description</mat-hint>
212
+ </mat-form-field>
213
+
214
+ <div class="row">
215
+ <mat-form-field appearance="outline" class="type-field">
216
+ <mat-label>Type</mat-label>
217
+ <mat-select formControlName="type">
218
+ @for (type of variableTypes; track type) {
219
+ <mat-option [value]="type">{{ type }}</mat-option>
220
+ }
221
+ </mat-select>
222
+ </mat-form-field>
223
+
224
+ <mat-form-field appearance="outline" class="path-field">
225
+ <mat-label>JSON Path</mat-label>
226
+ <input matInput formControlName="json_path" placeholder="$.data.bookingReference">
227
+ <mat-hint>JSONPath expression to extract value</mat-hint>
228
+ </mat-form-field>
229
+ </div>
230
+
231
+ <button mat-button color="warn" (click)="removeResponseMapping(i)">
232
+ <mat-icon>delete</mat-icon>
233
+ Remove Mapping
234
+ </button>
235
+ </div>
236
+ </mat-expansion-panel>
237
+ }
238
+ </div>
239
+
240
+ <!-- Retry Settings -->
241
+ <mat-divider></mat-divider>
242
+
243
+ <div class="retry-section" [formGroup]="$any(form.get('retry'))">
244
+ <h3>Retry Settings</h3>
245
+
246
+ <div class="row">
247
+ <mat-form-field appearance="outline">
248
+ <mat-label>Retry Count</mat-label>
249
+ <input matInput type="number" formControlName="retry_count">
250
+ <mat-hint>Number of retry attempts</mat-hint>
251
+ </mat-form-field>
252
+
253
+ <mat-form-field appearance="outline">
254
+ <mat-label>Backoff (seconds)</mat-label>
255
+ <input matInput type="number" formControlName="backoff_seconds">
256
+ <mat-hint>Delay between retries</mat-hint>
257
+ </mat-form-field>
258
+
259
+ <mat-form-field appearance="outline">
260
+ <mat-label>Strategy</mat-label>
261
+ <mat-select formControlName="strategy">
262
+ @for (strategy of retryStrategies; track strategy) {
263
+ <mat-option [value]="strategy">{{ strategy }}</mat-option>
264
+ }
265
+ </mat-select>
266
+ </mat-form-field>
267
+ </div>
268
+ </div>
269
+ </div>
270
+ </mat-tab>
271
+
272
+ <!-- Test Tab -->
273
+ <mat-tab label="Test">
274
+ <div class="tab-content">
275
+ <div class="test-section">
276
+ <h3>Test API Call</h3>
277
+
278
+ <div class="test-controls">
279
+ <button mat-raised-button color="primary"
280
+ (click)="testAPI()"
281
+ [disabled]="testing || !form.get('url')?.valid || !form.get('method')?.valid">
282
+ @if (testing) {
283
+ <ng-container>
284
+ <mat-icon class="spin">sync</mat-icon>
285
+ Testing...
286
+ </ng-container>
287
+ } @else {
288
+ <ng-container>
289
+ <mat-icon>play_arrow</mat-icon>
290
+ Test API
291
+ </ng-container>
292
+ }
293
+ </button>
294
+
295
+ <button mat-button (click)="updateTestRequestJson()">
296
+ <mat-icon>refresh</mat-icon>
297
+ Generate Test Data
298
+ </button>
299
+ </div>
300
+
301
+ <mat-form-field appearance="outline" class="full-width">
302
+ <mat-label>Test Request Body</mat-label>
303
+ <textarea matInput
304
+ [(ngModel)]="testRequestJson"
305
+ rows="10"
306
+ placeholder="Enter test request JSON here"></textarea>
307
+ <mat-hint>Variables will be replaced with test values</mat-hint>
308
+ </mat-form-field>
309
+
310
+ @if (testResult) {
311
+ <mat-divider></mat-divider>
312
+
313
+ <div class="test-result" [class.success]="testResult.success" [class.error]="!testResult.success">
314
+ <h4>Test Result</h4>
315
+
316
+ @if (testResult.success) {
317
+ <div class="result-status">
318
+ <mat-icon>check_circle</mat-icon>
319
+ <span>Success ({{ testResult.status_code }})</span>
320
+ </div>
321
+ } @else {
322
+ <div class="result-status">
323
+ <mat-icon>error</mat-icon>
324
+ <span>Failed: {{ testResult.error }}</span>
325
+ </div>
326
+ }
327
+
328
+ @if (testResult.response_time) {
329
+ <p><strong>Response Time:</strong> {{ testResult.response_time }}ms</p>
330
+ }
331
+
332
+ @if (testResult.response_body) {
333
+ <mat-form-field appearance="outline" class="full-width">
334
+ <mat-label>Response Body</mat-label>
335
+ <textarea matInput
336
+ [value]="testResult.response_body | json"
337
+ rows="10"
338
+ readonly></textarea>
339
+ </mat-form-field>
340
+ }
341
+
342
+ @if (testResult.request_body) {
343
+ <mat-form-field appearance="outline" class="full-width">
344
+ <mat-label>Actual Request Sent</mat-label>
345
+ <textarea matInput
346
+ [value]="testResult.request_body | json"
347
+ rows="8"
348
+ readonly></textarea>
349
+ </mat-form-field>
350
+ }
351
+ </div>
352
+ }
353
+ </div>
354
+ </div>
355
+ </mat-tab>
356
+
357
+ <!-- Auth Tab -->
358
+ <mat-tab label="Authentication">
359
+ <div class="tab-content" [formGroup]="$any(form.get('auth'))">
360
+ <mat-checkbox formControlName="enabled">
361
+ Enable Authentication
362
+ </mat-checkbox>
363
+
364
+ @if (form.get('auth.enabled')?.value) {
365
+ <mat-divider></mat-divider>
366
+
367
+ <div class="auth-section">
368
+ <h3>Token Configuration</h3>
369
+
370
+ <mat-form-field appearance="outline">
371
+ <mat-label>Token Endpoint</mat-label>
372
+ <input matInput formControlName="token_endpoint" placeholder="https://api.example.com/auth/token">
373
+ <mat-hint>URL to obtain authentication token</mat-hint>
374
+ @if (form.get('auth.token_endpoint')?.hasError('required') && form.get('auth.token_endpoint')?.touched) {
375
+ <mat-error>Token endpoint is required when auth is enabled</mat-error>
376
+ }
377
+ </mat-form-field>
378
+
379
+ <mat-form-field appearance="outline">
380
+ <mat-label>Token Response Path</mat-label>
381
+ <input matInput formControlName="response_token_path" placeholder="token">
382
+ <mat-hint>JSON path to extract token from response</mat-hint>
383
+ @if (form.get('auth.response_token_path')?.hasError('required') && form.get('auth.response_token_path')?.touched) {
384
+ <mat-error>Token path is required when auth is enabled</mat-error>
385
+ }
386
+ </mat-form-field>
387
+
388
+ <mat-form-field appearance="outline" class="full-width">
389
+ <mat-label>Token Request Body</mat-label>
390
+ <textarea matInput
391
+ formControlName="token_request_body"
392
+ rows="6"
393
+ (click)="saveCursorPosition('auth.token_request_body', $event)"
394
+ (keyup)="saveCursorPosition('auth.token_request_body', $event)"
395
+ placeholder='{"username": "api_user", "password": "api_pass"}'></textarea>
396
+ <mat-hint>JSON body for token request</mat-hint>
397
+ @if (!validateJSON('auth.token_request_body')) {
398
+ <mat-error>Invalid JSON format</mat-error>
399
+ }
400
+ </mat-form-field>
401
+
402
+ <!-- JSON Validation Indicator -->
403
+ <div class="json-validation-status">
404
+ @if (validateJSON('auth.token_request_body')) {
405
+ <mat-icon class="valid">check_circle</mat-icon>
406
+ <span class="valid">Valid JSON</span>
407
+ } @else {
408
+ <mat-icon class="invalid">error</mat-icon>
409
+ <span class="invalid">Invalid JSON</span>
410
+ }
411
+ </div>
412
+
413
+ <div class="template-variables">
414
+ <strong>Available Variables:</strong>
415
+ <mat-chip-set>
416
+ @for (variable of getTemplateVariables(); track variable) {
417
+ <mat-chip (click)="insertTemplateVariable('auth.token_request_body', variable)">
418
+ {{ variable }}
419
+ </mat-chip>
420
+ }
421
+ </mat-chip-set>
422
+ </div>
423
+
424
+ <mat-divider></mat-divider>
425
+
426
+ <h3>Token Refresh (Optional)</h3>
427
+
428
+ <mat-form-field appearance="outline">
429
+ <mat-label>Refresh Endpoint</mat-label>
430
+ <input matInput formControlName="token_refresh_endpoint" placeholder="https://api.example.com/auth/refresh">
431
+ <mat-hint>URL to refresh expired token</mat-hint>
432
+ </mat-form-field>
433
+
434
+ <mat-form-field appearance="outline" class="full-width">
435
+ <mat-label>Refresh Request Body</mat-label>
436
+ <textarea matInput
437
+ formControlName="token_refresh_body"
438
+ rows="4"
439
+ (click)="saveCursorPosition('auth.token_refresh_body', $event)"
440
+ (keyup)="saveCursorPosition('auth.token_refresh_body', $event)"
441
+ placeholder='{"refresh_token": "your_refresh_token"}'></textarea>
442
+ <mat-hint>JSON body for refresh request</mat-hint>
443
+ @if (!validateJSON('auth.token_refresh_body')) {
444
+ <mat-error>Invalid JSON format</mat-error>
445
+ }
446
+ </mat-form-field>
447
+
448
+ <!-- JSON Validation Indicator -->
449
+ <div class="json-validation-status">
450
+ @if (validateJSON('auth.token_refresh_body')) {
451
+ <mat-icon class="valid">check_circle</mat-icon>
452
+ <span class="valid">Valid JSON</span>
453
+ } @else {
454
+ <mat-icon class="invalid">error</mat-icon>
455
+ <span class="invalid">Invalid JSON</span>
456
+ }
457
+ </div>
458
+
459
+ <div class="template-variables">
460
+ <strong>Available Variables:</strong>
461
+ <mat-chip-set>
462
+ @for (variable of getTemplateVariables(); track variable) {
463
+ <mat-chip (click)="insertTemplateVariable('auth.token_refresh_body', variable)">
464
+ {{ variable }}
465
+ </mat-chip>
466
+ }
467
+ </mat-chip-set>
468
+ </div>
469
+ </div>
470
+ }
471
+ </div>
472
+ </mat-tab>
473
+ </mat-tab-group>
474
+ </mat-dialog-content>
475
+
476
+ <mat-dialog-actions align="end">
477
+ <button mat-button (click)="cancel()">
478
+ @if (data.mode === 'test') {
479
+ Close
480
+ } @else {
481
+ Cancel
482
+ }
483
+ </button>
484
+ @if (data.mode !== 'test') {
485
+ <button mat-raised-button color="primary"
486
+ (click)="save()"
487
+ [disabled]="saving || form.invalid">
488
+ @if (saving) {
489
+ <ng-container>
490
+ <mat-icon class="spin">sync</mat-icon>
491
+ Saving...
492
+ </ng-container>
493
+ } @else {
494
+ @if (data.mode === 'create' || data.mode === 'duplicate') {
495
+ Create
496
+ } @else {
497
+ Update
498
+ }
499
+ }
500
+ </button>
501
+ }
502
  </mat-dialog-actions>