Ver código fonte

upload CV frontend

pull/127/head
Dzenis Hadzifejzovic 3 anos atrás
pai
commit
bd9d58991b

+ 181
- 0
src/assets/styles/components/_uploadCVPage.scss Ver arquivo

@@ -0,0 +1,181 @@
.uploadCV-main-container {
display: flex;
width: 100%;
height: 100%;
background-color: gray;
justify-content: center;
align-items: center;
}

.uploadCV-container {
display: flex;
width: 512px;
height: 591px;
background-color: #fff;
border-radius: 18px;
padding: 36px;
flex-direction: column;
}

.uploadCV-top-container {
display: flex;
align-items: center;
height: fit-content;
justify-content: space-between;
}

.uploadCV-top-container-image1 {
width: 18px;
height: 16.88px;
}

.uploadCV-top-container-image2 {
width: 9px;
height: 10.5px;
margin-left: 54px;
}

.uploadCV-top-container-text {
margin-left: 9px;
}

.uploadCV-top-container-text > p:first-child {
font-family: Source Sans Pro;
font-size: 24px;
font-weight: 600;
line-height: 32px;
letter-spacing: 0.02em;
text-align: left;
}

.uploadCV-top-container-text > p:last-child {
font-family: Source Sans Pro;
font-size: 16px;
font-weight: 600;
line-height: 20px;
letter-spacing: 0em;
text-align: left;
color: #226cb0;
}

.uploadCV-content-container {
display: flex;
flex-direction: column;
margin-top: 36px;
}

.uploadCV-input {
width: 100%;
height: 140px;
border: 1px solid #e4e4e4;
border-radius: 6px;
margin-top: 6px;
display: flex;
justify-content: center;
align-items: center;
}

.uploadCV-content-sub-container {
display: flex;
flex-direction: column;
}

.uploadCV-content-sub-container > p {
font-family: Source Sans Pro;
font-size: 14px;
font-weight: 400;
line-height: 18px;
letter-spacing: 0em;
text-align: left;
}

.uploadCV-input-sub-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

.uploadCV-input-sub-container img {
width: 22px;
height: 22px;
}

.uploadCV-input-sub-container p {
width: 332px;
height: 40px;
font-family: Source Sans Pro;
font-size: 16px;
font-weight: 400;
line-height: 20.11px;
letter-spacing: 0em;
text-align: center;
}

.uploadCV-input2 {
width: 100%;
height: 140px;
border: 1px solid #e4e4e4;
border-radius: 6px;
margin-top: 6px;
padding: 15px;
resize: none;
}

.uploadCV-input2::placeholder {
color: #9d9d9d;
font-family: Source Sans Pro;
font-size: 14px;
font-style: italic;
font-weight: 400;
line-height: 18px;
letter-spacing: 0em;
text-align: left;
word-break: break-all;
white-space: normal;
}

.uploadCV-buttons-container {
display: flex;
margin-top: 35px;
justify-content: space-between;
flex-direction: row;
}

.uploadCV-button {
width: 202px;
height: 51px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;

gap: 10;
border-radius: 9px;
border: none;
font-family: "Source Sans Pro";
font-style: normal;
font-size: 12px;
line-height: 15px;
letter-spacing: 0.04em;
text-transform: uppercase;
color: #ffffff;
cursor: pointer;
}

.uploadCV-buttons-container > button:first-child {
@extend .uploadCV-button;
background: #ffffff;
color: #226cb0;
border: 1px solid #226cb0;
padding: 18px 72px;
font-weight: 600;
}

.uploadCV-buttons-container > button:last-child {
@extend .uploadCV-button;
background: #226cb0;
color: #ffffff;
padding: 18px 22px;
border: 1px solid #226cb0;
}

+ 2
- 2
src/components/Ads/ApplyForAd.js Ver arquivo

@@ -28,7 +28,7 @@ const ApplyForAd = ({ open, title, adId, onCloseModal }) => {
const [email, setEmail] = useState("");
const [bitBucketLink, setBitBucketLink] = useState("");
const [coverLetter, setCoverLetter] = useState("");
const [pdfFile, setPdfFile] = useState("");
const [pdfFile, setPdfFile] = useState(null);

const technologies = useSelector(selectTechnologies);
const dispatch = useDispatch();
@@ -77,7 +77,7 @@ const ApplyForAd = ({ open, title, adId, onCloseModal }) => {
bitBucketLink,
email,
coverLetter,
pdfFile: "PDF",
pdfFile,
handleApiResponseSuccess,
})
);

+ 55
- 41
src/components/Ads/ApplyForAdFourthStage.js Ver arquivo

@@ -1,7 +1,6 @@
import React, { useRef } from "react";
import React, { useState } from "react";
import PropTypes from "prop-types";
import uploadIcon from "../../assets/images/upload.png";
import { useDropzone } from "react-dropzone";

const ApplyForAdFourthStage = ({
coverLetter,
@@ -11,27 +10,13 @@ const ApplyForAdFourthStage = ({
onDecreaseStage,
onFinishedFourStages,
}) => {
const inputFile = useRef();
const { getRootProps, getInputProps } = useDropzone({
onDrop: (acceptedFiles) => setPdfFile(acceptedFiles),
});
const [dropzoneActive, setDropzoneActive] = useState(false);

const disabled = pdfFile === "" || coverLetter === "";
const disabled = pdfFile === null || coverLetter === "";

const handleFileUpload = (e) => {
const { files } = e.target;
if (files && files.length) {
// const filename = files[0].name;

// var parts = filename.split(".");
// const fileType = parts[parts.length - 1];

setPdfFile(files[0]);
}
};

const onButtonClick = () => {
inputFile.current.click();
const handleDrop = (e) => {
e.preventDefault();
setPdfFile(e.dataTransfer.files[0]);
};

return (
@@ -39,27 +24,56 @@ const ApplyForAdFourthStage = ({
<div className="apply-for-ad-modal-form-control">
<label>CV</label>
<div
className="apply-for-ad-modal-form-control-drag-and-drop"
{...getRootProps({
className: "apply-for-ad-modal-form-control-drag-and-drop",
})}
className="uploadCV-input"
onDragOver={(e) => {
setDropzoneActive(true);
e.preventDefault();
}}
onDragLeave={(e) => {
setDropzoneActive(false);
e.preventDefault();
}}
onDrop={(e) => handleDrop(e)}
style={{
backgroundColor: dropzoneActive ? "#F4F4F4" : "#ffffff",
}}
>
<img src={uploadIcon} alt="upload" />
<input
{...getInputProps()}
style={{ display: "none" }}
accept=".pdf"
ref={inputFile}
onChange={handleFileUpload}
type="file"
/>
<p>
Prevuci .pdf dokument u ovom delu ekrana ili{" "}
<button style={{ cursor: "pointer" }} onClick={onButtonClick}>
Pretraži
</button>{" "}
na računaru
</p>
<div className="uploadCV-input-sub-container">
<img src={uploadIcon} />
<div className="uploadCV-input-sub-container">
{pdfFile !== null ? (
<p>{pdfFile.name}</p>
) : (
<>
<p>
Prevuci .pdf dokument u ovom delu ekrana ili &nbsp;
<label
htmlFor="upload-file"
style={{
cursor: "pointer",
textDecoration: "underline",
color: "#1E92D0",
}}
>
Pretrazi
</label>
&nbsp;na racunaru
</p>
<input
type="file"
name="photo"
id="upload-file"
style={{ display: "none", zIndex: -1 }}
value={pdfFile}
onChange={(e) => {
console.log("Ovde smo");
setPdfFile(e.target.files[0]);
}}
/>
</>
)}
</div>
</div>
</div>
</div>
<div className="apply-for-ad-modal-form-control">

+ 2
- 6
src/pages/CandidatesPage/CandidatesPage.js Ver arquivo

@@ -195,12 +195,8 @@ const CandidatesPage = ({ history }) => {
) : (
<AdsCandidatesPage history={history} search={search} />
)}
<Fade in={isCVDisplayed} timeout={500} className="candidates-cv">
<iframe
id="iframepdf"
src={linkToCV}
style={{ border: "0px" }}
></iframe>
<Fade in={isCVDisplayed} timeout={400} className="candidates-cv">
<embed src={`data:application/pdf;base64,${linkToCV}`} />
</Fade>
</div>
</div>

+ 14
- 20
src/pages/CandidatesPage/TableViewPage.js Ver arquivo

@@ -12,6 +12,7 @@ import {
selectCandidates,
selectPagination,
} from "../../store/selectors/candidatesSelectors";
import { getCV } from "../../request/candidatesRequest";

const TableViewPage = ({
history,
@@ -24,7 +25,7 @@ const TableViewPage = ({
}) => {
const dispatch = useDispatch();
const candidates = useSelector(selectCandidates);
const pagination = useSelector(selectPagination); // pagination is total number of candidates on backend
const pagination = useSelector(selectPagination);
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.down("361"));

@@ -89,17 +90,17 @@ const TableViewPage = ({
);
};

const onHoverEnter = (e,linkToCV) => {
e.stopPropagation()
const changedLinkToCV = linkToCV.replace("view", "preview");
setLinkToCV(changedLinkToCV);
setIsCVDisplayed(true);
const onClickCV = (e, linkToCV) => {
e.stopPropagation();
console.log(linkToCV);
getCV("638077305621281656.pdf")
.then((res) => {
setLinkToCV(res.data);
setIsCVDisplayed(true);
})
.catch((e) => console.log(e));
};

// const onHoverLeave = () => {
// setIsCVDisplayed(false);
// };

return (
<div className="candidates-table">
<div style={{ overflowX: "auto", marginLeft: matches ? 36 : 72 }}>
@@ -132,7 +133,6 @@ const TableViewPage = ({
cursor: "pointer",
}}
onClick={() => navigate(candidate.applicantId)}
// onMouseLeave={onHoverLeave}
>
<td>
{(
@@ -160,19 +160,13 @@ const TableViewPage = ({
</td>
<td>{candidate.position}</td>
<td>
<a
href={candidate.CV}
<span
onClick={(e) => onClickCV(e, candidate.cv)}
className="cvLink"
onClick={(e) =>
onHoverEnter(
e,
"https://drive.google.com/file/d/1nhhRwitNmeAgetmCEBYlr1YNqaxhXJoD/view"
)
}
>
{candidate.firstName}
{candidate.lastName}.pdf
</a>
</span>
</td>
</tr>
))}

+ 1
- 0
src/request/apiEndpoints.js Ver arquivo

@@ -63,5 +63,6 @@ export default {
},
applicant: {
applyForAd: base + "/applicants/apply-for-ad",
getCV: base + "/applicants/get-CV",
},
};

+ 3
- 1
src/request/candidatesRequest.js Ver arquivo

@@ -1,4 +1,4 @@
import { deleteRequest, getRequest, postRequest } from ".";
import { deleteRequest, getRequest, postRequest,downloadPdf } from ".";
import apiEndpoints from "./apiEndpoints";

export const getFilteredCandidates = (payload) => {
@@ -63,3 +63,5 @@ export const getCandidateOptions = () =>

export const initializeProcessRequest = (payload) =>
postRequest(apiEndpoints.candidates.initProcess, payload);

export const getCV = (fileName) => downloadPdf(apiEndpoints.applicant.getCV + "?fileName=" + fileName);

+ 3
- 0
src/request/index.js Ver arquivo

@@ -29,6 +29,9 @@ export const deleteRequest = (url, params = null, options = null) =>
export const downloadRequest = (url, params = null, options = null) =>
request.get(url, { params, ...options, responseType: 'blob' });

export const downloadPdf = (url, params = null, options = null) =>
request.get(url, { params, ...options, responseType: 'application/pdf' });

export const replaceInUrl = (url, pathVariables = {}) => {
const keys = Object.keys(pathVariables);
if (!keys.length) {

+ 15
- 1
src/store/saga/applicantsSaga.js Ver arquivo

@@ -14,7 +14,21 @@ export function* applyForAdSaga({ payload }) {
try {
// const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN);
// yield call(addHeaderToken, JwtToken);
const { data } = yield call(applyForAdRequest, payload);
const formData = new FormData();
formData.append("adId", payload.adId);
formData.append("firstName", payload.firstName);
formData.append("lastName", payload.lastName);
formData.append("dateOfBirth", payload.dateOfBirth);
formData.append("phoneNumber", payload.phoneNumber);
formData.append("technologiesIds", payload.technologiesIds);
formData.append("experience", payload.experience);
formData.append("linkedinLink", payload.linkedinLink);
formData.append("githubLink", payload.githubLink);
formData.append("bitBucketLink", payload.bitBucketLink);
formData.append("email", payload.email);
formData.append("coverLetter", payload.coverLetter);
formData.append("pdfFile", payload.pdfFile);
const { data } = yield call(applyForAdRequest, formData);
yield put(applyForAd(data));
if (payload.handleApiResponseSuccess) {
yield call(payload.handleApiResponseSuccess);

Carregando…
Cancelar
Salvar