import { Button, Checkbox, darken, DialogActions, DialogContent, DialogTitle, Divider, Grid2, lighten, List, ListItem, ListItemSecondaryAction, ListItemText, Paper, Step, StepLabel, Stepper, Tab, Tabs, TextField, Theme, Typography } from "@mui/material"; import { makeStyles } from "@mui/styles"; import CancelSubmit from "common/CancelSubmit"; // import { darken, lighten } from "@material-ui/core/styles/colorManipulator"; // import { Theme } from "@material-ui/core/styles/createMuiTheme"; import DeleteButton from "common/DeleteButton"; import Loader from "common/Loader"; import ResponsiveDialog from "common/ResponsiveDialog"; import SearchBar from "common/SearchBar"; // import SearchBar from "common/SearchBar"; // import RemoveGroup from "group/RemoveGroup"; import { useDeviceAPI, useGroupAPI, usePrevious, useSnackbar } from "hooks"; import { Device, Group } from "models"; import { filterDevices } from "pbHelpers/Device"; import { groupsAreEqual } from "pbHelpers/Group"; import React, { useCallback, useEffect, useState } from "react"; import RemoveGroup from "./RemoveGroup"; const useStyles = makeStyles((theme: Theme) => { return ({ deviceListContainer: { marginBottom: theme.spacing(1) }, devicesList: { overflow: "auto", minHeight: "25vh", maxHeight: "35vh", height: "auto", background: theme.palette.mode === "dark" ? lighten(theme.palette.background.paper, 0.04) : darken(theme.palette.background.paper, 0.04) } }); }); interface Props { initialGroup?: Group; mode?: "add" | "update" | "remove" | undefined; isDialogOpen: boolean; closeDialogCallback: Function; refreshCallback: () => void; canEdit?: boolean; groupDevices?: Device[]; } export default function GroupSettings(props: Props) { const classes = useStyles(); const { success, error, warning } = useSnackbar(); const { initialGroup, mode, isDialogOpen, closeDialogCallback, refreshCallback, canEdit, groupDevices, } = props; const prevInitialGroup = usePrevious(initialGroup); const groupAPI = useGroupAPI(); const deviceAPI = useDeviceAPI(); const snackbar = useSnackbar() const [devices, setDevices] = useState([]); const [group, setGroup] = useState(initialGroup ? Group.clone(initialGroup) : new Group()); const [isRemoveGroupOpen, setIsRemoveGroupOpen] = useState( mode === "remove" ? true : false ); const [tabIndex, setTabIndex] = useState(0); const prevTabIndex = usePrevious(tabIndex); const [steps] = useState(["General group information", "Select devices for the group"]); const [deviceSearch, setDeviceSearch] = useState(""); const [loadingDevices, setLoadingDevices] = useState(false); const [groupDeviceNumbers, setGroupDeviceNumbers] = useState([]) useEffect(() => { let newNumbers: number[] = [] groupDevices?.forEach(device => { newNumbers.push(device.id()) }) setGroupDeviceNumbers(newNumbers) }, [groupDevices]) const loadDevices = useCallback(() => { setLoadingDevices(true); deviceAPI .list(1000000, 0, "asc") .then((response: any) => { let rDevices: Device[] = response.data.devices ? response.data.devices.map((device: any) => Device.any(device)) : []; setDevices(rDevices); }) .catch((_error: any) => { setDevices([]); }) .finally(() => setLoadingDevices(false)); }, [deviceAPI]); useEffect(() => { if (prevInitialGroup !== initialGroup) { setGroup(initialGroup ? Group.clone(initialGroup) : new Group()); //setDevices([]); } if (prevTabIndex !== 1 && tabIndex === 1) { loadDevices(); } }, [initialGroup, loadDevices, prevInitialGroup, prevTabIndex, props, tabIndex]); const close = () => { closeDialogCallback(); setGroup(initialGroup ? Group.clone(initialGroup) : new Group()); setDevices([]); setTabIndex(0); }; const submit = () => { const groupName = group.name(); switch (mode) { case "add": groupAPI .addGroup(group.settings) .then((_response: any) => { success(groupName + " was successfully created"); close(); refreshCallback(); }) .catch((err: any) => { err.response.data.error ? warning(err.response.data.error) : error("Error occured when creating a group"); close(); }) .finally(() => setTabIndex(0)); break; default: groupAPI .updateGroup(group.id(), group.settings) .then((_response: any) => { success(groupName + " was successfully updated"); close(); refreshCallback(); }) .catch((err: any) => { err.response.data.error ? warning(err.response.data.error) : error("Error occured when updating " + groupName); close(); }); break; } }; const openRemoveGroup = () => { setIsRemoveGroupOpen(true); }; const closeRemoveGroup = () => { setIsRemoveGroupOpen(false); if (mode === "remove") { close(); } }; const isFormValid = () => { const validGroup: boolean = group !== undefined && group !== null; const validUpdate: boolean = mode !== "update" || !groupsAreEqual(Group.clone(initialGroup), group); return validGroup && validUpdate; }; const changeTab = (value: number) => { setTabIndex(value); }; const nextStep = () => { let nextStepIndex = tabIndex + 1; setTabIndex(nextStepIndex); }; const backStep = () => { let backStepIndex = tabIndex - 1; setTabIndex(backStepIndex); }; const changeDeviceSearch = (value: string) => { setDeviceSearch(value); }; const changeName = (event: any) => { let updatedGroup = Group.clone(group); updatedGroup.settings.name = event.target.value; setGroup(updatedGroup); }; const changeDescription = (event: any) => { let updatedGroup = Group.clone(group); updatedGroup.settings.description = event.target.value; setGroup(updatedGroup); }; const changeDevices = (device: number) => { let updatedGroup = Group.clone(group); const exists = updatedGroup.settings.devices.includes(device); if (exists) { updatedGroup.settings.devices = updatedGroup.settings.devices.filter( groupDevice => groupDevice !== device ); } else { updatedGroup.settings.devices.push(device); } setGroup(updatedGroup); }; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UI BEGINS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ const title = () => { switch (mode) { case "add": return ( Group Creation Groups are a great way to organize your devices ); case "update": return ( Group Settings {group.name()} ); default: return ; } }; const generalGroupContent = () => { return ( ); }; const devicesGroupContent = () => { if (mode !== "add") return devicesTab(); return ( {loadingDevices ? ( ) : ( {filterDevices(deviceSearch, devices, []) .sort((a, b: Device) => a.name().localeCompare(b.name())) .map(device => { const label = `checkbox-list-secondary-label-${device.id()}`; return ( changeDevices(device.id())}> changeDevices(device.id())} checked={groupDeviceNumbers.includes(device.id())} inputProps={{ "aria-labelledby": label }} disabled={!canEdit} /> ); })} )} ); }; //useEffect(() => { // if (tabIndex === 1) loadDevices() //}, [groupDevices]) const addDevice = (device: number) => { groupAPI.addDevice(group.id(), device).then(() => { let newDevices = [...groupDeviceNumbers]; newDevices.push(device) setGroupDeviceNumbers(newDevices) snackbar.success("Device " + device + " successfully added to group") refreshCallback(); }).catch(() => { snackbar.error("Failed to add device") }); }; const removeDevice = (device: number) => { groupAPI.removeDevice(group.id(), device).then(() => { let newDevices = groupDeviceNumbers.filter(dev => dev !== device) setGroupDeviceNumbers(newDevices) snackbar.success("Device " + device + " successfully removed from group") refreshCallback(); }).catch(() => { snackbar.error("Failed to remove device") }) }; const devicesTab = () => { return ( {loadingDevices ? ( ) : ( {filterDevices(deviceSearch, devices, []) .sort((a, b: Device) => a.name().localeCompare(b.name())) .map(device => { const label = `checkbox-list-secondary-label-${device.id()}`; return ( changeDevices(device.id())}> { if (checked) addDevice(device.id()); else removeDevice(device.id()); }} // checked={Boolean(groupDevices?.find(dev => dev.id() === device.id()))} checked={groupDeviceNumbers.includes(device.id())} inputProps={{ "aria-labelledby": label }} disabled={!canEdit} /> ); })} )} ); }; const content = () => { if (mode === "add") { return ( {tabIndex === 0 && generalGroupContent()} {tabIndex === 1 && devicesGroupContent()} {steps.map(label => ( {label} ))} ); } else if (mode === "update") { return ( {tabIndex === 0 && generalGroupContent()} {tabIndex === 1 && devicesGroupContent()} ); } return ; }; const actions = () => { return ( {mode === "add" ? ( ) : ( mode === "update" && canEdit && Delete )} {/* {(mode === "add" && tabIndex === steps.length - 1) || mode === "update" ? canEdit && ( ) : mode === "add" && ( )} */} ); }; const dialogs = () => { return ( ); }; return ( {title()} {mode === "update" && ( { changeTab(value); }} indicatorColor="secondary" textColor="inherit" variant="fullWidth"> )} {content()} {actions()} {dialogs()} ); }