Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix state & district autofill & multiple other small issues in Patient Registration page #9711

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1302,6 +1302,7 @@
"organization_forbidden": "You don't have access to any organizations yet.",
"organization_not_found": "No Organizations Found",
"organizations": "Organizations",
"organizations_fetch_error": "Error while fetching organizations",
"origin_facility": "Current facility",
"other_details": "Other details",
"otp_verification_error": "Failed to verify OTP. Please try again later.",
Expand Down Expand Up @@ -1392,6 +1393,8 @@
"phone_number_verified": "Phone Number Verified",
"pincode": "Pincode",
"pincode_autofill": "State and District auto-filled from Pincode",
"pincode_district_auto_fill_error": "Failed to auto-fill district information",
"pincode_state_auto_fill_error": "Failed to auto-fill state and district information",
"play": "Play",
"play_audio": "Play Audio",
"please_assign_bed_to_patient": "Please assign a bed to this patient",
Expand Down
59 changes: 26 additions & 33 deletions src/components/Patient/PatientRegistration.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import careConfig from "@careConfig";
import { useMutation, useQuery } from "@tanstack/react-query";
import { navigate, useQueryParams } from "raviger";
import { Fragment, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";

import CareIcon from "@/CAREUI/icons/CareIcon";
import SectionNavigator from "@/CAREUI/misc/SectionNavigator";

import { Button } from "@/components/ui/button";
Expand All @@ -27,26 +27,23 @@ import Page from "@/components/Common/Page";
import DuplicatePatientDialog from "@/components/Facility/DuplicatePatientDialog";

import useAppHistory from "@/hooks/useAppHistory";
import { useStateAndDistrictFromPincode } from "@/hooks/useStateAndDistrictFromPincode";

import {
BLOOD_GROUP_CHOICES, // DOMESTIC_HEALTHCARE_SUPPORT_CHOICES,
GENDER_TYPES, // OCCUPATION_TYPES,
//RATION_CARD_CATEGORY, // SOCIOECONOMIC_STATUS_CHOICES ,
} from "@/common/constants";
import countryList from "@/common/static/countries.json";
import { validatePincode } from "@/common/validation";

import * as Notification from "@/Utils/Notifications";
import routes from "@/Utils/request/api";
import mutate from "@/Utils/request/mutate";
import query from "@/Utils/request/query";
import {
dateQueryString,
getPincodeDetails,
parsePhoneNumber,
} from "@/Utils/utils";
import { dateQueryString, parsePhoneNumber } from "@/Utils/utils";
import OrganizationSelector from "@/pages/Organization/components/OrganizationSelector";
import { PatientModel, validatePatient } from "@/types/emr/patient";
import { Organization } from "@/types/organization/organization";

import Autocomplete from "../ui/autocomplete";
import InputWithError from "../ui/input-with-error";
Expand Down Expand Up @@ -79,6 +76,7 @@ export default function PatientRegistration(
const [suppressDuplicateWarning, setSuppressDuplicateWarning] =
useState(!!patientId);
const [debouncedNumber, setDebouncedNumber] = useState<string>();
const [selectedLevels, setSelectedLevels] = useState<Organization[]>([]);

const sidebarItems = [
{ label: t("patient__general-info"), id: "general-info" },
Expand Down Expand Up @@ -188,32 +186,24 @@ export default function PatientRegistration(
}
}, [patientQuery.data]);

const handlePincodeChange = async (value: string) => {
if (!validatePincode(value)) return;
if (form.state && form.district) return;

const pincodeDetails = await getPincodeDetails(
value,
careConfig.govDataApiKey,
);
if (!pincodeDetails) return;

const { statename: _stateName, districtname: _districtName } =
pincodeDetails;

setShowAutoFilledPincode(true);
setTimeout(() => {
setShowAutoFilledPincode(false);
}, 2000);
};
const { stateOrg, districtOrg } = useStateAndDistrictFromPincode({
pincode: form.pincode?.toString() || "",
});

useEffect(() => {
const timeout = setTimeout(
() => handlePincodeChange(form.pincode?.toString() || ""),
1000,
);
return () => clearTimeout(timeout);
}, [form.pincode]);
const levels: Organization[] = [];
if (stateOrg) levels.push(stateOrg);
if (districtOrg) levels.push(districtOrg);
setSelectedLevels(levels);

if (levels.length == 2) {
setShowAutoFilledPincode(true);
const timer = setTimeout(() => {
setShowAutoFilledPincode(false);
}, 2000);
return () => clearTimeout(timer);
}
}, [stateOrg, districtOrg]);

const title = !patientId
? t("add_details_of_patient")
Expand Down Expand Up @@ -587,6 +577,7 @@ export default function PatientRegistration(
setForm((f) => ({ ...f, permanent_address: e.target.value }))
}
disabled={sameAddress}
className={sameAddress ? "cursor-not-allowed" : ""}
/>
</InputWithError>
{/* <br />
Expand All @@ -602,7 +593,7 @@ export default function PatientRegistration(
>
<Input {...fieldProps("pincode")} type="number" />
</InputWithError>
{/* {showAutoFilledPincode && (
{_showAutoFilledPincode && (
<div>
<CareIcon
icon="l-check-circle"
Expand All @@ -612,7 +603,7 @@ export default function PatientRegistration(
{t("pincode_autofill")}
</span>
</div>
)} */}
)}
<br />
<div className="grid grid-cols-2 gap-4">
<div>
Expand All @@ -637,12 +628,14 @@ export default function PatientRegistration(
<>
<OrganizationSelector
required={true}
parentSelectedLevels={selectedLevels}
onChange={(value) =>
setForm((f) => ({
...f,
geo_organization: value,
}))
}
errorMessage={errors.geo_organization?.[0]}
/>
</>
)}
Expand Down
44 changes: 44 additions & 0 deletions src/hooks/useOrganization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useQuery } from "@tanstack/react-query";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";

import query from "@/Utils/request/query";
import { Organization } from "@/types/organization/organization";
import organizationApi from "@/types/organization/organizationApi";

interface UseOrganizationParams {
orgType?: string;
parentId?: string;
name?: string;
enabled?: boolean;
}

export function useOrganization({
orgType = "",
parentId = "",
name = "",
enabled = true,
}: UseOrganizationParams) {
const { data, isLoading, isError } = useQuery<{ results: Organization[] }>({
queryKey: ["organization", orgType, name, parentId],
queryFn: query(organizationApi.list, {
queryParams: {
org_type: orgType,
parent: parentId,
name,
},
}),
enabled: enabled && !!name,
select: (res) => ({ results: res.results }),
});

const { t } = useTranslation();

isError && toast.error(t("organizations_fetch_error"));

return {
organizations: data?.results || [],
isLoading,
isError,
};
}
72 changes: 72 additions & 0 deletions src/hooks/useStateAndDistrictFromPincode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import careConfig from "@careConfig";
import { useQuery } from "@tanstack/react-query";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";

import { validatePincode } from "@/common/validation";

import { getPincodeDetails } from "@/Utils/utils";

import { useOrganization } from "./useOrganization";

interface UseStateAndDistrictProps {
pincode: string;
}

export function useStateAndDistrictFromPincode({
pincode,
}: UseStateAndDistrictProps) {
const {
data: pincodeDetails,
isLoading: isPincodeLoading,
isError: isPincodeError,
} = useQuery({
queryKey: ["pincode-details", pincode],
queryFn: () => getPincodeDetails(pincode, careConfig.govDataApiKey),
enabled: validatePincode(pincode),
});

const { t } = useTranslation();

const stateName = pincodeDetails?.statename || "";
const districtName = pincodeDetails?.districtname || "";

const {
organizations: stateOrgs,
isLoading: isStateLoading,
isError: isStateError,
} = useOrganization({
orgType: "govt",
parentId: "",
name: stateName,
enabled: !!stateName,
});

const stateOrg = stateOrgs?.[0];

const {
organizations: districtOrgs,
isLoading: isDistrictLoading,
isError: isDistrictError,
} = useOrganization({
orgType: "govt",
parentId: stateOrg?.id,
name: districtName,
enabled: !!stateOrg?.id && !!districtName,
});

isStateError && toast.info(t("pincode_state_auto_fill_error"));

isDistrictError &&
!isStateError &&
toast.info(t("pincode_district_auto_fill_error"));

const districtOrg = districtOrgs[0];

return {
stateOrg,
districtOrg,
isLoading: isPincodeLoading || isStateLoading || isDistrictLoading,
isError: isPincodeError || isStateError || isDistrictError,
};
}
32 changes: 28 additions & 4 deletions src/pages/Organization/components/OrganizationSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useQuery } from "@tanstack/react-query";
import { t } from "i18next";
import { useState } from "react";
import { useEffect, useState } from "react";

import CareIcon from "@/CAREUI/icons/CareIcon";

Expand All @@ -19,6 +19,8 @@ interface OrganizationSelectorProps {
onChange: (value: string) => void;
required?: boolean;
authToken?: string;
parentSelectedLevels?: Organization[];
errorMessage?: string;
}

interface AutoCompleteOption {
Expand All @@ -28,8 +30,10 @@ interface AutoCompleteOption {

// TODO: Rename to GovtOrganizationSelector
export default function OrganizationSelector(props: OrganizationSelectorProps) {
const { onChange, required } = props;
const [selectedLevels, setSelectedLevels] = useState<Organization[]>([]);
const { onChange, required, parentSelectedLevels } = props;
const [selectedLevels, setSelectedLevels] = useState<Organization[]>(
parentSelectedLevels || [],
);
const [searchQuery, setSearchQuery] = useDebouncedState("", 500);

const headers = props.authToken
Expand Down Expand Up @@ -101,9 +105,27 @@ export default function OrganizationSelector(props: OrganizationSelectorProps) {
};

const handleEdit = (level: number) => {
setSelectedLevels((prev) => prev.slice(0, level));
const newLevels = selectedLevels.slice(0, level);
setSelectedLevels(newLevels);

if (!newLevels.length) {
onChange("");
} else {
const lastOrg = newLevels[newLevels.length - 1];
if (!lastOrg.has_children) {
onChange(lastOrg.id);
} else {
onChange("");
}
}
};

useEffect(() => {
if (parentSelectedLevels) {
setSelectedLevels(parentSelectedLevels);
}
}, [parentSelectedLevels]);

const lastLevel = selectedLevels[selectedLevels.length - 1];

return (
Expand Down Expand Up @@ -145,6 +167,8 @@ export default function OrganizationSelector(props: OrganizationSelectorProps) {
? `SYSTEM__govt_org_type__${lastLevel.metadata?.govt_org_children_type || "default"}`
: "SYSTEM__govt_org_type__default",
)}
required={!!required}
errors={[props.errorMessage || ""]}
>
<Autocomplete
value=""
Expand Down
Loading