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

feat(interview-scheduler): backend integration #156

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,3 +298,9 @@ export const STATUS_OPTIONS = [
*/
export const DISABLED_DESCRIPTION_MESSAGE =
"You may not edit a Job Description that is currently posted to Topcoder.com. Please contact support@topcoder.com.";

/**
* The media URL to be shown on Interview popup
*/
export const INTERVIEW_POPUP_MEDIA_URL =
"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
9 changes: 7 additions & 2 deletions src/routes/JobForm/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import React, { useState, useEffect } from "react";
import PT from "prop-types";
import { toastr } from "react-redux-toastr";
import _ from "lodash";
import store from "../../store";
import Page from "components/Page";
import PageHeader from "components/PageHeader";
import { useData } from "hooks/useData";
Expand Down Expand Up @@ -61,8 +63,10 @@ const JobForm = ({ teamId, jobId }) => {

// as we are using `PUT` method (not `PATCH`) we have send ALL the fields
// fields which we don't send would become `null` otherwise
const getRequestData = (values) =>
_.pick(values, [
const getRequestData = (values) => {
const externalId = _.get(store.getState(), "authUser.userId");
values.externalId = externalId && _.toString(externalId);
return _.pick(values, [
"projectId",
"externalId",
"description",
Expand All @@ -76,6 +80,7 @@ const JobForm = ({ teamId, jobId }) => {
"skills",
"status",
]);
};

useEffect(() => {
if (skills && job && !options) {
Expand Down
8 changes: 0 additions & 8 deletions src/routes/PositionDetails/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
patchCandidateInterview,
} from "services/teams";
import { ACTION_TYPE } from "constants";
import { getFakeInterviews } from "utils/helpers";

/**
* Load Team Position details (team job)
Expand All @@ -22,13 +21,6 @@ export const loadPosition = (teamId, positionId) => ({
type: ACTION_TYPE.LOAD_POSITION,
payload: async () => {
const response = await getPositionDetails(teamId, positionId);

// inject mock interview data to candidates list
for (const candidate of response.data.candidates) {
const fakeInterviews = getFakeInterviews(candidate);
_.set(candidate, "interviews", fakeInterviews);
}

return response.data;
},
meta: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import React from "react";
import PT from "prop-types";
import SimpleModal from "components/SimpleModal";
import { INTERVIEW_POPUP_MEDIA_URL } from "constants";
import "./styles.module.scss";

function InterviewConfirmPopup({ open, onClose }) {
Expand Down Expand Up @@ -38,7 +39,7 @@ function InterviewConfirmPopup({ open, onClose }) {
</p>
<video
controls
src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
src={INTERVIEW_POPUP_MEDIA_URL}
styleName="video"
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
* Popup that allows user to schedule an interview
* Calls addInterview action
*/
import React, { useEffect, useState, useCallback } from "react";
import { getAuthUserProfile } from "@topcoder/micro-frontends-navbar-app";
import React, { useCallback } from "react";
import { Form } from "react-final-form";
import arrayMutators from "final-form-arrays";
import { FieldArray } from "react-final-form-arrays";
Expand All @@ -21,10 +20,6 @@ import RadioFieldGroup from "components/RadioFieldGroup";

/* Validators for Form */

const validateExists = (value) => {
return value ? undefined : "Required";
};

const validateIsEmail = (value) => {
if (!value) return undefined;
return /\S+@\S+\.\S+/.test(value) ? undefined : "Please enter valid email";
Expand All @@ -33,11 +28,6 @@ const validateIsEmail = (value) => {
const validator = (values) => {
const errors = {};

errors.myemail =
validateExists(values.myemail) || validateIsEmail(values.myemail);
errors.email2 =
validateExists(values.email2) || validateIsEmail(values.email2);

errors.emails = [];
if (values.emails) {
for (const email of values.emails) {
Expand All @@ -51,42 +41,28 @@ const validator = (values) => {
/********************* */

function InterviewDetailsPopup({ open, onClose, candidate, openNext }) {
const [isLoading, setIsLoading] = useState(true);
const [myEmail, setMyEmail] = useState("");
const [myId, setMyId] = useState("");
const dispatch = useDispatch();

useEffect(() => {
getAuthUserProfile().then((res) => {
setMyEmail(res.email || "");
setMyId(res.userId);
setIsLoading(false);
});
}, []);

const onSubmitCallback = useCallback(
async (formData) => {
const secondaryEmails =
const attendeesList =
formData.emails?.filter(
(email) => typeof email === "string" && email.length > 0
) || [];
const interviewData = {
xaiTemplate: formData.time,
attendeesList: [formData.myemail, formData.email2, ...secondaryEmails],
round: candidate.interviews.length + 1,
createdBy: myId,
attendeesList,
};

await dispatch(addInterview(candidate.id, interviewData));
},
[dispatch, candidate, myId]
[dispatch, candidate]
);

return isLoading ? null : (
return (
<Form
initialValues={{
myemail: myEmail,
time: "30-min-interview",
time: "30-minutes",
}}
onSubmit={onSubmitCallback}
mutators={{
Expand Down Expand Up @@ -149,11 +125,11 @@ function InterviewDetailsPopup({ open, onClose, candidate, openNext }) {
radios={[
{
label: "30 Minute Interview",
value: "30-min-interview",
value: "30-minutes",
},
{
label: "60 Minute Interview",
value: "60-min-interview",
value: "60-minutes",
},
]}
/>
Expand All @@ -164,32 +140,6 @@ function InterviewDetailsPopup({ open, onClose, candidate, openNext }) {
Please provide email addresses for all parties you would like
involved with the interview.
</p>
<FormField
field={{
name: "myemail",
type: FORM_FIELD_TYPE.TEXT,
placeholder: "Email Address",
label: "Email Address",
maxLength: 320,
customValidator: true,
}}
/>
<FormField
field={{
name: "email2",
type: FORM_FIELD_TYPE.TEXT,
placeholder: "Email Address",
label: "Email Address",
maxLength: 320,
customValidator: true,
}}
/>
<button
styleName="add-more modal-text"
onClick={() => push("emails")}
>
Add more
</button>
<FieldArray name="emails">
{({ fields }) => {
return fields.map((name, index) => (
Expand All @@ -207,18 +157,27 @@ function InterviewDetailsPopup({ open, onClose, candidate, openNext }) {
}}
/>
</div>
<span
tabIndex={0}
role="button"
onClick={() => fields.remove(index)}
styleName="remove-item"
>
&times;
</span>
{index > 0 && (
<span
tabIndex={0}
title="Remove"
role="button"
onClick={() => fields.remove(index)}
styleName="remove-item"
>
&times;
</span>
)}
</div>
));
}}
</FieldArray>
<button
styleName="add-more modal-text"
onClick={() => push("emails")}
>
Add more
</button>
</div>
<div styleName="bottom">
<p styleName="modal-text">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
background: #fff;
margin: 10px 0 0 0;
padding: 0;
color: blue;
color: #0D61BF;
border: none;
border-radius: 0;

Expand All @@ -60,15 +60,17 @@
}

.array-input {
width: 95%
width: 100%
}

.remove-item {
display: flex;
flex-direction: column;
justify-content: flex-end;
margin-bottom: 10px;
position: absolute;
right: 45px;
margin-top: 33px;
font-size: 33px;
color: #EF476F;
cursor: pointer;
&:focus {
outline: none;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import "./styles.module.scss";
import { formatDate } from "utils/format";

function LatestInterview({ interviews }) {
if (!interviews.length) {
if (!interviews || !interviews.length) {
return <div></div>;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,14 +272,15 @@ const PositionCandidates = ({ position, statusFilterKey, updateCandidate }) => {
>
Schedule Another Interview
</Button>
{candidate.interviews.length > 0 && (
<Button
type="secondary"
onClick={() => openPrevInterviewsPopup(candidate)}
>
View Previous Interviews
</Button>
)}
{candidate.interviews &&
candidate.interviews.length > 0 && (
<Button
type="secondary"
onClick={() => openPrevInterviewsPopup(candidate)}
>
View Previous Interviews
</Button>
)}
</div>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function PreviousInterviewsPopup(props) {

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

Expand Down
29 changes: 2 additions & 27 deletions src/services/teams.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
*/
import { axiosInstance as axios } from "./requestInterceptor";
import config from "../../config";
import { generateInterview } from "utils/helpers";

/**
* Get my teams.
Expand Down Expand Up @@ -68,34 +67,10 @@ export const patchPositionCandidate = (candidateId, partialCandidateData) => {
* @returns {Promise<object>} interview object
*/
export const patchCandidateInterview = (candidateId, interviewData) => {
// endpoint not currently implemented so response is mocked
/* return axios.patch(
return axios.patch(
`${config.API.V5}/jobCandidates/${candidateId}/requestInterview`,
interviewData
); */

const { attendeesList, xaiTemplate, createdBy, round } = interviewData;

return new Promise((resolve) => {
setTimeout(() => {
resolve({
data: generateInterview({
attendeesList,
xaiTemplate,
jobCandidates: candidateId,
updatedBy: "",
updatedAt: "",
startTimestamp: new Date(
Date.now() + 1000 * 60 * 60 * 24 * 3
).toString(), // returns the timestamp 3 days from now
createdAt: Date(),
createdBy,
status: "Scheduling",
round,
}),
});
}, 2000);
});
);
};

/**
Expand Down
Loading