@zwayam/apply-experience-library 0.1.2 → 0.1.4

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/README.md CHANGED
@@ -1,94 +1,291 @@
1
- # Apply Experience Web SDK
1
+ # Apply Experience Library
2
2
 
3
- Apply Experience is a reusable Add Profile widget for recruiter/admin web apps. It provides the popup UI for resume upload, candidate field capture, validations, phone fields, source/sub-source selection, and final profile submission flow.
3
+ `@zwayam/apply-experience-library` is a host-facing SDK for launching **Add Profile** and **Edit Profile** popups inside recruiter or admin web applications.
4
4
 
5
- This repository root is installable directly from GitHub. The host app provides job/user context and API functions; the SDK handles the Add Profile experience.
5
+ It is designed for hosts that already own authentication, API orchestration, and business rules, but want a reusable profile collection UI.
6
6
 
7
- ## Install From GitHub
7
+ ## What the SDK Handles
8
8
 
9
- ```bash
10
- npm install github:YOUR_GITHUB_USERNAME/YOUR_REPO_NAME#main
11
- ```
9
+ - resume upload and resume parsing flow
10
+ - manual profile creation flow
11
+ - add profile and edit profile popup UI
12
+ - dynamic field rendering from backend `applyFields`
13
+ - required validation, pattern validation, dependent field visibility
14
+ - country code + phone input handling
15
+ - source / source type / sub-source flow
16
+ - optional location autosuggestions
17
+ - final submit flow and success callbacks
18
+
19
+ ## What the Host App Handles
20
+
21
+ - authentication and session
22
+ - current job and user context
23
+ - backend API calls
24
+ - route-level state management
25
+ - refresh / navigation after success
12
26
 
13
- Or install a tagged version:
27
+ ## Install
14
28
 
15
29
  ```bash
16
- npm install github:YOUR_GITHUB_USERNAME/YOUR_REPO_NAME#v0.2.1
30
+ npm install @zwayam/apply-experience-library
17
31
  ```
18
32
 
19
- Import styles once:
33
+ Import styles once in your host app:
20
34
 
21
- ```js
35
+ ```ts
22
36
  import "@zwayam/apply-experience-library/styles.css";
23
37
  ```
24
38
 
25
- ## Required Inputs
39
+ ## Exported API
26
40
 
27
- The host app must provide:
41
+ The package exposes:
28
42
 
29
- - `container`: DOM element where the popup will mount.
30
- - `context`: current job, logged-in user, session, and optional environment data.
31
- - `api`: host-owned functions for loading config, parsing resume, submitting profile, and optional location suggestions.
43
+ - `mountAddProfile(options)`
44
+ - `mountEditProfile(options)`
45
+ - `registerAddProfileElement(options)`
46
+ - `registerEditProfileElement(options)`
32
47
 
33
- ```js
34
- const context = {
48
+ Use `mount*` for direct framework integration. Use `register*Element` if you prefer custom elements.
49
+
50
+ ## How It Is Built
51
+
52
+ The SDK is made in two layers:
53
+
54
+ 1. `packages/react`
55
+ - React implementation of Add Profile / Edit Profile
56
+ - field rendering, validations, workflow logic
57
+
58
+ 2. `packages/web`
59
+ - bundled host-facing package
60
+ - exports mount helpers that work in Angular, React, Vue, and vanilla JS
61
+
62
+ This means host apps do not need to know the internal React implementation details. They only provide `context`, `api`, and a DOM `container`.
63
+
64
+ ## Add Profile Context
65
+
66
+ ```ts
67
+ type AddProfileHostContext = {
68
+ job: {
69
+ id: string;
70
+ companyId: number | string;
71
+ departmentId?: number | string;
72
+ jobTitle: string;
73
+ jobCode: string;
74
+ location?: string;
75
+ minYearOfExperience?: number;
76
+ maxYearOfExperience?: number;
77
+ departmentName?: string;
78
+ };
79
+ user: {
80
+ id: string | number;
81
+ email: string;
82
+ name: string;
83
+ uuid?: string;
84
+ };
85
+ session: {
86
+ sessionId: string;
87
+ };
88
+ environment?: Record<string, string>;
89
+ };
90
+ ```
91
+
92
+ ### Example
93
+
94
+ ```ts
95
+ const addProfileContext = {
35
96
  job: {
36
97
  id: "263294",
37
98
  companyId: 16136,
99
+ departmentId: 12,
38
100
  jobTitle: "Java Developer",
39
- jobCode: "13949",
40
- location: "Kolkata",
41
- departmentName: "All Departments",
101
+ jobCode: "16667",
102
+ location: "Pune, Maharashtra, India",
103
+ minYearOfExperience: 2,
104
+ maxYearOfExperience: 5,
105
+ departmentName: "Engineering",
42
106
  },
43
107
  user: {
44
108
  id: 286813,
45
- email: "user@example.com",
109
+ email: "recruiter@example.com",
46
110
  name: "Recruiter Name",
111
+ uuid: "host-user-uuid",
47
112
  },
48
113
  session: {
49
114
  sessionId: "SESSION_ID",
50
115
  },
116
+ environment: {
117
+ tenant: "prod",
118
+ },
51
119
  };
120
+ ```
52
121
 
53
- const api = {
54
- loadInitialData: async () => ({
55
- applyFields: [],
122
+ ## Edit Profile Context
123
+
124
+ Edit Profile uses everything from Add Profile, plus:
125
+
126
+ ```ts
127
+ type EditProfileHostContext = AddProfileHostContext & {
128
+ applyId: string | number;
129
+ application?: Record<string, unknown>;
130
+ };
131
+ ```
132
+
133
+ ### Example
134
+
135
+ ```ts
136
+ const editProfileContext = {
137
+ ...addProfileContext,
138
+ applyId: 987654,
139
+ application: {
140
+ applyId: 987654,
141
+ candidateId: 12345,
142
+ },
143
+ };
144
+ ```
145
+
146
+ ## Add Profile API Contract
147
+
148
+ ```ts
149
+ type AddProfileApi = {
150
+ loadInitialData(
151
+ context: AddProfileHostContext,
152
+ ): Promise<{
153
+ applyFields: AddProfileFieldDefinition[];
56
154
  supportedFiles: {
57
- addProfile: {
58
- fileFormat: ["pdf", "doc", "docx"],
59
- fileSize: 3,
60
- },
61
- },
62
- profileSources: [],
155
+ addProfile?: {
156
+ fileFormat: string[];
157
+ fileSize?: number;
158
+ };
159
+ };
160
+ profileSources: Array<{
161
+ sourceOfProfile?: string;
162
+ profileSource: string;
163
+ subSourceValues?: string | null;
164
+ }>;
63
165
  phoneConfiguration: {
64
- countryIsoCode: "IN",
65
- whatsAppEnabled: true,
66
- maxCount: 3,
67
- },
68
- }),
166
+ countryIsoCode: string | null;
167
+ whatsAppEnabled: boolean;
168
+ maxCount: number;
169
+ };
170
+ resumeOptional?: boolean;
171
+ }>;
172
+
173
+ parseResume(
174
+ file: File,
175
+ context: AddProfileHostContext,
176
+ ): Promise<{
177
+ responseCode?: number;
178
+ reponseObject?: {
179
+ parsedObjectId?: string;
180
+ parsedResult?: {
181
+ profile?: Record<string, unknown>;
182
+ };
183
+ jobApplication?: Record<string, unknown>;
184
+ };
185
+ }>;
186
+
187
+ submitProfile(
188
+ payload: FormData,
189
+ context: AddProfileHostContext,
190
+ ): Promise<{
191
+ code?: number;
192
+ message?: string;
193
+ [key: string]: unknown;
194
+ }>;
195
+
196
+ getLocationSuggestions?(
197
+ query: string,
198
+ context: AddProfileHostContext,
199
+ ): Promise<string[]>;
200
+ };
201
+ ```
69
202
 
70
- parseResume: async (file) => {
71
- const formData = new FormData();
72
- formData.append("file", file);
73
- return fetch("/parse-resume", { method: "POST", body: formData }).then((res) => res.json());
74
- },
203
+ ## Edit Profile API Contract
75
204
 
76
- submitProfile: async (payload) => {
77
- return fetch("/add-profile", { method: "POST", body: payload }).then((res) => res.json());
78
- },
205
+ ```ts
206
+ type EditProfileApi = {
207
+ loadInitialData(
208
+ context: EditProfileHostContext,
209
+ ): Promise<{
210
+ jobApplication: Record<string, unknown>;
211
+ applyFieldsObject: Record<string, unknown>;
212
+ applyFields: AddProfileFieldDefinition[];
213
+ phoneConfiguration: {
214
+ countryIsoCode: string | null;
215
+ whatsAppEnabled: boolean;
216
+ maxCount: number;
217
+ };
218
+ phoneNumbers?: Array<Record<string, unknown>>;
219
+ supportedFiles?: {
220
+ addProfile?: {
221
+ fileFormat: string[];
222
+ fileSize?: number;
223
+ };
224
+ };
225
+ }>;
226
+
227
+ submitProfile(
228
+ payload: Record<string, unknown>,
229
+ context: EditProfileHostContext,
230
+ ): Promise<{
231
+ code?: number;
232
+ message?: string;
233
+ [key: string]: unknown;
234
+ }>;
235
+
236
+ getLocationSuggestions?(
237
+ query: string,
238
+ context: EditProfileHostContext,
239
+ ): Promise<string[]>;
79
240
  };
80
241
  ```
81
242
 
82
- ## React
243
+ ## Field Definitions
244
+
245
+ The SDK renders fields from backend `applyFields`.
246
+
247
+ Common field types include:
248
+
249
+ - `T` for text-like inputs
250
+ - `D` for searchable dropdowns
251
+ - `date`
252
+ - `phone`
253
+ - `checkbox`
254
+ - `location`
255
+ - `email`
256
+ - `consent`
257
+
258
+ Notes:
259
+
260
+ - `field.errorMessage` is used as first priority for validation messages when present.
261
+ - Generated SDK validation messages are used as fallback when API does not provide one.
262
+ - Currency formatting follows field metadata type such as `INRCURRENCY` / `CURRENCY`, matching host behavior.
263
+
264
+ ## React Usage
83
265
 
84
- ```jsx
266
+ ```tsx
85
267
  import { useEffect, useRef } from "react";
86
- import { mountAddProfile } from "@zwayam/apply-experience-library";
268
+ import {
269
+ mountAddProfile,
270
+ type MountedAddProfile,
271
+ type AddProfileApi,
272
+ type AddProfileHostContext,
273
+ } from "@zwayam/apply-experience-library";
87
274
  import "@zwayam/apply-experience-library/styles.css";
88
275
 
89
- export function AddProfile({ open, context, api, onClose }) {
90
- const rootRef = useRef(null);
91
- const mountedRef = useRef(null);
276
+ export function AddProfileLauncher({
277
+ open,
278
+ context,
279
+ api,
280
+ onClose,
281
+ }: {
282
+ open: boolean;
283
+ context: AddProfileHostContext;
284
+ api: AddProfileApi;
285
+ onClose(): void;
286
+ }) {
287
+ const rootRef = useRef<HTMLDivElement | null>(null);
288
+ const mountedRef = useRef<MountedAddProfile | null>(null);
92
289
 
93
290
  useEffect(() => {
94
291
  if (!open || !rootRef.current) return;
@@ -97,8 +294,12 @@ export function AddProfile({ open, context, api, onClose }) {
97
294
  container: rootRef.current,
98
295
  context,
99
296
  api,
297
+ title: "Add Profile",
100
298
  onClose,
101
- onSuccess: onClose,
299
+ onSuccess: () => {
300
+ mountedRef.current?.unmount();
301
+ onClose();
302
+ },
102
303
  });
103
304
 
104
305
  return () => mountedRef.current?.unmount();
@@ -108,55 +309,72 @@ export function AddProfile({ open, context, api, onClose }) {
108
309
  }
109
310
  ```
110
311
 
111
- ## Angular
312
+ ## Angular Usage
112
313
 
113
- Import styles in a global stylesheet:
314
+ Import styles once in a global stylesheet:
114
315
 
115
316
  ```scss
116
317
  @import "@zwayam/apply-experience-library/styles.css";
117
318
  ```
118
319
 
119
- Mount from a component:
320
+ Mount from a component or service:
120
321
 
121
322
  ```ts
122
- import { mountAddProfile, MountedAddProfile } from "@zwayam/apply-experience-library";
323
+ import {
324
+ mountAddProfile,
325
+ type MountedAddProfile,
326
+ type AddProfileApi,
327
+ type AddProfileHostContext,
328
+ } from "@zwayam/apply-experience-library";
123
329
 
124
330
  private mountedAddProfile?: MountedAddProfile;
125
331
 
126
- openAddProfile(container: Element, context: any, api: any) {
332
+ openAddProfile(container: Element, context: AddProfileHostContext, api: AddProfileApi) {
127
333
  this.mountedAddProfile = mountAddProfile({
128
334
  container,
129
335
  context,
130
336
  api,
337
+ title: "Add Profile",
131
338
  onClose: () => this.mountedAddProfile?.unmount(),
132
339
  onSuccess: () => {
133
340
  this.mountedAddProfile?.unmount();
134
- // refresh host data if needed
341
+ // refresh host data here
135
342
  },
136
343
  });
137
344
  }
138
345
  ```
139
346
 
140
- ## Vue
347
+ ## Vue Usage
141
348
 
142
349
  ```vue
143
- <script setup>
144
- import { onBeforeUnmount, onMounted, ref } from "vue";
350
+ <script setup lang="ts">
351
+ import { onBeforeUnmount, watch, ref } from "vue";
145
352
  import { mountAddProfile } from "@zwayam/apply-experience-library";
146
353
  import "@zwayam/apply-experience-library/styles.css";
147
354
 
148
- const root = ref(null);
149
- let mounted;
150
-
151
- onMounted(() => {
152
- mounted = mountAddProfile({
153
- container: root.value,
154
- context,
155
- api,
156
- onClose: () => mounted?.unmount(),
157
- onSuccess: () => mounted?.unmount(),
158
- });
159
- });
355
+ const props = defineProps<{
356
+ open: boolean;
357
+ context: any;
358
+ api: any;
359
+ }>();
360
+
361
+ const root = ref<HTMLElement | null>(null);
362
+ let mounted: ReturnType<typeof mountAddProfile> | null = null;
363
+
364
+ watch(
365
+ () => props.open,
366
+ (open) => {
367
+ if (!open || !root.value) return;
368
+
369
+ mounted = mountAddProfile({
370
+ container: root.value,
371
+ context: props.context,
372
+ api: props.api,
373
+ onClose: () => mounted?.unmount(),
374
+ onSuccess: () => mounted?.unmount(),
375
+ });
376
+ },
377
+ );
160
378
 
161
379
  onBeforeUnmount(() => mounted?.unmount());
162
380
  </script>
@@ -166,7 +384,7 @@ onBeforeUnmount(() => mounted?.unmount());
166
384
  </template>
167
385
  ```
168
386
 
169
- ## Vanilla JavaScript
387
+ ## Vanilla JavaScript Usage
170
388
 
171
389
  ```html
172
390
  <button id="open-add-profile">Add Profile</button>
@@ -177,28 +395,92 @@ onBeforeUnmount(() => mounted?.unmount());
177
395
  import { mountAddProfile } from "@zwayam/apply-experience-library";
178
396
  import "@zwayam/apply-experience-library/styles.css";
179
397
 
180
- document.querySelector("#open-add-profile").addEventListener("click", () => {
398
+ document.getElementById("open-add-profile").addEventListener("click", () => {
181
399
  const mounted = mountAddProfile({
182
- container: document.querySelector("#add-profile-root"),
183
- context,
184
- api,
400
+ container: document.getElementById("add-profile-root"),
401
+ context: addProfileContext,
402
+ api: addProfileApi,
185
403
  onClose: () => mounted.unmount(),
186
404
  onSuccess: () => mounted.unmount(),
187
405
  });
188
406
  });
189
407
  ```
190
408
 
409
+ ## Custom Element Usage
410
+
411
+ ```ts
412
+ import {
413
+ registerAddProfileElement,
414
+ registerEditProfileElement,
415
+ } from "@zwayam/apply-experience-library";
416
+
417
+ registerAddProfileElement({
418
+ getApi: () => addProfileApi,
419
+ getContext: () => addProfileContext,
420
+ });
421
+
422
+ registerEditProfileElement({
423
+ getApi: () => editProfileApi,
424
+ getContext: () => editProfileContext,
425
+ });
426
+ ```
427
+
428
+ Then use:
429
+
430
+ ```html
431
+ <apply-experience-add-profile></apply-experience-add-profile>
432
+ <apply-experience-edit-profile></apply-experience-edit-profile>
433
+ ```
434
+
435
+ ## Example API Object
436
+
437
+ ```ts
438
+ const addProfileApi = {
439
+ loadInitialData: async (context) => {
440
+ const response = await fetch(`/api/apply-fields?jobId=${context.job.id}`);
441
+ return response.json();
442
+ },
443
+
444
+ parseResume: async (file, context) => {
445
+ const formData = new FormData();
446
+ formData.append("file", file);
447
+ formData.append("sessionId", context.session.sessionId);
448
+ const response = await fetch("/api/parse-resume", {
449
+ method: "POST",
450
+ body: formData,
451
+ });
452
+ return response.json();
453
+ },
454
+
455
+ submitProfile: async (payload, context) => {
456
+ payload.append("sessionId", context.session.sessionId);
457
+ const response = await fetch("/api/add-profile", {
458
+ method: "POST",
459
+ body: payload,
460
+ });
461
+ return response.json();
462
+ },
463
+
464
+ getLocationSuggestions: async (query) => {
465
+ if (!query.trim()) return [];
466
+ const response = await fetch(`/api/location-suggestions?q=${encodeURIComponent(query)}`);
467
+ const data = await response.json();
468
+ return data.suggestions || [];
469
+ },
470
+ };
471
+ ```
472
+
191
473
  ## Development
192
474
 
193
475
  ```bash
194
476
  npm install
195
477
  npm run build
478
+ npm run playground
196
479
  ```
197
480
 
198
- The GitHub-installed package uses the built files from `packages/web/dist`, so keep `dist` committed when using GitHub installs.
199
-
200
- ## Notes
481
+ ## Publish
201
482
 
202
- - The host app owns authentication, session handling, and API endpoints.
203
- - The SDK should be unmounted when the popup closes or the host component is destroyed.
204
- - The root package is configured for GitHub installation and exports the bundled web SDK.
483
+ ```bash
484
+ npm run build
485
+ npm publish --access public
486
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zwayam/apply-experience-library",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Host-facing Apply Experience SDK for Angular, React, Vue, and other frameworks.",
5
5
  "type": "module",
6
6
  "main": "./packages/web/dist/index.js",