diff --git a/cypress/e2e/camera_spec/camera_boundary.cy.ts b/cypress/e2e/camera_spec/camera_boundary.cy.ts new file mode 100644 index 00000000000..3e8f4ebee4e --- /dev/null +++ b/cypress/e2e/camera_spec/camera_boundary.cy.ts @@ -0,0 +1,70 @@ +import { cy, describe, before, beforeEach, it } from "local-cypress"; +const user = { username: "devdistrictadmin", password: "Coronasafe@123" }; +describe("Camera Boundary", () => { + before(() => { + cy.loginByApi(user.username, user.password); + cy.saveLocalStorage(); + }); + beforeEach(() => { + cy.restoreLocalStorage(); + cy.awaitUrl("/assets"); + cy.get("input[id='search']").type("Dev Camera"); + cy.contains("a", "Dev Camera").contains("a", "ICU").click(); + cy.get("button[id='configure-asset']").click(); + }); + + it("Add new boundary", () => { + cy.get("input[name='bed']").type("bed 01"); + cy.get("li[role='option']").contains("Bed 01").click(); + cy.wait(2000); + cy.intercept("**/api/v1/assetbed/**").as("addBoundary"); + cy.get("button[id='add-boundary-preset']").click(); + cy.wait("@addBoundary"); + }); + + it("Update boundary", () => { + cy.get("input[name='bed']").type("bed 01"); + cy.get("li[role='option']").contains("Bed 01").click(); + cy.wait(2000); + cy.get("button[id='update-boundary-preset']").click(); + cy.intercept("**/api/v1/assetbed/**").as("updateBoundary"); + cy.get("button") + .find("svg.care-svg-icon__baseline.care-l-angle-right") + .should("be.visible") + .first() + .click(); + cy.wait("@updateBoundary"); + cy.get("button").contains("Next").click(); + cy.get("button") + .find("svg.care-svg-icon__baseline.care-l-angle-right") + .should("be.visible") + .first() + .click(); + cy.wait("@updateBoundary"); + cy.get("button").contains("Next").click(); + cy.get("button") + .find("svg.care-svg-icon__baseline.care-l-angle-up") + .should("be.visible") + .first() + .click(); + cy.wait("@updateBoundary"); + cy.get("button").contains("Next").click(); + cy.get("button") + .find("svg.care-svg-icon__baseline.care-l-angle-down") + .should("be.visible") + .first() + .click(); + cy.wait("@updateBoundary"); + cy.get("button").contains("Done").click(); + }); + + it("Delete boundary", () => { + cy.get("input[name='bed']").type("bed 01"); + cy.get("li[role='option']").contains("Bed 01").click(); + cy.wait(1000); + cy.intercept("**/api/v1/assetbed/**").as("deleteBoundary"); + cy.get("button[id='delete-boundary-preset']").click(); + cy.get("button").contains("Delete").click(); + cy.wait("@deleteBoundary"); + }); +}); diff --git a/src/Components/Assets/configure/CameraBoundayConfigure.tsx b/src/Components/Assets/configure/CameraBoundaryConfigure.tsx similarity index 98% rename from src/Components/Assets/configure/CameraBoundayConfigure.tsx rename to src/Components/Assets/configure/CameraBoundaryConfigure.tsx index c3630e39452..1bd598278f5 100644 --- a/src/Components/Assets/configure/CameraBoundayConfigure.tsx +++ b/src/Components/Assets/configure/CameraBoundaryConfigure.tsx @@ -85,7 +85,7 @@ export default function CameraBoundaryConfigure( {bed?.id && !toUpdateBoundary && (
- +
{`${ !boundaryPreset ? bed?.name @@ -119,7 +119,7 @@ export default function CameraBoundaryConfigure( onClick={() => { previewBoundary(); }} - id="delete-boundary-preset" + id="preview-boundary-preset" disabled={isPreview} > diff --git a/src/Components/Facility/Consultations/Feed.tsx b/src/Components/Facility/Consultations/Feed.tsx index 1ac7bbe5958..8c4129079a1 100644 --- a/src/Components/Facility/Consultations/Feed.tsx +++ b/src/Components/Facility/Consultations/Feed.tsx @@ -69,6 +69,8 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => { const [cameraState, setCameraState] = useState(null); const [boundaryPreset, setBoundaryPreset] = useState(); const [isFullscreen, setFullscreen] = useFullscreen(); + + // Information about subscription and camera occupier in case asset is not occupied by the current user const [showSubscriptionInfo, setShowSubscriptionInfo] = useState(false); const [showCameraOccupierInfo, setShowCameraOccupierInfo] = useState(false); const [cameraOccupier, setCameraOccupier] = useState({}); @@ -78,30 +80,7 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => { const [borderAlert, setBorderAlert] = useState(null); const authUser = useAuthUser(); - useEffect(() => { - const interval = setInterval(() => { - setTimeoutSeconds((prevSeconds) => prevSeconds - 1); - }, 1000); - - const resetTimer = () => { - setTimeoutSeconds(CAMERA_ACCESS_TIMEOUT); - }; - - document.addEventListener("mousemove", resetTimer); - - if (cameraOccupier.username) { - clearInterval(interval); - setTimeoutSeconds(CAMERA_ACCESS_TIMEOUT); - removeEventListener("mousemove", resetTimer); - } - - return () => { - clearInterval(interval); - document.removeEventListener("mousemove", resetTimer); - }; - }, [cameraOccupier]); - - // Notification hook + // Notification hook to get subscription info const { isSubscribed, isSubscribing, intialSubscriptionState, subscribe } = useNotificationSubscribe(); @@ -109,6 +88,7 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => { intialSubscriptionState(); }, [dispatch, isSubscribed]); + // display subscription info const subscriptionInfo = () => { return (
@@ -151,13 +131,11 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => { ); }; + //display current cameraoccupier info incase the asset is not occupied by the current user const currentCameraOccupierInfo = () => { return (
{ - // setShowCameraOccupierInfo(true); - // }} onMouseLeave={() => { setShowCameraOccupierInfo(false); }} @@ -204,9 +182,6 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => { onMouseEnter={() => { setShowCameraOccupierInfo(true); }} - // onMouseLeave={() => { - // setShowCameraOccupierInfo(false); - // }} >
{cameraOccupier?.firstName?.[0] ? ( @@ -408,6 +383,7 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => { } }, [cameraAsset, cameraMiddlewareHostname]); + //lock and unlock asset on mount and unmount useEffect(() => { if (cameraAsset.id) { lockAsset({ @@ -440,6 +416,31 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => { }; }, [cameraAsset, cameraMiddlewareHostname]); + //count down from CAMERA_ACCESS_TIMEOUT when mouse is idle to unlock asset after timeout + useEffect(() => { + const interval = setInterval(() => { + setTimeoutSeconds((prevSeconds) => prevSeconds - 1); + }, 1000); + + const resetTimer = () => { + setTimeoutSeconds(CAMERA_ACCESS_TIMEOUT); + }; + + document.addEventListener("mousemove", resetTimer); + + if (cameraOccupier.username) { + clearInterval(interval); + setTimeoutSeconds(CAMERA_ACCESS_TIMEOUT); + removeEventListener("mousemove", resetTimer); + } + + return () => { + clearInterval(interval); + document.removeEventListener("mousemove", resetTimer); + }; + }, [cameraOccupier]); + + //unlock asset after timeout useEffect(() => { if (timeoutSeconds === 0) { unlockAsset({}); @@ -459,6 +460,9 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => { } }, [timeoutSeconds]); + //Listen to push notifications for- + //1) camera access request + //2) camera access granted useMessageListener((data) => { if (data?.status == "success" && data?.asset_id === cameraAsset?.id) { lockAsset({ diff --git a/src/Components/Facility/Consultations/LiveFeed.tsx b/src/Components/Facility/Consultations/LiveFeed.tsx index d6f79154a85..8678de8966a 100644 --- a/src/Components/Facility/Consultations/LiveFeed.tsx +++ b/src/Components/Facility/Consultations/LiveFeed.tsx @@ -23,10 +23,10 @@ import Page from "../../Common/components/Page"; import ConfirmDialog from "../../Common/ConfirmDialog"; import { FieldLabel } from "../../Form/FormFields/FormField"; import useFullscreen from "../../../Common/hooks/useFullscreen"; -import { UpdateCameraBoundaryConfigure } from "../../Assets/configure/CameraBoundayConfigure"; +import { UpdateCameraBoundaryConfigure } from "../../Assets/configure/CameraBoundaryConfigure"; import { BoundaryRange } from "../../../Common/constants"; import CameraConfigure from "../../Assets/configure/CameraConfigure"; -import CameraBoundaryConfigure from "../../Assets/configure/CameraBoundayConfigure"; +import CameraBoundaryConfigure from "../../Assets/configure/CameraBoundaryConfigure"; import { direction } from "../../../Common/constants"; const LiveFeed = (props: any) => { @@ -64,12 +64,13 @@ const LiveFeed = (props: any) => { const newPreset: string = props.newPreset; const setNewPreset: (preset: string) => void = props.setNewPreset; - const boundaryPreset: any = props.boundaryPreset; - const addBoundaryPreset: () => void = props.addBoundaryPreset; - const deleteBoundaryPreset: () => void = props.deleteBoundaryPreset; - const setBoundaryPreset: (preset: any) => void = props.setBoundaryPreset; - const fetchBoundaryBedPreset: () => void = props.fetchBoundaryBedPreset; - const updateBoundaryPreset: () => void = props.updateBoundaryPreset; + // boundary preset configuration. + const boundaryPreset: any = props?.boundaryPreset; + const addBoundaryPreset: () => void = props?.addBoundaryPreset; + const deleteBoundaryPreset: () => void = props?.deleteBoundaryPreset; + const setBoundaryPreset: (preset: any) => void = props?.setBoundaryPreset; + const fetchBoundaryBedPreset: () => void = props?.fetchBoundaryBedPreset; + const updateBoundaryPreset: () => void = props?.updateBoundaryPreset; const [updateBoundaryInfo, setUpdateBoundaryInfo] = useState< Record >({ @@ -209,47 +210,7 @@ const LiveFeed = (props: any) => { }); }; - const gotoDirectionalBoundary = (): void => { - if (boundaryPreset?.meta?.range && direction) { - const { max_x, max_y, min_x, min_y }: BoundaryRange = - boundaryPreset.meta.range; - const position = { - x: 0, - y: 0, - zoom: 0.2, - }; - if (direction == "left") { - position.x = min_x; - position.y = (min_y + max_y) / 2; - setLoading("Moving to Left Boundary"); - } - if (direction == "right") { - position.x = max_x; - position.y = (min_y + max_y) / 2; - setLoading("Moving to Right Boundary"); - } - if (direction == "up") { - position.x = (min_x + max_x) / 2; - position.y = max_y; - setLoading("Moving to Top Boundary"); - } - if (direction == "down") { - position.x = (min_x + max_x) / 2; - position.y = min_y; - setLoading("Moving to Bottom Boundary"); - } - absoluteMove(position, { - onSuccess: () => setLoading(undefined), - onError: () => { - Notification.Error({ msg: "Something went wrong" }); - setLoading(undefined); - }, - }); - } else { - Notification.Error({ msg: "Something went wrong" }); - } - }; - + //conditionally disable feed buttons when modifying the boundary const disableFeedButton: (action: string) => boolean = (action) => { if ( (direction == "left" || direction == "right") && @@ -272,6 +233,7 @@ const LiveFeed = (props: any) => { return true; }; + //update a directional boundary when modifying the boundary const changeDirectionalBoundary = (option: any) => { const { max_x, max_y, min_x, min_y }: BoundaryRange = boundaryPreset.meta.range; @@ -318,6 +280,8 @@ const LiveFeed = (props: any) => { }, delay); }); }; + + //preivew all the edges of the boundary at a delay of 3 seconds const previewBoundary = async () => { if (boundaryPreset?.meta?.range) { setIsPreview(true); @@ -449,7 +413,49 @@ const LiveFeed = (props: any) => { } }, [bedPresets]); + //hook move to directional boundary when modifying the boundary useEffect(() => { + const gotoDirectionalBoundary = (): void => { + if (boundaryPreset?.meta?.range && direction) { + const { max_x, max_y, min_x, min_y }: BoundaryRange = + boundaryPreset.meta.range; + const position = { + x: 0, + y: 0, + zoom: 0.2, + }; + if (direction == "left") { + position.x = min_x; + position.y = (min_y + max_y) / 2; + setLoading("Moving to Left Boundary"); + } + if (direction == "right") { + position.x = max_x; + position.y = (min_y + max_y) / 2; + setLoading("Moving to Right Boundary"); + } + if (direction == "up") { + position.x = (min_x + max_x) / 2; + position.y = max_y; + setLoading("Moving to Top Boundary"); + } + if (direction == "down") { + position.x = (min_x + max_x) / 2; + position.y = min_y; + setLoading("Moving to Bottom Boundary"); + } + absoluteMove(position, { + onSuccess: () => setLoading(undefined), + onError: () => { + Notification.Error({ msg: "Something went wrong" }); + setLoading(undefined); + }, + }); + } else { + Notification.Error({ msg: "Something went wrong" }); + } + }; + if (boundaryPreset?.meta?.range && direction) { try { gotoDirectionalBoundary(); @@ -649,7 +655,7 @@ const LiveFeed = (props: any) => {
- +