Spaces:
Running
Running
<h2 mat-dialog-title> | |
{{ data.mode === 'create' ? 'Create New API' : data.mode === 'duplicate' ? 'Duplicate API' : 'Edit API' }} | |
</h2> | |
<mat-dialog-content> | |
<form [formGroup]="form"> | |
<mat-tab-group> | |
<!-- General Tab --> | |
<mat-tab label="General"> | |
<div class="tab-content"> | |
<mat-form-field appearance="outline" class="full-width"> | |
<mat-label>Name*</mat-label> | |
<input matInput formControlName="name" | |
placeholder="e.g., book_flight_api" | |
[readonly]="data.mode === 'edit'"> | |
<mat-hint>Use only letters, numbers, and underscores</mat-hint> | |
<mat-error *ngIf="form.get('name')?.hasError('required')">Name is required</mat-error> | |
<mat-error *ngIf="form.get('name')?.hasError('pattern')">Invalid characters in name</mat-error> | |
</mat-form-field> | |
<mat-form-field appearance="outline" class="full-width"> | |
<mat-label>URL*</mat-label> | |
<input matInput formControlName="url" | |
placeholder="https://api.example.com/endpoint"> | |
<mat-error *ngIf="form.get('url')?.hasError('required')">URL is required</mat-error> | |
<mat-error *ngIf="form.get('url')?.hasError('pattern')">Invalid URL format</mat-error> | |
</mat-form-field> | |
<mat-form-field appearance="outline"> | |
<mat-label>Method*</mat-label> | |
<mat-select formControlName="method"> | |
<mat-option *ngFor="let method of httpMethods" [value]="method"> | |
{{ method }} | |
</mat-option> | |
</mat-select> | |
</mat-form-field> | |
<div class="form-section"> | |
<label class="section-label">Body Template:</label> | |
<div class="json-editor"> | |
<textarea formControlName="body_template" | |
placeholder='{"key": "value"}' | |
rows="8" | |
class="json-textarea"></textarea> | |
<div class="template-helpers"> | |
<mat-expansion-panel> | |
<mat-expansion-panel-header> | |
<mat-panel-title> | |
<mat-icon>help_outline</mat-icon> | |
Template Variables | |
</mat-panel-title> | |
</mat-expansion-panel-header> | |
<div class="template-list"> | |
<mat-chip *ngFor="let templateVar of templateVariables" | |
(click)="insertTemplateVariable('body_template', templateVar.key)"> | |
{{ templateVar.key }} | |
</mat-chip> | |
</div> | |
</mat-expansion-panel> | |
</div> | |
<button mat-button type="button" | |
(click)="validateJSON('body_template')" | |
[color]="validateJSON('body_template') ? 'primary' : 'warn'"> | |
<mat-icon>check_circle</mat-icon> | |
Validate JSON | |
</button> | |
</div> | |
</div> | |
<mat-form-field appearance="outline"> | |
<mat-label>Timeout (seconds)*</mat-label> | |
<input matInput type="number" formControlName="timeout_seconds"> | |
<mat-error *ngIf="form.get('timeout_seconds')?.hasError('required')">Timeout is required</mat-error> | |
<mat-error *ngIf="form.get('timeout_seconds')?.hasError('min')">Minimum 1 second</mat-error> | |
<mat-error *ngIf="form.get('timeout_seconds')?.hasError('max')">Maximum 300 seconds</mat-error> | |
</mat-form-field> | |
<div class="form-section"> | |
<h4>Retry Settings</h4> | |
<div formGroupName="retry" class="retry-settings"> | |
<mat-form-field appearance="outline"> | |
<mat-label>Retry Count*</mat-label> | |
<input matInput type="number" formControlName="retry_count"> | |
</mat-form-field> | |
<mat-form-field appearance="outline"> | |
<mat-label>Backoff (seconds)*</mat-label> | |
<input matInput type="number" formControlName="backoff_seconds"> | |
</mat-form-field> | |
<mat-form-field appearance="outline"> | |
<mat-label>Strategy*</mat-label> | |
<mat-select formControlName="strategy"> | |
<mat-option value="static">Static</mat-option> | |
<mat-option value="exponential">Exponential</mat-option> | |
</mat-select> | |
</mat-form-field> | |
</div> | |
</div> | |
<mat-form-field appearance="outline" class="full-width"> | |
<mat-label>Response Prompt</mat-label> | |
<textarea matInput formControlName="response_prompt" | |
rows="4" | |
placeholder="Convert the API response to human-friendly text..."></textarea> | |
<mat-hint>Use {{'{{api_response}}'}} to reference the API response</mat-hint> | |
</mat-form-field> | |
<mat-form-field appearance="outline" class="full-width"> | |
<mat-label>Proxy URL (Optional)</mat-label> | |
<input matInput formControlName="proxy" | |
placeholder="http://proxy.example.com:8080"> | |
</mat-form-field> | |
</div> | |
</mat-tab> | |
<!-- Headers Tab --> | |
<mat-tab label="Headers"> | |
<div class="tab-content"> | |
<div class="headers-section"> | |
<div class="section-header"> | |
<h4>Request Headers</h4> | |
<button mat-raised-button color="primary" type="button" (click)="addHeader()"> | |
<mat-icon>add</mat-icon> | |
Add Header | |
</button> | |
</div> | |
<div formArrayName="headers" class="headers-list"> | |
<div *ngFor="let header of headers.controls; let i = index" | |
[formGroupName]="i" | |
class="header-row"> | |
<mat-form-field appearance="outline"> | |
<mat-label>Key</mat-label> | |
<input matInput formControlName="key" placeholder="Content-Type"> | |
</mat-form-field> | |
<mat-form-field appearance="outline" class="header-value"> | |
<mat-label>Value</mat-label> | |
<input matInput formControlName="value" placeholder="application/json"> | |
</mat-form-field> | |
<button mat-icon-button color="warn" type="button" (click)="removeHeader(i)"> | |
<mat-icon>delete</mat-icon> | |
</button> | |
</div> | |
</div> | |
<div *ngIf="headers.length === 0" class="empty-state"> | |
<mat-icon>info</mat-icon> | |
<p>No headers defined. Click "Add Header" to create one.</p> | |
</div> | |
<div class="template-helpers"> | |
<mat-expansion-panel> | |
<mat-expansion-panel-header> | |
<mat-panel-title> | |
<mat-icon>help_outline</mat-icon> | |
Common Headers | |
</mat-panel-title> | |
</mat-expansion-panel-header> | |
<div class="common-headers"> | |
<p><strong>Content-Type:</strong> application/json</p> | |
<p><strong>Authorization:</strong> Bearer {{'{{auth_tokens.api_name.token}}'}}</p> | |
<p><strong>Accept:</strong> application/json</p> | |
</div> | |
</mat-expansion-panel> | |
</div> | |
</div> | |
</div> | |
</mat-tab> | |
<!-- Auth Tab --> | |
<mat-tab label="Auth"> | |
<div class="tab-content"> | |
<div formGroupName="auth"> | |
<mat-checkbox formControlName="enabled"> | |
Enable Authentication | |
</mat-checkbox> | |
<div *ngIf="form.get('auth.enabled')?.value" class="auth-settings"> | |
<mat-form-field appearance="outline" class="full-width"> | |
<mat-label>Token Endpoint*</mat-label> | |
<input matInput formControlName="token_endpoint" | |
placeholder="https://api.example.com/auth"> | |
<mat-error *ngIf="form.get('auth.token_endpoint')?.hasError('required')"> | |
Token endpoint is required when auth is enabled | |
</mat-error> | |
</mat-form-field> | |
<mat-form-field appearance="outline" class="full-width"> | |
<mat-label>Response Token Path*</mat-label> | |
<input matInput formControlName="response_token_path" | |
placeholder="access_token or data.token"> | |
<mat-hint>JSON path to extract token from response (e.g., data.token)</mat-hint> | |
<mat-error *ngIf="form.get('auth.response_token_path')?.hasError('required')"> | |
Token path is required when auth is enabled | |
</mat-error> | |
</mat-form-field> | |
<div class="form-section"> | |
<label class="section-label">Token Request Body:</label> | |
<div class="json-editor"> | |
<textarea formControlName="token_request_body" | |
placeholder='{"grant_type": "client_credentials"}' | |
rows="4" | |
class="json-textarea"></textarea> | |
<button mat-button type="button" | |
(click)="validateJSON('auth.token_request_body')" | |
[color]="validateJSON('auth.token_request_body') ? 'primary' : 'warn'"> | |
<mat-icon>check_circle</mat-icon> | |
Validate JSON | |
</button> | |
</div> | |
</div> | |
<mat-divider></mat-divider> | |
<h4>Token Refresh (Optional)</h4> | |
<mat-form-field appearance="outline" class="full-width"> | |
<mat-label>Token Refresh Endpoint</mat-label> | |
<input matInput formControlName="token_refresh_endpoint" | |
placeholder="https://api.example.com/refresh"> | |
</mat-form-field> | |
<div class="form-section" *ngIf="form.get('auth.token_refresh_endpoint')?.value"> | |
<label class="section-label">Token Refresh Body:</label> | |
<div class="json-editor"> | |
<textarea formControlName="token_refresh_body" | |
placeholder='{"refresh_token": "value"}' | |
rows="4" | |
class="json-textarea"></textarea> | |
<button mat-button type="button" | |
(click)="validateJSON('auth.token_refresh_body')" | |
[color]="validateJSON('auth.token_refresh_body') ? 'primary' : 'warn'"> | |
<mat-icon>check_circle</mat-icon> | |
Validate JSON | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</mat-tab> | |
<!-- Test Tab --> | |
<mat-tab label="Test"> | |
<div class="tab-content"> | |
<div class="test-section"> | |
<h4>Test API Configuration</h4> | |
<p>Test your API with sample data before saving.</p> | |
<div class="test-info"> | |
<mat-chip-listbox> | |
<mat-chip-option selected>{{ form.get('method')?.value || 'POST' }}</mat-chip-option> | |
<mat-chip-option>{{ form.get('url')?.value || 'No URL set' }}</mat-chip-option> | |
</mat-chip-listbox> | |
</div> | |
<button mat-raised-button color="accent" | |
(click)="testAPI()" | |
[disabled]="testing || !form.get('url')?.valid"> | |
<mat-icon>play_arrow</mat-icon> | |
{{ testing ? 'Testing...' : 'Send Test Request' }} | |
</button> | |
<div *ngIf="testResult" class="test-result"> | |
<h4>Test Result:</h4> | |
<div class="result-status" [class.success]="testResult.success" [class.error]="!testResult.success"> | |
<mat-icon>{{ testResult.success ? 'check_circle' : 'error' }}</mat-icon> | |
<span>{{ testResult.success ? 'Success' : 'Failed' }}</span> | |
<span *ngIf="testResult.status_code" class="status-code"> | |
Status: {{ testResult.status_code }} | |
</span> | |
<span *ngIf="testResult.response_time_ms" class="response-time"> | |
Time: {{ testResult.response_time_ms }}ms | |
</span> | |
</div> | |
<div *ngIf="testResult.error" class="error-message"> | |
<mat-icon>error_outline</mat-icon> | |
{{ testResult.error }} | |
</div> | |
<div *ngIf="testResult.headers" class="response-section"> | |
<h5>Response Headers:</h5> | |
<pre>{{ testResult.headers | json }}</pre> | |
</div> | |
<div *ngIf="testResult.body" class="response-section"> | |
<h5>Response Body:</h5> | |
<pre>{{ testResult.body }}</pre> | |
</div> | |
</div> | |
<div class="test-note"> | |
<mat-icon>info</mat-icon> | |
<p>Note: Test requests use placeholder values for template variables.</p> | |
</div> | |
</div> | |
</div> | |
</mat-tab> | |
</mat-tab-group> | |
</form> | |
</mat-dialog-content> | |
<mat-dialog-actions align="end"> | |
<button mat-button (click)="cancel()">Cancel</button> | |
<button mat-raised-button color="primary" | |
(click)="save()" | |
[disabled]="form.invalid || saving"> | |
{{ saving ? 'Saving...' : 'Save' }} | |
</button> | |
</mat-dialog-actions> |