@africanies/angular-web-sdk 0.1.10 → 0.1.12
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.
- package/.nx/cache/{14995846848874973576 → 14892813664845064572}/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +1634 -1016
- package/.nx/cache/14892813664845064572/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -0
- package/.nx/cache/{14995846848874973576 → 14892813664845064572}/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +261 -137
- package/.nx/cache/{14876618240301288353 → 7821397806208783174}/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +2641 -1011
- package/.nx/cache/7821397806208783174/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -0
- package/.nx/cache/{14876618240301288353 → 7821397806208783174}/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +460 -147
- package/.nx/cache/run.json +16 -16
- package/.nx/cache/terminalOutputs/{14876618240301288353 → 14892813664845064572} +2 -2
- package/.nx/cache/terminalOutputs/16208231681037145050 +85 -0
- package/.nx/cache/terminalOutputs/{1446890025183346611 → 17909810599991457481} +15 -7
- package/.nx/cache/terminalOutputs/{7978448102466861029 → 2927637324428060203} +41 -31
- package/.nx/cache/terminalOutputs/{17394769338046050914 → 4787481949164239892} +2 -2
- package/.nx/cache/terminalOutputs/{14995846848874973576 → 7821397806208783174} +2 -2
- package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db +0 -0
- package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-shm +0 -0
- package/.nx/workspace-data/0091ABA9-5C90-580D-953C-8C94E763ADB5-v3.db-wal +0 -0
- package/.nx/workspace-data/d/daemon.log +3694 -0
- package/.nx/workspace-data/d/server-process.json +2 -2
- package/.nx/workspace-data/file-map.json +1508 -1478
- package/.nx/workspace-data/nx_files.nxt +0 -0
- package/.nx/workspace-data/project-graph.json +2 -2
- package/README.md +0 -17
- package/apps/playground/src/app/pages/forms-page.ts +70 -14
- package/apps/playground/src/app/snippets/forms.snippets.ts +41 -6
- package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs +1149 -142
- package/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +1 -1
- package/dist/libs/africanies-ui/types/africanies-africanies-ui.d.ts +205 -18
- package/libs/africanies-ui/src/index.ts +18 -0
- package/libs/africanies-ui/src/lib/forms/index.ts +20 -0
- package/libs/africanies-ui/src/lib/forms/phone-input/index.ts +21 -0
- package/libs/africanies-ui/src/lib/forms/phone-input/phone-codes.ts +264 -0
- package/libs/africanies-ui/src/lib/forms/phone-input/phone-input.component.ts +477 -0
- package/libs/africanies-ui/src/lib/forms/phone-input/phone-input.types.ts +35 -0
- package/libs/africanies-ui/src/lib/forms/phone-input/phone-input.utils.spec.ts +81 -0
- package/libs/africanies-ui/src/lib/forms/phone-input/phone-input.utils.ts +275 -0
- package/libs/africanies-ui/src/lib/modules/africanies-forms.module.ts +2 -0
- package/libs/africanies-ui/src/lib/navigation/segment/segment.component.ts +2 -1
- package/libs/africanies-ui/src/lib/navigation/shipping-mode-switch/shipping-mode-switch.component.spec.ts +15 -0
- package/libs/africanies-ui/src/lib/navigation/shipping-mode-switch/shipping-mode-switch.component.ts +6 -3
- package/libs/africanies-ui/src/lib/table/table.component.ts +70 -65
- package/package.json +1 -1
- package/.nx/cache/13466509747676776487/dist/libs/africanies-core/README.md +0 -85
- package/.nx/cache/13466509747676776487/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +0 -4634
- package/.nx/cache/13466509747676776487/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +0 -1
- package/.nx/cache/13466509747676776487/dist/libs/africanies-core/package.json +0 -31
- package/.nx/cache/13466509747676776487/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +0 -2743
- package/.nx/cache/14416694322692463866/dist/libs/africanies-models/README.md +0 -60
- package/.nx/cache/14416694322692463866/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs +0 -599
- package/.nx/cache/14416694322692463866/dist/libs/africanies-models/fesm2022/africanies-africanies-models.mjs.map +0 -1
- package/.nx/cache/14416694322692463866/dist/libs/africanies-models/package.json +0 -23
- package/.nx/cache/14416694322692463866/dist/libs/africanies-models/types/africanies-africanies-models.d.ts +0 -1452
- package/.nx/cache/14876618240301288353/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +0 -1
- package/.nx/cache/14995846848874973576/dist/libs/africanies-ui/fesm2022/africanies-africanies-ui.mjs.map +0 -1
- package/.nx/cache/17626862048742598274/dist/libs/africanies-theme/README.md +0 -46
- package/.nx/cache/17626862048742598274/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +0 -221
- package/.nx/cache/17626862048742598274/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +0 -1
- package/.nx/cache/17626862048742598274/dist/libs/africanies-theme/package.json +0 -35
- package/.nx/cache/17626862048742598274/dist/libs/africanies-theme/tailwind-preset.cjs +0 -253
- package/.nx/cache/17626862048742598274/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +0 -166
- package/.nx/cache/6664915487114632770/dist/libs/africanies-icons/README.md +0 -69
- package/.nx/cache/6664915487114632770/dist/libs/africanies-icons/assets/icons.sprite.svg +0 -1844
- package/.nx/cache/6664915487114632770/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs +0 -832
- package/.nx/cache/6664915487114632770/dist/libs/africanies-icons/fesm2022/africanies-africanies-icons.mjs.map +0 -1
- package/.nx/cache/6664915487114632770/dist/libs/africanies-icons/package.json +0 -30
- package/.nx/cache/6664915487114632770/dist/libs/africanies-icons/types/africanies-africanies-icons.d.ts +0 -108
- package/.nx/cache/6673686859704879504/dist/libs/africanies-theme/README.md +0 -46
- package/.nx/cache/6673686859704879504/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs +0 -231
- package/.nx/cache/6673686859704879504/dist/libs/africanies-theme/fesm2022/africanies-africanies-theme.mjs.map +0 -1
- package/.nx/cache/6673686859704879504/dist/libs/africanies-theme/package.json +0 -35
- package/.nx/cache/6673686859704879504/dist/libs/africanies-theme/tailwind-preset.cjs +0 -262
- package/.nx/cache/6673686859704879504/dist/libs/africanies-theme/types/africanies-africanies-theme.d.ts +0 -174
- package/.nx/cache/9040451482998546630/dist/libs/africanies-core/README.md +0 -85
- package/.nx/cache/9040451482998546630/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs +0 -4511
- package/.nx/cache/9040451482998546630/dist/libs/africanies-core/fesm2022/africanies-africanies-core.mjs.map +0 -1
- package/.nx/cache/9040451482998546630/dist/libs/africanies-core/package.json +0 -31
- package/.nx/cache/9040451482998546630/dist/libs/africanies-core/types/africanies-africanies-core.d.ts +0 -2741
- package/.nx/cache/terminalOutputs/13466509747676776487 +0 -21
- package/.nx/cache/terminalOutputs/13555699813138069787 +0 -6
- package/.nx/cache/terminalOutputs/14061940909886036014 +0 -173
- package/.nx/cache/terminalOutputs/14416694322692463866 +0 -108
- package/.nx/cache/terminalOutputs/17626862048742598274 +0 -21
- package/.nx/cache/terminalOutputs/6337357866566791975 +0 -6
- package/.nx/cache/terminalOutputs/6664915487114632770 +0 -44
- package/.nx/cache/terminalOutputs/6673686859704879504 +0 -21
- package/.nx/cache/terminalOutputs/783228502946611950 +0 -19
- package/.nx/cache/terminalOutputs/9040451482998546630 +0 -21
- /package/.nx/cache/{14876618240301288353 → 14892813664845064572}/dist/libs/africanies-ui/README.md +0 -0
- /package/.nx/cache/{14876618240301288353 → 14892813664845064572}/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
- /package/.nx/cache/{14876618240301288353 → 14892813664845064572}/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
- /package/.nx/cache/{14876618240301288353 → 14892813664845064572}/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +0 -0
- /package/.nx/cache/{14876618240301288353 → 14892813664845064572}/dist/libs/africanies-ui/assets/carriers/dhl.svg +0 -0
- /package/.nx/cache/{14876618240301288353 → 14892813664845064572}/dist/libs/africanies-ui/package.json +0 -0
- /package/.nx/cache/{14995846848874973576 → 7821397806208783174}/dist/libs/africanies-ui/README.md +0 -0
- /package/.nx/cache/{14995846848874973576 → 7821397806208783174}/dist/libs/africanies-ui/assets/brand/africanies-logo-mini.png +0 -0
- /package/.nx/cache/{14995846848874973576 → 7821397806208783174}/dist/libs/africanies-ui/assets/brand/africanies-logo.png +0 -0
- /package/.nx/cache/{14995846848874973576 → 7821397806208783174}/dist/libs/africanies-ui/assets/brand/africanies-logo.svg +0 -0
- /package/.nx/cache/{14995846848874973576 → 7821397806208783174}/dist/libs/africanies-ui/assets/carriers/dhl.svg +0 -0
- /package/.nx/cache/{14995846848874973576 → 7821397806208783174}/dist/libs/africanies-ui/package.json +0 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { countryFlagUrl } from '@africanies/africanies-core';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
PHONE_DIAL_CODES,
|
|
5
|
+
PHONE_MAX_NATIONAL_DIGITS,
|
|
6
|
+
PHONE_MIN_NATIONAL_DIGITS,
|
|
7
|
+
} from './phone-codes';
|
|
8
|
+
import type { PhoneCountryOption, PhoneNumberValue } from './phone-input.types';
|
|
9
|
+
|
|
10
|
+
const regionNames =
|
|
11
|
+
typeof Intl !== 'undefined' && 'DisplayNames' in Intl
|
|
12
|
+
? new Intl.DisplayNames(['en'], { type: 'region' })
|
|
13
|
+
: null;
|
|
14
|
+
|
|
15
|
+
/** Digits only from a phone draft. */
|
|
16
|
+
export function phoneDigitsOnly(value: string | null | undefined): string {
|
|
17
|
+
return (value ?? '').replace(/\D/g, '');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Normalize ISO2 to uppercase two letters, or `''`. */
|
|
21
|
+
export function normalizePhoneIso2(iso2: string | null | undefined): string {
|
|
22
|
+
const code = (iso2 ?? '').trim().toUpperCase();
|
|
23
|
+
return /^[A-Z]{2}$/.test(code) ? code : '';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Dial code for an ISO2 (with `+`), or `''` when unknown. */
|
|
27
|
+
export function dialCodeForIso2(iso2: string | null | undefined): string {
|
|
28
|
+
const code = normalizePhoneIso2(iso2);
|
|
29
|
+
return code ? (PHONE_DIAL_CODES[code] ?? '') : '';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** English country name from ISO2 (falls back to the code). */
|
|
33
|
+
export function phoneCountryName(iso2: string): string {
|
|
34
|
+
const code = normalizePhoneIso2(iso2);
|
|
35
|
+
if (!code) {
|
|
36
|
+
return '';
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
return regionNames?.of(code) ?? code;
|
|
40
|
+
} catch {
|
|
41
|
+
return code;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** All dial-code countries for the picker (sorted by name). */
|
|
46
|
+
export function phoneCountryOptions(): PhoneCountryOption[] {
|
|
47
|
+
return Object.keys(PHONE_DIAL_CODES)
|
|
48
|
+
.map((iso2) => {
|
|
49
|
+
const dialCode = PHONE_DIAL_CODES[iso2]!;
|
|
50
|
+
return {
|
|
51
|
+
iso2,
|
|
52
|
+
dialCode,
|
|
53
|
+
name: phoneCountryName(iso2),
|
|
54
|
+
flagUrl: countryFlagUrl(iso2, { width: 40 }),
|
|
55
|
+
};
|
|
56
|
+
})
|
|
57
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Filter dial-code countries by name, ISO2, or dial digits.
|
|
62
|
+
* Digit matching is skipped when the query has no digits — otherwise
|
|
63
|
+
* `''.includes('')` would keep every country for letter-only searches.
|
|
64
|
+
*/
|
|
65
|
+
export function filterPhoneCountries(
|
|
66
|
+
countries: readonly PhoneCountryOption[],
|
|
67
|
+
query: string,
|
|
68
|
+
): PhoneCountryOption[] {
|
|
69
|
+
const q = query.trim().toLowerCase();
|
|
70
|
+
if (!q) {
|
|
71
|
+
return [...countries];
|
|
72
|
+
}
|
|
73
|
+
const qDigits = phoneDigitsOnly(q);
|
|
74
|
+
return countries.filter((row) => {
|
|
75
|
+
if (
|
|
76
|
+
row.name.toLowerCase().includes(q) ||
|
|
77
|
+
row.iso2.toLowerCase().includes(q) ||
|
|
78
|
+
row.dialCode.toLowerCase().includes(q)
|
|
79
|
+
) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
return qDigits.length > 0
|
|
83
|
+
? phoneDigitsOnly(row.dialCode).includes(qDigits)
|
|
84
|
+
: false;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Match an E.164 / international string to the longest known dial code.
|
|
90
|
+
* Prefers `hintIso2` when several countries share the same dial code (e.g. `+1`).
|
|
91
|
+
*/
|
|
92
|
+
export function matchDialCode(
|
|
93
|
+
e164OrInternational: string,
|
|
94
|
+
hintIso2?: string | null,
|
|
95
|
+
): { iso2: string; dialCode: string; nationalDigits: string } | null {
|
|
96
|
+
const digits = phoneDigitsOnly(e164OrInternational);
|
|
97
|
+
if (!digits) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const hint = normalizePhoneIso2(hintIso2);
|
|
102
|
+
if (hint) {
|
|
103
|
+
const hintDial = PHONE_DIAL_CODES[hint];
|
|
104
|
+
if (hintDial) {
|
|
105
|
+
const hintDigits = phoneDigitsOnly(hintDial);
|
|
106
|
+
if (digits.startsWith(hintDigits)) {
|
|
107
|
+
return {
|
|
108
|
+
iso2: hint,
|
|
109
|
+
dialCode: hintDial,
|
|
110
|
+
nationalDigits: digits.slice(hintDigits.length),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let best: { iso2: string; dialCode: string; nationalDigits: string } | null =
|
|
117
|
+
null;
|
|
118
|
+
for (const [iso2, dialCode] of Object.entries(PHONE_DIAL_CODES)) {
|
|
119
|
+
const dialDigits = phoneDigitsOnly(dialCode);
|
|
120
|
+
if (!digits.startsWith(dialDigits)) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
const nationalDigits = digits.slice(dialDigits.length);
|
|
124
|
+
if (
|
|
125
|
+
!best ||
|
|
126
|
+
dialDigits.length > phoneDigitsOnly(best.dialCode).length
|
|
127
|
+
) {
|
|
128
|
+
best = { iso2, dialCode, nationalDigits };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return best;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Strip a leading dial code from a stored phone (classic `removeDialCode`).
|
|
136
|
+
* Returns national digits for prefilling the input.
|
|
137
|
+
*/
|
|
138
|
+
export function removeDialCode(
|
|
139
|
+
phone: string | null | undefined,
|
|
140
|
+
hintIso2?: string | null,
|
|
141
|
+
): string {
|
|
142
|
+
const raw = (phone ?? '').trim();
|
|
143
|
+
if (!raw) {
|
|
144
|
+
return '';
|
|
145
|
+
}
|
|
146
|
+
const matched = matchDialCode(raw, hintIso2);
|
|
147
|
+
if (matched) {
|
|
148
|
+
return matched.nationalDigits;
|
|
149
|
+
}
|
|
150
|
+
return phoneDigitsOnly(raw);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Group national digits for readable display (… + last 4). */
|
|
154
|
+
export function formatNationalDigits(digits: string): string {
|
|
155
|
+
const clean = phoneDigitsOnly(digits).slice(0, PHONE_MAX_NATIONAL_DIGITS);
|
|
156
|
+
if (!clean) {
|
|
157
|
+
return '';
|
|
158
|
+
}
|
|
159
|
+
if (clean.length <= 4) {
|
|
160
|
+
return clean;
|
|
161
|
+
}
|
|
162
|
+
const end = clean.slice(-4);
|
|
163
|
+
const head = clean.slice(0, -4).replace(/(\d{3})(?=\d)/g, '$1 ').trim();
|
|
164
|
+
return `${head} ${end}`.trim();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Build the structured value (or `null` when empty).
|
|
169
|
+
*/
|
|
170
|
+
export function buildPhoneNumberValue(
|
|
171
|
+
iso2: string,
|
|
172
|
+
nationalDraft: string,
|
|
173
|
+
): PhoneNumberValue | null {
|
|
174
|
+
const countryCode = normalizePhoneIso2(iso2);
|
|
175
|
+
const dialCode = dialCodeForIso2(countryCode);
|
|
176
|
+
if (!countryCode || !dialCode) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const nationalDigits = phoneDigitsOnly(nationalDraft).slice(
|
|
181
|
+
0,
|
|
182
|
+
PHONE_MAX_NATIONAL_DIGITS,
|
|
183
|
+
);
|
|
184
|
+
if (!nationalDigits) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const number = formatNationalDigits(nationalDigits);
|
|
189
|
+
const e164Number = `${dialCode}${nationalDigits}`;
|
|
190
|
+
const internationalNumber = `${dialCode} ${number}`.trim();
|
|
191
|
+
// Trunk-prefix `0` for national display when digits omit it (classic ngx shape).
|
|
192
|
+
const nationalNumber = nationalDigits.startsWith('0')
|
|
193
|
+
? number
|
|
194
|
+
: `0${number}`;
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
number,
|
|
198
|
+
internationalNumber,
|
|
199
|
+
nationalNumber,
|
|
200
|
+
e164Number,
|
|
201
|
+
countryCode,
|
|
202
|
+
dialCode,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Canonical outbound phone string — prefer E.164 from a structured value.
|
|
208
|
+
*/
|
|
209
|
+
export function phoneToE164(
|
|
210
|
+
phone: PhoneNumberValue | string | null | undefined,
|
|
211
|
+
): string {
|
|
212
|
+
if (phone == null) {
|
|
213
|
+
return '';
|
|
214
|
+
}
|
|
215
|
+
if (typeof phone === 'string') {
|
|
216
|
+
const digits = phoneDigitsOnly(phone);
|
|
217
|
+
if (!digits) {
|
|
218
|
+
return '';
|
|
219
|
+
}
|
|
220
|
+
return phone.trim().startsWith('+') ? `+${digits}` : phone.trim();
|
|
221
|
+
}
|
|
222
|
+
return phone.e164Number?.trim() || '';
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Soft validity: has dial code + enough national digits (not full libphonenumber).
|
|
227
|
+
*/
|
|
228
|
+
export function isPhoneNumberComplete(
|
|
229
|
+
phone: PhoneNumberValue | null | undefined,
|
|
230
|
+
): boolean {
|
|
231
|
+
if (!phone?.e164Number) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
const national = phoneDigitsOnly(phone.number);
|
|
235
|
+
return (
|
|
236
|
+
national.length >= PHONE_MIN_NATIONAL_DIGITS &&
|
|
237
|
+
national.length <= PHONE_MAX_NATIONAL_DIGITS
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Split an incoming control value (E.164 string or structured) into UI state.
|
|
243
|
+
*/
|
|
244
|
+
export function parsePhoneControlValue(
|
|
245
|
+
value: string | PhoneNumberValue | null | undefined,
|
|
246
|
+
fallbackIso2: string,
|
|
247
|
+
): { iso2: string; nationalDigits: string } {
|
|
248
|
+
const fallback = normalizePhoneIso2(fallbackIso2) || 'US';
|
|
249
|
+
|
|
250
|
+
if (value == null || value === '') {
|
|
251
|
+
return { iso2: fallback, nationalDigits: '' };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (typeof value === 'object') {
|
|
255
|
+
const iso2 = normalizePhoneIso2(value.countryCode) || fallback;
|
|
256
|
+
const national =
|
|
257
|
+
phoneDigitsOnly(value.number) ||
|
|
258
|
+
phoneDigitsOnly(value.nationalNumber) ||
|
|
259
|
+
removeDialCode(value.e164Number, iso2);
|
|
260
|
+
return { iso2, nationalDigits: national };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const matched = matchDialCode(value, fallback);
|
|
264
|
+
if (matched) {
|
|
265
|
+
return {
|
|
266
|
+
iso2: matched.iso2,
|
|
267
|
+
nationalDigits: matched.nationalDigits,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
iso2: fallback,
|
|
273
|
+
nationalDigits: phoneDigitsOnly(value),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
@@ -6,6 +6,7 @@ import { DatePickerComponent } from '../forms/date-picker';
|
|
|
6
6
|
import { FileUploadComponent } from '../forms/file-upload';
|
|
7
7
|
import { NumberInputComponent } from '../forms/number-input';
|
|
8
8
|
import { OtpInputComponent } from '../forms/otp-input';
|
|
9
|
+
import { PhoneInputComponent } from '../forms/phone-input';
|
|
9
10
|
import { RadioComponent } from '../forms/radio';
|
|
10
11
|
import { SelectComponent } from '../forms/select';
|
|
11
12
|
import { TextInputComponent } from '../forms/text-input';
|
|
@@ -24,6 +25,7 @@ const FORM_CONTROLS = [
|
|
|
24
25
|
DatePickerComponent,
|
|
25
26
|
FileUploadComponent,
|
|
26
27
|
OtpInputComponent,
|
|
28
|
+
PhoneInputComponent,
|
|
27
29
|
] as const;
|
|
28
30
|
|
|
29
31
|
/**
|
|
@@ -70,8 +70,9 @@ const DEFAULT_LINK_ACTIVE: IsActiveMatchOptions = {
|
|
|
70
70
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
71
71
|
imports: [AfricaniesIconComponent],
|
|
72
72
|
styles: `
|
|
73
|
+
/* Track p-1 (0.25×2) + 0.25 gap between pills — keep right inset equal to left. */
|
|
73
74
|
:host .africanies-segment-thumb {
|
|
74
|
-
width: calc((100% - 0.
|
|
75
|
+
width: calc((100% - 0.75rem) / 2);
|
|
75
76
|
}
|
|
76
77
|
`,
|
|
77
78
|
template: `
|
|
@@ -59,4 +59,19 @@ describe('ShippingModeSwitchComponent', () => {
|
|
|
59
59
|
expect(shipping.mode()).toBe('stn');
|
|
60
60
|
expect(toNigeria.getAttribute('aria-checked')).toBe('true');
|
|
61
61
|
});
|
|
62
|
+
|
|
63
|
+
it('should show the STN/SFN chip only when the mode panel is collapsed', () => {
|
|
64
|
+
fixture.componentRef.setInput('panelOpen', true);
|
|
65
|
+
fixture.detectChanges();
|
|
66
|
+
expect(fixture.nativeElement.textContent).not.toContain('SFN');
|
|
67
|
+
|
|
68
|
+
fixture.componentRef.setInput('panelOpen', false);
|
|
69
|
+
fixture.detectChanges();
|
|
70
|
+
expect(fixture.nativeElement.textContent).toContain('SFN');
|
|
71
|
+
|
|
72
|
+
shipping.setMode('stn');
|
|
73
|
+
fixture.detectChanges();
|
|
74
|
+
expect(fixture.nativeElement.textContent).toContain('STN');
|
|
75
|
+
expect(fixture.nativeElement.textContent).not.toContain('SFN');
|
|
76
|
+
});
|
|
62
77
|
});
|
package/libs/africanies-ui/src/lib/navigation/shipping-mode-switch/shipping-mode-switch.component.ts
CHANGED
|
@@ -128,9 +128,12 @@ const SHIPPING_MODE_GLYPH = `M88.42,68.75a48.8,48.8,0,0,1-4.74,11.1h9.89a7.67,7.
|
|
|
128
128
|
>
|
|
129
129
|
Shipping mode
|
|
130
130
|
</p>
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
<!-- STN / SFN chip only when the tiles are collapsed — open panel already shows both modes. -->
|
|
132
|
+
@if (!panelOpen()) {
|
|
133
|
+
<africanies-chip [variant]="modeChipVariant()" size="sm">
|
|
134
|
+
{{ modeChipLabel() }}
|
|
135
|
+
</africanies-chip>
|
|
136
|
+
}
|
|
134
137
|
<span class="min-w-0 flex-1" aria-hidden="true"></span>
|
|
135
138
|
<button
|
|
136
139
|
type="button"
|
|
@@ -45,8 +45,9 @@ import { TableColumn, TableSortChange } from './table-column';
|
|
|
45
45
|
* `<ng-template africaniesCellDef="key">` templates. Columns without a matching
|
|
46
46
|
* template fall back to rendering `row[key]` as plain text.
|
|
47
47
|
*
|
|
48
|
-
* Optional toolbar: top-left **shipping mode** segment
|
|
49
|
-
* `[showShippingMode]="false"` to
|
|
48
|
+
* Optional toolbar: top-left **shipping mode** segment on its own row above
|
|
49
|
+
* Refresh / Filter / Export (on by default; `[showShippingMode]="false"` to
|
|
50
|
+
* hide) and **Refresh** (`showRefresh` →
|
|
50
51
|
* {@link refreshClick}). Refresh is hidden while
|
|
51
52
|
* the body shows empty or error — those states expose Retry instead.
|
|
52
53
|
* While {@link refreshing} is true the rows stay on screen — the refresh icon
|
|
@@ -177,71 +178,75 @@ import { TableColumn, TableSortChange } from './table-column';
|
|
|
177
178
|
showFilter() ||
|
|
178
179
|
showExport()
|
|
179
180
|
) {
|
|
180
|
-
<div class="flex
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
<
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
name="refresh"
|
|
203
|
-
[size]="16"
|
|
204
|
-
[class]="refreshing() ? 'animate-spin' : ''"
|
|
205
|
-
/>
|
|
206
|
-
{{ refreshLabel() }}
|
|
207
|
-
</button>
|
|
208
|
-
}
|
|
209
|
-
</div>
|
|
210
|
-
<div class="flex items-center gap-2">
|
|
211
|
-
@if (showFilter()) {
|
|
212
|
-
<button
|
|
213
|
-
africanies-button
|
|
214
|
-
type="button"
|
|
215
|
-
variant="flat"
|
|
216
|
-
size="sm"
|
|
217
|
-
[attr.aria-label]="filterLabel()"
|
|
218
|
-
(click)="filterClick.emit()"
|
|
219
|
-
>
|
|
220
|
-
<africanies-icon name="filter" [size]="16" />
|
|
221
|
-
{{ filterLabel() }}
|
|
222
|
-
@if (filterCount() > 0) {
|
|
223
|
-
<span
|
|
224
|
-
class="inline-flex min-w-5 items-center justify-center rounded-full bg-ink px-1.5 py-0.5 text-caption font-semibold tabular-nums text-white dark:bg-white dark:text-ink"
|
|
181
|
+
<div class="flex w-full min-w-0 flex-col gap-3">
|
|
182
|
+
@if (showShippingMode()) {
|
|
183
|
+
<africanies-segment
|
|
184
|
+
class="min-w-0 self-start"
|
|
185
|
+
[items]="shippingModeItems"
|
|
186
|
+
[activeId]="shippingModeActiveId()"
|
|
187
|
+
ariaLabel="Shipping mode"
|
|
188
|
+
(activeIdChange)="onShippingModeChange($event)"
|
|
189
|
+
/>
|
|
190
|
+
}
|
|
191
|
+
@if (showToolbarRefresh() || showFilter() || showExport()) {
|
|
192
|
+
<div class="flex flex-wrap items-center justify-between gap-2">
|
|
193
|
+
<div class="flex min-w-0 flex-wrap items-center gap-2">
|
|
194
|
+
@if (showToolbarRefresh()) {
|
|
195
|
+
<button
|
|
196
|
+
africanies-button
|
|
197
|
+
type="button"
|
|
198
|
+
variant="flat"
|
|
199
|
+
size="sm"
|
|
200
|
+
[disabled]="loading() || refreshing()"
|
|
201
|
+
[attr.aria-label]="refreshLabel()"
|
|
202
|
+
(click)="refreshClick.emit()"
|
|
225
203
|
>
|
|
226
|
-
|
|
227
|
-
|
|
204
|
+
<africanies-icon
|
|
205
|
+
name="refresh"
|
|
206
|
+
[size]="16"
|
|
207
|
+
[class]="refreshing() ? 'animate-spin' : ''"
|
|
208
|
+
/>
|
|
209
|
+
{{ refreshLabel() }}
|
|
210
|
+
</button>
|
|
228
211
|
}
|
|
229
|
-
</
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
212
|
+
</div>
|
|
213
|
+
<div class="flex items-center gap-2">
|
|
214
|
+
@if (showFilter()) {
|
|
215
|
+
<button
|
|
216
|
+
africanies-button
|
|
217
|
+
type="button"
|
|
218
|
+
variant="flat"
|
|
219
|
+
size="sm"
|
|
220
|
+
[attr.aria-label]="filterLabel()"
|
|
221
|
+
(click)="filterClick.emit()"
|
|
222
|
+
>
|
|
223
|
+
<africanies-icon name="filter" [size]="16" />
|
|
224
|
+
{{ filterLabel() }}
|
|
225
|
+
@if (filterCount() > 0) {
|
|
226
|
+
<span
|
|
227
|
+
class="inline-flex min-w-5 items-center justify-center rounded-full bg-ink px-1.5 py-0.5 text-caption font-semibold tabular-nums text-white dark:bg-white dark:text-ink"
|
|
228
|
+
>
|
|
229
|
+
{{ filterCount() }}
|
|
230
|
+
</span>
|
|
231
|
+
}
|
|
232
|
+
</button>
|
|
233
|
+
}
|
|
234
|
+
@if (showExport()) {
|
|
235
|
+
<button
|
|
236
|
+
africanies-button
|
|
237
|
+
type="button"
|
|
238
|
+
variant="flat"
|
|
239
|
+
size="sm"
|
|
240
|
+
[attr.aria-label]="exportLabel()"
|
|
241
|
+
(click)="exportClick.emit()"
|
|
242
|
+
>
|
|
243
|
+
<africanies-icon name="download" [size]="16" />
|
|
244
|
+
{{ exportLabel() }}
|
|
245
|
+
</button>
|
|
246
|
+
}
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
}
|
|
245
250
|
</div>
|
|
246
251
|
}
|
|
247
252
|
|
package/package.json
CHANGED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
# @africanies/africanies-core
|
|
2
|
-
|
|
3
|
-
API and app-wiring layer for the AFRICANIES Web SDK: SDK config, HTTP client,
|
|
4
|
-
shipping mode, mode-region metadata, TanStack Query default helpers, and
|
|
5
|
-
route-driven overlay registration.
|
|
6
|
-
|
|
7
|
-
## Install peers
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
# Published on npm (@africanies scope)
|
|
11
|
-
npm install @africanies/africanies-core @africanies/africanies-models @africanies/africanies-storage
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
Peer dependencies: `@angular/common`, `@angular/core`, `@angular/router`,
|
|
15
|
-
`rxjs`, `@africanies/africanies-models`, `@africanies/africanies-storage`.
|
|
16
|
-
|
|
17
|
-
## Quick start
|
|
18
|
-
|
|
19
|
-
```ts
|
|
20
|
-
import { ApplicationConfig } from '@angular/core';
|
|
21
|
-
import {
|
|
22
|
-
provideAfricaniesSdk,
|
|
23
|
-
provideAfricaniesHttpClient,
|
|
24
|
-
provideOverlayRoutes,
|
|
25
|
-
createAfricaniesQueryClientDefaults,
|
|
26
|
-
} from '@africanies/africanies-core';
|
|
27
|
-
|
|
28
|
-
export const appConfig: ApplicationConfig = {
|
|
29
|
-
providers: [
|
|
30
|
-
provideAfricaniesSdk({ baseUrl: 'https://api.example.com', timeout: 30_000 }),
|
|
31
|
-
provideAfricaniesHttpClient(),
|
|
32
|
-
// optional extras:
|
|
33
|
-
// provideAfricaniesHttpClient({ interceptors: [correlationInterceptor] }),
|
|
34
|
-
// optional: provideOverlayRoutes([{ paramKey: 'modal', routes: { ... } }]),
|
|
35
|
-
],
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
// TanStack Query (app dependency — pin exact experimental version):
|
|
39
|
-
// new QueryClient({ defaultOptions: createAfricaniesQueryClientDefaults() })
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## Modules
|
|
43
|
-
|
|
44
|
-
| Area | Exports |
|
|
45
|
-
|------|---------|
|
|
46
|
-
| Config | `provideAfricaniesSdk`, `AFRICANIES_SDK_CONFIG`, `httpToasts` (`'off'` \| `'errors'` \| `'all'`) |
|
|
47
|
-
| Auth | `AuthService` (`forgot`), `isValidEmail`, `AuthTokenService` (`set` / `clear` / `get`) |
|
|
48
|
-
| Shipping | `ShippingModeService` (tab-scoped via `sessionStorage`), `shipmentModeInterceptor`, per-request `ApiRequestOptions.shippingMode` / `withShippingMode` |
|
|
49
|
-
| HTTP | `provideAfricaniesHttpClient`, `ApiClient` (auto-tags `withToast` from config / `ApiRequestOptions.toast`; optional `shippingMode` per call; HTTP failures → `Error` with formatted `.message`), `withToast`, `withShippingMode`, `httpToastInterceptor`, `normalize` (lifts Laravel validation bags into `errors` + joined `message`), `formatApiErrorMessage` (used internally; rare for hosts), `fieldErrorsMap`, `authInterceptor` |
|
|
50
|
-
| Query | `createAfricaniesQueryClientDefaults`, `provideAfricaniesQueryDefaults`, `listFetchKind` (body loader on first load / STN↔SFN; keep rows on focus / Refresh / pager) |
|
|
51
|
-
| Mode | `ModeConfigService`, `provideModeConfig`, `MODE_CONFIG_PATH` |
|
|
52
|
-
| Country | `CountryService`, `countryFlagUrl` / `mapCountrySelectOptions` (flagcdn.com), `COUNTRY_READ_PATH`, mappers |
|
|
53
|
-
| Service | `ServiceService` (`readPage` / `readAll` / `readById`), `SERVICE_READ_PATH` (`/public/service/read`), mappers |
|
|
54
|
-
| Document | `DocumentService` (`readPage` / `readAll` / `readById`), `DOCUMENT_READ_PATH` (`/public/document/read`) — preview via `file_ref` on by-id |
|
|
55
|
-
| Plan | `PlanService` (`readPage` / `readAll` / `readById`), `PLAN_READ_PATH` (`/public/plan/read`), mappers |
|
|
56
|
-
| Currency | `CurrencyService` (`readPage` / `readAll` / `readById`, `create` / `update` / `remove`), `CURRENCY_*_PATH`, mappers |
|
|
57
|
-
| Payment method | `PaymentMethodService` (`readPage` / `readAll` / `readById`, `update`), `PAYMENT_METHOD_READ_PATH` / `PAYMENT_METHOD_UPDATE_PATH`, mappers |
|
|
58
|
-
| Shipment method | `ShipmentMethodService` (`readPage` / `readAll` / `readById`), `SHIPMENT_METHOD_READ_PATH`, mappers |
|
|
59
|
-
| Warehouse | `WarehouseService` (`readPage` / `readAll` / `readById`), `WAREHOUSE_READ_PATH`, mappers |
|
|
60
|
-
| Zone | `ZoneService` (`readPage` / `readAll` / `readById`), `ZONE_READ_PATH`, mappers |
|
|
61
|
-
| Product | `ProductService` (`readPage` / `readAll` / `readById`), `PRODUCT_READ_PATH`, mappers |
|
|
62
|
-
| User | `UserService` (`me`, `changePassword`, `logoutFromAllSessions`), `USER_PATH`, `mapUser` (bare `/user` body; needs `AuthTokenService.set`) |
|
|
63
|
-
| Notification | `NotificationService` (`readPage` / `readAll` / `readOne`), `NOTIFICATION_READ_PATH`, `mapNotification` / `mapNotificationInboxItem` |
|
|
64
|
-
| File | `FileService` (`read` / `readMultiple` → `POST /file/read`), `FILE_READ_PATH`, `mapFileRead` / `mapFileReadList` |
|
|
65
|
-
| Overlay | `provideOverlayRoutes`, `RouteOverlayService`, `MODAL_SERVICE`, `DRAWER_SERVICE` |
|
|
66
|
-
| Filters | `FilterOptionsResolver` (`resolveField` lazy in drawer), `mergeFilterOptionLists`, warehouse/method mappers |
|
|
67
|
-
| Browser | `copyToClipboard`, `downloadCsv` / `csvCell` (UTF-8 CSV download) |
|
|
68
|
-
|
|
69
|
-
List-style GETs follow `ResourceId`: `null` (paginated), `'all'` (full dump), `number` (single).
|
|
70
|
-
|
|
71
|
-
| Filter `optionsSource` | SDK resolver |
|
|
72
|
-
|------------------------|--------------|
|
|
73
|
-
| `warehouses` | Lazy `FilterOptionsResolver.resolveField` → `WarehouseService.readAll()` |
|
|
74
|
-
| `shipmentMethods` | Lazy `FilterOptionsResolver.resolveField` → `ShipmentMethodService.readAll()` |
|
|
75
|
-
| `shipmentManifests` | Host `optionLists` (no built-in service yet) |
|
|
76
|
-
|
|
77
|
-
`MODAL_SERVICE` / `DRAWER_SERVICE` are tokens only — implementations ship in
|
|
78
|
-
`@africanies/africanies-ui` to avoid circular dependencies.
|
|
79
|
-
|
|
80
|
-
## Build / test
|
|
81
|
-
|
|
82
|
-
```bash
|
|
83
|
-
npx nx build africanies-core
|
|
84
|
-
npx nx test africanies-core
|
|
85
|
-
```
|