@africanies/angular-web-sdk 0.1.5 → 0.1.7

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 (142) hide show
  1. package/.nx/cache/{13524438527920092277 → 13466509747676776487}/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +140 -52
  2. package/.nx/cache/13466509747676776487/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +1 -0
  3. package/.nx/cache/{13524438527920092277 → 13466509747676776487}/dist/libs/africanies-core/package.json +3 -3
  4. package/.nx/cache/{13524438527920092277 → 13466509747676776487}/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +14 -11
  5. package/.nx/cache/{18132311043100376919 → 14995846848874973576}/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +539 -232
  6. package/.nx/cache/14995846848874973576/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -0
  7. package/.nx/cache/{18132311043100376919 → 14995846848874973576}/dist/libs/africanies-ui/package.json +5 -5
  8. package/.nx/cache/{5741611231803799383 → 14995846848874973576}/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +111 -28
  9. package/.nx/cache/{1275815545780424169 → 6673686859704879504}/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +16 -0
  10. package/.nx/cache/6673686859704879504/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -0
  11. package/.nx/cache/{14153928176747262656 → 6673686859704879504}/dist/libs/africanies-theme/package.json +3 -3
  12. package/.nx/cache/{1275815545780424169 → 6673686859704879504}/dist/libs/africanies-theme/tailwind-preset.cjs +31 -14
  13. package/.nx/cache/{14153928176747262656 → 6673686859704879504}/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +18 -1
  14. package/.nx/cache/run.json +18 -18
  15. package/.nx/cache/terminalOutputs/{4011892767661954819 → 13466509747676776487} +1 -1
  16. package/.nx/cache/terminalOutputs/1446890025183346611 +32 -0
  17. package/.nx/cache/terminalOutputs/{3064561615116090625 → 14995846848874973576} +7 -7
  18. package/.nx/cache/terminalOutputs/6337357866566791975 +6 -0
  19. package/.nx/cache/terminalOutputs/{1275815545780424169 → 6673686859704879504} +1 -1
  20. package/.nx/cache/terminalOutputs/{12148417737220755248 → 8032148447904822353} +182 -174
  21. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db +0 -0
  22. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-shm +0 -0
  23. package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-wal +0 -0
  24. package/.nx/workspace-data/d/daemon.log +2461 -0
  25. package/.nx/workspace-data/d/server-process.json +2 -2
  26. package/.nx/workspace-data/file-map.json +2747 -2747
  27. package/.nx/workspace-data/nx_files.nxt +0 -0
  28. package/.nx/workspace-data/project-graph.json +2 -2
  29. package/apps/playground/src/app/pages/button-page.ts +5 -3
  30. package/apps/playground/src/app/pages/feedback-page.ts +1 -1
  31. package/apps/playground/src/app/pages/home-page.ts +1 -1
  32. package/apps/playground/src/app/pages/icons-page.ts +3 -3
  33. package/apps/playground/src/app/pages/lecture-page.ts +3 -3
  34. package/apps/playground/src/app/pages/models-page.ts +1 -1
  35. package/apps/playground/src/app/pages/overlay-demos.ts +1 -1
  36. package/apps/playground/src/app/pages/tokens-page.ts +17 -3
  37. package/apps/playground/src/app/pages/usecases/onboarding-login.page.ts +1 -1
  38. package/apps/playground/src/app/pages/usecases/onboarding-reset-password.page.ts +1 -1
  39. package/apps/playground/src/app/pages/usecases/shipment-detail.page.ts +1 -1
  40. package/apps/playground/src/app/pages/viewport-preview.component.ts +1 -1
  41. package/apps/playground/src/app/snippets/button.snippets.ts +4 -3
  42. package/apps/playground/src/index.html +6 -0
  43. package/apps/playground/src/styles.css +4 -5
  44. package/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +131 -8
  45. package/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +1 -1
  46. package/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +8 -6
  47. package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +12 -2
  48. package/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +1 -1
  49. package/dist/libs/africanies-theme/tailwind-preset.cjs +16 -7
  50. package/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +11 -3
  51. package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +83 -63
  52. package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -1
  53. package/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +11 -8
  54. package/libs/africanies-core/src/index.ts +1 -0
  55. package/libs/africanies-core/src/lib/notification/index.ts +1 -1
  56. package/libs/africanies-core/src/lib/notification/notification-link.spec.ts +40 -7
  57. package/libs/africanies-core/src/lib/notification/notification-link.ts +143 -7
  58. package/libs/africanies-theme/src/lib/mode-color.safelist.ts +8 -2
  59. package/libs/africanies-theme/src/lib/mode-color.service.ts +16 -0
  60. package/libs/africanies-theme/tailwind-preset.cjs +16 -7
  61. package/libs/africanies-ui/src/lib/button/button.component.ts +5 -1
  62. package/libs/africanies-ui/src/lib/feedback/empty-state.component.ts +1 -1
  63. package/libs/africanies-ui/src/lib/feedback/error-state.component.ts +1 -1
  64. package/libs/africanies-ui/src/lib/forms/file-upload/file-preview.dialog.ts +1 -1
  65. package/libs/africanies-ui/src/lib/forms/file-upload/file-upload.component.ts +4 -4
  66. package/libs/africanies-ui/src/lib/forms/form-field.classes.ts +2 -2
  67. package/libs/africanies-ui/src/lib/forms/select/select.component.ts +1 -1
  68. package/libs/africanies-ui/src/lib/layout/app-shell-header.component.ts +1 -1
  69. package/libs/africanies-ui/src/lib/layout/app-shell.component.ts +2 -2
  70. package/libs/africanies-ui/src/lib/navigation/shipping-mode-switch/shipping-mode-switch.component.ts +2 -2
  71. package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.html +2 -2
  72. package/libs/africanies-ui/src/lib/navigation/side-nav/africanies-side-nav.component.ts +10 -6
  73. package/libs/africanies-ui/src/lib/notifications/notification-drawer.panel.ts +21 -8
  74. package/libs/africanies-ui/src/lib/overlay/drawer.service.ts +1 -1
  75. package/libs/africanies-ui/src/lib/overlay/modal.service.ts +1 -1
  76. package/libs/africanies-ui/src/lib/pagination/pagination.component.ts +3 -3
  77. package/libs/africanies-ui/src/lib/table/table.component.ts +5 -5
  78. package/libs/africanies-ui/src/lib/toast/toast-host.component.ts +4 -4
  79. package/libs/africanies-ui/src/lib/toast/toast-item.component.ts +1 -1
  80. package/package.json +1 -1
  81. package/.nx/cache/1275815545780424169/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +0 -1
  82. package/.nx/cache/1275815545780424169/dist/libs/africanies-theme/package.json +0 -36
  83. package/.nx/cache/1275815545780424169/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +0 -157
  84. package/.nx/cache/13524438527920092277/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +0 -1
  85. package/.nx/cache/14153928176747262656/dist/libs/africanies-theme/README.md +0 -46
  86. package/.nx/cache/14153928176747262656/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +0 -215
  87. package/.nx/cache/14153928176747262656/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +0 -1
  88. package/.nx/cache/14153928176747262656/dist/libs/africanies-theme/tailwind-preset.cjs +0 -245
  89. package/.nx/cache/18132311043100376919/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +0 -1
  90. package/.nx/cache/18132311043100376919/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +0 -5410
  91. package/.nx/cache/2782605790118376385/dist/libs/africanies-icons/README.md +0 -69
  92. package/.nx/cache/2782605790118376385/dist/libs/africanies-icons/assets/icons.sprite.svg +0 -1844
  93. package/.nx/cache/2782605790118376385/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs +0 -832
  94. package/.nx/cache/2782605790118376385/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs.map +0 -1
  95. package/.nx/cache/2782605790118376385/dist/libs/africanies-icons/package.json +0 -31
  96. package/.nx/cache/2782605790118376385/dist/libs/africanies-icons/types/africanies-africanies-icons.d.ts +0 -108
  97. package/.nx/cache/3064561615116090625/dist/libs/africanies-storage/README.md +0 -31
  98. package/.nx/cache/3064561615116090625/dist/libs/africanies-storage/fesm2022/africanies-africanies-storage.mjs +0 -243
  99. package/.nx/cache/3064561615116090625/dist/libs/africanies-storage/fesm2022/africanies-africanies-storage.mjs.map +0 -1
  100. package/.nx/cache/3064561615116090625/dist/libs/africanies-storage/package.json +0 -27
  101. package/.nx/cache/3064561615116090625/dist/libs/africanies-storage/types/africanies-africanies-storage.d.ts +0 -210
  102. package/.nx/cache/4011892767661954819/dist/libs/africanies-core/README.md +0 -85
  103. package/.nx/cache/4011892767661954819/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +0 -4546
  104. package/.nx/cache/4011892767661954819/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +0 -1
  105. package/.nx/cache/4011892767661954819/dist/libs/africanies-core/package.json +0 -32
  106. package/.nx/cache/4011892767661954819/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +0 -2740
  107. package/.nx/cache/5741611231803799383/dist/libs/africanies-ui/README.md +0 -68
  108. package/.nx/cache/5741611231803799383/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
  109. package/.nx/cache/5741611231803799383/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
  110. package/.nx/cache/5741611231803799383/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +0 -5
  111. package/.nx/cache/5741611231803799383/dist/libs/africanies-ui/assets/carriers/dhl.svg +0 -7
  112. package/.nx/cache/5741611231803799383/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +0 -19088
  113. package/.nx/cache/5741611231803799383/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +0 -1
  114. package/.nx/cache/5741611231803799383/dist/libs/africanies-ui/package.json +0 -36
  115. package/.nx/cache/6372470950255467653/dist/libs/africanies-models/README.md +0 -60
  116. package/.nx/cache/6372470950255467653/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs +0 -599
  117. package/.nx/cache/6372470950255467653/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs.map +0 -1
  118. package/.nx/cache/6372470950255467653/dist/libs/africanies-models/package.json +0 -24
  119. package/.nx/cache/6372470950255467653/dist/libs/africanies-models/types/africanies-africanies-models.d.ts +0 -1452
  120. package/.nx/cache/terminalOutputs/11768036696192889608 +0 -8
  121. package/.nx/cache/terminalOutputs/1227201614544602906 +0 -23
  122. package/.nx/cache/terminalOutputs/12538607787148205787 +0 -19
  123. package/.nx/cache/terminalOutputs/13524438527920092277 +0 -196
  124. package/.nx/cache/terminalOutputs/14153928176747262656 +0 -45
  125. package/.nx/cache/terminalOutputs/17404566492103808557 +0 -8
  126. package/.nx/cache/terminalOutputs/18132311043100376919 +0 -315
  127. package/.nx/cache/terminalOutputs/2436781291243085315 +0 -8
  128. package/.nx/cache/terminalOutputs/2782605790118376385 +0 -21
  129. package/.nx/cache/terminalOutputs/3681975492957101833 +0 -8
  130. package/.nx/cache/terminalOutputs/5741611231803799383 +0 -21
  131. package/.nx/cache/terminalOutputs/6037166070823946880 +0 -8
  132. package/.nx/cache/terminalOutputs/6372470950255467653 +0 -21
  133. package/.nx/cache/terminalOutputs/665190206482526466 +0 -175
  134. package/.nx/cache/terminalOutputs/7756595539965457927 +0 -23
  135. package/.nx/cache/terminalOutputs/9200013119269230075 +0 -8
  136. /package/.nx/cache/{13524438527920092277 → 13466509747676776487}/dist/libs/africanies-core/README.md +0 -0
  137. /package/.nx/cache/{18132311043100376919 → 14995846848874973576}/dist/libs/africanies-ui/README.md +0 -0
  138. /package/.nx/cache/{18132311043100376919 → 14995846848874973576}/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
  139. /package/.nx/cache/{18132311043100376919 → 14995846848874973576}/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
  140. /package/.nx/cache/{18132311043100376919 → 14995846848874973576}/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +0 -0
  141. /package/.nx/cache/{18132311043100376919 → 14995846848874973576}/dist/libs/africanies-ui/assets/carriers/dhl.svg +0 -0
  142. /package/.nx/cache/{1275815545780424169 → 6673686859704879504}/dist/libs/africanies-theme/README.md +0 -0
@@ -17,13 +17,14 @@ import { IsActiveMatchOptions, ActivatedRouteSnapshot, Router, UrlTree } from '@
17
17
  * Visual emphasis for {@link ButtonComponent}.
18
18
  *
19
19
  * - `primary` — filled mode accent (SFN green / STN orange) for the main action
20
- * - `secondary` — outlined chrome for secondary actions
20
+ * - `secondary` — stroked mode accent (white + mode border/text; portal `.btn-stroked`)
21
+ * - `flat` — neutral chrome (white + ink + soft border; portal `.btn-flat`)
21
22
  * - `ghost` — neutral text-only for low-emphasis actions (Cancel, tertiary)
22
23
  * - `ghost-primary` — text-only mode accent (SFN green / STN orange)
23
24
  * - `ghost-danger` — text-only destructive actions (Clear, Remove)
24
25
  * - `danger` — destructive confirmations and deletes
25
26
  */
26
- type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'ghost-primary' | 'ghost-danger' | 'underline' | 'danger';
27
+ type ButtonVariant = 'primary' | 'secondary' | 'flat' | 'ghost' | 'ghost-primary' | 'ghost-danger' | 'underline' | 'danger';
27
28
  /**
28
29
  * Control size for {@link ButtonComponent}.
29
30
  */
@@ -3590,9 +3591,10 @@ interface AfricaniesSideNavItem {
3590
3591
  /**
3591
3592
  * App shell side navigation with icons, nested items, and a collapsible rail.
3592
3593
  *
3593
- * **Design - ink spine:** a continuous mode-accent edge with soft active
3594
- * highlights. Collapsed mode keeps icons; hover opens a floating label blade
3595
- * (name + children) instead of a plain tooltip.
3594
+ * **Design - ink spine:** a continuous mode-accent edge; active leaves use
3595
+ * primary fill + white text (portal parity); expanded parents of an active
3596
+ * child use primary text on white. Collapsed mode keeps icons; hover opens a
3597
+ * floating label blade (name + children) instead of a plain tooltip.
3596
3598
  *
3597
3599
  * ## Parent items with children
3598
3600
  * **Expanded rail:** branches start open by default (`expandBranchesByDefault`).
@@ -3923,7 +3925,7 @@ declare class ShippingModeSwitchComponent {
3923
3925
  /** Active mode from {@link ShippingModeService}. */
3924
3926
  protected readonly mode: _angular_core.Signal<ShippingMode>;
3925
3927
  /** Host padding / divider — tighter when the rail is collapsed. */
3926
- protected readonly hostClass: _angular_core.Signal<"block shrink-0 border-t border-border px-1.5 py-3 dark:border-white/10" | "block shrink-0 border-t border-border px-2 py-3 dark:border-white/10">;
3928
+ protected readonly hostClass: _angular_core.Signal<"block shrink-0 border-t border-strong px-1.5 py-3 dark:border-white/10" | "block shrink-0 border-t border-strong px-2 py-3 dark:border-white/10">;
3927
3929
  /**
3928
3930
  * Requests a mode change through {@link ShippingModeService.requestModeChange}
3929
3931
  * so feature guards (e.g. Create Shipment confirm) can run first.
@@ -4122,6 +4124,7 @@ declare class NotificationDrawerPanel {
4122
4124
  private observer;
4123
4125
  protected readonly viewLinkClass: _angular_core.Signal<"inline-flex cursor-pointer items-center gap-1 self-start text-caption font-semibold underline underline-offset-[3px] transition-colors text-import decoration-import/35 hover:decoration-import dark:text-import-light dark:decoration-import-light/40 dark:hover:decoration-import-light" | "inline-flex cursor-pointer items-center gap-1 self-start text-caption font-semibold underline underline-offset-[3px] transition-colors text-export decoration-export/35 hover:decoration-export dark:text-export-light dark:decoration-export-light/40 dark:hover:decoration-export-light">;
4124
4126
  protected readonly title: _angular_core.Signal<string>;
4127
+ private readonly seededItems;
4125
4128
  protected readonly items: _angular_core.WritableSignal<AfricaniesNotification[]>;
4126
4129
  protected readonly markingId: _angular_core.WritableSignal<string | null>;
4127
4130
  protected readonly markingAll: _angular_core.WritableSignal<boolean>;
@@ -4291,7 +4294,7 @@ declare class AppShellComponent {
4291
4294
  protected readonly showContentChrome: _angular_core.Signal<boolean>;
4292
4295
  /** Full height in preview embeds so absolute drawers stay inside the frame. */
4293
4296
  protected readonly hostClass: _angular_core.Signal<"block h-full" | "block min-h-full">;
4294
- protected readonly shellRootClass: _angular_core.Signal<"relative flex h-full min-h-0" | "relative flex min-h-full">;
4297
+ protected readonly shellRootClass: _angular_core.Signal<"relative flex h-full min-h-0 bg-background-chrome dark:bg-ink-950" | "relative flex min-h-full bg-background-chrome dark:bg-ink-950">;
4295
4298
  protected readonly headerDensity: _angular_core.Signal<"desktop" | "mobile" | "tablet">;
4296
4299
  protected readonly showMobileBar: _angular_core.Signal<boolean>;
4297
4300
  protected readonly sidenavHostClass: _angular_core.Signal<"z-50 shrink-0 self-start sticky top-0 block h-full" | "z-50 shrink-0 self-start absolute inset-y-0 left-0 h-full shadow-xl" | "hidden" | "z-50 shrink-0 self-start h-dvh fixed inset-y-0 left-0 shadow-xl lg:sticky lg:top-0 lg:block" | "z-50 shrink-0 self-start h-dvh hidden lg:sticky lg:top-0 lg:block">;
@@ -4976,7 +4979,7 @@ declare class TableComponent<T = unknown> {
4976
4979
  protected readonly colSpan: _angular_core.Signal<number>;
4977
4980
  protected readonly errorMessage: _angular_core.Signal<string>;
4978
4981
  protected readonly staleError: _angular_core.Signal<string | null>;
4979
- protected readonly bodyKind: _angular_core.Signal<"loading" | "error" | "empty" | "rows">;
4982
+ protected readonly bodyKind: _angular_core.Signal<"error" | "loading" | "empty" | "rows">;
4980
4983
  protected readonly paginationLoading: _angular_core.Signal<boolean>;
4981
4984
  /** Top-left Refresh — only when rows are on screen (empty/error use in-body Retry). */
4982
4985
  protected readonly showToolbarRefresh: _angular_core.Signal<boolean>;
@@ -212,6 +212,7 @@ export {
212
212
  export {
213
213
  mapNotification,
214
214
  mapNotificationInboxItem,
215
+ isNotificationExternalLink,
215
216
  mapNotificationList,
216
217
  mapNotificationPayload,
217
218
  NOTIFICATION_READ_PATH,
@@ -7,4 +7,4 @@ export {
7
7
  NOTIFICATION_UPDATE_PATH,
8
8
  } from './notification.mapper';
9
9
  export { NotificationService } from './notification.service';
10
- export { resolveNotificationLinkForMode } from './notification-link';
10
+ export { isNotificationExternalLink, resolveNotificationLinkForMode } from './notification-link';
@@ -1,25 +1,58 @@
1
- import { resolveNotificationLinkForMode } from './notification-link';
1
+ import {
2
+ isNotificationExternalLink,
3
+ resolveNotificationLinkForMode,
4
+ } from './notification-link';
2
5
 
3
6
  describe('resolveNotificationLinkForMode', () => {
4
7
  const exportLink =
5
8
  'https://test-admin-export.africaniestest.com/portal/customer/deliveries?page=1&shipment_app=shipfromnaija';
6
9
 
7
- it('rewrites export links for STN import mode', () => {
10
+ it('preserves shipment_app and strips legacy host to a relative portal path', () => {
8
11
  expect(resolveNotificationLinkForMode(exportLink, 'stn')).toBe(
9
- 'https://test-admin-import.africaniestest.com/portal/customer/deliveries?page=1&shipment_app=shiptonaija',
12
+ '/portal/customer/deliveries?page=1&shipment_app=shipfromnaija',
10
13
  );
11
14
  });
12
15
 
13
- it('rewrites import links for SFN export mode', () => {
16
+ it('preserves shipment_app on import portal links', () => {
14
17
  const importLink =
15
18
  'https://test-customer-import.africaniestest.com/portal/shipment/track-shipment/items/90?shipment_app=shiptonaija';
16
19
 
17
20
  expect(resolveNotificationLinkForMode(importLink, 'sfn')).toBe(
18
- 'https://test-customer-export.africaniestest.com/portal/shipment/track-shipment/items/90?shipment_app=shipfromnaija',
21
+ '/portal/shipment/track-shipment/items/90?shipment_app=shiptonaija',
22
+ );
23
+ });
24
+
25
+ it('leaves relative portal links with shipment_app unchanged', () => {
26
+ expect(resolveNotificationLinkForMode(exportLink, 'sfn')).toBe(
27
+ '/portal/customer/deliveries?page=1&shipment_app=shipfromnaija',
28
+ );
29
+ });
30
+
31
+ it('strips shipment_app from S3 export links', () => {
32
+ const url =
33
+ 'https://africanies-staging-test.s3.amazonaws.com/var/www/html/storage/tmp/payouts.xlsx?X-Amz-Signature=abc&shipment_app=shipfromnaija';
34
+
35
+ expect(resolveNotificationLinkForMode(url, 'sfn')).toBe(
36
+ 'https://africanies-staging-test.s3.amazonaws.com/var/www/html/storage/tmp/payouts.xlsx?X-Amz-Signature=abc',
19
37
  );
20
38
  });
21
39
 
22
- it('leaves SFN export links unchanged when mode is SFN', () => {
23
- expect(resolveNotificationLinkForMode(exportLink, 'sfn')).toBe(exportLink);
40
+ it('adds shipment_app when missing on absolute portal URLs', () => {
41
+ const link =
42
+ 'https://test-admin-export.africaniestest.com/portal/customer/deliveries?page=1';
43
+
44
+ expect(resolveNotificationLinkForMode(link, 'stn')).toBe(
45
+ 'https://test-admin-import.africaniestest.com/portal/customer/deliveries?page=1&shipment_app=shiptonaija',
46
+ );
47
+ });
48
+ });
49
+
50
+ describe('isNotificationExternalLink', () => {
51
+ it('treats S3 spreadsheet exports as external', () => {
52
+ expect(
53
+ isNotificationExternalLink(
54
+ 'https://africanies-staging-test.s3.amazonaws.com/tmp/export.xlsx?X-Amz-Signature=abc',
55
+ ),
56
+ ).toBe(true);
24
57
  });
25
58
  });
@@ -7,26 +7,68 @@ const SHIPMENT_APP_BY_MODE: Record<ShippingMode, string> = {
7
7
  sfn: 'shipfromnaija',
8
8
  };
9
9
 
10
+ const QUERY_SHIPMENT_APP = 'shipment_app';
11
+
12
+ const LEGACY_PORTAL_HOST =
13
+ /^(?:test-)?(?:admin|customer)-(?:export|import)\.africaniestest\.com$/i;
14
+
15
+ /** Signed export downloads and other hosts that must not be rewritten in-app. */
16
+ export function isNotificationExternalLink(link: string): boolean {
17
+ const trimmed = decodeNotificationLink(link).trim();
18
+ if (!trimmed) {
19
+ return false;
20
+ }
21
+ try {
22
+ const url = new URL(trimmed);
23
+ if (/\.(xlsx|xls|csv)(\?|$)/i.test(url.pathname)) {
24
+ return true;
25
+ }
26
+ if (/\.amazonaws\.com$/i.test(url.hostname)) {
27
+ return true;
28
+ }
29
+ return !isLegacyPortalHost(url.hostname);
30
+ } catch {
31
+ return false;
32
+ }
33
+ }
34
+
10
35
  /**
11
- * Rewrite a notification portal URL for the active {@link ShippingMode}.
36
+ * Rewrite a notification href for the active {@link ShippingMode}.
12
37
  *
13
- * Swaps `-export` / `-import` host segments and sets `shipment_app` to
14
- * `shipfromnaija` (SFN) or `shiptonaija` (STN).
15
- * @param link
16
- * @param mode
38
+ * - Export / download URLs (S3, spreadsheets) pass through unchanged (no `shipment_app`).
39
+ * - Legacy portal hosts become relative `/portal/...` paths with query preserved.
40
+ * - When the notification already includes `shipment_app`, it is kept as-is.
41
+ * - Otherwise classic host / `shipment_app` rewriting applies for portal URLs.
17
42
  */
18
43
  export function resolveNotificationLinkForMode(
19
44
  link: string | null | undefined,
20
45
  mode: ShippingMode,
21
46
  ): string {
22
- const trimmed = asString(link).trim();
47
+ const trimmed = decodeNotificationLink(asString(link)).trim();
23
48
  if (!trimmed) {
24
49
  return trimmed;
25
50
  }
26
51
 
52
+ if (isNotificationExternalLink(trimmed)) {
53
+ return stripShipmentAppFromLink(trimmed);
54
+ }
55
+
56
+ const relativePortal = toRelativePortalNotificationLink(trimmed);
57
+ if (relativePortal && hasShipmentAppQuery(relativePortal)) {
58
+ return relativePortal;
59
+ }
60
+
61
+ const relative = normalizeRelativePortalLink(trimmed);
62
+ if (relative && hasShipmentAppQuery(relative)) {
63
+ return relative;
64
+ }
65
+
27
66
  try {
28
67
  const url = new URL(trimmed);
29
- url.searchParams.set('shipment_app', SHIPMENT_APP_BY_MODE[mode]);
68
+ if (url.searchParams.has(QUERY_SHIPMENT_APP)) {
69
+ return trimmed;
70
+ }
71
+ url.searchParams.set(QUERY_SHIPMENT_APP, SHIPMENT_APP_BY_MODE[mode]);
30
72
  url.hostname = rewriteNotificationHostForMode(url.hostname, mode);
31
73
  return url.toString();
32
74
  } catch {
@@ -48,6 +90,10 @@ function rewriteNotificationLinkFallback(
48
90
  link: string,
49
91
  mode: ShippingMode,
50
92
  ): string {
93
+ if (hasShipmentAppQuery(link)) {
94
+ return link;
95
+ }
96
+
51
97
  let out = link.replace(
52
98
  /shipment_app=(?:shipfromnaija|shiptonaija)/gi,
53
99
  `shipment_app=${SHIPMENT_APP_BY_MODE[mode]}`,
@@ -63,3 +109,93 @@ function rewriteNotificationLinkFallback(
63
109
  }
64
110
  return out.replace(/-import/gi, '-export');
65
111
  }
112
+
113
+ function toRelativePortalNotificationLink(link: string): string | null {
114
+ try {
115
+ const url = new URL(link);
116
+ if (!isLegacyPortalHost(url.hostname)) {
117
+ return null;
118
+ }
119
+
120
+ let path = url.pathname.replace(/^\/+/, '');
121
+ if (path.startsWith('ng/portal/')) {
122
+ path = path.slice('ng/'.length);
123
+ }
124
+ if (!path.startsWith('portal/') && path !== 'portal') {
125
+ return null;
126
+ }
127
+
128
+ const query = url.searchParams.toString();
129
+ return `/${path}${query ? `?${query}` : ''}`;
130
+ } catch {
131
+ return null;
132
+ }
133
+ }
134
+
135
+ function normalizeRelativePortalLink(link: string): string | null {
136
+ let path = link.split('?')[0]?.split('#')[0] ?? '';
137
+ const query = link.includes('?') ? link.slice(link.indexOf('?')) : '';
138
+ path = path.replace(/^\/+/, '');
139
+ if (path.startsWith('ng/portal/')) {
140
+ path = path.slice('ng/'.length);
141
+ }
142
+ if (!path.startsWith('portal/') && path !== 'portal') {
143
+ return null;
144
+ }
145
+ return `/${path}${query}`;
146
+ }
147
+
148
+ function stripShipmentAppFromLink(link: string): string {
149
+ const trimmed = link.trim();
150
+ if (!trimmed) {
151
+ return trimmed;
152
+ }
153
+ try {
154
+ const url = new URL(trimmed);
155
+ if (!url.searchParams.has(QUERY_SHIPMENT_APP)) {
156
+ return trimmed;
157
+ }
158
+ url.searchParams.delete(QUERY_SHIPMENT_APP);
159
+ return url.toString();
160
+ } catch {
161
+ const params = new URLSearchParams(
162
+ trimmed.includes('?') ? trimmed.slice(trimmed.indexOf('?') + 1) : '',
163
+ );
164
+ if (!params.has(QUERY_SHIPMENT_APP)) {
165
+ return trimmed;
166
+ }
167
+ params.delete(QUERY_SHIPMENT_APP);
168
+ const query = params.toString();
169
+ const base = trimmed.split('?')[0] ?? trimmed;
170
+ return query ? `${base}?${query}` : base;
171
+ }
172
+ }
173
+
174
+ function hasShipmentAppQuery(link: string): boolean {
175
+ try {
176
+ const query = link.includes('?') ? link.slice(link.indexOf('?') + 1) : '';
177
+ return new URLSearchParams(query).has(QUERY_SHIPMENT_APP);
178
+ } catch {
179
+ return /(?:^|[?&])shipment_app=/i.test(link);
180
+ }
181
+ }
182
+
183
+ function decodeNotificationLink(link: string): string {
184
+ let value = (link ?? '').trim();
185
+ if (!value.includes('%')) {
186
+ return value;
187
+ }
188
+ try {
189
+ const decoded = decodeURIComponent(value);
190
+ if (decoded !== value) {
191
+ value = decoded;
192
+ }
193
+ } catch {
194
+ // Keep original when not valid URI encoding.
195
+ }
196
+ return value;
197
+ }
198
+
199
+ function isLegacyPortalHost(hostname: string): boolean {
200
+ return LEGACY_PORTAL_HOST.test(hostname);
201
+ }
@@ -11,12 +11,12 @@
11
11
  * SFN (export / green):
12
12
  * text-export bg-export bg-export-subtle bg-export-light border-export
13
13
  * hover:bg-export-light hover:bg-export-subtle dark:bg-export/15 dark:hover:bg-export/15
14
- * dark:bg-[color-mix(in_srgb,#1cbd5d_15%,#212529)]
14
+ * dark:bg-[color-mix(in_srgb,#1cbd5d_15%,#212529)] dark:bg-export dark:text-export dark:border-export
15
15
  *
16
16
  * STN (import / orange):
17
17
  * text-import bg-import bg-import-subtle bg-import-light border-import
18
18
  * hover:bg-import-light hover:bg-import-subtle dark:bg-import/15 dark:hover:bg-import/15
19
- * dark:bg-[color-mix(in_srgb,#f08829_15%,#212529)]
19
+ * dark:bg-[color-mix(in_srgb,#f08829_15%,#212529)] dark:bg-import dark:text-import dark:border-import
20
20
  */
21
21
  export const MODE_COLOR_SAFELIST = [
22
22
  'text-export',
@@ -29,6 +29,9 @@ export const MODE_COLOR_SAFELIST = [
29
29
  'dark:bg-export/15',
30
30
  'dark:hover:bg-export/15',
31
31
  'dark:bg-[color-mix(in_srgb,#1cbd5d_15%,#212529)]',
32
+ 'dark:bg-export',
33
+ 'dark:text-export',
34
+ 'dark:border-export',
32
35
  'text-import',
33
36
  'bg-import',
34
37
  'bg-import-subtle',
@@ -39,4 +42,7 @@ export const MODE_COLOR_SAFELIST = [
39
42
  'dark:bg-import/15',
40
43
  'dark:hover:bg-import/15',
41
44
  'dark:bg-[color-mix(in_srgb,#f08829_15%,#212529)]',
45
+ 'dark:bg-import',
46
+ 'dark:text-import',
47
+ 'dark:border-import',
42
48
  ] as const;
@@ -42,6 +42,14 @@ export interface ModeColorClasses {
42
42
  * Soft hover highlight for menus and lists.
43
43
  */
44
44
  softHover: string;
45
+ /**
46
+ * Active nav leaf / collapsed rail — solid mode fill + white text.
47
+ */
48
+ activeFill: string;
49
+ /**
50
+ * Stroked secondary CTA — white fill, mode border + text (portal `.btn-stroked`).
51
+ */
52
+ stroked: string;
45
53
  }
46
54
 
47
55
  /**
@@ -84,6 +92,10 @@ export class ModeColorService {
84
92
  softSolid:
85
93
  'bg-export-subtle dark:bg-[color-mix(in_srgb,#1cbd5d_15%,#212529)]',
86
94
  softHover: 'hover:bg-export-subtle dark:hover:bg-export/15',
95
+ activeFill:
96
+ 'bg-export text-white border-transparent dark:bg-export dark:text-white',
97
+ stroked:
98
+ 'bg-white text-export border-export hover:bg-export-subtle dark:bg-ink-950 dark:text-export dark:border-export dark:hover:bg-export/15',
87
99
  };
88
100
  }
89
101
  return {
@@ -99,6 +111,10 @@ export class ModeColorService {
99
111
  softSolid:
100
112
  'bg-import-subtle dark:bg-[color-mix(in_srgb,#f08829_15%,#212529)]',
101
113
  softHover: 'hover:bg-import-subtle dark:hover:bg-import/15',
114
+ activeFill:
115
+ 'bg-import text-white border-transparent dark:bg-import dark:text-white',
116
+ stroked:
117
+ 'bg-white text-import border-import hover:bg-import-subtle dark:bg-ink-950 dark:text-import dark:border-import dark:hover:bg-import/15',
102
118
  };
103
119
  });
104
120
  }
@@ -92,8 +92,8 @@ function africaniesAutofillPlugin({ addBase }) {
92
92
  const rules = {};
93
93
  for (const selector of autofillSelectors) {
94
94
  rules[selector] = {
95
- WebkitTextFillColor: '#212529',
96
- caretColor: '#212529',
95
+ WebkitTextFillColor: '#1c2b3f',
96
+ caretColor: '#1c2b3f',
97
97
  boxShadow: '0 0 0 1000px #ffffff inset',
98
98
  transition: 'background-color 99999s ease-in-out 0s',
99
99
  };
@@ -194,9 +194,12 @@ module.exports = {
194
194
  black: '#000000',
195
195
  white: '#ffffff',
196
196
  ink: {
197
- DEFAULT: '#212529',
197
+ // Portal `.txt-primary` navy — primary body / heading text (light).
198
+ DEFAULT: '#1c2b3f',
198
199
  blue: '#192a3e',
199
200
  brand: '#1c2b3f',
201
+ // Previous ink DEFAULT — dark elevated cards / panels (`dark:bg-ink-surface`).
202
+ surface: '#212529',
200
203
  950: '#272729',
201
204
  },
202
205
  neutral: {
@@ -205,11 +208,17 @@ module.exports = {
205
208
  600: '#667185',
206
209
  },
207
210
  border: {
208
- // Light chrome on white match form field borders (`neutral-300`).
209
- // Previous `#f0f2f5` was too close to white and washed out.
210
- DEFAULT: '#c9d5e1',
211
+ // Soft hairline on white (portal `$border-bottom-gray`).
212
+ DEFAULT: '#f0f2f5',
213
+ // Nav pins / stronger chrome dividers.
214
+ strong: '#e0e6f0',
211
215
  },
212
216
  background: {
217
+ // Portal page shell (`container-wrap`).
218
+ chrome: '#f6f7f9',
219
+ // Nested wells inside white cards.
220
+ well: '#f3f3f3',
221
+ // Auth / welcome / soft hover.
213
222
  welcome: '#f9fafb',
214
223
  },
215
224
  export: {
@@ -236,7 +245,7 @@ module.exports = {
236
245
  },
237
246
  },
238
247
  fontFamily: {
239
- sans: ['Arial', 'sans-serif'],
248
+ sans: ['Inter', 'system-ui', 'sans-serif'],
240
249
  },
241
250
  fontSize: {
242
251
  // PROVISIONAL — see THEME.md; sizes may tighten after design QA.
@@ -14,7 +14,8 @@ import { ModeColorService } from '@africanies/africanies-theme';
14
14
  * Visual emphasis for {@link ButtonComponent}.
15
15
  *
16
16
  * - `primary` — filled mode accent (SFN green / STN orange) for the main action
17
- * - `secondary` — outlined chrome for secondary actions
17
+ * - `secondary` — stroked mode accent (white + mode border/text; portal `.btn-stroked`)
18
+ * - `flat` — neutral chrome (white + ink + soft border; portal `.btn-flat`)
18
19
  * - `ghost` — neutral text-only for low-emphasis actions (Cancel, tertiary)
19
20
  * - `ghost-primary` — text-only mode accent (SFN green / STN orange)
20
21
  * - `ghost-danger` — text-only destructive actions (Clear, Remove)
@@ -23,6 +24,7 @@ import { ModeColorService } from '@africanies/africanies-theme';
23
24
  export type ButtonVariant =
24
25
  | 'primary'
25
26
  | 'secondary'
27
+ | 'flat'
26
28
  | 'ghost'
27
29
  | 'ghost-primary'
28
30
  | 'ghost-danger'
@@ -167,6 +169,8 @@ export class ButtonComponent {
167
169
  } else if (variant === 'ghost-primary') {
168
170
  variantClass = this.modeColor.classes().ghostPrimary;
169
171
  } else if (variant === 'secondary') {
172
+ variantClass = this.modeColor.classes().stroked;
173
+ } else if (variant === 'flat') {
170
174
  variantClass =
171
175
  'bg-white dark:bg-ink-950 text-ink dark:text-white border-border dark:border-white/15 hover:bg-background-welcome dark:hover:bg-white/10';
172
176
  } else if (variant === 'ghost') {
@@ -40,7 +40,7 @@ import { ButtonComponent } from '../button/button.component';
40
40
  <button
41
41
  africanies-button
42
42
  type="button"
43
- variant="secondary"
43
+ variant="flat"
44
44
  [disabled]="refreshing()"
45
45
  (click)="retry.emit()"
46
46
  >
@@ -40,7 +40,7 @@ import { ButtonComponent } from '../button/button.component';
40
40
  <button
41
41
  africanies-button
42
42
  type="button"
43
- variant="secondary"
43
+ variant="flat"
44
44
  [disabled]="refreshing()"
45
45
  (click)="retry.emit()"
46
46
  >
@@ -77,7 +77,7 @@ type PreviewKind = 'image' | 'unavailable';
77
77
  <img
78
78
  [src]="src()"
79
79
  [alt]="data.file.name"
80
- class="max-h-[min(70vh,40rem)] w-full rounded-md object-contain bg-background-welcome ring-1 ring-border dark:bg-ink dark:ring-white/15"
80
+ class="max-h-[min(70vh,40rem)] w-full rounded-md object-contain bg-background-welcome ring-1 ring-border dark:bg-ink-950 dark:ring-white/15"
81
81
  />
82
82
  } @else {
83
83
  <div
@@ -125,7 +125,7 @@ let nextFileUploadId = 0;
125
125
  <button
126
126
  africanies-button
127
127
  type="button"
128
- variant="secondary"
128
+ variant="flat"
129
129
  size="sm"
130
130
  [disabled]="disabled()"
131
131
  (click)="openFilePicker()"
@@ -138,7 +138,7 @@ let nextFileUploadId = 0;
138
138
  <button
139
139
  africanies-button
140
140
  type="button"
141
- variant="secondary"
141
+ variant="flat"
142
142
  size="sm"
143
143
  [disabled]="disabled()"
144
144
  (click)="openCamera()"
@@ -193,7 +193,7 @@ let nextFileUploadId = 0;
193
193
  <button
194
194
  africanies-button
195
195
  type="button"
196
- variant="secondary"
196
+ variant="flat"
197
197
  size="sm"
198
198
  [disabled]="disabled()"
199
199
  (click)="openFilePicker()"
@@ -262,7 +262,7 @@ let nextFileUploadId = 0;
262
262
  <button
263
263
  africanies-button
264
264
  type="button"
265
- variant="secondary"
265
+ variant="flat"
266
266
  size="sm"
267
267
  [disabled]="disabled()"
268
268
  (click)="openFilePicker(); $event.stopPropagation()"
@@ -12,11 +12,11 @@ export const FORM_LABEL_CLASS =
12
12
 
13
13
  /** Default control shell — `h-10` matches md `africanies-button`. */
14
14
  export const FORM_FIELD_CLASS =
15
- 'flex items-center w-full h-10 box-border rounded-md border border-neutral-300 bg-white dark:bg-ink-950 text-ink dark:text-white dark:border-white/25 ' +
15
+ 'flex items-center w-full h-10 box-border rounded-md border border-border bg-white dark:bg-ink-950 text-ink dark:text-white dark:border-white/25 ' +
16
16
  'focus-within:outline focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-ink';
17
17
 
18
18
  /**
19
- * Error border — `!` so it wins over shell `border-neutral-300` /
19
+ * Error border — `!` so it wins over shell `border-border` /
20
20
  * `dark:border-white/25` (same specificity; those utilities sort later in the
21
21
  * generated CSS and otherwise paint over `border-danger`).
22
22
  *
@@ -186,7 +186,7 @@ const SELECT_PANEL_POSITIONS: ConnectedPosition[] = [
186
186
  @if (multiple() && selectedList().length) {
187
187
  @for (chip of selectedList(); track trackOption(chip)) {
188
188
  <span
189
- class="inline-flex items-center gap-1 rounded-md border border-neutral-300 bg-background-welcome dark:border-white/20 dark:bg-ink text-body-sm text-ink dark:text-white px-2 py-0.5"
189
+ class="inline-flex items-center gap-1 rounded-md border border-neutral-300 bg-background-welcome dark:border-white/20 dark:bg-ink-950 text-body-sm text-ink dark:text-white px-2 py-0.5"
190
190
  >
191
191
  @if (chip.prefix; as prefixIcon) {
192
192
  <africanies-icon [name]="prefixIcon" [size]="14" class="shrink-0" />
@@ -349,7 +349,7 @@ export class AppShellHeaderComponent {
349
349
  this.notificationsDrawer
350
350
  .open({
351
351
  title: this.notificationsTitle(),
352
- notifications: onLoadPage ? undefined : this.notifications(),
352
+ notifications: this.notifications(),
353
353
  onLoadPage,
354
354
  onMarkRead,
355
355
  onMarkAllRead,
@@ -199,8 +199,8 @@ export class AppShellComponent {
199
199
 
200
200
  protected readonly shellRootClass = computed(() =>
201
201
  this.layoutPreview()
202
- ? 'relative flex h-full min-h-0'
203
- : 'relative flex min-h-full',
202
+ ? 'relative flex h-full min-h-0 bg-background-chrome dark:bg-ink-950'
203
+ : 'relative flex min-h-full bg-background-chrome dark:bg-ink-950',
204
204
  );
205
205
 
206
206
  protected readonly headerDensity = computed((): 'mobile' | 'tablet' | 'desktop' => {
@@ -131,8 +131,8 @@ export class ShippingModeSwitchComponent {
131
131
  /** Host padding / divider — tighter when the rail is collapsed. */
132
132
  protected readonly hostClass = computed(() =>
133
133
  this.collapsed()
134
- ? 'block shrink-0 border-t border-border px-1.5 py-3 dark:border-white/10'
135
- : 'block shrink-0 border-t border-border px-2 py-3 dark:border-white/10',
134
+ ? 'block shrink-0 border-t border-strong px-1.5 py-3 dark:border-white/10'
135
+ : 'block shrink-0 border-t border-strong px-2 py-3 dark:border-white/10',
136
136
  );
137
137
 
138
138
  /**
@@ -1,5 +1,5 @@
1
1
  <aside
2
- class="relative flex h-full flex-col overflow-visible border-r border-border bg-white transition-[width] duration-200 ease-out dark:border-white/10 dark:bg-ink"
2
+ class="relative flex h-full flex-col overflow-visible border-r border-border bg-white transition-[width] duration-200 ease-out dark:border-white/10 dark:bg-ink-950"
3
3
  [style.width]="collapsed() ? '3.75rem' : '15rem'"
4
4
  [attr.aria-label]="ariaLabel()"
5
5
  >
@@ -61,7 +61,7 @@
61
61
  @if (!collapsed() && expandParents() && hasOpenBranches()) {
62
62
  <button
63
63
  type="button"
64
- class="absolute right-1.5 top-1.5 z-10 inline-flex size-7 cursor-pointer items-center justify-center rounded-md bg-white text-neutral-500 shadow-sm ring-1 ring-border/80 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:bg-ink dark:text-neutral-400 dark:ring-white/15 dark:hover:bg-white/10 dark:hover:text-white"
64
+ class="absolute right-1.5 top-1.5 z-10 inline-flex size-7 cursor-pointer items-center justify-center rounded-md bg-white text-neutral-500 shadow-sm ring-1 ring-border/80 transition-colors hover:bg-background-welcome hover:text-ink focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink dark:bg-ink-950 dark:text-neutral-400 dark:ring-white/15 dark:hover:bg-white/10 dark:hover:text-white"
65
65
  aria-label="Collapse all"
66
66
  (click)="collapseAllBranches()"
67
67
  >