Skip to content

Commit

Permalink
Introduce (i) Button Popover Component in HL7Monitor and VentilatorP…
Browse files Browse the repository at this point in the history
…atientVitalsMonitor (#7228)

* created a VitalsMonitorAssetPopover component

* replaced asset name with (i) button popover

* replaced DialogModal with headlessUI popover

* minor mobile ui fix HL7Monitor

* removed redundant code

* minor css fix and dynamic icon feature added

* spacing fix

* local fix to improper gender type

* reusable header for VitalsMonitor

* minor css fix

* minor popover css fix

* added hideHeader optional prop to VitalsMonitor

* hide header from VitalsMonitor in ConsultationUpdatesTab

* created and implemented VitalsMonitorFooter component

* hide header and footer from VitalsMonitor asset configure

---------

Co-authored-by: Rithvik Nishad <mail@rithviknishad.dev>
  • Loading branch information
thedevildude and rithviknishad committed Feb 28, 2024
1 parent b38789a commit ab67826
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 114 deletions.
9 changes: 8 additions & 1 deletion src/Components/Assets/AssetType/HL7Monitor.tsx
Expand Up @@ -131,12 +131,19 @@ const HL7Monitor = (props: HL7MonitorProps) => {
)}

{assetType === "HL7MONITOR" && (
<HL7PatientVitalsMonitor key={socketUrl} socketUrl={socketUrl} />
<HL7PatientVitalsMonitor
key={socketUrl}
socketUrl={socketUrl}
hideHeader={true}
hideFooter={true}
/>
)}
{assetType === "VENTILATOR" && (
<VentilatorPatientVitalsMonitor
key={socketUrl}
socketUrl={socketUrl}
hideHeader={true}
hideFooter={true}
/>
)}
</div>
Expand Down
3 changes: 2 additions & 1 deletion src/Components/Facility/CentralNursingStation.tsx
Expand Up @@ -207,7 +207,8 @@ export default function CentralNursingStation({ facilityId }: Props) {
?.start_date
}
key={`${props.patientAssetBed?.bed.id}-${hash}`}
{...props}
patientAssetBed={props.patientAssetBed}
socketUrl={props.socketUrl || ""}
config={config}
/>
</div>
Expand Down
Expand Up @@ -111,6 +111,7 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => {
meta: monitorBedData?.asset_object?.meta,
}}
socketUrl={hl7SocketUrl}
hideHeader={true}
/>
</div>
<div className="min-h-[400px] flex-1">
Expand All @@ -122,6 +123,7 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => {
meta: ventilatorBedData?.asset_object?.meta,
}}
socketUrl={ventilatorSocketUrl}
hideHeader={true}
/>
</div>
</div>
Expand Down Expand Up @@ -157,6 +159,7 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => {
}}
socketUrl={hl7SocketUrl}
config={vitals.config}
hideHeader={true}
/>
</div>
)}
Expand All @@ -173,6 +176,7 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => {
}}
socketUrl={ventilatorSocketUrl}
config={vitals.config}
hideHeader={true}
/>
</div>
)}
Expand Down
59 changes: 6 additions & 53 deletions src/Components/VitalsMonitor/HL7PatientVitalsMonitor.tsx
@@ -1,14 +1,14 @@
import { useEffect } from "react";
import useHL7VitalsMonitor from "./useHL7VitalsMonitor";
import { Link } from "raviger";
import { GENDER_TYPES } from "../../Common/constants";
import CareIcon from "../../CAREUI/icons/CareIcon";
import WaveformLabels from "./WaveformLabels";
import { classNames } from "../../Utils/utils";
import { IVitalsComponentProps, VitalsValueBase } from "./types";
import { triggerGoal } from "../../Integrations/Plausible";
import useAuthUser from "../../Common/hooks/useAuthUser";
import dayjs from "dayjs";
import VitalsMonitorHeader from "./VitalsMonitorHeader";
import VitalsMonitorFooter from "./VitalsMonitorFooter";

const minutesAgo = (timestamp: string) => {
return `${dayjs().diff(dayjs(timestamp), "minute")}m ago`;
Expand All @@ -18,7 +18,7 @@ export default function HL7PatientVitalsMonitor(props: IVitalsComponentProps) {
const { connect, waveformCanvas, data, isOnline } = useHL7VitalsMonitor(
props.config
);
const { patient, bed, asset } = props.patientAssetBed ?? {};
const { bed, asset } = props.patientAssetBed ?? {};
const authUser = useAuthUser();

useEffect(() => {
Expand All @@ -41,56 +41,8 @@ export default function HL7PatientVitalsMonitor(props: IVitalsComponentProps) {

return (
<div className="flex flex-col gap-1 rounded bg-[#020617] p-2">
{props.patientAssetBed && (
<div className="flex items-center justify-between px-2 tracking-wide">
<div className="flex flex-col gap-2 md:flex-row">
{patient ? (
<Link
href={`/facility/${patient.last_consultation?.facility}/patient/${patient.id}/consultation/${patient.last_consultation?.id}`}
className="font-bold uppercase text-white"
>
{patient?.name}
</Link>
) : (
<span className="flex items-center gap-1 text-gray-500">
<CareIcon className="care-l-ban" />
No Patient
</span>
)}
{patient && (
<span className="text-xs font-bold text-gray-400 md:text-sm">
{patient.age}y;{" "}
{GENDER_TYPES.find((g) => g.id === patient.gender)?.icon}
</span>
)}
</div>
<div className="flex flex-col items-center gap-2 text-xs md:flex-row md:text-sm">
{asset && (
<Link
className="flex items-center gap-1 text-gray-500"
href={`/facility/${patient?.facility_object?.id}/assets/${asset?.id}`}
>
<CareIcon className="care-l-monitor-heart-rate text-sm md:text-base" />
<span>{asset.name}</span>
</Link>
)}
{bed && (
<Link
className="flex flex-col items-center gap-2 text-gray-500 md:flex-row"
href={`/facility/${patient?.facility_object?.id}/location/${bed?.location_object?.id}/beds`}
>
<span className="flex items-center gap-1">
<CareIcon className="care-l-bed text-sm md:text-base" />
<span>{bed.name}</span>
</span>
<span className="flex items-center gap-1">
<CareIcon className="care-l-location-point text-sm md:text-base" />
<span>{bed.location_object?.name}</span>
</span>
</Link>
)}
</div>
</div>
{props.hideHeader ? null : (
<VitalsMonitorHeader patientAssetBed={props.patientAssetBed} />
)}
<div className="relative flex flex-col gap-2 md:flex-row md:justify-between">
<VitalsNonWaveformContent>
Expand Down Expand Up @@ -227,6 +179,7 @@ export default function HL7PatientVitalsMonitor(props: IVitalsComponentProps) {
</div>
</div>
</div>
{props.hideFooter ? null : <VitalsMonitorFooter asset={asset} />}
</div>
);
}
Expand Down
66 changes: 7 additions & 59 deletions src/Components/VitalsMonitor/VentilatorPatientVitalsMonitor.tsx
@@ -1,82 +1,27 @@
import { useEffect } from "react";
import { Link } from "raviger";
import { GENDER_TYPES } from "../../Common/constants";
import CareIcon from "../../CAREUI/icons/CareIcon";
import useVentilatorVitalsMonitor from "./useVentilatorVitalsMonitor";
import { IVitalsComponentProps, VitalsValueBase } from "./types";
import { classNames } from "../../Utils/utils";
import WaveformLabels from "./WaveformLabels";
import { VitalsNonWaveformContent } from "./HL7PatientVitalsMonitor";
import VitalsMonitorHeader from "./VitalsMonitorHeader";
import VitalsMonitorFooter from "./VitalsMonitorFooter";

export default function VentilatorPatientVitalsMonitor(
props: IVitalsComponentProps
) {
const { connect, waveformCanvas, data, isOnline } =
useVentilatorVitalsMonitor(props.config);
const { patient, bed, asset } = props.patientAssetBed ?? {};

useEffect(() => {
connect(props.socketUrl);
}, [props.socketUrl]);

return (
<div className="flex flex-col gap-1 rounded bg-[#020617] p-2">
{props.patientAssetBed && (
<div className="flex items-center justify-between px-2 tracking-wide">
<div className="flex items-center gap-2">
{patient ? (
<Link
href={`/facility/${patient.last_consultation?.facility}/patient/${patient.id}/consultation/${patient.last_consultation?.id}`}
className="font-bold uppercase text-white"
>
{patient?.name}
</Link>
) : (
<span className="flex items-center gap-1 text-gray-500">
<CareIcon className="care-l-ban" />
No Patient
</span>
)}
{patient && (
<span className="text-sm font-bold text-gray-400">
{patient.age}y;{" "}
{GENDER_TYPES.find((g) => g.id === patient.gender)?.icon}
</span>
)}
</div>
<div className="flex gap-3">
{asset && (
<div className="flex items-center gap-2 text-sm">
<Link
className="flex gap-2 text-gray-500"
href={`/facility/${patient?.facility_object?.id}/assets/${asset?.id}`}
>
<span className="flex items-center gap-1">
<CareIcon className="care-l-lungs text-base" />
{asset.name}
</span>
</Link>
</div>
)}
{bed && (
<div className="flex items-center gap-2 text-sm">
<Link
className="flex gap-2 text-gray-500"
href={`/facility/${patient?.facility_object?.id}/location/${bed?.location_object?.id}/beds`}
>
<span className="flex items-center gap-1">
<CareIcon className="care-l-bed text-base" />
{bed.name}
</span>
<span className="flex items-center gap-1">
<CareIcon className="care-l-location-point text-base" />
{bed.location_object?.name}
</span>
</Link>
</div>
)}
</div>
</div>
{props.hideHeader ? null : (
<VitalsMonitorHeader patientAssetBed={props.patientAssetBed} />
)}
<div className="relative flex flex-col gap-2 divide-x-0 divide-y divide-blue-600 md:flex-row md:justify-between md:divide-x md:divide-y-0">
<div>
Expand Down Expand Up @@ -138,6 +83,9 @@ export default function VentilatorPatientVitalsMonitor(
/>
</VitalsNonWaveformContent>
</div>
{props.hideFooter ? null : (
<VitalsMonitorFooter asset={props.patientAssetBed?.asset} />
)}
</div>
);
}
Expand Down
77 changes: 77 additions & 0 deletions src/Components/VitalsMonitor/VitalsMonitorAssetPopover.tsx
@@ -0,0 +1,77 @@
import CareIcon from "../../CAREUI/icons/CareIcon";
import { AssetData, assetClassProps } from "../Assets/AssetTypes";
import ButtonV2 from "../Common/components/ButtonV2";
import { navigate } from "raviger";
import { useTranslation } from "react-i18next";
import { Popover, Transition } from "@headlessui/react";
import { Fragment } from "react";

interface VitalsMonitorAssetPopoverProps {
asset?: AssetData;
}

const VitalsMonitorAssetPopover = ({
asset,
}: VitalsMonitorAssetPopoverProps) => {
const { t } = useTranslation();

return (
<Popover className="relative">
<Popover.Button>
<CareIcon className="care-l-info-circle cursor-pointer text-sm text-gray-500 hover:text-white md:text-base" />
</Popover.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute z-[100] mt-2 w-56 -translate-x-1/3 translate-y-[-280px] rounded-md bg-white md:w-[350px] md:-translate-y-full md:translate-x-6">
<div className="flex flex-col gap-3 p-5">
<div className="flex items-center gap-2 text-lg font-bold">
<CareIcon
className={`care-l-${
(
(asset?.asset_class &&
assetClassProps[asset.asset_class]) ||
assetClassProps.NONE
).icon
} text-2xl`}
/>
<p>{asset?.name}</p>
</div>
<div className="flex flex-col gap-1">
<p className="text-sm md:text-base">Middleware Hostname:</p>
<p className="break-words text-gray-600">
{asset?.resolved_middleware?.hostname}
</p>
</div>
<div className="flex flex-col gap-1">
<p className="text-sm md:text-base">Local IP Address:</p>
<p className="break-words text-gray-600">
{asset?.meta?.local_ip_address}
</p>
</div>
<ButtonV2
onClick={() =>
navigate(
`/facility/${asset?.location_object.facility?.id}/assets/${asset?.id}/configure`
)
}
id="configure-asset"
data-testid="asset-configure-button"
>
<CareIcon className="care-l-setting h-4" />
{t("configure")}
</ButtonV2>
</div>
</Popover.Panel>
</Transition>
</Popover>
);
};

export default VitalsMonitorAssetPopover;
17 changes: 17 additions & 0 deletions src/Components/VitalsMonitor/VitalsMonitorFooter.tsx
@@ -0,0 +1,17 @@
import { AssetData } from "../Assets/AssetTypes";
import VitalsMonitorAssetPopover from "./VitalsMonitorAssetPopover";

interface IVitalsMonitorFooterProps {
asset?: AssetData;
}

const VitalsMonitorFooter = ({ asset }: IVitalsMonitorFooterProps) => {
return (
<div className="flex w-full items-center gap-2 text-xs tracking-wide md:text-sm">
<p className="text-gray-500 ">{asset?.name}</p>
<VitalsMonitorAssetPopover asset={asset} />
</div>
);
};

export default VitalsMonitorFooter;

0 comments on commit ab67826

Please sign in to comment.