frontend/src/component/ComponentActions.tsx

213 lines
6.3 KiB
TypeScript

import {
Divider,
IconButton,
ListItemIcon,
ListItemText,
Menu,
MenuItem,
Theme,
Tooltip
} from "@mui/material";
import { AddCircle, DeleteOutline, MoreVert, SaveAlt, Settings } from "@mui/icons-material";
import ExportDataSettings from "component/ExportDataSettings";
import InteractionSettings from "interactions/InteractionSettings";
import { Component, Device } from "models";
import { Moment } from "moment";
import { isController, isSource } from "pbHelpers/ComponentType";
import { DeviceAvailabilityMap, OffsetAvailabilityMap } from "pbHelpers/DeviceAvailability";
import { pond } from "protobuf-ts/pond";
import { useGlobalState } from "providers";
import React, { useState } from "react";
import { or } from "utils/types";
import ComponentSettings from "./ComponentSettings";
import { green, teal } from "@mui/material/colors";
import { makeStyles } from "@mui/styles";
const useStyles = makeStyles((_theme: Theme) => {
return ({
addIcon: {
color: green["500"],
"&:hover": {
color: green["600"]
}
},
copyIcon: {
color: teal["500"],
"&:hover": {
color: teal["600"]
}
},
exportDataIcon: {
color: green["300"],
"&:hover": {
color: green["400"]
}
},
red: {
color: "var(--status-alert)"
}
})
});
interface Props {
device: Device;
component: Component;
components: Component[];
availablePositions: DeviceAvailabilityMap;
availableOffsets: OffsetAvailabilityMap;
permissions: Array<pond.Permission>;
refreshCallback: () => void;
initialStartDate?: Moment;
initialEndDate?: Moment;
deviceComponentPreferences?: pond.DeviceComponentPreferences;
}
export default function ComponentActions(props: Props) {
const classes = useStyles();
const {
device,
component,
components,
availablePositions,
availableOffsets,
permissions,
refreshCallback,
initialStartDate,
initialEndDate,
deviceComponentPreferences
} = props;
const [isComponentSettingsOpen, setIsComponentSettingsOpen] = useState<boolean>(false);
const [isAddInteractionOpen, setIsAddInteractionOpen] = useState<boolean>(false);
const [isExportDataOpen, setIsExportDataOpen] = useState<boolean>(false);
const [anchorEl, setAnchorEl] = useState<Element | null>(null);
const [componentSettingsMode, setComponentSettingsMode] = useState<string>("");
const [isJSON, setIsJSON] = useState(false);
const [{ user, as }] = useGlobalState();
const openMoreMenu = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
setAnchorEl(event.currentTarget);
};
const closeMoreMenu = () => {
setAnchorEl(null);
setIsComponentSettingsOpen(false);
};
const openComponentSettingsDialog = (mode: string) => {
setIsComponentSettingsOpen(true);
setComponentSettingsMode(or(mode, ""));
};
const closeComponentSettingsDialog = () => {
setIsComponentSettingsOpen(false);
};
const menu = () => {
const canEdit = permissions.includes(pond.Permission.PERMISSION_WRITE);
const canAddInteraction =
canEdit && (isSource(component.settings.type) || isController(component.settings.type));
return (
<Menu
id="moreMenu"
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={closeMoreMenu}
disableAutoFocusItem>
{canAddInteraction && (
<MenuItem dense onClick={() => setIsAddInteractionOpen(true)} divider>
<ListItemIcon>
<AddCircle className={classes.addIcon} />
</ListItemIcon>
<ListItemText primary="Add Interaction" />
</MenuItem>
)}
<MenuItem
dense
onClick={() => {
setIsJSON(false);
setIsExportDataOpen(true);
}}>
<ListItemIcon>
<SaveAlt className={classes.exportDataIcon} />
</ListItemIcon>
<ListItemText primary="Export Data" />
</MenuItem>
{canEdit && <Divider />}
{canEdit && (
<MenuItem dense onClick={() => openComponentSettingsDialog("remove")}>
<ListItemIcon>
<DeleteOutline className={classes.red} />
</ListItemIcon>
<ListItemText primary="Delete" />
</MenuItem>
)}
</Menu>
);
};
const dialogs = () => {
const canEdit = permissions.includes(pond.Permission.PERMISSION_WRITE);
type Mode = "add" | "remove" | "update" | undefined;
return (
<React.Fragment>
<ComponentSettings
mode={or(componentSettingsMode as Mode, "")}
device={device}
component={component}
isDialogOpen={isComponentSettingsOpen}
closeDialogCallback={closeComponentSettingsDialog}
refreshCallback={refreshCallback}
availablePositions={availablePositions}
availableOffsets={availableOffsets}
canEdit={canEdit}
deviceComponentPrefs={deviceComponentPreferences}
/>
<InteractionSettings
device={device}
components={components}
initialComponent={component}
mode="add"
isDialogOpen={isAddInteractionOpen}
closeDialogCallback={() => setIsAddInteractionOpen(false)}
refreshCallback={refreshCallback}
canEdit={canEdit}
/>
<ExportDataSettings
device={device}
component={component}
isDialogOpen={isExportDataOpen}
closeDialogCallback={() => setIsExportDataOpen(false)}
initialStartDate={initialStartDate}
initialEndDate={initialEndDate}
isJSON={isJSON}
// newMeasurements={newStructure}
user={user}
as={as}
/>
</React.Fragment>
);
};
return (
<React.Fragment>
<Tooltip
title="Settings"
aria-label="Component Settings"
onOpen={event => event.stopPropagation()}>
<IconButton color="default" onClick={() => openComponentSettingsDialog("")}>
<Settings />
</IconButton>
</Tooltip>
<Tooltip title="More">
<IconButton
aria-owns={Boolean(anchorEl) ? "More" : undefined}
aria-haspopup="true"
onClick={openMoreMenu}>
<MoreVert />
</IconButton>
</Tooltip>
{menu()}
{dialogs()}
</React.Fragment>
);
}