Skip to content

Commit

Permalink
Refactor: Add datePosted filter for Job Listings (#977)
Browse files Browse the repository at this point in the history
* Refactor: Add datePosted filter for Job Listings

- Added the datePosted field to the FindAllVisibleTpJobListingsArgsFilter input type in the types.ts file.
- Updated the fetchAllTpJobListingsUsingFilters function in the api.tsx file to include the datePosted parameter when making API requests.
- Modified the TpJobListingsService class in the tp-job-listings.service.ts file to filter job listings based on the datePosted field.
- Updated the JobListingCard.generated.ts file to include the isFromCareerPartner field in the JobListingCardJobListingPropFragment type.
- Modified the FilterDropdown.tsx file to add support for single select options.
- Updated the JobListingCard.tsx file to display a "Promoted" label for job listings posted by ReDI Career Partners.

* fix: add datePosted in clearFilters

* Conditionally apply career partner sorting based on datePosted value in BrowseJobseeker component

* Add 'Any time' option to job listing date filter and refactor date handling

- Introduced `jobListingCreatedDate` and `jobListingCreatedDateInAge` constants for better date filter management.
- Updated `BrowseJobseeker.tsx` to use these constants and handle 'Any time' option correctly.
- Modified `tp-job-listings.service.ts` to utilize `jobListingCreatedDateInAge` for date calculations.
  • Loading branch information
helloanil authored Oct 1, 2024
1 parent 571ca7d commit d258fd2
Show file tree
Hide file tree
Showing 14 changed files with 147 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class FindAllVisibleTpJobListingsArgsFilter {

isRemotePossible?: boolean

datePosted?: string

/**
* Job Fair Boolean Field(s)
* Uncomment & Rename (joins{Location}{Year}{Season}JobFair) the next field when there's an upcoming Job Fair
Expand Down
14 changes: 14 additions & 0 deletions apps/nestjs-api/src/tp-job-listings/tp-job-listings.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
TpJobListingStatus,
} from '@talent-connect/common-types'
import { deleteUndefinedProperties } from '@talent-connect/shared-utils'
import { jobListingCreatedDateInAge } from '@talent-connect/talent-pool/config'
import * as jsforce from 'jsforce'
import { CurrentUserInfo } from '../auth/current-user.interface'
import { SfApiTpJobListingsService } from '../salesforce-api/sf-api-tp-job-listings.service'
import { TpCompanyRepresentativeRelationshipsService } from '../tp-company-profiles/tp-company-representative-relationships.service'
Expand Down Expand Up @@ -67,6 +69,18 @@ export class TpJobListingsService {
if (_filter.filter.isRemotePossible) {
filter.Remote_Possible__c = true
}
if (_filter.filter.datePosted) {
const createdDate = new Date()

createdDate.setDate(
createdDate.getDate() -
jobListingCreatedDateInAge[_filter.filter.datePosted]
)

filter.CreatedDate = {
$gte: jsforce.SfDate.toDateTimeLiteral(createdDate),
}
}
/**
* Job Fair Boolean Field(s)
* Uncomment & Rename (joins{Location}{Year}{Season}JobFair) the next block when there's an upcoming Job Fair
Expand Down
4 changes: 4 additions & 0 deletions apps/redi-talent-pool/src/assets/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@
"question": "How can I browse Companies?",
"answer": "Select the 'Browse' option located in the left navigation bar, then begin exploring the job postings. Feel free to use filters to refine your search for more specific results."
},
{
"question": "How do ReDI Career Partners' job listings benefit me as a job seeker?",
"answer": "Job listings from ReDI Career Partners are given higher visibility on our platform because these trusted companies actively source ReDI talent and support newcomers. By applying to their positions, you benefit from opportunities with employers who are committed to your success and closely collaborate with us to create more career opportunities in Germany."
},
{
"question": "Will there be additional companies joining the Talent Pool?",
"answer": "Yes, we are actively engaging with numerous companies and expanding our network of partner companies featured in the Talent Pool."
Expand Down
30 changes: 26 additions & 4 deletions apps/redi-talent-pool/src/components/organisms/JobListingCard.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useMediaQuery, useTheme } from '@mui/material'
import { Tooltip, useMediaQuery, useTheme } from '@mui/material'
import { CardTags } from '@talent-connect/shared-atomic-design-components'
import { topSkillsIdToLabelMap } from '@talent-connect/talent-pool/config'
import React from 'react'
import { Card } from 'react-bulma-components'
import { NavLink } from 'react-router-dom'
import { Link, NavLink } from 'react-router-dom'
import CardLocation from '../../../../../libs/shared-atomic-design-components/src/lib/atoms/CardLocation'
import placeholderImage from '../../assets/images/company-placeholder-img.svg'
import './JobListingCard.scss'
Expand All @@ -15,6 +15,7 @@ interface JobListingCardProps {
toggleFavorite?: (id: string) => void
linkTo?: string
timestamp?: string
showPromotedLabel?: boolean
renderCTA?: () => React.ReactNode

onClick?: (e: React.MouseEvent) => void
Expand All @@ -25,6 +26,7 @@ export function JobListingCard({
linkTo,
onClick,
timestamp,
showPromotedLabel = false,
renderCTA,
}: JobListingCardProps) {
const {
Expand Down Expand Up @@ -71,8 +73,28 @@ export function JobListingCard({
<div className="job-posting-card__lastColumn">
<div className="job-posting-card__timeFooterBox">
{isTabletOrDesktop && renderCTA && renderCTA()}
{timestamp && (
<div className="job-posting-card__timestamp">{timestamp}</div>
{timestamp &&
(!showPromotedLabel || !jobListing.isFromCareerPartner) && (
<div className="job-posting-card__timestamp">{timestamp}</div>
)}
{showPromotedLabel && jobListing.isFromCareerPartner && (
<div className="job-posting-card__timestamp">
<Tooltip
title={
<span>
This job listing is promoted because it is posted by a
ReDI Career Partner. You can learn more about why we do
this by visiting the{' '}
<Link to="/faq" target="_blank">
FAQ section
</Link>
</span>
}
placement="top"
>
<span>Promoted</span>
</Tooltip>
</div>
)}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// THIS FILE IS GENERATED, DO NOT EDIT!
import * as Types from '@talent-connect/data-access';

export type JobListingCardJobListingPropFragment = { __typename?: 'TpJobListing', id: string, title?: string | null, idealTechnicalSkills?: Array<Types.TpTechnicalSkill> | null, companyName: string, profileAvatarImageS3Key?: string | null, createdAt: any, federalState?: Types.FederalState | null, location?: string | null, isRemotePossible?: boolean | null };
export type JobListingCardJobListingPropFragment = { __typename?: 'TpJobListing', id: string, title?: string | null, idealTechnicalSkills?: Array<Types.TpTechnicalSkill> | null, companyName: string, profileAvatarImageS3Key?: string | null, createdAt: any, federalState?: Types.FederalState | null, location?: string | null, isRemotePossible?: boolean | null, isFromCareerPartner: boolean };

export const JobListingCardJobListingPropFragmentDoc = `
fragment JobListingCardJobListingProp on TpJobListing {
Expand All @@ -14,5 +14,6 @@ export const JobListingCardJobListingPropFragmentDoc = `
federalState
location
isRemotePossible
isFromCareerPartner
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ fragment JobListingCardJobListingProp on TpJobListing {
federalState
location
isRemotePossible
isFromCareerPartner
}
52 changes: 48 additions & 4 deletions apps/redi-talent-pool/src/pages/app/browse/BrowseJobseeker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
employmentTypes,
employmentTypesIdToLabelMap,
germanFederalStates,
jobListingCreatedDate,
topSkills,
topSkillsIdToLabelMap,
} from '@talent-connect/talent-pool/config'
Expand All @@ -31,6 +32,7 @@ import { Redirect } from 'react-router-dom'
import {
ArrayParam,
BooleanParam,
StringParam,
useQueryParams,
withDefault,
} from 'use-query-params'
Expand Down Expand Up @@ -61,6 +63,7 @@ export function BrowseJobseeker() {
federalStates: withDefault(ArrayParam, []),
onlyFavorites: withDefault(BooleanParam, undefined),
isRemotePossible: withDefault(BooleanParam, undefined),
datePosted: withDefault(StringParam, undefined),
/**
* Job Fair Boolean Field(s)
* Uncomment & Rename (joins{Location}{Year}{Season}JobFair) the next field when there's an upcoming Job Fair
Expand All @@ -74,6 +77,7 @@ export function BrowseJobseeker() {
const federalStates = query.federalStates as FederalState[]
const onlyFavorites = query.onlyFavorites
const isRemotePossible = query.isRemotePossible
const datePosted = query.datePosted
/**
* Job Fair Boolean Field(s)
* Uncomment & Rename (joins{Location}{Year}{Season}JobFair) the next field when there's an upcoming Job Fair
Expand All @@ -88,6 +92,7 @@ export function BrowseJobseeker() {
employmentTypes: employmentType,
federalStates,
isRemotePossible,
datePosted,
/**
* Job Fair Boolean Field(s)
* Uncomment & Rename (joins{Location}{Year}{Season}JobFair) the next field when there's an upcoming Job Fair
Expand All @@ -102,8 +107,9 @@ export function BrowseJobseeker() {
* - Backend currently supports only sorting by one field, which is used for sorting by date
* - All fetch job listing queries are using one findAll query, which means this sort would have unexpected side effects
*/
const jobListings =
jobListingsQuery.data?.tpJobListings.sort(careerPartnerSortFn)
const jobListings = !datePosted
? jobListingsQuery.data?.tpJobListings.sort(careerPartnerSortFn)
: jobListingsQuery.data?.tpJobListings

const isFavorite = (jobListingId) =>
favouritedTpJobListingsQuery.data?.tpJobseekerFavoritedJobListings?.some(
Expand Down Expand Up @@ -155,6 +161,13 @@ export function BrowseJobseeker() {
}))
}

const onSelectDatePosted = (item) => {
setQuery((latestQuery) => ({
...latestQuery,
datePosted: item ?? undefined,
}))
}

const toggleFilters = (filtersArr, filterName, item) => {
const newFilters = toggleValueInArray(filtersArr, item)
setQuery((latestQuery) => ({ ...latestQuery, [filterName]: newFilters }))
Expand All @@ -180,6 +193,7 @@ export function BrowseJobseeker() {
employmentType: [],
federalStates: [],
isRemotePossible: undefined,
datePosted: undefined,
/**
* Job Fair Boolean Field(s)
* Uncomment & Rename (joins{Location}{Year}{Season}JobFair) the next field when there's an upcoming Job Fair
Expand All @@ -194,7 +208,8 @@ export function BrowseJobseeker() {
idealTechnicalSkills.length !== 0 ||
employmentType.length !== 0 ||
federalStates.length !== 0 ||
isRemotePossible
isRemotePossible ||
datePosted
/**
* Job Fair Boolean Field(s)
* Uncomment & Rename (joins{Location}{Year}{Season}JobFair) the next field when there's an upcoming Job Fair
Expand Down Expand Up @@ -301,7 +316,15 @@ export function BrowseJobseeker() {
}
/>
</div>
<div className="filters-inner"></div>
<div className="filters-inner">
<FilterDropdown
items={datePostedOptions}
className="filters__dropdown"
label="Date Posted"
onChange={onSelectDatePosted}
singleSelect
/>
</div>
</div>
<div className="filters">
<div className="filters-inner">
Expand Down Expand Up @@ -400,6 +423,17 @@ export function BrowseJobseeker() {
onClickHandler={toggleRemoteAvailableFilter}
/>
)}
{datePosted && (
<FilterTag
key="date-posted"
id="date-posted"
label={
datePostedOptions.find((item) => item.value === datePosted)
?.label
}
onClickHandler={() => onSelectDatePosted('')}
/>
)}
{/*
* Job Fair Boolean Field(s)
* Uncomment & Rename (joins{Location}{Year}{Season}JobFair) the next FilterTag when there's an upcoming Job Fair
Expand Down Expand Up @@ -443,6 +477,7 @@ export function BrowseJobseeker() {
addSuffix: true,
}
)}`}
showPromotedLabel
/>
</Columns.Column>
))}
Expand Down Expand Up @@ -498,3 +533,12 @@ const germanFederalStatesOptions = objectEntries(germanFederalStates).map(
label,
})
)

// Adding the 'Any time' option to the date posted filter as undefined cannot be used as a key
const datePostedOptions = [
{ value: null, label: 'Any time' },
...objectEntries(jobListingCreatedDate).map(([value, label]) => ({
value,
label,
})),
]
3 changes: 3 additions & 0 deletions apps/redi-talent-pool/src/services/api/api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ export interface TpJobListingFilters {
employmentType: string[]
federalStates: string[]
isRemotePossible: boolean
datePosted?: string
}

export async function fetchAllTpJobListingsUsingFilters({
Expand All @@ -332,6 +333,7 @@ export async function fetchAllTpJobListingsUsingFilters({
employmentType,
federalStates,
isRemotePossible,
datePosted,
}: TpJobListingFilters): Promise<Array<TpJobListing>> {
const filterRelatedPositions =
relatedPositions && relatedPositions.length !== 0
Expand Down Expand Up @@ -365,6 +367,7 @@ export async function fetchAllTpJobListingsUsingFilters({
employmentType: filterEmploymentTypeOptions,
federalState: filterFederalStates,
isRemotePossible,
createdAt: datePosted,
},
],
},
Expand Down
2 changes: 2 additions & 0 deletions apps/redi-talent-pool/src/utils/sort-job-listings.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { TpJobListing } from '@talent-connect/data-access'

export const careerPartnerSortFn = (a: TpJobListing, b: TpJobListing) => {
console.log('sorting')

if (a.isFromCareerPartner && !b.isFromCareerPartner) {
return -1
} else if (!a.isFromCareerPartner && b.isFromCareerPartner) {
Expand Down
1 change: 1 addition & 0 deletions libs/data-access/src/lib/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ export enum FederalState {
}

export type FindAllVisibleTpJobListingsArgsFilter = {
datePosted?: InputMaybe<Scalars['String']>;
employmentTypes?: InputMaybe<Array<TpEmploymentType>>;
federalStates?: InputMaybe<Array<FederalState>>;
isRemotePossible?: InputMaybe<Scalars['Boolean']>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
box-shadow: 0 3px 15px 0 rgba(0, 0, 0, 0.12),
0 2px 4px -1px rgba(0, 0, 0, 0.2);
max-height: 210px;
overflow: scroll;
overflow: auto;

&--show {
display: block;
Expand All @@ -57,6 +57,11 @@
+ li {
margin-top: 0.5rem;
}

.listItem--singleSelect {
display: flex;
cursor: pointer;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ type Item = { label: string; value: string }
interface Props {
label: string
className: string
selected: string[]
selected?: string[]
items: Item[]
onChange: (item: string) => void
singleSelect?: boolean
}

const baseClass = 'filter-dropdown'
Expand All @@ -21,6 +22,7 @@ const FilterDropdown = ({
selected,
items,
onChange,
singleSelect = false,
}: Props) => {
const filterDropdown = useRef<any>(null)
const [showDropdown, setShowDropdown] = useState(false)
Expand All @@ -31,6 +33,11 @@ const FilterDropdown = ({
}
}, [])

const handleSingleSelectOptionClick = (item: string) => {
onChange(item)
setShowDropdown(false)
}

useEffect(() => {
document.addEventListener('click', handleClick)
return () => {
Expand Down Expand Up @@ -66,12 +73,22 @@ const FilterDropdown = ({
>
{items.map((item) => (
<li key={item.value}>
<Checkbox
handleChange={() => onChange(item.value)}
checked={selected.includes(item.value)}
>
{item.label}
</Checkbox>
{singleSelect ? (
<span
className="listItem--singleSelect"
role="listitem"
onClick={() => handleSingleSelectOptionClick(item.value)}
>
{item.label}
</span>
) : (
<Checkbox
handleChange={() => onChange(item.value)}
checked={selected.includes(item.value)}
>
{item.label}
</Checkbox>
)}
</li>
))}
</ul>
Expand Down
Loading

0 comments on commit d258fd2

Please sign in to comment.