Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

JobForm.jsx 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. import propTypes from 'prop-types';
  2. import img from '../../assets/Group 305.png';
  3. import * as Yup from 'yup';
  4. import { Formik, Form, ErrorMessage } from 'formik';
  5. import { JobFormContext } from '../../context';
  6. import { useContext, useState, useRef, useEffect } from 'react';
  7. import * as emailjs from 'emailjs-com';
  8. import { motion } from 'framer-motion';
  9. import ReactGA from 'react-ga4';
  10. import MyDropzone from './MyDropzone';
  11. import HashPositions from './HashPositions';
  12. import ReCAPTCHA from 'react-google-recaptcha';
  13. import axios from 'axios';
  14. export default function JobForm(props) {
  15. const [sucMsg, setSucMsg] = useState(false);
  16. const captchaRef = useRef(null);
  17. const [msgText, setMsgText] = useState('');
  18. const cntCareersJobs = props.cntCareers;
  19. let defaultPositionSelection = props.defaultPositionSelection;
  20. //search context for prevous entry TODO
  21. const { jobForm, setJobForm } = useContext(JobFormContext);
  22. const [otherInputState, setOtherInputState] = useState(true);
  23. const [selectedPosition, setSelectedPosition] = useState('');
  24. const [errorMsg, setErrorMsg] = useState('');
  25. const fileInput = useRef();
  26. function changeFormHandler(event) {
  27. const { name, value } = event.target;
  28. setJobForm({
  29. ...jobForm,
  30. [name]: value,
  31. });
  32. }
  33. function dropzoneToFormData(files) {
  34. setJobForm({
  35. ...jobForm,
  36. file: files,
  37. });
  38. }
  39. function hashToFormData(selected, other) {
  40. setJobForm({
  41. ...jobForm,
  42. position: selected,
  43. other: other,
  44. });
  45. }
  46. useEffect(() => {
  47. if (jobForm.file !== '') {
  48. setErrorMsg('');
  49. }
  50. }, [jobForm.file]);
  51. const validationSchema = Yup.object({
  52. email: Yup.string().email('Invalid email format').required('Email is Required'),
  53. firstName: Yup.string()
  54. .min(2, 'First name too short')
  55. .max(50, 'First name too long')
  56. .required('First Name is Required'),
  57. lastName: Yup.string()
  58. .min(2, 'Last name too short')
  59. .max(50, 'Last name too long')
  60. .required('Last name is Required'),
  61. coverLetter: Yup.string()
  62. .trim()
  63. .min(2, 'Cover Letter too short')
  64. .required('Cover Letter is Required'),
  65. });
  66. return (
  67. <div className="mt-10 sm:mt-0 mx-auto">
  68. <div className="md:grid md:grid-cols-2 md:gap-6">
  69. <motion.div
  70. className="mt-5 md:mt-0 md:col-span-1"
  71. initial={{ x: -60, opacity: 0 }}
  72. animate={{ x: 0, opacity: 1 }}
  73. exit={{ x: -60, opacity: 0 }}
  74. transition={{ duration: 0.3, ease: 'easeOut' }}
  75. >
  76. <Formik
  77. initialValues={jobForm}
  78. validationSchema={validationSchema}
  79. onChange={changeFormHandler}
  80. onSubmit={async values => {
  81. const prepFile = async file => {
  82. if (file.size >= 2000000) {
  83. setErrorMsg('File too large!');
  84. return null;
  85. } else {
  86. const base64 = await convertBase64(file);
  87. return base64;
  88. }
  89. };
  90. const convertBase64 = file => {
  91. //console.log(file);
  92. return new Promise((resolve, reject) => {
  93. const fileReader = new FileReader();
  94. fileReader.readAsDataURL(file);
  95. fileReader.onload = () => {
  96. resolve(fileReader.result);
  97. };
  98. fileReader.onerror = error => {
  99. reject(error);
  100. };
  101. });
  102. };
  103. // if (fileInput.current.value === '') {
  104. // }
  105. if (jobForm.file === '') {
  106. setErrorMsg('CV is Required');
  107. } else {
  108. setErrorMsg('');
  109. // if (jobForm.file.size >= 2000000) {
  110. // setErrorMsg('File too large!');
  111. // } else {
  112. // const file = {
  113. // filename: 'CV.pdf',
  114. // data: jobForm.file,
  115. // };
  116. // const token = captchaRef.current.getValue();
  117. // captchaRef.current.reset();
  118. // if (token.length === 0) {
  119. // setSucMsg(true);
  120. // setMsgText('Please fill reCAPTCHA and try again. Thank you!');
  121. // } else {
  122. // await axios
  123. // .post(`${process.env.REACT_APP_CAPTCHA_API}/verify-token`, {
  124. // token,
  125. // })
  126. // .then(res => {
  127. // setSucMsg(true);
  128. // if (res.data.data.success) {
  129. // setMsgText('Submission Succesful! Thank you!');
  130. // props.mg.messages.create('dilig.net', {
  131. // from: `${values.firstName} ${values.lastName} <${values.email}>`,
  132. // to: ['hr@dilig.net'],
  133. // subject: 'Applying for a position',
  134. // text: `Position:${values.position} ${values.other}, Cover letter: ${values.coverLetter}, Link: ${values.link} `,
  135. // html: `<p>Position: ${selectedPosition} ${values.other}</p><p>Cover letter: ${values.coverLetter}</p><p>Link (optional): ${values.link}</p>`,
  136. // attachment: file,
  137. // });
  138. // } else
  139. // setMsgText('Please fill reCAPTCHA and try again. Thank you!');
  140. // })
  141. // .catch(error => {
  142. // console.log(error);
  143. // });
  144. //}
  145. //}
  146. prepFile(jobForm.file).then(async res => {
  147. const newtemplateParams = {
  148. Position: values.position,
  149. Other: values.other,
  150. Firstname: values.firstName,
  151. Lastname: values.lastName,
  152. Coverletter: values.coverLetter,
  153. Email: values.email,
  154. Link: values.link,
  155. File: res,
  156. };
  157. const token = captchaRef.current.getValue();
  158. captchaRef.current.reset();
  159. if (token.length === 0) {
  160. setSucMsg(true);
  161. setMsgText('Please fill reCAPTCHA and try again. Thank you!');
  162. } else {
  163. await axios
  164. .post(`${process.env.REACT_APP_CAPTCHA_API}/verify-token`, {
  165. token,
  166. })
  167. .then(res => {
  168. setSucMsg(true);
  169. if (res.data.data.success) {
  170. setMsgText('Submission Succesful! Thank you!');
  171. emailjs
  172. .send(
  173. process.env.REACT_APP_SERVICE_ID,
  174. process.env.REACT_APP_JOB_TEMPLATE_ID,
  175. newtemplateParams,
  176. process.env.REACT_APP_USER_ID,
  177. )
  178. .then(
  179. result => {
  180. console.log(result.text);
  181. setSucMsg(true);
  182. },
  183. error => {
  184. console.log(error.text);
  185. },
  186. );
  187. } else
  188. setMsgText('Please fill reCAPTCHA and try again. Thank you!');
  189. })
  190. .catch(error => {
  191. console.log(error);
  192. });
  193. }
  194. });
  195. }
  196. ReactGA.event('contact', {
  197. category: 'Contact',
  198. action: 'Job application',
  199. });
  200. }}
  201. >
  202. {props => (
  203. <Form onSubmit={props.handleSubmit}>
  204. <div className="sm:rounded-md">
  205. <div className="py-2 sm:py-6">
  206. <div className="">
  207. <div className="col-span-1 sm:col-span-1">
  208. <div className="py-1 hidden">
  209. <label
  210. htmlFor="first-name"
  211. className="block text-sm font-medium text-gray-700 dark:text-gray-400"
  212. >
  213. Position
  214. </label>
  215. <input
  216. type="text"
  217. name="position"
  218. id="position"
  219. onBlur={changeFormHandler}
  220. value={selectedPosition.substring(1)}
  221. onChange={props.handleChange}
  222. autoComplete="given-name"
  223. className="mt-1 disabled:bg-gray-100 disabled:border-gray-300 focus:ring-dg-primary-600 focus:border-dg-primary-900 block w-full shadow-sm sm:text-sm border-dg-primary-600 rounded-md transition duration-200"
  224. />
  225. <div className="h-4">
  226. <ErrorMessage
  227. name="position"
  228. component="div"
  229. className="text-sm text-right text-red-600"
  230. />
  231. </div>
  232. </div>
  233. <HashPositions
  234. changeFormHandler={changeFormHandler}
  235. hashToFormData={hashToFormData}
  236. cntCareers={cntCareersJobs}
  237. otherInputState={otherInputState}
  238. setOtherInputState={setOtherInputState}
  239. defaultPositionSelection={defaultPositionSelection}
  240. setSelectedPosition={setSelectedPosition}
  241. />
  242. <div className="py-1">
  243. <label
  244. htmlFor="first-name"
  245. className="block text-sm font-medium text-gray-700 dark:text-gray-400"
  246. >
  247. Other
  248. </label>
  249. <input
  250. disabled={otherInputState ? 'disabled' : ''}
  251. type="text"
  252. name="other"
  253. id="other"
  254. value={props.values.other}
  255. onChange={props.handleChange}
  256. className="mt-1 disabled:bg-gray-100 disabled:border-gray-300 dark:disabled:bg-gray-400 dark:disabled:border-gray-600 focus:ring-dg-primary-600 focus:border-dg-primary-900 dark:bg-dg-primary-1500 dark:text-white block w-full shadow-sm sm:text-sm border-dg-primary-600 rounded-md transition duration-200"
  257. />
  258. <div className="h-4">
  259. <ErrorMessage
  260. name="other"
  261. component="div"
  262. className="text-sm text-right text-red-600"
  263. />
  264. </div>
  265. </div>
  266. <div className="py-1">
  267. <label
  268. htmlFor="first-name"
  269. className="block text-sm font-medium text-gray-700 dark:text-gray-400"
  270. >
  271. First name
  272. </label>
  273. <input
  274. type="text"
  275. name="firstName"
  276. id="firstName"
  277. onBlur={changeFormHandler}
  278. value={props.values.firstName}
  279. onChange={props.handleChange}
  280. autoComplete="name"
  281. autofill="true"
  282. className="mt-1 focus:ring-dg-primary-600 focus:border-dg-primary-900 dark:bg-dg-primary-1500 dark:text-white block w-full shadow-sm sm:text-sm border-dg-primary-600 rounded-md transition duration-200"
  283. />
  284. <div className="h-4">
  285. <ErrorMessage
  286. name="firstName"
  287. component="div"
  288. className="text-sm text-right text-red-600"
  289. />
  290. </div>
  291. </div>
  292. <div className="py-1">
  293. <label
  294. htmlFor="last-name"
  295. className="block text-sm font-medium text-gray-700 dark:text-gray-400"
  296. >
  297. Last name
  298. </label>
  299. <input
  300. onBlur={changeFormHandler}
  301. type="text"
  302. name="lastName"
  303. id="lastName"
  304. value={props.values.lastName}
  305. onChange={props.handleChange}
  306. autoComplete="family-name"
  307. autofill="true"
  308. className="mt-1 focus:ring-dg-primary-900 focus:border-dg-primary-900 dark:bg-dg-primary-1500 dark:text-white block w-full shadow-sm sm:text-sm border-dg-primary-600 rounded-md transition duration-200"
  309. />
  310. <div className="h-4">
  311. <ErrorMessage
  312. name="lastName"
  313. component="div"
  314. className="text-sm text-right text-red-600"
  315. />
  316. </div>
  317. </div>
  318. <div className="py-1">
  319. <label
  320. htmlFor="email"
  321. className="block text-sm font-medium text-gray-700 dark:text-gray-400"
  322. >
  323. Email
  324. </label>
  325. <input
  326. onBlur={changeFormHandler}
  327. type="email"
  328. name="email"
  329. id="email"
  330. value={props.values.email}
  331. onChange={props.handleChange}
  332. autoComplete="email"
  333. autofill="true"
  334. className="mt-1 focus:ring-dg-primary-900 focus:border-dg-primary-900 dark:bg-dg-primary-1500 dark:text-white block w-full shadow-sm sm:text-sm border-dg-primary-600 rounded-md transition duration-200"
  335. />
  336. <div className="h-4">
  337. <ErrorMessage
  338. name="email"
  339. component="div"
  340. className="text-sm text-right text-red-600"
  341. />
  342. </div>
  343. </div>
  344. {/* <div className="form-check py-2">
  345. <input
  346. className="form-check-input appearance-none h-6 w-6 border text-dg-primary-900 border-dg-primary-600 rounded-sm bg-white checked:dg-primary-900 checked:border-dg-primary-900 focus:outline-none transition duration-200 align-top bg-no-repeat bg-center bg-contain float-left cursor-pointer focus:ring-dg-primary-900"
  347. type="checkbox"
  348. value=""
  349. id="meeting"
  350. />
  351. <label
  352. className="ml-3 form-check-label inline-block text-gray-800"
  353. htmlFor="meeting"
  354. >
  355. Schedule a meeting right away
  356. </label>
  357. </div> */}
  358. <div className="py-1">
  359. <label
  360. htmlFor="coverLetter"
  361. className="block text-sm font-medium text-gray-700 dark:text-gray-400"
  362. >
  363. Cover Letter
  364. </label>
  365. <textarea
  366. onBlur={changeFormHandler}
  367. type="text"
  368. placeholder="Why would you like to join us?"
  369. className="resize-y min-h-12 h-32 rounded-md mt-1 text-base focus:ring-dg-primary-900 focus:border-dg-primary-900 dark:bg-dg-primary-1500 dark:text-white sm:text-sm border-dg-primary-600 block w-full shadow-sm transition duration-200"
  370. name="coverLetter"
  371. id="coverLetter"
  372. value={props.values.coverLetter}
  373. onChange={props.handleChange}
  374. ></textarea>
  375. <div className="h-4">
  376. <ErrorMessage
  377. name="coverLetter"
  378. component="div"
  379. className="text-sm text-right text-red-600"
  380. />
  381. </div>
  382. </div>
  383. <div className="py-1">
  384. <label
  385. htmlFor="link"
  386. className="block text-sm font-medium text-gray-700 dark:text-gray-400"
  387. >
  388. Website link or Portfolio (optional)
  389. </label>
  390. <input
  391. type="url"
  392. name="link"
  393. id="link"
  394. onBlur={changeFormHandler}
  395. value={props.values.link}
  396. onChange={props.handleChange}
  397. className="mt-1 focus:ring-dg-primary-600 focus:border-dg-primary-900 dark:bg-dg-primary-1500 dark:text-white block w-full shadow-sm sm:text-sm border-dg-primary-600 rounded-md transition duration-200"
  398. />
  399. <div className="h-4">
  400. <ErrorMessage
  401. name="link"
  402. component="div"
  403. className="text-sm text-right text-red-600"
  404. />
  405. </div>
  406. </div>
  407. <div className="py-1 dark:text-gray-500">
  408. <MyDropzone
  409. dropzoneToFormData={dropzoneToFormData}
  410. props={props}
  411. fileInput={fileInput}
  412. />
  413. <div className="h-4">
  414. <div className="text-sm text-right">{errorMsg}</div>
  415. </div>
  416. </div>
  417. <div className="items-center justify-end flex">
  418. <ReCAPTCHA
  419. sitekey={process.env.REACT_APP_SITE_KEY}
  420. ref={captchaRef}
  421. />
  422. </div>
  423. <div className=" py-3 text-right">
  424. <button
  425. type="submit"
  426. className="btn btn_primary transition-all inline-flex justify-center py-4 px-14 border border-transparent shadow-md text-sm font-semibold rounded-xl text-white bg-dg-primary-600 hover:bg-dg-primary-900 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-dg-primary-600"
  427. >
  428. Submit
  429. </button>
  430. </div>
  431. {sucMsg && (
  432. <div className={'text-sm text-right text-dg-primary-900'}>
  433. {msgText}
  434. </div>
  435. )}
  436. </div>
  437. <div className="col-span-1 sm:col-span-1 lg:col-span-1"></div>
  438. </div>
  439. </div>
  440. </div>
  441. </Form>
  442. )}
  443. </Formik>
  444. </motion.div>
  445. <motion.div
  446. className="mt-5 md:mt-0 md:col-span-1 flex items-center"
  447. initial={{ x: 60, opacity: 0 }}
  448. animate={{ x: 0, opacity: 1 }}
  449. exit={{ x: 60, opacity: 0 }}
  450. transition={{ duration: 0.3, ease: 'easeOut' }}
  451. >
  452. <img src={img} alt="Our Team's image" />
  453. </motion.div>
  454. </div>
  455. </div>
  456. );
  457. }
  458. JobForm.propTypes = {
  459. defaultPositionSelection: propTypes.string,
  460. };