diff --git a/apps/api/package.json b/apps/api/package.json index 64550051..72be7884 100755 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -29,6 +29,7 @@ "@ien/common": "1.0.2", "@nestjs/common": "9.3.9", "@nestjs/core": "9.3.9", + "@nestjs/event-emitter": "2.1.1", "@nestjs/platform-express": "9.4.3", "@nestjs/swagger": "5.1.5", "@nestjs/typeorm": "8.0.2", diff --git a/apps/api/src/applicant/applicant.module.ts b/apps/api/src/applicant/applicant.module.ts index 48ce28eb..22b884ab 100644 --- a/apps/api/src/applicant/applicant.module.ts +++ b/apps/api/src/applicant/applicant.module.ts @@ -1,5 +1,7 @@ import { Logger, Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { EventEmitterModule } from '@nestjs/event-emitter'; + import { MailModule } from 'src/mail/mail.module'; import { IENApplicant } from './entity/ienapplicant.entity'; import { IENApplicantAudit } from './entity/ienapplicant-audit.entity'; @@ -47,6 +49,7 @@ import { EndOfJourneyService } from './endofjourney.service'; Pathway, SyncApplicantsAudit, ]), + EventEmitterModule.forRoot({ wildcard: true }), AuthModule, EmployeeModule, MailModule, diff --git a/apps/api/src/applicant/endofjourney.service.ts b/apps/api/src/applicant/endofjourney.service.ts index f39015cf..c60e4196 100644 --- a/apps/api/src/applicant/endofjourney.service.ts +++ b/apps/api/src/applicant/endofjourney.service.ts @@ -6,7 +6,7 @@ import { OnModuleInit, } from '@nestjs/common'; -import { Connection, EntityManager, createConnection } from 'typeorm'; +import { Connection, EntityManager, SelectQueryBuilder, createConnection } from 'typeorm'; import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc'; import timezone from 'dayjs/plugin/timezone'; @@ -15,6 +15,9 @@ import { AtsApplicant, END_OF_JOURNEY_FLAG, STATUS } from '@ien/common'; import { AppLogger } from 'src/common/logger.service'; import { IENApplicantStatusAudit } from './entity/ienapplicant-status-audit.entity'; import { IENMasterService } from './ien-master.service'; +import { OnEvent } from '@nestjs/event-emitter'; +import { SystemMilestoneEvent } from 'src/common/system-milestone-event'; +import { IENApplicant } from './entity/ienapplicant.entity'; dayjs.extend(utc); dayjs.extend(timezone); @@ -32,6 +35,17 @@ type IEN_APPLICANT_END_OF_JOURNEY = { status: string; ha_pcn_id: string; }; +type ApplicantWithMilestones = { + applicant_id: string; + milestones: { + id: string; + name: string; + start_date: string; + }[]; +}; +type GetEoJCompletedListsMeta = ( + query: SelectQueryBuilder, +) => SelectQueryBuilder; @Injectable() export class EndOfJourneyService implements OnModuleInit { @@ -69,7 +83,7 @@ export class EndOfJourneyService implements OnModuleInit { try { // handle end of journey: COMPLETED - await this.handleEndOfJourney( + await this.handleEndOfJourney( this.getCompletedLists, this.setCompletedLists, manager, @@ -93,8 +107,8 @@ export class EndOfJourneyService implements OnModuleInit { } } - async handleEndOfJourney( - getter: Getter, + async handleEndOfJourney( + getter: Getter, setter: Setter, manager: EntityManager, ): Promise { @@ -115,7 +129,10 @@ export class EndOfJourneyService implements OnModuleInit { * Checking for end of journey COMPLETED * QUITERIA: */ - getCompletedLists: Getter = async manager => { + getCompletedLists: Getter = async ( + manager, + meta = query => query, + ) => { const yesterday = dayjs().tz('America/Los_Angeles').subtract(1, 'day').toDate(); const oneYearBeforeYesterday = formatDateInPST( dayjs(yesterday).tz('America/Los_Angeles').subtract(1, 'year').toDate(), @@ -129,7 +146,7 @@ export class EndOfJourneyService implements OnModuleInit { * - effective_date: Date, the latest (most recent) effective_date related to the applicant's status. * - status: string, the status of the applicant (in this case, "Job Offer Accepted"). */ - const query = manager + let query = manager .createQueryBuilder(IENApplicantStatusAudit, 'audit') .select('audit.applicant_id') // Select applicant_id .addSelect("TO_CHAR(MAX(audit.effective_date), 'YYYY-MM-DD')", 'effective_date') // Format effective_date as YYYY-MM-DD @@ -148,7 +165,8 @@ export class EndOfJourneyService implements OnModuleInit { .groupBy('audit.applicant_id') // Group by applicant_id .addGroupBy('status.id') .addGroupBy('job.id'); - + query = meta(query); + query = this.checkReEngagedStatusForEoJCompleteQuery(query); const applicants = await query.getRawMany(); this.logger.log({ yesterday, oneYearBeforeYesterday, applicants }, 'END-OF-JOURNEY'); @@ -169,6 +187,28 @@ export class EndOfJourneyService implements OnModuleInit { .execute(); } }; + checkReEngagedStatusForEoJCompleteQuery( + query: SelectQueryBuilder, + ): SelectQueryBuilder { + return query.andWhere( + `NOT EXISTS ( + SELECT 1 + FROM ien_applicant_status_audit reengaged_audit + JOIN ien_applicant_status reengaged_status ON reengaged_audit.status_id = reengaged_status.id + WHERE reengaged_audit.applicant_id = audit.applicant_id + AND reengaged_status.status = :reEngagedStatus + AND reengaged_audit.start_date = ( + SELECT MAX(inner_audit.start_date) + FROM ien_applicant_status_audit inner_audit + JOIN ien_applicant_status inner_status ON inner_audit.status_id = inner_status.id + WHERE inner_audit.applicant_id = audit.applicant_id + AND inner_status.status = :reEngagedStatus + ) + AND reengaged_audit.start_date <= (audit.effective_date + INTERVAL '1 year') + )`, + { reEngagedStatus: STATUS.RE_ENGAGED }, + ); + } async handleNotProceedingMilestone( applicants: AtsApplicant[], @@ -214,20 +254,176 @@ export class EndOfJourneyService implements OnModuleInit { const results = []; for (const applicant of list) { // update the end_of_journey_flag and updated_date of the applicant - const result = await manager - .createQueryBuilder() + let query = this.getIncompleteQuery(manager, applicant.applicant_id); + query = this.checkReEngagedStatusForEoJIncompleteQuery(query); + + const result = await query .update('ien_applicants') .set({ end_of_journey: END_OF_JOURNEY_FLAG.JOURNEY_INCOMPLETE, updated_date: dayjs().tz('America/Los_Angeles').toDate(), }) - .where('id = :id', { id: applicant.applicant_id }) - .andWhere('(end_of_journey != :end_of_journey OR end_of_journey IS NULL)', { - end_of_journey: END_OF_JOURNEY_FLAG.JOURNEY_INCOMPLETE, - }) .execute(); results.push(result); } this.logger.log({ results }, 'END-OF-JOURNEY'); }; + getIncompleteQuery( + manager: EntityManager, + applicant_id: string, + ): SelectQueryBuilder { + return manager + .createQueryBuilder() + .from(IENApplicant, 'ien_applicants') + .where('id = :id', { id: applicant_id }) + .andWhere('(end_of_journey != :end_of_journey OR end_of_journey IS NULL)', { + end_of_journey: END_OF_JOURNEY_FLAG.JOURNEY_INCOMPLETE, + }); + } + checkReEngagedStatusForEoJIncompleteQuery( + query: SelectQueryBuilder, + ): SelectQueryBuilder { + return query.andWhere( + `NOT EXISTS ( + SELECT 1 + FROM ien_applicant_status_audit reengaged_audit + JOIN ien_applicant_status reengaged_status + ON reengaged_audit.status_id = reengaged_status.id + WHERE reengaged_audit.applicant_id = ien_applicants.id + AND reengaged_status.status = :reEngagedStatus + AND ( + SELECT MAX(inner_audit.start_date) + FROM ien_applicant_status_audit inner_audit + JOIN ien_applicant_status inner_status + ON inner_audit.status_id = inner_status.id + WHERE inner_audit.applicant_id = ien_applicants.id + AND inner_status.status = :reEngagedStatus + ) > ( + SELECT MAX(not_proceeding_audit.start_date) + FROM ien_applicant_status_audit not_proceeding_audit + JOIN ien_applicant_status not_proceeding_status + ON not_proceeding_audit.status_id = not_proceeding_status.id + WHERE not_proceeding_audit.applicant_id = ien_applicants.id + AND not_proceeding_status.status = :notProceedingStatus + ) + )`, + { reEngagedStatus: STATUS.RE_ENGAGED, notProceedingStatus: STATUS.NOT_PROCEEDING }, + ); + } + + /** + * Event handler for delete re-engaged applicants + */ + @OnEvent(`${SystemMilestoneEvent.REENGAGED}.*`) + async handleReEngagedDeleteEvent( + payload: IENApplicantStatusAudit, + event: SystemMilestoneEvent, + ): Promise { + this.logger.log(`Handling re-engaged event: ${event}`, 'END-OF-JOURNEY'); + + const connection = this.connection; + if (!connection) { + this.logger.error('Connection failed', 'END-OF-JOURNEY'); + return; + } + const queryRunner = connection.createQueryRunner(); + await queryRunner.startTransaction(); + const manager = queryRunner.manager; + + await this.handleReEngagedForJourneyComplete(manager, payload); + await this.handleReEngagedForJourneyIncomplete(manager, payload); + + await queryRunner.commitTransaction(); + } + + private async handleReEngagedForJourneyComplete( + manager: EntityManager, + payload: IENApplicantStatusAudit, + ): Promise { + // Implement the logic for handling re-engaged applicants for journey complete + this.logger.log( + `Handling re-engaged for journey complete for applicant ${payload.applicant.id}`, + 'END-OF-JOURNEY', + ); + const completedList = await this.getCompletedLists(manager, query => + query.andWhere('audit.applicant_id = :id', { id: payload.applicant.id }), + ); + + if (completedList.length === 0) { + this.cleanEndOfJourneyFlag(manager, payload); + } else { + this.setCompletedLists(manager, completedList); + } + } + + private async handleReEngagedForJourneyIncomplete( + manager: EntityManager, + payload: IENApplicantStatusAudit, + ): Promise { + // Implement the logic for handling re-engaged applicants for journey complete + this.logger.log( + `Handling re-engaged for journey incomplete for applicant ${payload.applicant.id}`, + 'END-OF-JOURNEY', + ); + + const query = manager + .createQueryBuilder(IENApplicant, 'applicant') + .select([ + 'applicant.id AS applicant_id', // Select applicant_id + 'status.id AS milestone_id', // Include milestone id + 'status.status AS milestone', // Include milestone (status) + 'audit.start_date AS milestone_start_date', // Include milestone start date + ]) + .leftJoin('ien_applicant_status_audit', 'audit', 'audit.applicant_id = applicant.id') + .leftJoin('ien_applicant_status', 'status', 'status.id = audit.status_id') + .where('applicant.id = :id', { id: payload.applicant.id }); + + const rawResults = await query.getRawMany(); + const applicants = rawResults.reduce((result, row) => { + let applicant = result.find(a => a.applicant_id === row.applicant_id); + + if (!applicant) { + applicant = { applicant_id: row.applicant_id, milestones: [] }; + result.push(applicant); + } + + if (row.milestone) { + applicant.milestones.push({ + id: row.milestone_id, + name: row.milestone, + start_date: row.milestone_start_date, + }); + } + + return result; + }, []); + + const notProceedingList = await this.getNotProceedingLists( + manager, + applicants as AtsApplicant[], + ); + + if (notProceedingList.length > 0) { + let query = this.getIncompleteQuery(manager, payload.applicant.id); + query = this.checkReEngagedStatusForEoJIncompleteQuery(query); + if ((await query.getCount()) === 0) { + this.cleanEndOfJourneyFlag(manager, payload); + } else { + this.setNotProceedingLists(manager, notProceedingList); + } + } + } + + private async cleanEndOfJourneyFlag(manager: EntityManager, payload: IENApplicantStatusAudit) { + await manager + .createQueryBuilder() + .update('ien_applicants') + .set({ + end_of_journey: null, + updated_date: dayjs().tz('America/Los_Angeles').toDate(), + }) + .where('id = :id', { id: payload.applicant.id }) + .andWhere('end_of_journey IS NOT NULL') + .execute(); + } } diff --git a/apps/api/src/applicant/ienapplicant.service.ts b/apps/api/src/applicant/ienapplicant.service.ts index a2d38a57..7b719a6e 100644 --- a/apps/api/src/applicant/ienapplicant.service.ts +++ b/apps/api/src/applicant/ienapplicant.service.ts @@ -3,7 +3,16 @@ import dayjs from 'dayjs'; import { BadRequestException, Inject, Injectable, Logger, NotFoundException } from '@nestjs/common'; import { FindManyOptions, getManager, In, IsNull, Repository } from 'typeorm'; import { InjectRepository } from '@nestjs/typeorm'; -import { ApplicantRO, EmployeeRO, HealthAuthorities, isAdmin, StatusCategory } from '@ien/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; + +import { + ApplicantRO, + EmployeeRO, + HealthAuthorities, + isAdmin, + STATUS, + StatusCategory, +} from '@ien/common'; import { AppLogger } from 'src/common/logger.service'; import { IENApplicant } from './entity/ienapplicant.entity'; import { IENUsers } from './entity/ienusers.entity'; @@ -23,6 +32,7 @@ import { import { IENHaPcn } from './entity/ienhapcn.entity'; import { IENApplicantRecruiter } from './entity/ienapplicant-employee.entity'; import { IENApplicantActiveFlag } from './entity/ienapplicant-active-flag.entity'; +import { SystemMilestoneEvent } from 'src/common/system-milestone-event'; @Injectable() export class IENApplicantService { @@ -42,6 +52,7 @@ export class IENApplicantService { private readonly haPcnRepository: Repository, @InjectRepository(IENApplicantRecruiter) private readonly recruiterRepository: Repository, + private eventEmitter: EventEmitter2, ) {} /** @@ -335,6 +346,14 @@ export class IENApplicantService { await manager.update(IENApplicant, status_audit.applicant.id, { updated_date: new Date(), }); + // handle update re-engaged + if (status_audit.status.status === STATUS.RE_ENGAGED) { + this.eventEmitter.emit( + SystemMilestoneEvent.CREATE_REENGAGED, + status_audit, + SystemMilestoneEvent.CREATE_REENGAGED, + ); + } return status_audit; }); return status_audit; @@ -401,6 +420,15 @@ export class IENApplicantService { ); }); + // handle update re-engaged + if (audit.status.status === STATUS.RE_ENGAGED) { + this.eventEmitter.emit( + SystemMilestoneEvent.UPDATE_REENGAGED, + audit, + SystemMilestoneEvent.UPDATE_REENGAGED, + ); + } + return audit; } @@ -432,6 +460,15 @@ export class IENApplicantService { [status.applicant.id], manager, ); + + // handle delete re-engaged + if (status.status.status === STATUS.RE_ENGAGED) { + this.eventEmitter.emit( + SystemMilestoneEvent.DELETE_REENGAGED, + status, + SystemMilestoneEvent.DELETE_REENGAGED, + ); + } }); } diff --git a/apps/api/src/common/system-milestone-event.ts b/apps/api/src/common/system-milestone-event.ts new file mode 100644 index 00000000..66a5d561 --- /dev/null +++ b/apps/api/src/common/system-milestone-event.ts @@ -0,0 +1,6 @@ +export enum SystemMilestoneEvent { + REENGAGED = 'REENGAGED', + CREATE_REENGAGED = 'REENGAGED.CREATE', + UPDATE_REENGAGED = 'REENGAGED.UPDATE', + DELETE_REENGAGED = 'REENGAGED.DELETE', +} diff --git a/apps/web/package.json b/apps/web/package.json index 5df8243a..71278d03 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -17,38 +17,38 @@ "@fortawesome/free-solid-svg-icons": "6.5.2", "@fortawesome/react-fontawesome": "0.2.2", "@headlessui/react": "2.1.2", - "@hookform/resolvers": "^3.9.1", + "@hookform/resolvers": "3.9.1", "@ien/common": "1.0.2", - "@radix-ui/react-dialog": "^1.1.2", - "@radix-ui/react-label": "^2.1.0", - "@radix-ui/react-popover": "^1.1.2", - "@radix-ui/react-select": "^2.1.2", - "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-dialog": "1.1.2", + "@radix-ui/react-label": "2.1.0", + "@radix-ui/react-popover": "1.1.2", + "@radix-ui/react-select": "2.1.2", + "@radix-ui/react-slot": "1.1.0", "@types/cookie": "0.4.1", "@types/cookies": "0.7.7", "axios": "0.28.0", - "class-variance-authority": "^0.7.1", + "class-variance-authority": "0.7.1", "classnames": "2.3.1", "cookie": "0.4.2", - "date-fns": "^4.1.0", + "date-fns": "4.0.0", "eventemitter3": "4.0.7", "formik": "2.4.6", "lodash": "4.17.21", - "lucide-react": "^0.464.0", + "lucide-react": "0.464.0", "next": "14.2.5", "oidc-client-ts": "3.0.1", "react": "18.3.1", "react-datepicker": "4.8.0", - "react-day-picker": "^9.4.1", + "react-day-picker": "9.4.1", "react-dom": "18.3.1", - "react-hook-form": "^7.53.2", + "react-hook-form": "7.53.2", "react-oidc-context": "2.3.0", "react-select": "5.8.0", "react-toastify": "10.0.5", - "tailwind-merge": "^2.5.5", + "tailwind-merge": "2.5.5", "use-http": "1.0.26", "xlsx-js-style": "1.2.0", - "zod": "^3.23.8" + "zod": "3.23.8" }, "devDependencies": { "@next/eslint-plugin-next": "14.2.5", diff --git a/apps/web/src/components/display/DeleteMilestoneModal.tsx b/apps/web/src/components/display/DeleteMilestoneModal.tsx index 40c58f96..77cf6d11 100644 --- a/apps/web/src/components/display/DeleteMilestoneModal.tsx +++ b/apps/web/src/components/display/DeleteMilestoneModal.tsx @@ -20,7 +20,7 @@ export const DeleteMilestoneModal: React.FC = ( const handleSubmit = async () => { await deleteApplicantStatus(userId, milestoneId); - await fetchApplicant(); + fetchApplicant(); onClose(milestoneId); }; diff --git a/apps/web/src/components/milestone-logs/system/SystemForm.tsx b/apps/web/src/components/milestone-logs/system/SystemForm.tsx index 4eec1a8e..64019db7 100644 --- a/apps/web/src/components/milestone-logs/system/SystemForm.tsx +++ b/apps/web/src/components/milestone-logs/system/SystemForm.tsx @@ -1,7 +1,7 @@ import { z } from 'zod'; import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { CalendarIcon } from 'lucide-react'; import { format, parseISO } from 'date-fns'; @@ -61,6 +61,7 @@ export function SystemForm() { const milestones = useGetMilestoneOptions(StatusCategory.SYSTEM); const { applicant, fetchApplicant } = useApplicantContext(); + const [calendarOpen, setCalendarOpen] = useState(false); // 1. Define your form. const form = useForm>({ @@ -168,7 +169,7 @@ export function SystemForm() { render={({ field }) => ( Start date - + { + field.onChange(value); + setCalendarOpen(false); + }} /> diff --git a/apps/web/src/components/milestone-logs/system/SystemMilestoneTable.tsx b/apps/web/src/components/milestone-logs/system/SystemMilestoneTable.tsx index ec6ec8ae..e5787e4c 100644 --- a/apps/web/src/components/milestone-logs/system/SystemMilestoneTable.tsx +++ b/apps/web/src/components/milestone-logs/system/SystemMilestoneTable.tsx @@ -146,14 +146,13 @@ export const SystemMilestoneTable = ({ category }: MilestoneTableProps) => { pageOptions={{ pageIndex, pageSize, total: filteredMilestones.length }} onChange={handlePageOptions(setPageSize, setPageIndex)} /> - {!!selectedMilestone && ( - setDeleteModalVisible(false)} - visible={deleteModalVisible} - userId={authUser?.user_id} - milestoneId={selectedMilestone.id} - /> - )} + + setDeleteModalVisible(false)} + visible={deleteModalVisible} + userId={authUser?.user_id} + milestoneId={selectedMilestone?.id ?? '0'} + /> ); }; diff --git a/apps/web/src/utils/milestone-utils.ts b/apps/web/src/utils/milestone-utils.ts index 5a369a96..bd486efc 100755 --- a/apps/web/src/utils/milestone-utils.ts +++ b/apps/web/src/utils/milestone-utils.ts @@ -1,4 +1,6 @@ -// src/utils/milestoneUtils.ts +/** + * This file is refactor from the file `milestoneTable.tsx` because of the sonqrube duplication code issue. + */ import { PageOptions } from '@components'; import { ApplicantRO, ApplicantStatusAuditRO } from '@ien/common'; diff --git a/apps/web/tailwind.config.js b/apps/web/tailwind.config.js index 2f74c9d8..7530591e 100644 --- a/apps/web/tailwind.config.js +++ b/apps/web/tailwind.config.js @@ -9,6 +9,7 @@ module.exports = { extend: { colors: { primary: '#003366', + 'primary-foreground': '#FFFFFF', bcYellowPrimary: '#FCBA19', bcBlack: '#313132', bcDeepBlack: '#272833', diff --git a/yarn.lock b/yarn.lock index f30cffe5..1117e299 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1301,7 +1301,7 @@ __metadata: languageName: node linkType: hard -"@hookform/resolvers@npm:^3.9.1": +"@hookform/resolvers@npm:3.9.1": version: 3.9.1 resolution: "@hookform/resolvers@npm:3.9.1" peerDependencies: @@ -1353,6 +1353,7 @@ __metadata: "@nestjs/cli": 8.0.0 "@nestjs/common": 9.3.9 "@nestjs/core": 9.3.9 + "@nestjs/event-emitter": 2.1.1 "@nestjs/platform-express": 9.4.3 "@nestjs/schematics": 8.0.0 "@nestjs/swagger": 5.1.5 @@ -1440,14 +1441,14 @@ __metadata: "@fortawesome/free-solid-svg-icons": 6.5.2 "@fortawesome/react-fontawesome": 0.2.2 "@headlessui/react": 2.1.2 - "@hookform/resolvers": ^3.9.1 + "@hookform/resolvers": 3.9.1 "@ien/common": 1.0.2 "@next/eslint-plugin-next": 14.2.5 - "@radix-ui/react-dialog": ^1.1.2 - "@radix-ui/react-label": ^2.1.0 - "@radix-ui/react-popover": ^1.1.2 - "@radix-ui/react-select": ^2.1.2 - "@radix-ui/react-slot": ^1.1.0 + "@radix-ui/react-dialog": 1.1.2 + "@radix-ui/react-label": 2.1.0 + "@radix-ui/react-popover": 1.1.2 + "@radix-ui/react-select": 2.1.2 + "@radix-ui/react-slot": 1.1.0 "@tailwindcss/typography": 0.4.1 "@testing-library/jest-dom": 6.4.8 "@testing-library/react": 16.0.0 @@ -1459,12 +1460,12 @@ __metadata: autoprefixer: 10.4.0 axios: 0.28.0 babel-jest: 29.7.0 - class-variance-authority: ^0.7.1 + class-variance-authority: 0.7.1 classnames: 2.3.1 cookie: 0.4.2 cy-verify-downloads: 0.1.8 cypress: 13.0.0 - date-fns: ^4.1.0 + date-fns: 4.0.0 dotenv: 16.0.0 eslint: 8.57.0 eslint-config-next: 14.2.5 @@ -1475,26 +1476,26 @@ __metadata: identity-obj-proxy: 3.0.0 jest: 27.4.2 lodash: 4.17.21 - lucide-react: ^0.464.0 + lucide-react: 0.464.0 next: 14.2.5 oidc-client-ts: 3.0.1 postcss: 8.4.31 postcss-loader: 8.1.1 react: 18.3.1 react-datepicker: 4.8.0 - react-day-picker: ^9.4.1 + react-day-picker: 9.4.1 react-dom: 18.3.1 - react-hook-form: ^7.53.2 + react-hook-form: 7.53.2 react-oidc-context: 2.3.0 react-select: 5.8.0 react-test-renderer: 17.0.2 react-toastify: 10.0.5 - tailwind-merge: ^2.5.5 + tailwind-merge: 2.5.5 tailwindcss: 2.2.19 typescript: 5.7.2 use-http: 1.0.26 xlsx-js-style: 1.2.0 - zod: ^3.23.8 + zod: 3.23.8 languageName: unknown linkType: soft @@ -2172,6 +2173,18 @@ __metadata: languageName: node linkType: hard +"@nestjs/event-emitter@npm:2.1.1": + version: 2.1.1 + resolution: "@nestjs/event-emitter@npm:2.1.1" + dependencies: + eventemitter2: 6.4.9 + peerDependencies: + "@nestjs/common": ^8.0.0 || ^9.0.0 || ^10.0.0 + "@nestjs/core": ^8.0.0 || ^9.0.0 || ^10.0.0 + checksum: 41736b7a5603a23f9d41acfd27408b8dfce6f20b100429c4bb6b8ea444921796ecdc73dc3eda8c5a2fec1c3e5123d7c81337563c611856886dca082dc4d71f10 + languageName: node + linkType: hard + "@nestjs/mapped-types@npm:1.0.0": version: 1.0.0 resolution: "@nestjs/mapped-types@npm:1.0.0" @@ -2541,7 +2554,7 @@ __metadata: languageName: node linkType: hard -"@radix-ui/react-dialog@npm:^1.1.2": +"@radix-ui/react-dialog@npm:1.1.2": version: 1.1.2 resolution: "@radix-ui/react-dialog@npm:1.1.2" dependencies: @@ -2658,7 +2671,7 @@ __metadata: languageName: node linkType: hard -"@radix-ui/react-label@npm:^2.1.0": +"@radix-ui/react-label@npm:2.1.0": version: 2.1.0 resolution: "@radix-ui/react-label@npm:2.1.0" dependencies: @@ -2677,7 +2690,7 @@ __metadata: languageName: node linkType: hard -"@radix-ui/react-popover@npm:^1.1.2": +"@radix-ui/react-popover@npm:1.1.2": version: 1.1.2 resolution: "@radix-ui/react-popover@npm:1.1.2" dependencies: @@ -2797,7 +2810,7 @@ __metadata: languageName: node linkType: hard -"@radix-ui/react-select@npm:^2.1.2": +"@radix-ui/react-select@npm:2.1.2": version: 2.1.2 resolution: "@radix-ui/react-select@npm:2.1.2" dependencies: @@ -2836,7 +2849,7 @@ __metadata: languageName: node linkType: hard -"@radix-ui/react-slot@npm:1.1.0, @radix-ui/react-slot@npm:^1.1.0": +"@radix-ui/react-slot@npm:1.1.0": version: 1.1.0 resolution: "@radix-ui/react-slot@npm:1.1.0" dependencies: @@ -5817,7 +5830,7 @@ __metadata: languageName: node linkType: hard -"class-variance-authority@npm:^0.7.1": +"class-variance-authority@npm:0.7.1": version: 0.7.1 resolution: "class-variance-authority@npm:0.7.1" dependencies: @@ -6665,6 +6678,13 @@ __metadata: languageName: node linkType: hard +"date-fns@npm:4.0.0": + version: 4.0.0 + resolution: "date-fns@npm:4.0.0" + checksum: dd7f9962985e5b08165d9cd06350598c6042b7e1593c7b73c2eaceb07bb13c5b29f887705a285a8a4a9ffb2bbf669a285ece01691f169a8a0d01de7b23779bc0 + languageName: node + linkType: hard + "date-fns@npm:^2.0.1, date-fns@npm:^2.24.0": version: 2.29.1 resolution: "date-fns@npm:2.29.1" @@ -8042,6 +8062,13 @@ __metadata: languageName: node linkType: hard +"eventemitter2@npm:6.4.9": + version: 6.4.9 + resolution: "eventemitter2@npm:6.4.9" + checksum: be59577c1e1c35509c7ba0e2624335c35bbcfd9485b8a977384c6cc6759341ea1a98d3cb9dbaa5cea4fff9b687e504504e3f9c2cc1674cf3bd8a43a7c74ea3eb + languageName: node + linkType: hard + "eventemitter3@npm:4.0.7": version: 4.0.7 resolution: "eventemitter3@npm:4.0.7" @@ -12050,7 +12077,7 @@ __metadata: languageName: node linkType: hard -"lucide-react@npm:^0.464.0": +"lucide-react@npm:0.464.0": version: 0.464.0 resolution: "lucide-react@npm:0.464.0" peerDependencies: @@ -14236,7 +14263,7 @@ __metadata: languageName: node linkType: hard -"react-day-picker@npm:^9.4.1": +"react-day-picker@npm:9.4.1": version: 9.4.1 resolution: "react-day-picker@npm:9.4.1" dependencies: @@ -14287,7 +14314,7 @@ __metadata: languageName: node linkType: hard -"react-hook-form@npm:^7.53.2": +"react-hook-form@npm:7.53.2": version: 7.53.2 resolution: "react-hook-form@npm:7.53.2" peerDependencies: @@ -16016,7 +16043,7 @@ __metadata: languageName: node linkType: hard -"tailwind-merge@npm:^2.5.5": +"tailwind-merge@npm:2.5.5": version: 2.5.5 resolution: "tailwind-merge@npm:2.5.5" checksum: c835c763642988aa7185f242f93a61a6ce45d577edbefa4016727e664d9e130fa153b0d3a8bbcfcefd1de6a204e9d302971826d4d1068b6fd92ef66a27f35351 @@ -18070,7 +18097,7 @@ __metadata: languageName: node linkType: hard -"zod@npm:^3.23.8": +"zod@npm:3.23.8": version: 3.23.8 resolution: "zod@npm:3.23.8" checksum: 15949ff82118f59c893dacd9d3c766d02b6fa2e71cf474d5aa888570c469dbf5446ac5ad562bb035bf7ac9650da94f290655c194f4a6de3e766f43febd432c5c