diff --git a/src/components/Encounter/EncounterActions.tsx b/src/components/Encounter/EncounterActions.tsx new file mode 100644 index 00000000000..5919290ddfc --- /dev/null +++ b/src/components/Encounter/EncounterActions.tsx @@ -0,0 +1,202 @@ +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { Link } from "raviger"; +import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; + +import { cn } from "@/lib/utils"; + +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "@/components/ui/alert-dialog"; +import { Button, buttonVariants } from "@/components/ui/button"; +import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; + +import { PLUGIN_Component } from "@/PluginEngine"; +import routes from "@/Utils/request/api"; +import mutate from "@/Utils/request/mutate"; +import { Encounter, completedEncounterStatus } from "@/types/emr/encounter"; + +interface EncounterActionsProps { + encounter: Encounter; + variant?: "default" | "outline" | "ghost"; + size?: "default" | "sm" | "lg" | "icon"; + disableButtons?: boolean; + className?: string; + layout?: "dropdown" | "standalone"; +} + +export default function EncounterActions({ + encounter, + variant = "outline", + size = "default", + disableButtons = false, + className, + layout = "standalone", +}: EncounterActionsProps) { + const { t } = useTranslation(); + const queryClient = useQueryClient(); + + const { mutate: updateEncounter } = useMutation({ + mutationFn: mutate(routes.encounter.update, { + pathParams: { id: encounter.id }, + }), + onSuccess: () => { + toast.success(t("encounter_marked_as_complete")); + queryClient.invalidateQueries({ queryKey: ["encounter", encounter.id] }); + }, + onError: () => { + toast.error(t("error_updating_encounter")); + }, + }); + + const handleMarkAsComplete = () => { + updateEncounter({ + ...encounter, + status: "completed", + organizations: encounter.organizations.map((org) => org.id), + patient: encounter.patient.id, + encounter_class: encounter.encounter_class, + period: encounter.period, + hospitalization: encounter.hospitalization, + priority: encounter.priority, + external_identifier: encounter.external_identifier, + facility: encounter.facility.id, + }); + }; + + if (completedEncounterStatus.includes(encounter.status) || disableButtons) { + return null; + } + + const ActionItems = () => { + if (layout === "dropdown") { + return ( + <> + + + {t("treatment_summary")} + + + + + {t("discharge_summary")} + + + + + e.preventDefault()}> + {t("mark_as_complete")} + + + + + {t("mark_as_complete")} + + {t("mark_encounter_as_complete_confirmation")} + + + + + + + {t("cancel")} + + {t("mark_as_complete")} + + + + + + ); + } + + return ( +
+ + + + + + + + + {t("mark_as_complete")} + + {t("mark_encounter_as_complete_confirmation")} + + + + + + + {t("cancel")} + + {t("mark_as_complete")} + + + + + +
+ ); + }; + + return ActionItems(); +} diff --git a/src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx b/src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx index 4f9c245594a..bba948cfd72 100644 --- a/src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx +++ b/src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx @@ -1,14 +1,11 @@ import { useQuery } from "@tanstack/react-query"; import { t } from "i18next"; -import { Link, useQueryParams } from "raviger"; +import { useQueryParams } from "raviger"; import { Trans, useTranslation } from "react-i18next"; import { cn } from "@/lib/utils"; -import CareIcon from "@/CAREUI/icons/CareIcon"; - import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { Separator } from "@/components/ui/separator"; @@ -297,8 +294,8 @@ function ResponseCard({ } export default function QuestionnaireResponsesList({ + encounter, patientId, - facilityId, isPrintPreview = false, onlyUnstructured, }: Props) { @@ -314,8 +311,9 @@ export default function QuestionnaireResponsesList({ limit: RESULTS_PER_PAGE_LIMIT, offset: ((qParams.page ?? 1) - 1) * RESULTS_PER_PAGE_LIMIT, }), + encounter: encounter?.id, only_unstructured: onlyUnstructured, - subject_type: "patient", + subject_type: encounter ? "encounter" : "patient", }, maxPages: isPrintPreview ? undefined : 1, pageSize: isPrintPreview ? 100 : RESULTS_PER_PAGE_LIMIT, @@ -325,23 +323,6 @@ export default function QuestionnaireResponsesList({ return (
-
-
- {t("updates")} -
- -
{isLoading ? (
diff --git a/src/components/Facility/ConsultationDetails/QuickAccess.tsx b/src/components/Facility/ConsultationDetails/QuickAccess.tsx index 649bfe6ef8b..6c23a0b90ed 100644 --- a/src/components/Facility/ConsultationDetails/QuickAccess.tsx +++ b/src/components/Facility/ConsultationDetails/QuickAccess.tsx @@ -6,6 +6,7 @@ import CareIcon from "@/CAREUI/icons/CareIcon"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; +import EncounterActions from "@/components/Encounter/EncounterActions"; import LinkDepartmentsSheet from "@/components/Patient/LinkDepartmentsSheet"; import useQuestionnaireOptions from "@/hooks/useQuestionnaireOptions"; @@ -44,6 +45,15 @@ export default function QuickAccess({ encounter }: QuickAccessProps) { )} + {/* Encounter Actions */} +
+

+ {t("actions")} +

+ + +
+ {/* Departments and Teams */}
diff --git a/src/components/Patient/PatientDetailsTab/index.tsx b/src/components/Patient/PatientDetailsTab/index.tsx index 8b5e14145e7..05a45162d0d 100644 --- a/src/components/Patient/PatientDetailsTab/index.tsx +++ b/src/components/Patient/PatientDetailsTab/index.tsx @@ -1,4 +1,3 @@ -import QuestionnaireResponsesList from "@/components/Facility/ConsultationDetails/QuestionnaireResponsesList"; import EncounterHistory from "@/components/Patient/PatientDetailsTab//EncounterHistory"; import { HealthProfileSummary } from "@/components/Patient/PatientDetailsTab//HealthProfileSummary"; import { Demography } from "@/components/Patient/PatientDetailsTab/Demography"; @@ -9,6 +8,7 @@ import { Appointments } from "./Appointments"; import { PatientFilesTab } from "./PatientFiles"; import { PatientUsers } from "./PatientUsers"; import { ResourceRequests } from "./ResourceRequests"; +import { Updates } from "./patientUpdates"; export interface PatientProps { facilityId?: string; @@ -35,7 +35,7 @@ export const patientTabs = [ }, { route: "updates", - component: QuestionnaireResponsesList, + component: Updates, }, { route: "resource_requests", diff --git a/src/components/Patient/PatientDetailsTab/patientUpdates.tsx b/src/components/Patient/PatientDetailsTab/patientUpdates.tsx new file mode 100644 index 00000000000..6e387b8e26a --- /dev/null +++ b/src/components/Patient/PatientDetailsTab/patientUpdates.tsx @@ -0,0 +1,36 @@ +import { Link } from "raviger"; +import { useTranslation } from "react-i18next"; + +import CareIcon from "@/CAREUI/icons/CareIcon"; + +import { Button } from "@/components/ui/button"; + +import QuestionnaireResponsesList from "@/components/Facility/ConsultationDetails/QuestionnaireResponsesList"; + +import { PatientProps } from "."; + +export const Updates = (props: PatientProps) => { + const { patientId, facilityId } = props; + const { t } = useTranslation(); + + return ( +
+
+

{t("updates")}

+ +
+ +
+ ); +}; diff --git a/src/components/Patient/PatientInfoCard.tsx b/src/components/Patient/PatientInfoCard.tsx index 71c0f1b51a8..81d44f6a34f 100644 --- a/src/components/Patient/PatientInfoCard.tsx +++ b/src/components/Patient/PatientInfoCard.tsx @@ -1,4 +1,3 @@ -import { useMutation, useQueryClient } from "@tanstack/react-query"; import { BedSingle, Building, @@ -10,29 +9,16 @@ import { } from "lucide-react"; import { Link } from "raviger"; import { useTranslation } from "react-i18next"; -import { toast } from "sonner"; import { cn } from "@/lib/utils"; import CareIcon from "@/CAREUI/icons/CareIcon"; -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from "@/components/ui/alert-dialog"; import { Badge } from "@/components/ui/badge"; -import { Button, buttonVariants } from "@/components/ui/button"; +import { Button } from "@/components/ui/button"; import { DropdownMenu, DropdownMenuContent, - DropdownMenuItem, DropdownMenuLabel, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; @@ -43,14 +29,13 @@ import { } from "@/components/ui/popover"; import { Avatar } from "@/components/Common/Avatar"; +import EncounterActions from "@/components/Encounter/EncounterActions"; import { LocationSheet } from "@/components/Location/LocationSheet"; import { LocationTree } from "@/components/Location/LocationTree"; import LinkDepartmentsSheet from "@/components/Patient/LinkDepartmentsSheet"; import { PLUGIN_Component } from "@/PluginEngine"; import dayjs from "@/Utils/dayjs"; -import routes from "@/Utils/request/api"; -import mutate from "@/Utils/request/mutate"; import { formatDateTime, formatPatientAge } from "@/Utils/utils"; import { Encounter, @@ -70,35 +55,6 @@ export interface PatientInfoCardProps { export default function PatientInfoCard(props: PatientInfoCardProps) { const { patient, encounter, disableButtons = false } = props; const { t } = useTranslation(); - const queryClient = useQueryClient(); - - const { mutate: updateEncounter } = useMutation({ - mutationFn: mutate(routes.encounter.update, { - pathParams: { id: encounter.id }, - }), - onSuccess: () => { - toast.success(t("encounter_marked_as_complete")); - queryClient.invalidateQueries({ queryKey: ["encounter", encounter.id] }); - }, - onError: () => { - toast.error(t("error_updating_encounter")); - }, - }); - - const handleMarkAsComplete = () => { - updateEncounter({ - ...encounter, - status: "completed", - organizations: encounter.organizations.map((org) => org.id), - patient: encounter.patient.id, - encounter_class: encounter.encounter_class, - period: encounter.period, - hospitalization: encounter.hospitalization, - priority: encounter.priority, - external_identifier: encounter.external_identifier, - facility: encounter.facility.id, - }); - }; return ( <> @@ -475,71 +431,22 @@ export default function PatientInfoCard(props: PatientInfoCardProps) { className="flex w-full flex-col gap-3 lg:w-auto 2xl:flex-row" data-cy="update-encounter-button" > - - - - - - - {t("actions")} - - - {t("treatment_summary")} - - - - - {t("discharge_summary")} - - - - e.preventDefault()}> - {t("mark_as_complete")} - - - - - - - - - {t("mark_as_complete")} - - - {t("mark_encounter_as_complete_confirmation")} - - - + + + + + + {t("actions")} + - - - {t("cancel")} - - - {t("mark_as_complete")} - - - - + +
)}
diff --git a/src/components/Resource/ResourceForm.tsx b/src/components/Resource/ResourceForm.tsx index fa443f489e0..ed98ad5c380 100644 --- a/src/components/Resource/ResourceForm.tsx +++ b/src/components/Resource/ResourceForm.tsx @@ -76,7 +76,6 @@ export default function ResourceForm({ facilityId, id }: ResourceProps) { name: z.string(), }) .nullable(), - assigned_to: z.string().min(1, { message: t("field_required") }), emergency: z.enum(["true", "false"]), title: z.string().min(1, { message: t("field_required") }), reason: z.string().min(1, { message: t("field_required") }), @@ -85,6 +84,9 @@ export default function ResourceForm({ facilityId, id }: ResourceProps) { .min(1, { message: t("field_required") }), referring_facility_contact_number: validators().phoneNumber.required, priority: z.number().default(1), + assigned_to: id + ? z.string().min(1, { message: t("field_required") }) + : z.string().optional(), }); type ResourceFormValues = z.infer; @@ -409,24 +411,27 @@ export default function ResourceForm({ facilityId, id }: ResourceProps) { )} /> - ( - - {t("assigned_to")} - - - - - - )} - /> + + {id && ( + ( + + {t("assigned_to")} + + + + + + )} + /> + )}
diff --git a/src/components/Users/UserForm.tsx b/src/components/Users/UserForm.tsx index ee78d887d9f..3e0051ce923 100644 --- a/src/components/Users/UserForm.tsx +++ b/src/components/Users/UserForm.tsx @@ -119,7 +119,7 @@ export default function UserForm({ const form = useForm({ resolver: zodResolver(userFormSchema), defaultValues: { - user_type: "staff", + user_type: "nurse", username: "", password: "", c_password: "", diff --git a/src/components/ui/markdown.tsx b/src/components/ui/markdown.tsx index 828f304ce9c..ff70de30b0a 100644 --- a/src/components/ui/markdown.tsx +++ b/src/components/ui/markdown.tsx @@ -9,7 +9,7 @@ const md = new MarkdownIt({ breaks: true, linkify: true, typographer: true, - quotes: ['""', "''"], + quotes: "“”‘’", }); export interface MarkdownProps extends React.HTMLAttributes { diff --git a/src/pages/Appointments/AppointmentDetail.tsx b/src/pages/Appointments/AppointmentDetail.tsx index ba3b6e4a044..929f6468740 100644 --- a/src/pages/Appointments/AppointmentDetail.tsx +++ b/src/pages/Appointments/AppointmentDetail.tsx @@ -308,9 +308,24 @@ const AppointmentDetails = ({
-

{appointment.patient.phone_number}

+

+ + {appointment.patient.phone_number} + +

- {t("emergency")}: {appointment.patient.emergency_phone_number} + {t("emergency")}:{" "} + {appointment.patient.emergency_phone_number && ( + + {appointment.patient.emergency_phone_number} + + )}