Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…esm-3.x into main
  • Loading branch information
Felix Kiprotich committed Jan 17, 2025
2 parents 4a482be + 211a31e commit 4ed1321
Show file tree
Hide file tree
Showing 23 changed files with 325 additions and 1,193 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
Diagnosis,
Package,
PatientBenefit,
SHAIntervension,
SHAIntervention,
shifIdentifiersResponse,
} from '../types';

Expand Down Expand Up @@ -52,10 +52,10 @@ export const packages = [
},
] as Array<Package>;

export const intervensions = patientBenefits.map(({ interventionCode, interventionName }) => ({
export const interventions = patientBenefits.map(({ interventionCode, interventionName }) => ({
interventionCode,
interventionName,
})) as Array<SHAIntervension>;
})) as Array<SHAIntervention>;

export const coverageEligibilityResponse = [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import useProvider from '../../hooks/useProvider';
import { PatientBenefit } from '../../types';
import { preAuthenticateBenefit, preauthSchema } from '../benefits-package.resources';
import styles from './benefits-pre-auth-form.scss';
import PackageIntervensions from './package-intervensions.component';
import PackageInterventions from './package-interventions.component';
import { ErrorState } from '@openmrs/esm-patient-common-lib';

type BenefitsPreAuth = z.infer<typeof preauthSchema>;
Expand Down Expand Up @@ -240,8 +240,9 @@ const BenefitPreAuthForm: React.FC<BenefitPreAuthFormProps> = ({ closeWorkspace,
</Column>
{selectedPackageObservable && (
<Column>
<PackageIntervensions
<PackageInterventions
category={packages.find((package_) => package_.uuid === selectedPackageObservable)?.packageCode ?? ''}
patientUuid={patientUuid}
/>
</Column>
)}
Expand All @@ -255,7 +256,7 @@ const BenefitPreAuthForm: React.FC<BenefitPreAuthFormProps> = ({ closeWorkspace,
invalid={form.formState.errors[field.name]?.message}
invalidText={form.formState.errors[field.name]?.message}
id="diagnoses"
titleText={t('diagnosis', 'Diagnosis')}
titleText={t('finalDiagnosis', 'Final Diagnosis')}
selectedItems={field.value}
label="Choose option"
items={diagnoses.map((r) => r.id)}
Expand All @@ -279,7 +280,7 @@ const BenefitPreAuthForm: React.FC<BenefitPreAuthFormProps> = ({ closeWorkspace,
<Button className={styles.button} kind="secondary" onClick={closeWorkspace}>
{t('discard', 'Discard')}
</Button>
<Button className={styles.button} kind="primary" type="submit">
<Button className={styles.button} kind="primary" type="submit" disabled={isSubmitting}>
{isSubmitting ? (
<>
<Loading className={styles.button_spinner} withOverlay={false} small /> {t('submitting', 'Submitting')}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,57 @@
import { InlineLoading, InlineNotification, MultiSelect } from '@carbon/react';
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import { useInterventions } from '../../hooks/useInterventions';
import { InterventionsFilter, toQueryParams, useInterventions } from '../../hooks/useInterventions';
import { eligibilityRequestShema } from '../benefits-package.resources';
import { usePatient } from '@openmrs/esm-framework';

type EligibilityRequest = z.infer<typeof eligibilityRequestShema>;

type PackageIntervensionsProps = {
type PackageInterventionsProps = {
category: string;
patientUuid: string;
};
const PackageIntervensions: React.FC<PackageIntervensionsProps> = ({ category }) => {
const { error, interventions, isLoading } = useInterventions(category);
const PackageInterventions: React.FC<PackageInterventionsProps> = ({ category, patientUuid }) => {
const [filters, setFilters] = useState<InterventionsFilter>({ package_code: category });
const { error: patientError, isLoading: isPatientLoading, patient } = usePatient(patientUuid);
const { error, interventions, isLoading } = useInterventions(filters);

const form = useFormContext<EligibilityRequest>();
const { t } = useTranslation();

useEffect(() => {
setFilters((state) => ({
...state,
package_code: category,
applicable_gender: patient?.gender === 'male' ? 'MALE' : patient?.gender === 'female' ? 'FEMALE' : undefined,
}));
}, [category, patient]);

useEffect(() => {
form.setValue('interventions', []);
}, [category]);

Check warning on line 34 in packages/esm-billing-app/src/benefits-package/forms/package-interventions.component.tsx

View workflow job for this annotation

GitHub Actions / build

React Hook useEffect has a missing dependency: 'form'. Either include it or remove the dependency array

if (isLoading) {
return <InlineLoading status="active" iconDescription="Loading" description="Loading intervensions..." />;
if (isLoading || isPatientLoading) {
return (
<InlineLoading
status="active"
iconDescription="Loading"
description={t('loadingInterventions', 'Loading interventions') + '...'}
/>
);
}

if (error) {
if (error || patientError) {
return (
<InlineNotification
aria-label="closes notification"
kind="error"
lowContrast={true}
statusIconDescription="notification"
title={t('failure', 'Error loading intervensions')}
title={t('failure', 'Error loading interventions')}
subtitle={error?.message ?? patientError?.message}
/>
);
}
Expand Down Expand Up @@ -60,4 +80,4 @@ const PackageIntervensions: React.FC<PackageIntervensionsProps> = ({ category })
);
};

export default PackageIntervensions;
export default PackageInterventions;
53 changes: 43 additions & 10 deletions packages/esm-billing-app/src/billing-form/hie.resource.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,41 @@ import { openmrsFetch, restBaseUrl, useConfig, usePatient } from '@openmrs/esm-f
import useSWR from 'swr';
import { BillingConfig } from '../config-schema';

type EligibilityResponse = { message: { eligible: 0 | 1; possible_solution: string; reason: string } };
export interface EligibilityResponse {
requestIdType: number;
requestIdNumber: string;
memberCrNumber: string;
fullName: string;
memberType: string;
coverageStartDate: Date;
coverageEndDate: Date;
status: number;
message: string;
reason: string;
possibleSolution: null;
coverageType: string;
primaryContributor: null;
employerDetails: EmployerDetails;
dependants: Array<unknown>;
active: boolean;
}

export interface EmployerDetails {
employerName: string;
jobGroup: string;
scheme: Scheme;
}

export interface Scheme {
schemeCode: string;
schemeName: string;
schemeCategoryCode: string;
schemeCategoryName: string;
memberPolicyStartDate: string;
memberPolicyEndDate: string;
joinDate: string;
leaveDate: string;
}

type HIEEligibilityResponse = {
insurer: string;
Expand All @@ -11,7 +45,7 @@ type HIEEligibilityResponse = {
eligibility_response: EligibilityResponse;
};

export const useHIEEligibility = (patientUuid: string) => {
export const useSHAEligibility = (patientUuid: string, shaIdentificationNumber?: fhir.Identifier[]) => {
const { patient } = usePatient(patientUuid);
const { nationalIdUUID } = useConfig<BillingConfig>();

Expand All @@ -20,19 +54,18 @@ export const useHIEEligibility = (patientUuid: string) => {
.filter((identifier) => identifier.type.coding.some((coding) => coding.code === nationalIdUUID))
?.at(0)?.value;

const url = `${restBaseUrl}/insuranceclaims/CoverageEligibilityRequest?patientUuid=${patientUuid}&nationalId=${nationalId}`;
const url =
shaIdentificationNumber?.length > 0
? `${restBaseUrl}/insuranceclaims/CoverageEligibilityRequest?patientUuid=${patientUuid}&nationalId=${nationalId}`
: undefined; // this is to avoid making the request if the patient does not have a SHA Id.
const { data, error, isLoading, mutate } = useSWR<{ data: Array<HIEEligibilityResponse> }>(url, openmrsFetch, {
errorRetryCount: 0,
});

return {
data:
data?.data.map((d) => {
return {
...d,
eligibility_response: JSON.parse(d.eligibility_response as unknown as string) as EligibilityResponse,
};
}) ?? [],
data: data
? { ...(JSON.parse(data?.data.at(0)?.eligibility_response as unknown as string) as EligibilityResponse) }
: undefined,
isLoading,
error,
mutate,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { ActionableNotification, Form, InlineLoading, InlineNotification, Tooltip } from '@carbon/react';
import { CheckboxCheckedFilled, Information } from '@carbon/react/icons';
import { formatDate, navigate, useConfig, usePatient } from '@openmrs/esm-framework';
import capitalize from 'lodash/capitalize';
import { isWithinInterval } from 'date-fns';
import React from 'react';
import { useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { BillingConfig } from '../../config-schema';
import { useHIEEligibility } from '../hie.resource';
import { useSHAEligibility } from '../hie.resource';
import styles from './sha-number-validity.scss';

type SHANumberValidityProps = {
Expand All @@ -17,36 +17,27 @@ type SHANumberValidityProps = {
const SHANumberValidity: React.FC<SHANumberValidityProps> = ({ paymentMethod, patientUuid }) => {
const { t } = useTranslation();
const { shaIdentificationNumberUUID } = useConfig<BillingConfig>();
const { patient, isLoading } = usePatient(patientUuid);
const { patient, isLoading: isLoadingPatientUuid } = usePatient(patientUuid);
const { watch } = useFormContext();
const isSHA = watch('insuranceScheme')?.includes('SHA');
const { data, isLoading: isLoadingHIEEligibility, error } = useHIEEligibility(patientUuid);

const shaIdentificationNumber = patient?.identifier
?.filter((identifier) => identifier)
.filter((identifier) => identifier.type.coding.some((coding) => coding.code === shaIdentificationNumberUUID));

const isHIEEligible = true;
const { data, isLoading: isLoadingHIEEligibility, error } = useSHAEligibility(patientUuid, shaIdentificationNumber);

if (!isSHA) {
return null;
}
const isRegisteredOnSHA = Boolean(data?.coverageEndDate) && Boolean(data?.coverageStartDate);
const isNotRegisteredOnSHA = data?.active === false;

if (isLoadingHIEEligibility || isLoading) {
return <InlineLoading status="active" description={t('loading', 'Loading ...')} />;
}
const isActive = isRegisteredOnSHA
? isWithinInterval(new Date(), {
start: new Date(data?.coverageStartDate),
end: new Date(data?.coverageEndDate),
})
: false;

if (error) {
return (
<InlineNotification
aria-label="closes notification"
kind="error"
lowContrast={true}
statusIconDescription="notification"
title={t('error', 'Error')}
subtitle={t('errorRetrievingHIESubscription', 'Error retrieving HIE subscription')}
/>
);
if (!isSHA) {
return null;
}

if (shaIdentificationNumber?.length === 0) {
Expand All @@ -68,49 +59,61 @@ const SHANumberValidity: React.FC<SHANumberValidityProps> = ({ paymentMethod, pa
);
}

if (!isHIEEligible) {
if (isLoadingHIEEligibility || isLoadingPatientUuid) {
return <InlineLoading status="active" description={t('loading', 'Loading ...')} />;
}

if (error) {
return (
<InlineNotification
aria-label="closes notification"
kind="error"
lowContrast={true}
statusIconDescription="notification"
title={t('error', 'Error')}
subtitle={t('errorRetrievingHIESubscription', 'Error retrieving HIE subscription')}
/>
);
}

if (isNotRegisteredOnSHA) {
return (
<ActionableNotification
<InlineNotification
title={t('pendingHIEVerification', 'Pending HIE verification')}
subtitle={t('pendingVerificationReason', data[0].eligibility_response.message.reason)}
closeOnEscape
inline={false}
actionButtonLabel={t('verify', 'Verify')}
subtitle={data?.message}
className={styles.missingSHANumber}
onActionButtonClick={() => {
navigate({ to: `\${openmrsSpaBase}/patient/${patientUuid}/edit` });
}}
/>
);
}

return (
<Form className={styles.formContainer}>
{data?.map(({ inforce, insurer, start }, index) => {
return (
<div key={`${index}${insurer}`} className={styles.hieCard}>
<div className={Boolean(inforce) ? styles.hieCardItemActive : styles.hieCardItemInActive}>
<span className={styles.hieInsurerTitle}>{t('insurer', 'Insurer:')}</span>{' '}
<span className={styles.hieInsurerValue}>{capitalize(insurer)}</span>
{start && (
<Tooltip className={styles.tooltip} align="bottom" label={`Active from ${formatDate(new Date(start))}`}>
<button className="sb-tooltip-trigger" type="button">
<Information />
</button>
</Tooltip>
)}
</div>
<div className={Boolean(inforce) ? styles.hieCardItemActive : styles.hieCardItemInActive}>
<CheckboxCheckedFilled />
<span className={Boolean(inforce) ? styles.activeSubscription : styles.inActiveSubscription}>
{inforce ? t('active', 'Active') : t('inactive', 'Inactive')}
</span>
</div>
if (isRegisteredOnSHA) {
return (
<Form className={styles.formContainer}>
<div className={styles.hieCard}>
<div className={isActive ? styles.hieCardItemActive : styles.hieCardItemInActive}>
<span className={styles.hieInsurerTitle}>{t('insurer', 'Insurer:')}</span>{' '}
<span className={styles.hieInsurerValue}>SHA</span>
{isActive && (
<Tooltip
className={styles.tooltip}
align="bottom"
label={`Active from ${formatDate(new Date(data?.coverageStartDate))}`}>
<button className="sb-tooltip-trigger" type="button">
<Information />
</button>
</Tooltip>
)}
</div>
<div className={isActive ? styles.hieCardItemActive : styles.hieCardItemInActive}>
<CheckboxCheckedFilled />
<span className={isActive ? styles.activeSubscription : styles.inActiveSubscription}>
{isActive ? t('active', 'Active') : t('inactive', 'Inactive')}
</span>
</div>
);
})}
</Form>
);
</div>
</Form>
);
}
};

export default SHANumberValidity;
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { Controller, FormProvider, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import { z } from 'zod';
import PackageIntervensions from '../../../benefits-package/forms/package-intervensions.component';
import PackageInterventions from '../../../benefits-package/forms/package-interventions.component';
import { formatDate } from '../../../helpers/functions';
import { useSystemSetting } from '../../../hooks/getMflCode';
import usePackages from '../../../hooks/usePackages';
Expand Down Expand Up @@ -314,8 +314,9 @@ const ClaimsForm: React.FC<ClaimsFormProps> = ({ bill, selectedLineItems }) => {
{selectedPackageObservable && (
<Column>
<Layer className={styles.input}>
<PackageIntervensions
<PackageInterventions
category={packages.find((package_) => package_.uuid === selectedPackageObservable)?.packageCode ?? ''}
patientUuid={patientUuid}
/>
</Layer>
</Column>
Expand Down
Loading

0 comments on commit 4ed1321

Please sign in to comment.