/** * Playground snippets — form controls (@africanies/africanies-ui). */ export /** * */ const FORMS_TEXT = ` // Text fields: label + optional hint/error. Bind with [(value)]. // Prefix/suffix slots use attribute selectors (prefix, suffix). import { Component, signal } from '@angular/core'; import { AfricaniesIconComponent } from '@africanies/africanies-icons'; import { TextInputComponent } from '@africanies/africanies-ui'; @Component({ selector: 'app-shipment-reference-form', standalone: true, imports: [TextInputComponent, AfricaniesIconComponent], template: \`
\`, }) export class ShipmentReferenceFormComponent { protected readonly tracking = signal('AWB-10482'); protected readonly reference = signal('PO-7781'); protected readonly email = signal('not-an-email'); protected readonly locked = signal('WH-LOCKED-01'); } `; export /** * */ const FORMS_TEXTAREA = ` // Same label / hint / error pattern as text inputs, just multi-line. import { Component, signal } from '@angular/core'; import { TextareaComponent } from '@africanies/africanies-ui'; @Component({ selector: 'app-shipment-instructions-form', standalone: true, imports: [TextareaComponent], template: \`
\`, }) export class ShipmentInstructionsFormComponent { protected readonly instructions = signal('Fragile — keep upright'); protected readonly notesError = signal('x'.repeat(12)); } `; export /** * */ const FORMS_NUMBER = ` // Bound value is number | null. Prefix/suffix are display chrome only — // commas in the UI are cosmetic; send the numeric value to the API. import { DecimalPipe } from '@angular/common'; import { Component, signal } from '@angular/core'; import { NumberInputComponent } from '@africanies/africanies-ui'; @Component({ selector: 'app-shipment-value-form', standalone: true, imports: [DecimalPipe, NumberInputComponent], template: \`
$ kg

Stored amount: {{ amount() == null ? '—' : (amount()! | number: '1.0-6') }}

\`, }) export class ShipmentValueFormComponent { protected readonly amount = signal(12500); protected readonly weight = signal(24.5); } `; export /** * */ const FORMS_PHONE = ` // [(value)] is an E.164 string (+234…). Edit screens prepopulate from the API // phone field; countryIso2 is a hint when dial codes are shared (e.g. +1). import { Component, signal } from '@angular/core'; import { PhoneInputComponent } from '@africanies/africanies-ui'; @Component({ selector: 'app-consignee-phone-form', standalone: true, imports: [PhoneInputComponent], template: \` \`, }) export class ConsigneePhoneFormComponent { protected readonly phone = signal('+2348012345678'); protected readonly lockedPhone = signal('+233244123456'); } `; export /** * */ const FORMS_SELECT = ` // Single/multi select binds [(selected)]. [searchable] for long lists. // allowFreeText = inline tags; [create] opens a modal (needs provideAfricaniesUiOverlays()). import { Component, signal } from '@angular/core'; import { AfricaniesIconComponent } from '@africanies/africanies-icons'; import { SelectComponent, type SelectCreateConfig, type SelectOption, } from '@africanies/africanies-ui'; // Example modal close payload — your modal component returns this shape. interface WarehouseCreateResult { id: string; name: string; } @Component({ selector: 'app-shipment-routing-form', standalone: true, imports: [SelectComponent, AfricaniesIconComponent], template: \`
\`, }) export class ShipmentRoutingFormComponent { protected readonly warehouses = signal[]>([ { label: 'Lagos Hub', value: 'los', prefix: 'warehouse', suffix: 'globe' }, { label: 'Accra Depot', value: 'acc', prefix: 'warehouse' }, ]); protected readonly selectedWarehouse = signal | null>(null); protected readonly tags = signal[]>([ { label: 'Fragile', value: 'fragile', prefix: 'warning' }, { label: 'Priority', value: 'priority', prefix: 'alarm' }, ]); protected readonly selectedTags = signal[]>([]); protected readonly incoterms: SelectOption[] = [ { label: 'DDP', value: 'ddp' }, { label: 'DAP', value: 'dap' }, { label: 'EXW', value: 'exw' }, ]; protected readonly selectedIncoterm = signal | null>(null); protected readonly carriers = signal[]>([ { label: 'DHL Express', value: 'dhl', prefix: 'airplane' }, { label: 'FedEx', value: 'fedex', prefix: 'airplane' }, ]); protected readonly selectedCarrier = signal | null>(null); // Modal-backed create — component is your standalone creation form. protected readonly warehouseCreate: SelectCreateConfig = { label: 'Add warehouse', component: WarehouseCreateModalComponent, data: { region: 'west-africa' }, mapResult: (result) => ({ label: result.name, value: result.id, prefix: 'warehouse', }), }; } // Stub — replace with your real modal panel opened via ModalService. @Component({ selector: 'app-warehouse-create-modal', standalone: true, template: '' }) export class WarehouseCreateModalComponent {} // app.config.ts — required once per app when any select uses [create]: // // import { provideAfricaniesUiOverlays } from '@africanies/africanies-ui'; // // export const appConfig: ApplicationConfig = { // providers: [provideAfricaniesUiOverlays(), /* … */], // }; `; export /** * */ const FORMS_CHOICE = ` // Checkbox / toggle for booleans; radio for exactly-one-of. All use [(value)]. import { Component, signal } from '@angular/core'; import { CheckboxComponent, RadioComponent, ToggleComponent, type RadioOption, } from '@africanies/africanies-ui'; @Component({ selector: 'app-shipment-options-form', standalone: true, imports: [CheckboxComponent, ToggleComponent, RadioComponent], template: \`
\`, }) export class ShipmentOptionsFormComponent { protected readonly signature = signal(true); protected readonly insured = signal(false); protected readonly terms = signal(false); protected readonly smsNotify = signal(true); protected readonly holdAtDepot = signal(false); protected readonly customsHold = signal(false); protected readonly customsHoldSaving = signal(false); protected readonly serviceLevels: RadioOption[] = [ { label: 'Economy', value: 'economy' }, { label: 'Standard', value: 'standard' }, { label: 'Express', value: 'express' }, ]; protected readonly serviceLevel = signal('standard'); protected saveCustomsHold(next: boolean): void { this.customsHoldSaving.set(true); window.setTimeout(() => { this.customsHold.set(next); this.customsHoldSaving.set(false); }, 1200); } } `; export /** * */ const FORMS_DATE_FILE = ` // Date picker emits YYYY-MM-DD (string | null). Keep that ISO string as-is. import { Component, signal } from '@angular/core'; import { DatePickerComponent } from '@africanies/africanies-ui'; @Component({ selector: 'app-shipment-schedule-form', standalone: true, imports: [DatePickerComponent], template: \`
\`, }) export class ShipmentScheduleFormComponent { protected readonly readyDate = signal('2026-08-15'); protected readonly cutoffDate = signal(null); } `; export /** * */ const FORMS_ADDRESS = ` // Google Places address field. Bootstrap with provideGooglePlaces({ apiKey }). // [(value)] is AddressPlace | null; (placeSelected) fires when a suggestion is chosen. import { Component, signal } from '@angular/core'; import { AfricaniesIconComponent } from '@africanies/africanies-icons'; import { AddressInputComponent, type AddressPlace, provideGooglePlaces, } from '@africanies/africanies-ui'; // app.config.ts export const appConfig = { providers: [ provideGooglePlaces({ apiKey: import.meta.env['NG_APP_GOOGLE_MAPS_KEY'] }), ], }; @Component({ selector: 'app-pickup-address-form', standalone: true, imports: [AddressInputComponent, AfricaniesIconComponent], template: \`
{{ pickup() | json }}
\`, }) export class PickupAddressFormComponent { protected readonly pickup = signal(null); protected onPlace(place: AddressPlace): void { console.log('selected', place); } } `; export /** * */ const FORMS_FILE_UPLOAD = ` // Browse, drag-drop, or camera. You get FileUploadResult[] on (filesSelected) — // upload them yourself. accept filters client-side; mismatches are skipped. // Added files include an underlined View action for a larger preview. import { Component, signal } from '@angular/core'; import { FileUploadComponent, type FileUploadResult, } from '@africanies/africanies-ui'; @Component({ selector: 'app-kyc-upload-form', standalone: true, imports: [FileUploadComponent], template: \` \`, }) export class KycUploadFormComponent { protected readonly files = signal([]); protected onFiles(next: FileUploadResult[]): void { this.files.set(next); } } `; export /** * */ const FORMS_OTP = ` // OTP is one string, not an array. (resend) is your cue to call the API — // the control only runs the cooldown UI. (completed) fires when all digits are in. // variant="masked" hides each digit (PIN-style); the model is still the real code. import { Component, signal } from '@angular/core'; import { OtpInputComponent } from '@africanies/africanies-ui'; @Component({ selector: 'app-verify-otp-form', standalone: true, imports: [OtpInputComponent], template: \` \`, }) export class VerifyOtpFormComponent { protected readonly code = signal('482913'); protected readonly pin = signal('7391'); protected verify(code: string): void { // POST /auth/verify { code } console.log('OTP complete', code); } protected confirmPin(pin: string): void { // POST /wallet/confirm { pin } console.log('PIN complete', pin); } protected sendAgain(): void { // POST /auth/resend-otp console.log('Resend OTP'); } } `; export /** * */ const FORMS_LIVE_VALUES = ` // Same signals you bound with [(value)] / [(selected)] — read them anywhere. // Call them as functions: tracking(), amount(). Don't forget the (). import { DecimalPipe } from '@angular/common'; import { Component, signal } from '@angular/core'; import { type FileUploadResult, type SelectOption } from '@africanies/africanies-ui'; @Component({ selector: 'app-shipment-summary', standalone: true, imports: [DecimalPipe], template: \`
Tracking
{{ tracking() || '—' }}
Amount
{{ amount() == null ? '—' : (amount()! | number: '1.0-6') }}
Warehouse
{{ selectedWarehouse()?.label ?? '—' }}
Tags
{{ tagSummary() }}
Service
{{ serviceLevel() ?? '—' }}
Files
{{ files().length ? files() .map((f) => f.file.name) .join(', ') : '—' }}
\`, }) export class ShipmentSummaryComponent { // These signals are the same instances bound in sibling form controls. protected readonly tracking = signal(''); protected readonly amount = signal(12500); protected readonly selectedWarehouse = signal | null>(null); protected readonly selectedTags = signal[]>([]); protected readonly serviceLevel = signal('standard'); protected readonly files = signal([]); protected tagSummary(): string { const selected = this.selectedTags(); if (!selected.length) { return '—'; } return selected.map((t) => t.label).join(', '); } } `;