diff --git a/src/Components/Assets/AssetType/ONVIFCamera.tsx b/src/Components/Assets/AssetType/ONVIFCamera.tsx index 21beb5f7bf8..d5dfff0aac0 100644 --- a/src/Components/Assets/AssetType/ONVIFCamera.tsx +++ b/src/Components/Assets/AssetType/ONVIFCamera.tsx @@ -1,20 +1,20 @@ -import { useEffect, useState } from "react"; -import { AssetData, ResolvedMiddleware } from "../AssetTypes"; +import { SyntheticEvent, useEffect, useState } from "react"; +import useAuthUser from "../../../Common/hooks/useAuthUser"; +import { checkIfValidIP } from "../../../Common/validation"; import * as Notification from "../../../Utils/Notifications.js"; -import { BedModel } from "../../Facility/models"; import { getCameraConfig } from "../../../Utils/transformUtils"; -import CameraConfigure from "../configure/CameraConfigure"; import Loading from "../../Common/Loading"; -import { checkIfValidIP } from "../../../Common/validation"; -import TextFormField from "../../Form/FormFields/TextFormField"; import { Submit } from "../../Common/components/ButtonV2"; -import { SyntheticEvent } from "react"; -import useAuthUser from "../../../Common/hooks/useAuthUser"; +import { BedModel } from "../../Facility/models"; +import TextFormField from "../../Form/FormFields/TextFormField"; +import { AssetData, ResolvedMiddleware } from "../AssetTypes"; +import CameraConfigure from "../configure/CameraConfigure"; -import request from "../../../Utils/request/request"; import routes from "../../../Redux/api"; +import request from "../../../Utils/request/request"; import useQuery from "../../../Utils/request/useQuery"; +import { useTranslation } from "react-i18next"; import CareIcon from "../../../CAREUI/icons/CareIcon"; interface Props { @@ -25,6 +25,7 @@ interface Props { } const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { + const { t } = useTranslation(); const [isLoading, setIsLoading] = useState(true); const [assetType, setAssetType] = useState(""); const [middlewareHostname, setMiddlewareHostname] = useState(""); @@ -40,7 +41,7 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { const [loadingAddPreset, setLoadingAddPreset] = useState(false); const [loadingSetConfiguration, setLoadingSetConfiguration] = useState(false); const [refreshPresetsHash, setRefreshPresetsHash] = useState( - Number(new Date()), + Number(new Date()) ); const { data: facility, loading } = useQuery(routes.getPermittedFacility, { pathParams: { id: facilityId }, @@ -99,15 +100,13 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { }; try { setLoadingAddPreset(true); - const response = await fetch( - `https://${resolvedMiddleware?.hostname}/status?hostname=${config.hostname}&port=${config.port}&username=${config.username}&password=${config.password}`, + `https://${resolvedMiddleware?.hostname}/status?hostname=${config.hostname}&port=${config.port}&username=${config.username}&password=${config.password}` ); if (!response.ok) { throw new Error("Network error"); } const presetData = await response.json(); - const { res } = await request(routes.createAssetBed, { body: { meta: { ...data, ...presetData }, @@ -145,7 +144,7 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { name="middleware_hostname" label={
-

Middleware Hostname

+

{t("middleware_hostname")}

{resolvedMiddleware?.source != "asset" && (
{ className="tooltip text-indigo-500 hover:text-indigo-600" /> - Middleware hostname sourced from asset{" "} + {t("middleware_hostname_sourced_from_asset")}{" "} {resolvedMiddleware?.source}
@@ -166,7 +165,7 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { /> setCameraAddress(value)} @@ -174,14 +173,14 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { /> setUsername(value)} /> { /> {
diff --git a/src/Components/Facility/Consultations/LiveFeed.tsx b/src/Components/Facility/Consultations/LiveFeed.tsx index 621a0baf0aa..12394857d09 100644 --- a/src/Components/Facility/Consultations/LiveFeed.tsx +++ b/src/Components/Facility/Consultations/LiveFeed.tsx @@ -1,56 +1,53 @@ -import { useEffect, useState, useRef, LegacyRef } from "react"; +import { AxiosError } from "axios"; +import { LegacyRef, useEffect, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; +import ReactPlayer from "react-player"; import { useDispatch } from "react-redux"; import useKeyboardShortcut from "use-keyboard-shortcut"; -import { - listAssetBeds, - partialUpdateAssetBed, - deleteAssetBed, -} from "../../../Redux/actions"; +import CareIcon from "../../../CAREUI/icons/CareIcon"; import { getCameraPTZ } from "../../../Common/constants"; +import { useFeedPTZ } from "../../../Common/hooks/useFeedPTZ"; +import useFullscreen from "../../../Common/hooks/useFullscreen"; import { StreamStatus, useMSEMediaPlayer, } from "../../../Common/hooks/useMSEplayer"; -import { useFeedPTZ } from "../../../Common/hooks/useFeedPTZ"; +import useWindowDimensions from "../../../Common/hooks/useWindowDimensions"; +import { deleteAssetBed, partialUpdateAssetBed } from "../../../Redux/actions"; +import routes from "../../../Redux/api"; import * as Notification from "../../../Utils/Notifications.js"; -import { FeedCameraPTZHelpButton } from "./Feed"; -import { AxiosError } from "axios"; +import request from "../../../Utils/request/request"; +import useQuery from "../../../Utils/request/useQuery"; +import { isIOS } from "../../../Utils/utils"; import { BedSelect } from "../../Common/BedSelect"; -import { BedModel } from "../models"; -import useWindowDimensions from "../../../Common/hooks/useWindowDimensions"; -import CareIcon from "../../../CAREUI/icons/CareIcon"; -import Page from "../../Common/components/Page"; import ConfirmDialog from "../../Common/ConfirmDialog"; +import Page from "../../Common/components/Page"; import { FieldLabel } from "../../Form/FormFields/FormField"; -import useFullscreen from "../../../Common/hooks/useFullscreen"; -import ReactPlayer from "react-player"; -import { isIOS } from "../../../Utils/utils"; +import { BedModel, CameraPresetModel } from "../models"; +import { FeedCameraPTZHelpButton } from "./Feed"; const LiveFeed = (props: any) => { + const { t } = useTranslation(); const middlewareHostname = props.middlewareHostname; const [presetsPage, setPresetsPage] = useState(0); const cameraAsset = props.asset; const [presets, setPresets] = useState([]); - const [bedPresets, setBedPresets] = useState([]); const [showDefaultPresets, setShowDefaultPresets] = useState(false); const [precision, setPrecision] = useState(1); const [streamStatus, setStreamStatus] = useState( - StreamStatus.Offline, + StreamStatus.Offline ); const [videoStartTime, setVideoStartTime] = useState(null); const [bed, setBed] = useState({}); - const [preset, setNewPreset] = useState(""); const [loading, setLoading] = useState(); const dispatch: any = useDispatch(); - const [page, setPage] = useState({ - count: 0, - limit: 8, - offset: 0, - }); - const [toDelete, setToDelete] = useState(null); - const [toUpdate, setToUpdate] = useState(null); + const [toDelete, setToDelete] = useState(); + const [toUpdate, setToUpdate] = useState(); const [_isFullscreen, setFullscreen] = useFullscreen(); + const [offset, setOffset] = useState(0); + const limit = 8; + const { width } = useWindowDimensions(); const extremeSmallScreenBreakpoint = 320; const isExtremeSmallScreen = @@ -75,7 +72,7 @@ const LiveFeed = (props: any) => { const refreshPresetsHash = props.refreshPresetsHash; // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [currentPreset, setCurrentPreset] = useState(); + const [currentPreset, setCurrentPreset] = useState(); const { absoluteMove, getCameraStatus, @@ -114,67 +111,64 @@ const LiveFeed = (props: any) => { return timeDifference - video.currentTime; }; - const getBedPresets = async (id: any) => { - const bedAssets = await dispatch( - listAssetBeds({ - asset: id, - limit: page.limit, - offset: page.offset, - }), - ); - setBedPresets(bedAssets?.data?.results); - setPage({ - ...page, - count: bedAssets?.data?.count, - }); - }; + const { data: bedPresets, refetch: refetchBedPresets } = useQuery( + routes.getCameraPresets, + { + query: { + limit: limit, + offset: offset, + }, + pathParams: { + external_id: cameraAsset?.id, + }, + } + ); - const deletePreset = async (id: any) => { + const deletePreset = async (id: string) => { const res = await dispatch(deleteAssetBed(id)); if (res?.status === 204) { Notification.Success({ msg: "Preset deleted successfully" }); - getBedPresets(cameraAsset.id); + refetchBedPresets(); } else { Notification.Error({ msg: "Error while deleting Preset: " + (res?.data?.detail || ""), }); } - setToDelete(null); + setToDelete(undefined); }; - const updatePreset = async (currentPreset: any) => { - const data = { - bed_id: bed.id, - preset_name: preset, - }; - const response = await dispatch( - partialUpdateAssetBed( - { - asset: currentPreset.asset_object.id, - bed: bed.id, - meta: { - ...currentPreset.meta, - ...data, - }, - }, - currentPreset?.id, - ), - ); - if (response && response.status === 200) { + const updatePreset = async (currentPreset: CameraPresetModel) => { + const resp = await request(routes.partialUpdateAssetBed, { + body: { + asset: currentPreset?.asset_bed_object.asset?.id, + bed: bed.id, + }, + pathParams: { + external_id: currentPreset?.asset_bed_object.id || "", + }, + }); + if (resp && resp.res?.status === 200) { Notification.Success({ msg: "Preset Updated" }); } else { Notification.Error({ msg: "Something Went Wrong" }); } - getBedPresets(cameraAsset?.id); + refetchBedPresets(); fetchCameraPresets(); - setToUpdate(null); + setToUpdate(undefined); }; - const gotoBedPreset = (preset: any) => { + const gotoBedPreset = (preset: CameraPresetModel) => { setLoading("Moving"); - absoluteMove(preset.meta.position, { - onSuccess: () => setLoading(undefined), - }); + absoluteMove( + { + x: preset?.x || 0.0, + y: preset?.y || 0.0, + zoom: preset?.zoom || 1, + }, + { + onSuccess: () => setLoading(undefined), + } + ); }; useEffect(() => { @@ -187,16 +181,16 @@ const LiveFeed = (props: any) => { }, []); useEffect(() => { - setNewPreset(toUpdate?.meta?.preset_name); - setBed(toUpdate?.bed_object); - }, [toUpdate]); - - useEffect(() => { - getBedPresets(cameraAsset.id); - if (bedPresets?.[0]?.position) { - absoluteMove(bedPresets[0]?.position, {}); + refetchBedPresets(); + const position = { + x: bedPresets?.results[0]?.x || 0.0, + y: bedPresets?.results[0]?.y || 0.0, + zoom: bedPresets?.results[0]?.zoom || 1, + }; + if (position) { + absoluteMove(position, {}); } - }, [page.offset, cameraAsset.id, refreshPresetsHash]); + }, [offset, refreshPresetsHash]); const startStreamFeed = () => { startStream({ @@ -216,7 +210,7 @@ const LiveFeed = (props: any) => { })); }; useEffect(() => { - let tId: any; + let tId: NodeJS.Timeout; if (streamStatus !== StreamStatus.Playing) { setStreamStatus(StreamStatus.Loading); tId = setTimeout(() => { @@ -229,17 +223,10 @@ const LiveFeed = (props: any) => { }; }, [startStream, streamStatus]); - const handlePagination = (cOffset: number) => { - setPage({ - ...page, - offset: cOffset, - }); - }; - const cameraPTZActionCBs: { [key: string]: (option: any) => void } = { precision: () => { setPrecision((precision: number) => - precision === 16 ? 1 : precision * 2, + precision === 16 ? 1 : precision * 2 ); }, reset: () => { @@ -258,25 +245,21 @@ const LiveFeed = (props: any) => { getCameraStatus({ onSuccess: async (data) => { console.log({ currentPreset, data }); - if (currentPreset?.asset_object?.id && data?.position) { + if (currentPreset?.asset_bed_object?.asset?.id && data?.position) { setLoading(option.loadingLabel); console.log("Updating Preset"); const response = await dispatch( partialUpdateAssetBed( { - asset: currentPreset.asset_object.id, - bed: currentPreset.bed_object.id, - meta: { - ...currentPreset.meta, - position: data?.position, - }, + asset: currentPreset.asset_bed_object?.asset?.id, + bed: currentPreset.asset_bed_object?.bed?.id, }, - currentPreset?.id, - ), + currentPreset?.asset_bed_object?.id || "" + ) ); if (response && response.status === 200) { Notification.Success({ msg: "Preset Updated" }); - getBedPresets(cameraAsset?.id); + refetchBedPresets(); fetchCameraPresets(); } setLoading(undefined); @@ -309,39 +292,40 @@ const LiveFeed = (props: any) => { } return ( - + {toDelete && (

- Preset: {toDelete.meta.preset_name} + {t("preset")} {toDelete.preset_name}

- Bed: {toDelete.bed_object.name} + {t("bed")}:{" "} + {toDelete.asset_bed_object?.bed?.name}

} action="Delete" variant="danger" - onClose={() => setToDelete(null)} - onConfirm={() => deletePreset(toDelete.id)} + onClose={() => setToDelete(undefined)} + onConfirm={() => deletePreset(toDelete?.id || "")} /> )} {toUpdate && ( setToUpdate(null)} + onClose={() => setToUpdate(undefined)} onConfirm={() => updatePreset(toUpdate)} >
- Bed + {t("bed")} setBed(selected as BedModel)} @@ -357,7 +341,6 @@ const LiveFeed = (props: any) => {
- {/* ADD VIDEO PLAYER HERE */}
{isIOS ? (
@@ -412,7 +395,7 @@ const LiveFeed = (props: any) => { calculateVideoLiveDelay() > 3 && (
- Slow Network Detected + {t("slow_network_detected")}
)} @@ -424,39 +407,43 @@ const LiveFeed = (props: any) => {
)} - {/* { streamStatus > 0 && */}
{streamStatus === StreamStatus.Offline && (

- STATUS: OFFLINE + {t("status")}:{" "} + {t("offline")}

- Feed is currently not live. + {t("feed_is_currently_not_live")}

- Click refresh button to try again. + {t("click_refresh_button_to_try_again")}

)} {streamStatus === StreamStatus.Stop && (

- STATUS: STOPPED + {t("status")}:{" "} + STOPPED +

+

+ {t("feed_is_stopped")}

-

Feed is Stooped.

- Click refresh button to start feed. + {t("click_refresh_button_to_start_feed")}

)} {streamStatus === StreamStatus.Loading && (

- STATUS: LOADING + {t("status")}:{" "} + {t("loading")}

- Fetching latest feed. + {t("fetching_latest_feed")}

)} @@ -513,7 +500,7 @@ const LiveFeed = (props: any) => { setShowDefaultPresets(true); }} > - Default Presets + {t("default_presets")}
@@ -549,7 +536,7 @@ const LiveFeed = (props: any) => { setLoading(undefined); console.log("Preset Updated", option); }, - }, + } ); }} > @@ -559,44 +546,49 @@ const LiveFeed = (props: any) => { ) : ( <> - {bedPresets?.map((preset: any, index: number) => ( -
- -
- + {bedPresets?.results?.map( + (preset: CameraPresetModel, index: number) => ( +
+
+ + +
-
- ))} + ) + )} )}
@@ -626,19 +618,15 @@ const LiveFeed = (props: any) => {
@@ -648,11 +636,12 @@ const LiveFeed = (props: any) => { )}
diff --git a/src/Components/Facility/models.tsx b/src/Components/Facility/models.tsx index 594d3bb08a1..769a76a8145 100644 --- a/src/Components/Facility/models.tsx +++ b/src/Components/Facility/models.tsx @@ -207,7 +207,7 @@ export interface InventoryItemsModel { { id: number; name: string; - }, + } ]; } @@ -240,6 +240,25 @@ export interface BedModel { modified_date?: string; } +export interface CameraPresetModel { + id?: string; + asset_bed_object: { + id?: string; + asset?: AssetData; + bed?: BedModel; + }; + x?: number; + y?: number; + zoom?: number; + preset_name?: string; + + created_date?: string; + modified_date?: string; + + created_by?: UserBareMinimum; + updated_by?: UserBareMinimum; +} + export interface CurrentBed { id: string; consultation: string; diff --git a/src/Locale/en/Asset.json b/src/Locale/en/Asset.json index f24549ee0b6..5ba7fe6e868 100644 --- a/src/Locale/en/Asset.json +++ b/src/Locale/en/Asset.json @@ -9,7 +9,5 @@ "back": "Back", "close": "Close", "update_asset_service_record": "Update Asset Service Record", - "eg_details_on_functionality_service_etc": "Eg. Details on functionality, service, etc.", - "updating": "Updating", - "update": "Update" -} + "eg_details_on_functionality_service_etc": "Eg. Details on functionality, service, etc." +} \ No newline at end of file diff --git a/src/Locale/en/Common.json b/src/Locale/en/Common.json index 84466c99a72..c425dcdaab4 100644 --- a/src/Locale/en/Common.json +++ b/src/Locale/en/Common.json @@ -39,6 +39,7 @@ "modified": "Modified", "updated": "Updated", "update": "Update", + "updating": "Updating", "configure": "Configure", "assigned_to": "Assigned to", "cancel": "Cancel", @@ -162,5 +163,6 @@ "DD/MM/YYYY": "DD/MM/YYYY", "clear_all_filters": "Clear All Filters", "summary": "Summary", - "report": "Report" + "report": "Report", + "refresh": "Refresh" } \ No newline at end of file diff --git a/src/Locale/en/Hub.json b/src/Locale/en/Hub.json index 43454d1e215..024e039d1ed 100644 --- a/src/Locale/en/Hub.json +++ b/src/Locale/en/Hub.json @@ -1,14 +1,32 @@ { "monitor": "Monitor", + "preset": "Preset", + "middleware_hostname": "Middleware Hostname", + "middleware_hostname_sourced_from_asset": "Middleware hostname sourced from asset", + "local_ip_address": "Local IP Address", + "stream_uuid": "Stream UUID", + "set_configuration": "Set Configuration", "show_default_presets": "Show Default Presets", "show_patient_presets": "Show Patient Presets", + "default_presets": "Default Presets", + "patient_presets": "Patient Presets", + "fetching_latest_feed": "Fetching latest feed", "moving_camera": "Moving Camera", + "click_refresh_button_to_start_feed": "Click refresh button to start feed", + "feed_is_stopped": "Feed is stopped", "full_screen": "Full Screen", "feed_is_currently_not_live": "Feed is currently not live", + "click_refresh_to_try_again": "Click refresh to try again", + "slow_network_detected": "Slow Network Detected", + "update_preset": "Update Preset", + "delete_preset": "Are you sure you want to delete this preset?", + "live_feed": "Live Feed", "zoom_out": "Zoom Out", "zoom_in": "Zoom In", "right": "Right", "left": "Left", "down": "Down", - "up": "Up" -} + "up": "Up", + "stopped": "Stopped", + "offline": "Offline" +} \ No newline at end of file diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index 4dd3480ec8c..fc6e894b094 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -41,6 +41,7 @@ import { } from "../Components/ExternalResult/models"; import { BedModel, + CameraPresetModel, CapacityModal, ConsultationModel, CreateBedBody, @@ -102,8 +103,8 @@ import { InvestigationSessionType } from "../Components/Facility/Investigations/ import { Investigation } from "../Components/Facility/Investigations/Reports/types"; import { HCXPolicyModel } from "../Components/HCX/models"; import { IComment, IResource } from "../Components/Resource/models"; -import { IShift } from "../Components/Shifting/models"; import { ScribeModel } from "../Components/Scribe/Scribe"; +import { IShift } from "../Components/Shifting/models"; /** * A fake function that returns an empty object casted to type T @@ -464,6 +465,10 @@ const routes = { path: "/api/v1/asset/{external_id}/operate_assets/", method: "POST", }, + addCameraPreset: { + path: "/api/v1/assetbed/{external_id}/add_camera_preset/", + method: "POST", + }, // Patient Asset Beds (for CNS and Monitoring Hub) listPatientAssetBeds: { @@ -1265,6 +1270,11 @@ const routes = { method: "GET", TRes: Type>(), }, + getCameraPresets: { + path: "/api/v1/asset/{external_id}/get_presets/", + method: "GET", + TRes: Type>(), + }, // Asset transaction endpoints