From f6490390be55d5c25bcf9579c93b285f9fa05855 Mon Sep 17 00:00:00 2001 From: rithviknishad Date: Sun, 29 Sep 2024 13:30:43 +0530 Subject: [PATCH] update asset configure, add camera preset configure for beds --- .../Assets/AssetType/HL7Monitor.tsx | 83 +++++- .../Assets/AssetType/ONVIFCamera.tsx | 68 +---- .../Assets/configure/CameraConfigure.tsx | 79 ----- .../Assets/configure/MonitorConfigure.tsx | 87 ------ .../CameraFeed/CameraFeedWithBedPresets.tsx | 20 +- ...etBedSelect.tsx => CameraPresetSelect.tsx} | 24 +- .../CameraFeed/CameraPresetsConfigure.tsx | 269 ++++++++++++------ src/Components/CameraFeed/routes.ts | 11 +- src/Components/CameraFeed/useOperateCamera.ts | 6 +- .../ConsultationFeedTab.tsx | 88 +++--- 10 files changed, 354 insertions(+), 381 deletions(-) delete mode 100644 src/Components/Assets/configure/CameraConfigure.tsx delete mode 100644 src/Components/Assets/configure/MonitorConfigure.tsx rename src/Components/CameraFeed/{AssetBedSelect.tsx => CameraPresetSelect.tsx} (88%) diff --git a/src/Components/Assets/AssetType/HL7Monitor.tsx b/src/Components/Assets/AssetType/HL7Monitor.tsx index 6583157018d..383af38fa28 100644 --- a/src/Components/Assets/AssetType/HL7Monitor.tsx +++ b/src/Components/Assets/AssetType/HL7Monitor.tsx @@ -1,7 +1,6 @@ import { SyntheticEvent, useEffect, useState } from "react"; -import { AssetData, ResolvedMiddleware } from "../AssetTypes"; +import { AssetClass, AssetData, ResolvedMiddleware } from "../AssetTypes"; import * as Notification from "../../../Utils/Notifications.js"; -import MonitorConfigure from "../configure/MonitorConfigure"; import Loading from "../../Common/Loading"; import { checkIfValidIP } from "../../../Common/validation"; import Card from "../../../CAREUI/display/Card"; @@ -13,6 +12,10 @@ import VentilatorPatientVitalsMonitor from "../../VitalsMonitor/VentilatorPatien import useAuthUser from "../../../Common/hooks/useAuthUser"; import request from "../../../Utils/request/request"; import routes from "../../../Redux/api"; +import { BedModel } from "../../Facility/models"; +import useQuery from "../../../Utils/request/useQuery"; +import { FieldLabel } from "../../Form/FormFields/FormField"; +import { BedSelect } from "../../Common/BedSelect"; interface HL7MonitorProps { assetId: string; @@ -151,3 +154,79 @@ const HL7Monitor = (props: HL7MonitorProps) => { ); }; export default HL7Monitor; + +const saveLink = async (assetId: string, bedId: string) => { + await request(routes.createAssetBed, { + body: { + asset: assetId, + bed: bedId, + }, + }); + Notification.Success({ msg: "AssetBed Link created successfully" }); +}; +const updateLink = async ( + assetbedId: string, + assetId: string, + bed: BedModel, +) => { + await request(routes.partialUpdateAssetBed, { + pathParams: { external_id: assetbedId }, + body: { + asset: assetId, + bed: bed.id ?? "", + }, + }); + Notification.Success({ msg: "AssetBed Link updated successfully" }); +}; + +function MonitorConfigure({ asset }: { asset: AssetData }) { + const [bed, setBed] = useState({}); + const [shouldUpdateLink, setShouldUpdateLink] = useState(false); + const { data: assetBed } = useQuery(routes.listAssetBeds, { + query: { asset: asset.id }, + onResponse: ({ res, data }) => { + if (res?.status === 200 && data && data.results.length > 0) { + setBed(data.results[0].bed_object); + setShouldUpdateLink(true); + } + }, + }); + + return ( +
{ + e.preventDefault(); + if (shouldUpdateLink) { + updateLink( + assetBed?.results[0].id as string, + asset.id as string, + bed as BedModel, + ); + } else { + saveLink(asset.id as string, bed?.id as string); + } + }} + > +
+
+ Bed + setBed(selected as BedModel)} + selected={bed} + error="" + multiple={false} + location={asset?.location_object?.id} + facility={asset?.location_object?.facility?.id} + not_occupied_by_asset_type={AssetClass.HL7MONITOR} + className="w-full" + /> +
+ + + {shouldUpdateLink ? "Update Bed" : "Save Bed"} + +
+
+ ); +} diff --git a/src/Components/Assets/AssetType/ONVIFCamera.tsx b/src/Components/Assets/AssetType/ONVIFCamera.tsx index 6f20f65836c..cad38270cef 100644 --- a/src/Components/Assets/AssetType/ONVIFCamera.tsx +++ b/src/Components/Assets/AssetType/ONVIFCamera.tsx @@ -1,9 +1,7 @@ import { useEffect, useState } from "react"; -import { AssetData, ResolvedMiddleware } from "../AssetTypes"; +import { ResolvedMiddleware } from "../AssetTypes"; 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"; @@ -14,9 +12,9 @@ import useAuthUser from "../../../Common/hooks/useAuthUser"; import request from "../../../Utils/request/request"; import routes from "../../../Redux/api"; import useQuery from "../../../Utils/request/useQuery"; - import CareIcon from "../../../CAREUI/icons/CareIcon"; import useOperateCamera from "../../CameraFeed/useOperateCamera"; +import CameraFeed from "../../CameraFeed/CameraFeed"; interface Props { assetId: string; @@ -36,17 +34,12 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [streamUuid, setStreamUuid] = useState(""); - const [bed, setBed] = useState({}); - const [newPreset, setNewPreset] = useState(""); - const [loadingAddPreset, setLoadingAddPreset] = useState(false); const [loadingSetConfiguration, setLoadingSetConfiguration] = useState(false); const { data: facility, loading } = useQuery(routes.getPermittedFacility, { pathParams: { id: facilityId }, }); const authUser = useAuthUser(); - const { operate } = useOperateCamera(assetId ?? "", true); - useEffect(() => { if (asset) { setAssetType(asset?.asset_class); @@ -90,49 +83,18 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { } }; - const addPreset = async (e: SyntheticEvent) => { - e.preventDefault(); - const data = { - bed_id: bed.id, - preset_name: newPreset, - }; - try { - setLoadingAddPreset(true); - - const { data: presetData } = await operate({ type: "get_status" }); + const { operate, key } = useOperateCamera(asset.id); - const { res } = await request(routes.createAssetBed, { - body: { - meta: { ...data, ...presetData }, - asset: assetId, - bed: bed?.id as string, - }, - }); - if (res?.status === 201) { - Notification.Success({ - msg: "Preset Added Successfully", - }); - setBed({}); - setNewPreset(""); - } else { - Notification.Error({ - msg: "Something went wrong..!", - }); - } - } catch (e) { - Notification.Error({ - msg: "Something went wrong..!", - }); - } - setLoadingAddPreset(false); - }; if (isLoading || loading || !facility) return ; return ( -
+
{["DistrictAdmin", "StateAdmin"].includes(authUser.user_type) && ( -
-
+ +
{ )} {assetType === "ONVIF" ? ( - +
+ +
) : null}
); diff --git a/src/Components/Assets/configure/CameraConfigure.tsx b/src/Components/Assets/configure/CameraConfigure.tsx deleted file mode 100644 index f92e2d81b7d..00000000000 --- a/src/Components/Assets/configure/CameraConfigure.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { SyntheticEvent } from "react"; -import { AssetData } from "../AssetTypes"; -import { BedSelect } from "../../Common/BedSelect"; -import { BedModel } from "../../Facility/models"; -import { Submit } from "../../Common/components/ButtonV2"; -import TextFormField from "../../Form/FormFields/TextFormField"; -import Card from "../../../CAREUI/display/Card"; -import { FieldErrorText } from "../../Form/FormFields/FormField"; -import CameraFeed from "../../CameraFeed/CameraFeed"; -import useOperateCamera from "../../CameraFeed/useOperateCamera"; - -interface CameraConfigureProps { - asset: AssetData; - addPreset(e: SyntheticEvent): void; - setBed(bed: BedModel): void; - bed: BedModel; - newPreset: string; - setNewPreset(preset: string): void; - isLoading: boolean; -} -export default function CameraConfigure({ - asset, - addPreset, - setBed, - bed, - isLoading, - newPreset, - setNewPreset, -}: CameraConfigureProps) { - const { operate, key } = useOperateCamera(asset.id); - - return ( -
- - -
-
- - setBed(selected as BedModel)} - selected={bed} - error="" - multiple={false} - location={asset?.location_object?.id} - facility={asset?.location_object?.facility?.id} - /> -
-
- - setNewPreset(e.value)} - errorClassName="hidden" - /> - {newPreset.length > 12 && ( - - )} -
-
-
- -
- -
- - - -
- ); -} diff --git a/src/Components/Assets/configure/MonitorConfigure.tsx b/src/Components/Assets/configure/MonitorConfigure.tsx deleted file mode 100644 index 785b82873de..00000000000 --- a/src/Components/Assets/configure/MonitorConfigure.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { useState } from "react"; -import { BedSelect } from "../../Common/BedSelect"; -import { BedModel } from "../../Facility/models"; -import { AssetClass, AssetData } from "../AssetTypes"; -import * as Notification from "../../../Utils/Notifications.js"; -import { Submit } from "../../Common/components/ButtonV2"; -import { FieldLabel } from "../../Form/FormFields/FormField"; -import request from "../../../Utils/request/request"; -import routes from "../../../Redux/api"; -import useQuery from "../../../Utils/request/useQuery"; -import CareIcon from "../../../CAREUI/icons/CareIcon"; - -const saveLink = async (assetId: string, bedId: string) => { - await request(routes.createAssetBed, { - body: { - asset: assetId, - bed: bedId, - }, - }); - Notification.Success({ msg: "AssetBed Link created successfully" }); -}; -const update_Link = async ( - assetbedId: string, - assetId: string, - bed: BedModel, -) => { - await request(routes.partialUpdateAssetBed, { - pathParams: { external_id: assetbedId }, - body: { - asset: assetId, - bed: bed.id ?? "", - }, - }); - Notification.Success({ msg: "AssetBed Link updated successfully" }); -}; - -export default function MonitorConfigure({ asset }: { asset: AssetData }) { - const [bed, setBed] = useState({}); - const [updateLink, setUpdateLink] = useState(false); - const { data: assetBed } = useQuery(routes.listAssetBeds, { - query: { asset: asset.id }, - onResponse: ({ res, data }) => { - if (res?.status === 200 && data && data.results.length > 0) { - setBed(data.results[0].bed_object); - setUpdateLink(true); - } - }, - }); - - return ( -
{ - e.preventDefault(); - if (updateLink) { - update_Link( - assetBed?.results[0].id as string, - asset.id as string, - bed as BedModel, - ); - } else { - saveLink(asset.id as string, bed?.id as string); - } - }} - > -
-
- Bed - setBed(selected as BedModel)} - selected={bed} - error="" - multiple={false} - location={asset?.location_object?.id} - facility={asset?.location_object?.facility?.id} - not_occupied_by_asset_type={AssetClass.HL7MONITOR} - className="w-full" - /> -
- - - {updateLink ? "Update Bed" : "Save Bed"} - -
-
- ); -} diff --git a/src/Components/CameraFeed/CameraFeedWithBedPresets.tsx b/src/Components/CameraFeed/CameraFeedWithBedPresets.tsx index 7268397b81a..11e5fc3f23e 100644 --- a/src/Components/CameraFeed/CameraFeedWithBedPresets.tsx +++ b/src/Components/CameraFeed/CameraFeedWithBedPresets.tsx @@ -1,32 +1,28 @@ import { useState } from "react"; -import { AssetBedModel, AssetData } from "../Assets/AssetTypes"; +import { AssetData } from "../Assets/AssetTypes"; import CameraFeed from "./CameraFeed"; import useQuery from "../../Utils/request/useQuery"; -import routes from "../../Redux/api"; -import useSlug from "../../Common/hooks/useSlug"; -import { CameraPresetDropdown } from "./AssetBedSelect"; +import { CameraPresetDropdown } from "./CameraPresetSelect"; import useOperateCamera from "./useOperateCamera"; import { classNames } from "../../Utils/utils"; +import { CameraPreset, FeedRoutes } from "./routes"; interface Props { asset: AssetData; } export default function LocationFeedTile(props: Props) { - const facility = useSlug("facility"); - const [preset, setPreset] = useState(); - - const { data, loading } = useQuery(routes.listAssetBeds, { - query: { limit: 100, facility, asset: props.asset?.id }, + const [preset, setPreset] = useState(); + const { operate, key } = useOperateCamera(props.asset.id); + const { data, loading } = useQuery(FeedRoutes.listPresets, { + query: { limit: 100, asset: props.asset.id, position: true }, }); - const { operate, key } = useOperateCamera(props.asset.id, true); - return ( string; - onChange?: (value: AssetBedModel) => void; + options: CameraPreset[]; + value?: CameraPreset; + label?: (value: CameraPreset) => string; + onChange?: (value: CameraPreset) => void; } export default function CameraPresetSelect(props: Props) { @@ -71,16 +70,13 @@ export const CameraPresetDropdown = ( props: Props & { placeholder: string }, ) => { const selected = props.value; - - const options = props.options.filter(({ meta }) => meta.type !== "boundary"); - const label = props.label ?? defaultLabel; return (
- {options.length === 0 + {props.options.length === 0 ? "No presets" : selected ? label(selected) @@ -113,7 +109,7 @@ export const CameraPresetDropdown = ( as="ul" className="absolute z-20 max-h-48 w-full overflow-auto rounded-b-lg bg-white py-1 text-base shadow-lg ring-1 ring-secondary-500 focus:outline-none md:max-h-60" > - {options?.map((obj) => ( + {props.options.map((obj) => ( { - return `${bed_object.name}: ${meta.preset_name}`; +const defaultLabel = (preset: CameraPreset) => { + return `${preset.asset_bed.bed_object.name}: ${preset.name}`; }; diff --git a/src/Components/CameraFeed/CameraPresetsConfigure.tsx b/src/Components/CameraFeed/CameraPresetsConfigure.tsx index 74fb089a214..851f144fa13 100644 --- a/src/Components/CameraFeed/CameraPresetsConfigure.tsx +++ b/src/Components/CameraFeed/CameraPresetsConfigure.tsx @@ -1,48 +1,54 @@ import { useState } from "react"; import routes from "../../Redux/api"; import useQuery from "../../Utils/request/useQuery"; -import { AssetBedModel, AssetData } from "../Assets/AssetTypes"; +import { AssetBedModel } from "../Assets/AssetTypes"; import Page from "../Common/components/Page"; import Loading from "../Common/Loading"; import CameraFeed from "./CameraFeed"; import useOperateCamera from "./useOperateCamera"; -import ButtonV2 from "../Common/components/ButtonV2"; +import ButtonV2, { Submit } from "../Common/components/ButtonV2"; import CareIcon from "../../CAREUI/icons/CareIcon"; import request from "../../Utils/request/request"; -import { FeedRoutes } from "./routes"; +import { CameraPreset, FeedRoutes, GetStatusResponse } from "./routes"; import { classNames } from "../../Utils/utils"; +import { + Listbox, + ListboxButton, + ListboxOption, + ListboxOptions, +} from "@headlessui/react"; +import { dropdownOptionClassNames } from "../Form/MultiSelectMenuV2"; +import DialogModal from "../Common/Dialog"; +import TextFormField from "../Form/FormFields/TextFormField"; +import { Success } from "../../Utils/Notifications"; type Props = { locationId: string; -} & ( - | { assetId: string; bedId?: undefined } - | { assetId?: undefined; bedId?: string } - | { assetId: string; bedId: string } -); + bedId: string; +}; + +type OnvifPreset = { name: string; value: number }; export default function CameraPresetsConfigure(props: Props) { const [current, setCurrent] = useState(); const camerasQuery = useQuery(routes.listAssets, { - query: { location: props.locationId, asset_class: "ONVIF" }, - prefetch: !props.assetId, + query: { + location: props.locationId, + asset_class: "ONVIF", + }, }); - // const bedsQuery = useQuery(routes.listFacilityBeds, { - // query: { location: props.locationId }, - // prefetch: !props.bedId, - // }); - const assetBedsQuery = useQuery(routes.listAssetBeds, { query: { - asset: props.assetId, bed: props.bedId, }, onResponse: ({ data }) => setCurrent(data?.results[0]), }); - const cameraPresetsQuery = useQuery(FeedRoutes.listPresets, { + const cameraPresetsQuery = useQuery(FeedRoutes.listAssetBedPresets, { pathParams: { assetbed_id: current?.id ?? "" }, + query: { position: true }, prefetch: !!current?.id, }); @@ -62,7 +68,7 @@ export default function CameraPresetsConfigure(props: Props) { return (
-
+
Cameras
    @@ -72,13 +78,13 @@ export default function CameraPresetsConfigure(props: Props) {
  • - + {assetBed.asset_object.name}
    @@ -96,8 +102,7 @@ export default function CameraPresetsConfigure(props: Props) { - +
  • @@ -123,7 +128,7 @@ export default function CameraPresetsConfigure(props: Props) { {camerasNotLinked.map((camera) => (
  • {camera.name} @@ -140,18 +145,22 @@ export default function CameraPresetsConfigure(props: Props) { const { res } = await request(routes.createAssetBed, { body: { asset: camera.id, bed: props.bedId }, }); - if (res?.ok) { camerasQuery.refetch(); assetBedsQuery.refetch(); } }} > - +
  • ))}
+ {camerasNotLinked.length === 0 && assetBeds.length === 0 && ( + + No cameras available in this location + + )}
{/* Camera Presets */} @@ -190,16 +199,21 @@ export default function CameraPresetsConfigure(props: Props) { border tooltip="Delete preset" tooltipClassName="tooltip-bottom translate-y-2 -translate-x-1/2 text-xs" - // onClick={async () => { - // const { res } = await request(routes.deleteAssetBed, { - // pathParams: { external_id: assetBed.id }, - // }); - - // if (res?.ok) { - // camerasQuery.refetch(); - // assetBedsQuery.refetch(); - // } - // }} + onClick={async () => { + const { res } = await request( + FeedRoutes.deletePreset, + { + pathParams: { + assetbed_id: current.id, + id: preset.id, + }, + }, + ); + if (res?.ok) { + Success({ msg: "Preset deleted" }); + cameraPresetsQuery.refetch(); + } + }} > @@ -225,8 +239,9 @@ export default function CameraPresetsConfigure(props: Props) {
) : ( cameraPresetsQuery.refetch()} /> )}
@@ -235,64 +250,150 @@ export default function CameraPresetsConfigure(props: Props) { ); } -const LinkedCameraFeed = (props: { asset: AssetData }) => { - const { operate, key } = useOperateCamera(props.asset.id, true); - const [cameraPresets, setCameraPresets] = useState>(); - // const [search, setSearch] = useState(""); +const LinkedCameraFeed = (props: { + assetBed: AssetBedModel; + onPresetCreated: () => void; +}) => { + const { operate, key } = useOperateCamera(props.assetBed.asset_object.id); + const [onvifPresets, setOnvifPresets] = useState(); + const [currentOnvifPreset, setCurrentOnvifPreset] = useState(); + const [createPresetPosition, setCreatePresetPosition] = + useState(); + const [presetName, setPresetName] = useState(""); return (
+ { + setCreatePresetPosition(undefined); + setPresetName(""); + }} + > + setPresetName(value)} + errorClassName="hidden" + placeholder="Specify an identifiable name for the new preset" + /> + {/*
+ {JSON.stringify(createPresetPosition, undefined, " ")} +
*/} +
+ { + const { res } = await request(FeedRoutes.createPreset, { + pathParams: { assetbed_id: props.assetBed.id }, + body: { + name: presetName, + position: createPresetPosition, + }, + }); + if (!res?.ok) { + return; + } + setCreatePresetPosition(undefined); + setPresetName(""); + Success({ msg: "Preset created" }); + props.onPresetCreated(); + }} + disabled={!presetName} + /> +
+
{ - if (!cameraPresets) { - setCameraPresets(presets); + if (!onvifPresets) { + setOnvifPresets( + Object.entries(presets).map(([name, value]) => ({ + name, + value, + })), + ); } }} - /> + > +
+ { + setCurrentOnvifPreset(preset); + operate({ + type: "goto_preset", + data: { + preset: preset.value, + }, + }); + }} + disabled={!onvifPresets?.length} + > +
+ + + {!onvifPresets?.length + ? "No presets" + : (currentOnvifPreset?.name ?? + "Select to move to an ONVIF Preset")} + + + + + + + {onvifPresets?.map((obj) => ( + + classNames( + dropdownOptionClassNames(args), + "px-2 py-1.5", + ) + } + value={obj} + > + {obj.name} + + ))} + +
+
+ { + const { data } = await operate({ type: "get_status" }); + if (data) { + setCreatePresetPosition( + (data as GetStatusResponse).result.position, + ); + } + }} + > + + Create new preset + +
+
- {/*
-
ONVIF Presets
- setSearch(e.value)} - errorClassName="hidden" - /> -
    - {!!cameraPresets && - Object.keys(cameraPresets).map((key) => ( -
  • - - {key} - -
    - setCurrent(assetBed)} - > - - -
    -
  • - ))} -
-
*/}
); }; diff --git a/src/Components/CameraFeed/routes.ts b/src/Components/CameraFeed/routes.ts index bc4b1c0cd98..025bdd52b3b 100644 --- a/src/Components/CameraFeed/routes.ts +++ b/src/Components/CameraFeed/routes.ts @@ -30,8 +30,8 @@ export type GetPresetsResponse = { export type CameraPreset = { readonly id: string; name: string; - readonly asset_bed_object: AssetBedModel; - position?: { x: number; y: number; z: number }; + readonly asset_bed: AssetBedModel; + position?: PTZPayload; boundary?: { x0: number; y0: number; x1: number; y1: number }; readonly created_by: PerformedByModel; readonly updated_by: PerformedByModel; @@ -50,11 +50,16 @@ export const FeedRoutes = { TBody: Type<{ action: OperationAction }>(), }, - listPresets: { + listAssetBedPresets: { path: "/api/v1/assetbed/{assetbed_id}/camera_presets/", method: "GET", TRes: Type>(), }, + listPresets: { + path: "/api/v1/camera_presets/", + method: "GET", + TRes: Type>(), + }, createPreset: { path: "/api/v1/assetbed/{assetbed_id}/camera_presets/", method: "POST", diff --git a/src/Components/CameraFeed/useOperateCamera.ts b/src/Components/CameraFeed/useOperateCamera.ts index bfddbf5b887..0e65fb0130c 100644 --- a/src/Components/CameraFeed/useOperateCamera.ts +++ b/src/Components/CameraFeed/useOperateCamera.ts @@ -54,7 +54,7 @@ export type OperationAction = * This hook is used to control the PTZ of a camera asset and retrieve other related information. * @param id The external id of the camera asset */ -export default function useOperateCamera(id: string, silent = false) { +export default function useOperateCamera(id: string) { const [key, setKey] = useState(0); return { @@ -70,14 +70,14 @@ export default function useOperateCamera(id: string, silent = false) { type: "get_status", }, }, - silent, + silent: true, }); } return request(FeedRoutes.operateAsset, { pathParams: { id }, body: { action }, - silent, + silent: true, }); }, }; diff --git a/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx index 2e999d1956e..6a8784ba6fb 100644 --- a/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx +++ b/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx @@ -1,14 +1,12 @@ import { useEffect, useRef, useState } from "react"; import { ConsultationTabProps } from "./index"; -import { AssetBedModel, AssetData } from "../../Assets/AssetTypes"; -import routes from "../../../Redux/api"; +import { AssetData } from "../../Assets/AssetTypes"; import useQuery from "../../../Utils/request/useQuery"; import CameraFeed from "../../CameraFeed/CameraFeed"; import Loading from "../../Common/Loading"; -import AssetBedSelect from "../../CameraFeed/AssetBedSelect"; +import CameraPresetSelect from "../../CameraFeed/CameraPresetSelect"; import { triggerGoal } from "../../../Integrations/Plausible"; import useAuthUser from "../../../Common/hooks/useAuthUser"; -import useSlug from "../../../Common/hooks/useSlug"; import CareIcon from "../../../CAREUI/icons/CareIcon"; import ButtonV2 from "../../Common/components/ButtonV2"; import useOperateCamera, { @@ -20,18 +18,21 @@ import ConfirmDialog from "../../Common/ConfirmDialog"; import useBreakpoints from "../../../Common/hooks/useBreakpoints"; import { Warn } from "../../../Utils/Notifications"; import { useTranslation } from "react-i18next"; -import { GetStatusResponse } from "../../CameraFeed/routes"; +import { + CameraPreset, + FeedRoutes, + GetStatusResponse, +} from "../../CameraFeed/routes"; import StillWatching from "../../CameraFeed/StillWatching"; export const ConsultationFeedTab = (props: ConsultationTabProps) => { const { t } = useTranslation(); const authUser = useAuthUser(); - const facility = useSlug("facility"); const bed = props.consultationData.current_bed?.bed_object; const feedStateSessionKey = `encounterFeedState[${props.consultationId}]`; const [asset, setAsset] = useState(); - const [preset, setPreset] = useState(); + const [preset, setPreset] = useState(); const [showPresetSaveConfirmation, setShowPresetSaveConfirmation] = useState(false); const [isUpdatingPreset, setIsUpdatingPreset] = useState(false); @@ -52,23 +53,19 @@ export const ConsultationFeedTab = (props: ConsultationTabProps) => { } }, []); - const { key, operate } = useOperateCamera(asset?.id ?? "", true); + const { key, operate } = useOperateCamera(asset?.id ?? ""); - const { data, loading, refetch } = useQuery(routes.listAssetBeds, { - query: { limit: 100, facility, bed: bed?.id, asset: asset?.id }, + const presetsQuery = useQuery(FeedRoutes.listPresets, { + query: { bed: bed?.id, position: true, limit: 100 }, prefetch: !!bed, onResponse: ({ data }) => { if (!data) { return; } - const presets = data.results.filter( - (obj) => - obj.asset_object.meta?.asset_type === "CAMERA" && - obj.meta.type !== "boundary", - ); - + const presets = data.results; const lastStateJSON = sessionStorage.getItem(feedStateSessionKey); + const preset = (() => { if (lastStateJSON) { @@ -77,23 +74,34 @@ export const ConsultationFeedTab = (props: ConsultationTabProps) => { return presets.find((obj) => obj.id === lastState.value); } if (lastState.type === "position") { + const assetBedObj = presets.find( + (p) => p.asset_bed.id === lastState.assetBed, + )?.asset_bed; + + if (!assetBedObj) { + return; + } + return { ...presets[0], id: "", - meta: { ...presets[0].meta, position: lastState.value }, - }; + asset_bed: assetBedObj, + position: lastState.value, + } satisfies CameraPreset; } } })() ?? presets[0]; + console.log({ preset, presets }); + if (preset) { setPreset(preset); - setAsset(preset.asset_object); + setAsset(preset.asset_bed.asset_object); } }, }); - const presets = data?.results; + const presets = presetsQuery.data?.results; const handleUpdatePreset = async () => { if (!preset) return; @@ -102,17 +110,17 @@ export const ConsultationFeedTab = (props: ConsultationTabProps) => { const { data } = await operate({ type: "get_status" }); const { position } = (data as { result: { position: PTZPayload } }).result; - - const { data: updated } = await request(routes.partialUpdateAssetBed, { - pathParams: { external_id: preset.id }, + const { data: updated } = await request(FeedRoutes.updatePreset, { + pathParams: { + assetbed_id: preset.asset_bed.id, + external_id: preset.id, + }, body: { - asset: preset.asset_object.id, - bed: preset.bed_object.id, - meta: { ...preset.meta, position }, + position, }, }); - await refetch(); + await presetsQuery.refetch(); setPreset(updated); setHasMoved(false); @@ -124,7 +132,7 @@ export const ConsultationFeedTab = (props: ConsultationTabProps) => { if (divRef.current) { divRef.current.scrollIntoView({ behavior: "smooth" }); } - }, [!!bed, loading, !!asset, divRef.current]); + }, [!!bed, presetsQuery.loading, !!asset, divRef.current]); useEffect(() => { if (preset?.id) { @@ -138,7 +146,7 @@ export const ConsultationFeedTab = (props: ConsultationTabProps) => { } }, [feedStateSessionKey, preset]); - if (loading) { + if (presetsQuery.loading) { return ; } @@ -169,8 +177,11 @@ export const ConsultationFeedTab = (props: ConsultationTabProps) => { { + if (!preset) { + return; + } setHasMoved(true); setTimeout(async () => { const { data } = await operate({ type: "get_status" }); @@ -179,6 +190,7 @@ export const ConsultationFeedTab = (props: ConsultationTabProps) => { feedStateSessionKey, JSON.stringify({ type: "position", + assetBed: preset.asset_bed.id, value: (data as GetStatusResponse).result.position, } satisfies LastAccessedPosition), ); @@ -204,26 +216,19 @@ export const ConsultationFeedTab = (props: ConsultationTabProps) => {
{presets ? ( <> - obj.meta.preset_name} + label={(obj) => obj.name} value={preset} onChange={(value) => { triggerGoal("Camera Preset Clicked", { - presetName: preset?.meta?.preset_name, + presetName: preset?.name, consultationId: props.consultationId, userId: authUser.id, result: "success", }); setHasMoved(false); - // Voluntarily copying to trigger change of reference of the position attribute, so that the useEffect of CameraFeed that handles the moves gets triggered. - setPreset({ - ...value, - meta: { - ...value.meta, - position: { ...value.meta.position }, - }, - }); + setPreset(value); }} /> {isUpdatingPreset ? ( @@ -269,6 +274,7 @@ type LastAccessedPreset = { type LastAccessedPosition = { type: "position"; + assetBed: string; value: PTZPayload; };