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

Added a Date/time and round type filter for log updates #6713

Merged
merged 14 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@ import PrescriptionsTable from "../../Medicine/PrescriptionsTable";
import Chip from "../../../CAREUI/display/Chip";
import { formatAge, formatDate, formatDateTime } from "../../../Utils/utils";
import ReadMore from "../../Common/components/Readmore";
import { DailyRoundsList } from "../Consultations/DailyRoundsList";
import DailyRoundsList from "../Consultations/DailyRoundsList";

const PageTitle = lazy(() => import("../../Common/PageTitle"));

export const ConsultationUpdatesTab = (props: ConsultationTabProps) => {
const dispatch: any = useDispatch();
const [showAutomatedRounds, setShowAutomatedRounds] = useState(true);
const [hl7SocketUrl, setHL7SocketUrl] = useState<string>();
const [ventilatorSocketUrl, setVentilatorSocketUrl] = useState<string>();
const [monitorBedData, setMonitorBedData] = useState<AssetBedModel>();
Expand Down Expand Up @@ -674,31 +673,7 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => {
</div>
</div>
<div className="w-full pl-4 xl:w-1/3">
<div className="flex flex-col md:flex-row md:items-center md:justify-between">
<PageTitle title="Update Log" hideBack breadcrumbs={false} />
<div className="mb-[2rem] pl-[1.5rem] md:mb-[0.125rem]">
<input
className="relative float-left ml-[-1.5rem] mr-[6px] mt-[0.15rem] h-[1.125rem] w-[1.125rem] appearance-none rounded-[0.25rem] border-[0.125rem] border-solid border-[rgba(0,0,0,0.25)] bg-white outline-none before:pointer-events-none before:absolute before:h-[0.875rem] before:w-[0.875rem] before:scale-0 before:rounded-full before:bg-transparent before:opacity-0 before:shadow-[0px_0px_0px_13px_transparent] before:content-[''] checked:border-primary checked:bg-primary checked:before:opacity-[0.16] checked:after:absolute checked:after:-mt-px checked:after:ml-[0.25rem] checked:after:block checked:after:h-[0.8125rem] checked:after:w-[0.375rem] checked:after:rotate-45 checked:after:border-[0.125rem] checked:after:border-l-0 checked:after:border-t-0 checked:after:border-solid checked:after:border-white checked:after:bg-transparent checked:after:content-[''] hover:cursor-pointer hover:before:opacity-[0.04] hover:before:shadow-[0px_0px_0px_13px_rgba(0,0,0,0.6)] focus:shadow-none focus:transition-[border-color_0.2s] focus:before:scale-100 focus:before:opacity-[0.12] focus:before:shadow-[0px_0px_0px_13px_rgba(0,0,0,0.6)] focus:before:transition-[box-shadow_0.2s,transform_0.2s] focus:after:absolute focus:after:z-[1] focus:after:block focus:after:h-[0.875rem] focus:after:w-[0.875rem] focus:after:rounded-[0.125rem] focus:after:bg-white focus:after:content-[''] checked:focus:border-primary checked:focus:bg-primary checked:focus:before:scale-100 checked:focus:before:shadow-[0px_0px_0px_13px_#3b71ca] checked:focus:before:transition-[box-shadow_0.2s,transform_0.2s] checked:focus:after:-mt-px checked:focus:after:ml-[0.25rem] checked:focus:after:h-[0.8125rem] checked:focus:after:w-[0.375rem] checked:focus:after:rotate-45 checked:focus:after:rounded-none checked:focus:after:border-[0.125rem] checked:focus:after:border-l-0 checked:focus:after:border-t-0 checked:focus:after:border-solid checked:focus:after:border-white checked:focus:after:bg-transparent"
type="checkbox"
id="automated-rounds-visible-checkbox"
checked={showAutomatedRounds}
onChange={() => setShowAutomatedRounds((s) => !s)}
/>
<label
className="inline-block pl-[0.15rem] hover:cursor-pointer"
htmlFor="automated-rounds-visible-checkbox"
>
Show Automated Rounds
</label>
</div>
</div>
<DailyRoundsList
facilityId={props.facilityId}
patientId={props.patientId}
consultationId={props.consultationId}
consultationData={props.consultationData}
showAutomatedRounds={showAutomatedRounds}
/>
<DailyRoundsList consultation={props.consultationData} />
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import CareIcon from "../../../../CAREUI/icons/CareIcon";
import ButtonV2 from "../../../Common/components/ButtonV2";
import { DailyRoundsModel } from "../../../Patient/models";
import LogUpdateCardAttribute from "./LogUpdateCardAttribute";
import { ConsultationModel } from "../../models";

interface Props {
round: DailyRoundsModel;
consultationData: any;
consultationData: ConsultationModel;
onViewDetails: () => void;
onUpdateLog?: () => void;
}
Expand Down
115 changes: 115 additions & 0 deletions src/Components/Facility/Consultations/DailyRoundsFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { Popover, Transition } from "@headlessui/react";
import ButtonV2 from "../../Common/components/ButtonV2";
import { Fragment } from "react";
import { SelectFormField } from "../../Form/FormFields/SelectFormField";
import TextFormField from "../../Form/FormFields/TextFormField";
import CareIcon from "../../../CAREUI/icons/CareIcon";
import dayjs from "dayjs";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { DailyRoundTypes, DailyRoundsModel } from "../../Patient/models";
import { FieldChangeEvent } from "../../Form/FormFields/Utils";

type FilterState = {
rounds_type?: DailyRoundsModel["rounds_type"];
taken_at_after?: string;
taken_at_before?: string;
};

interface Props {
onApply: (filter: FilterState) => void;
}

export default function DailyRoundsFilter(props: Props) {
const { t } = useTranslation();
const [filter, setFilter] = useState<FilterState>({});

const field = (name: keyof FilterState) => ({
name,
value: filter[name],
onChange: (e: FieldChangeEvent<unknown>) =>
setFilter({ ...filter, [e.name]: e.value }),
labelClassName: "text-sm",
errorClassName: "hidden",
});

return (
<div className="flex flex-row-reverse items-center gap-4 md:flex-row">
<Popover className="relative ">
<Popover.Button>
<ButtonV2 variant="secondary" className="mr-5 border">
<CareIcon className="care-l-filter" />
{t("filter")}
</ButtonV2>
</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 right-0 z-30 mt-1 w-80 px-4 sm:px-0 md:w-96 lg:max-w-3xl">
<div className="rounded-lg shadow-lg ring-1 ring-gray-400">
<div className="rounded-t-lg bg-gray-100 px-6 py-4">
<div className="flow-root rounded-md">
<span className="block text-sm text-gray-800">
{t("filter_by")}
</span>
</div>
</div>
<div className="relative flex flex-col gap-4 rounded-b-lg bg-white p-6">
<SelectFormField
{...field("rounds_type")}
label={t("Round Type")}
options={DailyRoundTypes}
placeholder={t("show_all")}
optionLabel={(o) => t(o)}
optionValue={(o) => o}
/>
<TextFormField
{...field("taken_at_after")}
label="Measured after"
type="datetime-local"
max={dayjs().format("YYYY-MM-DDTHH:mm")}
/>
<TextFormField
{...field("taken_at_before")}
label="Measured before"
type="datetime-local"
max={dayjs().format("YYYY-MM-DDTHH:mm")}
/>

<Popover.Button>
<ButtonV2
variant="secondary"
onClick={() => {
setFilter({});
props.onApply({});
}}
border
className="w-full"
>
{t("clear")}
</ButtonV2>
</Popover.Button>
<Popover.Button>
<ButtonV2
variant="primary"
onClick={() => props.onApply(filter)}
border
className="w-full"
>
{t("apply")}
</ButtonV2>
</Popover.Button>
</div>
</div>
</Popover.Panel>
</Transition>
</Popover>
</div>
);
}
134 changes: 65 additions & 69 deletions src/Components/Facility/Consultations/DailyRoundsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,88 +6,84 @@ import { useTranslation } from "react-i18next";
import LoadingLogUpdateCard from "./DailyRounds/LoadingCard";
import routes from "../../../Redux/api";
import PaginatedList from "../../../CAREUI/misc/PaginatedList";
import PageTitle from "../../Common/PageTitle";
import DailyRoundsFilter from "./DailyRoundsFilter";
import { ConsultationModel } from "../models";
import { useSlugs } from "../../../Common/hooks/useSlug";

export const DailyRoundsList = (props: any) => {
interface Props {
consultation: ConsultationModel;
}

export default function DailyRoundsList({ consultation }: Props) {
const [facilityId, patientId, consultationId] = useSlugs(
"facility",
"patient",
"consultation"
);
const { t } = useTranslation();
const {
facilityId,
patientId,
consultationId,
consultationData,
showAutomatedRounds,
} = props;

const consultationUrl = `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}`;

return (
<PaginatedList
route={routes.getDailyReports}
pathParams={{
consultationId,
}}
query={{
rounds_type: showAutomatedRounds ? "" : "NORMAL,VENTILATOR,ICU",
}}
pathParams={{ consultationId }}
>
{(_) => (
<div className="-mt-2 flex w-full flex-col gap-4">
<div className="flex max-h-[85vh] flex-col gap-4 overflow-y-auto overflow-x-hidden px-3">
<PaginatedList.WhenEmpty className="flex w-full justify-center rounded-md border-b border-gray-200 bg-white px-5 py-1 text-center text-2xl font-bold text-gray-500">
<span className="flex justify-center rounded-lg bg-white p-3 text-gray-700">
{t("no_consultation_updates")}
</span>
</PaginatedList.WhenEmpty>
<PaginatedList.WhenLoading>
<>
{Array.from({ length: 3 }).map((_, i) => (
<LoadingLogUpdateCard key={i} />
))}
</>
</PaginatedList.WhenLoading>
<PaginatedList.Items<DailyRoundsModel> className="flex grow flex-col gap-3">
{(item, items) => {
if (item.rounds_type === "AUTOMATED") {
{({ refetch }) => (
<>
<div className="flex flex-1 justify-between">
<PageTitle title="Update Log" hideBack breadcrumbs={false} />
<DailyRoundsFilter onApply={(query) => refetch({ query })} />
</div>

<div className="-mt-2 flex w-full flex-col gap-4">
<div className="flex max-h-[85vh] flex-col gap-4 overflow-y-auto overflow-x-hidden px-3">
<PaginatedList.WhenEmpty className="flex w-full justify-center border-b border-gray-200 bg-white p-5 text-center text-2xl font-bold text-gray-500">
<span className="flex justify-center rounded-lg bg-white p-3 text-gray-700 ">
{t("no_consultation_updates")}
</span>
</PaginatedList.WhenEmpty>
<PaginatedList.WhenLoading>
<>
{Array.from({ length: 3 }).map((_, i) => (
<LoadingLogUpdateCard key={i} />
))}
</>
</PaginatedList.WhenLoading>
<PaginatedList.Items<DailyRoundsModel> className="flex grow flex-col gap-3">
{(item, items) => {
if (item.rounds_type === "AUTOMATED") {
return (
<VirtualNursingAssistantLogUpdateCard
round={item}
previousRound={items[items.indexOf(item) + 1]}
/>
);
}

const itemUrl =
item.rounds_type === "NORMAL"
? `${consultationUrl}/daily-rounds/${item.id}`
: `${consultationUrl}/daily_rounds/${item.id}`;

return (
<VirtualNursingAssistantLogUpdateCard
<DefaultLogUpdateCard
round={item}
previousRound={items[items.indexOf(item) + 1]}
consultationData={consultation}
onViewDetails={() => navigate(itemUrl)}
onUpdateLog={() => navigate(`${itemUrl}/update`)}
/>
);
}
return (
<DefaultLogUpdateCard
round={item}
consultationData={consultationData}
onViewDetails={() => {
if (item.rounds_type === "NORMAL") {
navigate(
`/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily-rounds/${item.id}`
);
} else {
navigate(
`/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily_rounds/${item.id}`
);
}
}}
onUpdateLog={() => {
if (item.rounds_type === "NORMAL") {
navigate(
`/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily-rounds/${item.id}/update`
);
} else {
navigate(
`/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily_rounds/${item.id}/update`
);
}
}}
/>
);
}}
</PaginatedList.Items>
<div className="flex w-full items-center justify-center">
<PaginatedList.Paginator hideIfSinglePage />
}}
</PaginatedList.Items>
<div className="flex w-full items-center justify-center">
<PaginatedList.Paginator hideIfSinglePage />
</div>
</div>
</div>
</div>
</>
)}
</PaginatedList>
);
};
}
2 changes: 1 addition & 1 deletion src/Components/Facility/models.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export interface ConsultationModel {
history_of_present_illness?: string;
facility?: number;
facility_name?: string;
id?: string;
id: string;
modified_date?: string;
other_symptoms?: string;
patient?: string;
Expand Down
2 changes: 1 addition & 1 deletion src/Components/Form/FormFields/SelectFormField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type OptionCallback<T, R> = (option: T) => R;

type SelectFormFieldProps<T, V = T> = FormFieldBaseProps<V> & {
placeholder?: React.ReactNode;
options: T[];
options: readonly T[];
position?: "above" | "below";
optionLabel: OptionCallback<T, React.ReactNode>;
optionSelectedLabel?: OptionCallback<T, React.ReactNode>;
Expand Down
7 changes: 5 additions & 2 deletions src/Components/Patient/models.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,8 @@ export interface DailyRoundsOutput {
quantity: number;
}

export const DailyRoundTypes = ["NORMAL", "VENTILATOR", "AUTOMATED"] as const;

export interface DailyRoundsModel {
ventilator_spo2?: number;
spo2?: string;
Expand All @@ -290,7 +292,7 @@ export interface DailyRoundsModel {
medication_given?: Array<any>;
additional_symptoms_text?: string;
current_health?: string;
id?: any;
id: string;
other_symptoms?: string;
admitted_to?: string;
patient_category?: PatientCategory;
Expand All @@ -299,7 +301,7 @@ export interface DailyRoundsModel {
created_date?: string;
modified_date?: string;
taken_at?: string;
rounds_type?: "NORMAL" | "VENTILATOR" | "ICU" | "AUTOMATED";
rounds_type: (typeof DailyRoundTypes)[number];
last_updated_by_telemedicine?: boolean;
created_by_telemedicine?: boolean;
created_by?: {
Expand All @@ -314,6 +316,7 @@ export interface DailyRoundsModel {
};
bed?: string;
}

export interface FacilityNameModel {
id?: string;
name?: string;
Expand Down
3 changes: 2 additions & 1 deletion src/Locale/en/Common.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"clear": "Clear",
"apply": "Apply",
"filter_by": "Filter By",
"filter": "Filter",
"ordering": "Ordering",
"phone_number": "Phone Number",
"emergency_contact_number": "Emergency Contact Number",
Expand Down Expand Up @@ -158,4 +159,4 @@
"clear_selection": "Clear selection",
"select_date": "Select date",
"DD/MM/YYYY": "DD/MM/YYYY"
}
}
Loading
Loading