import { Button, DialogActions, DialogContent, DialogTitle, Grid2 as Grid, MenuItem, TextField, Theme, Typography } from "@mui/material"; import { makeStyles } from "@mui/styles"; import DeleteButton from "common/DeleteButton"; import ResponsiveDialog from "common/ResponsiveDialog"; import SearchSelect, { Option } from "common/SearchSelect"; import { usePreferenceAPI, usePrevious, useSnackbar } from "hooks"; import { cloneDeep } from "lodash"; import { Backpack, Component, Device, Interaction } from "models"; import { backpackOptions } from "pbHelpers/Backpack"; import { DeviceComponentKey } from "pbHelpers/Component"; import { ListDeviceProductDescribers } from "products/DeviceProduct"; import { pond } from "protobuf-ts/pond"; import { useBackpackAPI } from "providers/pond/backpackAPI"; import React, { useCallback, useEffect, useState } from "react"; const useStyles = makeStyles((theme: Theme) => { return ({ contentSpacing: { marginTop: theme.spacing(1), marginBottom: theme.spacing(1) } }) }); interface Props { isDialogOpen: boolean; closeDialogCallback: Function; refreshCallback: Function; device: Device; components: Component[]; interactions: Interaction[]; tagIds: string[]; } export default function SaveDeviceProfile(props: Props) { const backpackAPI = useBackpackAPI(); const preferenceAPI = usePreferenceAPI(); const classes = useStyles(); const { isDialogOpen, closeDialogCallback, refreshCallback, device, components, interactions, tagIds } = props; const { success, error } = useSnackbar(); const prevOpen = usePrevious(isDialogOpen); const [backpacks, setBackpacks] = useState([]); const [backpackName, setBackpackName] = useState(); const [loading, setLoading] = useState(false); const [isNew, setIsNew] = useState(false); const [targetBackpackID, setTargetBackpackID] = useState(0); const [product, setProduct] = useState(0); const defaultState = () => { setBackpacks([]); setBackpackName(""); setIsNew(false); setTargetBackpackID(0); }; const close = () => { closeDialogCallback(); defaultState(); }; const loadBackpacks = useCallback(() => { const { listBackpacks } = backpackAPI; setLoading(true); listBackpacks() .then((response: any) => { const rawBackpacks = response.data.backpacks; const backpacks: Backpack[] = []; if (rawBackpacks && rawBackpacks.length > 0) { rawBackpacks.forEach((b: any) => { backpacks.push(Backpack.create(pond.Backpack.fromObject(b))); }); } setBackpacks(backpacks.sort((b1, b2) => (b1.name() > b2.name() ? 1 : -1))); }) .catch((err: any) => { setBackpacks([]); error(err ? err : "Error occured while loading device profiles"); }) .finally(() => setLoading(false)); }, [backpackAPI, error]); useEffect(() => { if (isDialogOpen === true && isDialogOpen !== prevOpen) { loadBackpacks(); } }, [isDialogOpen, prevOpen, loadBackpacks]); const assembleBackpack = (): Promise => { const { getPreferences } = preferenceAPI; return new Promise((resolve, reject) => { let getDevicePreference = getPreferences("device", [device.id().toString()]); let getComponentPreferences = getPreferences( "component", components.map(c => DeviceComponentKey(device, c)) ); let promises = [getDevicePreference, getComponentPreferences]; Promise.all(promises) .then(responses => { let rDevicePreferences = responses[0]; let rComponentPreferences = responses[1]; let componentPreferences = {} as { [k: string]: pond.UserPreferences; }; rComponentPreferences.forEach(pref => { let match = components.find(c => DeviceComponentKey(device, c) === pref.key); if (match && pref.preferences) { componentPreferences[match.locationString()] = pref.preferences; } }); let deviceCopy = cloneDeep(device.settings); deviceCopy.deviceId = 0; deviceCopy.platform = pond.DevicePlatform.DEVICE_PLATFORM_INVALID; if (rDevicePreferences.length < 1) { rDevicePreferences.push(pond.ModelPreferences.create()); } let backpack = pond.BackpackSettings.create({ backpackId: targetBackpackID, product: product, name: backpackName, device: deviceCopy, components: components.map(c => { let component = cloneDeep(c.settings); component.key = ""; return component; }), interactions: interactions.map(i => { let interaction = cloneDeep(i.settings); interaction.key = ""; return interaction; }), tagKeys: tagIds, devicePreferences: rDevicePreferences[0].preferences, componentPreferences: componentPreferences }); resolve(backpack); }) .catch(() => { let reason = "Failed loading user preferences"; error(reason); reject(reason); }); }); }; const submit = () => { const { addBackpack, updateBackpack } = backpackAPI; assembleBackpack().then((backpack: pond.BackpackSettings) => { if (isNew) { addBackpack(backpack) .then((response: any) => { success("Successfully created " + backpackName + " using " + device.name() + "!"); refreshCallback(); }) .catch((err: any) => { error(err ? err : "Error occured while creating " + backpackName + "."); }) .finally(() => close()); } else { updateBackpack(targetBackpackID, backpack) .then((response: any) => { success("Successfully updated " + backpackName + " using " + device.name() + "!"); refreshCallback(); }) .catch((err: any) => { error(err ? err : "Error occured while updating " + backpackName + "."); }) .finally(() => close()); } }); }; const removeSelectedProfile = () => { const { removeBackpack } = backpackAPI; const backpackID = targetBackpackID; const backpack = backpacks.find(b => b.id() === backpackID); if (targetBackpackID && backpack) { let backpackName = backpack.name(); removeBackpack(backpackID) .then(() => { success("Successfully removed " + backpackName); let updatedBackpacks = cloneDeep(backpacks); defaultState(); setBackpacks(updatedBackpacks.filter(b => b.id() !== backpackID)); }) .catch(err => error(err ? err : "Error occured while removing " + backpackName)); } }; const changeTargetBackpack = (option: Option | null) => { let isNew = option && option.new === true ? true : false; setIsNew(isNew); setTargetBackpackID(isNew || !(option && option.value) ? 0 : Number(option.value)); setBackpackName(option && option.label ? option.label : backpackName); }; const isFormValid = (): boolean => { const isValidBackpackName = isNew ? backpackName !== "" : true; const isValidTargetBackpack = isNew ? true : targetBackpackID > 0; return isValidBackpackName && isValidTargetBackpack; }; // UI Begins const content = () => { const options = backpackOptions(backpacks); let selected: Option | undefined = options.find( option => (option.value as number) === targetBackpackID ); return ( changeTargetBackpack(option)} loading={loading} creatable /> setProduct(+e.target.value)} margin="normal" fullWidth variant="outlined"> {ListDeviceProductDescribers().map(describer => ( {describer.label} ))} ); }; const actions = () => { return ( removeSelectedProfile()}> Delete ); }; return ( Save Device Profile {device.name()} {content()} {actions()} ); }