You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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