Skip to content

Commit

Permalink
Treatment Summary UI Enhancement (#10563)
Browse files Browse the repository at this point in the history
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
AdityaJ2305 and coderabbitai[bot] authored Feb 27, 2025
1 parent c097957 commit b40c931
Show file tree
Hide file tree
Showing 13 changed files with 677 additions and 419 deletions.
2 changes: 2 additions & 0 deletions public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1510,6 +1510,7 @@
"next_sessions": "Next Sessions",
"next_week_short": "Next wk",
"no": "No",
"no_active_medication_recorded": "No Active Medication Recorded",
"no_address_provided": "No address provided",
"no_allergies_recorded": "No allergies recorded",
"no_appointments": "No appointments found",
Expand Down Expand Up @@ -1551,6 +1552,7 @@
"no_log_update_delta": "No changes since previous log update",
"no_log_updates": "No log updates found",
"no_medical_history_available": "No Medical History Available",
"no_medication_recorded": "No Medication Recorded",
"no_medications": "No Medications",
"no_medications_found_for_this_encounter": "No medications found for this encounter.",
"no_medications_to_administer": "No medications to administer",
Expand Down
8 changes: 6 additions & 2 deletions src/Routers/routes/ConsultationRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ const consultationRoutes: AppRoutes = {
/>
),
"/facility/:facilityId/patient/:patientId/encounter/:encounterId/treatment_summary":
({ facilityId, encounterId }) => (
<TreatmentSummary facilityId={facilityId} encounterId={encounterId} />
({ facilityId, encounterId, patientId }) => (
<TreatmentSummary
facilityId={facilityId}
encounterId={encounterId}
patientId={patientId}
/>
),
"/facility/:facilityId/patient/:patientId/encounter/:encounterId/questionnaire":
({ facilityId, encounterId, patientId }) => (
Expand Down
66 changes: 66 additions & 0 deletions src/components/Common/PrintTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { t } from "i18next";

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

import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";

type HeaderRow = {
key: string;
width?: number;
};

type TableRowType = Record<string, string | undefined>;
interface GenericTableProps {
headers: HeaderRow[];
rows: TableRowType[] | undefined;
}

export default function PrintTable({ headers, rows }: GenericTableProps) {
return (
<div className="overflow-hidden rounded-lg border border-gray">
<Table className="w-full">
<TableHeader>
<TableRow className="bg-transparent hover:bg-transparent divide-x divide-gray border-b-gray">
{headers.map(({ key, width }, index) => (
<TableHead
className={cn(
index == 0 && "first:rounded-l-md",
"h-auto py-1 pl-2 pr-2 text-black text-center ",
width && `w-${width}`,
)}
key={key}
>
{t(key)}
</TableHead>
))}
</TableRow>
</TableHeader>
<TableBody>
{!!rows &&
rows.map((row, index) => (
<TableRow
key={index}
className="bg-transparent hover:bg-transparent divide-x divide-gray"
>
{headers.map(({ key }) => (
<TableCell
className="break-words whitespace-normal text-center"
key={key}
>
{row[key] || "-"}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,23 @@ function StructuredResponseBadge({
);
}

function ResponseCard({ item }: { item: QuestionnaireResponse }) {
function ResponseCard({
item,
isPrintPreview,
}: {
item: QuestionnaireResponse;
isPrintPreview?: boolean;
}) {
const isStructured = !item.questionnaire;
const structuredType = Object.keys(item.structured_responses || {})[0];

return (
<Card className="flex flex-col py-3 px-4 transition-colors hover:bg-muted/50">
<Card
className={cn(
"flex flex-col py-3 px-4 transition-colors hover:bg-muted/50",
isPrintPreview && "shadow-none",
)}
>
<div className="flex items-start justify-between">
<div className="space-y-1">
<div className="flex items-center gap-2 text-xs text-gray-500">
Expand Down Expand Up @@ -317,7 +328,12 @@ export default function QuestionnaireResponsesList({
) : (
<div>
{questionnarieResponses?.results?.length === 0 ? (
<Card className="p-6">
<Card
className={cn(
"p-6",
isPrintPreview && "shadow-none border-gray",
)}
>
<div className="text-lg font-medium text-gray-500">
{t("no_questionnaire_responses")}
</div>
Expand All @@ -327,7 +343,11 @@ export default function QuestionnaireResponsesList({
{questionnarieResponses?.results?.map(
(item: QuestionnaireResponse) => (
<li key={item.id} className="w-full">
<ResponseCard key={item.id} item={item} />
<ResponseCard
key={item.id}
item={item}
isPrintPreview={isPrintPreview}
/>
</li>
),
)}
Expand Down
203 changes: 107 additions & 96 deletions src/components/Medicine/MedicationAdministration/AdministrationTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import React, { useCallback, useMemo, useState } from "react";
import CareIcon from "@/CAREUI/icons/CareIcon";

import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Card, CardContent } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import {
Popover,
Expand Down Expand Up @@ -647,113 +647,124 @@ export const AdministrationTab: React.FC<AdministrationTabProps> = ({
content = <EmptyState searching searchQuery={searchQuery} />;
} else {
content = (
<ScrollArea className="w-full whitespace-nowrap rounded-md">
<Card className="w-full border-none shadow-none min-w-[640px]">
<div className="grid grid-cols-[minmax(200px,2fr),repeat(4,minmax(140px,1fr)),40px]">
{/* Top row without vertical borders */}
<div className="col-span-full grid grid-cols-subgrid">
<div className="flex items-center justify-between p-4 bg-gray-50 border-t border-gray-50">
<div className="flex items-center gap-2 whitespace-break-spaces">
{lastModifiedDate && (
<div className="text-xs text-[#6b7280]">
{t("last_modified")}{" "}
{formatDistanceToNow(lastModifiedDate)} {t("ago")}
</div>
)}
<>
{!filteredMedications.length && (
<CardContent className="p-2">
<p className="text-gray-500 w-full flex justify-center mb-3">
{t("no_active_medication_recorded")}
</p>
</CardContent>
)}
<ScrollArea className="w-full whitespace-nowrap rounded-md">
<Card className="w-full border-none shadow-none min-w-[640px]">
<div className="grid grid-cols-[minmax(200px,2fr),repeat(4,minmax(140px,1fr)),40px]">
{/* Top row without vertical borders */}
<div className="col-span-full grid grid-cols-subgrid">
<div className="flex items-center justify-between p-4 bg-gray-50 border-t border-gray-50">
<div className="flex items-center gap-2 whitespace-break-spaces">
{lastModifiedDate && (
<div className="text-xs text-[#6b7280]">
{t("last_modified")}{" "}
{formatDistanceToNow(lastModifiedDate)} {t("ago")}
</div>
)}
</div>
<div className="flex justify-end items-center bg-gray-50 rounded">
<Button
variant="outline"
size="icon"
className="h-8 w-8 text-gray-400 mr-2"
onClick={handlePreviousSlot}
disabled={!canGoBack}
title={
!canGoBack
? t("cannot_go_before_prescription_date")
: ""
}
>
<CareIcon icon="l-angle-left" className="h-4 w-4" />
</Button>
</div>
</div>
<div className="flex justify-end items-center bg-gray-50 rounded">
{visibleSlots.map((slot) => (
<TimeSlotHeader
key={`${format(slot.date, "yyyy-MM-dd")}-${slot.start}`}
slot={slot}
isCurrentSlot={isTimeInSlot(currentDate, slot)}
isEndSlot={slot.date.getTime() === currentDate.getTime()}
/>
))}
<div className="flex justify-start items-center px-1 bg-gray-50">
<Button
variant="outline"
size="icon"
className="h-8 w-8 text-gray-400 mr-2"
onClick={handlePreviousSlot}
disabled={!canGoBack}
title={
!canGoBack ? t("cannot_go_before_prescription_date") : ""
}
className="h-8 w-8 text-gray-400"
onClick={handleNextSlot}
disabled={isTimeInSlot(currentDate, visibleSlots[3])}
>
<CareIcon icon="l-angle-left" className="h-4 w-4" />
<CareIcon icon="l-angle-right" className="h-4 w-4" />
</Button>
</div>
</div>
{visibleSlots.map((slot) => (
<TimeSlotHeader
key={`${format(slot.date, "yyyy-MM-dd")}-${slot.start}`}
slot={slot}
isCurrentSlot={isTimeInSlot(currentDate, slot)}
isEndSlot={slot.date.getTime() === currentDate.getTime()}
/>
))}
<div className="flex justify-start items-center px-1 bg-gray-50">
<Button
variant="outline"
size="icon"
className="h-8 w-8 text-gray-400"
onClick={handleNextSlot}
disabled={isTimeInSlot(currentDate, visibleSlots[3])}
>
<CareIcon icon="l-angle-right" className="h-4 w-4" />
</Button>
</div>
</div>

{/* Main content with borders */}
<div className="col-span-full grid grid-cols-subgrid divide-x divide-[#e5e7eb] border-l border-r">
{/* Headers */}
<div className="p-4 font-medium text-sm border-t bg-[#F3F4F6] text-secondary-700">
{t("medicine")}:
</div>
{visibleSlots.map((slot, i) => (
<div
key={`${format(slot.date, "yyyy-MM-dd")}-${slot.start}`}
className="p-4 font-semibold text-xs text-center border-t relative bg-[#F3F4F6] text-secondary-700"
>
{i === endSlotIndex &&
slot.date.getTime() === currentDate.getTime() && (
<div className="absolute top-0 left-1/2 -translate-y-1/2 -translate-x-1/2">
<div className="h-2 w-2 rounded-full bg-blue-500" />
</div>
)}
{slot.label}
{/* Main content with borders */}
<div className="col-span-full grid grid-cols-subgrid divide-x divide-[#e5e7eb] border-l border-r">
{/* Headers */}
<div className="p-4 font-medium text-sm border-t bg-[#F3F4F6] text-secondary-700">
{t("medicine")}:
</div>
))}
<div className="border-t bg-[#F3F4F6]" />

{/* Medication rows */}
{filteredMedications?.map((medication) => (
<MedicationRow
key={medication.id}
medication={medication}
visibleSlots={visibleSlots}
currentDate={currentDate}
administrations={administrations?.results}
onAdminister={handleAdminister}
onEditAdministration={handleEditAdministration}
onDiscontinue={handleDiscontinue}
/>
))}
{visibleSlots.map((slot, i) => (
<div
key={`${format(slot.date, "yyyy-MM-dd")}-${slot.start}`}
className="p-4 font-semibold text-xs text-center border-t relative bg-[#F3F4F6] text-secondary-700"
>
{i === endSlotIndex &&
slot.date.getTime() === currentDate.getTime() && (
<div className="absolute top-0 left-1/2 -translate-y-1/2 -translate-x-1/2">
<div className="h-2 w-2 rounded-full bg-blue-500" />
</div>
)}
{slot.label}
</div>
))}
<div className="border-t bg-[#F3F4F6]" />

{/* Medication rows */}
{filteredMedications?.map((medication) => (
<MedicationRow
key={medication.id}
medication={medication}
visibleSlots={visibleSlots}
currentDate={currentDate}
administrations={administrations?.results}
onAdminister={handleAdminister}
onEditAdministration={handleEditAdministration}
onDiscontinue={handleDiscontinue}
/>
))}
</div>
</div>
</div>

{stoppedMedications?.results?.length > 0 && !searchQuery.trim() && (
<div
className="p-4 border-t border-[#e5e7eb] flex items-center gap-2 cursor-pointer hover:bg-gray-50"
onClick={() => setShowStopped(!showStopped)}
>
<CareIcon
icon={showStopped ? "l-eye-slash" : "l-eye"}
className="h-4 w-4"
/>
<span className="text-sm underline">
{showStopped ? t("hide") : t("show")}{" "}
{`${stoppedMedications?.results?.length} ${t("stopped")}`}{" "}
{t("prescriptions")}
</span>
</div>
)}
</Card>
<ScrollBar orientation="horizontal" />
</ScrollArea>
{stoppedMedications?.results?.length > 0 && !searchQuery.trim() && (
<div
className="p-4 border-t border-[#e5e7eb] flex items-center gap-2 cursor-pointer hover:bg-gray-50"
onClick={() => setShowStopped(!showStopped)}
>
<CareIcon
icon={showStopped ? "l-eye-slash" : "l-eye"}
className="h-4 w-4"
/>
<span className="text-sm underline">
{showStopped ? t("hide") : t("show")}{" "}
{`${stoppedMedications?.results?.length} ${t("stopped")}`}{" "}
{t("prescriptions")}
</span>
</div>
)}
</Card>
<ScrollBar orientation="horizontal" />
</ScrollArea>
</>
);
}

Expand Down
Loading

0 comments on commit b40c931

Please sign in to comment.