Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 8d52552

Browse files
authored
Merge pull request #156 from cagdas001/feature/interview-scheduler-gui
feat(interview-scheduler): backend integration
2 parents 746ded4 + 5f783a0 commit 8d52552

File tree

11 files changed

+61
-186
lines changed

11 files changed

+61
-186
lines changed

src/constants/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,3 +298,9 @@ export const STATUS_OPTIONS = [
298298
*/
299299
export const DISABLED_DESCRIPTION_MESSAGE =
300300
"You may not edit a Job Description that is currently posted to Topcoder.com. Please contact support@topcoder.com.";
301+
302+
/**
303+
* The media URL to be shown on Interview popup
304+
*/
305+
export const INTERVIEW_POPUP_MEDIA_URL =
306+
"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";

src/routes/JobForm/index.jsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import React, { useState, useEffect } from "react";
99
import PT from "prop-types";
1010
import { toastr } from "react-redux-toastr";
11+
import _ from "lodash";
12+
import store from "../../store";
1113
import Page from "components/Page";
1214
import PageHeader from "components/PageHeader";
1315
import { useData } from "hooks/useData";
@@ -61,8 +63,10 @@ const JobForm = ({ teamId, jobId }) => {
6163

6264
// as we are using `PUT` method (not `PATCH`) we have send ALL the fields
6365
// fields which we don't send would become `null` otherwise
64-
const getRequestData = (values) =>
65-
_.pick(values, [
66+
const getRequestData = (values) => {
67+
const externalId = _.get(store.getState(), "authUser.userId");
68+
values.externalId = externalId && _.toString(externalId);
69+
return _.pick(values, [
6670
"projectId",
6771
"externalId",
6872
"description",
@@ -76,6 +80,7 @@ const JobForm = ({ teamId, jobId }) => {
7680
"skills",
7781
"status",
7882
]);
83+
};
7984

8085
useEffect(() => {
8186
if (skills && job && !options) {

src/routes/PositionDetails/actions/index.js

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
patchCandidateInterview,
99
} from "services/teams";
1010
import { ACTION_TYPE } from "constants";
11-
import { getFakeInterviews } from "utils/helpers";
1211

1312
/**
1413
* Load Team Position details (team job)
@@ -22,13 +21,6 @@ export const loadPosition = (teamId, positionId) => ({
2221
type: ACTION_TYPE.LOAD_POSITION,
2322
payload: async () => {
2423
const response = await getPositionDetails(teamId, positionId);
25-
26-
// inject mock interview data to candidates list
27-
for (const candidate of response.data.candidates) {
28-
const fakeInterviews = getFakeInterviews(candidate);
29-
_.set(candidate, "interviews", fakeInterviews);
30-
}
31-
3224
return response.data;
3325
},
3426
meta: {

src/routes/PositionDetails/components/InterviewConfirmPopup/index.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import React from "react";
77
import PT from "prop-types";
88
import SimpleModal from "components/SimpleModal";
9+
import { INTERVIEW_POPUP_MEDIA_URL } from "constants";
910
import "./styles.module.scss";
1011

1112
function InterviewConfirmPopup({ open, onClose }) {
@@ -38,7 +39,7 @@ function InterviewConfirmPopup({ open, onClose }) {
3839
</p>
3940
<video
4041
controls
41-
src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
42+
src={INTERVIEW_POPUP_MEDIA_URL}
4243
styleName="video"
4344
/>
4445
</div>

src/routes/PositionDetails/components/InterviewDetailsPopup/index.jsx

Lines changed: 25 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
* Popup that allows user to schedule an interview
55
* Calls addInterview action
66
*/
7-
import React, { useEffect, useState, useCallback } from "react";
8-
import { getAuthUserProfile } from "@topcoder/micro-frontends-navbar-app";
7+
import React, { useCallback } from "react";
98
import { Form } from "react-final-form";
109
import arrayMutators from "final-form-arrays";
1110
import { FieldArray } from "react-final-form-arrays";
@@ -21,10 +20,6 @@ import RadioFieldGroup from "components/RadioFieldGroup";
2120

2221
/* Validators for Form */
2322

24-
const validateExists = (value) => {
25-
return value ? undefined : "Required";
26-
};
27-
2823
const validateIsEmail = (value) => {
2924
if (!value) return undefined;
3025
return /\S+@\S+\.\S+/.test(value) ? undefined : "Please enter valid email";
@@ -33,11 +28,6 @@ const validateIsEmail = (value) => {
3328
const validator = (values) => {
3429
const errors = {};
3530

36-
errors.myemail =
37-
validateExists(values.myemail) || validateIsEmail(values.myemail);
38-
errors.email2 =
39-
validateExists(values.email2) || validateIsEmail(values.email2);
40-
4131
errors.emails = [];
4232
if (values.emails) {
4333
for (const email of values.emails) {
@@ -51,42 +41,28 @@ const validator = (values) => {
5141
/********************* */
5242

5343
function InterviewDetailsPopup({ open, onClose, candidate, openNext }) {
54-
const [isLoading, setIsLoading] = useState(true);
55-
const [myEmail, setMyEmail] = useState("");
56-
const [myId, setMyId] = useState("");
5744
const dispatch = useDispatch();
5845

59-
useEffect(() => {
60-
getAuthUserProfile().then((res) => {
61-
setMyEmail(res.email || "");
62-
setMyId(res.userId);
63-
setIsLoading(false);
64-
});
65-
}, []);
66-
6746
const onSubmitCallback = useCallback(
6847
async (formData) => {
69-
const secondaryEmails =
48+
const attendeesList =
7049
formData.emails?.filter(
7150
(email) => typeof email === "string" && email.length > 0
7251
) || [];
7352
const interviewData = {
7453
xaiTemplate: formData.time,
75-
attendeesList: [formData.myemail, formData.email2, ...secondaryEmails],
76-
round: candidate.interviews.length + 1,
77-
createdBy: myId,
54+
attendeesList,
7855
};
7956

8057
await dispatch(addInterview(candidate.id, interviewData));
8158
},
82-
[dispatch, candidate, myId]
59+
[dispatch, candidate]
8360
);
8461

85-
return isLoading ? null : (
62+
return (
8663
<Form
8764
initialValues={{
88-
myemail: myEmail,
89-
time: "30-min-interview",
65+
time: "30-minutes",
9066
}}
9167
onSubmit={onSubmitCallback}
9268
mutators={{
@@ -149,11 +125,11 @@ function InterviewDetailsPopup({ open, onClose, candidate, openNext }) {
149125
radios={[
150126
{
151127
label: "30 Minute Interview",
152-
value: "30-min-interview",
128+
value: "30-minutes",
153129
},
154130
{
155131
label: "60 Minute Interview",
156-
value: "60-min-interview",
132+
value: "60-minutes",
157133
},
158134
]}
159135
/>
@@ -164,32 +140,6 @@ function InterviewDetailsPopup({ open, onClose, candidate, openNext }) {
164140
Please provide email addresses for all parties you would like
165141
involved with the interview.
166142
</p>
167-
<FormField
168-
field={{
169-
name: "myemail",
170-
type: FORM_FIELD_TYPE.TEXT,
171-
placeholder: "Email Address",
172-
label: "Email Address",
173-
maxLength: 320,
174-
customValidator: true,
175-
}}
176-
/>
177-
<FormField
178-
field={{
179-
name: "email2",
180-
type: FORM_FIELD_TYPE.TEXT,
181-
placeholder: "Email Address",
182-
label: "Email Address",
183-
maxLength: 320,
184-
customValidator: true,
185-
}}
186-
/>
187-
<button
188-
styleName="add-more modal-text"
189-
onClick={() => push("emails")}
190-
>
191-
Add more
192-
</button>
193143
<FieldArray name="emails">
194144
{({ fields }) => {
195145
return fields.map((name, index) => (
@@ -207,18 +157,27 @@ function InterviewDetailsPopup({ open, onClose, candidate, openNext }) {
207157
}}
208158
/>
209159
</div>
210-
<span
211-
tabIndex={0}
212-
role="button"
213-
onClick={() => fields.remove(index)}
214-
styleName="remove-item"
215-
>
216-
&times;
217-
</span>
160+
{index > 0 && (
161+
<span
162+
tabIndex={0}
163+
title="Remove"
164+
role="button"
165+
onClick={() => fields.remove(index)}
166+
styleName="remove-item"
167+
>
168+
&times;
169+
</span>
170+
)}
218171
</div>
219172
));
220173
}}
221174
</FieldArray>
175+
<button
176+
styleName="add-more modal-text"
177+
onClick={() => push("emails")}
178+
>
179+
Add more
180+
</button>
222181
</div>
223182
<div styleName="bottom">
224183
<p styleName="modal-text">

src/routes/PositionDetails/components/InterviewDetailsPopup/styles.module.scss

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
background: #fff;
4444
margin: 10px 0 0 0;
4545
padding: 0;
46-
color: blue;
46+
color: #0D61BF;
4747
border: none;
4848
border-radius: 0;
4949

@@ -60,15 +60,17 @@
6060
}
6161

6262
.array-input {
63-
width: 95%
63+
width: 100%
6464
}
6565

6666
.remove-item {
67-
display: flex;
68-
flex-direction: column;
69-
justify-content: flex-end;
70-
margin-bottom: 10px;
67+
position: absolute;
68+
right: 45px;
69+
margin-top: 33px;
7170
font-size: 33px;
7271
color: #EF476F;
7372
cursor: pointer;
73+
&:focus {
74+
outline: none;
75+
}
7476
}

src/routes/PositionDetails/components/LatestInterview/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import "./styles.module.scss";
99
import { formatDate } from "utils/format";
1010

1111
function LatestInterview({ interviews }) {
12-
if (!interviews.length) {
12+
if (!interviews || !interviews.length) {
1313
return <div></div>;
1414
}
1515

src/routes/PositionDetails/components/PositionCandidates/index.jsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -272,14 +272,15 @@ const PositionCandidates = ({ position, statusFilterKey, updateCandidate }) => {
272272
>
273273
Schedule Another Interview
274274
</Button>
275-
{candidate.interviews.length > 0 && (
276-
<Button
277-
type="secondary"
278-
onClick={() => openPrevInterviewsPopup(candidate)}
279-
>
280-
View Previous Interviews
281-
</Button>
282-
)}
275+
{candidate.interviews &&
276+
candidate.interviews.length > 0 && (
277+
<Button
278+
type="secondary"
279+
onClick={() => openPrevInterviewsPopup(candidate)}
280+
>
281+
View Previous Interviews
282+
</Button>
283+
)}
283284
</div>
284285
)}
285286
</div>

src/routes/PositionDetails/components/PreviousInterviewsPopup/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ function PreviousInterviewsPopup(props) {
1515

1616
// sorts interviews and returns list of PrevInterviewItems
1717
const showPrevInterviews = (interviews) => {
18-
const sortedInterviews = interviews
18+
const sortedInterviews = (interviews || [])
1919
.slice()
2020
.sort((a, b) => a.round - b.round);
2121

src/services/teams.js

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
*/
44
import { axiosInstance as axios } from "./requestInterceptor";
55
import config from "../../config";
6-
import { generateInterview } from "utils/helpers";
76

87
/**
98
* Get my teams.
@@ -68,34 +67,10 @@ export const patchPositionCandidate = (candidateId, partialCandidateData) => {
6867
* @returns {Promise<object>} interview object
6968
*/
7069
export const patchCandidateInterview = (candidateId, interviewData) => {
71-
// endpoint not currently implemented so response is mocked
72-
/* return axios.patch(
70+
return axios.patch(
7371
`${config.API.V5}/jobCandidates/${candidateId}/requestInterview`,
7472
interviewData
75-
); */
76-
77-
const { attendeesList, xaiTemplate, createdBy, round } = interviewData;
78-
79-
return new Promise((resolve) => {
80-
setTimeout(() => {
81-
resolve({
82-
data: generateInterview({
83-
attendeesList,
84-
xaiTemplate,
85-
jobCandidates: candidateId,
86-
updatedBy: "",
87-
updatedAt: "",
88-
startTimestamp: new Date(
89-
Date.now() + 1000 * 60 * 60 * 24 * 3
90-
).toString(), // returns the timestamp 3 days from now
91-
createdAt: Date(),
92-
createdBy,
93-
status: "Scheduling",
94-
round,
95-
}),
96-
});
97-
}, 2000);
98-
});
73+
);
9974
};
10075

10176
/**

0 commit comments

Comments
 (0)