@africanies/angular-web-sdk 0.1.12 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/.nx/cache/13293303228070520113/dist/libs/africanies-core/README.md +85 -0
  2. package/.nx/cache/13293303228070520113/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +4699 -0
  3. package/.nx/cache/13293303228070520113/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +1 -0
  4. package/.nx/cache/13293303228070520113/dist/libs/africanies-core/package.json +31 -0
  5. package/.nx/cache/13293303228070520113/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +2748 -0
  6. package/.nx/cache/13387631073062967069/dist/libs/africanies-theme/README.md +46 -0
  7. package/.nx/cache/13387631073062967069/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +249 -0
  8. package/.nx/cache/13387631073062967069/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -0
  9. package/.nx/cache/13387631073062967069/dist/libs/africanies-theme/package.json +35 -0
  10. package/.nx/cache/13387631073062967069/dist/libs/africanies-theme/tailwind-preset.cjs +330 -0
  11. package/.nx/cache/13387631073062967069/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +178 -0
  12. package/.nx/cache/16704640000606796352/dist/libs/africanies-storage/README.md +31 -0
  13. package/.nx/cache/16704640000606796352/dist/libs/africanies-storage/fesm2022/africanies-africanies-storage.mjs +249 -0
  14. package/.nx/cache/16704640000606796352/dist/libs/africanies-storage/fesm2022/africanies-africanies-storage.mjs.map +1 -0
  15. package/.nx/cache/16704640000606796352/dist/libs/africanies-storage/package.json +26 -0
  16. package/.nx/cache/16704640000606796352/dist/libs/africanies-storage/types/africanies-africanies-storage.d.ts +210 -0
  17. package/.nx/cache/2427575433215940812/dist/libs/africanies-ui/README.md +68 -0
  18. package/.nx/cache/2427575433215940812/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
  19. package/.nx/cache/2427575433215940812/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
  20. package/.nx/cache/2427575433215940812/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +5 -0
  21. package/.nx/cache/2427575433215940812/dist/libs/africanies-ui/assets/carriers/dhl.svg +7 -0
  22. package/.nx/cache/2427575433215940812/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +21251 -0
  23. package/.nx/cache/2427575433215940812/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -0
  24. package/.nx/cache/2427575433215940812/dist/libs/africanies-ui/package.json +35 -0
  25. package/.nx/cache/2427575433215940812/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +5803 -0
  26. package/.nx/cache/7730350229628359718/dist/libs/africanies-icons/README.md +69 -0
  27. package/.nx/cache/7730350229628359718/dist/libs/africanies-icons/assets/icons.sprite.svg +1851 -0
  28. package/.nx/cache/7730350229628359718/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs +839 -0
  29. package/.nx/cache/7730350229628359718/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs.map +1 -0
  30. package/.nx/cache/7730350229628359718/dist/libs/africanies-icons/package.json +30 -0
  31. package/.nx/cache/7730350229628359718/dist/libs/africanies-icons/types/africanies-africanies-icons.d.ts +108 -0
  32. package/.nx/cache/run.json +35 -35
  33. package/.nx/cache/terminalOutputs/10603154579345867998 +397 -0
  34. package/.nx/cache/terminalOutputs/12354163057083828162 +80 -0
  35. package/.nx/cache/terminalOutputs/13293303228070520113 +21 -0
  36. package/.nx/cache/terminalOutputs/13387631073062967069 +21 -0
  37. package/.nx/cache/terminalOutputs/14876351543379349919 +176 -0
  38. package/.nx/cache/terminalOutputs/15336883221450327589 +21 -0
  39. package/.nx/cache/terminalOutputs/16208231681037145050 +86 -84
  40. package/.nx/cache/terminalOutputs/16704640000606796352 +21 -0
  41. package/.nx/cache/terminalOutputs/1811006905937373707 +74 -0
  42. package/.nx/cache/terminalOutputs/2005781115771702035 +8 -0
  43. package/.nx/cache/terminalOutputs/2360743690449633551 +21 -0
  44. package/.nx/cache/terminalOutputs/2427575433215940812 +21 -0
  45. package/.nx/cache/terminalOutputs/2981230413857865352 +8 -0
  46. package/.nx/cache/terminalOutputs/3243550283886864639 +8 -0
  47. package/.nx/cache/terminalOutputs/3380188914363501109 +231 -0
  48. package/.nx/cache/terminalOutputs/3699068646900552690 +8 -0
  49. package/.nx/cache/terminalOutputs/5175669339257931410 +8 -0
  50. package/.nx/cache/terminalOutputs/6040230831125637867 +8 -0
  51. package/.nx/cache/terminalOutputs/7730350229628359718 +21 -0
  52. package/.nx/cache/terminalOutputs/8098730470212963224 +87 -0
  53. package/.nx/cache/terminalOutputs/8928690901537686687 +21 -0
  54. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db +0 -0
  55. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-shm +0 -0
  56. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-wal +0 -0
  57. package/.nx/workspace-data/d/daemon.log +2859 -0
  58. package/.nx/workspace-data/d/server-process.json +2 -2
  59. package/.nx/workspace-data/file-map.json +2155 -2183
  60. package/.nx/workspace-data/nx_files.nxt +0 -0
  61. package/.nx/workspace-data/project-graph.json +2 -2
  62. package/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +60 -0
  63. package/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +1 -1
  64. package/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs +6 -0
  65. package/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs.map +1 -1
  66. package/dist/libs/africanies-storage/fesm2022/africanies-africanies-storage.mjs +6 -0
  67. package/dist/libs/africanies-storage/fesm2022/africanies-africanies-storage.mjs.map +1 -1
  68. package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +6 -0
  69. package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -1
  70. package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +297 -51
  71. package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -1
  72. package/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +12 -12
  73. package/libs/africanies-core/src/lib/auth/auth-token.service.ts +3 -0
  74. package/libs/africanies-core/src/lib/auth/auth.service.ts +3 -0
  75. package/libs/africanies-core/src/lib/country/country.service.ts +3 -0
  76. package/libs/africanies-core/src/lib/currency/currency.service.ts +3 -0
  77. package/libs/africanies-core/src/lib/document/document.service.ts +3 -0
  78. package/libs/africanies-core/src/lib/file/file.service.ts +3 -0
  79. package/libs/africanies-core/src/lib/filters/filter-options.resolver.ts +3 -0
  80. package/libs/africanies-core/src/lib/http/api-client.ts +3 -0
  81. package/libs/africanies-core/src/lib/mode/mode-config.service.ts +3 -0
  82. package/libs/africanies-core/src/lib/notification/notification.service.ts +3 -0
  83. package/libs/africanies-core/src/lib/overlay/route-overlay.service.ts +3 -0
  84. package/libs/africanies-core/src/lib/payment-method/payment-method.service.ts +3 -0
  85. package/libs/africanies-core/src/lib/plan/plan.service.ts +3 -0
  86. package/libs/africanies-core/src/lib/product/product.service.ts +3 -0
  87. package/libs/africanies-core/src/lib/service/service.service.ts +3 -0
  88. package/libs/africanies-core/src/lib/shipment-method/shipment-method.service.ts +3 -0
  89. package/libs/africanies-core/src/lib/shipping/shipping-mode.service.ts +3 -0
  90. package/libs/africanies-core/src/lib/user/user.service.ts +3 -0
  91. package/libs/africanies-core/src/lib/warehouse/warehouse.service.ts +3 -0
  92. package/libs/africanies-core/src/lib/zone/zone.service.ts +3 -0
  93. package/libs/africanies-icons/src/lib/africanies-icon.component.ts +3 -0
  94. package/libs/africanies-icons/src/lib/icon-registry.service.ts +3 -0
  95. package/libs/africanies-storage/src/lib/local-storage.service.ts +3 -0
  96. package/libs/africanies-storage/src/lib/session-storage.service.ts +3 -0
  97. package/libs/africanies-theme/src/lib/mode-color.service.ts +3 -0
  98. package/libs/africanies-theme/src/lib/theme.service.ts +3 -0
  99. package/libs/africanies-ui/src/index.ts +11 -11
  100. package/libs/africanies-ui/src/lib/accordion/accordion.component.ts +3 -0
  101. package/libs/africanies-ui/src/lib/action-menu/action-menu-trigger.directive.ts +3 -0
  102. package/libs/africanies-ui/src/lib/action-menu/action-menu.component.ts +3 -0
  103. package/libs/africanies-ui/src/lib/alert/alert.component.ts +3 -0
  104. package/libs/africanies-ui/src/lib/avatar/avatar-menu.component.ts +3 -0
  105. package/libs/africanies-ui/src/lib/avatar/avatar.component.ts +3 -0
  106. package/libs/africanies-ui/src/lib/brand/brand-logo.component.ts +3 -0
  107. package/libs/africanies-ui/src/lib/button/button.component.ts +3 -0
  108. package/libs/africanies-ui/src/lib/carrier/carrier-logo.component.ts +3 -0
  109. package/libs/africanies-ui/src/lib/chip/chip.component.ts +3 -0
  110. package/libs/africanies-ui/src/lib/content-stack/content-stack.component.ts +3 -0
  111. package/libs/africanies-ui/src/lib/copy-button/copy-button.component.ts +3 -0
  112. package/libs/africanies-ui/src/lib/feedback/async-state.component.ts +3 -0
  113. package/libs/africanies-ui/src/lib/feedback/empty-state.component.ts +3 -0
  114. package/libs/africanies-ui/src/lib/feedback/error-indicator.component.ts +3 -0
  115. package/libs/africanies-ui/src/lib/feedback/error-state.component.ts +3 -0
  116. package/libs/africanies-ui/src/lib/feedback/loading-state.component.ts +3 -0
  117. package/libs/africanies-ui/src/lib/filters/filter-drawer.panel.ts +3 -0
  118. package/libs/africanies-ui/src/lib/filters/filter-drawer.service.ts +3 -0
  119. package/libs/africanies-ui/src/lib/filters/filter-query.service.ts +3 -0
  120. package/libs/africanies-ui/src/lib/forms/address-input/address-input.component.ts +3 -0
  121. package/libs/africanies-ui/src/lib/forms/address-input/google-places.service.ts +3 -0
  122. package/libs/africanies-ui/src/lib/forms/checkbox/checkbox.component.ts +3 -0
  123. package/libs/africanies-ui/src/lib/forms/date-picker/date-picker.component.ts +3 -0
  124. package/libs/africanies-ui/src/lib/forms/file-upload/camera-capture.dialog.ts +3 -0
  125. package/libs/africanies-ui/src/lib/forms/file-upload/file-preview.dialog.ts +3 -0
  126. package/libs/africanies-ui/src/lib/forms/file-upload/file-upload.component.ts +3 -0
  127. package/libs/africanies-ui/src/lib/forms/index.ts +3 -3
  128. package/libs/africanies-ui/src/lib/forms/number-input/number-input.component.ts +3 -0
  129. package/libs/africanies-ui/src/lib/forms/otp-input/otp-input.component.ts +3 -0
  130. package/libs/africanies-ui/src/lib/forms/phone-input/phone-input.component.ts +9 -6
  131. package/libs/africanies-ui/src/lib/forms/radio/radio.component.ts +3 -0
  132. package/libs/africanies-ui/src/lib/forms/search-combobox/search-combobox.component.ts +3 -0
  133. package/libs/africanies-ui/src/lib/forms/select/select.component.ts +3 -0
  134. package/libs/africanies-ui/src/lib/forms/text-input/text-input.component.ts +3 -0
  135. package/libs/africanies-ui/src/lib/forms/textarea/textarea.component.ts +3 -0
  136. package/libs/africanies-ui/src/lib/forms/toggle/toggle.component.ts +3 -0
  137. package/libs/africanies-ui/src/lib/image/image.component.ts +3 -0
  138. package/libs/africanies-ui/src/lib/info-popover/info-popover-content.directive.ts +3 -0
  139. package/libs/africanies-ui/src/lib/info-popover/info-popover-trigger.directive.ts +3 -0
  140. package/libs/africanies-ui/src/lib/info-popover/info-popover.component.ts +3 -0
  141. package/libs/africanies-ui/src/lib/layout/app-shell-content-header.component.ts +3 -0
  142. package/libs/africanies-ui/src/lib/layout/app-shell-header-slot.directive.ts +3 -0
  143. package/libs/africanies-ui/src/lib/layout/app-shell-header.component.ts +9 -0
  144. package/libs/africanies-ui/src/lib/layout/app-shell.component.ts +3 -0
  145. package/libs/africanies-ui/src/lib/layout/header-weather.spec.ts +62 -94
  146. package/libs/africanies-ui/src/lib/layout/header-weather.ts +54 -61
  147. package/libs/africanies-ui/src/lib/layout/page-header.component.ts +3 -0
  148. package/libs/africanies-ui/src/lib/modules/africanies-actions.module.ts +3 -0
  149. package/libs/africanies-ui/src/lib/modules/africanies-feedback.module.ts +3 -0
  150. package/libs/africanies-ui/src/lib/modules/africanies-forms.module.ts +3 -0
  151. package/libs/africanies-ui/src/lib/modules/africanies-info-popover.module.ts +3 -0
  152. package/libs/africanies-ui/src/lib/modules/africanies-navigation.module.ts +3 -0
  153. package/libs/africanies-ui/src/lib/modules/africanies-stepper.module.ts +3 -0
  154. package/libs/africanies-ui/src/lib/modules/africanies-table.module.ts +3 -0
  155. package/libs/africanies-ui/src/lib/modules/africanies-tooltip.module.ts +3 -0
  156. package/libs/africanies-ui/src/lib/navigation/breadcrumb/breadcrumb.component.ts +3 -0
  157. package/libs/africanies-ui/src/lib/navigation/segment/segment.component.ts +3 -0
  158. package/libs/africanies-ui/src/lib/navigation/shipping-mode-switch/shipping-mode-switch.component.ts +3 -0
  159. package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.ts +3 -0
  160. package/libs/africanies-ui/src/lib/navigation/tabs/tab-def.directive.ts +3 -0
  161. package/libs/africanies-ui/src/lib/navigation/tabs/tabs.component.ts +3 -0
  162. package/libs/africanies-ui/src/lib/notifications/notification-drawer.panel.ts +3 -0
  163. package/libs/africanies-ui/src/lib/notifications/notification-drawer.service.ts +3 -0
  164. package/libs/africanies-ui/src/lib/overlay/confirm-dialog.component.ts +3 -0
  165. package/libs/africanies-ui/src/lib/overlay/confirm.service.ts +3 -0
  166. package/libs/africanies-ui/src/lib/overlay/drawer.service.ts +3 -0
  167. package/libs/africanies-ui/src/lib/overlay/modal.service.ts +3 -0
  168. package/libs/africanies-ui/src/lib/overlay/overlay-frame.component.ts +9 -0
  169. package/libs/africanies-ui/src/lib/pagination/pagination.component.ts +3 -0
  170. package/libs/africanies-ui/src/lib/stepper/step-def.directive.ts +3 -0
  171. package/libs/africanies-ui/src/lib/stepper/stepper.component.ts +3 -0
  172. package/libs/africanies-ui/src/lib/table/cell-def.directive.ts +3 -0
  173. package/libs/africanies-ui/src/lib/table/header-cell-def.directive.ts +3 -0
  174. package/libs/africanies-ui/src/lib/table/row-detail-def.directive.ts +3 -0
  175. package/libs/africanies-ui/src/lib/table/table.component.ts +3 -0
  176. package/libs/africanies-ui/src/lib/toast/toast-host.component.ts +4 -1
  177. package/libs/africanies-ui/src/lib/toast/toast-item.component.ts +3 -0
  178. package/libs/africanies-ui/src/lib/toast/toast.service.ts +3 -0
  179. package/libs/africanies-ui/src/lib/tooltip/tooltip-trigger.directive.ts +3 -0
  180. package/libs/africanies-ui/src/lib/tooltip/tooltip.component.ts +3 -0
  181. package/package.json +1 -1
@@ -37,12 +37,12 @@ export {
37
37
  PHONE_DIAL_CODES,
38
38
  PHONE_MAX_NATIONAL_DIGITS,
39
39
  PHONE_MIN_NATIONAL_DIGITS,
40
- type PhoneCountryOption,
41
- PhoneInputComponent,
42
- type PhoneNumberValue,
43
40
  phoneCountryName,
41
+ type PhoneCountryOption,
44
42
  phoneCountryOptions,
45
43
  phoneDigitsOnly,
44
+ PhoneInputComponent,
45
+ type PhoneNumberValue,
46
46
  phoneToE164,
47
47
  removeDialCode,
48
48
  } from './phone-input';
@@ -148,6 +148,9 @@ function formatWhileTyping(raw: string): {
148
148
  }
149
149
  `,
150
150
  })
151
+ /**
152
+ * Numeric field following the shared AFRICANIES form field pattern.
153
+ */
151
154
  export class NumberInputComponent implements ControlValueAccessor {
152
155
  protected readonly controlId = `africanies-number-input-${++nextNumberInputId}`;
153
156
  protected readonly hintId = `${this.controlId}-hint`;
@@ -148,6 +148,9 @@ export type OtpInputVariant = 'default' | 'masked';
148
148
  </div>
149
149
  `,
150
150
  })
151
+ /**
152
+ * One-time-passcode field — discrete digit cells, single string model.
153
+ */
151
154
  export class OtpInputComponent implements ControlValueAccessor, OnDestroy {
152
155
  private readonly destroyRef = inject(DestroyRef);
153
156
  private readonly cellInputs =
@@ -1,3 +1,8 @@
1
+ import {
2
+ CdkConnectedOverlay,
3
+ CdkOverlayOrigin,
4
+ type ConnectedPosition,
5
+ } from '@angular/cdk/overlay';
1
6
  import {
2
7
  afterNextRender,
3
8
  booleanAttribute,
@@ -15,18 +20,13 @@ import {
15
20
  untracked,
16
21
  viewChild,
17
22
  } from '@angular/core';
18
- import {
19
- CdkConnectedOverlay,
20
- CdkOverlayOrigin,
21
- type ConnectedPosition,
22
- } from '@angular/cdk/overlay';
23
23
  import {
24
24
  type ControlValueAccessor,
25
25
  NG_VALUE_ACCESSOR,
26
26
  } from '@angular/forms';
27
27
 
28
- import { AfricaniesIconComponent } from '@africanies/africanies-icons';
29
28
  import { countryFlagUrl } from '@africanies/africanies-core';
29
+ import { AfricaniesIconComponent } from '@africanies/africanies-icons';
30
30
 
31
31
  import {
32
32
  FORM_DISABLED_CLASS,
@@ -225,6 +225,9 @@ const PANEL_POSITIONS: ConnectedPosition[] = [
225
225
  </ng-template>
226
226
  `,
227
227
  })
228
+ /**
229
+ * International phone field — flag + dial code + national number.
230
+ */
228
231
  export class PhoneInputComponent implements ControlValueAccessor {
229
232
  protected readonly controlId = `africanies-phone-input-${++nextPhoneInputId}`;
230
233
  protected readonly hintId = `${this.controlId}-hint`;
@@ -118,6 +118,9 @@ let nextRadioId = 0;
118
118
  </fieldset>
119
119
  `,
120
120
  })
121
+ /**
122
+ * Radio group following the shared AFRICANIES form field pattern.
123
+ */
121
124
  export class RadioComponent<T = string> implements ControlValueAccessor {
122
125
  private readonly modeColor = inject(ModeColorService);
123
126
 
@@ -269,6 +269,9 @@ const PANEL_POSITIONS: ConnectedPosition[] = [
269
269
  }
270
270
  `,
271
271
  })
272
+ /**
273
+ * Generic type-as-you-search combobox with a debounced async lookup and overlay result list.
274
+ */
272
275
  export class SearchComboboxComponent<T> implements ControlValueAccessor {
273
276
  private readonly destroyRef = inject(DestroyRef);
274
277
 
@@ -443,6 +443,9 @@ const SELECT_PANEL_POSITIONS: ConnectedPosition[] = [
443
443
  }
444
444
  `,
445
445
  })
446
+ /**
447
+ * Option select following the shared AFRICANIES form field pattern.
448
+ */
446
449
  export class SelectComponent<T = string> implements ControlValueAccessor {
447
450
  private readonly modal = inject(ModalService, { optional: true });
448
451
  protected readonly modeColor = inject(ModeColorService);
@@ -115,6 +115,9 @@ export type TextInputType =
115
115
  }
116
116
  `,
117
117
  })
118
+ /**
119
+ * Single-line text field — **reference** form control for `@africanies/africanies-ui`.
120
+ */
118
121
  export class TextInputComponent implements ControlValueAccessor {
119
122
  /** Stable ids so label / aria-describedby stay paired across re-renders. */
120
123
  protected readonly controlId = `africanies-text-input-${++nextTextInputId}`;
@@ -82,6 +82,9 @@ let nextTextareaId = 0;
82
82
  }
83
83
  `,
84
84
  })
85
+ /**
86
+ * Multi-line text field following the shared form-control pattern (`shared AFRICANIES form field pattern`). Supports prefix/suffix slots, field-level `error`, and {@link ControlValueAccessor}.
87
+ */
85
88
  export class TextareaComponent implements ControlValueAccessor {
86
89
  protected readonly controlId = `africanies-textarea-${++nextTextareaId}`;
87
90
  protected readonly hintId = `${this.controlId}-hint`;
@@ -105,6 +105,9 @@ let nextToggleId = 0;
105
105
  </div>
106
106
  `,
107
107
  })
108
+ /**
109
+ * Boolean switch following the shared AFRICANIES form field pattern.
110
+ */
108
111
  export class ToggleComponent implements ControlValueAccessor {
109
112
  private readonly modeColor = inject(ModeColorService);
110
113
 
@@ -95,6 +95,9 @@ export type ImageFit = 'cover' | 'contain';
95
95
  </div>
96
96
  `,
97
97
  })
98
+ /**
99
+ * Image with a shaped loading frame and fallback while the asset fetches or if it fails to render.
100
+ */
98
101
  export class ImageComponent {
99
102
  private readonly imgRef = viewChild<ElementRef<HTMLImageElement>>('img');
100
103
 
@@ -20,4 +20,7 @@ import { Directive } from '@angular/core';
20
20
  selector: '[africaniesInfoPopoverContent]',
21
21
  standalone: true,
22
22
  })
23
+ /**
24
+ * Marks projected content as the panel body for {@link InfoPopoverComponent}.
25
+ */
23
26
  export class InfoPopoverContentDirective {}
@@ -18,4 +18,7 @@ import { Directive } from '@angular/core';
18
18
  selector: '[africaniesInfoPopoverTrigger]',
19
19
  standalone: true,
20
20
  })
21
+ /**
22
+ * Marks projected content as the custom trigger for {@link InfoPopoverComponent}.
23
+ */
21
24
  export class InfoPopoverTriggerDirective {}
@@ -205,6 +205,9 @@ const PLACEMENT_POSITIONS: Record<InfoPopoverPlacement, ConnectedPosition[]> = {
205
205
  </ng-template>
206
206
  `,
207
207
  })
208
+ /**
209
+ * Hover / focus / click info panel with a white card, optional title, and fully projected body content (any child component or markup).
210
+ */
208
211
  export class InfoPopoverComponent {
209
212
  protected readonly panelId = `africanies-info-popover-${++nextInfoPopoverId}`;
210
213
 
@@ -71,6 +71,9 @@ import { PageHeaderComponent } from './page-header.component';
71
71
  }
72
72
  `,
73
73
  })
74
+ /**
75
+ * Content chrome above the app-shell body: Back, breadcrumbs, then the page title and optional subtitle.
76
+ */
74
77
  export class AppShellContentHeaderComponent {
75
78
  private readonly router = inject(Router);
76
79
 
@@ -9,4 +9,7 @@ import { Directive } from '@angular/core';
9
9
  selector: '[africaniesAppShellHeader]',
10
10
  standalone: true,
11
11
  })
12
+ /**
13
+ * Marks projected content as the custom header for {@link AppShellComponent}.
14
+ */
12
15
  export class AppShellHeaderSlotDirective {}
@@ -38,6 +38,9 @@ export type AppShellHeaderDensity = 'mobile' | 'tablet' | 'desktop';
38
38
  selector: '[africaniesAppShellHeaderStart]',
39
39
  standalone: true,
40
40
  })
41
+ /**
42
+ * Marks projected content as extra leading chrome in {@link AppShellHeaderComponent}.
43
+ */
41
44
  export class AppShellHeaderStartDirective {}
42
45
 
43
46
  /**
@@ -47,6 +50,9 @@ export class AppShellHeaderStartDirective {}
47
50
  selector: '[africaniesAppShellHeaderEnd]',
48
51
  standalone: true,
49
52
  })
53
+ /**
54
+ * Marks projected content as extra trailing chrome in {@link AppShellHeaderComponent}.
55
+ */
50
56
  export class AppShellHeaderEndDirective {}
51
57
 
52
58
  /**
@@ -206,6 +212,9 @@ export class AppShellHeaderEndDirective {}
206
212
  </div>
207
213
  `,
208
214
  })
215
+ /**
216
+ * Product header bar for {@link AppShellComponent}: a short time-of-day kicker plus the given name, local weather, live clock, notification inbox drawer, and avatar menu. Breadcrumbs and Back belong in the content column via {@link AppShellContentHeaderComponent}.
217
+ */
209
218
  export class AppShellHeaderComponent {
210
219
  private readonly notificationsDrawer = inject(NotificationDrawerService);
211
220
  private readonly destroyRef = inject(DestroyRef);
@@ -85,6 +85,9 @@ export type AppShellLayoutPreview = 'mobile' | 'tablet' | 'desktop';
85
85
  },
86
86
  templateUrl: './app-shell.component.html',
87
87
  })
88
+ /**
89
+ * Application chrome scaffold: side nav + sticky header + constrained content.
90
+ */
88
91
  export class AppShellComponent {
89
92
  protected readonly modeColor = inject(ModeColorService);
90
93
  protected readonly logoMiniUrl = inject(AFRICANIES_BRAND_LOGO_MINI_URL);
@@ -66,18 +66,15 @@ describe('loadHeaderWeather', () => {
66
66
  ).resolves.toBeNull();
67
67
  });
68
68
 
69
- it('reads Open-Meteo after IP geolocation and reverse-geocodes the city', async () => {
69
+ it('does not label the device with the IP city', async () => {
70
70
  const fetchFn = jest.fn(async (url: string) => {
71
71
  if (String(url).includes('geojs')) {
72
72
  return jsonResponse({
73
- latitude: '9.0765',
74
- longitude: '7.3986',
73
+ latitude: '6.45',
74
+ longitude: '3.4',
75
75
  city: 'Lagos',
76
76
  });
77
77
  }
78
- if (String(url).includes('bigdatacloud')) {
79
- return jsonResponse({ city: 'Abuja', locality: 'Abuja' });
80
- }
81
78
  return jsonResponse({
82
79
  current: { weather_code: 61, temperature_2m: 27.4 },
83
80
  });
@@ -88,38 +85,18 @@ describe('loadHeaderWeather', () => {
88
85
  ).resolves.toEqual({
89
86
  kind: 'rain',
90
87
  temperatureC: 27.4,
91
- city: 'Abuja',
92
- });
93
- expect(fetchFn).toHaveBeenCalledTimes(3);
94
- });
95
-
96
- it('falls back to the IP city when reverse geocode is empty', async () => {
97
- const fetchFn = jest.fn(async (url: string) => {
98
- if (String(url).includes('geojs')) {
99
- return jsonResponse({ latitude: 1, longitude: 2, city: 'Lagos' });
100
- }
101
- if (String(url).includes('bigdatacloud')) {
102
- return jsonResponse({});
103
- }
104
- return jsonResponse({ current: { weather_code: 0, temperature_2m: 30 } });
105
- });
106
-
107
- await expect(
108
- loadHeaderWeather(fetchFn as unknown as typeof fetch),
109
- ).resolves.toEqual({
110
- kind: 'clear',
111
- temperatureC: 30,
112
- city: 'Lagos',
88
+ city: undefined,
113
89
  });
90
+ expect(fetchFn).not.toHaveBeenCalledWith(
91
+ expect.stringContaining('geocoding-api'),
92
+ );
114
93
  });
115
94
 
116
- it('reuses the same-hour session cache', async () => {
95
+ it('reuses the same-hour session cache when the device city was resolved', async () => {
96
+ stubDeviceAt(9.0765, 7.3986);
117
97
  const fetchFn = jest.fn(async (url: string) => {
118
- if (String(url).includes('geojs')) {
119
- return jsonResponse({ latitude: 1, longitude: 2 });
120
- }
121
- if (String(url).includes('bigdatacloud')) {
122
- return jsonResponse({ city: 'Accra' });
98
+ if (String(url).includes('geocoding-api')) {
99
+ return jsonResponse({ results: [{ name: 'Abuja' }] });
123
100
  }
124
101
  return jsonResponse({ current: { weather_code: 0, temperature_2m: 30 } });
125
102
  });
@@ -127,17 +104,16 @@ describe('loadHeaderWeather', () => {
127
104
  const first = await loadHeaderWeather(fetchFn as unknown as typeof fetch);
128
105
  const second = await loadHeaderWeather(fetchFn as unknown as typeof fetch);
129
106
 
130
- expect(first).toEqual(second);
131
- expect(fetchFn).toHaveBeenCalledTimes(3);
107
+ expect(first).toEqual({ kind: 'clear', temperatureC: 30, city: 'Abuja' });
108
+ expect(second).toEqual(first);
109
+ expect(fetchFn).toHaveBeenCalledTimes(2);
132
110
  });
133
111
 
134
112
  it('fetches again when the cached hour no longer matches', async () => {
113
+ stubDeviceAt(9.0765, 7.3986);
135
114
  const fetchFn = jest.fn(async (url: string) => {
136
- if (String(url).includes('geojs')) {
137
- return jsonResponse({ latitude: 1, longitude: 2, city: 'Lagos' });
138
- }
139
- if (String(url).includes('bigdatacloud')) {
140
- return jsonResponse({ city: 'Lagos' });
115
+ if (String(url).includes('geocoding-api')) {
116
+ return jsonResponse({ results: [{ name: 'Abuja' }] });
141
117
  }
142
118
  return jsonResponse({ current: { weather_code: 0, temperature_2m: 30.2 } });
143
119
  });
@@ -146,7 +122,7 @@ describe('loadHeaderWeather', () => {
146
122
  const cacheKey = Object.keys(sessionStorage).find((key) =>
147
123
  key.startsWith('africanies-header-weather'),
148
124
  );
149
- expect(cacheKey).toBeDefined();
125
+ expect(cacheKey).toBe('africanies-header-weather-v8');
150
126
  const stored = JSON.parse(sessionStorage.getItem(cacheKey!) ?? '{}') as {
151
127
  hour?: string;
152
128
  };
@@ -154,35 +130,15 @@ describe('loadHeaderWeather', () => {
154
130
  sessionStorage.setItem(cacheKey!, JSON.stringify(stored));
155
131
 
156
132
  await loadHeaderWeather(fetchFn as unknown as typeof fetch);
157
- expect(fetchFn).toHaveBeenCalledTimes(6);
133
+ expect(fetchFn).toHaveBeenCalledTimes(4);
158
134
  });
159
135
 
160
- it('uses browser coordinates when geolocation succeeds', async () => {
161
- Object.defineProperty(navigator, 'geolocation', {
162
- configurable: true,
163
- value: {
164
- getCurrentPosition: (success: PositionCallback) => {
165
- success({
166
- coords: {
167
- latitude: 9.0765,
168
- longitude: 7.3986,
169
- accuracy: 10,
170
- altitude: null,
171
- altitudeAccuracy: null,
172
- heading: null,
173
- speed: null,
174
- toJSON: () => ({}),
175
- },
176
- timestamp: Date.now(),
177
- toJSON: () => ({}),
178
- } as GeolocationPosition);
179
- },
180
- },
181
- });
136
+ it('uses device coordinates and ignores the IP city', async () => {
137
+ stubDeviceAt(9.0765, 7.3986);
182
138
 
183
139
  const fetchFn = jest.fn(async (url: string) => {
184
- if (String(url).includes('bigdatacloud')) {
185
- return jsonResponse({ city: 'Abuja' });
140
+ if (String(url).includes('geocoding-api')) {
141
+ return jsonResponse({ results: [{ name: 'Abuja' }] });
186
142
  }
187
143
  if (String(url).includes('geojs')) {
188
144
  return jsonResponse({
@@ -201,34 +157,15 @@ describe('loadHeaderWeather', () => {
201
157
  temperatureC: 31,
202
158
  city: 'Abuja',
203
159
  });
160
+ expect(fetchFn).not.toHaveBeenCalledWith(expect.stringContaining('geojs'));
204
161
  });
205
162
 
206
- it('uses the IP city when browser geo succeeds but reverse geocode is empty', async () => {
207
- Object.defineProperty(navigator, 'geolocation', {
208
- configurable: true,
209
- value: {
210
- getCurrentPosition: (success: PositionCallback) => {
211
- success({
212
- coords: {
213
- latitude: 9.0765,
214
- longitude: 7.3986,
215
- accuracy: 10,
216
- altitude: null,
217
- altitudeAccuracy: null,
218
- heading: null,
219
- speed: null,
220
- toJSON: () => ({}),
221
- },
222
- timestamp: Date.now(),
223
- toJSON: () => ({}),
224
- } as GeolocationPosition);
225
- },
226
- },
227
- });
163
+ it('omits the city when the device fix has no place name', async () => {
164
+ stubDeviceAt(9.0765, 7.3986);
228
165
 
229
166
  const fetchFn = jest.fn(async (url: string) => {
230
- if (String(url).includes('bigdatacloud')) {
231
- return jsonResponse({ city: '' });
167
+ if (String(url).includes('geocoding-api')) {
168
+ return jsonResponse({ results: [{ name: '' }] });
232
169
  }
233
170
  if (String(url).includes('geojs')) {
234
171
  return jsonResponse({
@@ -245,13 +182,14 @@ describe('loadHeaderWeather', () => {
245
182
  ).resolves.toEqual({
246
183
  kind: 'clear',
247
184
  temperatureC: 31,
248
- city: 'Lagos',
185
+ city: undefined,
249
186
  });
187
+ expect(fetchFn).not.toHaveBeenCalledWith(expect.stringContaining('geojs'));
250
188
  });
251
189
 
252
190
  it('refetches when the hour cache has weather but no city', async () => {
253
191
  sessionStorage.setItem(
254
- 'africanies-header-weather-v7',
192
+ 'africanies-header-weather-v8',
255
193
  JSON.stringify({
256
194
  hour: `${new Date().getFullYear()}-${new Date().getMonth()}-${new Date().getDate()}-${new Date().getHours()}`,
257
195
  kind: 'cloudy',
@@ -274,7 +212,7 @@ describe('loadHeaderWeather', () => {
274
212
  ).resolves.toEqual({
275
213
  kind: 'cloudy',
276
214
  temperatureC: 25,
277
- city: 'Lagos',
215
+ city: undefined,
278
216
  });
279
217
  expect(fetchFn).toHaveBeenCalled();
280
218
  });
@@ -304,6 +242,36 @@ describe('headerWeatherIcon', () => {
304
242
  });
305
243
  });
306
244
 
245
+ function stubDeviceAt(latitude: number, longitude: number): void {
246
+ Object.defineProperty(navigator, 'permissions', {
247
+ configurable: true,
248
+ value: {
249
+ query: async () => ({ state: 'granted' }),
250
+ },
251
+ });
252
+ Object.defineProperty(navigator, 'geolocation', {
253
+ configurable: true,
254
+ value: {
255
+ getCurrentPosition: (success: PositionCallback) => {
256
+ success({
257
+ coords: {
258
+ latitude,
259
+ longitude,
260
+ accuracy: 10,
261
+ altitude: null,
262
+ altitudeAccuracy: null,
263
+ heading: null,
264
+ speed: null,
265
+ toJSON: () => ({}),
266
+ },
267
+ timestamp: Date.now(),
268
+ toJSON: () => ({}),
269
+ } as GeolocationPosition);
270
+ },
271
+ },
272
+ });
273
+ }
274
+
307
275
  function jsonResponse(body: unknown): Pick<Response, 'ok' | 'json'> {
308
276
  return {
309
277
  ok: true,