From ea7823c7c76cc5bf1997bda6e1eef7c23866b446 Mon Sep 17 00:00:00 2001 From: Jerry-EY <146475472+jerry-ey@users.noreply.github.com> Date: Wed, 8 Jan 2025 07:10:21 +0800 Subject: [PATCH] IEN-949 | Search ATS1_ID (#696) * add search ats1_id * fix unit test * fix unit test * fix unit test --------- Co-authored-by: Jerry Wang --- .../applicant/ienapplicant.util.service.ts | 4 +- apps/api/src/common/search-names.ts | 38 +++++++++++++++++++ apps/web/src/components/Search.tsx | 13 +++++-- .../web/tests/components/form/Search.test.tsx | 15 +++++--- 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/apps/api/src/applicant/ienapplicant.util.service.ts b/apps/api/src/applicant/ienapplicant.util.service.ts index db5979709..0d3ef0b1a 100755 --- a/apps/api/src/applicant/ienapplicant.util.service.ts +++ b/apps/api/src/applicant/ienapplicant.util.service.ts @@ -15,7 +15,7 @@ import { IENJobTitle } from './entity/ienjobtitles.entity'; import { IENJobLocation } from './entity/ienjoblocation.entity'; import { IENStatusReason } from './entity/ienstatus-reason.entity'; import { IENMasterService } from './ien-master.service'; -import { searchNames } from '../common/search-names'; +import { searchNamesAndAts1Id } from 'src/common/search-names'; @Injectable() export class IENApplicantUtilService { @@ -93,7 +93,7 @@ export class IENApplicantUtilService { } } if (name) { - searchNames(builder, 'applicant.name', name); + searchNamesAndAts1Id(builder, ['applicant.name', 'applicant.ats1_id'], name); } if (sortKey === 'recruiter') { diff --git a/apps/api/src/common/search-names.ts b/apps/api/src/common/search-names.ts index ca2b43af4..02b9cce59 100644 --- a/apps/api/src/common/search-names.ts +++ b/apps/api/src/common/search-names.ts @@ -1,5 +1,28 @@ import { Brackets, WhereExpressionBuilder } from 'typeorm'; +const ATS1_ID_LENGTH = 6; +export const searchNamesAndAts1Id = ( + builder: WhereExpressionBuilder, + fields: [nameField: string, ats1IdField: string], + keyword: string, +) => { + const keywords = keyword + .trim() + .split(' ') + .filter(item => item.length); + + // Check if the keyword is a potential ATS1 ID + if ( + keywords.length === 1 && + keywords[0].length === ATS1_ID_LENGTH && + !isNaN(Number(keywords[0])) + ) { + searchAts1Id(builder, fields[1], keyword); + } else { + searchNames(builder, fields[0], keyword); + } +}; + export const searchNames = (builder: WhereExpressionBuilder, field: string, keyword: string) => { const keywords = keyword .trim() @@ -30,3 +53,18 @@ export const searchNames = (builder: WhereExpressionBuilder, field: string, keyw builder.andWhere(`${field} ilike :name`, { name: `%${keyword.trim()}%` }); } }; + +export const searchAts1Id = (builder: WhereExpressionBuilder, field: string, keyword: string) => { + const keywords = keyword + .trim() + .split(' ') + .filter(item => item.length); + + if ( + keywords.length === 1 && + keywords[0].length === ATS1_ID_LENGTH && + !isNaN(Number(keywords[0])) + ) { + builder.andWhere(`${field} = :ats1_id`, { ats1_id: `${keyword.trim()}` }); + } +}; diff --git a/apps/web/src/components/Search.tsx b/apps/web/src/components/Search.tsx index 8307ac367..bceced810 100644 --- a/apps/web/src/components/Search.tsx +++ b/apps/web/src/components/Search.tsx @@ -54,11 +54,18 @@ export const Search = (props: SearchProps) => { const getResultText = (applicant: ApplicantRO) => { if (!isHmbc(authUser) || !applicant.status) { - return {applicant.name}; + return ( + + {applicant.ats1_id} - {applicant.name} + + ); } return ( <> - {applicant.name} found in {applicant.status.status} + + {applicant.ats1_id} - {applicant.name} + {' '} + found in {applicant.status.status} ); }; @@ -74,7 +81,7 @@ export const Search = (props: SearchProps) => { onChange={handleChange} onFocus={() => handleFocus(true)} onKeyDown={handleEnter} - placeholder='Search by first name or last name' + placeholder='Search by first name, last name or ATS1 ID' className='flex-grow focus:outline-none placeholder-bcGray' data-cy='search-input' /> diff --git a/apps/web/tests/components/form/Search.test.tsx b/apps/web/tests/components/form/Search.test.tsx index 038700490..a7930ff86 100644 --- a/apps/web/tests/components/form/Search.test.tsx +++ b/apps/web/tests/components/form/Search.test.tsx @@ -5,21 +5,26 @@ describe('Search', () => { it('renders a search box', async () => { const mock = jest.fn(); const searchData = [ - { id: '1', name: 'Jane Doe', status: { id: 1, status: 'Recruitment' } }, - { id: '2', name: 'Mark Twain', status: { id: 5, status: 'Final Milestone' } }, + { ats1_id: '111111', id: '1', name: 'Jane Doe', status: { id: 1, status: 'Recruitment' } }, + { + ats1_id: '222222', + id: '2', + name: 'Mark Twain', + status: { id: 5, status: 'Final Milestone' }, + }, ]; const search = async (): Promise => searchData; render(); - const input = screen.getByPlaceholderText('Search by first name or last name'); + const input = screen.getByPlaceholderText('Search by first name, last name or ATS1 ID'); expect(input).toBeInTheDocument(); input.focus(); fireEvent.change(input, { target: { value: 'Mark' } }); - for (const { name } of searchData) { + for (const { name, ats1_id } of searchData) { await waitFor(() => { - expect(screen.getByText(name)).toBeInTheDocument(); + expect(screen.getByText(`${ats1_id} - ${name}`)).toBeInTheDocument(); }); }