From 2811c2b5549aaeac96bbafdd03ee79b56dfe1cab Mon Sep 17 00:00:00 2001 From: Eduardo Peredo Rivero Date: Wed, 13 Mar 2024 20:51:03 -0500 Subject: [PATCH] add logic for step 7 --- src/CompositionRoot.ts | 14 + src/domain/entities/MidwiferyPersonnel.ts | 42 ++ src/domain/entities/Settings.ts | 4 +- ...idwiferyPersonnelDisaggregationsUseCase.ts | 22 + .../ValidateMidwiferyAndPersonnelUseCase.ts | 382 ++++++++++++++++++ .../analysis-filter/AnalysisFilter.tsx | 2 +- src/webapp/components/issues/IssueTable.tsx | 11 +- src/webapp/pages/analysis/steps.ts | 14 +- .../NursingMidwiferyStep.tsx | 56 ++- .../useNursingMidwiferyStep.tsx | 164 ++++---- 10 files changed, 584 insertions(+), 127 deletions(-) create mode 100644 src/domain/entities/MidwiferyPersonnel.ts create mode 100644 src/domain/usecases/GetMidwiferyPersonnelDisaggregationsUseCase.ts create mode 100644 src/domain/usecases/ValidateMidwiferyAndPersonnelUseCase.ts diff --git a/src/CompositionRoot.ts b/src/CompositionRoot.ts index 74957a4..6c1d0c2 100644 --- a/src/CompositionRoot.ts +++ b/src/CompositionRoot.ts @@ -50,6 +50,8 @@ import { DataValueTestRepository } from "./data/repositories/DataValueTestReposi import { GetDisaggregationsUseCase } from "$/domain/usecases/GetDisaggregationsUseCase"; import { GetMissingDisaggregatesUseCase } from "./domain/usecases/GetMissingDisaggregatesUseCase"; import { GetCategoryDesaggregationsUseCase } from "$/domain/usecases/GetCategoryDesaggregationsUseCase"; +import { ValidateMidwiferyAndPersonnelUseCase } from "./domain/usecases/ValidateMidwiferyAndPersonnelUseCase"; +import { GetMidwiferyPersonnelDisaggregationsUseCase } from "$/domain/usecases/GetMidwiferyPersonnelDisaggregationsUseCase"; export type CompositionRoot = ReturnType; @@ -122,6 +124,18 @@ function getCompositionRoot(repositories: Repositories, metadata: MetadataItem) }, issues: { save: new SaveIssueUseCase(repositories.qualityAnalysisRepository, metadata) }, settings: { get: new GetSettingsUseCase(repositories.settingsRepository) }, + nursingMidwifery: { + getDisaggregations: new GetMidwiferyPersonnelDisaggregationsUseCase( + repositories.settingsRepository + ), + validate: new ValidateMidwiferyAndPersonnelUseCase( + repositories.qualityAnalysisRepository, + repositories.issueRepository, + repositories.dataValueRepository, + repositories.moduleRepository, + repositories.settingsRepository + ), + }, }; } diff --git a/src/domain/entities/MidwiferyPersonnel.ts b/src/domain/entities/MidwiferyPersonnel.ts new file mode 100644 index 0000000..c66d4dc --- /dev/null +++ b/src/domain/entities/MidwiferyPersonnel.ts @@ -0,0 +1,42 @@ +import { Maybe } from "$/utils/ts-utils"; +import { NamedRef } from "@eyeseetea/d2-logger/domain/entities/Base"; +import { convertToNumberOrZero } from "../usecases/common/utils"; +import { DataElement } from "./DataElement"; +import { DataValue } from "./DataValue"; +import { Struct } from "./generic/Struct"; +import { Id, Period } from "./Ref"; + +type MidwiferyNursingAttrs = { + midwifery: MidwiferyNursingValue; + personnel: MidwiferyNursingValue; + isMidwiferyGreater: boolean; + period: Period; + countryId: Id; + combination: NamedRef; +}; + +type MidwiferyNursingValue = { + dataElement: DataElement; + dataValue: Maybe; +}; + +export class MidwiferyNursing extends Struct() { + static build(data: MidwiferyNursingAttrs): MidwiferyNursing { + return this.create({ + ...data, + isMidwiferyGreater: this.checkIfMidwiferyIsGreater(data.midwifery, data.personnel), + }); + } + + private static checkIfMidwiferyIsGreater( + midwifery: MidwiferyNursingValue, + nursing: MidwiferyNursingValue + ): boolean { + if (!midwifery.dataValue && !nursing.dataValue) return false; + + const midwiferyValue = convertToNumberOrZero(midwifery.dataValue?.value); + const nursingValue = convertToNumberOrZero(nursing.dataValue?.value); + + return midwiferyValue > nursingValue; + } +} diff --git a/src/domain/entities/Settings.ts b/src/domain/entities/Settings.ts index bb1886d..d542c43 100644 --- a/src/domain/entities/Settings.ts +++ b/src/domain/entities/Settings.ts @@ -1,3 +1,4 @@ +import { Maybe } from "$/utils/ts-utils"; import { Either } from "./generic/Either"; import { ValidationError } from "./generic/Errors"; import { Struct } from "./generic/Struct"; @@ -22,8 +23,9 @@ export type SectionDisaggregation = { id: Id; disaggregationId: Id; name: string; - type: "combos"; + type: "combos" | "key_occupations" | "edu_occupations"; combinations: string[]; + nursingMidwifery: Maybe; }; export class Settings extends Struct() { diff --git a/src/domain/usecases/GetMidwiferyPersonnelDisaggregationsUseCase.ts b/src/domain/usecases/GetMidwiferyPersonnelDisaggregationsUseCase.ts new file mode 100644 index 0000000..141f10b --- /dev/null +++ b/src/domain/usecases/GetMidwiferyPersonnelDisaggregationsUseCase.ts @@ -0,0 +1,22 @@ +import { Id } from "$/domain/entities/Ref"; +import { SectionDisaggregation } from "$/domain/entities/Settings"; +import { SettingsRepository } from "$/domain/repositories/SettingsRepository"; +import { Future } from "$/domain/entities/generic/Future"; +import _ from "$/domain/entities/generic/Collection"; +import { FutureData } from "$/data/api-futures"; + +export class GetMidwiferyPersonnelDisaggregationsUseCase { + constructor(private settingsRepository: SettingsRepository) {} + + execute(sectionId: Id): FutureData { + return this.settingsRepository.get().flatMap(settings => { + const section = settings.sections.find(section => section.id === sectionId); + if (!section) + return Future.error(new Error(`Cannot found section settings: ${sectionId}`)); + const disaggregationsSorted = _(section.disaggregations) + .sortBy(disaggregation => disaggregation.name) + .value(); + return Future.success(disaggregationsSorted); + }); + } +} diff --git a/src/domain/usecases/ValidateMidwiferyAndPersonnelUseCase.ts b/src/domain/usecases/ValidateMidwiferyAndPersonnelUseCase.ts new file mode 100644 index 0000000..44f2bd8 --- /dev/null +++ b/src/domain/usecases/ValidateMidwiferyAndPersonnelUseCase.ts @@ -0,0 +1,382 @@ +import { FutureData } from "$/data/api-futures"; +import { DataElement } from "$/domain/entities/DataElement"; +import { QualityAnalysis } from "$/domain/entities/QualityAnalysis"; +import { Id, Period } from "$/domain/entities/Ref"; +import { Future } from "$/domain/entities/generic/Future"; +import { DataValueRepository } from "$/domain/repositories/DataValueRepository"; +import { ModuleRepository } from "$/domain/repositories/ModuleRepository"; +import { QualityAnalysisRepository } from "$/domain/repositories/QualityAnalysisRepository"; +import _ from "lodash"; +import { UCAnalysis } from "./common/UCAnalysis"; +import { UCDataValue } from "./common/UCDataValue"; +import { DataValue } from "$/domain/entities/DataValue"; +import { MidwiferyNursing } from "$/domain/entities/MidwiferyPersonnel"; +import { SettingsRepository } from "../repositories/SettingsRepository"; +import { SectionDisaggregation } from "$/domain/entities/Settings"; +import { UCIssue } from "./common/UCIssue"; +import { QualityAnalysisIssue } from "$/domain/entities/QualityAnalysisIssue"; +import { IssueRepository } from "../repositories/IssueRepository"; +import { getCurrentSection } from "./common/utils"; + +export class ValidateMidwiferyAndPersonnelUseCase { + analysisUseCase: UCAnalysis; + dataValueUseCase: UCDataValue; + issueUseCase: UCIssue; + constructor( + private analysisRepository: QualityAnalysisRepository, + private issueRepository: IssueRepository, + private dataValueRepository: DataValueRepository, + private moduleRepository: ModuleRepository, + private settingsRepository: SettingsRepository + ) { + this.analysisUseCase = new UCAnalysis(this.analysisRepository); + this.dataValueUseCase = new UCDataValue(this.dataValueRepository); + this.issueUseCase = new UCIssue(this.issueRepository); + } + + execute(options: ValidateMidwiferyAndPersonnelOptions): FutureData { + return this.getSettingsSections(options).flatMap(disaggregations => { + return this.validateMidwiferyAndPersonnel(options, disaggregations); + }); + } + + private getSettingsSections( + options: ValidateMidwiferyAndPersonnelOptions + ): FutureData { + return this.settingsRepository.get().flatMap(settings => { + const section = settings.sections.find(section => section.id === options.sectionId); + if (!section) + return Future.error( + new Error(`Cannot found section settings: ${options.sectionId}`) + ); + const onlySelectedDisaggregations = section.disaggregations.filter(disaggregation => + options.disaggregationsIds.includes(disaggregation.id) + ); + return Future.success(onlySelectedDisaggregations); + }); + } + + private validateMidwiferyAndPersonnel( + options: ValidateMidwiferyAndPersonnelOptions, + disaggregations: SectionDisaggregation[] + ): FutureData { + return this.getAnalysis(options).flatMap(analysis => { + return this.getDataElementsWithValues(analysis).flatMap( + ({ dataElements, dataValues }) => { + const midwiferyNursingValues = this.getMidwiferyPersonnelValues( + dataValues, + dataElements, + disaggregations + ); + + return this.issueUseCase + .getTotalIssuesBySection(analysis, options.sectionId) + .flatMap(totalIssues => { + const issues = this.createIssues( + midwiferyNursingValues, + analysis, + options.sectionId, + totalIssues + ); + return this.persistIssues(issues, analysis, options, issues.length); + }); + } + ); + }); + } + + private persistIssues( + issues: QualityAnalysisIssue[], + analysis: QualityAnalysis, + options: ValidateMidwiferyAndPersonnelOptions, + totalIssues: number + ): Future { + return this.issueUseCase.save(issues, analysis.id).flatMap(() => { + const analysisToUpdate = this.analysisUseCase.updateAnalysis( + analysis, + options.sectionId, + totalIssues + ); + return this.analysisRepository.save([analysisToUpdate]).flatMap(() => { + return Future.success(analysisToUpdate); + }); + }); + } + + private createIssues( + midwiferyNursing: MidwiferyNursing[], + analysis: QualityAnalysis, + sectionId: Id, + totalIssues: number + ): QualityAnalysisIssue[] { + const section = getCurrentSection(analysis, sectionId); + const onlyGreaterMidwifery = midwiferyNursing.filter( + midwifery => midwifery.isMidwiferyGreater + ); + return onlyGreaterMidwifery.map((midwifery, index) => { + const currentNumber = totalIssues + (index + 1); + const prefix = `${analysis.sequential.value}-S07`; + const issueNumber = this.issueUseCase.generateIssueNumber(currentNumber, prefix); + return this.issueUseCase.buildDefaultIssue( + { + categoryOptionComboId: midwifery.combination.id, + countryId: midwifery.countryId, + period: midwifery.period, + dataElementId: midwifery.midwifery.dataElement.id, + description: "Midwifery personnel count is higher than Nursing personnel count", + issueNumber: issueNumber, + correlative: String(currentNumber), + }, + section.id + ); + }); + } + + private getMidwiferyPersonnelValues( + dataValues: Record, + dataElements: DataElement[], + disaggregations: SectionDisaggregation[] + ): MidwiferyNursing[] { + const dataValuesByKeys = _(dataValues).keys().value(); + + return _(dataValuesByKeys) + .map((key): MidwiferyNursing[] => { + const dvCountryPeriod = dataValues[key]; + const [countryId, period] = key.split("."); + if (!countryId || !period) { + throw Error(`Cannot found country or period: ${key}`); + } + if (!dvCountryPeriod) return []; + + return _(disaggregations) + .map(settingDisaggregation => { + const currentDataElements = this.getDataElementsBySettingType( + settingDisaggregation, + dataElements + ); + const dataElementsToCheck = + this.getDataElementsToCheck(settingDisaggregation); + if (this.isOccupation(settingDisaggregation)) { + const result = this.getOccupationValues( + currentDataElements, + dataElementsToCheck, + dvCountryPeriod, + period, + countryId + ); + return result; + } else { + const result = this.getMidwiferyNursingValues( + dataElementsToCheck, + currentDataElements, + dvCountryPeriod, + period, + countryId + ); + return result; + } + }) + .flatten() + .value(); + }) + .flatten() + .value(); + } + + private getMidwiferyNursingValues( + dataElementsToCheck: string[][], + dataElements: DataElement[], + dataValues: DataValue[], + period: Period, + countryId: Id + ) { + return _(dataElementsToCheck) + .flatMap(dataElementToCheck => { + const [nursing, midwifery] = dataElementToCheck; + if (!nursing || !midwifery) return []; + const personnelDe = dataElements.find(de => de.name.includes(nursing)); + const midwiferyDe = dataElements.find(de => de.name.includes(midwifery)); + + if (!personnelDe || !midwiferyDe) return []; + + const combinations = personnelDe?.disaggregation?.options || []; + + const result = _(combinations) + .map(combination => { + const personnelDv = dataValues.find( + dv => + dv.dataElementId === personnelDe?.id && + dv.categoryOptionComboId === combination.id + ); + const midwiferyDv = dataValues.find( + dv => + dv.dataElementId === midwiferyDe?.id && + dv.categoryOptionComboId === combination.id + ); + + return MidwiferyNursing.build({ + combination: combination, + midwifery: { dataElement: midwiferyDe, dataValue: midwiferyDv }, + personnel: { dataElement: personnelDe, dataValue: personnelDv }, + isMidwiferyGreater: false, + period: period, + countryId: countryId, + }); + }) + .value(); + + return result; + }) + .value(); + } + + private getOccupationValues( + dataElements: DataElement[], + dataElementsToCheck: string[][], + dataValues: DataValue[], + period: Period, + countryId: Id + ) { + return _(dataElements) + .flatMap(dataElement => { + return _(dataElementsToCheck) + .map(dataElementToCheck => { + const [nursing, midwifery] = dataElementToCheck; + if (!nursing || !midwifery) return undefined; + + const nursingCombination = dataElement.disaggregation?.options.find( + option => option.name === nursing + ); + const midwiferyCombination = dataElement.disaggregation?.options.find( + option => option.name === midwifery + ); + + if (!nursingCombination || !midwiferyCombination) return undefined; + + const nursingDv = dataValues.find( + dataValue => + dataValue.dataElementId === dataElement.id && + dataValue.categoryOptionComboId === nursingCombination?.id + ); + const midwiferyDv = dataValues.find( + dataValue => + dataValue.dataElementId === dataElement.id && + dataValue.categoryOptionComboId === midwiferyCombination?.id + ); + + return MidwiferyNursing.build({ + combination: midwiferyCombination, + midwifery: { dataElement: dataElement, dataValue: midwiferyDv }, + personnel: { dataElement: dataElement, dataValue: nursingDv }, + isMidwiferyGreater: false, + period: period, + countryId: countryId, + }); + }) + .compact() + .value(); + }) + .value(); + } + + private getDataElementsToCheck(sectionDisaggregation: SectionDisaggregation): string[][] { + if (sectionDisaggregation.type === "edu_occupations") { + return [ + ["2. Nursing Professionals", "5. Midwifery Professionals"], + ["3. Nursing Associate Professionals", "6. Midwifery Associate Professionals"], + ["4. Nurses not further defined", "7. Midwives not further defined"], + ]; + } else if (sectionDisaggregation.type === "key_occupations") { + return [["2. Nursing Personnel", "3. Midwifery Personnel"]]; + } else { + return [ + ["2 - Nursing Personnel", "3 - Midwifery personnel"], + ["2.1 - Nursing Professionals", "3.1 - Midwifery Professionals"], + [ + "2.2 - Nursing Associate Professionals", + "3.2 - Midwifery Associate Professionals", + ], + ["2.3 - Nurses not further defined", "3.3 - Midwives not further defined"], + ]; + } + } + + private getDataElementsBySettingType( + settingDisaggregation: SectionDisaggregation, + dataElements: DataElement[] + ): DataElement[] { + if (this.isOccupation(settingDisaggregation)) { + return dataElements.filter(dataElement => { + const disaggregationFromName = dataElement.name.split(" - ")[0]; + return ( + settingDisaggregation.disaggregationId === dataElement.disaggregation?.id && + disaggregationFromName === settingDisaggregation.id + ); + }); + } else { + return dataElements.filter( + dataElement => dataElement.disaggregation?.id === settingDisaggregation.id + ); + } + } + + private isOccupation(sectionDisaggregation: SectionDisaggregation): boolean { + return ( + sectionDisaggregation.type === "edu_occupations" || + sectionDisaggregation.type === "key_occupations" + ); + } + + private getAnalysis( + options: ValidateMidwiferyAndPersonnelOptions + ): FutureData { + return this.analysisUseCase.getById(options.analysisId); + } + + private getDataElementsWithValues( + analysis: QualityAnalysis + ): FutureData<{ dataValues: Record; dataElements: DataElement[] }> { + return this.moduleRepository.getByIds([analysis.module.id]).flatMap(modules => { + const module = modules[0]; + if (!module) throw Error(`Module not found: ${analysis.module.id}`); + + const sorted = _(module.dataElements) + .sortBy(dataElement => dataElement.name) + .value(); + + return this.getDataValues(analysis, sorted); + }); + } + + private getDataValues( + analysis: QualityAnalysis, + dataElements: DataElement[] + ): FutureData<{ dataValues: Record; dataElements: DataElement[] }> { + return this.dataValueUseCase + .get( + analysis.countriesAnalysis, + [analysis.module.id], + [analysis.startDate, analysis.endDate] + ) + .flatMap(dataValues => { + const dataElementsByKey = _(dataElements) + .keyBy(de => de.id) + .value(); + + const filteredDataValues = dataValues.filter(dataValue => + Boolean(dataElementsByKey[dataValue.dataElementId]) + ); + + const dvByCountryPeriod = + this.dataValueUseCase.getByCountryAndPeriod(filteredDataValues); + + return Future.success({ dataElements, dataValues: dvByCountryPeriod }); + }); + } +} + +type ValidateMidwiferyAndPersonnelOptions = { + analysisId: Id; + sectionId: Id; + disaggregationsIds: Id[]; +}; diff --git a/src/webapp/components/analysis-filter/AnalysisFilter.tsx b/src/webapp/components/analysis-filter/AnalysisFilter.tsx index c161ed8..099014b 100644 --- a/src/webapp/components/analysis-filter/AnalysisFilter.tsx +++ b/src/webapp/components/analysis-filter/AnalysisFilter.tsx @@ -9,7 +9,7 @@ import { Maybe } from "$/utils/ts-utils"; import { MenuButton } from "../menu-button/MenuButton"; import { Id } from "$/domain/entities/Ref"; -const currentYear = new Date().getFullYear(); +const currentYear = new Date().getFullYear() + 1; export const periods = Collection.range(currentYear - 5, currentYear) .map(period => { diff --git a/src/webapp/components/issues/IssueTable.tsx b/src/webapp/components/issues/IssueTable.tsx index 976f457..fd64a1d 100644 --- a/src/webapp/components/issues/IssueTable.tsx +++ b/src/webapp/components/issues/IssueTable.tsx @@ -17,7 +17,16 @@ export function useTableConfig() { { name: "country", text: i18n.t("Country"), sortable: false }, { name: "period", text: i18n.t("Period"), sortable: false }, { name: "dataElement", text: i18n.t("Data Element"), sortable: false }, - { name: "categoryOption", text: i18n.t("Category"), sortable: false }, + { + name: "categoryOption", + text: i18n.t("Category"), + sortable: false, + getValue: value => { + return value.categoryOption?.name === "default" + ? "Total" + : value.categoryOption?.name; + }, + }, { name: "description", text: i18n.t("Description"), diff --git a/src/webapp/pages/analysis/steps.ts b/src/webapp/pages/analysis/steps.ts index 4cce8d5..f0530b0 100644 --- a/src/webapp/pages/analysis/steps.ts +++ b/src/webapp/pages/analysis/steps.ts @@ -18,6 +18,8 @@ export const disaggregateKey = "3. Missing disaggregates in selected catcombos " export const practitionersKey = "4. Medical doctors analysis: General Practicioners missing and double counts"; +export const missingNursing = "7. Missing nursing personnel when midwifery personnel is present"; + export const steps = [ { key: "configuration", @@ -56,15 +58,15 @@ export const steps = [ completed: false, }, { - key: "midwifery", - label: i18n.t("Midwifery"), - component: MidwiferyStep, + key: missingNursing, + label: i18n.t("Nursing/Midwifery"), + component: NursingMidwiferyStep, completed: false, }, { - key: "nursing-midwifery", - label: i18n.t("Nursing/Midwifery"), - component: NursingMidwiferyStep, + key: "midwifery", + label: i18n.t("Midwifery"), + component: MidwiferyStep, completed: false, }, { diff --git a/src/webapp/pages/analysis/steps/7-nursingMidwifery/NursingMidwiferyStep.tsx b/src/webapp/pages/analysis/steps/7-nursingMidwifery/NursingMidwiferyStep.tsx index c66adc0..c4aa35a 100644 --- a/src/webapp/pages/analysis/steps/7-nursingMidwifery/NursingMidwiferyStep.tsx +++ b/src/webapp/pages/analysis/steps/7-nursingMidwifery/NursingMidwiferyStep.tsx @@ -1,58 +1,54 @@ import React from "react"; import i18n from "$/utils/i18n"; -import { EmptyState } from "$/webapp/components/empty-state/EmptyState"; -import { Typography, Button } from "@material-ui/core"; import styled from "styled-components"; import { useNursingMidwiferyStep } from "./useNursingMidwiferyStep"; import { SelectMultiCheckboxes } from "$/webapp/components/selectmulti-checkboxes/SelectMultiCheckboxes"; +import { useParams } from "react-router-dom"; +import { StepAnalysis } from "../StepAnalysis"; +import { missingNursing } from "../../steps"; interface PageProps { name: string; } -export const NursingMidwiferyStep: React.FC = React.memo(props => { - const { name = "Missing nursing personnel when midwifery personnel is present" } = props; - const { catCombosList, values, handleChange, runAnalysis } = useNursingMidwiferyStep(); +export const NursingMidwiferyStep: React.FC = React.memo(() => { + const { id } = useParams<{ id: string }>(); + const { + analysis, + disaggregations, + selectedDisaggregations, + handleChange, + reload, + runAnalysis, + } = useNursingMidwiferyStep({ analysisId: id }); + const section = analysis?.sections.find(section => section.name === missingNursing); + + if (!analysis || !section) return null; return ( - - {i18n.t(name)} + - - - + ); }); const Container = styled.section``; -const AnalysisHeader = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - height: 5rem; - gap: 1rem; - margin-block-end: 1.75rem; - flex-wrap: wrap; -`; - -const StyledTypography = styled(Typography)` - font-size: 1.2rem; - font-weight: 500; - max-width: 22rem; -`; - const FiltersContainer = styled.div` display: flex; align-items: center; diff --git a/src/webapp/pages/analysis/steps/7-nursingMidwifery/useNursingMidwiferyStep.tsx b/src/webapp/pages/analysis/steps/7-nursingMidwifery/useNursingMidwiferyStep.tsx index 11f47ba..b18c28a 100644 --- a/src/webapp/pages/analysis/steps/7-nursingMidwifery/useNursingMidwiferyStep.tsx +++ b/src/webapp/pages/analysis/steps/7-nursingMidwifery/useNursingMidwiferyStep.tsx @@ -1,102 +1,90 @@ -import { useState } from "react"; +import React from "react"; +import { useLoading, useSnackbar } from "@eyeseetea/d2-ui-components"; + import i18n from "$/utils/i18n"; +import { useAnalysisById } from "$/webapp/hooks/useAnalysis"; +import { Id } from "$/domain/entities/Ref"; +import { useAppContext } from "$/webapp/contexts/app-context"; +import { missingNursing } from "../../steps"; + +export function useNursingMidwiferyStep(props: UseNursingMidwiferyStepProps) { + const { analysisId } = props; + const { compositionRoot } = useAppContext(); + const snackbar = useSnackbar(); + const loading = useLoading(); -export function useNursingMidwiferyStep() { - const catCombosList = [ - { - value: "ACTIVITY", - text: i18n.t("Activity Level"), - }, - { - value: "SEX", - text: i18n.t("Sex"), - }, - { - value: "AGE", - text: i18n.t("Age Group"), - }, - { - value: "AGESEX", - text: i18n.t("Age Group + Sex"), - }, - { - value: "BIRTHPLACE", - text: i18n.t("Place of Birth"), - }, - { - value: "TRAININGPLACE", - text: i18n.t("Place of Training"), - }, - { - value: "FOREIGNTRAINED", - text: i18n.t("Foreign Trained"), - }, - { - value: "OWNERSHIP", - text: i18n.t("Ownership"), - }, - { - value: "WORKINGFACILITYTYPE", - text: i18n.t("Working Facility Type"), - }, - { - value: "NADOMESTICTHWF", - text: i18n.t("Newly Active domestic trained HWF"), - }, - { - value: "NAFOREIGNTE", - text: i18n.t("Newly Active foreign trained employed"), - }, - { - value: "VOLUNTARY", - text: i18n.t("Voluntary Exits"), - }, - { - value: "INVOLUNTARY", - text: i18n.t("Involuntary exits"), - }, - { - value: "VACANCY", - text: i18n.t("Vacancy rate"), - }, - { - value: "CONTRACT", - text: i18n.t("Contract Type"), - }, - { - value: "APPLICATIONS", - text: i18n.t("Applications"), - }, - { - value: "ENROLLED", - text: i18n.t("Enrolled"), - }, - { - value: "GRADUATESGENDER", - text: i18n.t("Graduates by Gender"), - }, - { - value: "GRADUATESINSTITUTION", - text: i18n.t("Graduates by institution ownership"), - }, - ]; + const [reload, refreshReload] = React.useState(0); + const { analysis, setAnalysis } = useAnalysisById({ id: analysisId }); + const [disaggregations, setDisaggregations] = React.useState<{ value: Id; text: string }[]>([]); + const [selectedDisaggregations, setSelectedDissagregations] = React.useState([]); - const defaultValues = catCombosList.map(option => option.text); + React.useEffect(() => { + if (!analysis) return; + const section = analysis?.sections.find(section => section.name === missingNursing); + if (!section) return; - const [values, setValues] = useState(defaultValues); + loading.show(true, i18n.t("Loading")); + compositionRoot.nursingMidwifery.getDisaggregations.execute(section.name).run( + result => { + const selectedDisaggregations = result.map(item => ({ + value: item.id, + text: item.name, + })); + setDisaggregations(selectedDisaggregations); + setSelectedDissagregations(selectedDisaggregations.map(item => item.value)); + loading.hide(); + }, + error => { + loading.hide(); + snackbar.error(error.message); + } + ); + }, [analysis, compositionRoot.nursingMidwifery.getDisaggregations, loading, snackbar]); const handleChange = (values: string[]) => { - setValues(values); + setSelectedDissagregations(values); }; - const runAnalysis = () => { - alert(`run analysis`); - }; + const runAnalysis = React.useCallback(() => { + if (!analysis) return false; + const section = analysis?.sections.find(section => section.name === missingNursing); + if (!section) return false; + loading.show(true, i18n.t("Running analysis...")); + compositionRoot.nursingMidwifery.validate + .execute({ + analysisId: analysis.id, + disaggregationsIds: selectedDisaggregations, + sectionId: section.name, + }) + .run( + analysis => { + refreshReload(reload + 1); + setAnalysis(analysis); + loading.hide(); + }, + err => { + snackbar.error(err.message); + loading.hide(); + } + ); + }, [ + analysis, + compositionRoot.nursingMidwifery.validate, + loading, + reload, + setAnalysis, + snackbar, + selectedDisaggregations, + ]); return { - catCombosList, - values, + analysis, + reload, + disaggregations, + selectedDisaggregations, handleChange, runAnalysis, }; } + +type UseNursingMidwiferyStepProps = { analysisId: Id };