diff --git a/package-lock.json b/package-lock.json
index c6ca662919b..e0d37135114 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,13 +20,13 @@
"@pnotify/core": "^5.2.0",
"@pnotify/mobile": "^5.2.0",
"@radix-ui/react-alert-dialog": "^1.1.2",
- "@radix-ui/react-checkbox": "^1.1.2",
+ "@radix-ui/react-checkbox": "^1.1.3",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.2",
- "@radix-ui/react-radio-group": "^1.2.1",
+ "@radix-ui/react-radio-group": "^1.2.2",
"@radix-ui/react-scroll-area": "^1.2.2",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-separator": "^1.1.0",
diff --git a/package.json b/package.json
index 72cfe7eddd9..7b783c4f778 100644
--- a/package.json
+++ b/package.json
@@ -58,13 +58,13 @@
"@pnotify/core": "^5.2.0",
"@pnotify/mobile": "^5.2.0",
"@radix-ui/react-alert-dialog": "^1.1.2",
- "@radix-ui/react-checkbox": "^1.1.2",
+ "@radix-ui/react-checkbox": "^1.1.3",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.2",
- "@radix-ui/react-radio-group": "^1.2.1",
+ "@radix-ui/react-radio-group": "^1.2.2",
"@radix-ui/react-scroll-area": "^1.2.2",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-separator": "^1.1.0",
diff --git a/public/locale/en.json b/public/locale/en.json
index 8b7aa656e0a..c3a95aeee5d 100644
--- a/public/locale/en.json
+++ b/public/locale/en.json
@@ -337,8 +337,11 @@
"admitted_on": "Admitted On",
"advanced_filters": "Advanced Filters",
"age": "Age",
+ "age_input_warning": "While entering a patient's age is an option, please note that only the year of birth will be captured from this information.",
+ "age_input_warning_bold": "Recommended only when the patient's date of birth is unknown",
"all_changes_have_been_saved": "All changes have been saved",
"all_details": "All Details",
+ "all_patients": "All Patients",
"allergies": "Allergies",
"allow_transfer": "Allow Transfer",
"allowed_formats_are": "Allowed formats are",
@@ -629,6 +632,7 @@
"date_of_result": "Covid confirmation date",
"date_of_return": "Date of Return",
"date_of_test": "Date of sample collection for Covid testing",
+ "day": "Day",
"days": "Days",
"death_report": "Death Report",
"delete": "Delete",
@@ -679,6 +683,7 @@
"disease_status": "Disease status",
"district": "District",
"district_program_management_supporting_unit": "District Program Management Supporting Unit",
+ "dob": "DOB",
"dob_format": "Please enter date in DD/MM/YYYY format",
"doc_will_visit_patient": "will visit the patient at the scheduled time.",
"doctor_experience_error": "Please enter a valid number between 0 and 100.",
@@ -731,8 +736,10 @@
"emergency_contact": "Emergency Contact",
"emergency_contact_number": "Emergency Contact Number",
"emergency_contact_person_name": "Emergency Contact Person Name",
+ "emergency_contact_person_name_details": "Emergency contact person (Father, Mother, Spouse, Sibling, Friend)",
"emergency_contact_person_name_volunteer": "Emergency Contact Person Name (Volunteer)",
"emergency_contact_volunteer": "Emergency Contact (Volunteer)",
+ "emergency_phone_number": "Emergency Phone Number",
"empty_date_time": "--:-- --; --/--/----",
"encounter_date_field_label__A": "Date & Time of Admission to the Facility",
"encounter_date_field_label__DC": "Date & Time of Domiciliary Care commencement",
@@ -835,6 +842,7 @@
"full_name": "Full Name",
"full_screen": "Full Screen",
"gender": "Gender",
+ "general_info_detail": "Provide the patient's personal details, including name, date of birth, gender, and contact information for accurate identification and communication.",
"generate_link_abha": "Generate/Link ABHA Number",
"generate_report": "Generate Report",
"generated_summary_caution": "This is a computer generated summary using the information captured in the CARE system.",
@@ -901,9 +909,11 @@
"insurance__insurer_name": "Insurer Name",
"insurance__member_id": "Member ID",
"insurance__policy_name": "Policy ID / Policy Name",
+ "insurance_details_detail": "Include details of all the Insurance Policies held by the Patient for smooth insurance processing",
"insurer_name_required": "Insurer Name is required",
"international_mobile": "International Mobile",
"invalid_asset_id_msg": "Oops! The asset ID you entered does not appear to be valid.",
+ "invalid_date_format": "Invalid date format, expected {{format}}",
"invalid_email": "Please enter a valid email address",
"invalid_ip_address": "Invalid IP Address",
"invalid_link_msg": "It appears that the password reset link you have used is either invalid or expired. Please request a new password reset link.",
@@ -954,6 +964,7 @@
"lab_tests": "Lab Tests",
"label": "Label",
"landline": "Indian landline",
+ "landmark": "Landmark",
"language_selection": "Language Selection",
"languages": "Languages",
"last_administered": "Last administered",
@@ -1048,6 +1059,7 @@
"modified_date": "Modified Date",
"modified_on": "Modified On",
"monitor": "Monitor",
+ "month": "Month",
"more_details": "More details",
"more_info": "More Info",
"morning_slots": "Morning Slots",
@@ -1081,6 +1093,7 @@
"no_facilities": "No Facilities found",
"no_files_found": "No {{type}} files found",
"no_home_facility": "No home facility",
+ "no_home_facility_found": "No home facility found",
"no_image_found": "No image found",
"no_investigation": "No investigation Reports found",
"no_investigation_suggestions": "No Investigation Suggestions",
@@ -1195,6 +1208,8 @@
"patient_phone_number": "Patient Phone Number",
"patient_profile": "Patient Profile",
"patient_profile_created_by": "Patient profile created by",
+ "patient_records_found": "Patient Records Found",
+ "patient_records_found_description": "It appears that there are patient records that contain the same phone number as the one you just entered. ",
"patient_registration": "Patient Registration",
"patient_registration__address": "Address",
"patient_registration__age": "Age",
@@ -1204,9 +1219,13 @@
"patient_registration__contact": "Emergency Contact",
"patient_registration__gender": "Gender",
"patient_registration__name": "Name",
+ "patient_registration_error": "Could not register patient",
+ "patient_registration_success": "Patient Registered Successfully",
"patient_state": "Patient State",
"patient_status": "Patient Status",
"patient_transfer_birth_match_note": "Note: Year of birth must match the patient to process the transfer request.",
+ "patient_update_error": "Could not update patient",
+ "patient_update_success": "Patient Updated Sucessfully",
"patients": "Patients",
"pending": "Pending",
"permanent_address": "Permanent Address",
@@ -1219,7 +1238,9 @@
"phone_no": "Phone no.",
"phone_number": "Phone Number",
"phone_number_at_current_facility": "Phone Number of Contact person at current Facility",
+ "phone_number_min_error": "Phone number must be at least 10 characters long",
"pincode": "Pincode",
+ "pincode_autofill": "State and District auto-filled from Pincode",
"please_assign_bed_to_patient": "Please assign a bed to this patient",
"please_check_your_messages": "Please check your messages",
"please_confirm_password": "Please confirm your new password.",
@@ -1227,6 +1248,7 @@
"please_enter_current_password": "Please enter your current password.",
"please_enter_new_password": "Please enter your new password.",
"please_enter_username": "Please enter the username",
+ "please_fix_errors": "Please fix the errors in the highlighted fields and try submitting again.",
"please_select_a_facility": "Please select a facility",
"please_select_breathlessness_level": "Please select Breathlessness Level",
"please_select_district": "Please select the district",
@@ -1274,6 +1296,7 @@
"preset_updated": "Preset updated",
"prev_sessions": "Prev Sessions",
"primary_ph_no": "Primary Ph No.",
+ "primary_phone_no": "Primary ph. no.",
"principal": "Principal",
"principal_diagnosis": "Principal diagnosis",
"print": "Print",
@@ -1394,6 +1417,7 @@
"search_icd11_placeholder": "Search for ICD-11 Diagnoses",
"search_investigation_placeholder": "Search Investigation & Groups",
"search_patient": "Search Patient",
+ "search_patients": "Search Patients",
"search_resource": "Search Resource",
"see_attachments": "See Attachments",
"select": "Select",
@@ -1452,6 +1476,7 @@
"skill_added_successfully": "Skill added successfully",
"skills": "Skills",
"social_profile": "Social Profile",
+ "social_profile_detail": "Include occupation, ration card category, socioeconomic status, and
domestic healthcare support for a complete profile.",
"socioeconomic_status": "Socioeconomic status",
"software_update": "Software Update",
"something_went_wrong": "Something went wrong..!",
@@ -1498,6 +1523,7 @@
"time_slot": "Time Slot",
"titrate_dosage": "Titrate Dosage",
"to_be_conducted": "To be conducted",
+ "to_proceed_with_registration": "To proceed with registration, please create a new patient.",
"total_amount": "Total Amount",
"total_beds": "Total Beds",
"total_patients": "Total Patients",
@@ -1523,6 +1549,7 @@
"type_b_cylinders": "B Type Cylinders",
"type_c_cylinders": "C Type Cylinders",
"type_d_cylinders": "D Type Cylinders",
+ "type_patient_name": "Type Patient Name",
"type_to_search": "Type to search",
"type_your_comment": "Type your comment",
"type_your_reason_here": "Type your reason here",
@@ -1582,11 +1609,14 @@
"upload_headings__supporting_info": "Upload Supporting Info",
"upload_report": "Upload Report",
"uploading": "Uploading",
+ "use_address_as_permanent": "Use this address for permanent address",
"use_existing_abha_address": "Use Existing ABHA Address",
+ "use_phone_number_for_emergency": "Use this phone number for emergency contact",
"user_add_error": "Error while adding User",
"user_added_successfully": "User added successfully",
"user_delete_error": "Error while deleting User",
"user_deleted_successfully": "User Deleted Successfully",
+ "user_deleted_successfuly": "User Deleted Successfully",
"user_details": "User Details",
"user_details_update_error": "Error while updating user details",
"user_details_update_success": "User details updated successfully",
@@ -1646,6 +1676,7 @@
"voice_autofill": "Voice Autofill",
"volunteer_assigned": "Volunteer assigned successfully",
"volunteer_contact": "Volunteer Contact",
+ "volunteer_contact_detail": "Provide the name and contact details of a volunteer who can assist
the patient in emergencies. This should be someone outside the family.",
"volunteer_unassigned": "Volunteer unassigned successfully",
"volunteer_update": "Volunteer updated successfully",
"waitlist": "Waitlist",
@@ -1659,6 +1690,7 @@
"width": "Width ({{unit}})",
"with": "with",
"working_status": "Working Status",
+ "year": "Year",
"year_of_birth": "Year of Birth",
"years": "years",
"years_of_experience": "Years of Experience",
diff --git a/src/CAREUI/interactive/FiltersSlideover.tsx b/src/CAREUI/interactive/FiltersSlideover.tsx
index 959fd0621e8..6e4feeaf852 100644
--- a/src/CAREUI/interactive/FiltersSlideover.tsx
+++ b/src/CAREUI/interactive/FiltersSlideover.tsx
@@ -4,6 +4,8 @@ import { useTranslation } from "react-i18next";
import CareIcon from "@/CAREUI/icons/CareIcon";
import SlideOver from "@/CAREUI/interactive/SlideOver";
+import { Button } from "@/components/ui/button";
+
import ButtonV2 from "@/components/Common/ButtonV2";
import useFilters from "@/hooks/useFilters";
@@ -31,7 +33,7 @@ export default function FiltersSlideover({
setOpen={advancedFilter.setShow}
title={
-
{t("advanced_filters")}
+
{t("filters")}
void }) => {
const { t } = useTranslation();
return (
-
- {t("advanced_filters")}
-
+ {t("filters")}
+
);
};
diff --git a/src/CAREUI/misc/SectionNavigator.tsx b/src/CAREUI/misc/SectionNavigator.tsx
new file mode 100644
index 00000000000..a10ba799a2d
--- /dev/null
+++ b/src/CAREUI/misc/SectionNavigator.tsx
@@ -0,0 +1,79 @@
+import { useEffect, useState } from "react";
+
+import { cn } from "@/lib/utils";
+
+import { Button } from "@/components/ui/button";
+
+export default function SectionNavigator(props: {
+ sections: { label: string; id: string }[];
+ className?: string;
+}) {
+ const { sections, className } = props;
+
+ const [activeSection, setActiveSection] = useState(null);
+
+ useEffect(() => {
+ const updateActiveSection = () => {
+ sections.forEach((section) => {
+ const element = document.getElementById(section.id);
+ if (element) {
+ const rect = element.getBoundingClientRect();
+ if (rect.top >= 0 && rect.bottom <= window.innerHeight) {
+ setActiveSection(section.id);
+ }
+ }
+ });
+ };
+
+ const observer = new IntersectionObserver(
+ (entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) {
+ setActiveSection(entry.target.id);
+ }
+ });
+ },
+ { rootMargin: "0px 0px -80% 0px", threshold: 0.1 },
+ );
+
+ sections.forEach((section) => {
+ const element = document.getElementById(section.id);
+ if (element) {
+ observer.observe(element);
+ }
+ });
+
+ updateActiveSection(); // Update on page load
+
+ return () => {
+ sections.forEach((section) => {
+ const element = document.getElementById(section.id);
+ if (element) {
+ observer.unobserve(element);
+ }
+ });
+ };
+ }, [sections]);
+
+ return (
+
+ {sections.map((section) => (
+ {
+ const element = document.getElementById(section.id);
+ if (element) {
+ element.scrollIntoView({ behavior: "smooth" });
+ }
+ }}
+ >
+ {section.label}
+
+ ))}
+
+ );
+}
diff --git a/src/Routers/routes/PatientRoutes.tsx b/src/Routers/routes/PatientRoutes.tsx
index 31d6d99a037..1f1270a02cd 100644
--- a/src/Routers/routes/PatientRoutes.tsx
+++ b/src/Routers/routes/PatientRoutes.tsx
@@ -2,22 +2,24 @@ import DeathReport from "@/components/DeathReport/DeathReport";
import InvestigationReports from "@/components/Facility/Investigations/Reports";
import FileUploadPage from "@/components/Patient/FileUploadPage";
import { InsuranceDetails } from "@/components/Patient/InsuranceDetails";
-import { PatientManager } from "@/components/Patient/ManagePatients";
import { patientTabs } from "@/components/Patient/PatientDetailsTab";
import { PatientHome } from "@/components/Patient/PatientHome";
+import PatientIndex from "@/components/Patient/PatientIndex";
import PatientNotes from "@/components/Patient/PatientNotes";
-import { PatientRegister } from "@/components/Patient/PatientRegister";
+import PatientRegistration from "@/components/Patient/PatientRegistration";
import { AppRoutes } from "@/Routers/AppRouter";
const PatientRoutes: AppRoutes = {
- "/patients": () => ,
+ "/patients": () => ,
+ "/patients/live": () => ,
+ "/patients/discharged": () => ,
"/patient/:id": ({ id }) => ,
"/patient/:id/investigation_reports": ({ id }) => (
),
- "/facility/:facilityId/patient": ({ facilityId }) => (
-
+ "/facility/:facilityId/register-patient": ({ facilityId }) => (
+
),
"/facility/:facilityId/patient/:id": ({ facilityId, id }) => (
@@ -33,7 +35,7 @@ const PatientRoutes: AppRoutes = {
),
"/facility/:facilityId/patient/:id/update": ({ facilityId, id }) => (
-
+
),
"/facility/:facilityId/patient/:patientId/notes": ({
facilityId,
diff --git a/src/Utils/utils.ts b/src/Utils/utils.ts
index 2868c2c7d91..6c4124b7f02 100644
--- a/src/Utils/utils.ts
+++ b/src/Utils/utils.ts
@@ -240,6 +240,7 @@ export const parsePhoneNumber = (phoneNumber: string, countryCode?: string) => {
if (phoneNumber === "+91") return "";
const phoneCodes: Record = phoneCodesJson;
let parsedNumber = phoneNumber.replace(/[-+() ]/g, "");
+ if (parsedNumber.length < 12) return "";
if (countryCode && phoneCodes[countryCode]) {
parsedNumber = phoneCodes[countryCode].code + parsedNumber;
} else if (!phoneNumber.startsWith("+")) {
diff --git a/src/components/Common/Breadcrumbs.tsx b/src/components/Common/Breadcrumbs.tsx
index c2c4aa57446..4c602d9f9cc 100644
--- a/src/components/Common/Breadcrumbs.tsx
+++ b/src/components/Common/Breadcrumbs.tsx
@@ -109,7 +109,7 @@ export default function Breadcrumbs({
Home
diff --git a/src/components/Common/SearchByMultipleFields.tsx b/src/components/Common/SearchByMultipleFields.tsx
index 5302bc0838d..d752b94647e 100644
--- a/src/components/Common/SearchByMultipleFields.tsx
+++ b/src/components/Common/SearchByMultipleFields.tsx
@@ -30,7 +30,6 @@ import PhoneNumberFormField from "@/components/Form/FormFields/PhoneNumberFormFi
interface SearchOption {
key: string;
- label: string;
type: "text" | "phone";
placeholder: string;
value: string;
@@ -243,7 +242,7 @@ const SearchByMultipleFields: React.FC = ({
{t(option.key)}
- {option.label.charAt(0).toUpperCase()}
+ {option.shortcutKey}
))}
@@ -252,7 +251,7 @@ const SearchByMultipleFields: React.FC = ({
- {renderSearchInput}
+ {renderSearchInput}
{error && (
diff --git a/src/components/Common/SortDropdown.tsx b/src/components/Common/SortDropdown.tsx
index 2e5ccd95015..d1d753a70a1 100644
--- a/src/components/Common/SortDropdown.tsx
+++ b/src/components/Common/SortDropdown.tsx
@@ -2,7 +2,13 @@ import { useTranslation } from "react-i18next";
import CareIcon from "@/CAREUI/icons/CareIcon";
-import DropdownMenu, { DropdownItem } from "@/components/Common/Menu";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
export interface SortOption {
isAscending: boolean;
@@ -22,31 +28,31 @@ interface Props {
export default function SortDropdownMenu(props: Props) {
const { t } = useTranslation();
return (
-
}
- containerClassName="w-full md:w-auto z-20"
+
props.onSelect({ ordering: v })}
>
- {props.options.map(({ isAscending, value }, i) => (
- props.onSelect({ ordering: value })}
- icon={
+
+ {props.selected ? (
+
+ ) : (
+
+
+ {t("sort_by")}
+
+ )}
+
+
+ {props.options.map(({ isAscending, value }, i) => (
+
- }
- >
- {t("SORT_OPTIONS__" + value)}
-
- ))}
-
+ {t("SORT_OPTIONS__" + value)}
+
+ ))}
+
+
);
}
diff --git a/src/components/ExternalResult/FacilitiesSelectDialogue.tsx b/src/components/ExternalResult/FacilitiesSelectDialogue.tsx
index 621fe834bc7..d925fedd9fa 100644
--- a/src/components/ExternalResult/FacilitiesSelectDialogue.tsx
+++ b/src/components/ExternalResult/FacilitiesSelectDialogue.tsx
@@ -1,6 +1,9 @@
import { useTranslation } from "react-i18next";
-import { Cancel, Submit } from "@/components/Common/ButtonV2";
+import CareIcon from "@/CAREUI/icons/CareIcon";
+
+import { Button } from "@/components/ui/button";
+
import DialogModal from "@/components/Common/Dialog";
import { FacilitySelect } from "@/components/Common/FacilitySelect";
import { FacilityModel } from "@/components/Facility/models";
@@ -45,13 +48,19 @@ const FacilitiesSelectDialog = (props: Props) => {
}
/>
-
-
+
+ {t("cancel")}
+
+
+ disabled={!selectedFacility?.id}
+ >
+
+ {t("select")}
+
);
diff --git a/src/components/Facility/DuplicatePatientDialog.tsx b/src/components/Facility/DuplicatePatientDialog.tsx
index 241750cec6b..111571c20ad 100644
--- a/src/components/Facility/DuplicatePatientDialog.tsx
+++ b/src/components/Facility/DuplicatePatientDialog.tsx
@@ -1,18 +1,27 @@
import { useState } from "react";
import { useTranslation } from "react-i18next";
-import { Cancel, Submit } from "@/components/Common/ButtonV2";
+import CareIcon from "@/CAREUI/icons/CareIcon";
+
import DialogModal from "@/components/Common/Dialog";
import { DupPatientModel } from "@/components/Facility/models";
+import { Button } from "../ui/button";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "../ui/table";
+
interface Props {
patientList: Array
;
handleOk: (action: string) => void;
handleCancel: () => void;
}
-const tdClass = "border border-secondary-400 p-2 text-left";
-
const DuplicatePatientDialog = (props: Props) => {
const { t } = useTranslation();
const { patientList, handleOk, handleCancel } = props;
@@ -20,7 +29,7 @@ const DuplicatePatientDialog = (props: Props) => {
return (
{
- It appears that there are patient records that contain the same
- phone number as the one you just entered. (
+ {t("patient_records_found_description")}(
{patientList[0].phone_number} )
-
-
-
-
- {["Patient Name and ID", "Gender"].map((heading, i) => (
-
- {heading}
-
- ))}
-
-
-
+
+
+
+
+ {[`${t("patient_name")} / ID`, t("gender")].map(
+ (heading, i) => (
+ {heading}
+ ),
+ )}
+
+
+
{patientList.map((patient, i) => {
return (
-
-
+
+
{patient.name}
ID : {patient.patient_id}
-
- {patient.gender}
-
+
+ {patient.gender}
+
);
})}
-
-
+
+
@@ -107,17 +112,18 @@ const DuplicatePatientDialog = (props: Props) => {
-
-
+
+ {t("close")}
+
+ handleOk(action)}
disabled={!action}
- label="Continue"
- />
+ variant={"primary"}
+ >
+
+ {t("continue")}
+
);
diff --git a/src/components/Facility/FacilityHome.tsx b/src/components/Facility/FacilityHome.tsx
index 7881a43123d..c5e36f744b8 100644
--- a/src/components/Facility/FacilityHome.tsx
+++ b/src/components/Facility/FacilityHome.tsx
@@ -41,7 +41,36 @@ import useTanStackQueryInstead from "@/Utils/request/useQuery";
import { getAuthorizationHeader } from "@/Utils/request/utils";
import { sleep } from "@/Utils/utils";
-import { patientRegisterAuth } from "../Patient/PatientRegister";
+import { UserModel } from "../Users/models";
+import { FacilityModel } from "./models";
+
+export function patientRegisterAuth(
+ authUser: UserModel,
+ facilityObject: FacilityModel | undefined,
+ facilityId: string,
+) {
+ const showAllFacilityUsers = ["DistrictAdmin", "StateAdmin"];
+ if (
+ !showAllFacilityUsers.includes(authUser.user_type) &&
+ authUser.home_facility_object?.id === facilityId
+ ) {
+ return true;
+ }
+ if (
+ authUser.user_type === "DistrictAdmin" &&
+ authUser.district === facilityObject?.district
+ ) {
+ return true;
+ }
+ if (
+ authUser.user_type === "StateAdmin" &&
+ authUser.state === facilityObject?.state
+ ) {
+ return true;
+ }
+
+ return false;
+}
type Props = {
facilityId: string;
diff --git a/src/components/Facility/FacilityList.tsx b/src/components/Facility/FacilityList.tsx
index f62f630dcaa..f2c2801e24e 100644
--- a/src/components/Facility/FacilityList.tsx
+++ b/src/components/Facility/FacilityList.tsx
@@ -184,7 +184,6 @@ export const FacilityList = () => {
options={[
{
key: "facility_district_name",
- label: "Facility or District Name",
type: "text" as const,
placeholder: "facility_search_placeholder",
value: qParams.search || "",
diff --git a/src/components/Patient/ManagePatients.tsx b/src/components/Patient/ManagePatients.tsx
index 4862d4fc3e2..aa9e7fde839 100644
--- a/src/components/Patient/ManagePatients.tsx
+++ b/src/components/Patient/ManagePatients.tsx
@@ -4,13 +4,10 @@ import { ReactNode, useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Avatar } from "@/components/Common/Avatar";
-import ButtonV2 from "@/components/Common/ButtonV2";
import { ExportMenu } from "@/components/Common/Export";
import Loading from "@/components/Common/Loading";
-import Page from "@/components/Common/Page";
import SearchByMultipleFields from "@/components/Common/SearchByMultipleFields";
import SortDropdownMenu from "@/components/Common/SortDropdown";
-import Tabs from "@/components/Common/Tabs";
import useAuthUser from "@/hooks/useAuthUser";
import useFilters from "@/hooks/useFilters";
@@ -50,6 +47,7 @@ import { getDiagnosesByIds } from "../Diagnosis/utils";
import FacilitiesSelectDialogue from "../ExternalResult/FacilitiesSelectDialogue";
import DoctorVideoSlideover from "../Facility/DoctorVideoSlideover";
import { FacilityModel, PatientCategory } from "../Facility/models";
+import { Button } from "../ui/button";
import {
DIAGNOSES_FILTER_LABELS,
DiagnosesFilterKey,
@@ -343,13 +341,6 @@ export const PatientManager = () => {
},
);
- const { data: permittedFacilities } = useTanStackQueryInstead(
- routes.getPermittedFacilities,
- {
- query: { limit: 1 },
- },
- );
-
const LastAdmittedToTypeBadges = () => {
const badge = (key: string, value: string | undefined, id: string) => {
return (
@@ -741,9 +732,6 @@ export const PatientManager = () => {
);
}
- const onlyAccessibleFacility =
- permittedFacilities?.count === 1 ? permittedFacilities.results[0] : null;
-
const searchOptions = [
{
key: "name",
@@ -804,169 +792,86 @@ export const PatientManager = () => {
);
return (
-
-
-
+
+
+ {!!params.facility && (
+
{
- const showAllFacilityUsers = ["DistrictAdmin", "StateAdmin"];
- if (
- qParams.facility &&
- showAllFacilityUsers.includes(authUser.user_type)
- )
- navigate(`/facility/${qParams.facility}/patient`);
- else if (
- qParams.facility &&
- !showAllFacilityUsers.includes(authUser.user_type) &&
- authUser.home_facility_object?.id !== qParams.facility
- )
- Notification.Error({
- msg: "Oops! Non-Home facility users don't have permission to perform this action.",
- });
- else if (
- !showAllFacilityUsers.includes(authUser.user_type) &&
- authUser.home_facility_object?.id
- ) {
- navigate(
- `/facility/${authUser.home_facility_object.id}/patient`,
- );
- } else if (onlyAccessibleFacility)
- navigate(`/facility/${onlyAccessibleFacility.id}/patient`);
- else if (
- !showAllFacilityUsers.includes(authUser.user_type) &&
- !authUser.home_facility_object?.id
- )
- Notification.Error({
- msg: "Oops! No home facility found",
- });
- else setShowDialog("create");
+ triggerGoal("Doctor Connect Clicked", {
+ facilityId: qParams.facility,
+ userId: authUser.id,
+ page: "FacilityPatientsList",
+ });
+ setShowDoctors(true);
}}
- className="w-full lg:w-fit"
>
-
-
- Add Patient
-
-
-
-
-
{
- if (tab === 0) {
- updateQuery({ is_active: "True" });
- } else {
- const id = qParams.facility || onlyAccessibleFacility?.id;
- if (id) {
- navigate(`facility/${id}/discharged-patients`);
- return;
- }
+
+ Doctor Connect
+
+ )}
- if (
- authUser.user_type === "StateAdmin" ||
- authUser.user_type === "StateReadOnlyAdmin"
- ) {
- updateQuery({ is_active: "False" });
- return;
- }
-
- Notification.Warn({
- msg: t("select_facility_for_discharged_patients_warning"),
- });
- setShowDialog("list-discharged");
- }
- }}
- currentTab={tabValue}
- />
- {!!params.facility && (
- advancedFilter.setShow(true)} />
+
+
+ {!isExportAllowed ? (
+
{
- triggerGoal("Doctor Connect Clicked", {
- facilityId: qParams.facility,
- userId: authUser.id,
- page: "FacilityPatientsList",
- });
- setShowDoctors(true);
+ advancedFilter.setShow(true);
+ setTimeout(() => {
+ const element = document.getElementById("bed-type-select");
+ if (element) element.scrollIntoView({ behavior: "smooth" });
+ Notification.Warn({
+ msg: "Please select a seven day period.",
+ });
+ }, 500);
}}
+ className="gap-2"
>
-
- Doctor Connect
-
- )}
-
- advancedFilter.setShow(true)}
- />
-
-
- {!isExportAllowed ? (
- {
- advancedFilter.setShow(true);
- setTimeout(() => {
- const element =
- document.getElementById("bed-type-select");
- if (element)
- element.scrollIntoView({ behavior: "smooth" });
- Notification.Warn({
- msg: "Please select a seven day period.",
+
+ Export
+
+ ) : (
+ {
+ const query = {
+ ...params,
+ csv: true,
+ facility: qParams.facility,
+ };
+ delete qParams.is_active;
+ const { data } = await request(routes.patientList, {
+ query,
});
- }, 500);
- }}
- className="mr-5 w-full lg:w-fit"
- >
-
- Export
-
- ) : (
- {
- const query = {
- ...params,
- csv: true,
- facility: qParams.facility,
- };
- delete qParams.is_active;
- const { data } = await request(routes.patientList, {
- query,
- });
- return data ?? null;
- },
- parse: preventDuplicatePatientsDuetoPolicyId,
+ return data ?? null;
},
- ]}
- />
- )}
+ parse: preventDuplicatePatientsDuetoPolicyId,
+ },
+ ]}
+ />
+ )}
- {!isExportAllowed && (
-
- Select a seven day period
-
- )}
-
+ {!isExportAllowed && (
+
+ Select a seven day period
+
+ )}
- }
- >
+
+
setSelectedFacility(e)}
@@ -1009,7 +914,7 @@ export const PatientManager = () => {
className="w-full"
/>
-
+
{
setShow={setShowDoctors}
/>
-
+ >
);
};
diff --git a/src/components/Patient/PatientFilter.tsx b/src/components/Patient/PatientFilter.tsx
index e2ab6cd30b4..9db5f34209e 100644
--- a/src/components/Patient/PatientFilter.tsx
+++ b/src/components/Patient/PatientFilter.tsx
@@ -1,7 +1,9 @@
import careConfig from "@careConfig";
import dayjs from "dayjs";
+import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
+import FilterBadge from "@/CAREUI/display/FilterBadge";
import CareIcon from "@/CAREUI/icons/CareIcon";
import FiltersSlideover from "@/CAREUI/interactive/FiltersSlideover";
@@ -22,10 +24,13 @@ import {
import MultiSelectMenuV2 from "@/components/Form/MultiSelectMenuV2";
import SelectMenuV2 from "@/components/Form/SelectMenuV2";
import DiagnosesFilter, {
+ DIAGNOSES_FILTER_LABELS,
+ DiagnosesFilterKey,
FILTER_BY_DIAGNOSES_KEYS,
} from "@/components/Patient/DiagnosesFilter";
import useAuthUser from "@/hooks/useAuthUser";
+import useFilters from "@/hooks/useFilters";
import useMergeState from "@/hooks/useMergeState";
import {
@@ -34,14 +39,19 @@ import {
DISCHARGE_REASONS,
FACILITY_TYPES,
GENDER_TYPES,
+ PATIENT_CATEGORIES,
PATIENT_FILTER_CATEGORIES,
RATION_CARD_CATEGORY,
} from "@/common/constants";
+import { parseOptionId } from "@/common/utils";
import routes from "@/Utils/request/api";
import request from "@/Utils/request/request";
import useTanStackQueryInstead from "@/Utils/request/useQuery";
-import { dateQueryString } from "@/Utils/utils";
+import { dateQueryString, humanizeStrings } from "@/Utils/utils";
+
+import { ICD11DiagnosisModel } from "../Diagnosis/types";
+import { getDiagnosesByIds } from "../Diagnosis/utils";
const getDate = (value: any) =>
value && dayjs(value).isValid() && dayjs(value).toDate();
@@ -782,3 +792,266 @@ export default function PatientFilter(props: any) {
);
}
+
+export function PatientFilterBadges() {
+ const { t } = useTranslation();
+
+ const { qParams, FilterBadges, updateQuery } = useFilters({
+ limit: 12,
+ cacheBlacklist: [
+ "name",
+ "patient_no",
+ "phone_number",
+ "emergency_phone_number",
+ ],
+ });
+
+ const [diagnoses, setDiagnoses] = useState
([]);
+
+ const { data: districtData } = useTanStackQueryInstead(routes.getDistrict, {
+ pathParams: {
+ id: qParams.district,
+ },
+ prefetch: !!Number(qParams.district),
+ });
+
+ const { data: LocalBodyData } = useTanStackQueryInstead(routes.getLocalBody, {
+ pathParams: {
+ id: qParams.lsgBody,
+ },
+ prefetch: !!Number(qParams.lsgBody),
+ });
+
+ const { data: facilityData } = useTanStackQueryInstead(
+ routes.getAnyFacility,
+ {
+ pathParams: {
+ id: qParams.facility,
+ },
+ prefetch: !!qParams.facility,
+ },
+ );
+ const { data: facilityAssetLocationData } = useTanStackQueryInstead(
+ routes.getFacilityAssetLocation,
+ {
+ pathParams: {
+ facility_external_id: qParams.facility,
+ external_id: qParams.last_consultation_current_bed__location,
+ },
+ prefetch: !!qParams.last_consultation_current_bed__location,
+ },
+ );
+
+ const LastAdmittedToTypeBadges = () => {
+ const badge = (key: string, value: string | undefined, id: string) => {
+ return (
+ value && (
+ {
+ const lcat = qParams.last_consultation_admitted_bed_type_list
+ .split(",")
+ .filter((x: string) => x != id)
+ .join(",");
+ updateQuery({
+ ...qParams,
+ last_consultation_admitted_bed_type_list: lcat,
+ });
+ }}
+ />
+ )
+ );
+ };
+ return qParams.last_consultation_admitted_bed_type_list
+ .split(",")
+ .map((id: string) => {
+ const text = ADMITTED_TO.find((obj) => obj.id == id)?.text;
+ return badge("Bed Type", text, id);
+ });
+ };
+
+ const HasConsentTypesBadges = () => {
+ const badge = (key: string, value: string | undefined, id: string) => {
+ return (
+ value && (
+ {
+ const lcat = qParams.last_consultation__consent_types
+ .split(",")
+ .filter((x: string) => x != id)
+ .join(",");
+ updateQuery({
+ ...qParams,
+ last_consultation__consent_types: lcat,
+ });
+ }}
+ />
+ )
+ );
+ };
+
+ return qParams.last_consultation__consent_types
+ .split(",")
+ .map((id: string) => {
+ const text = [
+ ...CONSENT_TYPE_CHOICES,
+ { id: "None", text: "No Consents" },
+ ].find((obj) => obj.id == id)?.text;
+ return badge("Has Consent", text, id);
+ });
+ };
+
+ const getTheCategoryFromId = () => {
+ let category_name;
+ if (qParams.category) {
+ category_name = PATIENT_CATEGORIES.find(
+ (item: any) => qParams.category === item.id,
+ )?.text;
+
+ return String(category_name);
+ } else {
+ return "";
+ }
+ };
+
+ const getDiagnosisFilterValue = (key: DiagnosesFilterKey) => {
+ const ids: string[] = (qParams[key] ?? "").split(",");
+ return ids.map((id) => diagnoses.find((obj) => obj.id == id)?.label ?? id);
+ };
+
+ useEffect(() => {
+ const ids: string[] = [];
+ FILTER_BY_DIAGNOSES_KEYS.forEach((key) => {
+ ids.push(...(qParams[key] ?? "").split(",").filter(Boolean));
+ });
+ const existing = diagnoses.filter(({ id }) => ids.includes(id));
+ const objIds = existing.map((o) => o.id);
+ const diagnosesToBeFetched = ids.filter((id) => !objIds.includes(id));
+ getDiagnosesByIds(diagnosesToBeFetched).then((data) => {
+ const retrieved = data.filter(Boolean) as ICD11DiagnosisModel[];
+ setDiagnoses([...existing, ...retrieved]);
+ });
+ }, [
+ qParams.diagnoses,
+ qParams.diagnoses_confirmed,
+ qParams.diagnoses_provisional,
+ qParams.diagnoses_unconfirmed,
+ qParams.diagnoses_differential,
+ ]);
+
+ return (
+ [
+ phoneNumber("Primary number", "phone_number"),
+ phoneNumber("Emergency number", "emergency_phone_number"),
+ badge("Patient name", "name"),
+ badge("IP/OP number", "patient_no"),
+ ...dateRange("Modified", "modified_date"),
+ ...dateRange("Created", "created_date"),
+ ...dateRange("Admitted", "last_consultation_encounter_date"),
+ ...dateRange("Discharged", "last_consultation_discharge_date"),
+ // Admitted to type badges
+ badge("No. of vaccination doses", "number_of_doses"),
+ kasp(),
+ badge("COWIN ID", "covin_id"),
+ badge("Is Antenatal", "is_antenatal"),
+ badge("Review Missed", "review_missed"),
+ badge("Is Medico-Legal Case", "last_consultation_medico_legal_case"),
+ value(
+ "Ration Card Category",
+ "ration_card_category",
+ qParams.ration_card_category
+ ? t(`ration_card__${qParams.ration_card_category}`)
+ : "",
+ ),
+ value(
+ "Facility",
+ "facility",
+ qParams.facility ? facilityData?.name || "" : "",
+ ),
+ value(
+ "Location",
+ "last_consultation_current_bed__location",
+ qParams.last_consultation_current_bed__location
+ ? facilityAssetLocationData?.name ||
+ qParams.last_consultation_current_bed__locations
+ : "",
+ ),
+ badge("Facility Type", "facility_type"),
+ value(
+ "District",
+ "district",
+ qParams.district ? districtData?.name || "" : "",
+ ),
+ ordering(),
+ value("Category", "category", getTheCategoryFromId()),
+ value(
+ "Respiratory Support",
+ "ventilator_interface",
+ qParams.ventilator_interface &&
+ t(`RESPIRATORY_SUPPORT_SHORT__${qParams.ventilator_interface}`),
+ ),
+ value(
+ "Gender",
+ "gender",
+ parseOptionId(GENDER_TYPES, qParams.gender) || "",
+ ),
+ {
+ name: "Admitted to",
+ value: ADMITTED_TO[qParams.last_consultation_admitted_to],
+ paramKey: "last_consultation_admitted_to",
+ },
+ ...range("Age", "age"),
+ {
+ name: "LSG Body",
+ value: qParams.lsgBody ? LocalBodyData?.name || "" : "",
+ paramKey: "lsgBody",
+ },
+ ...FILTER_BY_DIAGNOSES_KEYS.map((key) =>
+ value(
+ DIAGNOSES_FILTER_LABELS[key],
+ key,
+ humanizeStrings(getDiagnosisFilterValue(key)),
+ ),
+ ),
+ badge("Declared Status", "is_declared_positive"),
+ ...dateRange("Declared positive", "date_declared_positive"),
+ ...dateRange("Last vaccinated", "last_vaccinated_date"),
+ {
+ name: "Telemedicine",
+ paramKey: "last_consultation_is_telemedicine",
+ },
+ value(
+ "Discharge Reason",
+ "last_consultation__new_discharge_reason",
+ parseOptionId(
+ DISCHARGE_REASONS,
+ qParams.last_consultation__new_discharge_reason,
+ ) || "",
+ ),
+ ]}
+ children={
+ (qParams.last_consultation_admitted_bed_type_list ||
+ qParams.last_consultation__consent_types) && (
+ <>
+ {qParams.last_consultation_admitted_bed_type_list &&
+ LastAdmittedToTypeBadges()}
+ {qParams.last_consultation__consent_types &&
+ HasConsentTypesBadges()}
+ >
+ )
+ }
+ />
+ );
+}
diff --git a/src/components/Patient/PatientIndex.tsx b/src/components/Patient/PatientIndex.tsx
new file mode 100644
index 00000000000..76f2d7503a8
--- /dev/null
+++ b/src/components/Patient/PatientIndex.tsx
@@ -0,0 +1,384 @@
+import dayjs from "dayjs";
+import { navigate } from "raviger";
+import { useCallback, useState } from "react";
+import { useTranslation } from "react-i18next";
+import useKeyboardShortcut from "use-keyboard-shortcut";
+
+import { cn } from "@/lib/utils";
+
+import CareIcon from "@/CAREUI/icons/CareIcon";
+
+import { Button } from "@/components/ui/button";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+import { SectionTabs } from "@/components/ui/tabs";
+
+import Loading from "@/components/Common/Loading";
+import Page from "@/components/Common/Page";
+import SearchByMultipleFields from "@/components/Common/SearchByMultipleFields";
+import FacilitiesSelectDialogue from "@/components/ExternalResult/FacilitiesSelectDialogue";
+import { FacilityModel } from "@/components/Facility/models";
+import { PatientManager } from "@/components/Patient/ManagePatients";
+import PatientFilter, {
+ PatientFilterBadges,
+} from "@/components/Patient/PatientFilter";
+import { getPatientUrl } from "@/components/Patient/Utils";
+
+import useAuthUser from "@/hooks/useAuthUser";
+import useFilters from "@/hooks/useFilters";
+
+import { GENDER_TYPES } from "@/common/constants";
+
+import * as Notification from "@/Utils/Notifications";
+import routes from "@/Utils/request/api";
+import useQuery from "@/Utils/request/useQuery";
+import { formatPatientAge, parsePhoneNumber } from "@/Utils/utils";
+
+export default function PatientIndex(props: {
+ tab?: "live" | "discharged" | "search";
+}) {
+ const { t } = useTranslation();
+ const { tab = "search" } = props;
+ const [showDialog, setShowDialog] = useState<"create" | "list-discharged">();
+ const [selectedFacility, setSelectedFacility] = useState({
+ name: "",
+ });
+ const {
+ qParams,
+ updateQuery,
+ advancedFilter,
+ Pagination,
+ resultsPerPage,
+ clearSearch,
+ } = useFilters({
+ limit: 12,
+ cacheBlacklist: [
+ "name",
+ "patient_no",
+ "phone_number",
+ "emergency_phone_number",
+ ],
+ });
+
+ const searchOptions = [
+ {
+ key: "name",
+ type: "text" as const,
+ placeholder: "search_by_patient_name",
+ value: qParams.name || "",
+ shortcutKey: "n",
+ },
+ {
+ key: "patient_no",
+ type: "text" as const,
+ placeholder: "search_by_patient_no",
+ value: qParams.patient_no || "",
+ shortcutKey: "u",
+ },
+ {
+ key: "phone_number",
+ type: "phone" as const,
+ placeholder: "Search_by_phone_number",
+ value: qParams.phone_number || "",
+ shortcutKey: "p",
+ },
+
+ {
+ key: "emergency_contact_number",
+ type: "phone" as const,
+ placeholder: "search_by_emergency_phone_number",
+ value: qParams.emergency_phone_number || "",
+ shortcutKey: "e",
+ },
+ ];
+
+ const authUser = useAuthUser();
+
+ const handleSearch = useCallback(
+ (key: string, value: string) => {
+ const updatedQuery = {
+ phone_number:
+ key === "phone_number"
+ ? value.length >= 13 || value === ""
+ ? value
+ : undefined
+ : undefined,
+ name: key === "name" ? value : undefined,
+ patient_no: key === "patient_no" ? value : undefined,
+ emergency_phone_number:
+ key === "emergency_contact_number"
+ ? value.length >= 13 || value === ""
+ ? value
+ : undefined
+ : undefined,
+ };
+
+ updateQuery(updatedQuery);
+ },
+ [updateQuery],
+ );
+
+ const getCleanedParams = (
+ params: Record,
+ ) => {
+ const cleaned: typeof params = {};
+ Object.keys(params).forEach((key) => {
+ if (params[key] !== 0 && params[key] !== "") {
+ cleaned[key] = params[key];
+ }
+ });
+ return cleaned;
+ };
+
+ const params = getCleanedParams({
+ ...qParams,
+ page: qParams.page || 1,
+ limit: resultsPerPage,
+ is_active:
+ !qParams.last_consultation__new_discharge_reason &&
+ (qParams.is_active || "True"),
+ phone_number: qParams.phone_number
+ ? parsePhoneNumber(qParams.phone_number)
+ : undefined,
+ emergency_phone_number: qParams.emergency_phone_number
+ ? parsePhoneNumber(qParams.emergency_phone_number)
+ : undefined,
+ local_body: qParams.lsgBody || undefined,
+ offset: (qParams.page ? qParams.page - 1 : 0) * resultsPerPage,
+ last_menstruation_start_date_after:
+ (qParams.is_antenatal === "true" &&
+ dayjs().subtract(9, "month").format("YYYY-MM-DD")) ||
+ undefined,
+ });
+
+ const isValidSearch = searchOptions.some((o) => !!o.value);
+
+ const listingQuery = useQuery(routes.patientList, {
+ query: params,
+ prefetch: isValidSearch,
+ });
+
+ const { data: permittedFacilities } = useQuery(
+ routes.getPermittedFacilities,
+ {
+ query: { limit: 1 },
+ },
+ );
+
+ const onlyAccessibleFacility =
+ permittedFacilities?.count === 1 ? permittedFacilities.results[0] : null;
+
+ const handleAddPatient = () => {
+ let facilityId = "";
+ const showAllFacilityUsers = ["DistrictAdmin", "StateAdmin"];
+ const userCanSeeAllFacilities = showAllFacilityUsers.includes(
+ authUser?.user_type,
+ );
+ const userHomeFacilityId = authUser?.home_facility_object?.id;
+ if (qParams.facility && userCanSeeAllFacilities)
+ facilityId = qParams.facility;
+ else if (
+ qParams.facility &&
+ !userCanSeeAllFacilities &&
+ userHomeFacilityId !== qParams.facility
+ )
+ Notification.Error({
+ msg: t("permission_denied"),
+ });
+ else if (!userCanSeeAllFacilities && userHomeFacilityId) {
+ facilityId = userHomeFacilityId;
+ } else if (onlyAccessibleFacility)
+ facilityId = onlyAccessibleFacility.id || "";
+ else if (!userCanSeeAllFacilities && !userHomeFacilityId) {
+ Notification.Error({
+ msg: t("no_home_facility_found"),
+ });
+ return;
+ } else {
+ setShowDialog("create");
+ return;
+ }
+ navigate(`/facility/${facilityId}/register-patient`);
+ };
+
+ function AddPatientButton(props: { outline?: boolean }) {
+ useKeyboardShortcut(["Shift", "P"], handleAddPatient);
+ return (
+
+
+ {t("add_new_patient")}
+
+ SHIFT P
+
+
+ );
+ }
+
+ return (
+ }
+ >
+ {
+ if (value === "discharged") {
+ const id = qParams.facility || onlyAccessibleFacility?.id;
+ if (id) {
+ navigate(`facility/${id}/discharged-patients`);
+ return;
+ }
+
+ if (
+ authUser.user_type === "StateAdmin" ||
+ authUser.user_type === "StateReadOnlyAdmin"
+ ) {
+ navigate("/patients/discharged?is_active=false");
+ return;
+ }
+ setShowDialog("list-discharged");
+ } else if (value === "search") {
+ navigate("/patients");
+ } else if (value === "live") {
+ navigate("/patients/live");
+ }
+ }}
+ tabs={[
+ {
+ label: t("search_patients"),
+ value: "search",
+ },
+ {
+ label: t("all_patients"),
+ value: "live",
+ },
+ {
+ label: t("discharged_patients"),
+ value: "discharged",
+ },
+ ]}
+ />
+ {tab === "search" ? (
+
+
+
+
+
advancedFilter.setShow(true)}
+ >
+
+ {t("filters")}
+
+
+
+
+ {isValidSearch &&
+ !listingQuery.loading &&
+ !listingQuery.data?.results.length && (
+
+ {t("no_records_found")}
+
+ {t("to_proceed_with_registration")}
+
+
+ )}
+ {isValidSearch && listingQuery.loading &&
}
+ {isValidSearch && !!listingQuery.data?.results.length && (
+
+
+
+ {t("name")}/IP/OP
+ {t("primary_phone_no")}
+
+ {t("dob")}/{t("age")}
+
+ {t("sex")}
+
+
+
+ {listingQuery.data?.results.map((patient) => (
+ navigate(getPatientUrl(patient))}
+ >
+
+ {patient.name}
+
+
+ {patient.last_consultation?.patient_no}
+
+ {patient.phone_number}
+
+ {!!patient.date_of_birth &&
+ dayjs(patient.date_of_birth).format("DD-MM-YYYY")}{" "}
+ ({formatPatientAge(patient)})
+
+
+ {GENDER_TYPES.find((g) => g.id === patient.gender)?.text}
+
+
+ ))}
+
+
+ )}
+ {listingQuery.data && (
+
+ )}
+
+ ) : (
+
+ )}
+
+ setSelectedFacility(e)}
+ selectedFacility={selectedFacility}
+ handleOk={() => {
+ switch (showDialog) {
+ case "create":
+ navigate(`/facility/${selectedFacility.id}/register-patient`);
+ break;
+ case "list-discharged":
+ navigate(`/facility/${selectedFacility.id}/discharged-patients`);
+ break;
+ }
+ }}
+ handleCancel={() => {
+ setShowDialog(undefined);
+ setSelectedFacility({ name: "" });
+ }}
+ />
+
+ );
+}
diff --git a/src/components/Patient/PatientRegister.tsx b/src/components/Patient/PatientRegister.tsx
deleted file mode 100644
index 15780de28ff..00000000000
--- a/src/components/Patient/PatientRegister.tsx
+++ /dev/null
@@ -1,1765 +0,0 @@
-import careConfig from "@careConfig";
-import { navigate } from "raviger";
-import { useCallback, useEffect, useReducer, useState } from "react";
-import { useTranslation } from "react-i18next";
-
-import CareIcon from "@/CAREUI/icons/CareIcon";
-
-import AccordionV2 from "@/components/Common/AccordionV2";
-import ButtonV2 from "@/components/Common/ButtonV2";
-import CollapseV2 from "@/components/Common/CollapseV2";
-import ConfirmDialog from "@/components/Common/ConfirmDialog";
-import DialogModal from "@/components/Common/Dialog";
-import Loading from "@/components/Common/Loading";
-import PageTitle from "@/components/Common/PageTitle";
-import Spinner from "@/components/Common/Spinner";
-import DuplicatePatientDialog from "@/components/Facility/DuplicatePatientDialog";
-import TransferPatientDialog from "@/components/Facility/TransferPatientDialog";
-import {
- DistrictModel,
- DupPatientModel,
- FacilityModel,
- LocalBodyModel,
- WardModel,
-} from "@/components/Facility/models";
-import {
- FieldError,
- PhoneNumberValidator,
- RequiredFieldValidator,
-} from "@/components/Form/FieldValidators";
-import Form from "@/components/Form/Form";
-import AutocompleteFormField from "@/components/Form/FormFields/Autocomplete";
-import CheckBoxFormField from "@/components/Form/FormFields/CheckBoxFormField";
-import DateFormField from "@/components/Form/FormFields/DateFormField";
-import {
- FieldErrorText,
- FieldLabel,
-} from "@/components/Form/FormFields/FormField";
-import PhoneNumberFormField from "@/components/Form/FormFields/PhoneNumberFormField";
-import RadioFormField from "@/components/Form/FormFields/RadioFormField";
-import { SelectFormField } from "@/components/Form/FormFields/SelectFormField";
-import TextAreaFormField from "@/components/Form/FormFields/TextAreaFormField";
-import TextFormField from "@/components/Form/FormFields/TextFormField";
-import SelectMenuV2 from "@/components/Form/SelectMenuV2";
-import InsuranceDetailsBuilder from "@/components/HCX/InsuranceDetailsBuilder";
-import { HCXPolicyModel } from "@/components/HCX/models";
-import HCXPolicyValidator from "@/components/HCX/validators";
-import {
- Occupation,
- PatientMeta,
- PatientModel,
-} from "@/components/Patient/models";
-import { UserModel } from "@/components/Users/models";
-
-import useAppHistory from "@/hooks/useAppHistory";
-import useAuthUser from "@/hooks/useAuthUser";
-import useDebounce from "@/hooks/useDebounce";
-
-import {
- BLOOD_GROUPS,
- DOMESTIC_HEALTHCARE_SUPPORT_CHOICES,
- GENDER_TYPES,
- MEDICAL_HISTORY_CHOICES,
- OCCUPATION_TYPES,
- RATION_CARD_CATEGORY,
- SOCIOECONOMIC_STATUS_CHOICES,
- VACCINES,
-} from "@/common/constants";
-import countryList from "@/common/static/countries.json";
-import { statusType, useAbortableEffect } from "@/common/utils";
-import { validateName, validatePincode } from "@/common/validation";
-
-import { PLUGIN_Component } from "@/PluginEngine";
-import { RestoreDraftButton } from "@/Utils/AutoSave";
-import * as Notification from "@/Utils/Notifications";
-import { usePubSub } from "@/Utils/pubsubContext";
-import routes from "@/Utils/request/api";
-import request from "@/Utils/request/request";
-import useTanStackQueryInstead from "@/Utils/request/useQuery";
-import {
- compareBy,
- dateQueryString,
- getPincodeDetails,
- includesIgnoreCase,
- parsePhoneNumber,
- scrollTo,
-} from "@/Utils/utils";
-
-import ErrorPage from "../ErrorPages/DefaultErrorPage";
-
-export type PatientForm = PatientModel &
- PatientMeta & { age?: number; is_postpartum?: boolean };
-
-interface PatientRegisterProps extends PatientModel {
- facilityId: string;
-}
-
-interface medicalHistoryModel {
- id?: number;
- disease: string | number;
- details: string;
-}
-
-const medicalHistoryChoices = MEDICAL_HISTORY_CHOICES.reduce(
- (acc: Array<{ [x: string]: string }>, cur) => [
- ...acc,
- { [`medical_history_${cur.id}`]: "" },
- ],
- [],
-);
-const genderTypes = GENDER_TYPES;
-const bloodGroups = [...BLOOD_GROUPS];
-const occupationTypes = OCCUPATION_TYPES;
-const vaccines = [...VACCINES];
-
-const initForm: any = {
- name: "",
- age: "",
- year_of_birth: "",
- gender: "",
- phone_number: "+91",
- emergency_phone_number: "+91",
- blood_group: "",
- is_declared_positive: "false",
- date_declared_positive: new Date(),
- date_of_birth: null,
- medical_history: [],
- nationality: "India",
- passport_no: "",
- state: "",
- district: "",
- local_body: "",
- ward: "",
- address: "",
- permanent_address: "",
- sameAddress: true,
- village: "",
- allergies: "",
- pincode: "",
- present_health: "",
- date_of_return: null,
- is_antenatal: "false",
- date_of_test: null,
- treatment_plan: false,
- ongoing_medication: "",
- designation_of_health_care_worker: "",
- instituion_of_health_care_worker: "",
- covin_id: "",
- is_vaccinated: "false",
- number_of_doses: "0",
- vaccine_name: null,
- last_vaccinated_date: null,
- ...medicalHistoryChoices,
- ration_card_category: null,
-};
-
-const initError = Object.assign(
- {},
- ...Object.keys(initForm).map((k) => ({ [k]: "" })),
-);
-
-const initialState = {
- form: { ...initForm },
- errors: { ...initError },
-};
-
-const patientFormReducer = (state = initialState, action: any) => {
- switch (action.type) {
- case "set_form": {
- return {
- ...state,
- form: action.form,
- };
- }
- case "set_error": {
- return {
- ...state,
- errors: action.errors,
- };
- }
- default:
- return state;
- }
-};
-export const parseOccupationFromExt = (occupation: Occupation) => {
- const occupationObject = OCCUPATION_TYPES.find(
- (item) => item.value === occupation,
- );
- return occupationObject?.id;
-};
-
-export const PatientRegister = (props: PatientRegisterProps) => {
- const authUser = useAuthUser();
- const { t } = useTranslation();
- const { goBack } = useAppHistory();
- const { facilityId, id } = props;
- const [state, dispatch] = useReducer(patientFormReducer, initialState);
- const [showAlertMessage, setAlertMessage] = useState({
- show: false,
- message: "",
- title: "",
- });
- const [isLoading, setIsLoading] = useState(false);
- const [formField, setFormField] = useState();
- const [resetNum, setResetNum] = useState(false);
- const [isDistrictLoading, setIsDistrictLoading] = useState(false);
- const [isLocalbodyLoading, setIsLocalbodyLoading] = useState(false);
- const [isWardLoading, setIsWardLoading] = useState(false);
- const [districts, setDistricts] = useState([]);
- const [localBody, setLocalBody] = useState([]);
- const [ward, setWard] = useState([]);
- const [ageInputType, setAgeInputType] = useState<
- "date_of_birth" | "age" | "alert_for_age"
- >("date_of_birth");
- const [statusDialog, setStatusDialog] = useState<{
- show?: boolean;
- transfer?: boolean;
- patientList: Array;
- }>({ patientList: [] });
- const [patientName, setPatientName] = useState("");
- const [showAutoFilledPincode, setShowAutoFilledPincode] = useState(false);
- const [insuranceDetails, setInsuranceDetails] = useState(
- [],
- );
- const [isEmergencyNumberEnabled, setIsEmergencyNumberEnabled] =
- useState(false);
- const [insuranceDetailsError, setInsuranceDetailsError] =
- useState();
-
- const { publish } = usePubSub();
-
- const headerText = !id ? "Add Details of Patient" : "Update Patient Details";
- const buttonText = !id ? "Add Patient" : "Save Details";
-
- useEffect(() => {
- const getQueryParams = () => {
- const params = new URLSearchParams(window.location.search);
- return {
- section: params.get("section"),
- };
- };
-
- const { section } = getQueryParams();
- if (section) {
- setTimeout(() => {
- const element = document.getElementById(section);
- if (element) {
- element.scrollIntoView({ behavior: "smooth" });
- }
- }, 2000);
- }
- }, []);
-
- const fetchDistricts = useCallback(async (id: number) => {
- if (id > 0) {
- setIsDistrictLoading(true);
- const { res, data } = await request(routes.getDistrictByState, {
- pathParams: { id },
- });
- if (res?.ok && data) {
- setDistricts(data);
- }
- setIsDistrictLoading(false);
- return data ? [...data] : [];
- }
- }, []);
-
- const fetchLocalBody = useCallback(async (id: string) => {
- if (Number(id) > 0) {
- setIsLocalbodyLoading(true);
- const { data } = await request(routes.getLocalbodyByDistrict, {
- pathParams: { id },
- });
- setIsLocalbodyLoading(false);
- setLocalBody(data || []);
- } else {
- setLocalBody([]);
- }
- }, []);
-
- const fetchWards = useCallback(async (id: string) => {
- if (Number(id) > 0) {
- setIsWardLoading(true);
- const { data } = await request(routes.getWardByLocalBody, {
- pathParams: { id },
- });
- setIsWardLoading(false);
- if (data) {
- setWard(data.results);
- }
- } else {
- setWard([]);
- }
- }, []);
-
- const fetchData = useCallback(
- async (status: statusType) => {
- setIsLoading(true);
- const { res, data } = await request(routes.getPatient, {
- pathParams: { id: id ? id : 0 },
- });
-
- if (!status.aborted) {
- if (res?.ok && data) {
- setPatientName(data.name || "");
- if (!data.date_of_birth) {
- setAgeInputType("age");
- }
- const formData = {
- ...data,
- age: data.year_of_birth
- ? new Date().getFullYear() - data.year_of_birth
- : "",
- nationality: data.nationality ? data.nationality : "India",
- gender: data.gender ? data.gender : undefined,
- state: data.state ? data.state : "",
- district: data.district ? data.district : "",
- blood_group: data.blood_group
- ? data.blood_group === "UNKNOWN"
- ? "UNK"
- : data.blood_group
- : "",
- local_body: data.local_body ? data.local_body : "",
- ward: data.ward_object ? data.ward_object.id : undefined,
- village: data.village ? data.village : "",
- medical_history: [] as number[],
- is_antenatal: String(!!data.is_antenatal),
- last_menstruation_start_date: data.last_menstruation_start_date,
- date_of_delivery: data.date_of_delivery,
- is_postpartum: String(!!data.date_of_delivery),
- allergies: data.allergies ? data.allergies : "",
- pincode: data.pincode ? data.pincode : "",
- ongoing_medication: data.ongoing_medication
- ? data.ongoing_medication
- : "",
-
- is_declared_positive: data.is_declared_positive
- ? String(data.is_declared_positive)
- : "false",
- designation_of_health_care_worker:
- data.designation_of_health_care_worker
- ? data.designation_of_health_care_worker
- : "",
- instituion_of_health_care_worker:
- data.instituion_of_health_care_worker
- ? data.instituion_of_health_care_worker
- : "",
- meta_info: data.meta_info ?? {},
- occupation: data.meta_info?.occupation
- ? parseOccupationFromExt(data.meta_info.occupation)
- : null,
-
- is_vaccinated: String(data.is_vaccinated),
- number_of_doses: data.number_of_doses
- ? String(data.number_of_doses)
- : "0",
- vaccine_name: data.vaccine_name ? data.vaccine_name : null,
- last_vaccinated_date: data.last_vaccinated_date
- ? data.last_vaccinated_date
- : null,
- };
- formData.sameAddress = data.address === data.permanent_address;
- setIsEmergencyNumberEnabled(
- data.phone_number === data.emergency_phone_number,
- );
- (data.medical_history ? data.medical_history : []).forEach(
- (i: any) => {
- const medicalHistory = MEDICAL_HISTORY_CHOICES.find(
- (j) =>
- String(j.text).toLowerCase() ===
- String(i.disease).toLowerCase(),
- );
- if (medicalHistory) {
- formData.medical_history.push(Number(medicalHistory.id));
- (formData as any)[`medical_history_${medicalHistory.id}`] =
- i.details;
- }
- },
- );
- dispatch({
- type: "set_form",
- form: formData,
- });
- Promise.all([
- fetchDistricts(data.state ?? 0),
- fetchLocalBody(data.district ? String(data.district) : ""),
- fetchWards(data.local_body ? String(data.local_body) : ""), // Convert data.local_body to string
- ]);
- } else {
- goBack();
- }
- setIsLoading(false);
- }
- },
- [id],
- );
-
- useTanStackQueryInstead(routes.hcx.policies.list, {
- query: {
- patient: id,
- },
- prefetch: !!id,
- onResponse: ({ data }) => {
- if (data) {
- setInsuranceDetails(data.results);
- } else {
- setInsuranceDetails([]);
- }
- },
- });
-
- const { data: stateData, loading: isStateLoading } = useTanStackQueryInstead(
- routes.statesList,
- );
-
- useAbortableEffect(
- (status: statusType) => {
- if (id) {
- fetchData(status);
- }
- },
- [dispatch, fetchData],
- );
-
- const { data: facilityObject } = useTanStackQueryInstead(
- routes.getAnyFacility,
- {
- pathParams: { id: facilityId },
- prefetch: !!facilityId,
- },
- );
-
- const validateForm = (form: any) => {
- const errors: Partial> = {};
-
- const insuranceDetailsError = insuranceDetails
- .map((policy) => HCXPolicyValidator(policy, careConfig.hcx.enabled))
- .find((error) => !!error);
- setInsuranceDetailsError(insuranceDetailsError);
-
- errors["insurance_details"] = insuranceDetailsError;
-
- Object.keys(form).forEach((field) => {
- let phoneNumber, emergency_phone_number;
- switch (field) {
- case "name": {
- const requiredError = RequiredFieldValidator()(form[field]);
- if (requiredError) {
- errors[field] = requiredError;
- } else if (!validateName(form[field])) {
- errors[field] = t("min_char_length_error", { min_length: 3 });
- }
- return;
- }
- case "address":
- case "gender":
- errors[field] = RequiredFieldValidator()(form[field]);
- return;
- case "last_menstruation_start_date":
- if (form.is_antenatal === "true") {
- errors[field] = RequiredFieldValidator()(form[field]);
- }
- return;
- case "date_of_delivery":
- if (form.is_postpartum === "true") {
- errors[field] = RequiredFieldValidator()(form[field]);
- }
- return;
- case "age":
- case "date_of_birth": {
- const field = ageInputType === "age" ? "age" : "date_of_birth";
-
- errors[field] = RequiredFieldValidator()(form[field]);
- if (errors[field]) {
- return;
- }
-
- if (field === "age") {
- if (form.age < 0) {
- errors.age = "Age cannot be less than 0";
- return;
- }
-
- form.date_of_birth = null;
- form.year_of_birth = new Date().getFullYear() - form.age;
- }
-
- if (field === "date_of_birth") {
- form.age = null;
- form.year_of_birth = null;
- }
-
- return;
- }
- case "permanent_address":
- if (!form.sameAddress) {
- errors[field] = RequiredFieldValidator()(form[field]);
- }
- return;
- case "local_body":
- if (form.nationality === "India" && !Number(form[field])) {
- errors[field] = "Please select a localbody";
- }
- return;
- case "district":
- if (form.nationality === "India" && !Number(form[field])) {
- errors[field] = "Please select district";
- }
- return;
- case "state":
- if (form.nationality === "India" && !Number(form[field])) {
- errors[field] = "Please enter the state";
- }
- return;
- case "pincode":
- if (!validatePincode(form[field])) {
- errors[field] = "Please enter valid pincode";
- }
- return;
- case "passport_no":
- if (form.nationality !== "India" && !form[field]) {
- errors[field] = "Please enter the passport number";
- }
- return;
- case "phone_number":
- phoneNumber = parsePhoneNumber(form[field]);
- if (
- !form[field] ||
- !phoneNumber ||
- !PhoneNumberValidator()(phoneNumber) === undefined
- ) {
- errors[field] = "Please enter valid phone number";
- }
- return;
- case "emergency_phone_number":
- emergency_phone_number = parsePhoneNumber(form[field]);
- if (
- !form[field] ||
- !emergency_phone_number ||
- !PhoneNumberValidator()(emergency_phone_number) === undefined
- ) {
- errors[field] = "Please enter valid phone number";
- }
- return;
- case "blood_group":
- if (!form[field]) {
- errors[field] = "Please select a blood group";
- }
- return;
- case "is_vaccinated":
- if (form.is_vaccinated === "true") {
- if (form.number_of_doses === "0") {
- errors["number_of_doses"] =
- "Please fill the number of doses taken";
- }
- if (form.vaccine_name === null || form.vaccine_name === "Select") {
- errors["vaccine_name"] = "Please select vaccine name";
- }
-
- if (!form.last_vaccinated_date) {
- errors["last_vaccinated_date"] =
- "Please enter last vaccinated date";
- }
- }
- return;
- case "medical_history":
- if (!form[field].length) {
- errors[field] = "Please fill the medical history";
- }
- return;
- default:
- return;
- }
- });
-
- const firstError = Object.keys(errors).find((e) => errors[e]);
- if (firstError) {
- scrollTo(firstError);
- }
-
- return errors;
- };
-
- const handlePincodeChange = async (e: any, setField: any) => {
- if (!validatePincode(e.value)) return;
-
- const pincodeDetails = await getPincodeDetails(
- e.value,
- careConfig.govDataApiKey,
- );
- if (!pincodeDetails) return;
-
- const matchedState = stateData?.results?.find((state) => {
- return includesIgnoreCase(state.name, pincodeDetails.statename);
- });
- if (!matchedState) return;
-
- const fetchedDistricts = await fetchDistricts(matchedState.id);
- if (!fetchedDistricts) return;
-
- const matchedDistrict = fetchedDistricts.find((district) => {
- return includesIgnoreCase(district.name, pincodeDetails.districtname);
- });
- if (!matchedDistrict) return;
-
- setField({ name: "state", value: matchedState.id });
- setField({ name: "district", value: matchedDistrict.id.toString() }); // Convert matchedDistrict.id to string
-
- fetchLocalBody(matchedDistrict.id.toString()); // Convert matchedDistrict.id to string
- setShowAutoFilledPincode(true);
- setTimeout(() => {
- setShowAutoFilledPincode(false);
- }, 2000);
- };
-
- const handleSubmit = async (formData: any) => {
- setIsLoading(true);
- const medical_history: Array = [];
- formData.medical_history.forEach((id: number) => {
- const medData = MEDICAL_HISTORY_CHOICES.find((i) => i.id === id);
- if (medData) {
- const details = formData[`medical_history_${medData.id}`];
- medical_history.push({
- disease: medData.text,
- details: details ? details : "",
- });
- }
- });
- const data = {
- phone_number: parsePhoneNumber(formData.phone_number),
- emergency_phone_number: parsePhoneNumber(formData.emergency_phone_number),
- date_of_birth:
- ageInputType === "date_of_birth"
- ? dateQueryString(formData.date_of_birth)
- : null,
- year_of_birth: ageInputType === "age" ? formData.year_of_birth : null,
- date_of_test: formData.date_of_test ? formData.date_of_test : undefined,
- date_declared_positive:
- JSON.parse(formData.is_declared_positive) &&
- formData.date_declared_positive
- ? formData.date_declared_positive
- : null,
- covin_id:
- formData.is_vaccinated === "true" ? formData.covin_id : undefined,
- is_vaccinated: formData.is_vaccinated,
- number_of_doses:
- formData.is_vaccinated === "true"
- ? Number(formData.number_of_doses)
- : Number("0"),
- vaccine_name:
- formData.vaccine_name &&
- formData.vaccine_name !== "Select" &&
- formData.is_vaccinated === "true"
- ? formData.vaccine_name
- : null,
- last_vaccinated_date:
- formData.is_vaccinated === "true"
- ? formData.last_vaccinated_date
- ? formData.last_vaccinated_date
- : null
- : null,
- name: formData.name,
- pincode: formData.pincode ? formData.pincode : undefined,
- gender: Number(formData.gender),
- nationality: formData.nationality,
- is_antenatal: formData.is_antenatal,
- last_menstruation_start_date:
- formData.is_antenatal === "true"
- ? dateQueryString(formData.last_menstruation_start_date)
- : null,
- date_of_delivery:
- formData.is_postpartum === "true"
- ? dateQueryString(formData.date_of_delivery)
- : null,
- passport_no:
- formData.nationality !== "India" ? formData.passport_no : undefined,
- state: formData.nationality === "India" ? formData.state : undefined,
- district:
- formData.nationality === "India" ? formData.district : undefined,
- local_body:
- formData.nationality === "India" ? formData.local_body : undefined,
- ward: formData.ward,
- meta_info: {
- ...formData.meta_info,
- occupation: formData.occupation ?? null,
- },
- village: formData.village,
- address: formData.address ? formData.address : undefined,
- permanent_address: formData.sameAddress
- ? formData.address
- : formData.permanent_address
- ? formData.permanent_address
- : undefined,
- present_health: formData.present_health
- ? formData.present_health
- : undefined,
- allergies: formData.allergies,
- ongoing_medication: formData.ongoing_medication,
- is_declared_positive: JSON.parse(formData.is_declared_positive),
- designation_of_health_care_worker:
- formData.designation_of_health_care_worker,
- instituion_of_health_care_worker:
- formData.instituion_of_health_care_worker,
- blood_group: formData.blood_group ? formData.blood_group : undefined,
- medical_history,
- is_active: true,
- ration_card_category: formData.ration_card_category,
- };
- const { res, data: requestData } = id
- ? await request(routes.updatePatient, {
- pathParams: { id },
- body: data,
- })
- : await request(routes.addPatient, {
- body: { ...data, facility: facilityId },
- });
- if (res?.ok && requestData) {
- publish("patient:upsert", requestData);
-
- await Promise.all(
- insuranceDetails.map(async (obj) => {
- const policy = {
- ...obj,
- patient: requestData.id,
- insurer_id: obj.insurer_id || undefined,
- insurer_name: obj.insurer_name || undefined,
- };
- policy.id
- ? await request(routes.hcx.policies.update, {
- pathParams: { external_id: policy.id },
- body: policy,
- })
- : await request(routes.hcx.policies.create, {
- body: policy,
- });
- }),
- );
-
- dispatch({ type: "set_form", form: initForm });
- if (!id) {
- setAlertMessage({
- show: true,
- message: `Please note down patient name: ${formData.name} and patient ID: ${requestData.id}`,
- title: "Patient Added Successfully",
- });
- navigate(
- `/facility/${facilityId}/patient/${requestData.id}/consultation`,
- );
- } else {
- Notification.Success({
- msg: "Patient updated successfully",
- });
- goBack();
- }
- }
- setIsLoading(false);
- };
-
- const handleMedicalCheckboxChange = (e: any, id: number, field: any) => {
- const values = field("medical_history").value ?? [];
- if (e.value) {
- values.push(id);
- } else {
- values.splice(values.indexOf(id), 1);
- }
-
- if (id !== 1 && values.includes(1)) {
- values.splice(values.indexOf(1), 1);
- } else if (id === 1) {
- values.length = 0;
- values.push(1);
- }
-
- field("medical_history").onChange({
- name: "medical_history",
- value: values,
- });
- };
-
- const duplicateCheck = useDebounce(async (phoneNo: string) => {
- if (
- phoneNo &&
- PhoneNumberValidator()(parsePhoneNumber(phoneNo) ?? "") === undefined
- ) {
- const query = {
- phone_number: parsePhoneNumber(phoneNo),
- };
- const { res, data } = await request(routes.searchPatient, {
- query,
- });
- if (res?.ok && data?.results) {
- const duplicateList = !id
- ? data.results
- : data.results.filter(
- (item: DupPatientModel) => item.patient_id !== id,
- );
- if (duplicateList.length) {
- setStatusDialog({
- show: true,
- patientList: duplicateList,
- });
- }
- }
- } else {
- setStatusDialog({
- show: false,
- patientList: [],
- });
- }
- }, 300);
-
- const handleDialogClose = (action: string) => {
- if (action === "transfer") {
- setStatusDialog({ ...statusDialog, show: false, transfer: true });
- } else if (action === "back") {
- setStatusDialog({ ...statusDialog, show: true, transfer: false });
- } else {
- setStatusDialog({ show: false, transfer: false, patientList: [] });
- }
- };
-
- const renderMedicalHistory = (id: number, title: string, field: any) => {
- const checkboxField = `medical_history_check_${id}`;
- const textField = `medical_history_${id}`;
- return (
-
-
- handleMedicalCheckboxChange(e, id, field)}
- name={checkboxField}
- label={id !== 1 ? title : "NONE"}
- />
-
- {id !== 1 && (field("medical_history").value ?? []).includes(id) && (
-
-
-
- )}
-
- );
- };
-
- if (isLoading) {
- return ;
- }
-
- if (
- !isLoading &&
- facilityId &&
- facilityObject &&
- !patientRegisterAuth(authUser, facilityObject, facilityId)
- ) {
- return ;
- }
-
- return (
-
- );
- }}
-
- );
-};
-
-export function patientRegisterAuth(
- authUser: UserModel,
- facilityObject: FacilityModel | undefined,
- facilityId: string,
-) {
- const showAllFacilityUsers = ["DistrictAdmin", "StateAdmin"];
- if (
- !showAllFacilityUsers.includes(authUser.user_type) &&
- authUser.home_facility_object?.id === facilityId
- ) {
- return true;
- }
- if (
- authUser.user_type === "DistrictAdmin" &&
- authUser.district === facilityObject?.district
- ) {
- return true;
- }
- if (
- authUser.user_type === "StateAdmin" &&
- authUser.state === facilityObject?.state
- ) {
- return true;
- }
-
- return false;
-}
diff --git a/src/components/Patient/PatientRegistration.tsx b/src/components/Patient/PatientRegistration.tsx
new file mode 100644
index 00000000000..4f69050dccb
--- /dev/null
+++ b/src/components/Patient/PatientRegistration.tsx
@@ -0,0 +1,1069 @@
+import careConfig from "@careConfig";
+import { useQuery } from "@tanstack/react-query";
+import { navigate } 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";
+import { Checkbox } from "@/components/ui/checkbox";
+import { InputErrors } from "@/components/ui/errors";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
+import { Textarea } from "@/components/ui/textarea";
+
+import DialogModal from "@/components/Common/Dialog";
+import Loading from "@/components/Common/Loading";
+import Page from "@/components/Common/Page";
+import DuplicatePatientDialog from "@/components/Facility/DuplicatePatientDialog";
+import TransferPatientDialog from "@/components/Facility/TransferPatientDialog";
+import { PatientModel } from "@/components/Patient/models";
+
+import useAppHistory from "@/hooks/useAppHistory";
+
+import {
+ BLOOD_GROUPS,
+ 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 query from "@/Utils/request/query";
+import useMutation from "@/Utils/request/useMutation";
+import {
+ dateQueryString,
+ getPincodeDetails,
+ includesIgnoreCase,
+ parsePhoneNumber,
+} from "@/Utils/utils";
+
+import Autocomplete from "../ui/autocomplete";
+import FormField from "../ui/form-field";
+
+interface PatientRegistrationPageProps {
+ facilityId: string;
+ patientId?: string;
+}
+
+export default function PatientRegistration(
+ props: PatientRegistrationPageProps,
+) {
+ const { patientId, facilityId } = props;
+ const { t } = useTranslation();
+ const { goBack } = useAppHistory();
+
+ const [samePhoneNumber, setSamePhoneNumber] = useState(false);
+ const [sameAddress, setSameAddress] = useState(true);
+ const [ageDob, setAgeDob] = useState<"dob" | "age">("dob");
+ const [showAutoFilledPincode, setShowAutoFilledPincode] = useState(false);
+ const [form, setForm] = useState>({
+ nationality: "India",
+ phone_number: "+91",
+ emergency_phone_number: "+91",
+ });
+ const [feErrors, setFeErrors] = useState<
+ Partial>
+ >({});
+ const [suppressDuplicateWarning, setSuppressDuplicateWarning] =
+ useState(!!patientId);
+ const [showTransferDialog, setShowTransferDialog] = useState(false);
+
+ const sidebarItems = [
+ { label: t("patient__general-info"), id: "general-info" },
+ { label: t("social_profile"), id: "social-profile" },
+ //{ label: t("volunteer_contact"), id: "volunteer-contact" },
+ //{ label: t("patient__insurance-details"), id: "insurance-details" },
+ ];
+
+ const mutationFields: (keyof PatientModel)[] = [
+ "name",
+ "phone_number",
+ "emergency_phone_number",
+ "gender",
+ "blood_group",
+ "date_of_birth",
+ "age",
+ "address",
+ "permanent_address",
+ "pincode",
+ "nationality",
+ "state",
+ "district",
+ "local_body",
+ "ward",
+ "village",
+ "meta_info",
+ "ration_card_category",
+ ];
+
+ const mutationData: Partial = {
+ ...Object.fromEntries(
+ Object.entries(form).filter(([key]) =>
+ mutationFields.includes(key as keyof PatientModel),
+ ),
+ ),
+ date_of_birth:
+ ageDob === "dob" ? dateQueryString(form.date_of_birth) : undefined,
+ year_of_birth: ageDob === "age" ? form.year_of_birth : undefined,
+ is_active: true,
+ is_antenatal: false,
+ passport_no: form.nationality === "Indian" ? form.passport_no : undefined,
+ meta_info: {
+ ...(form.meta_info as any),
+ occupation:
+ form.meta_info?.occupation === ""
+ ? undefined
+ : form.meta_info?.occupation,
+ },
+ };
+
+ const createPatientMutation = useMutation(routes.addPatient, {
+ body: { ...mutationData, facility: facilityId, ward_old: undefined },
+ onResponse: (resp) => {
+ if (resp.error) {
+ Notification.Error({
+ msg: t("patient_registration_error"),
+ });
+ } else {
+ Notification.Success({
+ msg: t("patient_registration_success"),
+ });
+ navigate(
+ `/facility/${facilityId}/patient/${resp.data?.id}/consultation`,
+ );
+ }
+ },
+ });
+
+ const updatePatientMutation = useMutation(routes.updatePatient, {
+ pathParams: { id: patientId || "" },
+ body: { ...mutationData, ward_old: undefined },
+ onResponse: (data) => {
+ if (data.error) {
+ Notification.Error({
+ msg: t("patient_update_error"),
+ });
+ } else {
+ Notification.Success({
+ msg: t("patient_update_success"),
+ });
+ goBack();
+ }
+ },
+ });
+
+ const patientQuery = useQuery({
+ queryKey: ["patient", patientId],
+ queryFn: query(routes.getPatient, {
+ pathParams: { id: patientId || "" },
+ }),
+ enabled: !!patientId,
+ });
+
+ const setAddress = async (args: {
+ state: (typeof form)["state"];
+ district?: (typeof form)["district"];
+ local_body?: (typeof form)["local_body"];
+ ward?: string;
+ }) => {
+ const { state, district, local_body, ward } = args;
+ setForm((f) => ({
+ ...f,
+ state,
+ }));
+ await new Promise((resolve) => setTimeout(resolve, 500));
+ const districts = await districtsQuery.refetch();
+
+ const matchedDistrict = districts.data?.find((d) => d.id === district);
+ if (!matchedDistrict) return;
+ setForm((f) => ({
+ ...f,
+ district: matchedDistrict.id,
+ }));
+
+ if (local_body) {
+ await new Promise((resolve) => setTimeout(resolve, 500));
+ const localBodies = await localBodyQuery.refetch();
+
+ const matchedLocalBody = localBodies.data?.find(
+ (lb) => lb.id === local_body,
+ );
+ if (!matchedLocalBody) return;
+ setForm((f) => ({
+ ...f,
+ local_body: matchedLocalBody.id,
+ }));
+
+ if (ward) {
+ await new Promise((resolve) => setTimeout(resolve, 500));
+ const wards = await wardsQuery.refetch();
+
+ const matchedWard = wards.data?.results.find(
+ (w) => w.id === Number(ward),
+ );
+ if (!matchedWard) return;
+ setForm((f) => ({
+ ...f,
+ ward: matchedWard.id.toString(),
+ }));
+ }
+ }
+ };
+
+ useEffect(() => {
+ if (patientQuery.data) {
+ setForm(patientQuery.data);
+ if (patientQuery.data.year_of_birth && !patientQuery.data.date_of_birth) {
+ setAgeDob("age");
+ }
+ if (
+ patientQuery.data.phone_number ===
+ patientQuery.data.emergency_phone_number
+ )
+ setSamePhoneNumber(true);
+ if (patientQuery.data.address === patientQuery.data.permanent_address)
+ setSameAddress(true);
+ setAddress({
+ state: patientQuery.data.state,
+ district: patientQuery.data.district,
+ local_body: patientQuery.data.local_body,
+ ward: patientQuery.data.ward,
+ });
+ }
+ }, [patientQuery.data]);
+
+ const statesQuery = useQuery({
+ queryKey: ["states"],
+ queryFn: query(routes.statesList),
+ });
+
+ const districtsQuery = useQuery({
+ queryKey: ["districts", form.state],
+ enabled: !!form.state,
+ queryFn: query(routes.getDistrictByState, {
+ pathParams: { id: form.state?.toString() || "" },
+ }),
+ });
+
+ const localBodyQuery = useQuery({
+ queryKey: ["localbodies", form.district],
+ enabled: !!form.district,
+ queryFn: query(routes.getLocalbodyByDistrict, {
+ pathParams: { id: form.district?.toString() || "" },
+ }),
+ });
+
+ const wardsQuery = useQuery({
+ queryKey: ["wards", form.local_body],
+ enabled: !!form.local_body,
+ queryFn: query(routes.getWardByLocalBody, {
+ pathParams: { id: form.local_body?.toString() || "" },
+ }),
+ });
+
+ 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 matchedState = statesQuery.data?.results?.find((state) => {
+ return includesIgnoreCase(state.name, pincodeDetails.statename);
+ });
+ if (!matchedState) return;
+ setForm((f) => ({
+ ...f,
+ state: matchedState.id,
+ }));
+ await new Promise((resolve) => setTimeout(resolve, 500));
+ const districts = await districtsQuery.refetch();
+
+ const matchedDistrict = districts.data?.find((district) => {
+ return includesIgnoreCase(district.name, pincodeDetails.districtname);
+ });
+ if (!matchedDistrict) return;
+ setForm((f) => ({
+ ...f,
+ district: matchedDistrict.id,
+ }));
+
+ setShowAutoFilledPincode(true);
+ setTimeout(() => {
+ setShowAutoFilledPincode(false);
+ }, 2000);
+ };
+
+ useEffect(() => {
+ const timeout = setTimeout(
+ () => handlePincodeChange(form.pincode?.toString() || ""),
+ 1000,
+ );
+ return () => clearTimeout(timeout);
+ }, [form.pincode]);
+
+ const title = !patientId
+ ? t("add_details_of_patient")
+ : t("update_patient_details");
+
+ const errors = { ...feErrors, ...createPatientMutation.error };
+
+ const fieldProps = (field: keyof typeof form) => ({
+ value: form[field] as string,
+ onChange: (e: React.ChangeEvent) =>
+ setForm((f) => ({
+ ...f,
+ [field]: e.target.value === "" ? undefined : e.target.value,
+ })),
+ });
+
+ const selectProps = (field: keyof typeof form) => ({
+ value: (form[field] as string)?.toString(),
+ onValueChange: (value: string) =>
+ setForm((f) => ({ ...f, [field]: value })),
+ });
+
+ const handleDialogClose = (action: string) => {
+ if (action === "transfer") {
+ setShowTransferDialog(true);
+ } else if (action === "back") {
+ setShowTransferDialog(false);
+ } else {
+ setSuppressDuplicateWarning(true);
+ setShowTransferDialog(false);
+ }
+ };
+
+ const handleFormSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ const errors: Record = {};
+ const requiredFields: Array = [
+ "name",
+ "phone_number",
+ "emergency_phone_number",
+ "gender",
+ "blood_group",
+ ageDob === "dob" ? "date_of_birth" : "year_of_birth",
+ "pincode",
+ "nationality",
+ "address",
+ "permanent_address",
+ ];
+
+ if (form.nationality === "India") {
+ requiredFields.push("state", "district", "local_body");
+ }
+
+ requiredFields.forEach((field) => {
+ if (!form[field]) {
+ errors[field] = errors[field] || [];
+ errors[field].push(`This field is required`);
+ } else if (
+ ageDob === "dob" &&
+ field === "date_of_birth" &&
+ !/^(19[0-9]{2}|20[0-9]{2}|2100)-(0?[1-9]|1[0-2])-(0?[1-9]|[12]\d|3[01])$/.test(
+ form[field],
+ )
+ ) {
+ errors[field] = errors[field] || [];
+ errors[field].push(t("invalid_date_format", { format: "DD-MM-YYYY" }));
+ } else if (
+ (field === "phone_number" || field === "emergency_phone_number") &&
+ form[field]?.length < 13
+ ) {
+ errors[field] = errors[field] || [];
+ errors[field].push(t("phone_number_min_error"));
+ }
+ });
+
+ if (Object.keys(errors).length > 0) {
+ setFeErrors(errors);
+ Notification.Error({
+ msg: t("please_fix_errors"),
+ });
+ } else {
+ patientId
+ ? updatePatientMutation.mutate()
+ : createPatientMutation.mutate();
+ }
+ };
+
+ const [debouncedNumber, setDebouncedNumber] = useState();
+
+ useEffect(() => {
+ const handler = setTimeout(() => {
+ if (!patientId || patientQuery.data?.phone_number !== form.phone_number) {
+ setSuppressDuplicateWarning(false);
+ }
+ setDebouncedNumber(form.phone_number);
+ }, 500);
+
+ return () => {
+ clearTimeout(handler);
+ };
+ }, [form.phone_number]);
+
+ const patientPhoneSearch = useQuery({
+ queryKey: ["patients", "phone-number", debouncedNumber],
+ queryFn: query(routes.searchPatient, {
+ queryParams: {
+ phone_number: parsePhoneNumber(debouncedNumber || "") || "",
+ },
+ }),
+ enabled: !!parsePhoneNumber(debouncedNumber || ""),
+ });
+
+ const duplicatePatients = patientPhoneSearch.data?.results.filter(
+ (p) => p.patient_id !== patientId,
+ );
+ if (patientId && patientQuery.isLoading) {
+ return ;
+ }
+
+ return (
+
+
+
+ {!patientPhoneSearch.isLoading &&
+ !!duplicatePatients?.length &&
+ !!parsePhoneNumber(debouncedNumber || "") &&
+ !suppressDuplicateWarning && (
+ {
+ handleDialogClose("close");
+ }}
+ />
+ )}
+ {!!duplicatePatients?.length && (
+ {
+ handleDialogClose("close");
+ }}
+ title="Patient Transfer Form"
+ className="max-w-md md:min-w-[600px]"
+ >
+ handleDialogClose("close")}
+ handleCancel={() => {
+ handleDialogClose("close");
+ }}
+ facilityId={facilityId}
+ />
+
+ )}
+
+ );
+}
diff --git a/src/components/Patient/Utils.ts b/src/components/Patient/Utils.ts
index 1f3be3162ef..d1998749432 100644
--- a/src/components/Patient/Utils.ts
+++ b/src/components/Patient/Utils.ts
@@ -17,3 +17,21 @@ export function isPatientMandatoryDataFilled(patient: PatientModel) {
patient.blood_group
);
}
+
+export const getPatientUrl = (patient: PatientModel) => {
+ let patientUrl = "";
+ if (!isPatientMandatoryDataFilled(patient)) {
+ patientUrl = `/facility/${patient.facility}/patient/${patient.id}`;
+ } else if (
+ patient.last_consultation &&
+ patient.last_consultation?.facility === patient.facility &&
+ !(patient.last_consultation?.discharge_date && patient.is_active)
+ ) {
+ patientUrl = `/facility/${patient.facility}/patient/${patient.id}/consultation/${patient.last_consultation.id}`;
+ } else if (patient.facility) {
+ patientUrl = `/facility/${patient.facility}/patient/${patient.id}`;
+ } else {
+ patientUrl = `/patient/${patient.id}`;
+ }
+ return patientUrl;
+};
diff --git a/src/components/Patient/models.tsx b/src/components/Patient/models.tsx
index dc3a0f5f681..93568ac220b 100644
--- a/src/components/Patient/models.tsx
+++ b/src/components/Patient/models.tsx
@@ -138,7 +138,6 @@ export interface PatientModel {
created_by?: UserBareMinimum;
assigned_to?: number | null;
assigned_to_object?: AssignedToObjectModel;
- occupation?: Occupation;
meta_info?: PatientMeta;
age?: string;
}
diff --git a/src/components/ui/autocomplete.tsx b/src/components/ui/autocomplete.tsx
index 955141f096b..c7b847c1391 100644
--- a/src/components/ui/autocomplete.tsx
+++ b/src/components/ui/autocomplete.tsx
@@ -18,13 +18,18 @@ import {
PopoverTrigger,
} from "@/components/ui/popover";
+interface AutoCompleteOption {
+ label: string;
+ value: string;
+}
+
interface AutocompleteProps {
- options: string[];
+ options: AutoCompleteOption[];
value: string;
onChange: (value: string) => void;
- onSearch: (value: string) => void;
placeholder?: string;
noOptionsMessage?: string;
+ disabled?: boolean;
}
export default function Autocomplete({
@@ -33,6 +38,7 @@ export default function Autocomplete({
onChange,
placeholder = "Select...",
noOptionsMessage = "No options found",
+ disabled,
}: AutocompleteProps) {
const [open, setOpen] = React.useState(false);
@@ -43,23 +49,31 @@ export default function Autocomplete({
variant="outline"
role="combobox"
aria-expanded={open}
- className="w-full justify-between"
+ className="w-full justify-between overflow-hidden"
+ disabled={disabled}
>
- {value ? options.find((option) => option === value) : placeholder}
+ {value
+ ? options.find((option) => option.value === value)?.label
+ : placeholder}
-
+
{noOptionsMessage}
{options.map((option) => (
{
+ key={option.value}
+ value={option.label}
+ onSelect={(v) => {
+ const currentValue =
+ options.find(
+ (option) =>
+ option.label.toLowerCase() === v.toLowerCase(),
+ )?.value || "";
onChange(currentValue === value ? "" : currentValue);
setOpen(false);
}}
@@ -67,10 +81,10 @@ export default function Autocomplete({
- {option}
+ {option.label}
))}
diff --git a/src/components/ui/checkbox.tsx b/src/components/ui/checkbox.tsx
index a2e7ee8c61d..e6e6cfe0326 100644
--- a/src/components/ui/checkbox.tsx
+++ b/src/components/ui/checkbox.tsx
@@ -11,7 +11,7 @@ const Checkbox = React.forwardRef<
+ {errors?.map((error, i) => (
+
+ {error}
+
+ ))}
+
+ ) : null;
+}
diff --git a/src/components/ui/form-field.tsx b/src/components/ui/form-field.tsx
new file mode 100644
index 00000000000..f8894419623
--- /dev/null
+++ b/src/components/ui/form-field.tsx
@@ -0,0 +1,24 @@
+import { InputErrors } from "./errors";
+import { Label } from "./label";
+
+export default function FormField(props: {
+ label?: string;
+ required?: boolean;
+ errors?: string[];
+ children: React.ReactNode;
+}) {
+ const { label, errors, children, required } = props;
+
+ return (
+ <>
+ {label && (
+
+ {label}
+ {required && * }
+
+ )}
+ {children}
+
+ >
+ );
+}
diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx
index d335de80adf..b08142d72c6 100644
--- a/src/components/ui/tabs.tsx
+++ b/src/components/ui/tabs.tsx
@@ -50,4 +50,26 @@ const TabsContent = React.forwardRef<
));
TabsContent.displayName = TabsPrimitive.Content.displayName;
-export { Tabs, TabsList, TabsTrigger, TabsContent };
+interface SectionTabsProps {
+ activeTab: string;
+ onChange: (newTab: string) => void;
+ tabs: { label: string; value: string }[];
+}
+
+const SectionTabs = (props: SectionTabsProps) => (
+
+
+ {props.tabs.map(({ label, value }) => (
+
+ {label}
+
+ ))}
+
+
+);
+
+export { Tabs, TabsList, TabsTrigger, TabsContent, SectionTabs };
diff --git a/src/hooks/useFilters.tsx b/src/hooks/useFilters.tsx
index 63ffa58822e..8486a4dc622 100644
--- a/src/hooks/useFilters.tsx
+++ b/src/hooks/useFilters.tsx
@@ -209,7 +209,7 @@ export default function useFilters({
return (
{compiledBadges.map((props) => (
diff --git a/src/pages/Facility/FacilitiesPage.tsx b/src/pages/Facility/FacilitiesPage.tsx
index fdc7b930f9a..a1b1ae8f049 100644
--- a/src/pages/Facility/FacilitiesPage.tsx
+++ b/src/pages/Facility/FacilitiesPage.tsx
@@ -69,7 +69,6 @@ export function FacilitiesPage() {
options={[
{
key: "facility_district_pincode",
- label: "Facility/District/Pincode",
type: "text" as const,
placeholder: "facility_search_placeholder_pincode",
value: qParams.search || "",
diff --git a/src/pages/Facility/FacilityDetailsPage.tsx b/src/pages/Facility/FacilityDetailsPage.tsx
index d624afe31dd..615196736e6 100644
--- a/src/pages/Facility/FacilityDetailsPage.tsx
+++ b/src/pages/Facility/FacilityDetailsPage.tsx
@@ -17,7 +17,7 @@ import routes from "@/Utils/request/api";
import request from "@/Utils/request/request";
import { PaginatedResponse, RequestResult } from "@/Utils/request/types";
-import { DoctorModel, FeatureBadge } from "./Utils";
+import { FeatureBadge } from "./Utils";
import { UserCard } from "./components/UserCard";
interface Props {
diff --git a/src/pluginTypes.ts b/src/pluginTypes.ts
index 91c055f3901..b8200ee4cfd 100644
--- a/src/pluginTypes.ts
+++ b/src/pluginTypes.ts
@@ -8,10 +8,12 @@ import { AppRoutes } from "./Routers/AppRouter";
import { ConsultationTabProps } from "./components/Facility/ConsultationDetails";
import { FormContextValue } from "./components/Form/FormContext";
import { PatientInfoCardProps } from "./components/Patient/PatientInfoCard";
-import { PatientForm } from "./components/Patient/PatientRegister";
-import { PatientModel } from "./components/Patient/models";
+import { PatientMeta, PatientModel } from "./components/Patient/models";
import { pluginMap } from "./pluginMap";
+export type PatientForm = PatientModel &
+ PatientMeta & { age?: number; is_postpartum?: boolean };
+
export type DoctorConnectButtonComponentType = React.FC<{
user: UserAssignedModel;
}>;