diff --git a/.env.development b/.env.development deleted file mode 100644 index 2592df8b54..0000000000 --- a/.env.development +++ /dev/null @@ -1,5 +0,0 @@ -# Development environment settings -REACT_APP_APIENDPOINT="http://localhost:4500/api" -SKIP_PREFLIGHT_CHECK=true -DISABLE_ESLINT_PLUGIN=true -REACT_APP_DEF_PWD=123Welcome! \ No newline at end of file diff --git a/src/actions/collaborationAdsActions.js b/src/actions/collaborationAdsActions.js new file mode 100644 index 0000000000..6500892552 --- /dev/null +++ b/src/actions/collaborationAdsActions.js @@ -0,0 +1,35 @@ +import axios from 'axios'; +import { ENDPOINTS } from '../utils/URL'; +import {toast} from 'react-toastify'; + +export const CREATE_COLLABORATION_ADS_REQUEST = 'CREATE_COLLABORATION_ADS_REQUEST'; +export const CREATE_COLLABORATION_ADS_SUCCESS = 'CREATE_COLLABORATION_ADS_SUCCESS'; +export const CREATE_COLLABORATION_ADS_FAIL = 'CREATE_COLLABORATION_ADS_FAIL'; + +// eslint-disable-next-line import/prefer-default-export +export const createCollaborationAds = (formData) => async (dispatch) => { + + try { + dispatch({ type: CREATE_COLLABORATION_ADS_REQUEST }); + const response = await axios.post(`${ENDPOINTS.JOBS}`, formData); + + dispatch({ + type: 'CREATE_COLLABORATION_ADS_SUCCESS', + payload: response.data, // You can also use formData directly + }); + + if (response.status === 200 || response.status === 201) + toast.success('Collaboration Ads created successfully!'); + } + catch(error) { + toast.error('Error updating the details. Please try again.'); + dispatch({ type: 'CREATE_COLLABORATION_ADS_FAIL' }); + + + if (error.response?.status === 500) { + toast.error('Error updating the details. Please try again.'); + } + else { toast.error('Error updating the details. Please try again.'); } + } +}; + diff --git a/src/components/Collaboration/JobAdsCreation.jsx b/src/components/Collaboration/JobAdsCreation.jsx new file mode 100644 index 0000000000..6677526b74 --- /dev/null +++ b/src/components/Collaboration/JobAdsCreation.jsx @@ -0,0 +1,571 @@ +import { useEffect, useState, useRef } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; +import styles from './JobAdsCreation.module.css'; +import { toast } from 'react-toastify'; +import { Editor } from '@tinymce/tinymce-react'; +import { ENDPOINTS } from '../../utils/URL'; +import OneCommunityImage from '../../assets/images/logo2.png'; +import { isValidDropboxImageUrl, isValidUrl } from '../../utils/checkValidURL'; +import { createCollaborationAds } from '../../actions/collaborationAdsActions'; +import getWordCount from '../../utils/getWordCount'; +import hasPermission from '../../utils/permissions'; + +function JobAdsCreation() { + const dispatch = useDispatch(); + // const canCreateCollabJobAds = hasPermission('createCollabJobAds'); + // console.log('canCreateCollabJobAds'); + // console.log(canCreateCollabJobAds); + + const [canCreateCollabJobAds, setCanCreateCollabJobAds] = useState(null); + + useEffect(() => { + const checkPermission = async () => { + try { + const result = await dispatch(hasPermission('createCollabJobAds')); + console.log('createCollabJobAds permission:', result); + setCanCreateCollabJobAds(Boolean(result)); + } catch (error) { + console.error('Error checking permission', error); + setCanCreateCollabJobAds(false); + } + }; + + checkPermission(); + }, [dispatch]); + + // const canCreateCollabJobAdsDis = await dispatch(hasPermission('createCollabJobAds')); + const [loading, setLoading] = useState(''); + // const formFields = ['imageUrl', 'location', 'applyLink', 'jobDetailsLink']; + const textareaFields = [ + { key: 'description', display: 'Description' }, + { key: 'requirements', display: 'Requirements' }, + // { key: 'skills', display: 'Skills' }, + { key: 'projects', display: 'Projects' }, + // { key: 'whoareyou', display: 'Who are you' }, + // apply: '', + { key: 'ourCommunity', display: 'Our Community' }, + // { key: 'whoweare', display: 'Who are we' }, + ]; + + const formFields = [ + { key: 'imageUrl', display: 'Image Url' }, + { key: 'location', display: 'Location' }, + // { key: 'applyLink', display: 'Apply Link' }, + // { key: 'jobDetailsLink', display: 'Job Details Link' }, + ]; + + const initialState = { + title: '', + category: '', + /* new fields */ + // about: '', + requirements: '', + // skills: '', + projects: '', + // whoareyou: '', + // apply: '', + // whoweare: '', + ourCommunity: '', + /* newly */ + description: '', + imageUrl: '', + location: 'remote', + applyLink: '', + jobDetailsLink: '', + }; + const [formData, setFormData] = useState({ ...initialState }); + const [categories, setCategories] = useState([]); + const [positions, setPositions] = useState([]); + const [jobFormsAll, setJobFormsAll] = useState([]); + const [jobTemplates, setJobTemplates] = useState([]); + + const [errors, setErrors] = useState({}); + const darkMode = useSelector(state => state.theme.darkMode); + // const dispatch = useDispatch(); + const textareaRef = useRef(null); + + const TINY_MCE_INIT_OPTIONS = { + license_key: 'gpl', + menubar: false, + // placeholder: 'Description (10-word minimum) and reference link', + // advlist + plugins: 'autolink autoresize lists link help wordcount preview ', + toolbar: + // eslint-disable-next-line no-multi-str + 'bold italic underline link removeformat | bullist numlist outdent indent |\ + styleselect fontsizeselect | forecolor backcolor |\ + help | preview ', + branding: false, + toolbar_mode: 'sliding', + min_height: 180, + max_height: 600, + width: 700, + autoresize_bottom_margin: 1, + content_style: 'body { cursor: text !important; }', + // images_upload_handler: customImageUploadHandler, + skin: darkMode ? 'oxide-dark' : 'oxide', + content_css: darkMode ? 'dark' : 'default', + }; + + const TINY_MCE_INIT_OPTIONS_MEDIA = { + license_key: 'gpl', + menubar: false, + // placeholder: 'Description (10-word minimum) and reference link', + // advlist + plugins: 'autolink autoresize lists link help wordcount preview media', + toolbar: + // eslint-disable-next-line no-multi-str + 'bold italic underline link removeformat | bullist numlist outdent indent |\ + styleselect fontsizeselect | forecolor backcolor |\ + help | preview | media', + branding: false, + toolbar_mode: 'sliding', + min_height: 180, + max_height: 600, + autoresize_bottom_margin: 1, + content_style: 'body { cursor: text !important; }', + // images_upload_handler: customImageUploadHandler, + skin: darkMode ? 'oxide-dark' : 'oxide', + content_css: darkMode ? 'dark' : 'default', + }; + + const submitJobAds = async () => { + setLoading(true); + try { + /* + const response = await axios.post(`${ENDPOINTS.JOBS}`, formData); + console.log('response inside submitJobAds:'); + console.log(response); + if (!response.status === 201) { + throw new Error(`Failed to submit jobs: ${response.statusText}`); + } + + const data = await response.data; + console.log('data inside submitJobAds:'); + console.log(data); + + toast.success('Collaboration Ads created successfully'); + */ + dispatch(createCollaborationAds(formData)); + + // const res = await createCollaborationAds(formData); + // console.log(res); + setLoading(false); + setFormData({ ...initialState }); + } catch (error) { + toast.error('Failed to submit jobs'); + } + }; + + const fetchCategories = async () => { + try { + const response = await fetch(`${ENDPOINTS.JOB_CATEGORIES}`, { method: 'GET' }); + if (!response.ok) throw new Error(`Failed to fetch categories: ${response.statusText}`); + + const data = await response.json(); + const sortedCategories = data.categories.sort((a, b) => a.localeCompare(b)); + setCategories(sortedCategories); + } catch (error) { + toast.error('Error fetching categories'); + } + }; + + // Get all templates + const fetchTemplates = async () => { + try { + const res = await fetch(`${ENDPOINTS.GET_ALL_TEMPLATES}`, { + method: 'GET', + headers: { + Authorization: localStorage.getItem('token'), + }, + }); + + if (!res.ok) throw new Error(`Failed to fetch all Templates: ${response.statusText}`); + + const data = await res.json(); + + setJobTemplates(data.templates); + } catch (error) { + toast.error('Error fetching Templates'); + } + }; + + const fetchJobFormsAll = async () => { + try { + // eslint-disable-next-line no-console + console.log(`${ENDPOINTS.GET_ALL_JOB_FORMS}`); + const response = await fetch(`${ENDPOINTS.GET_ALL_JOB_FORMS}`, { + method: 'GET', + headers: { + Authorization: localStorage.getItem('token'), + }, + }); + if (!response.ok) throw new Error(`Failed to fetch all jobForms: ${response.statusText}`); + // eslint-disable-next-line no-console + console.log(response); + const data = await response.json(); + setJobFormsAll(data.forms); + } catch (error) { + toast.error('Error fetching jobFormsAll'); + } + }; + + const fetchPositions = async categoryInput => { + try { + const response = await fetch( + `${ENDPOINTS.JOB_POSITIONS}/?category=${encodeURIComponent(categoryInput)}`, + { + method: 'GET', + }, + ); + if (!response.ok) { + throw new Error(`Failed to fetch positions: ${response.statusText}`); + } + + const data = await response.json(); + const sortedPositions = data.positions.sort((a, b) => a.localeCompare(b)); + setPositions(sortedPositions); + } catch (error) { + toast.error('Error fetching positions'); + } + }; + + const handleSubmit = event => { + event.preventDefault(); + console.log('formData'); + console.log(formData); + if (!formData.category) { + setErrors({ category: 'Category is required' }); + toast.error('Category is required'); + return; + } + if (!formData.title) { + setErrors({ title: 'Title is required' }); + toast.error('Title is required'); + return; + } + + if (!formData.description) { + setErrors({ description: 'Description is required' }); + toast.error('Description is required'); + return; + } + // + const descriptionWordCount = getWordCount(formData.description); + // eslint-disable-next-line no-console + console.log(`word count ${descriptionWordCount}`); + + if (descriptionWordCount < 30) { + setErrors({ description: 'Description must be at least 30 characters long' }); + toast.error('Description must be at least 30 characters long'); + textareaRef.current?.focus(); + return; + } + + /* if (!formData.about) { + setErrors({ about: 'About is required' }); + toast.error('About is required'); + return; + } + // + const aboutWordCount = getWordCount(formData.about); + // eslint-disable-next-line no-console + console.log(`word count ${aboutWordCount}`); + + if (aboutWordCount < 30) { + setErrors({ about: 'About must be at least 30 words long' }); + toast.error('About must be at least 30 words long'); + textareaRef.current?.focus(); + return; + } */ + if (!formData.requirements) { + setErrors({ requirements: 'Requirements is required' }); + toast.error('Requirements is required'); + return; + } + // + const requirementsWordCount = getWordCount(formData.requirements); + // eslint-disable-next-line no-console + console.log(`word count ${requirementsWordCount}`); + + if (requirementsWordCount < 30) { + setErrors({ requirements: 'Requirements must be at least 30 words long' }); + toast.error('Requirements must be at least 30 words long'); + textareaRef.current?.focus(); + return; + } + if (!formData.projects) { + setErrors({ projects: 'Projects is required' }); + toast.error('Projects is required'); + return; + } + // + const projectsWordCount = getWordCount(formData.projects); + // eslint-disable-next-line no-console + console.log(`word count ${projectsWordCount}`); + + if (projectsWordCount < 1) { + setErrors({ projects: 'Projects must be at least 1 word long' }); + toast.error('Projects must be at least 1 word long'); + textareaRef.current?.focus(); + return; + } + if (!formData.ourCommunity) { + setErrors({ ourCommunity: 'Our Community is required' }); + toast.error('Our Community is required'); + return; + } + // + const ourCommunityWordCount = getWordCount(formData.ourCommunity); + // eslint-disable-next-line no-console + console.log(`word count ${ourCommunityWordCount}`); + + if (ourCommunityWordCount < 30) { + setErrors({ ourCommunity: 'Our Community must be at least 30 words long' }); + toast.error('Our Community must be at least 30 words long'); + textareaRef.current?.focus(); + return; + } + + if (!formData.imageUrl) { + setErrors({ imageUrl: 'ImageURL is required' }); + toast.error('ImageURL is required'); + return; + } + + if (!formData.imageUrl || !isValidDropboxImageUrl(formData.imageUrl)) { + setErrors({ imageUrl: 'Enter a valid ImageURL' }); + toast.error('Enter a valid ImageURL'); + return; + } + if (!formData.location) { + setErrors({ location: 'Location is required' }); + toast.error('Location is required'); + return; + } + if (formData.location !== 'remote') { + setErrors({ location: 'Location should be remote only' }); + toast.error('Location should be remote only'); + return; + } + + if (!formData.applyLink) { + setErrors({ applyLink: 'Apply Link is required' }); + toast.error('Apply Link is required'); + return; + } + // eslint-disable-next-line no-console + console.log('formData.applyLink'); + // eslint-disable-next-line no-console + console.log(formData.applyLink); + // eslint-disable-next-line no-console + console.log(isValidUrl(applyLink)); + if (!formData.applyLink || !isValidUrl(formData.applyLink)) { + setErrors({ applyLink: 'Enter the valid Apply Link' }); + toast.error('Enter the valid Apply Link'); + return; + } + // eslint-disable-next-line no-console + console.log(formData.jobDetailsLink); + // eslint-disable-next-line no-console + console.log(isValidUrl(formData.jobDetailsLink)); + + const emptyMsg = ''; + setErrors(emptyMsg); + submitJobAds(); + setFormData({ ...initialState }); + }; + + const handleCancel = event => { + event.preventDefault(); + const emptyMsg = ''; + setErrors(emptyMsg); + setFormData({ ...initialState }); + }; + + const handleChange = event => { + const { name, value } = event.target; + setFormData(prev => ({ ...prev, [name]: value })); + // Clear error for this specific field automatically + setErrors(prev => ({ + ...prev, + [name]: '', + })); + if (name === 'category') { + if (value === '') { + setPositions([]); + return; + } + fetchPositions(value); + } + if (name === 'applyLink') { + // eslint-disable-next-line no-console + console.log('APPLYLINK'); + + // eslint-disable-next-line no-console + console.log(value); + } + }; + + useEffect(() => { + fetchCategories(); + // fetchPositions(); + fetchJobFormsAll(); + fetchTemplates(); + }, []); + + return ( +
+ {canCreateCollabJobAds === false ? ( +
You do not have permission to create Collaboration Job Ads.
+ ) : canCreateCollabJobAds === true ? ( + <> +
+ + One Community Logo + +
+
+

Collaboration Ads Creation

+
+ +
+
+ + +
+ + {!errors.category ? null :
{errors.category}
} + +
+ + +
+ + {!errors.title ? null :
{errors.title}
} + {textareaFields.map((field, display, idx) => ( + <> +
+ + + + setFormData(prev => ({ ...prev, [field.key]: newVal })) + } + /> +
+ {!errors[field.key] ? null : ( +
{errors[field.key]}
+ )} + + ))} + {formFields.map((field, display, idx) => ( + <> +
+ + +
+ {errors[field.key] &&
{errors[field.key]}
} + + ))} +
+ + +
+ + {!errors.applyLink ? null :
{errors.applyLink}
} + +
+ + +
+
+ + ) : ( + '' + )} +
+ ); +} + +export default JobAdsCreation; diff --git a/src/components/Collaboration/JobAdsCreation.module.css b/src/components/Collaboration/JobAdsCreation.module.css new file mode 100644 index 0000000000..b4cae32e41 --- /dev/null +++ b/src/components/Collaboration/JobAdsCreation.module.css @@ -0,0 +1,212 @@ +.jobAds-header { + display:flex; + justify-content: center; + align-items: center; +} +.jobAds-creation { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + margin: 0 auto; + padding: 20px; + gap:20px; + background-color: #f9f9f9; +} +.jobAds-creation .header img { + height: auto; + margin: 0 auto; + flex-direction: column; + +} +.jobAds-creation .h3 { + justify-content: center; + display:flex; + align-items: center; +} +.title-header { + display: flex; + justify-content: center; +} +.input-error { + display: flex; + flex-direction: row; + align-items: flex-start; /*center; + /* justify-content: center; */ + /* width: 100%; */ + width: 1000px; + padding-left: 50px; + padding-top:10px; + margin-left:50px; +/* background-color: aliceblue; */ +} +.input-item{ + display:flex; + flex-direction: row; + align-items: flex-start; + justify-content:center; + margin-bottom: 1rem; + /* text-align: center; */ + text-align:left; + + background-color: blueviolet; +} +.input-label { + display: flex; + align-items: center; + width:200px; + text-align:justify; /*center; + background-color: #445A77; */ + } +.jobAds-input{ +max-width: 100%; +width: 700px; +/* background-color: #1E88E5; */ +} +/*textarea { + width:100%; + height:auto; + min-height:150px; + background-color: aquamarine; +}*/ +input { + width:100%; + height:30px; +} +.error { color: red; + font-size: 0.9em; + margin-top: 5px; + margin-bottom: 10px; + text-align: center; +} +.jobAds-creation-button-group { + margin-top:20px; + display: flex; + width: 90%; + justify-content: space-evenly; +} +.submit-button,.cancel-button { + /*padding: 10px 20px; */ + padding: 10px; + font-size: 1em; + cursor: pointer; + transition: background 0.2s ease; + +} +.jobAds-creation-container { + display: flex; + flex-direction: column; + justify-items: center; + width: 90%; + margin: 0 auto; + background-color: #fff; + border: 1px solid #ddd; +} + + +.user-collaboration-dark-mode { + background-color: #121212; /*#1B2A41;*/ + color: #E0E0E0; /* #f1f1f1; */ +} +.user-collaboration-dark-mode .jobAds-creation-container { + background-color: #1E1E1E; +} +.user-collaboration-dark-mode .jobAds-creation-container .jobAds-creation-button-group{ + background-color:#2A2A2A; + /* #32455A; /* lighter gray-blue background */ + color:#E0E0E0; + border: 1px solid #445A77; /* optional for definition */ + +} + +/* Title */ +.user-collaboration-dark-mode h2 { + text-align: center; + color: #1E88E5; /* accent blue */ + margin-bottom: 2rem; +} + +/* Labels */ +.user-collaboration-dark-mode label { + color: #B0BEC5; /* muted gray for labels */ + } + + /* Inputs & Selects */ +.user-collaboration-dark-mode input, +.user-collaboration-dark-mode select, +.user-collaboration-dark-mode textarea { + background-color: #2A2A2A; + color: #E0E0E0; +} +.user-collaboration-dark-mode .jobAds-creation-container .input-label{ + background-color:transparent; /* #2E3A4D; /* slightly lighter than main bg */ + color:#A0A0A0; /* #f1f1f1; /* keep text readable */ +} +.user-collaboration-dark-mode textarea::placeholder, +.user-collaboration-dark-mode select::placeholder, +.user-collaboration-dark-mode input::placeholder +{ + + color: #888; /* softer placeholders */ +} +.user-collaboration-dark-mode .jobAds-creation-button-group .submit-button { + background-color: #1E88E5; + color: #fff; + border: none; + padding: 0.7rem 1.5rem; + border-radius: 6px; + cursor: pointer; + font-weight: bold; + transition: background 0.2s ease; +} + +.user-collaboration-dark-mode .jobAds-creation-button-group .submit-btn:hover { + background-color: #1565C0; /* darker blue on hover */ +} + +/* Cancel Button */ +.user-collaboration-dark-mode .jobAds-creation-button-group .cancel-btn { + background-color: #555; + color: #fff; + border: none; + padding: 0.7rem 1.5rem; + border-radius: 6px; + cursor: pointer; + font-weight: bold; + transition: background 0.2s ease; +} + +.user-collaboration-dark-mode .jobAds-creation-button-group .cancel-btn:hover { + background-color: #666; +} + +@media (max-width: 768px) { + .jobAds-creation { + padding: 10px; + } + .jobAds-input { + width: 100%; + } + .input-error { + width: 100%; + } + .input-item { + flex-direction: column; + align-items: center; + } + .input-label { + width: 100px; + font-size: 0.9em; + } + .submit-button, .cancel-button { + width: 100px; + font-size: 0.9em; + padding: 8px 16px; + } + .jobAds-creation-container { + width: 100%; + padding-top: 10px; + padding-bottom: 50px; + } +} \ No newline at end of file diff --git a/src/components/Collaboration/JobApplyLink.jsx b/src/components/Collaboration/JobApplyLink.jsx new file mode 100644 index 0000000000..b98be3e893 --- /dev/null +++ b/src/components/Collaboration/JobApplyLink.jsx @@ -0,0 +1,215 @@ +import { Route, useParams } from 'react-router-dom'; +import { useSelector } from 'react-redux'; +import { useEffect, useState } from 'react'; + +import { toast } from 'react-toastify'; +import { ENDPOINTS } from '../../utils/URL'; + +//import { ApiEndpoint } from '../../utils/URL'; +import OneCommunityImage from '../../assets/images/logo2.png'; +import styles from '../Collaboration/JobApplyLink.module.css'; + +function JobApplyLink() { + const { formId } = useParams(); + const [jobFormsAll, setJobFormsAll] = useState([]); + + const [jobForms, setJobForms] = useState([]); + + const [loading, setLoading] = useState(false); + const [applyLink, setApplyLink] = useState(''); + const fetchJobFormsAll = async () => { + try { + // eslint-disable-next-line no-console + console.log(`${ENDPOINTS.GET_ALL_JOB_FORMS}`); + const response = await fetch(`${ENDPOINTS.GET_ALL_JOB_FORMS}`, { + method: 'GET', + headers: { + Authorization: localStorage.getItem('token'), + }, + }); + if (!response.ok) throw new Error(`Failed to fetch all jobForms: ${response.statusText}`); + // eslint-disable-next-line no-console + console.log(response); + const data = await response.json(); + setJobFormsAll(data.forms); + } catch (error) { + toast.error('Error fetching jobFormsAll'); + } + }; + useEffect(() => { + fetchJobFormsAll(); + }, []); + + const getJobForms = async () => { + // console.log(`formId: ${formId}`); + console.log(`applyLink:${applyLink}`); + const formId = new URL(applyLink).pathname.split('/').pop(); + + try { + setLoading(true); + /*console.log(`res is ${ENDPOINTS.APIEndpoint()}/jobforms/${formId}`); + const response = await fetch(`${ENDPOINTS.APIEndpoint()}/jobforms/${formId}`, { + method: 'get', + headers: { + Authorization: localStorage.getItem('token'), + }, + }); + */ + console.log(`res is ${ENDPOINTS.APIEndpoint()}/jobforms/${formId}`); + const response = await fetch(`${ENDPOINTS.APIEndpoint()}/jobforms/${formId}`, { + method: 'get', + headers: { + Authorization: localStorage.getItem('token'), + }, + }); + + console.log(response); + if (!response.ok) throw new Error(`Failed to fetch all Templates: ${response.statusText}`); + + const data = await response.json(); + console.log(data); + // console.log(data.template); + // console.log(data.template.fields.length); + + setJobForms(data); + // console.log(jobTemplate); + // console.log(jobTemplate.template.fields.length); + setLoading(false); + } catch (error) { + toast.error('Error fetching JobForms'); + } + }; + + useEffect(() => { + console.log(`before calling getTemplate`); + getJobForms(); + }, []); + const darkMode = useSelector(state => state.theme.darkMode); + + const handleSubmit = e => { + e.preventDefault(); + alert('form submitted'); + /* try { + setLoading(true); + console.log(`res is ${ENDPOINTS.APIEndpoint()}/jobforms/${formId}`); + const response = await fetch(`${ENDPOINTS.APIEndpoint()}/jobforms/${formId}`, { + method: 'get', + headers: { + Authorization: localStorage.getItem('token'), + }, + }); + console.log(response); + if (!response.ok) throw new Error(`Failed to fetch all Templates: ${response.statusText}`); + + const data = await response.json(); + console.log(data); + // console.log(data.template); + // console.log(data.template.fields.length); + }*/ + }; + const handleChange = event => { + const { name, value } = event.target; + // eslint-disable-next-line no-console + console.log(value); + setApplyLink(value); + }; + useEffect(() => { + console.log(`before calling getJobForms`); + getJobForms(); + }, [applyLink]); + + const resetForm = e => { + alert('form cancelled'); + }; + return !loading ? ( +
+
+ + One Community Logo + +
+ + +

{jobForms.form && jobForms.form.title}

+
{jobForms.form && jobForms.form.description}
+ +
+ {jobForms.form && jobForms.form.questions && jobForms.form.questions.length > 0 + ? jobForms.form.questions.map(question => ( +
+

{question.questionText}

+ + {question.questionType === 'textbox' ? ( + + ) : question.questionType === 'textarea' ? ( +