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 LiveFeedScreen page #6497

Merged
merged 10 commits into from
Nov 7, 2023
14 changes: 12 additions & 2 deletions src/Components/Facility/FacilityHome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const FacilityHome = (props: any) => {
const { t } = useTranslation();
const { facilityId } = props;
const dispatch: any = useDispatch();
const [facilityData, setFacilityData] = useState<FacilityModel>({});
const [facilityData, setFacilityData] = useState<FacilityModel | any>({});
const [capacityData, setCapacityData] = useState<Array<CapacityModal>>([]);
const [doctorData, setDoctorData] = useState<Array<DoctorModal>>([]);
const [isLoading, setIsLoading] = useState(false);
Expand Down Expand Up @@ -495,7 +495,7 @@ export const FacilityHome = (props: any) => {
</div>
<div className="mt-10 flex items-center gap-3">
<div>
{facilityData.features?.some((feature) =>
{facilityData.features?.some((feature: any) =>
FACILITY_FEATURE_TYPES.some((f) => f.id === feature)
) && (
<h1 className="text-lg font-semibold">Available features</h1>
Expand Down Expand Up @@ -617,6 +617,16 @@ export const FacilityHome = (props: any) => {
<CareIcon className="care-l-monitor-heart-rate text-lg" />
<span>Central Nursing Station</span>
</ButtonV2>
<ButtonV2
variant="primary"
ghost
border
className="mt-2 flex w-full flex-row justify-center md:w-auto"
onClick={() => navigate(`/facility/${facilityId}/livefeed`)}
>
<CareIcon className="care-l-video text-lg" />
<span>Live Monitoring</span>
</ButtonV2>
<ButtonV2
variant="primary"
ghost
Expand Down
303 changes: 303 additions & 0 deletions src/Components/Facility/LiveFeedScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
import { Fragment, useContext, useEffect, useState } from "react";
import useFilters from "../../Common/hooks/useFilters";
import useFullscreen from "../../Common/hooks/useFullscreen";
import { FacilityModel } from "./models";
import Loading from "../Common/Loading";
import Page from "../Common/components/Page";
import ButtonV2 from "../Common/components/ButtonV2";
import CareIcon from "../../CAREUI/icons/CareIcon";
import { classNames } from "../../Utils/utils";
import { LocationSelect } from "../Common/LocationSelect";
import Pagination from "../Common/Pagination";
import { SidebarShrinkContext } from "../Common/Sidebar/Sidebar";
import { AssetData } from "../Assets/AssetTypes";
import { Popover, Transition } from "@headlessui/react";
import { FieldLabel } from "../Form/FormFields/FormField";
import CheckBoxFormField from "../Form/FormFields/CheckBoxFormField";
import { useTranslation } from "react-i18next";
import { SortOption } from "../Common/SortDropdown";
import { SelectFormField } from "../Form/FormFields/SelectFormField";
import LiveFeedTile from "./LiveFeedTile";
import { getCameraConfig } from "../../Utils/transformUtils";
import { getPermittedFacility, listAssets } from "../../Redux/actions";
import { useDispatch } from "react-redux";
rithviknishad marked this conversation as resolved.
Show resolved Hide resolved

const PER_PAGE_LIMIT = 6;

const SORT_OPTIONS: SortOption[] = [
{ isAscending: true, value: "bed__name" },
{ isAscending: false, value: "-bed__name" },
{ isAscending: false, value: "-created_date" },
{ isAscending: true, value: "created_date" },
];

interface Props {
facilityId: string;
}

const getOrderingList = async (
facilityId: string,
setOrdering: (order: string) => void
) => {
const orderData = localStorage.getItem("live-feed-order");
if (orderData) {
const order = JSON.parse(orderData);
const orderValue = order.find((item: any) => item.facility === facilityId);
setOrdering(orderValue.order);
}
};

const setOrderingList = async (facilityId: string, order: string) => {
const orderData = localStorage.getItem("live-feed-order") || "[]";
const orderList = JSON.parse(orderData);
const index = orderList.findIndex(
(item: any) => item.facility === facilityId
);
if (index !== -1) {
orderList[index].order = order;
} else {
orderList.push({ facility: facilityId, order });
}
localStorage.setItem("live-feed-order", JSON.stringify(orderList));
};

export default function LiveFeedScreen({ facilityId }: Props) {
const { t } = useTranslation();
const dispatch = useDispatch<any>();
const [isFullscreen, setFullscreen] = useFullscreen();
const sidebar = useContext(SidebarShrinkContext);

const [facility, setFacility] = useState<FacilityModel>();
const [assets, setAssets] = useState<AssetData[]>();
const [totalCount, setTotalCount] = useState(0);
const { qParams, updateQuery, removeFilter, updatePage } = useFilters({
limit: PER_PAGE_LIMIT,
});
const [ordering, setOrdering] = useState<string>("bed__name");

const [refresh_presets_hash, setRefreshPresetsHash] = useState<number>(
Number(new Date())
);

// To automatically collapse sidebar.
useEffect(() => {
sidebar.setShrinked(true);

return () => {
sidebar.setShrinked(sidebar.shrinked);
};
}, []);

useEffect(() => {
getOrderingList(facilityId, setOrdering);
}, [facilityId]);

useEffect(() => {
async function fetchFacilityOrObject() {
if (facility) return facility;
const res = await dispatch(getPermittedFacility(facilityId));
if (res.status !== 200) return;
setFacility(res.data);
return res.data as FacilityModel;
}

async function fetchData() {
setAssets(undefined);

const filters = {
...qParams,
page: qParams.page || 1,
limit: PER_PAGE_LIMIT,
offset: (qParams.page ? qParams.page - 1 : 0) * PER_PAGE_LIMIT,
asset_class: "ONVIF",
facility: facilityId || "",
location: qParams.location,
ordering: qParams.ordering || ordering,
bed_is_occupied: qParams.bed_is_occupied,
};

const [facilityObj, res] = await Promise.all([
fetchFacilityOrObject(),
dispatch(listAssets(filters)),
]);

if (!facilityObj || res.status !== 200) {
return;
}
console.log(facilityObj, res.data);
const entries = res.data.results;

setTotalCount(entries.length);
setAssets(entries);
}
fetchData();
setRefreshPresetsHash(Number(new Date()));
}, [
dispatch,
facilityId,
qParams.page,
qParams.location,
qParams.ordering,
qParams.bed_is_occupied,
]);

return (
<Page
title="Live Monitoring"
backUrl={`/facility/${facilityId}/`}
noImplicitPadding
breadcrumbs={false}
options={
<div className="flex flex-row-reverse items-center gap-4 md:flex-row">
<Popover className="relative">
<Popover.Button>
<ButtonV2 variant="secondary" border>
<CareIcon className="care-l-setting text-lg" />
Settings and Filters
</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 z-30 mt-1 w-80 -translate-x-1/3 px-4 sm:px-0 md:w-96 md:-translate-x-1/2 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">
<span className="font-bold ">{totalCount}</span> Camera
present
</span>
</div>
</div>
<div className="relative flex flex-col gap-8 rounded-b-lg bg-white p-6">
<div>
<FieldLabel className="text-sm">
Filter by Location
</FieldLabel>
<div className="flex w-full items-center gap-2">
<LocationSelect
key={qParams.location}
name="Facilities"
setSelected={(location) => updateQuery({ location })}
selected={qParams.location}
showAll={false}
multiple={false}
facilityId={facilityId}
errors=""
errorClassName="hidden"
/>
{qParams.location && (
<ButtonV2
variant="secondary"
circle
border
onClick={() => removeFilter("location")}
>
Clear
</ButtonV2>
)}
</div>
</div>
<SelectFormField
name="ordering"
label={t("sort_by")}
required
value={qParams.ordering || ordering}
onChange={({ value }) => {
updateQuery({ ordering: value });
setOrderingList(facilityId, value);
}}
options={SORT_OPTIONS}
optionLabel={({ value }) => t("SortOptions." + value)}
optionIcon={({ isAscending }) => (
<CareIcon
className={
isAscending
? "care-l-sort-amount-up"
: "care-l-sort-amount-down"
}
/>
)}
optionValue={({ value }) => value}
labelClassName="text-sm"
errorClassName="hidden"
/>
<CheckBoxFormField
name="bed_is_occupied"
label="Hide Cameras without Patient"
value={
qParams.bed_is_occupied === "true" ||
qParams.bed_is_occupied === undefined
}
onChange={({ name, value }) => {
if (value) {
updateQuery({ [name]: value });
} else {
removeFilter(name);
}
}}
labelClassName="text-sm"
errorClassName="hidden"
/>
<ButtonV2
variant="secondary"
border
onClick={() => setFullscreen(!isFullscreen)}
className="tooltip !h-11"
>
<CareIcon
className={classNames(
isFullscreen
? "care-l-compress-arrows"
: "care-l-expand-arrows-alt",
"text-lg"
)}
/>
{isFullscreen ? "Exit Fullscreen" : "Fullscreen"}
</ButtonV2>
</div>
</div>
</Popover.Panel>
</Transition>
</Popover>

<Pagination
className=""
cPage={qParams.page}
defaultPerPage={PER_PAGE_LIMIT}
data={{ totalCount }}
onChange={(page) => updatePage(page)}
/>
</div>
}
>
{assets === undefined ? (
<Loading />
) : assets.length === 0 ? (
<div className="flex h-[80vh] w-full items-center justify-center text-center text-black">
No Camera present in this location or facility.
</div>
) : (
<div className="mt-1 grid grid-cols-1 gap-2 pl-4 xl:grid-cols-1 3xl:grid-cols-2">
{assets.map((asset, idx) => (
<div className="text-clip" key={idx}>
{/* <LiveFeedTile assetId={asset.asset.id} key={asset?.bed.id} /> */}
<LiveFeedTile
middlewareHostname={facility?.middleware_address}
asset={getCameraConfig(asset)}
showRefreshButton={true}
refreshPresetsHash={refresh_presets_hash}
/>
</div>
))}
</div>
)}
</Page>
);
}
Loading
Loading