Skip to content
Open
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
118 changes: 112 additions & 6 deletions src/components/Collaboration/JobApplicationForm/JobApplicationForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import { useSelector } from 'react-redux';
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { parsePhoneNumberFromString } from 'libphonenumber-js/max';
import moment from 'moment-timezone';

function JobApplicationForm() {
const [forms, setForms] = useState([]);
Expand All @@ -17,7 +19,9 @@
const [showDescription, setShowDescription] = useState(false);
const [applicantName, setApplicantName] = useState('');
const [applicantEmail, setApplicantEmail] = useState('');
const [locationTimezone, setLocationTimezone] = useState('');
const [location, setLocation] = useState('');
const [timeZone, setTimeZone] = useState('');
const [errors, setErrors] = useState({});

Check warning on line 24 in src/components/Collaboration/JobApplicationForm/JobApplicationForm.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this useless assignment to variable "errors".

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZ1pUc_nDgxE3fQDG__f&open=AZ1pUc_nDgxE3fQDG__f&pullRequest=5115

Check warning on line 24 in src/components/Collaboration/JobApplicationForm/JobApplicationForm.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the declaration of the unused 'errors' variable.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZ1pUc_nDgxE3fQDG__e&open=AZ1pUc_nDgxE3fQDG__e&pullRequest=5115
const [phone, setPhone] = useState('');
const [companyPosition, setCompanyPosition] = useState('');
const [websiteSocial, setWebsiteSocial] = useState('');
Expand All @@ -26,6 +30,66 @@

const darkMode = useSelector(state => state.theme?.darkMode);

const validateEmail = email => {
if (!email.trim()) return 'Email is required.';

if (!email.includes('@') || !email.includes('.')) {
return 'Please enter a valid email address.';
}

return '';
};

const validatePhone = phone => {
if (!phone.trim()) return 'Phone number is required.';
try {
const phoneNumber = parsePhoneNumberFromString(phone);
if (!phoneNumber || !phoneNumber.isPossible()) {

Check warning on line 47 in src/components/Collaboration/JobApplicationForm/JobApplicationForm.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer using an optional chain expression instead, as it's more concise and easier to read.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZ1pUc_nDgxE3fQDG__h&open=AZ1pUc_nDgxE3fQDG__h&pullRequest=5115
return 'Please enter a valid phone number format (e.g., +1 213-456-7890).';
}
} catch {
return 'Invalid phone number format.';
}
return '';
};

const validateLocation = location => {
if (!location.trim()) return 'Location is required.';
if (!/^[a-zA-Z\s,.-]{3,}$/.test(location)) {
return 'Please enter a valid location (e.g., City, Country).';
}
return '';
};

const validateTimeZone = tz => {
if (!tz) return 'Time zone is required.';
return '';
};

const validateAllFields = () => {
const newErrors = {};

if (!applicantName.trim()) {
newErrors.name = 'Name is required.';
}

const emailError = validateEmail(applicantEmail);
if (emailError) newErrors.email = emailError;

const phoneError = validatePhone(phone);
if (phoneError) newErrors.phone = phoneError;

const locationError = validateLocation(location);
if (locationError) newErrors.location = locationError;

const tzError = validateTimeZone(timeZone);
if (tzError) newErrors.timeZone = tzError;

return newErrors;
};

const isFormValid = () => Object.keys(validateAllFields()).length === 0;

Check warning on line 91 in src/components/Collaboration/JobApplicationForm/JobApplicationForm.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the declaration of the unused 'isFormValid' variable.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZ1pUc_nDgxE3fQDG__i&open=AZ1pUc_nDgxE3fQDG__i&pullRequest=5115

Check warning on line 91 in src/components/Collaboration/JobApplicationForm/JobApplicationForm.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this useless assignment to variable "isFormValid".

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZ1pUc_nDgxE3fQDG__j&open=AZ1pUc_nDgxE3fQDG__j&pullRequest=5115

useEffect(() => {
async function fetchForms() {
try {
Expand Down Expand Up @@ -101,6 +165,9 @@
const missing = [];
if (!applicantName.trim()) missing.push('Name');
if (!applicantEmail.trim()) missing.push('Email');
if (!phone.trim()) missing.push('Phone Number');
if (!location.trim()) missing.push('Location');
if (!timeZone.trim()) missing.push('Time Zone');

if (filteredForm?.questions?.length) {
for (const [idx, q] of filteredForm.questions.entries()) {
Expand All @@ -117,23 +184,50 @@
const handleSubmit = async e => {
e.preventDefault();

const newErrors = validateAllFields();

if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);

const errorMessages = Object.values(newErrors).filter(Boolean);

errorMessages.forEach((msg, index) => {
setTimeout(() => {
toast.error(msg, { autoClose: 4000 });
}, index * 1200); // 👈 controls spacing between toasts
});

return;
}

const missing = validateBeforeSubmit();
if (missing.length > 0) {
toast.error(`Please complete required fields: ${missing.join(', ')}`, { autoClose: 7000 });
toast.error(
<div>
<div>Please complete required fields:</div>
{missing.map((field, index) => (
<div key={index}>• {field}</div>

Check warning on line 209 in src/components/Collaboration/JobApplicationForm/JobApplicationForm.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not use Array index in keys

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HighestGoodNetworkApp&issues=AZ1pUc_nDgxE3fQDG__k&open=AZ1pUc_nDgxE3fQDG__k&pullRequest=5115
))}
</div>,
{ autoClose: 7000 },
);
return;
}

toast.success('Application submitted. A copy will be sent to your email.');

// reset (unchanged)
setApplicantName('');
setApplicantEmail('');
setLocationTimezone('');
setLocation('');
setTimeZone('');
setPhone('');
setCompanyPosition('');
setWebsiteSocial('');
setResumeFile(null);
if (resumeInputRef.current) resumeInputRef.current.value = '';
setAnswers(new Array((filteredForm?.questions ?? []).length).fill(''));
setErrors({});
};

return (
Expand Down Expand Up @@ -218,11 +312,23 @@
/>
<input
type="text"
placeholder="Location & Timezone"
placeholder="Location"
className={styles.inputField}
value={locationTimezone}
onChange={e => setLocationTimezone(e.target.value)}
value={location}
onChange={e => setLocation(e.target.value)}
/>
<select
className={styles.inputField}
value={timeZone}
onChange={e => setTimeZone(e.target.value)}
>
<option value="">Select Time Zone</option>
{moment.tz.names().map(tz => (
<option key={tz} value={tz}>
{tz}
</option>
))}
</select>
<input
type="text"
placeholder="Phone Number"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
background-color: #f9f9f9;
padding: 2%;
width: 75%;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
box-shadow: 0 0 10px rgb(0 0 0 / 10%);
}

.formTitle {
Expand Down Expand Up @@ -144,12 +144,9 @@
}

.popupOverlay {
inset: 0;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(30, 41, 65, 0.7);
background: rgb(30 41 65 / 70%);
display: flex;
align-items: center;
justify-content: center;
Expand All @@ -164,7 +161,7 @@
border-radius: 16px;
max-width: 420px;
min-width: 320px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.32);
box-shadow: 0 8px 32px rgb(0 0 0 / 32%);
display: flex;
flex-direction: column;
align-items: flex-start;
Expand Down Expand Up @@ -210,7 +207,7 @@
cursor: pointer;
padding: 8px 12px;
border-radius: 8px;
background: #ffffff;
background: #fff;
border: 1px solid #cfcfcf;
color: #222;
font-size: 14px;
Expand All @@ -221,7 +218,7 @@

.resumeLabel:hover {
background: #f6f6f6;
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.04);
box-shadow: 0 6px 18px rgb(0 0 0 / 4%);
transform: translateY(-1px);
}

Expand All @@ -247,7 +244,7 @@
.darkMode .formContainer {
background: #1c2541;
color: #e0e0e0;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.7);
box-shadow: 0 8px 24px rgb(0 0 0 / 70%);
transition: background 0.3s, color 0.3s;
}

Expand Down Expand Up @@ -286,7 +283,7 @@
.darkMode .btn {
background: #225163;
color: #f1f1f1;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
box-shadow: 0 2px 8px rgb(0 0 0 / 18%);
transition: background 0.3s, color 0.3s;
}

Expand All @@ -302,13 +299,13 @@
}

.darkMode .popupOverlay {
background: rgba(30, 41, 65, 0.85);
background: rgb(30 41 65 / 85%);
}

.darkMode .popupContent {
background: #225163;
color: #f1f1f1;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.7);
box-shadow: 0 8px 32px rgb(0 0 0 / 70%);
}

.darkMode .popupCloseBtn {
Expand Down Expand Up @@ -353,11 +350,49 @@

.darkMode .resumeLabel:hover {
background: #22364e;
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
box-shadow: 0 6px 18px rgb(0 0 0 / 35%);
}

.darkMode .resumeLabel .fileName {
background: rgba(255, 255, 255, 0.04);
background: rgb(255 255 255 / 4%);
color: #cfe7ff;
border-color: rgba(255, 255, 255, 0.05);
border-color: rgb(255 255 255 / 5%);
}

.formProfileDetailGroup select {
flex: 1;
min-width: 200px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #fff;
font-family: Arial, sans-serif;
font-size: 14px;
appearance: none;

/* Custom arrow */
background-image: url("data:image/svg+xml;utf8,<svg fill='black' height='20' viewBox='0 0 20 20' width='20' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/></svg>");
background-repeat: no-repeat;
background-position: right 10px center;
background-size: 16px 16px;
padding-right: 35px;
cursor: pointer;
}

.formProfileDetailGroup select:focus {
outline: none;
border-color: #98cb03;
box-shadow: 0 0 3px rgb(152 203 3 / 50%);
}

.darkMode .formProfileDetailGroup select {
background-color: #181a1b;
color: #f1f1f1;
border: 1px solid #444;
background-image: url("data:image/svg+xml;utf8,<svg fill='lightgray' height='20' viewBox='0 0 20 20' width='20' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/></svg>");
}

.darkMode .formProfileDetailGroup select:focus {
border-color: #9c0;
box-shadow: 0 0 3px rgb(156 255 0 / 50%);
}
Loading