diff --git a/src/components/Patient/ManagePatients.tsx b/src/components/Patient/ManagePatients.tsx index 6fba4d6e734..f16a427c922 100644 --- a/src/components/Patient/ManagePatients.tsx +++ b/src/components/Patient/ManagePatients.tsx @@ -1,16 +1,40 @@ import dayjs from "dayjs"; import { Link, navigate } from "raviger"; -import { ReactNode, useCallback, useEffect, useState } from "react"; +import { ReactNode, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; +import Chip from "@/CAREUI/display/Chip"; +import CountBlock from "@/CAREUI/display/Count"; +import FilterBadge from "@/CAREUI/display/FilterBadge"; +import RecordMeta from "@/CAREUI/display/RecordMeta"; +import CareIcon from "@/CAREUI/icons/CareIcon"; +import { AdvancedFilterButton } from "@/CAREUI/interactive/FiltersSlideover"; + +import { Badge } from "@/components/ui/badge"; + 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 { ICD11DiagnosisModel } from "@/components/Diagnosis/types"; +import { getDiagnosesByIds } from "@/components/Diagnosis/utils"; +import FacilitiesSelectDialogue from "@/components/ExternalResult/FacilitiesSelectDialogue"; +import DoctorVideoSlideover from "@/components/Facility/DoctorVideoSlideover"; +import { FacilityModel, PatientCategory } from "@/components/Facility/models"; +import { PhoneNumberValidator } from "@/components/Form/FieldValidators"; +import PhoneNumberFormField from "@/components/Form/FormFields/PhoneNumberFormField"; +import { FieldChangeEvent } from "@/components/Form/FormFields/Utils"; +import SearchInput from "@/components/Form/SearchInput"; +import { + DIAGNOSES_FILTER_LABELS, + DiagnosesFilterKey, + FILTER_BY_DIAGNOSES_KEYS, +} from "@/components/Patient/DiagnosesFilter"; +import PatientFilter from "@/components/Patient/PatientFilter"; +import { isPatientMandatoryDataFilled } from "@/components/Patient/Utils"; import useAuthUser from "@/hooks/useAuthUser"; import useFilters from "@/hooks/useFilters"; @@ -27,36 +51,17 @@ import { } from "@/common/constants"; import { parseOptionId } from "@/common/utils"; +import { triggerGoal } from "@/Integrations/Plausible"; +import * as Notification from "@/Utils/Notifications"; import routes from "@/Utils/request/api"; - -import Chip from "../../CAREUI/display/Chip"; -import CountBlock from "../../CAREUI/display/Count"; -import FilterBadge from "../../CAREUI/display/FilterBadge"; -import RecordMeta from "../../CAREUI/display/RecordMeta"; -import CareIcon from "../../CAREUI/icons/CareIcon"; -import { AdvancedFilterButton } from "../../CAREUI/interactive/FiltersSlideover"; -import { triggerGoal } from "../../Integrations/Plausible"; -import * as Notification from "../../Utils/Notifications"; -import request from "../../Utils/request/request"; -import useQuery from "../../Utils/request/useQuery"; +import request from "@/Utils/request/request"; +import useQuery from "@/Utils/request/useQuery"; import { formatPatientAge, humanizeStrings, isAntenatal, parsePhoneNumber, -} from "../../Utils/utils"; -import { ICD11DiagnosisModel } from "../Diagnosis/types"; -import { getDiagnosesByIds } from "../Diagnosis/utils"; -import FacilitiesSelectDialogue from "../ExternalResult/FacilitiesSelectDialogue"; -import DoctorVideoSlideover from "../Facility/DoctorVideoSlideover"; -import { FacilityModel, PatientCategory } from "../Facility/models"; -import { - DIAGNOSES_FILTER_LABELS, - DiagnosesFilterKey, - FILTER_BY_DIAGNOSES_KEYS, -} from "./DiagnosesFilter"; -import PatientFilter from "./PatientFilter"; -import { isPatientMandatoryDataFilled } from "./Utils"; +} from "@/Utils/utils"; interface TabPanelProps { children?: ReactNode; @@ -90,7 +95,6 @@ export const PatientManager = () => { Pagination, FilterBadges, resultsPerPage, - clearSearch, } = useFilters({ limit: 12, cacheBlacklist: [ @@ -107,6 +111,30 @@ export const PatientManager = () => { const [diagnoses, setDiagnoses] = useState([]); const [showDialog, setShowDialog] = useState<"create" | "list-discharged">(); const [showDoctors, setShowDoctors] = useState(false); + const [phoneNumber, _setPhoneNumber] = useState(""); + const [emergencyPhoneNumber, _setEmergencyPhoneNumber] = useState(""); + + const setPhoneNumber = (value: string) => { + _setPhoneNumber(value); + const error = PhoneNumberValidator()(value); + if (!error) { + updateQuery({ phone_number: value }); + } + if ((value === "+91" || value === "") && qParams.phone_number) { + updateQuery({ phone_number: null }); + } + }; + + const setEmergencyPhoneNumber = (value: string) => { + _setEmergencyPhoneNumber(value); + const error = PhoneNumberValidator()(value); + if (!error) { + updateQuery({ emergency_phone_number: value }); + } + if ((value === "+91" || value === "") && qParams.emergency_phone_number) { + updateQuery({ emergency_phone_number: null }); + } + }; const tabValue = qParams.last_consultation__new_discharge_reason || @@ -293,6 +321,14 @@ export const PatientManager = () => { const { loading: isLoading, data } = useQuery(routes.patientList, { query: params, + onResponse: () => { + if (!params.phone_number) { + _setPhoneNumber("+91"); + } + if (!params.emergency_phone_number) { + _setEmergencyPhoneNumber("+91"); + } + }, }); const getTheCategoryFromId = () => { @@ -596,15 +632,26 @@ export const PatientManager = () => { )} {patient.review_time && !patient.last_consultation?.discharge_date && - Number(patient.last_consultation?.review_interval) > 0 && - dayjs().isAfter(patient.review_time) && ( - + Number(patient.last_consultation?.review_interval) > 0 && ( + + + {dayjs().isAfter(patient.review_time) + ? `Review Missed ${Math.abs( + dayjs().diff(dayjs(patient.review_time), "days"), + )} days ago` + : `Review Due in ${Math.abs( + dayjs(patient.review_time).diff(dayjs(), "days"), + )} days`} + )} + {patient.last_consultation?.is_readmission && ( { ); } + const queryField = (name: string, defaultValue?: T) => { + return { + name, + value: qParams[name] || defaultValue, + onChange: (e: FieldChangeEvent) => updateQuery({ [e.name]: e.value }), + }; + }; + const onlyAccessibleFacility = permittedFacilities?.count === 1 ? permittedFacilities.results[0] : null; - const searchOptions = [ - { - key: "phone_number", - label: "Phone Number", - type: "phone" as const, - placeholder: "Search_by_phone_number", - value: qParams.phone_number || "", - shortcutKey: "p", - }, - { - key: "name", - label: "Name", - type: "text" as const, - placeholder: "search_by_patient_name", - value: qParams.name || "", - shortcutKey: "n", - }, - { - key: "patient_no", - label: "IP/OP No", - type: "text" as const, - placeholder: "search_by_patient_no", - value: qParams.patient_no || "", - shortcutKey: "u", - }, - { - key: "emergency_contact_number", - label: "Emergency Contact Phone Number", - type: "phone" as const, - placeholder: "search_by_emergency_phone_number", - value: qParams.emergency_phone_number || "", - shortcutKey: "e", - }, - ]; - - 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], - ); - return (
@@ -991,23 +986,59 @@ export const PatientManager = () => { }} /> -
-
- +
+
+
+ +
+
+
+
+
+ + +
+
+ setPhoneNumber(e.value)} + types={["mobile", "landline"]} + className="w-full grow" + error={((phoneNumber || "+91") === "+91" && "") || undefined} + /> + setEmergencyPhoneNumber(e.value)} + types={["mobile", "landline"]} + className="w-full" + error={ + ((emergencyPhoneNumber || "+91") === "+91" && "") || undefined + } + /> +
+
- -
{ />
- +
{managePatients}
diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx new file mode 100644 index 00000000000..f2188660576 --- /dev/null +++ b/src/components/ui/badge.tsx @@ -0,0 +1,56 @@ +import { type VariantProps, cva } from "class-variance-authority"; +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +/** + * Badge component for displaying status indicators. + * @param variant - The visual style variant of the badge + * @param className - Additional CSS classes to apply + * // Usage for review status + * Review Missed + * Review Due in 2 days + */ + + +const badgeVariants = cva( + "inline-flex items-center rounded-md border border-gray-200 px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-gray-950 focus:ring-offset-2 dark:border-gray-800 dark:focus:ring-gray-300", + { + variants: { + variant: { + default: + "border-transparent bg-gray-900 text-gray-50 shadow hover:bg-gray-900/80 dark:bg-gray-50 dark:text-gray-900 dark:hover:bg-gray-50/80", + secondary: + "border-transparent bg-gray-100 text-gray-900 hover:bg-gray-100/80 dark:bg-gray-800 dark:text-gray-50 dark:hover:bg-gray-800/80", + destructive: + "border-transparent bg-red-500 text-gray-50 shadow hover:bg-red-500/80 dark:bg-red-900 dark:text-gray-50 dark:hover:bg-red-900/80", + outline: "text-gray-950 dark:text-gray-50", + purple: + "border-transparent bg-purple-200 text-purple-800 shadow hover:bg-purple-300 dark:bg-purple-900 dark:text-purple-100 dark:hover:bg-purple-900", + success: + "border-transparent bg-green-500 text-gray-50 shadow hover:bg-green-500/80 dark:bg-green-900 dark:text-gray-50 dark:hover:bg-green-900/80", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ); +} + +export { Badge, badgeVariants }; +