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 3 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
15 changes: 14 additions & 1 deletion public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -733,14 +733,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 @@ -766,7 +770,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 @@ -948,6 +952,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 @@ -961,6 +966,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 @@ -970,6 +976,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 @@ -1070,6 +1077,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 @@ -1390,6 +1399,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 @@ -1635,6 +1645,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_username": "Please enter the username",
"please_fix_errors": "Please fix the errors in the highlighted fields and try submitting again.",
Expand Down Expand Up @@ -1754,6 +1765,7 @@
"refresh": "Refresh",
"refresh_list": "Refresh List",
"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 @@ -1988,6 +2000,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 @@ -31,6 +31,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 @@ -562,6 +562,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
15 changes: 8 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";
import { Quantity } from "@/types/questionnaire/quantity";

const DATE_FORMAT = "DD/MM/YYYY";
Expand Down Expand Up @@ -273,14 +269,19 @@ export const conditionalArrayAttribute = <T>(
return condition ? attributes : [];
};

export const stringifyGeoOrganization = (org: Organization) => {
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);
};
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
95 changes: 75 additions & 20 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,18 +650,59 @@ 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">
<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="mx-2 flex flex-col flex-wrap gap-3 sm:flex-row justify-between mt-2">
<div className="flex sm:flex-row flex-wrap flex-col gap-4 sm:items-center">
<FilterButton />
</div>
<FileUploadButtons />
<div className="flex flex-row gap-2">
{subPage === "discharge_summary" && (
<>
<Button
variant="outline_primary"
className="flex flex-row items-center"
onClick={() =>
queryClient.invalidateQueries({ queryKey: ["files"] })
}
>
<CareIcon icon="l-sync" />
<span className="ml-2">{t("refresh")}</span>
</Button>
<Button
variant="primary"
className="flex flex-row items-center"
onClick={() => generateDischargeSummary()}
disabled={isGenerating}
>
<CareIcon icon="l-file-medical" />
<span className="ml-2">
{isGenerating
? t("generating")
: t("generate_discharge_summary")}
</span>
</Button>
</>
)}
<FileUploadButtons />
</div>
</div>
<FilterBadges />
{fileCategories.map((category) => (
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