central-auth-js-sdk 1.2.9 → 1.2.10

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/core/api.js CHANGED
@@ -82,6 +82,18 @@ export function createApi(baseUrl = DEFAULT_BASE, options = {}) {
82
82
  return request('POST', '/signup/mobile/save', { email, mobile });
83
83
  },
84
84
 
85
+ /** POST /signup/org/details — Save team, sub-team, designation for user (including social login) */
86
+ async signupSaveOrgDetails({ loginToken, email, userId, team, sub_team, designation }) {
87
+ const payload = {};
88
+ if (loginToken) payload.login_token = loginToken;
89
+ if (email) payload.email = email;
90
+ if (userId) payload.user_id = userId;
91
+ if (team !== undefined) payload.team = team;
92
+ if (sub_team !== undefined) payload.sub_team = sub_team;
93
+ if (designation !== undefined) payload.designation = designation;
94
+ return request('POST', '/signup/org/details', payload);
95
+ },
96
+
85
97
  /** POST /signup/password/set — Password step */
86
98
  async signupPasswordSet(email, password) {
87
99
  return request('POST', '/signup/password/set', { email, password });
package/core/flow.js CHANGED
@@ -8,6 +8,7 @@ export const STEPS = {
8
8
  EMAIL_VERIFY: 'emailVerify',
9
9
  MOBILE: 'mobile',
10
10
  MOBILE_VERIFY: 'mobileVerify',
11
+ ORG_DETAILS: 'orgDetails',
11
12
  PASSWORD: 'password',
12
13
  FACE: 'face',
13
14
  SUCCESS: 'success',
@@ -32,6 +33,7 @@ const STEP_ORDER = {
32
33
  STEPS.EMAIL,
33
34
  STEPS.MOBILE,
34
35
  STEPS.MOBILE_VERIFY,
36
+ STEPS.ORG_DETAILS,
35
37
  STEPS.FACE,
36
38
  STEPS.SUCCESS,
37
39
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "central-auth-js-sdk",
3
- "version": "1.2.9",
3
+ "version": "1.2.10",
4
4
  "description": "Vanilla JS sign-up and login flow SDK — embeddable auth UI with configurable methods",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/signup.js CHANGED
@@ -194,9 +194,14 @@ export function mount(container, options = {}) {
194
194
  );
195
195
 
196
196
  if (shouldSkipOtp) {
197
- // Skip OTP verification, save mobile directly and advance to password step
197
+ // Skip OTP verification, save mobile directly and advance
198
198
  await api.signupMobileSave(state.get('email'), mobile);
199
- flow.setStep(STEPS.PASSWORD);
199
+ if (isSocial()) {
200
+ const skipOrg = state.get('collectOrgDetails') === false || state.get('showOrgDetails') === false;
201
+ flow.setStep(skipOrg ? (enableFaceRegistration ? STEPS.FACE : STEPS.SUCCESS) : STEPS.ORG_DETAILS);
202
+ } else {
203
+ flow.setStep(STEPS.PASSWORD);
204
+ }
200
205
  return;
201
206
  }
202
207
 
@@ -246,6 +251,10 @@ export function mount(container, options = {}) {
246
251
  const mobile = state.get('mobile');
247
252
  if (provider === 'microsoft') await api.microsoftMobileVerify(token, mobile, otp, appIdentifier);
248
253
  else await api.googleMobileVerify(token, mobile, otp, appIdentifier);
254
+
255
+ const skipOrg = state.get('collectOrgDetails') === false || state.get('showOrgDetails') === false;
256
+ flow.setStep(skipOrg ? (enableFaceRegistration ? STEPS.FACE : STEPS.SUCCESS) : STEPS.ORG_DETAILS);
257
+ return;
249
258
  } else {
250
259
  await api.signupMobileVerify(state.get('email'), state.get('mobile'), otp);
251
260
  }
@@ -257,6 +266,34 @@ export function mount(container, options = {}) {
257
266
  }
258
267
  }
259
268
 
269
+ async function handleOrgDetailsSubmit(data) {
270
+ state.clearError();
271
+ state.setMultiple({
272
+ team: data.team,
273
+ subTeam: data.subTeam,
274
+ designation: data.designation,
275
+ });
276
+ try {
277
+ await api.signupSaveOrgDetails({
278
+ loginToken: state.get('loginToken'),
279
+ email: state.get('email'),
280
+ userId: state.get('userId'),
281
+ team: data.team,
282
+ sub_team: data.subTeam,
283
+ designation: data.designation,
284
+ });
285
+ if (enableFaceRegistration) {
286
+ flow.setStep(STEPS.FACE);
287
+ } else {
288
+ flow.setStep(STEPS.SUCCESS);
289
+ }
290
+ } catch (err) {
291
+ const msg = err?.data?.error || err?.message || 'Failed to save organization details';
292
+ state.set('error', msg);
293
+ showError(msg);
294
+ }
295
+ }
296
+
260
297
  async function handlePasswordSubmit(password) {
261
298
  state.clearError();
262
299
  state.set('password', password);
@@ -330,6 +367,7 @@ export function mount(container, options = {}) {
330
367
  onMobileSubmit: handleMobileSubmit,
331
368
  onMobileVerify: handleMobileVerify,
332
369
  onMobileResend: handleMobileResend,
370
+ onOrgDetailsSubmit: handleOrgDetailsSubmit,
333
371
  onPasswordSubmit: handlePasswordSubmit,
334
372
  onFaceSubmit: handleFaceSubmit,
335
373
  onBack: () => {
@@ -9,6 +9,7 @@ import { createEmailVerifyScreen } from './email-verify.js';
9
9
  import { createMobileEntryScreen } from './mobile-entry.js';
10
10
  import { createMobileVerifyScreen } from './mobile-verify.js';
11
11
  import { createPasswordSetupScreen } from './password-setup.js';
12
+ import { createOrgDetailsScreen } from './org-details.js';
12
13
  import { createFaceRegistrationScreen } from './face-registration.js';
13
14
  import { createSuccessScreen } from './success.js';
14
15
 
@@ -19,6 +20,7 @@ export function createScreen(step, state, flow, callbacks) {
19
20
  onMobileSubmit,
20
21
  onMobileVerify,
21
22
  onMobileResend,
23
+ onOrgDetailsSubmit,
22
24
  onPasswordSubmit,
23
25
  onFaceSubmit,
24
26
  onBack,
@@ -68,6 +70,21 @@ export function createScreen(step, state, flow, callbacks) {
68
70
  }
69
71
  );
70
72
 
73
+ case STEPS.ORG_DETAILS:
74
+ return createOrgDetailsScreen(
75
+ (data) => onOrgDetailsSubmit(data),
76
+ onBack,
77
+ {
78
+ team: state.get('team') || '',
79
+ subTeam: state.get('subTeam') || '',
80
+ designation: state.get('designation') || '',
81
+ },
82
+ {
83
+ collectOrgDetails: state.get('collectOrgDetails'),
84
+ showOrgDetails: state.get('showOrgDetails'),
85
+ }
86
+ );
87
+
71
88
  case STEPS.PASSWORD:
72
89
  return createPasswordSetupScreen(onPasswordSubmit, onBack);
73
90
 
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Organization Details Screen — Team, Sub-team & Designation
3
+ * v2.0 — warm, modern design for onboarding & social signup
4
+ */
5
+
6
+ const BRIEFCASE_ICON = 'M20 7h-4V4c0-1.1-.9-2-2-2h-4c-1.1 0-2 .9-2 2v3H4c-1.1 0-2 .9-2 2v11c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2zM10 4h4v3h-4V4zm10 16H4V9h16v11z';
7
+ const ARROW_LEFT = 'M19 12H5m7 7l-7-7 7-7';
8
+ const ARROW_RIGHT = 'M5 12h14m-7-7l7 7-7 7';
9
+
10
+ export function createOrgDetailsScreen(onNext, onBack, prefill = {}, options = {}) {
11
+ const wrap = document.createElement('div');
12
+ wrap.className = 'wf-screen';
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 fieldsHtml = '';
20
+ if (showTeam && showSubTeam) {
21
+ fieldsHtml += `
22
+ <div class="wf-row-2">
23
+ <div class="wf-field">
24
+ <label class="wf-label" for="wf-org-team">Team</label>
25
+ <input type="text" id="wf-org-team" placeholder="e.g. Engineering" class="wf-input" autocomplete="organization" />
26
+ </div>
27
+ <div class="wf-field">
28
+ <label class="wf-label" for="wf-org-subteam">Sub-team</label>
29
+ <input type="text" id="wf-org-subteam" placeholder="e.g. Backend" class="wf-input" />
30
+ </div>
31
+ </div>
32
+ `;
33
+ } else if (showTeam) {
34
+ fieldsHtml += `
35
+ <div class="wf-field">
36
+ <label class="wf-label" for="wf-org-team">Team</label>
37
+ <input type="text" id="wf-org-team" placeholder="e.g. Engineering" class="wf-input" autocomplete="organization" />
38
+ </div>
39
+ `;
40
+ } else if (showSubTeam) {
41
+ fieldsHtml += `
42
+ <div class="wf-field">
43
+ <label class="wf-label" for="wf-org-subteam">Sub-team</label>
44
+ <input type="text" id="wf-org-subteam" placeholder="e.g. Backend" class="wf-input" />
45
+ </div>
46
+ `;
47
+ }
48
+
49
+ if (showDesignation) {
50
+ fieldsHtml += `
51
+ <div class="wf-field">
52
+ <label class="wf-label" for="wf-org-designation">Designation</label>
53
+ <input type="text" id="wf-org-designation" placeholder="e.g. Software Engineer" class="wf-input" autocomplete="organization-title" />
54
+ </div>
55
+ `;
56
+ }
57
+
58
+ wrap.innerHTML = `
59
+ <div class="wf-screen-header">
60
+ <div class="wf-icon-box">
61
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="${BRIEFCASE_ICON}"/></svg>
62
+ </div>
63
+ <h2 class="wf-screen-title">Complete your profile</h2>
64
+ <p class="wf-screen-desc">Add your team and role details to personalize your workspace</p>
65
+ </div>
66
+ <form class="wf-form" id="wf-org-form">
67
+ ${fieldsHtml}
68
+ <div class="wf-btn-row">
69
+ <button type="button" class="wf-btn wf-btn-outline wf-btn-lg" data-action="back" aria-label="Go back">
70
+ <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>
71
+ </button>
72
+ <button type="submit" class="wf-btn wf-btn-primary wf-btn-lg wf-btn-flex" id="wf-btn-org-submit">
73
+ Continue
74
+ <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>
75
+ </button>
76
+ </div>
77
+ </form>
78
+ `;
79
+
80
+ const form = wrap.querySelector('#wf-org-form');
81
+ const teamInput = wrap.querySelector('#wf-org-team');
82
+ const subTeamInput = wrap.querySelector('#wf-org-subteam');
83
+ const designationInput = wrap.querySelector('#wf-org-designation');
84
+
85
+ if (prefill.team && teamInput) teamInput.value = prefill.team;
86
+ if (prefill.subTeam && subTeamInput) subTeamInput.value = prefill.subTeam;
87
+ if (prefill.designation && designationInput) designationInput.value = prefill.designation;
88
+
89
+ form.addEventListener('submit', (e) => {
90
+ e.preventDefault();
91
+ onNext({
92
+ team: teamInput ? teamInput.value.trim() : '',
93
+ subTeam: subTeamInput ? subTeamInput.value.trim() : '',
94
+ designation: designationInput ? designationInput.value.trim() : '',
95
+ });
96
+ });
97
+
98
+ const backBtn = wrap.querySelector('button[data-action="back"]');
99
+ if (backBtn && typeof onBack === 'function') {
100
+ backBtn.addEventListener('click', onBack);
101
+ }
102
+
103
+ return wrap;
104
+ }