Skip to content

Commit 489601a

Browse files
authored
New Components - gem (#15763)
* gem init * [Components] gem #15752 Sources - New Candidate Actions - Create Candidate * pnpm update * pnpm update * pnpm update * some adjusts * fix field name * some adjusts * some adjusts
1 parent 03b3933 commit 489601a

File tree

8 files changed

+599
-8
lines changed

8 files changed

+599
-8
lines changed
Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
import { SOURCED_FROM } from "../../common/constants.mjs";
2+
import { parseObject } from "../../common/utils.mjs";
3+
import gem from "../../gem.app.mjs";
4+
5+
export default {
6+
key: "gem-create-candidate",
7+
name: "Create Candidate",
8+
description: "Creates a new candidate in Gem. [See the documentation](https://api.gem.com/v0/reference#tag/Candidates/paths/~1v0~1candidates/post)",
9+
version: "0.0.1",
10+
type: "action",
11+
props: {
12+
gem,
13+
createdBy: {
14+
propDefinition: [
15+
gem,
16+
"createdBy",
17+
],
18+
},
19+
firstName: {
20+
type: "string",
21+
label: "First Name",
22+
description: "Candidate's first name",
23+
optional: true,
24+
},
25+
lastName: {
26+
type: "string",
27+
label: "Last Name",
28+
description: "Candidate's last name",
29+
optional: true,
30+
},
31+
nickname: {
32+
type: "string",
33+
label: "Nickname",
34+
description: "Candidate's nickname",
35+
optional: true,
36+
},
37+
primaryEmail: {
38+
type: "string",
39+
label: "Primary Email Address",
40+
description: "Candidate's primary email address",
41+
},
42+
additionalEmails: {
43+
type: "string[]",
44+
label: "Email Addresses",
45+
description: "List of candidate's additional email addresses",
46+
optional: true,
47+
},
48+
linkedInHandle: {
49+
type: "string",
50+
label: "LinkedIn Handle",
51+
description: "Enter your candidate's unique LinkedIn identifier (e.g., \"satyanadella\"). This helps the system check for duplicates before creating a new candidate entry.",
52+
optional: true,
53+
},
54+
title: {
55+
type: "string",
56+
label: "Title",
57+
description: "Candidate's job title",
58+
optional: true,
59+
},
60+
company: {
61+
type: "string",
62+
label: "Company",
63+
description: "Candidate's company name",
64+
optional: true,
65+
},
66+
location: {
67+
type: "string",
68+
label: "Location",
69+
description: "Candidate's location",
70+
optional: true,
71+
},
72+
school: {
73+
type: "string",
74+
label: "School",
75+
description: "Candidate's school",
76+
optional: true,
77+
},
78+
educationInfoNumber: {
79+
type: "integer",
80+
label: "Education Info Quantity",
81+
description: "The number of education info objects to be created",
82+
reloadProps: true,
83+
optional: true,
84+
},
85+
workInfoNumber: {
86+
type: "integer",
87+
label: "Work Info Quantity",
88+
description: "The number of work info objects to be created",
89+
reloadProps: true,
90+
optional: true,
91+
},
92+
profileUrls: {
93+
type: "string[]",
94+
label: "Profile URLs",
95+
description: "If `Profile URLs` is provided with an array of urls, social `profiles` will be generated based on the provided urls and attached to the candidate",
96+
optional: true,
97+
},
98+
phoneNumber: {
99+
type: "string",
100+
label: "Phone Number",
101+
description: "Candidate's phone number",
102+
optional: true,
103+
},
104+
projectIds: {
105+
propDefinition: [
106+
gem,
107+
"projectIds",
108+
],
109+
optional: true,
110+
},
111+
customFields: {
112+
type: "object",
113+
label: "Custom Fields",
114+
description: "An object containing new custom field values. Only custom fields specified are updated. **Format: {\"custom_field_id\": \"value\"}**. [See the documentation](https://api.gem.com/v0/reference#tag/Candidates/paths/~1v0~1candidates/post) for further details.",
115+
optional: true,
116+
},
117+
sourcedFrom: {
118+
type: "string",
119+
label: "Sourced From",
120+
description: "Where the candidate was sourced from",
121+
options: SOURCED_FROM,
122+
optional: true,
123+
},
124+
autofill: {
125+
type: "boolean",
126+
label: "Autofill",
127+
description: "Requires `Linked In Handle` to be non-null. Attempts to fill in any missing fields.",
128+
optional: true,
129+
},
130+
},
131+
async additionalProps() {
132+
const props = {};
133+
if (this.educationInfoNumber) {
134+
for (let i = 1; i <= this.educationInfoNumber; i++) {
135+
props[`educationInfo${i}School`] = {
136+
type: "string",
137+
label: `Education Info ${i} - School`,
138+
description: `Education info ${i} school of the candidate`,
139+
optional: true,
140+
};
141+
props[`educationInfo${i}University`] = {
142+
type: "string",
143+
label: `Education Info ${i} - University`,
144+
description: `Education info ${i} university of the candidate`,
145+
optional: true,
146+
};
147+
props[`educationInfo${i}StartDate`] = {
148+
type: "string",
149+
label: `Education Info ${i} - Start Date`,
150+
description: `Education info ${i} start date of the candidate`,
151+
optional: true,
152+
};
153+
props[`educationInfo${i}EndDate`] = {
154+
type: "string",
155+
label: `Education Info ${i} - End Date`,
156+
description: `Education info ${i} end date of the candidate`,
157+
optional: true,
158+
};
159+
props[`educationInfo${i}FieldOfSchool`] = {
160+
type: "string",
161+
label: `Education Info ${i} - Field Of School`,
162+
description: `Education info ${i} field of school of the candidate`,
163+
optional: true,
164+
};
165+
props[`educationInfo${i}Major1`] = {
166+
type: "string",
167+
label: `Education Info ${i} - Major 1`,
168+
description: `Education info ${i} major 1 of the candidate`,
169+
optional: true,
170+
};
171+
props[`educationInfo${i}Major2`] = {
172+
type: "string",
173+
label: `Education Info ${i} - Major 2`,
174+
description: `Education info ${i} major 2 of the candidate`,
175+
optional: true,
176+
};
177+
props[`educationInfo${i}Degree`] = {
178+
type: "string",
179+
label: `Education Info ${i} - Degree`,
180+
description: `Education info ${i} degree of the candidate`,
181+
optional: true,
182+
};
183+
}
184+
}
185+
if (this.workInfoNumber) {
186+
for (let i = 1; i <= this.workInfoNumber; i++) {
187+
props[`WorkInfo${i}Company`] = {
188+
type: "string",
189+
label: `Work Info ${i} - Company`,
190+
description: `Work info ${i} company of the candidate`,
191+
optional: true,
192+
};
193+
props[`WorkInfo${i}Title`] = {
194+
type: "string",
195+
label: `Work Info ${i} - Title`,
196+
description: `Work info ${i} title of the candidate`,
197+
optional: true,
198+
};
199+
props[`WorkInfo${i}WorkStartDate`] = {
200+
type: "string",
201+
label: `Work Info ${i} - Work Start Date`,
202+
description: `Work info ${i} work start date of the candidate`,
203+
optional: true,
204+
};
205+
props[`WorkInfo${i}WorkEndDate`] = {
206+
type: "string",
207+
label: `Work Info ${i} - Work End Date`,
208+
description: `Work info ${i} work end date of the candidate`,
209+
optional: true,
210+
};
211+
props[`WorkInfo${i}IsCurrent`] = {
212+
type: "boolean",
213+
label: `Work Info ${i} - Is Current`,
214+
description: `Work info ${i} is Current of the candidate`,
215+
optional: true,
216+
};
217+
}
218+
}
219+
220+
return props;
221+
},
222+
async run({ $ }) {
223+
const educationInfo = [];
224+
const workInfo = [];
225+
for (var i = 1; i <= this.educationInfoNumber; i++) {
226+
educationInfo.push({
227+
school: this[`educationInfo${i}School`],
228+
parsed_university: this[`educationInfo${i}University`],
229+
start_date: this[`educationInfo${i}StartDate`],
230+
end_date: this[`educationInfo${i}EndDate`],
231+
field_of_study: this[`educationInfo${i}FieldOfSchool`],
232+
parsed_major_1: this[`educationInfo${i}Major1`],
233+
parsed_major_2: this[`educationInfo${i}Major2`],
234+
degree: this[`educationInfo${i}Degree`],
235+
});
236+
}
237+
238+
for (var j = 1; j <= this.workInfoNumber; j++) {
239+
workInfo.push({
240+
company: this[`WorkInfo${j}Company`],
241+
title: this[`WorkInfo${j}Title`],
242+
work_start_date: this[`WorkInfo${j}WorkStartDate`],
243+
work_end_date: this[`WorkInfo${j}WorkEndDate`],
244+
is_current: this[`WorkInfo${j}IsCurrent`],
245+
});
246+
}
247+
248+
const emails = [
249+
{
250+
email_address: this.primaryEmail,
251+
is_primary: true,
252+
},
253+
];
254+
if (this.additionalEmails) emails.push(...parseObject(this.additionalEmails).map((email) => ({
255+
email_address: email,
256+
is_primary: false,
257+
})));
258+
259+
if (emails.length === 0) {
260+
throw new Error("Primary Email Address is required");
261+
}
262+
const candidate = await this.gem.createCandidate({
263+
$,
264+
data: {
265+
created_by: this.createdBy,
266+
first_name: this.firstName,
267+
last_name: this.lastName,
268+
nickname: this.nickname,
269+
emails,
270+
linked_in_handle: this.linkedInHandle,
271+
title: this.title,
272+
company: this.company,
273+
location: this.location,
274+
school: this.school,
275+
education_info: educationInfo,
276+
work_info: workInfo,
277+
profile_urls: parseObject(this.profileUrls),
278+
phone_number: this.phoneNumber,
279+
project_ids: parseObject(this.projectIds),
280+
custom_fields: Object.entries(parseObject(this.customFields))?.map(([
281+
key,
282+
value,
283+
]) => ({
284+
custom_field_id: key,
285+
value,
286+
})),
287+
sourced_from: this.sourcedFrom,
288+
autofill: this.autofill,
289+
},
290+
});
291+
$.export(
292+
"$summary", `Created candidate ${candidate.first_name} ${candidate.last_name} with ID: ${candidate.id}`,
293+
);
294+
return candidate;
295+
},
296+
};

components/gem/common/constants.mjs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export const LIMIT = 100;
2+
3+
export const SOURCED_FROM = [
4+
"SeekOut",
5+
"hireEZ",
6+
"Starcircle",
7+
"Censia",
8+
"Consider",
9+
];

components/gem/common/utils.mjs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
export const parseObject = (obj) => {
2+
if (!obj) return [];
3+
4+
if (Array.isArray(obj)) {
5+
return obj.map((item) => {
6+
if (typeof item === "string") {
7+
try {
8+
return JSON.parse(item);
9+
} catch (e) {
10+
return item;
11+
}
12+
}
13+
return item;
14+
});
15+
}
16+
if (typeof obj === "string") {
17+
try {
18+
return JSON.parse(obj);
19+
} catch (e) {
20+
return obj;
21+
}
22+
}
23+
return obj;
24+
};

0 commit comments

Comments
 (0)