import { CdkConnectedOverlay, CdkOverlayOrigin, type ConnectedPosition, } from '@angular/cdk/overlay'; import { ChangeDetectionStrategy, Component, computed, inject, signal, } from '@angular/core'; import { AFRICANIES_SDK_CONFIG, AuthTokenService } from '@africanies/africanies-core'; import { AfricaniesIconComponent } from '@africanies/africanies-icons'; import { AlertComponent, ButtonComponent, TextareaComponent, ToastService, } from '@africanies/africanies-ui'; /** Same end-aligned placement as {@link ActionMenuComponent}. */ const TOKEN_PANEL_POSITIONS: ConnectedPosition[] = [ { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 6, }, { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -6, }, ]; /** * Playground-only control to paste a bearer token for live SDK HTTP calls. * * Panel uses CDK connected overlay (same pattern as action menu / select) so it * is not clipped by header overflow and gets an opaque surface token. */ @Component({ selector: 'app-playground-access-token', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ AlertComponent, ButtonComponent, TextareaComponent, AfricaniesIconComponent, CdkOverlayOrigin, CdkConnectedOverlay, ], template: ` {{ hasToken() ? 'Connected' : 'API token' }} API access {{ apiBaseUrl() }} @if (hasToken()) { } @else { } Sent as Authorization: Bearer … on outbound HTTP via the SDK interceptor. Clear token Save & connect `, }) export class PlaygroundAccessTokenComponent { private readonly auth = inject(AuthTokenService); private readonly sdkConfig = inject(AFRICANIES_SDK_CONFIG); private readonly toast = inject(ToastService); protected readonly panelPositions = TOKEN_PANEL_POSITIONS; protected readonly panelOpen = signal(false); protected readonly draft = signal(''); protected readonly hasToken = computed(() => this.auth.token() != null); protected readonly apiBaseUrl = computed(() => this.sdkConfig.baseUrl); protected readonly connectedMessage = computed(() => { const token = this.auth.token(); if (!token) { return ''; } const suffix = token.length <= 4 ? '••••' : `••••${token.slice(-4)}`; return `Requests include your saved token (${suffix}). Replace below to update.`; }); protected togglePanel(): void { this.panelOpen.update((open) => !open); } protected closePanel(): void { this.panelOpen.set(false); } protected save(): void { this.auth.set(this.draft()); this.draft.set(''); this.closePanel(); this.toast.success( 'SDK HTTP calls will include your bearer token.', 'Connected', ); } protected clear(): void { this.auth.clear(); this.draft.set(''); this.toast.info( 'Live catalog and profile calls may fail until you connect again.', 'Token cleared', ); } }
{{ apiBaseUrl() }}
Sent as Authorization: Bearer … on outbound HTTP via the SDK interceptor.