i-darrshan's picture
update
051fc03
import React, { useState, useRef } from 'react';
import { FaMapPin, FaCalendarAlt, FaBriefcase } from 'react-icons/fa';
import styled from 'styled-components';
import { createGlobalStyle } from 'styled-components';
import IntlTelInput from 'intl-tel-input/reactWithUtils';
import 'intl-tel-input/styles';
import axios from 'axios';
import privacyPolicyData from "../../data/privacyPolicyData";
import { showCustomAlert, showCustomError, showCustomSuccess } from '../../utils/showalerts';
const GlobalStyles = createGlobalStyle`
.iti__country {
color: #333;
background-color: #f8f9fa;
}
.iti__country:hover {
background-color: #3f7ad3;
color: white;
}
.iti__country.iti__active {
background-color: #3f7ad3;
color: white;
}
.flexer {
display: grid;
grid-template-columns: 1fr;
}
@media (min-width: 768px) {
.flexer {
grid-template-columns: 60% 40%;
gap: 2rem;
}
}
ul li::before {
content: "\f00c";
font-family: "Font Awesome 5 Free";
font-weight: 900;
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
color: green;
}
`;
const FormContainer = styled.div`
display: flex;
flex-direction: column;
align-items: center;
max-width: 900px;
margin-top: 2rem;
width: 100%;
`;
const Form = styled.form`
display: flex;
flex-direction: column;
gap: 1rem;
width: 80%;
max-height: fit-content !important;
align-self: center;
justify-self: center;
background-color: #2d3748;
padding: 24px;
border-radius: 0.375rem;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
`;
const Input = styled.input`
width: 100%;
padding: 0.75rem 1rem;
font-size: 1rem;
border: 3px solid ${(props) => (props.invalid ? '#de493e' : '#3267B9')};
border-radius: 0.375rem;
background-origin: border-box;
color: black;
&:focus {
outline: none;
box-shadow: 0 0 8px rgba(63, 122, 211, 0.5);
}
`;
const IntlTelInputWrapper = styled.div`
width: 100%;
.iti {
width: 100%;
color: black;
}
input {
width: 100%;
padding: 0.75rem 1rem;
font-size: 1rem;
border: 3px solid ${(props) => (props.invalid ? '#de493e' : '#3267B9')};
border-radius: 0.375rem;
background-origin: border-box;
color: black;
&:focus {
outline: none;
box-shadow: 0 0 8px rgba(63, 122, 211, 0.5);
}
}
`;
const Textarea = styled.textarea`
width: 100%;
padding: 0.75rem 1rem;
font-size: 1rem;
border: 3px solid #3267B9;
border-radius: 0.375rem;
color: black;
&:focus {
outline: none;
box-shadow: 0 0 8px rgba(63, 122, 211, 0.8);
}
`;
const Select = styled.select`
width: 100%;
padding: 0.75rem 1rem;
font-size: 1rem;
border: 3px solid #3267B9;
border-radius: 0.375rem;
color: black;
&:focus {
outline: none;
box-shadow: 0 0 8px rgba(63, 122, 211, 0.5);
}
`;
const SubmitButton = styled.button`
width: 100%;
padding: 0.75rem 1.5rem;
background: #3267B9;
color: white;
font-weight: 600;
border-radius: 0.375rem;
transition: background 0.3s;
&:hover {
background: #3f7ad3;
}
`;
const FileInputWrapper = styled.div`
position: relative;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
`;
const FileInputButton = styled.label`
display: inline-block;
padding: 0.75rem 1.5rem;
background-color: #3f7ad3;
color: white;
border-radius: 30px;
cursor: pointer;
text-align: center;
width: 100%;
max-width: 250px;
text-transform: uppercase;
font-weight: 500;
transition: background-color 0.3s ease;
&:hover {
background-color: #669df0;
}
`;
const FileInput = styled.input`
display: none;
`;
const FileNameDisplay = styled.span`
margin-top: 0.5rem;
font-size: 0.875rem;
color: #A0AEC0;
`;
const ConsentWrapper = styled.div`
display: grid;
grid-template-columns: auto 1fr;
gap: 0.5rem;
color: #d5d5d5;
`;
const JobPageSection = ({ job }) => {
const {
title,
location,
status,
postedDate,
description,
responsibilities = [],
skillsRequired = [],
desirableSkills = [],
perks = []
} = job;
const [phone, setPhone] = useState('');
const [isValid, setIsValid] = useState(false);
const [fileName, setFileName] = useState('');
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [experience, setExperience] = useState('');
const [role, setRole] = useState(title); // Set initial role as the job title
const [linkedin, setLinkedin] = useState('');
const [consent, setConsent] = useState(false); // State to track consent
const [errors, setErrors] = useState({}); // State for storing error messages
const fieldRefs = useRef({
name: React.createRef(),
email: React.createRef(),
phone: React.createRef(),
linkedin: React.createRef(),
experience: React.createRef(),
});
const validateName = (name) => name.length >= 3;
const validateEmail = (email) => /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email);
const validatePhone = () => isValid;
const validateLinkedin = (linkedin) => /^https?:\/\/(www\.)?linkedin\.com\//.test(linkedin);
const formatStatus = (status) => status === "OPENED" ? "Open" : "Closed";
const formatDate = (date) => {
const options = { year: 'numeric', month: 'long', day: 'numeric' };
return new Date(date).toLocaleDateString(undefined, options);
};
const handleInputChange = (e, setter, fieldName) => {
setter(e.target.value);
setErrors({});
};
const handleBlur = (fieldName) => {
const validationErrors = {};
if (fieldName === 'name' && !validateName(name)) {
validationErrors.name = 'Name should be at least 3 characters long.';
}
if (fieldName === 'email' && !validateEmail(email)) {
validationErrors.email = 'Invalid email format.';
}
if (fieldName === 'phone' && !validatePhone()) {
validationErrors.phone = 'Please enter a valid phone number.';
}
if (fieldName === 'linkedin' && !validateLinkedin(linkedin)) {
validationErrors.linkedin = "LinkedIn URL should start with 'https://www.linkedin.com'.";
}
setErrors(validationErrors);
console.log(isValid);
if (Object.keys(validationErrors).length > 0) {
console.log(fieldRefs.current[fieldName]);
fieldRefs.current[fieldName].current.focus();
}
};
const handleFileChange = (e) => {
const file = e.target.files[0];
const validExtensions = ['pdf', 'docx', 'doc'];
if (file) {
const fileExtension = file.name.split('.').pop().toLowerCase();
if (!validExtensions.includes(fileExtension)) {
showCustomError("Invalid file type. Please upload a .pdf, .docx, or .doc file.");
setFileName('');
e.target.value = '';
return;
}
setFileName(file.name);
}
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!name || !email || !phone || !experience || !fileName || !linkedin) {
showCustomError("Please fill out all required fields.");
return;
}
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!emailRegex.test(email)) {
showCustomError("Please enter a valid email address.");
return;
}
if (!isValid) {
showCustomError("Please enter a valid phone number.");
return;
}
//Set the policy version number
const policyVersion = privacyPolicyData.version;
try {
const formData = new FormData();
formData.append('name', name);
formData.append('email', email);
formData.append('phone', phone);
formData.append('experience', experience);
formData.append('linkedin', linkedin);
formData.append('consent', consent);
formData.append('policyVersion', policyVersion);
formData.append('role', role); // Add role to the form data
formData.append('resume', document.getElementById('fileInput').files[0]);
showCustomAlert("Please wait.... Your submission is in progress");
const response = await axios.post('/api/submit-job-application', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
});
if (response.status >= 200 && response.status < 300) {
// Assuming the backend sends a success message in the response data
const successMessage = response.data.message || "Your application has been submitted successfully!";
showCustomSuccess(successMessage);
// Optionally clear the form fields here
// setYourState('');
} else {
// Handle cases where the response status is not successful
const errorMessage = response.data?.message || "An unexpected error occurred. Please try again.";
showCustomError(errorMessage);
}
// Reset form fields
setName('');
setEmail('');
setPhone('');
setExperience('');
setFileName('');
setLinkedin('');
setConsent(false);
document.getElementById("fileInput").value = '';
} catch (error) {
console.error('Error:', error);
// Reset form fields
setName('');
setEmail('');
setPhone('');
setExperience('');
setFileName('');
setLinkedin('');
setConsent(false);
if (error.response.data.error ==="Unable to send email for the confirmation of submission, but the application was received suvvessfully!"){
showCustomAlert("Unable to send email for the confirmation of submission, but the application was received suvvessfully!");
}else{
showCustomError(error.response.data.error || "An unexpected error occurred. Please try again.");
}
}
};
return (
<>
<GlobalStyles />
<div className="container" style={{ margin: "0 auto", padding: "24px" }}>
<h2
style={{
fontSize: "2.5rem",
fontWeight: "600",
color: "#3267B9",
marginBottom: "1rem",
}}
data-testid="job-title"
>
{title}
</h2>
<div
style={{
display: "flex",
gap: "1rem",
color: "#A0AEC0",
marginBottom: "2rem",
fontSize: "0.875rem",
}}
>
<p style={{ display: "flex", alignItems: "center" }}>
<FaMapPin style={{ marginRight: "0.5rem" }} />
{location}
</p>
<p style={{ display: "flex", alignItems: "center" }}>
<FaBriefcase style={{ marginRight: "0.5rem" }} />
{formatStatus(status)}
</p>
<p style={{ display: "flex", alignItems: "center" }}>
<FaCalendarAlt style={{ marginRight: "0.5rem" }} />
Posted on {formatDate(postedDate)}
</p>
</div>
<div className="flexer">
<div>
<div>
<h3
style={{
fontSize: "1.5rem",
fontWeight: "600",
color: "white",
marginBottom: "1rem",
}}
>
Job Description
</h3>
<p style={{ fontSize: "1rem", color: "#A0AEC0" }}>
{description}
</p>
</div>
<div
className="pt-5"
style={{ display: "flex", flexDirection: "column", gap: "2rem" }}
>
<div>
<h3
style={{
fontSize: "1.5rem",
fontWeight: "600",
color: "white",
marginBottom: "1rem",
}}
>
Responsibilities
</h3>
<ul
style={{
paddingLeft: "1.5rem",
listStyleType: "disc",
color: "#A0AEC0",
}}
>
{responsibilities.map((item, index) => (
<li key={index} style={{ marginBottom: "0.75rem" }}>
{item}
</li>
))}
</ul>
</div>
<div>
<h3
style={{
fontSize: "1.5rem",
fontWeight: "600",
color: "white",
marginBottom: "1rem",
}}
>
Skills Required
</h3>
<ul
style={{
paddingLeft: "1.5rem",
listStyleType: "disc",
color: "#A0AEC0",
}}
>
{skillsRequired.map((item, index) => (
<li key={index} style={{ marginBottom: "0.75rem" }}>
{item}
</li>
))}
</ul>
</div>
<div>
<h3
style={{
fontSize: "1.5rem",
fontWeight: "600",
color: "white",
marginBottom: "1rem",
}}
>
Desirable Skills
</h3>
<ul
style={{
paddingLeft: "1.5rem",
listStyleType: "disc",
color: "#A0AEC0",
}}
>
{desirableSkills.map((item, index) => (
<li key={index} style={{ marginBottom: "0.75rem" }}>
{item}
</li>
))}
</ul>
</div>
<div>
<h3
style={{
fontSize: "1.5rem",
fontWeight: "600",
color: "white",
marginBottom: "1rem",
}}
>
What We Offer
</h3>
<ul
style={{
paddingLeft: "1.5rem",
listStyleType: "disc",
color: "#A0AEC0",
}}
>
{perks.map((item, index) => (
<li key={index} style={{ marginBottom: "0.75rem" }}>
{item}
</li>
))}
</ul>
</div>
</div>
</div>
{status === "OPENED" && (
<div style={{ flex: 1, minWidth: "200px" }}>
<div style={{ alignItems: "center", textAlign: "center" }}>
<h3
style={{
fontSize: "1.5rem",
fontWeight: "600",
color: "white",
marginBottom: "1rem",
}}
>
Apply for This Position
</h3>
<p
style={{
fontSize: "0.875rem",
color: "#A0AEC0",
marginBottom: "1.5rem",
}}
>
Fill in your details and upload your resume.
</p>
</div>
<FormContainer>
<Form onSubmit={handleSubmit}>
<label style={{ display: "flex" }}>
Name{" "}
<div style={{ color: "red", marginLeft: "10px" }}>*</div>
</label>
<Input
ref={fieldRefs.current.name}
type="text"
value={name}
placeholder="Your Full Name"
onChange={(e) => {
// Only allow letters (including spaces and accented characters)
const regex = /^[A-Za-z\s]*$/;
if (regex.test(e.target.value) || e.target.value === '') {
handleInputChange(e, setName, 'name');
}
}}
invalid={errors.name}
onBlur={() => handleBlur('name')}
required
/>
{errors.name && <span style={{ color: '#de493e', fontSize: '0.875rem' }}>{errors.name}</span>}
<label style={{ display: "flex" }}>
E-Mail{" "}
<div style={{ color: "red", marginLeft: "10px" }}>*</div>
</label>
<Input
ref={fieldRefs.current.email}
type="email"
value={email}
onChange={(e) => handleInputChange(e, setEmail, 'email')}
invalid={errors.email}
onBlur={() => handleBlur('email')}
placeholder="Your Email Address"
required
/>
{errors.email && <span style={{ color: '#de493e', fontSize: '0.875rem' }}>{errors.email}</span>}
<label style={{ display: "flex" }}>
Phone Number{" "}
<div style={{ color: "red", marginLeft: "10px" }}>*</div>
</label>
<IntlTelInputWrapper invalid={errors.phone}>
<IntlTelInput
value={phone}
onChangeNumber={setPhone}
onChangeValidity={setIsValid}
initOptions={{
initialCountry: "in",
separateDialCode: true,
}}
inputProps={{
placeholder: "Contact Number",
required: true,
style: { width: "100%" },
}}
onPhoneBlur={() => handleBlur('phone')}
/>
</IntlTelInputWrapper>
{errors.phone && <span style={{ color: '#de493e', fontSize: '0.875rem' }}>{errors.phone}</span>}
<label style={{ display: "flex" }}>Experience <div style={{ color: "red", marginLeft: "10px" }}>*</div> </label>{" "}
{/*<div style={{color:'red', marginLeft:"10px"}}>*</div> */}
<Textarea
value={experience}
onChange={(e) => setExperience(e.target.value)}
placeholder="Relevant Experience"
></Textarea>
{/* Role Selection Dropdown */}
<div style={{ marginBottom: "1.5rem" }}>
<label
style={{
display: "flex",
fontSize: "1rem",
color: "#EDF2F7",
}}
>
Role
<div style={{ color: "red", marginLeft: "10px" }}>*</div>
</label>
<Select
value={role}
onChange={(e) => setRole(e.target.value)}
disabled
>
<option value="Head of Sales">Head of Sales</option>
<option value="Web Designer & Developer">
Web Designer & Developer
</option>
<option value="Senior Software Developer">
Senior Software Developer
</option>
<option value="System Administrator (Part-time)">
System Administrator (Part-time)
</option>
<option value="Scientific Curators">
Scientific Curators
</option>
</Select>
</div>
{/* strategy Field */}
<label
style={{
display: "flex",
fontSize: "1rem",
color: "#EDF2F7",
}}
>
LinkedIn Profile URL
<div style={{ color: "red", marginLeft: "10px" }}>*</div>
</label>
<Input
ref={fieldRefs.current.linkedin}
type="text"
value={linkedin}
onChange={(e) => handleInputChange(e, setLinkedin, 'linkedin')}
invalid={errors.linkedin}
onBlur={() => handleBlur('linkedin')}
placeholder="Provide us your LinkedIn to know you more"
required
></Input>
{errors.linkedin && <span style={{ color: '#de493e', fontSize: '0.875rem' }}>{errors.linkedin}</span>}
<div style={{ marginBottom: "1.5rem" }}>
<label
style={{
display: "flex",
fontSize: "1rem",
color: "#EDF2F7",
}}
>
Upload Resume{" "}
<div style={{ color: "red", marginLeft: "10px" }}>*</div>
</label>
<FileInputWrapper>
<FileInputButton htmlFor="fileInput">
Choose File
</FileInputButton>
<FileInput
id="fileInput"
data-testid="file-input"
type="file"
accept=".pdf,.docx,.doc"
onChange={handleFileChange}
required
/>
{fileName && (
<FileNameDisplay>{fileName}</FileNameDisplay>
)}
</FileInputWrapper>
</div>
{/* Consent Checkbox */}
<ConsentWrapper>
<Input
type="checkbox"
id="gdpr-consent"
checked={consent}
onChange={() => setConsent(!consent)}
required
/>
<label htmlFor="gdpr-consent">
I consent to the processing of my submitted data in accordance with the{' '}
<a href="/privacy-policy" target="_blank" rel="noopener noreferrer">
<i style={{ color: "#3f7ad3" }}>Privacy Policy</i>
</a>.{' '}
<span style={{ color: "red", marginLeft: "5px" }}>*</span>
</label>
</ConsentWrapper>
<SubmitButton type="submit" data-testid="submit-btn">
Submit Application
</SubmitButton>
</Form>
</FormContainer>
</div>
)}
</div>
</div>
</>
);
};
export default JobPageSection;