Skip to content
Snippets Groups Projects
useFetchJobsList.js 8.41 KiB
Newer Older
Nitin Jadhav's avatar
Nitin Jadhav committed
import { useEffect, useState } from "react";
import { parseDateFromTimeHtml } from "helpers/date-helpers";
import { stripHtml } from "helpers/helpers";
import { useFetch } from "helpers/useFetch";
import { locations, positionTypes } from "data/jobs-data.json";
import { cloneDeep } from "lodash-es";
Nitin Jadhav's avatar
Nitin Jadhav committed

/* Custom hook for jobs fetching and filtering. If jobLocation is not provided, assume all job locations */
export function useFetchJobsList(filters, jobLocation) {
Nitin Jadhav's avatar
Nitin Jadhav committed
  const { data, loading, error } = useFetch(
    "jobs?_format=json&source=contenthub",
    []
  );

  const [extendedJobs, setExtendedJobs] = useState([]);
  const [filteredJobs, setFilteredJobs] = useState([]);
  const [locationJobsCount, setLocationJobsCount] = useState([]);
  const [positionJobsCount, setPositionJobsCount] = useState([]);

  useEffect(() => {
    /* Extend job object with metadata necessary for display and search */
    if (data && data.length) {
      const dataAtLocation = jobLocation
        ? data.filter((datum) =>
            datum.field_jobs_duty_station.includes(jobLocation) ||
            // RT ticket #598961 - display jobs available in all sites
            datum.field_jobs_duty_station.includes("All EMBL Sites")
          )
        : data;
      let extendedJobs = extendJobsObjects(dataAtLocation); //remove nulls due to potential error while extending
Nitin Jadhav's avatar
Nitin Jadhav committed
      setExtendedJobs(extendedJobs);

      //LOCATIONS WITH JOB COUNT: count jobs at each location
      const locationsWithJobCount = Object.entries(locations).map(
        ([location, locationTitle]) => {
          return {
            location,
            locationTitle,
            jobCount: getJobsAtLocationCount(extendedJobs, location),
          };
        }
      );
      //sort on location titles
      locationsWithJobCount.sort((obj1, obj2) =>
        obj1.locationTitle.localeCompare(obj2.locationTitle)
      );
      setLocationJobsCount(locationsWithJobCount);

      //JOB TYPES WITH JOB COUNT: count jobs of each jobType
      const positionsWithJobCount = Object.entries(positionTypes).map(
        ([position, positionTitle]) => {
          return {
            position,
            positionTitle,
            jobCount: getPositionsOfTypeCount(extendedJobs, position),
          };
        }
      );
      positionsWithJobCount.sort((obj1, obj2) =>
        obj1.positionTitle.localeCompare(obj2.positionTitle)
      );
      setPositionJobsCount(positionsWithJobCount);
    }
  }, [data]);

  useEffect(() => {
    if (extendedJobs?.length) {
      let newFilteredJobs = [...extendedJobs];

      if (filters) {
        const {
          searchTerm,
          selectedLocations,
          selectedJobTypes,
          selectedClosingDateSortDir,
        } = filters;

        if (searchTerm) {
          newFilteredJobs = newFilteredJobs.filter((job) =>
Nitin Jadhav's avatar
Nitin Jadhav committed
            jobContainsSearchTerm(job, searchTerm.toLowerCase())
Nitin Jadhav's avatar
Nitin Jadhav committed
          );

          newFilteredJobs = cloneDeep(newFilteredJobs);
          newFilteredJobs = highlightTerm(newFilteredJobs, searchTerm);
Nitin Jadhav's avatar
Nitin Jadhav committed
        }

        if (selectedLocations.length) {
          newFilteredJobs = newFilteredJobs.filter((job) =>
            selectedLocations.find((location) => {
              /*
               * Exception: when job title contains arise (fellowship), dont show it under EMBO location
               * This was a requirement from Lenka Stejskal, HR Recruitment Partner | EMBL Recruitment
               * Tracked here: https://trello.com/c/p7lty9fI/204-hiding-arise-fellowships-under-embo-location-filter
               */
              if (
                job.$lowercase_title.includes("arise") &&
                (location === "EMBO" || job.field_jobs_group === "EMBO")
              ) {
                return false;
              }

Nitin Jadhav's avatar
Nitin Jadhav committed
              return (
                (location === "EMBO" && job.field_jobs_group === "EMBO") ||
                job.$lowercase_field_jobs_duty_station.includes(location) ||
                job.$stripped_field_jobs_duty_station.includes("All EMBL Sites")
Nitin Jadhav's avatar
Nitin Jadhav committed
              );
            })
          );
        }
        if (selectedJobTypes.length) {
          newFilteredJobs = newFilteredJobs.filter((job) =>
            selectedJobTypes.includes(job.field_jobs_type_key)
          );
        }

        // sort on closing date
        newFilteredJobs.sort((job1, job2) => {
          //$field_jobs_expiration is a date object
          if (selectedClosingDateSortDir === "ASC") {
            return job1.$field_jobs_expiration - job2.$field_jobs_expiration;
          }
          if (selectedClosingDateSortDir === "DESC") {
            return job2.$field_jobs_expiration - job1.$field_jobs_expiration;
          } else return 0;
        });
      }
      setFilteredJobs(newFilteredJobs);
    }
  }, [extendedJobs, filters]);

  return { filteredJobs, positionJobsCount, locationJobsCount, loading, error };
}

const getJobsAtLocationCount = (jobsList, locationKey) => {
  const lowercaseKey = locationKey.toLowerCase();

  //EMBO is a special case as the duty station is Heidelberg
  const jobsAtLocation = jobsList.filter(
    ({
      field_jobs_group,
      $lowercase_field_jobs_duty_station,
      $stripped_field_jobs_duty_station,
    }) => {
      return (
        (locationKey === "EMBO" && field_jobs_group === "EMBO") ||
        $lowercase_field_jobs_duty_station.includes(lowercaseKey) ||
        $stripped_field_jobs_duty_station === "All EMBL Sites"
Nitin Jadhav's avatar
Nitin Jadhav committed
      );
    }
  );
  return jobsAtLocation.length;
};

const getPositionsOfTypeCount = (jobsList, jobTypekey) => {
  const jobsAtLocation = jobsList.filter(
    ({ field_jobs_type_key }) => field_jobs_type_key === jobTypekey
  );
  return jobsAtLocation.length;
};

export function extendJobsObjects(jobs = []) {
  let extendedJobs = jobs.map((job) => {
    try {
      return {
        ...job,
        $lowercase_title: job.title.toLowerCase(),
        $lowercase_field_jobs_type: job.field_jobs_type?.toLowerCase() || "",
Nitin Jadhav's avatar
Nitin Jadhav committed
        $stripped_field_jobs_duty_station: stripHtml(
          job.field_jobs_duty_station
        ),
        $lowercase_field_jobs_duty_station: stripHtml(
          job.field_jobs_duty_station
        )?.toLowerCase(),
        $stripped_field_jobs_description: stripHtml(job.field_jobs_description),
        $lowercase_field_jobs_description: stripHtml(
          job.field_jobs_description
        ).toLowerCase(),
        $lowercase_field_jobs_your_role: stripHtml(
          job.field_jobs_your_role
        ).toLowerCase(),
        $lowercase_field_jobs_you_have: stripHtml(
          job.field_jobs_you_have
        ).toLowerCase(),
        $lowercase_field_jobs_you_also_have: stripHtml(
          job.field_jobs_you_also_have
        ).toLowerCase(),
Nitin Jadhav's avatar
Nitin Jadhav committed
        $field_jobs_expiration: parseDateFromTimeHtml(
          job.field_jobs_expiration
        ),
        $uid: Math.random().toString(),
Nitin Jadhav's avatar
Nitin Jadhav committed
      };
    } catch (e) {
      console.error("error: ", job);
      return null;
    }
  });
  extendedJobs = extendedJobs.map((item) => item); //remove nulls due to potential error while extending
  return extendedJobs;
}

function jobContainsSearchTerm(job, searchTerm) {
  return (
    job.$lowercase_title.includes(searchTerm) ||
    job.$lowercase_field_jobs_type.includes(searchTerm) ||
Nitin Jadhav's avatar
Nitin Jadhav committed
    job.$lowercase_field_jobs_description.includes(searchTerm) ||
    job.$lowercase_field_jobs_duty_station.includes(searchTerm) ||
    job.$lowercase_field_jobs_your_role.includes(searchTerm) ||
    job.$lowercase_field_jobs_you_have.includes(searchTerm) ||
    job.$lowercase_field_jobs_you_also_have.includes(searchTerm)
Nitin Jadhav's avatar
Nitin Jadhav committed
  );
}
function highlightTerm(jobs, searchTerm) {
  const termRegEx = new RegExp(searchTerm, "ig");

  jobs.forEach((job) => {
    job.title = job.title.replaceAll(termRegEx, creteHighlightMarkup);
    job.field_jobs_description = job.field_jobs_description.replaceAll(
      termRegEx,
      creteHighlightMarkup
    );
    job.field_jobs_duty_station = job.field_jobs_duty_station.replaceAll(
      termRegEx,
      creteHighlightMarkup
    job.field_jobs_type = job.field_jobs_type.replaceAll(
      termRegEx,
      creteHighlightMarkup
    );
    job.field_jobs_your_role = job.field_jobs_your_role.replaceAll(
      termRegEx,
      creteHighlightMarkup
    );
    job.field_jobs_you_have = job.field_jobs_you_have.replaceAll(
      termRegEx,
      creteHighlightMarkup
    );
    job.field_jobs_you_also_have = job.field_jobs_you_also_have.replaceAll(
      termRegEx,
      creteHighlightMarkup
    );
  });
  return jobs;
}

const creteHighlightMarkup = (instance) => `<mark>${instance}</mark>`;