central-auth-js-sdk 1.2.6 → 1.2.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.
- package/INTEGRATION_GUIDE.md +125 -0
- package/auth.js +11 -0
- package/core/api.js +5 -0
- package/package.json +1 -1
- package/signup.js +25 -2
- package/ui/screens/email-verify.js +46 -15
- package/ui/screens/index.js +8 -1
- package/ui/screens/mobile-entry.js +47 -6
package/INTEGRATION_GUIDE.md
CHANGED
|
@@ -111,6 +111,9 @@ When **`autoResumeOAuth`** is `true` (default) and **`defaultContainer`** is set
|
|
|
111
111
|
| `loginMethods` | object | — | Default login methods (see Login config) |
|
|
112
112
|
| `frsAsSecondFactor` | boolean | `false` | Default: require face verification after primary auth |
|
|
113
113
|
| `enableFaceRegistration` | boolean | `true` | Whether sign-up includes face registration step |
|
|
114
|
+
| `collectOrgDetails` | boolean \| object | `true` | Whether to show/collect organization fields (Team, Sub-team, Designation) during registration (`true` \| `false`, or `{ team, subTeam, designation }`). |
|
|
115
|
+
| `mobileVerification` | string | `'india_only'` | Mobile verification mode: `'india_only'` \| `'none'` \| `'all'`. Default `'india_only'` verifies +91 via OTP and auto-skips OTP for international numbers. |
|
|
116
|
+
| `skipMobileOtp` | boolean | `false` | Explicitly skip mobile OTP verification for all numbers when `true`. |
|
|
114
117
|
| `onContinue` | function | — | Default callback when user clicks Continue on success (see [§ 9](#9-returning-to-the-parent-application)) |
|
|
115
118
|
| `onBack` | function | — | Default callback when user clicks ← Back on the first screen of signup/login (see [§ 9](#9-returning-to-the-parent-application)) |
|
|
116
119
|
| `onLoginComplete` | function | — | Default login-complete callback |
|
|
@@ -135,6 +138,8 @@ const auth = initializeAuth({
|
|
|
135
138
|
appIdentifier: 'my-app',
|
|
136
139
|
defaultContainer: '#auth-container',
|
|
137
140
|
autoResumeOAuth: true,
|
|
141
|
+
mobileVerification: 'india_only', // default: verify +91 with OTP, skip for international
|
|
142
|
+
collectOrgDetails: true, // set false to hide Team, Sub-team, Designation
|
|
138
143
|
loginMethods: {
|
|
139
144
|
emailPassword: true,
|
|
140
145
|
emailOtp: true,
|
|
@@ -162,6 +167,10 @@ Starts the **sign-up flow** (email + name → verify → mobile → verify → p
|
|
|
162
167
|
| `env` | string | no | Environment: `'local'` \| `'dev'` \| `'staging'` \| `'prod'` (default from init or `'local'`) |
|
|
163
168
|
| `appIdentifier` | string | no | Optional. When provided, sent with relevant API requests; omit to not send. |
|
|
164
169
|
| `enableFaceRegistration` | boolean | no | Include face registration step (default from init or `true`) |
|
|
170
|
+
| `collectOrgDetails` | boolean \| object | no | Show/collect Team, Sub-team, Designation during registration (default: `true`, set `false` to hide all, or pass `{ team, subTeam, designation }` for granular control). See [§ 5.3](#53-organization-details-team-sub-team--designation). |
|
|
171
|
+
| `showOrgDetails` | boolean | no | Alias for `collectOrgDetails`. |
|
|
172
|
+
| `mobileVerification` | string | no | Mobile OTP verification policy: `'india_only'` (default) \| `'none'` \| `'all'`. See [§ 5.2](#52-mobile-verification--international-numbers-skip-otp). |
|
|
173
|
+
| `skipMobileOtp` | boolean | no | Skip mobile OTP verification directly (`true` / `false`). |
|
|
165
174
|
| **`onSignupComplete(payload)`** | function | no | Called **once** when signup completes. `payload`: `email`, `mobile`, `userId` + any fields from final API (e.g. `access_token`, `refresh_token`). |
|
|
166
175
|
| **`onContinue()`** | function | no | Called when user clicks **Continue to application** on the success screen. Use to unmount and redirect. |
|
|
167
176
|
| **`onBack()`** | function | no | Called when user clicks **← Back** on the **first screen** of signup. Use to unmount the SDK and return to your app. See [§ 9](#9-returning-to-the-parent-application). |
|
|
@@ -279,6 +288,122 @@ auth.startLogin({ theme: { primaryColor: '#2563EB' } }); // overrides to blue f
|
|
|
279
288
|
|
|
280
289
|
---
|
|
281
290
|
|
|
291
|
+
## 5.2. Mobile verification & international numbers (Skip OTP)
|
|
292
|
+
|
|
293
|
+
By default, the SDK uses **`mobileVerification: 'india_only'`**. This provides frictionless onboarding for international users while preserving carrier-grade SMS/WhatsApp OTP verification for domestic Indian numbers.
|
|
294
|
+
|
|
295
|
+
### Verification modes
|
|
296
|
+
|
|
297
|
+
| Mode | Behavior | Button Label | WhatsApp OTP Option |
|
|
298
|
+
|------|----------|--------------|---------------------|
|
|
299
|
+
| `'india_only'` *(Default)* | **+91 (India):** Sends OTP via SMS or WhatsApp and requires 6-digit verification.<br>**International (+1, +44, +971, +65, etc.):** Collects phone number, skips OTP verification, and transitions directly to Password Setup. | "Send Code" (+91)<br>"Continue" (International) | Shown for +91, Hidden for International |
|
|
300
|
+
| `'none'` | Skips OTP for **all** phone numbers. Collects mobile number and transitions immediately to Password Setup. | "Continue" | Hidden |
|
|
301
|
+
| `'all'` | Enforces OTP verification for **all** phone numbers regardless of country code. | "Send Code" | Shown |
|
|
302
|
+
|
|
303
|
+
> **Shortcut:** You can also pass `skipMobileOtp: true` in `startSignup` or `initializeAuth` to bypass mobile OTP verification completely.
|
|
304
|
+
|
|
305
|
+
### How it works under the hood
|
|
306
|
+
|
|
307
|
+
1. **Dynamic UI:** When an international country code is selected (or when OTP is skipped), the SDK dynamically switches the CTA button from *"Send Code"* to *"Continue"*, hides the *"Send WhatsApp OTP instead"* button, and updates the prompt text to *"Enter your mobile number to continue"*.
|
|
308
|
+
2. **Flexible length validation:** Indian numbers (`+91`) require $\ge 10$ digits; international numbers accept between 7 and 15 digits (E.164 standard) to support numbers across different countries (e.g. Singapore `+65` with 8 digits, UAE `+971` with 9 digits).
|
|
309
|
+
3. **Backend integration:**
|
|
310
|
+
- **Standard flow (with OTP):** Calls `POST /signup/mobile/send_otp` $\rightarrow$ user enters OTP $\rightarrow$ calls `POST /signup/mobile/verify`.
|
|
311
|
+
- **Skip OTP flow:** Calls `POST /signup/mobile/save` with `{ email, mobile }` to persist the mobile number, advances user signup status to `MOBILE_VERIFIED`, and transitions directly to `STEPS.PASSWORD`.
|
|
312
|
+
|
|
313
|
+
### Example configurations
|
|
314
|
+
|
|
315
|
+
#### 1. Default (India OTP only, international numbers skip OTP)
|
|
316
|
+
|
|
317
|
+
No extra configuration needed! This is the out-of-the-box default:
|
|
318
|
+
|
|
319
|
+
```javascript
|
|
320
|
+
const instance = startSignup({
|
|
321
|
+
container: '#auth-container',
|
|
322
|
+
env: 'dev',
|
|
323
|
+
// mobileVerification: 'india_only' is active by default
|
|
324
|
+
onSignupComplete(payload) {
|
|
325
|
+
console.log('User signed up:', payload);
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
#### 2. Skip mobile OTP for all countries
|
|
331
|
+
|
|
332
|
+
```javascript
|
|
333
|
+
const instance = startSignup({
|
|
334
|
+
container: '#auth-container',
|
|
335
|
+
env: 'dev',
|
|
336
|
+
mobileVerification: 'none', // or pass: skipMobileOtp: true
|
|
337
|
+
onSignupComplete(payload) {
|
|
338
|
+
console.log('Signed up without mobile OTP:', payload);
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
#### 3. Require OTP for all numbers (Global OTP verification)
|
|
344
|
+
|
|
345
|
+
```javascript
|
|
346
|
+
const instance = startSignup({
|
|
347
|
+
container: '#auth-container',
|
|
348
|
+
env: 'dev',
|
|
349
|
+
mobileVerification: 'all',
|
|
350
|
+
});
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## 5.3. Organization details (Team, Sub-team & Designation)
|
|
356
|
+
|
|
357
|
+
During the email verification step, the SDK by default renders fields to collect **Team**, **Sub-team**, and **Designation** alongside First and Last Name.
|
|
358
|
+
|
|
359
|
+
If your application does not need to collect organization details, or if you only want to collect a subset of them, use **`collectOrgDetails`** (or its alias **`showOrgDetails`**).
|
|
360
|
+
|
|
361
|
+
### Configuration options
|
|
362
|
+
|
|
363
|
+
| Value | Behavior |
|
|
364
|
+
|-------|----------|
|
|
365
|
+
| `true` *(Default)* | Shows all 3 fields (**Team**, **Sub-team**, **Designation**). |
|
|
366
|
+
| `false` | Completely hides **Team**, **Sub-team**, and **Designation** inputs from the registration screen. The user only enters OTP, First Name, and Last Name. |
|
|
367
|
+
| Object `{ team, subTeam, designation }` | Granular control to show only specific fields (e.g. `{ team: true, subTeam: false, designation: true }`). |
|
|
368
|
+
|
|
369
|
+
### Examples
|
|
370
|
+
|
|
371
|
+
#### 1. Hide all organization fields during signup
|
|
372
|
+
|
|
373
|
+
```javascript
|
|
374
|
+
const instance = startSignup({
|
|
375
|
+
container: '#auth-container',
|
|
376
|
+
env: 'dev',
|
|
377
|
+
collectOrgDetails: false, // or showOrgDetails: false
|
|
378
|
+
});
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
#### 2. Granular control (e.g. collect Team and Designation, hide Sub-team)
|
|
382
|
+
|
|
383
|
+
```javascript
|
|
384
|
+
const instance = startSignup({
|
|
385
|
+
container: '#auth-container',
|
|
386
|
+
env: 'dev',
|
|
387
|
+
collectOrgDetails: {
|
|
388
|
+
team: true,
|
|
389
|
+
subTeam: false,
|
|
390
|
+
designation: true,
|
|
391
|
+
},
|
|
392
|
+
});
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
#### 3. Set globally via `initializeAuth`
|
|
396
|
+
|
|
397
|
+
```javascript
|
|
398
|
+
const auth = initializeAuth({
|
|
399
|
+
env: 'dev',
|
|
400
|
+
defaultContainer: '#auth-container',
|
|
401
|
+
collectOrgDetails: false, // will apply to all startSignup calls by default
|
|
402
|
+
});
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
282
407
|
## 6. startLogin(config)
|
|
283
408
|
|
|
284
409
|
Starts the **login flow**. If the parent enables **only one** login method, the SDK skips the method picker and goes straight to that flow.
|
package/auth.js
CHANGED
|
@@ -122,6 +122,9 @@ export function initializeAuth(config = {}) {
|
|
|
122
122
|
loginMethods: null,
|
|
123
123
|
frsAsSecondFactor: false,
|
|
124
124
|
enableFaceRegistration: true,
|
|
125
|
+
collectOrgDetails: true,
|
|
126
|
+
mobileVerification: 'india_only',
|
|
127
|
+
skipMobileOtp: false,
|
|
125
128
|
theme: null,
|
|
126
129
|
...config,
|
|
127
130
|
};
|
|
@@ -156,6 +159,10 @@ export function initializeAuth(config = {}) {
|
|
|
156
159
|
* @param {string} [config.env] - Environment: 'local' | 'dev' | 'staging' | 'prod' (default from init or 'local')
|
|
157
160
|
* @param {string} [config.appIdentifier] - Override app identifier
|
|
158
161
|
* @param {boolean} [config.enableFaceRegistration] - Include face registration step (default from init or true)
|
|
162
|
+
* @param {boolean|Object} [config.collectOrgDetails=true] - Whether to show/collect Team, Sub-team, and Designation during registration (or pass object for granular control: { team, subTeam, designation })
|
|
163
|
+
* @param {boolean} [config.showOrgDetails] - Alias for collectOrgDetails
|
|
164
|
+
* @param {string} [config.mobileVerification] - Mobile verification mode: 'india_only' | 'none' | 'all' (default: 'india_only')
|
|
165
|
+
* @param {boolean} [config.skipMobileOtp] - Explicitly skip mobile OTP verification (default: false)
|
|
159
166
|
* @param {Function} [config.onSignupComplete] - Called when signup completes with (payload). Payload: email, mobile, userId, plus any token/fields from the final API response (e.g. access_token, refresh_token).
|
|
160
167
|
* @param {Function} [config.onContinue] - Called when user clicks Continue on the success screen (e.g. unmount and redirect).
|
|
161
168
|
* @param {Function} [config.onSwitchToLogin] - Called when user clicks "Sign in" (e.g. unmount signup and start login, e.g. login + FRS).
|
|
@@ -176,6 +183,10 @@ export function startSignup(config = {}) {
|
|
|
176
183
|
apiBaseUrl: resolveApiBaseUrl(merged),
|
|
177
184
|
appIdentifier: merged.appIdentifier,
|
|
178
185
|
enableFaceRegistration: merged.enableFaceRegistration,
|
|
186
|
+
collectOrgDetails: config.collectOrgDetails ?? config.showOrgDetails ?? merged.collectOrgDetails,
|
|
187
|
+
showOrgDetails: config.showOrgDetails ?? merged.showOrgDetails,
|
|
188
|
+
mobileVerification: config.mobileVerification ?? merged.mobileVerification,
|
|
189
|
+
skipMobileOtp: config.skipMobileOtp ?? merged.skipMobileOtp,
|
|
179
190
|
theme: merged.theme || undefined,
|
|
180
191
|
onSignupComplete: config.onSignupComplete ?? merged.onSignupComplete,
|
|
181
192
|
onContinue: config.onContinue ?? merged.onContinue,
|
package/core/api.js
CHANGED
|
@@ -77,6 +77,11 @@ export function createApi(baseUrl = DEFAULT_BASE, options = {}) {
|
|
|
77
77
|
return request('POST', '/signup/mobile/verify', { email, mobile, otp });
|
|
78
78
|
},
|
|
79
79
|
|
|
80
|
+
/** POST /signup/mobile/save — Save mobile without OTP verification (e.g. international numbers) */
|
|
81
|
+
async signupMobileSave(email, mobile) {
|
|
82
|
+
return request('POST', '/signup/mobile/save', { email, mobile });
|
|
83
|
+
},
|
|
84
|
+
|
|
80
85
|
/** POST /signup/password/set — Password step */
|
|
81
86
|
async signupPasswordSet(email, password) {
|
|
82
87
|
return request('POST', '/signup/password/set', { email, password });
|
package/package.json
CHANGED
package/signup.js
CHANGED
|
@@ -70,10 +70,20 @@ export function mount(container, options = {}) {
|
|
|
70
70
|
onContinue = null,
|
|
71
71
|
onSwitchToLogin = null,
|
|
72
72
|
onBack = null,
|
|
73
|
+
skipMobileOtp = false,
|
|
74
|
+
mobileVerification = 'india_only',
|
|
75
|
+
collectOrgDetails = true,
|
|
76
|
+
showOrgDetails,
|
|
73
77
|
} = options;
|
|
74
78
|
|
|
75
79
|
const flow = new FlowController();
|
|
76
80
|
const state = new StateManager();
|
|
81
|
+
state.set('skipMobileOtp', skipMobileOtp);
|
|
82
|
+
state.set('mobileVerification', mobileVerification);
|
|
83
|
+
state.set('collectOrgDetails', collectOrgDetails);
|
|
84
|
+
if (showOrgDetails !== undefined) {
|
|
85
|
+
state.set('showOrgDetails', showOrgDetails);
|
|
86
|
+
}
|
|
77
87
|
const api = createApi(apiBaseUrl);
|
|
78
88
|
|
|
79
89
|
const layout = createLayout(el, () => {});
|
|
@@ -172,11 +182,24 @@ export function mount(container, options = {}) {
|
|
|
172
182
|
}
|
|
173
183
|
}
|
|
174
184
|
|
|
175
|
-
async function handleMobileSubmit(mobile, channel = 'sms') {
|
|
185
|
+
async function handleMobileSubmit(mobile, channel = 'sms', opts = {}) {
|
|
176
186
|
state.clearError();
|
|
177
187
|
state.set('mobile', mobile);
|
|
178
188
|
state.set('mobileChannel', channel);
|
|
179
189
|
try {
|
|
190
|
+
const shouldSkipOtp = opts?.skipOtp ?? (
|
|
191
|
+
state.get('skipMobileOtp') === true ||
|
|
192
|
+
state.get('mobileVerification') === 'none' ||
|
|
193
|
+
(state.get('mobileVerification') !== 'all' && !mobile.startsWith('+91'))
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
if (shouldSkipOtp) {
|
|
197
|
+
// Skip OTP verification, save mobile directly and advance to password step
|
|
198
|
+
await api.signupMobileSave(state.get('email'), mobile);
|
|
199
|
+
flow.setStep(STEPS.PASSWORD);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
180
203
|
let res;
|
|
181
204
|
if (isSocial()) {
|
|
182
205
|
const token = state.get('loginToken');
|
|
@@ -189,7 +212,7 @@ export function mount(container, options = {}) {
|
|
|
189
212
|
if (res?.channel) state.set('mobileChannel', res.channel);
|
|
190
213
|
flow.goNext();
|
|
191
214
|
} catch (err) {
|
|
192
|
-
const msg = err?.data?.error || err?.message || 'Failed to
|
|
215
|
+
const msg = err?.data?.error || err?.message || 'Failed to submit mobile number';
|
|
193
216
|
state.set('error', msg);
|
|
194
217
|
showError(msg);
|
|
195
218
|
}
|
|
@@ -7,10 +7,54 @@ const MAIL_CHECK = 'M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2
|
|
|
7
7
|
const ARROW_LEFT = 'M19 12H5m7 7l-7-7 7-7';
|
|
8
8
|
const ARROW_RIGHT = 'M5 12h14m-7-7l7 7-7 7';
|
|
9
9
|
|
|
10
|
-
export function createEmailVerifyScreen(email, onNext, onBack, prefill = {}) {
|
|
10
|
+
export function createEmailVerifyScreen(email, onNext, onBack, prefill = {}, options = {}) {
|
|
11
11
|
const wrap = document.createElement('div');
|
|
12
12
|
wrap.className = 'wf-screen';
|
|
13
13
|
|
|
14
|
+
const orgOpt = options.collectOrgDetails !== undefined ? options.collectOrgDetails : options.showOrgDetails;
|
|
15
|
+
const showTeam = typeof orgOpt === 'object' && orgOpt !== null ? Boolean(orgOpt.team) : orgOpt !== false;
|
|
16
|
+
const showSubTeam = typeof orgOpt === 'object' && orgOpt !== null ? Boolean(orgOpt.subTeam) : orgOpt !== false;
|
|
17
|
+
const showDesignation = typeof orgOpt === 'object' && orgOpt !== null ? Boolean(orgOpt.designation) : orgOpt !== false;
|
|
18
|
+
|
|
19
|
+
let orgFieldsHtml = '';
|
|
20
|
+
if (showTeam && showSubTeam) {
|
|
21
|
+
orgFieldsHtml += `
|
|
22
|
+
<div class="wf-row-2">
|
|
23
|
+
<div class="wf-field">
|
|
24
|
+
<label class="wf-label" for="wf-team">Team</label>
|
|
25
|
+
<input type="text" id="wf-team" placeholder="Engineering" class="wf-input" autocomplete="organization" />
|
|
26
|
+
</div>
|
|
27
|
+
<div class="wf-field">
|
|
28
|
+
<label class="wf-label" for="wf-sub-team">Sub-team</label>
|
|
29
|
+
<input type="text" id="wf-sub-team" placeholder="Backend" class="wf-input" />
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
`;
|
|
33
|
+
} else if (showTeam) {
|
|
34
|
+
orgFieldsHtml += `
|
|
35
|
+
<div class="wf-field">
|
|
36
|
+
<label class="wf-label" for="wf-team">Team</label>
|
|
37
|
+
<input type="text" id="wf-team" placeholder="Engineering" class="wf-input" autocomplete="organization" />
|
|
38
|
+
</div>
|
|
39
|
+
`;
|
|
40
|
+
} else if (showSubTeam) {
|
|
41
|
+
orgFieldsHtml += `
|
|
42
|
+
<div class="wf-field">
|
|
43
|
+
<label class="wf-label" for="wf-sub-team">Sub-team</label>
|
|
44
|
+
<input type="text" id="wf-sub-team" placeholder="Backend" class="wf-input" />
|
|
45
|
+
</div>
|
|
46
|
+
`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (showDesignation) {
|
|
50
|
+
orgFieldsHtml += `
|
|
51
|
+
<div class="wf-field">
|
|
52
|
+
<label class="wf-label" for="wf-designation">Designation</label>
|
|
53
|
+
<input type="text" id="wf-designation" placeholder="Software Engineer" class="wf-input" autocomplete="organization-title" />
|
|
54
|
+
</div>
|
|
55
|
+
`;
|
|
56
|
+
}
|
|
57
|
+
|
|
14
58
|
const otpInputs = Array(6).fill(0).map((_, i) => `
|
|
15
59
|
<input type="text" inputmode="numeric" maxlength="1" class="wf-otp-input" id="wf-email-otp-${i}" data-idx="${i}" autocomplete="one-time-code" />
|
|
16
60
|
`).join('');
|
|
@@ -38,20 +82,7 @@ export function createEmailVerifyScreen(email, onNext, onBack, prefill = {}) {
|
|
|
38
82
|
<input type="text" id="wf-last-name" placeholder="Doe" class="wf-input" required autocomplete="family-name" />
|
|
39
83
|
</div>
|
|
40
84
|
</div>
|
|
41
|
-
|
|
42
|
-
<div class="wf-field">
|
|
43
|
-
<label class="wf-label" for="wf-team">Team</label>
|
|
44
|
-
<input type="text" id="wf-team" placeholder="Engineering" class="wf-input" autocomplete="organization" />
|
|
45
|
-
</div>
|
|
46
|
-
<div class="wf-field">
|
|
47
|
-
<label class="wf-label" for="wf-sub-team">Sub-team</label>
|
|
48
|
-
<input type="text" id="wf-sub-team" placeholder="Backend" class="wf-input" />
|
|
49
|
-
</div>
|
|
50
|
-
</div>
|
|
51
|
-
<div class="wf-field">
|
|
52
|
-
<label class="wf-label" for="wf-designation">Designation</label>
|
|
53
|
-
<input type="text" id="wf-designation" placeholder="Software Engineer" class="wf-input" autocomplete="organization-title" />
|
|
54
|
-
</div>
|
|
85
|
+
${orgFieldsHtml}
|
|
55
86
|
<div class="wf-btn-row">
|
|
56
87
|
<button type="button" class="wf-btn wf-btn-outline wf-btn-lg" data-action="back" aria-label="Go back">
|
|
57
88
|
<svg class="wf-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="${ARROW_LEFT}"/></svg>
|
package/ui/screens/index.js
CHANGED
|
@@ -44,11 +44,18 @@ export function createScreen(step, state, flow, callbacks) {
|
|
|
44
44
|
team: state.get('team') || '',
|
|
45
45
|
subTeam: state.get('subTeam') || '',
|
|
46
46
|
designation: state.get('designation') || '',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
collectOrgDetails: state.get('collectOrgDetails'),
|
|
50
|
+
showOrgDetails: state.get('showOrgDetails'),
|
|
47
51
|
}
|
|
48
52
|
);
|
|
49
53
|
|
|
50
54
|
case STEPS.MOBILE:
|
|
51
|
-
return createMobileEntryScreen(onMobileSubmit, onBack
|
|
55
|
+
return createMobileEntryScreen(onMobileSubmit, onBack, {
|
|
56
|
+
skipMobileOtp: state.get('skipMobileOtp'),
|
|
57
|
+
mobileVerification: state.get('mobileVerification'),
|
|
58
|
+
});
|
|
52
59
|
|
|
53
60
|
case STEPS.MOBILE_VERIFY:
|
|
54
61
|
return createMobileVerifyScreen(
|
|
@@ -23,7 +23,7 @@ const COUNTRY_CODES = [
|
|
|
23
23
|
{ code: "52", label: "+52" },
|
|
24
24
|
];
|
|
25
25
|
|
|
26
|
-
export function createMobileEntryScreen(onNext, onBack) {
|
|
26
|
+
export function createMobileEntryScreen(onNext, onBack, options = {}) {
|
|
27
27
|
const wrap = document.createElement("div");
|
|
28
28
|
wrap.className = "wf-screen";
|
|
29
29
|
|
|
@@ -37,7 +37,7 @@ export function createMobileEntryScreen(onNext, onBack) {
|
|
|
37
37
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="${PHONE_ICON}"/></svg>
|
|
38
38
|
</div>
|
|
39
39
|
<h2 class="wf-screen-title">Add your mobile number</h2>
|
|
40
|
-
<p class="wf-screen-desc">We will send a verification code to confirm this number</p>
|
|
40
|
+
<p class="wf-screen-desc" id="wf-mobile-desc">We will send a verification code to confirm this number</p>
|
|
41
41
|
</div>
|
|
42
42
|
<form class="wf-form" id="wf-mobile-form">
|
|
43
43
|
<div class="wf-field">
|
|
@@ -54,11 +54,11 @@ export function createMobileEntryScreen(onNext, onBack) {
|
|
|
54
54
|
<svg class="wf-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="${ARROW_LEFT}"/></svg>
|
|
55
55
|
</button>
|
|
56
56
|
<button type="submit" class="wf-btn wf-btn-primary wf-btn-lg wf-btn-flex" id="wf-btn-send-sms">
|
|
57
|
-
Send Code
|
|
57
|
+
<span id="wf-mobile-btn-text">Send Code</span>
|
|
58
58
|
<svg class="wf-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="${ARROW_RIGHT}"/></svg>
|
|
59
59
|
</button>
|
|
60
60
|
</div>
|
|
61
|
-
<div class="wf-channel-alt">
|
|
61
|
+
<div class="wf-channel-alt" id="wf-mobile-channel-alt">
|
|
62
62
|
<div class="wf-divider-text"><span>or</span></div>
|
|
63
63
|
<button type="button" class="wf-btn wf-btn-outline wf-btn-whatsapp-outline wf-btn-lg wf-btn-full" id="wf-btn-send-wa">
|
|
64
64
|
<svg class="wf-icon-sm" viewBox="0 0 24 24" fill="currentColor"><path d="${WHATSAPP_ICON}"/></svg>
|
|
@@ -71,14 +71,55 @@ export function createMobileEntryScreen(onNext, onBack) {
|
|
|
71
71
|
const form = wrap.querySelector("#wf-mobile-form");
|
|
72
72
|
const countrySelect = wrap.querySelector("#wf-mobile-country");
|
|
73
73
|
const input = wrap.querySelector("#wf-mobile");
|
|
74
|
+
const descEl = wrap.querySelector("#wf-mobile-desc");
|
|
75
|
+
const btnTextEl = wrap.querySelector("#wf-mobile-btn-text");
|
|
76
|
+
const channelAlt = wrap.querySelector("#wf-mobile-channel-alt");
|
|
74
77
|
const waBtn = wrap.querySelector("#wf-btn-send-wa");
|
|
75
78
|
|
|
79
|
+
function isSkipOtp() {
|
|
80
|
+
if (options.skipMobileOtp === true || options.mobileVerification === "none") {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
if (options.mobileVerification === "all") {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
// Default: india_only -> skip OTP for non-+91 numbers
|
|
87
|
+
return countrySelect.value !== "91";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function updateUiState() {
|
|
91
|
+
const skip = isSkipOtp();
|
|
92
|
+
if (skip) {
|
|
93
|
+
if (descEl) descEl.textContent = "Enter your mobile number to continue";
|
|
94
|
+
if (btnTextEl) btnTextEl.textContent = "Continue";
|
|
95
|
+
if (channelAlt) channelAlt.style.display = "none";
|
|
96
|
+
} else {
|
|
97
|
+
if (descEl) descEl.textContent = "We will send a verification code to confirm this number";
|
|
98
|
+
if (btnTextEl) btnTextEl.textContent = "Send Code";
|
|
99
|
+
if (channelAlt) channelAlt.style.display = "";
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
countrySelect.addEventListener("change", updateUiState);
|
|
104
|
+
updateUiState();
|
|
105
|
+
|
|
106
|
+
input.addEventListener("input", () => {
|
|
107
|
+
input.setCustomValidity("");
|
|
108
|
+
});
|
|
109
|
+
|
|
76
110
|
function submitMobile(channel = "sms") {
|
|
77
111
|
const digits = input.value.trim().replace(/\D/g, "");
|
|
78
|
-
|
|
112
|
+
const minLen = countrySelect.value === "91" ? 10 : 7;
|
|
113
|
+
if (digits.length >= minLen && digits.length <= 15) {
|
|
79
114
|
const full = "+" + countrySelect.value + digits;
|
|
80
|
-
|
|
115
|
+
const skipOtp = isSkipOtp();
|
|
116
|
+
onNext(full, channel, { skipOtp });
|
|
81
117
|
} else {
|
|
118
|
+
input.setCustomValidity(
|
|
119
|
+
digits.length < minLen
|
|
120
|
+
? `Please enter a valid phone number (at least ${minLen} digits)`
|
|
121
|
+
: "Phone number cannot exceed 15 digits"
|
|
122
|
+
);
|
|
82
123
|
input.focus();
|
|
83
124
|
if (typeof input.reportValidity === "function") {
|
|
84
125
|
input.reportValidity();
|