Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Discharge Summary #10570

Merged
merged 7 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -744,14 +744,18 @@
"discharge_disposition": "Discharge Disposition",
"discharge_from_care": "Discharge from CARE",
"discharge_prescription": "Discharge Prescription",
"discharge_summaries": "Discharge Summaries",
"discharge_summary": "Discharge Summary",
"discharge_summary_emailed": "Discharged Summary emailed",
"discharge_summary_generated": "Discharge Summary generated",
"discharge_summary_not_ready": "Discharge summary is not ready yet.",
"discharged": "Discharged",
"discharged_on": "Discharged On",
"discharged_patients": "Discharged Patients",
"discharged_patients_empty": "No discharged patients present in this facility",
"discharged_to": "Discharged to",
"disclaimer": "Disclaimer",
"disclaimer_computer_generated_summary": "Disclaimer: This is a computer generated summary and may not be 100% accurate. Please verify the details before using it.",
"discontinue": "Discontinue",
"discontinue_caution_note": "Are you sure you want to discontinue this prescription?",
"discontinued": "Discontinued",
Expand All @@ -777,7 +781,7 @@
"dosage_instructions": "Dosage Instructions",
"down": "Down",
"download": "Download",
"download_discharge_summary": "Download discharge summary",
"download_discharge_summary": "Download Discharge Summary",
"download_type": "Download Type",
"downloading": "Downloading",
"downloading_abha_card": "Generating ABHA Card, Please hold on",
Expand Down Expand Up @@ -961,6 +965,7 @@
"enter_valid_dob": "Enter a valid date of birth",
"enter_valid_dob_age": "Please enter an age greater than 15 years",
"enter_year_of_birth_to_verify": "Enter year of birth to verify",
"enter_your_valid_email_address_to_receive_the_discharge_summary": "Enter your valid email address to receive the discharge summary",
"entered_in_error": "Entered in Error",
"entered_in_error_warning": "This action cannot be undone. The appointment will be marked as entered in error and removed from the system.",
"entity_count_one": "{{count}} {{entity}}",
Expand All @@ -974,6 +979,7 @@
"error_fetching_user_data": "Error while fetching user data",
"error_fetching_user_details": "Error while fetching user details: ",
"error_fetching_users_data": "Failed to load user data. Please try again later.",
"error_generating_discharge_summary": "Error generating discharge summary",
"error_loading_questionnaire_response": "Error loading questionnaire response",
"error_updating_encounter": "Error to Updating Encounter",
"error_verifying_otp": "Error while verifying OTP, Please request a new OTP",
Expand All @@ -983,6 +989,7 @@
"etiology_identified": "Etiology identified",
"evening_slots": "Evening Slots",
"events": "Events",
"example_email_address": "example@email.com",
"exception": "Exception",
"exception_created": "Exception created successfully",
"exception_deleted": "Exception deleted",
Expand Down Expand Up @@ -1088,6 +1095,8 @@
"gender_is_required": "Gender is required",
"general": "General",
"general_info_detail": "Provide the patient's personal details, including name, date of birth, gender, and contact information for accurate identification and communication.",
"generate": "Generate",
"generate_discharge_summary": "Generate Discharge Summary",
"generate_link_abha": "Generate/Link ABHA Number",
"generate_report": "Generate Report",
"generated_on": "Generated on:",
Expand Down Expand Up @@ -1411,6 +1420,7 @@
"no_data_found": "No data found",
"no_departments_teams_found": "No Departments or Teams found",
"no_diagnoses_recorded": "No diagnoses recorded",
"no_discharge_summaries_found": "No Discharge Summaries found",
"no_doctors_found": "No Doctors Found",
"no_duplicate_facility": "You should not create duplicate facilities",
"no_encounters_found": "No encounters found",
Expand Down Expand Up @@ -1656,6 +1666,7 @@
"please_enter_confirm_password": "Please confirm your new password",
"please_enter_correct_birth_year": "Please enter the correct birth year to verify the patient details.",
"please_enter_current_password": "Please enter your current password.",
"please_enter_email_address": "Please enter email address",
"please_enter_new_password": "Please enter your new password.",
"please_enter_organization_name": "Please enter an organization name",
"please_enter_username": "Please enter the username",
Expand Down Expand Up @@ -1777,7 +1788,9 @@
"referred_to": "Referred to",
"refresh": "Refresh",
"refresh_list": "Refresh List",
"refreshed": "Refreshed",
"refuted": "Refuted",
"regenerate_discharge_summary": "Regenerate Discharge Summary",
"register_hospital": "Register Hospital",
"register_page_title": "Register As Hospital Administrator",
"register_patient": "Register Patient",
Expand Down Expand Up @@ -2020,6 +2033,7 @@
"send_reset_link": "Send Reset Link",
"send_sample_to_collection_centre_description": "Are you sure you want to send the sample to Collection Centre?",
"send_sample_to_collection_centre_title": "Send sample to collection centre",
"sending": "Sending",
"serial_number": "Serial Number",
"serviced_on": "Serviced on",
"session_capacity": "Session Capacity",
Expand Down
13 changes: 13 additions & 0 deletions src/Routers/routes/ConsultationRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ const consultationRoutes: AppRoutes = {
tab={tab}
/>
),
"/facility/:facilityId/encounter/:encounterId/:tab/:subPage": ({
facilityId,
encounterId,
tab,
subPage,
}) => (
<EncounterShow
facilityId={facilityId}
encounterId={encounterId}
tab={tab}
subPage={subPage}
/>
),
"/facility/:facilityId/patient/:patientId/consultation": ({
facilityId,
patientId,
Expand Down
5 changes: 5 additions & 0 deletions src/Utils/request/api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,11 @@ const routes = {
TRes: Type<Encounter>(),
TBody: Type<{ organization: string }>(),
},
generateDischargeSummary: {
path: "/api/v1/encounter/{encounterId}/generate_discharge_summary/",
method: "POST",
TRes: Type<{ detail: string }>(),
},
},

// New Patient Routes
Expand Down
22 changes: 15 additions & 7 deletions src/Utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ import dayjs from "@/Utils/dayjs";
import { Time } from "@/Utils/types";
import { Patient } from "@/types/emr/newPatient";
import { PatientModel } from "@/types/emr/patient";
import {
Organization,
OrganizationParent,
} from "@/types/organization/organization";

const DATE_FORMAT = "DD/MM/YYYY";
const TIME_FORMAT = "hh:mm A";
Expand Down Expand Up @@ -230,16 +226,28 @@ export const conditionalAttribute = <T>(
return condition ? attributes : {};
};

export const stringifyGeoOrganization = (org: Organization) => {
export const conditionalArrayAttribute = <T>(
condition: boolean,
attributes: T[],
) => {
return condition ? attributes : [];
};

export const stringifyNestedObject = <
T extends { name: string; parent?: Partial<T> },
>(
obj: T,
separator = ", ",
) => {
const levels: string[] = [];

let current: OrganizationParent | undefined = org;
let current: Partial<T> | undefined = obj;
while (current?.name) {
levels.push(current.name);
current = current.parent;
}

return levels.join(", ");
return levels.join(separator);
};

export const mergeAutocompleteOptions = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import LinkDepartmentsSheet from "@/components/Patient/LinkDepartmentsSheet";

import useQuestionnaireOptions from "@/hooks/useQuestionnaireOptions";

import { stringifyNestedObject } from "@/Utils/utils";
import { Encounter } from "@/types/emr/encounter";

interface QuickAccessProps {
Expand Down Expand Up @@ -85,7 +86,7 @@ export default function QuickAccess({ encounter }: QuickAccessProps) {
variant="outline"
title={`Organization: ${org.name}${org.description ? ` - ${org.description}` : ""}`}
>
{org.name}
{stringifyNestedObject(org)}
</Badge>
))
: t("no_organizations_added_yet")}
Expand Down
102 changes: 80 additions & 22 deletions src/components/Files/FilesTab.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { useQuery } from "@tanstack/react-query";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import dayjs from "dayjs";
import { t } from "i18next";
import { Link } from "raviger";
import { useEffect, useState } from "react";
import { toast } from "sonner";

import { cn } from "@/lib/utils";

Expand All @@ -25,7 +27,7 @@ import {
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Tabs, TabsContent } from "@/components/ui/tabs";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import {
Tooltip,
TooltipContent,
Expand All @@ -46,7 +48,9 @@ import useFilters from "@/hooks/useFilters";
import { FILE_EXTENSIONS } from "@/common/constants";

import routes from "@/Utils/request/api";
import mutate from "@/Utils/request/mutate";
import query from "@/Utils/request/query";
import { HTTPError } from "@/Utils/request/types";
import { usePermissions } from "@/context/PermissionContext";
import { Encounter } from "@/types/emr/encounter";
import { Patient } from "@/types/emr/newPatient";
Expand All @@ -57,10 +61,11 @@ export interface FilesTabProps {
patientId?: string;
encounter?: Encounter;
patient?: Patient;
subPage?: string;
}

export const FilesTab = (props: FilesTabProps) => {
const { patientId, type, encounter } = props;
const { patientId, type, encounter, subPage = "all" } = props;
const { qParams, updateQuery, Pagination, resultsPerPage } = useFilters({
limit: 14,
});
Expand All @@ -72,6 +77,7 @@ export const FilesTab = (props: FilesTabProps) => {
useState<FileUploadModel | null>(null);
const [openAudioPlayerDialog, setOpenAudioPlayerDialog] = useState(false);
const { hasPermission } = usePermissions();
const queryClient = useQueryClient();

const associatingId =
{
Expand All @@ -81,22 +87,30 @@ export const FilesTab = (props: FilesTabProps) => {

const fileCategories = [
{ value: "all", label: "All" },
{ value: "imaging", label: "Imaging" },
{ value: "lab_reports", label: "Lab Reports" },
{ value: "documents", label: "Documents" },
{ value: "audio", label: "Audio" },
{ value: "xray", label: "X-Ray" },
{ value: "identity_proof", label: "Identity Proof" },
{ value: "unspecified", label: "Unspecified" },
{ value: "discharge_summary", label: "Discharge Summary" },
] as const;

const handleTabChange = (value: (typeof fileCategories)[number]["value"]) => {
updateQuery({ file_category: value === "all" ? undefined : value });
};
const { mutate: generateDischargeSummary, isPending: isGenerating } =
useMutation<{ detail: string }, HTTPError>({
mutationFn: mutate(routes.encounter.generateDischargeSummary, {
pathParams: { encounterId: encounter?.id || "" },
}),
onSuccess: (response) => {
toast.success(response.detail);
refetch();
},
});

const {
data: files,
isLoading: filesLoading,
refetch,
} = useQuery({
queryKey: ["files", type, associatingId, qParams],
queryKey: ["files", type, associatingId, qParams, subPage],
queryFn: query(routes.viewUpload, {
queryParams: {
file_type: type,
Expand All @@ -106,7 +120,7 @@ export const FilesTab = (props: FilesTabProps) => {
...(qParams.is_archived !== undefined && {
is_archived: qParams.is_archived,
}),
//file_category: qParams.file_category,
...(subPage !== "all" && { file_category: subPage }),
},
}),
});
Expand Down Expand Up @@ -636,17 +650,61 @@ export const FilesTab = (props: FilesTabProps) => {
fileUpload={fileUpload}
associatingId={associatingId}
/>
<Tabs
defaultValue="all"
value={qParams.file_category || "all"}
onValueChange={(value) =>
handleTabChange(value as (typeof fileCategories)[number]["value"])
}
>
<div className="mx-2 flex flex-col flex-wrap gap-3 sm:flex-row justify-between">
<div className="flex sm:flex-row flex-wrap flex-col gap-4 sm:items-center">
<FilterButton />
</div>
<Tabs defaultValue={subPage}>
<TabsList className="grid w-auto grid-cols-2 w-fit">
<TabsTrigger value="all" asChild>
<Link
className="text-gray-600"
href={`/facility/${encounter?.facility.id}/encounter/${encounter?.id}/files/all`}
>
{t("all")}
</Link>
</TabsTrigger>
<TabsTrigger value="discharge_summary" asChild>
<Link
className="text-gray-600"
href={`/facility/${encounter?.facility.id}/encounter/${encounter?.id}/files/discharge_summary`}
>
{t("discharge_summary")}
</Link>
</TabsTrigger>
</TabsList>
<div className="grid grid-cols-2 lg:grid-cols-4 gap-2 mt-2">
<FilterButton />
{subPage === "discharge_summary" && (
<>
<Button
variant="outline_primary"
className="flex flex-row items-center"
onClick={async () => {
await queryClient.invalidateQueries({
queryKey: ["files"],
});
toast.success(t("refreshed"));
}}
>
<CareIcon icon="l-sync" />
<span className="ml-2">{t("refresh")}</span>
</Button>
</>
)}
{subPage === "discharge_summary" && (
<>
<Button
variant="primary"
className="flex flex-row items-center"
onClick={() => generateDischargeSummary()}
disabled={isGenerating}
>
<CareIcon icon="l-file-medical" className="hidden md:block" />
<span>
{isGenerating
? t("generating")
: t("generate_discharge_summary")}
</span>
</Button>
</>
)}
<FileUploadButtons />
</div>
<FilterBadges />
Expand Down
2 changes: 1 addition & 1 deletion src/components/Location/LocationHistorySheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function LocationHistorySheet({
return (
<Sheet>
<SheetTrigger asChild>{trigger}</SheetTrigger>
<SheetContent className="w-full sm:max-w-xl">
<SheetContent className="w-full sm:max-w-lg">
<SheetHeader className="px-1">
<SheetTitle>{t("location_history")}</SheetTitle>
</SheetHeader>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Location/LocationTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function LocationNode({

return (
<LocationNode location={location.parent} isLast={false} datetime={datetime}>
<div className="flex flex-col gap-2 ml-6">
<div className="flex flex-col gap-2 ml-2">
<div className="flex items-center text-sm">
<CareIcon
icon="l-corner-down-right"
Expand Down
10 changes: 9 additions & 1 deletion src/components/Patient/PatientInfoCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ export default function PatientInfoCard(props: PatientInfoCardProps) {
data-cy="update-encounter-button"
>
<AlertDialog>
<DropdownMenu modal={false}>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="primary">
{t("update")}
Expand All @@ -462,6 +462,14 @@ export default function PatientInfoCard(props: PatientInfoCardProps) {
{t("treatment_summary")}
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link
href={`/facility/${encounter.facility.id}/encounter/${encounter.id}/files/discharge_summary`}
className="cursor-pointer text-gray-800"
>
{t("discharge_summary")}
</Link>
</DropdownMenuItem>
<AlertDialogTrigger asChild>
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
{t("mark_as_complete")}
Expand Down
Loading
Loading