@material-ui/core#Menu TypeScript Examples

The following examples show how to use @material-ui/core#Menu. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: LoginIcon.tsx    From cognitive-search-static-web-apps-sample-ui with MIT License 6 votes vote down vote up
render(): JSX.Element {

        const state = this.props.state;

        return (<>

            <Button color={state.isLoggedInAnonymously ? "secondary" : "inherit"}
                onClick={evt => state.menuAnchorElement = evt.currentTarget}
            >
                <Tooltip title={state.isLoggedInAnonymously ? "ANONYMOUS" : state.userName}>
                    <AccountCircle />
                </Tooltip>
            </Button>

            <Menu
                anchorEl={state.menuAnchorElement}
                open={!state.isLoggedInAnonymously && !!state.menuAnchorElement}
                onClose={() => state.menuAnchorElement = undefined}
            >
                <MenuItem onClick={() => state.logout()}>Login under a different name</MenuItem>
            </Menu>
            
        </>);
    }
Example #2
Source File: MoreMenu.tsx    From abacus with GNU General Public License v2.0 6 votes vote down vote up
export default function MoreMenu({ children }: { children: React.ReactNode }): JSX.Element {
  const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null)
  const open = Boolean(anchorEl)

  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    setAnchorEl(event.currentTarget)
  }

  const handleClose = () => {
    setAnchorEl(null)
  }

  return (
    <div>
      <IconButton aria-label='more' aria-controls='menu' aria-haspopup='true' onClick={handleClick}>
        <MoreVertIcon />
      </IconButton>
      <Menu id='menu' anchorEl={anchorEl} keepMounted open={open} onClose={handleClose} onClick={handleClose}>
        {children}
      </Menu>
    </div>
  )
}
Example #3
Source File: context_menu.tsx    From jupyter-extensions with Apache License 2.0 6 votes vote down vote up
ContextMenuContainer = withStyles({
  paper: {
    border: '1px solid var(--jp-border-color0)',
    borderRadius: 0,
  },
  list: {
    padding: 0,
  },
})(Menu)
Example #4
Source File: UserSettingsMenu.tsx    From backstage with Apache License 2.0 6 votes vote down vote up
UserSettingsMenu = () => {
  const identityApi = useApi(identityApiRef);
  const [open, setOpen] = React.useState(false);
  const [anchorEl, setAnchorEl] = React.useState<undefined | HTMLElement>(
    undefined,
  );

  const handleOpen = (event: React.MouseEvent<HTMLButtonElement>) => {
    setAnchorEl(event.currentTarget);
    setOpen(true);
  };

  const handleClose = () => {
    setAnchorEl(undefined);
    setOpen(false);
  };

  return (
    <>
      <IconButton
        data-testid="user-settings-menu"
        aria-label="more"
        onClick={handleOpen}
      >
        <MoreVertIcon />
      </IconButton>
      <Menu anchorEl={anchorEl} open={open} onClose={handleClose}>
        <MenuItem data-testid="sign-out" onClick={() => identityApi.signOut()}>
          <ListItemIcon>
            <SignOutIcon />
          </ListItemIcon>
          Sign Out
        </MenuItem>
      </Menu>
    </>
  );
}
Example #5
Source File: icon_button_menu.tsx    From jupyter-extensions with Apache License 2.0 6 votes vote down vote up
render() {
    const { icon, menuItems } = this.props;
    const { anchorEl } = this.state;
    const iconElement = icon || <MoreVert />;
    return (
      <span>
        <IconButton onClick={e => this._onOpenMenu(e)}>
          {iconElement}
        </IconButton>
        <Menu
          anchorEl={anchorEl}
          open={Boolean(anchorEl)}
          keepMounted
          onClose={this._onMenuClose}
        >
          {menuItems(this._onMenuClose)}
        </Menu>
      </span>
    );
  }
Example #6
Source File: SelectStamp.tsx    From bee-dashboard with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
export default function SimpleMenu({ stamps, selectedStamp, setSelected }: Props): ReactElement | null {
  const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null)

  if (!stamps) return null

  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    setAnchorEl(event.currentTarget)
  }

  const handleClose = () => setAnchorEl(null)

  return (
    <div>
      <Button variant="contained" aria-haspopup="true" onClick={handleClick}>
        Change
      </Button>
      <Menu anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
        {stamps.map(stamp => (
          <MenuItem
            key={stamp.batchID}
            onClick={() => {
              setSelected(stamp)
              handleClose()
            }}
            selected={stamp.batchID === selectedStamp?.batchID}
          >
            <ListItemIcon>{stamp.usageText}</ListItemIcon>
            <Typography variant="body2">{stamp.batchID.slice(0, 8)}[…]</Typography>
          </MenuItem>
        ))}
      </Menu>
    </div>
  )
}
Example #7
Source File: Table.stories.tsx    From kodiak-ui with MIT License 5 votes vote down vote up
function Actions({
  onActionSelect,
}: {
  onActionSelect: (value: string) => void
}) {
  const [anchorEl, setAnchorEl] = React.useState(null)

  const handleClick = (event: any) => {
    setAnchorEl(event.currentTarget)
  }

  const handleClose = () => {
    setAnchorEl(null)
  }

  const handleSelect = (value: string) => {
    onActionSelect && onActionSelect(value)
    handleClose()
  }

  return (
    <>
      <Tooltip title="Actions">
        <IconButton aria-label="actions" onClick={handleClick}>
          <MoreVertIcon />
        </IconButton>
      </Tooltip>
      <Menu
        id="simple-menu"
        anchorEl={anchorEl}
        keepMounted
        open={Boolean(anchorEl)}
        onClose={handleClose}
      >
        <MenuItem onClick={() => handleSelect('Duplicate')}>
          <ListItemIcon>
            <FileCopyIcon />
          </ListItemIcon>
          <ListItemText primary="Duplicate" />
        </MenuItem>
        <MenuItem onClick={() => handleSelect('Delete')}>
          <ListItemIcon>
            <DeleteIcon />
          </ListItemIcon>
          <ListItemText primary="Delete" />
        </MenuItem>
      </Menu>
    </>
  )
}
Example #8
Source File: TechDocsReaderPageSubheader.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
TechDocsReaderPageSubheader = ({
  toolbarProps,
}: {
  toolbarProps?: ToolbarProps;
}) => {
  const classes = useStyles();
  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);

  const handleClick = useCallback((event: MouseEvent<HTMLButtonElement>) => {
    setAnchorEl(event.currentTarget);
  }, []);

  const handleClose = useCallback(() => {
    setAnchorEl(null);
  }, []);

  const {
    entityMetadata: { value: entityMetadata, loading: entityMetadataLoading },
  } = useTechDocsReaderPage();

  const addons = useTechDocsAddons();

  const subheaderAddons = addons.renderComponentsByLocation(
    locations.Subheader,
  );

  const settingsAddons = addons.renderComponentsByLocation(locations.Settings);

  if (!subheaderAddons && !settingsAddons) return null;

  // No entity metadata = 404. Don't render subheader on 404.
  if (entityMetadataLoading === false && !entityMetadata) return null;

  return (
    <Toolbar classes={classes} {...toolbarProps}>
      <Box
        display="flex"
        justifyContent="flex-end"
        width="100%"
        flexWrap="wrap"
      >
        {subheaderAddons}
        {settingsAddons ? (
          <>
            <Tooltip title="Settings">
              <IconButton
                aria-controls="tech-docs-reader-page-settings"
                aria-haspopup="true"
                onClick={handleClick}
              >
                <SettingsIcon />
              </IconButton>
            </Tooltip>
            <Menu
              id="tech-docs-reader-page-settings"
              getContentAnchorEl={null}
              anchorEl={anchorEl}
              anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
              open={Boolean(anchorEl)}
              onClose={handleClose}
              keepMounted
            >
              {settingsAddons}
            </Menu>
          </>
        ) : null}
      </Box>
    </Toolbar>
  );
}
Example #9
Source File: with-custom-menu.tsx    From react-component-library with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
withCustomMenu = (): StoryFnReactReturnType => {
    const avatar = <Avatar src={tRex} alt={'User Avatar'} />;
    const open = (): void => {
        store.set({ open: true });
    };
    const close = (): void => {
        store.set({ open: false });
    };

    const menu = (state: any): JSX.Element => (
        <Menu open={state.open} onClose={close}>
            <div key={'header'} style={{ position: 'relative', padding: 10 }}>
                <Typography variant={'h6'}>Welcome, </Typography>
                <Typography style={{ fontWeight: 600, marginTop: '-10px' }} variant={'h3'}>
                    T-Rex
                </Typography>
                <div
                    style={{
                        position: 'absolute',
                        right: 0,
                        top: 0,
                        height: '100%',
                        width: '100%',
                        opacity: 0.2,
                        backgroundSize: 'cover',
                        backgroundImage: `url(${tRex})`,
                    }}
                />
            </div>
            <Divider key={'divider-1'} />
            <MenuItem onClick={close} key={'account'}>
                My Account
            </MenuItem>
            <MenuItem onClick={close} key={'logout'}>
                Logout
            </MenuItem>
            <Divider key={'divider-2'} />
            <img
                key={'footer'}
                alt={'tRex'}
                style={{ textAlign: 'center', padding: '12px 16px 0 16px', height: 40 }}
                src={EatonLogo}
            />
        </Menu>
    );

    store.set({ open: false });

    return (
        <State store={store}>
            {(state): JSX.Element => <UserMenu avatar={avatar} onOpen={open} menu={menu(state)} />}
        </State>
    );
}
Example #10
Source File: Layout.tsx    From Demae with MIT License 5 votes vote down vote up
AccountMenu = () => {

	const [user] = useUser()
	const [roles] = useRoles()

	const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
	const menuOpen = Boolean(anchorEl)
	const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
		setAnchorEl(event.currentTarget);
	}
	const handleClose = () => {
		setAnchorEl(null);
	}

	if (user) {
		return (
			<>
				<IconButton
					onClick={handleMenu}
					color="inherit"
				>
					<AccountCircle />
				</IconButton>
				<Menu
					style={{ width: "120px" }}
					anchorEl={anchorEl}
					anchorOrigin={{ vertical: "top", horizontal: "right", }}
					keepMounted
					transformOrigin={{ vertical: "top", horizontal: "right", }}
					open={menuOpen}
					onClose={handleClose}
				>
					{roles.map(role => <UserMenuItem key={role.id} role={role} />)}
					<Divider />
					<MenuItem key={"signout"} onClick={async () => {
						await firebase.auth().signOut()
					}}>SignOut</MenuItem>
				</Menu>
			</>
		)
	} else {
		return (
			<IconButton
				color="inherit"
			>
				<AccountCircle />
			</IconButton>
		)
	}
}
Example #11
Source File: ContextMenu.tsx    From project-tauntaun with GNU Lesser General Public License v3.0 5 votes vote down vote up
export function ContextMenu(props: ContextMenuProps) {
  const { options, position, onOptionSelected } = props;

  const [visible, setVisible] = useState(true);
  const [savedPosition, setSavedPosition] = useState(position);

  if (position !== savedPosition) {
    setSavedPosition(position);
    if (!visible) {
      setVisible(true);
    }
  }

  const left = position ? position.xy.x : 0;
  const top = position ? position.xy.y : 0;

  const handleClose = () => {
    setVisible(false);
  };

  const handleClick = (value: string) => {
    onOptionSelected?.(value, position ? position : defaultClickPositionValue);
    setVisible(false);
  };

  return (
    <Menu
      id="simple-menu"
      anchorPosition={{ left: left, top: top }}
      anchorReference="anchorPosition"
      open={visible}
      onClose={handleClose}
      onContextMenu={handleClose}
    >
      {options.map((option, index) => (
        <MenuItem key={`ContextMenuItem ${index}`} onClick={() => handleClick(option.value)}>
          {option.label}
        </MenuItem>
      ))}
      <MenuItem onClick={handleClose}>Close</MenuItem>
    </Menu>
  );
}
Example #12
Source File: ControlsSummaryFeatureMenu.tsx    From Teyvat.moe with GNU General Public License v3.0 5 votes vote down vote up
_ControlsSummaryFeatureMenu: FunctionComponent<ControlsSummaryFeatureMenuProps> = ({
  featureKey,

  displayed,

  hideFeature,
  showFeature,
  clearAllFeature,
  clearExpiredFeature,
  locateFeature,
}) => {
  const classes = useStyles();

  const mapFeature = getMapFeature(featureKey);
  const doesExpire = (mapFeature.respawn ?? 'none') !== 'none';

  const [menuAnchor, setMenuAnchor] = useState<HTMLButtonElement | null>(null);

  const handleOpen: React.MouseEventHandler<HTMLButtonElement> = (event) => {
    setMenuAnchor(event.currentTarget);
  };

  const handleClose = () => {
    setMenuAnchor(null);
  };

  return (
    <>
      <Tooltip place="left" />
      <IconButton classes={{ root: classes.menuButtonRoot }} onClick={handleOpen}>
        <MenuIcon />
      </IconButton>

      <Menu
        id="summary-menu"
        anchorEl={menuAnchor}
        open={Boolean(menuAnchor)}
        onClose={handleClose}
        getContentAnchorEl={null}
        anchorOrigin={{
          vertical: 'bottom',
          horizontal: 'left',
        }}
        transformOrigin={{
          vertical: 'bottom',
          horizontal: 'left',
        }}
      >
        <MenuItem onClick={locateFeature}>{t('map-ui:locate')}</MenuItem>
        {doesExpire ? (
          <MenuItem onClick={clearExpiredFeature}>{t('map-ui:clear-refreshed-markers')}</MenuItem>
        ) : null}
        {displayed ? (
          <MenuItem onClick={hideFeature}>{t('map-ui:hide-feature')}</MenuItem>
        ) : (
          <MenuItem onClick={showFeature}>{t('map-ui:show-feature')}</MenuItem>
        )}
        <MenuItem onClick={clearAllFeature}>{t('map-ui:clear-all')}</MenuItem>
      </Menu>
    </>
  );
}
Example #13
Source File: NewsMenu.tsx    From The-TypeScript-Workshop with MIT License 5 votes vote down vote up
NewsMenu = () => {
  const [anchorEl, setAnchorEl] = useState<Element>();
  const history = useHistory();
  const user = useContext(UserContext);

  const handleClick = (
    event: React.MouseEvent<HTMLButtonElement, MouseEvent>
  ) => setAnchorEl(event.currentTarget);

  const handleAdd = () => history.push('/add');

  const handleClose = () => setAnchorEl(undefined);

  const handleLogOut = async () => {
    await auth.signOut();
    history.push('/signin');
  };

  const handleSignIn = () => history.push('/signin');
  const handleSignUp = () => history.push('/signup');

  return (
    <div>
      <Button
        area-controls="simple-menu"
        area-haspopup="true"
        color="inherit"
        onClick={handleClick}
      >
        Menu
      </Button>
      <Menu
        id="simple-menu"
        anchorEl={anchorEl}
        keepMounted
        open={Boolean(anchorEl)}
        onClose={handleClose}
      >
        {user ? (
          <div>
            <MenuItem onClick={handleAdd}>Add a Story</MenuItem>
            <MenuItem onClick={handleLogOut}>Log Out</MenuItem>
          </div>
        ) : (
          <div>
            <MenuItem onClick={handleSignIn}>Sign In</MenuItem>
            <MenuItem onClick={handleSignUp}>Sign Up</MenuItem>
          </div>
        )}
      </Menu>
    </div>
  );
}
Example #14
Source File: UserMenu.tsx    From knboard with MIT License 5 votes vote down vote up
UserMenu = () => {
  const user = useSelector((state: RootState) => state.auth.user);
  const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
  const dispatch = useDispatch();
  const history = useHistory();

  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    setAnchorEl(event.currentTarget);
  };

  const handleClose = () => {
    setAnchorEl(null);
  };

  const handleNotImplemented = () => {
    dispatch(createInfoToast("Not implemented yet ?"));
  };

  const handleLogout = () => {
    setAnchorEl(null);
    dispatch(logout());
    history.push("/");
  };

  const handleToProfile = () => {
    setAnchorEl(null);
    history.push("/profile");
  };

  return (
    <>
      <Button
        aria-controls="user-menu"
        aria-haspopup="true"
        onClick={handleClick}
        data-testid="user-menu"
        css={css`
          min-width: 1.5rem;
          padding: 0;
          border-radius: 50%;
          &:hover {
            background-color: initial;
          }
        `}
      >
        <Avatar
          css={avatarStyles}
          src={user?.photo_url || ""}
          alt="user-avatar"
        >
          {user?.username.charAt(0)}
        </Avatar>
      </Button>
      <Menu
        id="user-menu"
        anchorEl={anchorEl}
        getContentAnchorEl={null}
        anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
        transformOrigin={{ vertical: "top", horizontal: "center" }}
        open={Boolean(anchorEl)}
        onClose={handleClose}
        transitionDuration={0}
        keepMounted
      >
        <Username>{user?.username}</Username>
        <MenuItem onClick={handleToProfile}>Profile</MenuItem>
        <MenuItem onClick={handleNotImplemented}>Available Shortcuts</MenuItem>
        <MenuItem onClick={handleLogout}>Logout</MenuItem>
      </Menu>
    </>
  );
}
Example #15
Source File: icon_button_menu.spec.tsx    From jupyter-extensions with Apache License 2.0 5 votes vote down vote up
describe('IconButtonMenu', () => {
  const menuItemsProp = (closeHandler: MenuCloseHandler) => (
    <React.Fragment>
      <MenuItem onClick={closeHandler}>Item 1</MenuItem>
      <MenuItem onClick={closeHandler}>Item 2</MenuItem>
      <MenuItem onClick={closeHandler}>Item 3</MenuItem>
    </React.Fragment>
  );

  it('Renders menu items', () => {
    const iconButtonMenu = shallow(
      <IconButtonMenu menuItems={menuItemsProp} />
    );

    expect(iconButtonMenu).toMatchSnapshot();
  });

  it('Renders with provided icon', () => {
    const iconButtonMenu = shallow(
      <IconButtonMenu menuItems={menuItemsProp} icon={<Add />} />
    );

    expect(iconButtonMenu).toMatchSnapshot();
  });

  it('Opens from icon button and closes when an item is clicked', () => {
    const iconButtonMenu = shallow(
      <IconButtonMenu menuItems={menuItemsProp} />
    );
    expect(iconButtonMenu.find(Menu).prop('open')).toBe(false);

    const openMenuButton = iconButtonMenu.find(IconButton).first();
    openMenuButton.simulate('click', {
      currentTarget: openMenuButton.getElement(),
    });

    expect(iconButtonMenu.find(Menu).prop('open')).toBe(true);
    const menuItems = iconButtonMenu.find(MenuItem);
    expect(menuItems.length).toBe(3);
    menuItems.first().simulate('click');
    expect(iconButtonMenu.find(Menu).prop('open')).toBe(false);
  });
});
Example #16
Source File: ActionsMenu.tsx    From max-todos with MIT License 4 votes vote down vote up
export default function ActionsMenu({
  deleteTodo,
  setEditOpen,
  markStar,
  todo,
}: Props) {
  const [anchorEl, setAnchorEl] = useState<
    (EventTarget & HTMLButtonElement) | null
  >(null);
  const open = Boolean(anchorEl);
  const MenuIcon = useChangeMenuIcon();

  const options: Option[] = [
    {
      name: todo.starred ? OptionName.UNSTAR : OptionName.STAR,
      customColor: todo.starred ? "#CCA43A" : "#000",
      icon: todo.starred ? StarIcon : StarIconOutlined,
      method: () => {
        markStar(todo.id);
        setAnchorEl(null);
      },
    },
    {
      name: OptionName.EDIT,
      iconColor: "primary",
      textColor: "primary",
      icon: EditIcon,
      method: () => {
        setEditOpen(true);
        setAnchorEl(null);
      },
    },
    {
      name: OptionName.DELETE,
      iconColor: "error",
      textColor: "error",
      icon: DeleteIcon,
      method: (e) => {
        deleteTodo(e);
        setAnchorEl(null);
      },
    },
  ];
  const handleClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
    setAnchorEl(e.currentTarget);
  };

  const handleEvent = (option: OptionName, e: any) => {
    if (option === "Star") markStar(todo.id);
    else if (option === "Edit") setEditOpen(true);
    else if (option === "Delete") deleteTodo(e);
    setAnchorEl(null);
  };

  return (
    <div>
      <IconButton
        aria-label="more"
        aria-controls="long-menu"
        aria-haspopup="true"
        onClick={handleClick}
        centerRipple={false}
      >
        <MenuIcon />
      </IconButton>
      <Menu
        id="long-menu"
        anchorEl={anchorEl}
        keepMounted
        open={open}
        onClose={handleEvent}
        PaperProps={{
          style: {
            maxHeight: ITEM_HEIGHT * 4.5,
            width: "20ch",
          },
        }}
      >
        {options.map((option) => (
          <MenuItem key={option.name} onClick={option.method}>
            <option.icon
              color={option.iconColor}
              htmlColor={option.customColor}
            />
            &nbsp;
            <Typography
              color={option.textColor}
              style={{ color: option.customColor }}
            >
              {option.name}
            </Typography>
          </MenuItem>
        ))}
      </Menu>
    </div>
  );
}
Example #17
Source File: Vulnerability.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 4 votes vote down vote up
Vulnerability: React.FC = () => {
  const { vulnerabilityId } = useParams();
  const { apiGet, apiPut } = useAuthContext();
  const [vulnerability, setVulnerability] = useState<VulnerabilityType>();
  const [comment, setComment] = useState<string>('');
  const [showCommentForm, setShowCommentForm] = useState<boolean>(false);
  const [menuAnchor, setMenuAnchor] = React.useState<null | HTMLElement>(null);
  const classes = useStyles();
  const history = useHistory();

  const formatDate = (date: string) => {
    return format(parseISO(date), 'MM-dd-yyyy');
  };

  const fetchVulnerability = useCallback(async () => {
    try {
      const result = await apiGet<VulnerabilityType>(
        `/vulnerabilities/${vulnerabilityId}`
      );
      setVulnerability(result);
    } catch (e) {
      console.error(e);
    }
  }, [vulnerabilityId, apiGet]);

  const updateVulnerability = async (body: { [key: string]: string }) => {
    try {
      if (!vulnerability) return;
      const res = await apiPut<VulnerabilityType>(
        '/vulnerabilities/' + vulnerability.id,
        {
          body: body
        }
      );
      setVulnerability({
        ...vulnerability,
        state: res.state,
        substate: res.substate,
        actions: res.actions
      });
    } catch (e) {
      console.error(e);
    }
  };

  useEffect(() => {
    fetchVulnerability();
  }, [fetchVulnerability]);

  if (!vulnerability) return <></>;

  const references = vulnerability.references.map((ref) => ref);
  if (vulnerability.cve)
    references.unshift({
      name: 'NIST National Vulnerability Database',
      url: `https://nvd.nist.gov/vuln/detail/${vulnerability.cve}`,
      source: '',
      tags: []
    });

  const states = [
    'unconfirmed',
    'exploitable',
    'false-positive',
    'accepted-risk',
    'remediated'
  ];
  interface dnstwist {
    'domain-name': string;
    fuzzer: string;
    'dns-a'?: string;
    'dns-aaas'?: string;
    'dns-mx'?: string;
    'dns-ns'?: string;
    'date-first-observed'?: string;
  }

  return (
    <>
      {/* <Alert severity="info">
        This vulnerability is found on 17 domains you have access to.
      </Alert> */}
      <div className={classes.root}>
        <p>
          <Link
            to="# "
            onClick={() => history.goBack()}
            className={classes.backLink}
          >
            <ChevronLeft
              style={{
                height: '100%',
                verticalAlign: 'middle',
                marginTop: '-2px'
              }}
            ></ChevronLeft>
            Go back
          </Link>
        </p>

        <div className={classes.contentWrapper}>
          <div className={classes.content}>
            <div
              className={classes.panel}
              style={{
                flex: '0 0 45%'
              }}
            >
              <Paper classes={{ root: classes.cardRoot }}>
                <div className={classes.title}>
                  <h4>{vulnerability.title}</h4>
                  <Button
                    aria-haspopup="true"
                    onClick={(event: React.MouseEvent<HTMLButtonElement>) =>
                      setMenuAnchor(event.currentTarget)
                    }
                  >
                    <Flag
                      style={{
                        fontSize: '14px',
                        color: '#A9AEB1',
                        marginRight: '5px'
                      }}
                    ></Flag>
                    Mark Item <ArrowDropDown />
                  </Button>
                  <Menu
                    anchorEl={menuAnchor}
                    keepMounted
                    open={Boolean(menuAnchor)}
                    getContentAnchorEl={null}
                    onClose={() => setMenuAnchor(null)}
                    anchorOrigin={{
                      vertical: 'bottom',
                      horizontal: 'center'
                    }}
                    transformOrigin={{
                      vertical: 'top',
                      horizontal: 'center'
                    }}
                  >
                    {states.map((state) => (
                      <MenuItem
                        key={state}
                        onClick={() => {
                          updateVulnerability({
                            substate: state
                          });
                          setMenuAnchor(null);
                        }}
                        style={{ outline: 'none' }}
                      >
                        {stateMap[state]}
                      </MenuItem>
                    ))}
                  </Menu>
                </div>
                <Chip
                  style={{
                    marginLeft: '1.5rem'
                  }}
                  // icon={<Check></Check>}
                  label={`${vulnerability.state[0].toUpperCase()}${vulnerability.state.slice(
                    1
                  )} (${stateMap[vulnerability.substate]})`}
                  color={
                    vulnerability.state === 'open' ? 'secondary' : 'default'
                  }
                />
                <div className={classes.inner}>
                  <div className={classes.section}>
                    <h4 className={classes.subtitle}>Description</h4>
                    {vulnerability.description}
                  </div>
                  <div className={classes.section}>
                    <h4 className={classes.subtitle}>References</h4>
                    {references &&
                      references.map((ref, index) => (
                        <p key={index}>
                          <a
                            href={ref.url}
                            target="_blank"
                            rel="noopener noreferrer"
                          >
                            {ref.name ? ref.name : ref.url}
                          </a>
                          {ref.tags.length > 0
                            ? ' - ' + ref.tags.join(',')
                            : ''}
                        </p>
                      ))}
                  </div>
                  {vulnerability.source === 'hibp' && (
                    <div className={classes.section}>
                      <h4 className={classes.subtitle}>Data</h4>
                      <Table aria-label="simple table">
                        <TableHead>
                          <TableRow>
                            <TableCell>Exposed Emails</TableCell>
                            <TableCell align="right">Breaches</TableCell>
                          </TableRow>
                        </TableHead>
                        <TableBody>
                          {Object.keys(
                            vulnerability.structuredData['emails']
                          ).map((keyName, keyIndex) => (
                            <TableRow key={keyName}>
                              <TableCell component="th" scope="row">
                                {keyName}
                              </TableCell>
                              <TableCell align="right">
                                {vulnerability.structuredData['emails'][
                                  keyName
                                ].join(',  ')}
                              </TableCell>
                            </TableRow>
                          ))}
                        </TableBody>
                      </Table>
                    </div>
                  )}
                  {vulnerability.source === 'lookingGlass' && (
                    <div className={classes.section}>
                      <h4 className={classes.subtitle}>Data</h4>
                      <Table aria-label="simple table">
                        <TableHead>
                          <TableRow>
                            <TableCell>First Seen</TableCell>
                            <TableCell align="right">Last Seen</TableCell>
                            <TableCell align="right">Vuln Name</TableCell>
                            <TableCell align="right">Type</TableCell>
                          </TableRow>
                        </TableHead>
                        <TableBody>
                          {vulnerability.structuredData['lookingGlassData'].map(
                            (col: any) => (
                              <TableRow key={col.right_name}>
                                <TableCell component="th" scope="row">
                                  {formatDistanceToNow(
                                    parseISO(col.firstSeen)
                                  ) + ' ago'}
                                </TableCell>
                                <TableCell align="right">
                                  {formatDistanceToNow(parseISO(col.lastSeen)) +
                                    ' ago'}
                                </TableCell>
                                <TableCell align="right">
                                  {col.right_name}
                                </TableCell>
                                <TableCell align="right">
                                  {col.vulnOrMal}
                                </TableCell>
                              </TableRow>
                            )
                          )}
                        </TableBody>
                      </Table>
                    </div>
                  )}
                  {vulnerability.source === 'dnstwist' && (
                    <div className={classes.section}>
                      <h4 className={classes.subtitle}>Data</h4>
                      <TableContainer>
                        <Table aria-label="simple table">
                          <TableHead>
                            <TableRow>
                              <TableCell>Domain Name</TableCell>
                              <TableCell>IP Address / A Record</TableCell>
                              <TableCell>MX Record</TableCell>
                              <TableCell>NS Record</TableCell>
                              <TableCell>Date Observed</TableCell>
                              <TableCell>Fuzzer</TableCell>
                            </TableRow>
                          </TableHead>
                          <TableBody>
                            {vulnerability.structuredData['domains'].map(
                              (dom: dnstwist) => (
                                <TableRow key={dom['domain-name']}>
                                  <TableCell component="th" scope="row">
                                    {dom['domain-name']}
                                  </TableCell>
                                  <TableCell>{dom['dns-a']}</TableCell>
                                  <TableCell>{dom['dns-mx']}</TableCell>
                                  <TableCell>{dom['dns-ns']}</TableCell>
                                  <TableCell>
                                    {dom['date-first-observed']}
                                  </TableCell>
                                  <TableCell>{dom['fuzzer']}</TableCell>
                                </TableRow>
                              )
                            )}
                          </TableBody>
                        </Table>
                      </TableContainer>
                    </div>
                  )}
                </div>
              </Paper>
            </div>
            <div
              className={classes.panel}
              style={{
                flex: '0 0 30%'
              }}
            >
              <Paper className={classes.cardRoot}>
                <div className={classes.inner}>
                  <div className={classes.section}>
                    <h2 className={classes.subtitle}>Team notes</h2>
                    <button
                      onClick={() => {
                        setShowCommentForm(!showCommentForm);
                      }}
                      className={classes.linkSmall}
                    >
                      Add new note
                    </button>
                  </div>
                  {showCommentForm && (
                    <div>
                      <TextareaAutosize
                        style={{
                          width: '100%',
                          padding: 10,
                          marginBottom: '20px'
                        }}
                        rowsMin={4}
                        placeholder="Leave a Note"
                        onChange={(e) => setComment(e.target.value)}
                      />
                      <Button
                        onClick={() => {
                          updateVulnerability({
                            comment
                          });
                          setComment('');
                          setShowCommentForm(false);
                        }}
                        style={{
                          width: 150,
                          marginBottom: '20px'
                        }}
                        variant="contained"
                        color="secondary"
                      >
                        Save
                      </Button>
                    </div>
                  )}
                  {vulnerability.actions &&
                    vulnerability.actions
                      .filter((action) => action.type === 'comment')
                      .map((action, index) => (
                        <div className={classes.section} key={index}>
                          <h4
                            className={classes.subtitle}
                            style={{ fontSize: '16px', display: 'inline' }}
                          >
                            {action.userName}
                          </h4>
                          <span style={{ float: 'right', display: 'inline' }}>
                            {formatDistanceToNow(parseISO(action.date))} ago
                          </span>
                          <ReactMarkdown linkTarget="_blank">
                            {action.value || ''}
                          </ReactMarkdown>
                        </div>
                      ))}
                </div>
              </Paper>
              <Paper className={classes.cardRoot}>
                <div className={classes.inner}>
                  <div className={classes.section}>
                    <h2 className={classes.subtitle}>Vulnerability History</h2>
                  </div>
                  <Timeline
                    style={{
                      position: 'relative',
                      marginLeft: '-90%'
                    }}
                    align="left"
                  >
                    {vulnerability.actions &&
                      vulnerability.actions
                        .filter(
                          (action) =>
                            action.type === 'state-change' && action.substate
                        )
                        .map((action, index) => (
                          <TimelineItem key={index}>
                            <TimelineSeparator>
                              <TimelineDot />
                              <TimelineConnector />
                            </TimelineSeparator>{' '}
                            <TimelineContent>
                              State {action.automatic ? 'automatically ' : ''}
                              changed to {action.state} (
                              {stateMap[action.substate!].toLowerCase()})
                              {action.userName ? ' by ' + action.userName : ''}{' '}
                              <br></br>
                              <span
                                style={{
                                  color: '#A9AEB1'
                                }}
                              >
                                {formatDate(action.date)}
                              </span>
                            </TimelineContent>
                          </TimelineItem>
                        ))}

                    <TimelineItem>
                      <TimelineSeparator>
                        <TimelineDot />
                      </TimelineSeparator>
                      <TimelineContent>
                        Vulnerability opened<br></br>
                        <span
                          style={{
                            color: '#A9AEB1'
                          }}
                        >
                          {formatDate(vulnerability.createdAt)}
                        </span>
                      </TimelineContent>
                    </TimelineItem>
                  </Timeline>
                </div>
              </Paper>
              <Paper className={classes.cardRoot}>
                <div className={classes.inner}>
                  <div className={classes.section}>
                    <h2 className={classes.subtitle}>Provenance</h2>
                    <p>
                      <strong>Root Domain: </strong>
                      {vulnerability.domain.fromRootDomain}
                    </p>
                    <p>
                      <strong>Subdomain: </strong>
                      {vulnerability.domain.name} (
                      {vulnerability.domain.subdomainSource})
                    </p>
                    {vulnerability.service && (
                      <p>
                        <strong>Service/Port: </strong>
                        {vulnerability.service.service
                          ? vulnerability.service.service
                          : vulnerability.service.port}{' '}
                        ({vulnerability.service.serviceSource})
                      </p>
                    )}
                    {vulnerability.cpe && (
                      <>
                        <p>
                          <strong>Product: </strong>
                          {vulnerability.cpe}
                        </p>
                      </>
                    )}
                    <p>
                      <strong>Vulnerability: </strong>
                      {vulnerability.title} ({vulnerability.source})
                    </p>
                  </div>
                </div>
              </Paper>
              {vulnerability.source === 'hibp' && (
                <Paper className={classes.cardRoot}>
                  <div className={classes.inner}>
                    <div className={classes.section}>
                      <h2 className={classes.subtitle}>Breaches</h2>
                      <Table aria-label="simple table">
                        <TableHead>
                          <TableRow>
                            <TableCell>Breach Name</TableCell>
                            <TableCell align="right">Date Added</TableCell>
                          </TableRow>
                        </TableHead>
                        <TableBody>
                          {Object.keys(vulnerability.structuredData['breaches'])
                            .sort(
                              (a, b) =>
                                parseISO(
                                  vulnerability.structuredData['breaches'][b][
                                    'AddedDate'
                                  ]
                                ).getTime() -
                                parseISO(
                                  vulnerability.structuredData['breaches'][a][
                                    'AddedDate'
                                  ]
                                ).getTime()
                            )
                            .map((keyName, keyIndex) => (
                              <TableRow key={keyName}>
                                <TableCell component="th" scope="row">
                                  {keyName}
                                </TableCell>
                                <TableCell align="right">
                                  {formatDistanceToNow(
                                    parseISO(
                                      vulnerability.structuredData['breaches'][
                                        keyName
                                      ]['AddedDate']
                                    )
                                  ) + ' ago'}
                                </TableCell>
                              </TableRow>
                            ))}
                        </TableBody>
                      </Table>
                    </div>
                  </div>
                </Paper>
              )}
            </div>
          </div>
        </div>
      </div>
    </>
  );
}
Example #18
Source File: NavItem.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 4 votes vote down vote up
NavItem: React.FC<Props> = (props) => {
  const { title, path, nested, exact, onClick } = props;
  const match = useRouteMatch(path ?? '');
  const history = useHistory();
  const [anchor, setAnchor] = useState<any>(null);
  const [mouseInButton, setMouseInButton] = useState(false);
  const [mouseInMenu, setMouseInMenu] = useState(false);
  const classes = useStyles();

  const onClickButton = (e: any) => {
    setAnchor(e.currentTarget);
    setMouseInButton(true);
  };

  const onCloseMenu = () => {
    setMouseInMenu(false);
    setMouseInButton(false);
    setAnchor(null);
  };

  const navigateTo = (to: string) => {
    setMouseInMenu(false);
    setMouseInButton(false);
    setAnchor(null);
    history.push(to);
  };

  return (
    <>
      {path ? (
        <NavLink
          to={path}
          activeClassName={path !== '#' ? classes.activeLink : classes.link}
          className={classes.link}
          onClick={onClick ? onClick : onClickButton}
          exact={exact}
          style={{ outline: 'none' }}
        >
          {title}
        </NavLink>
      ) : (
        <Button
          className={clsx(classes.link, {
            [classes.activeLink]: !!match
          })}
          onClick={onClick ? onClick : onClickButton}
          style={{ outline: 'none' }}
        >
          {title}
        </Button>
      )}
      {nested && (
        <Menu
          id={`menu-${title}`}
          open={(mouseInButton || mouseInMenu) && !!anchor}
          anchorEl={anchor}
          onClose={onCloseMenu}
          getContentAnchorEl={null}
          keepMounted
          anchorOrigin={{
            vertical: 'bottom',
            horizontal: 'center'
          }}
          transformOrigin={{
            vertical: 'top',
            horizontal: 'center'
          }}
        >
          {nested.map((item) => (
            <MenuItem
              key={item.title.toString()}
              onClick={
                item.onClick ? item.onClick : () => navigateTo(item.path)
              }
              style={{ outline: 'none' }}
            >
              {item.title}
            </MenuItem>
          ))}
        </Menu>
      )}
    </>
  );
}
Example #19
Source File: MoreMenu.tsx    From max-todos with MIT License 4 votes vote down vote up
MoreMenu = () => {
  const [anchorEl, setAnchorEl] = useState<
    (EventTarget & HTMLButtonElement) | null
  >(null);
  const [deleteOpen, setDeleteOpen] = useState(false);
  const open = Boolean(anchorEl);
  const MenuIcon = useChangeMenuIcon();
  const { todos, deleteAll } = useContext(MainContext)!;

  const handleClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) =>
    setAnchorEl(e.currentTarget);
  const handleClose = () => setAnchorEl(null);

  interface Option {
    name: string;
    iconColor:
      | "error"
      | "action"
      | "inherit"
      | "disabled"
      | "primary"
      | "secondary"
      | undefined;
    textColor:
      | "error"
      | "inherit"
      | "primary"
      | "secondary"
      | "initial"
      | "textPrimary"
      | "textSecondary"
      | undefined;
    disabled: boolean;
    icon: SvgIconComponent;
    method: () => void;
  }

  const options: Option[] = [
    {
      name: "Delete All",
      iconColor: "error",
      textColor: "error",
      disabled: todos.length === 0,
      icon: DeleteSweepIcon,
      method: () => {
        handleClose();
        setDeleteOpen(true);
      },
    },
  ];

  return (
    <div>
      <IconButton
        aria-label="more"
        aria-controls="long-menu"
        aria-haspopup="true"
        onClick={handleClick}
        color="inherit"
      >
        <MenuIcon />
      </IconButton>
      <Menu
        id="long-menu"
        anchorEl={anchorEl}
        keepMounted
        open={open}
        onClose={handleClose}
        PaperProps={{
          style: {
            width: "20ch",
          },
        }}
      >
        {options.map((option) => (
          <MenuItem
            key={option.name}
            disabled={option.disabled}
            onClick={option.method}
          >
            <option.icon color={option.iconColor} /> &nbsp;
            <Typography color={option.textColor}>{option.name}</Typography>
          </MenuItem>
        ))}
      </Menu>
      <DeleteAllConfirm
        yes={() => {
          setDeleteOpen(false);
          setTimeout(() => deleteAll(), 200);
        }}
        open={deleteOpen}
        close={() => setDeleteOpen(false)}
      />
    </div>
  );
}
Example #20
Source File: Menu.tsx    From signer with Apache License 2.0 4 votes vote down vote up
MoreMenu = observer((props: Props) => {
  const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);

  const classes = useStyles();

  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    setAnchorEl(event.currentTarget);
  };

  const handleClose = () => {
    setTimeout(() => {
      setAnchorEl(null);
    }, 200);
  };

  return (
    <div>
      <IconButton
        edge="end"
        aria-controls="simple-menu"
        aria-haspopup="true"
        onClick={handleClick}
        style={{ color: '#C4C4C4' }}
      >
        <MenuIcon />
      </IconButton>
      <Menu
        id={'simple-menu'}
        anchorEl={anchorEl}
        transformOrigin={{ vertical: 'top', horizontal: 'right' }}
        open={Boolean(anchorEl)}
        onClose={handleClose}
      >
        <List
          aria-labelledby="nested-list-subheader"
          subheader={
            <ListSubheader component="div" id="nested-list-subheader">
              {props.accountManager.userAccounts.length > 0
                ? 'Accounts'
                : 'No Account'}
            </ListSubheader>
          }
        >
          {props.accountManager.userAccounts.map((account, i) => {
            return (
              <ListItem
                key={i}
                button
                dense={true}
                onClick={() => {
                  props.accountManager.switchToAccount(account.alias);
                  handleClose();
                }}
              >
                {account.alias ===
                props.accountManager.activeUserAccount?.alias ? (
                  <CheckIcon fontSize={'small'} />
                ) : (
                  <Icon className={'fa fa-fw'} fontSize={'small'} />
                )}
                <ListItemText primary={account.alias} />
              </ListItem>
            );
          })}
          <Divider light />
          {props.accountManager.userAccounts.length > 0 && (
            <ListItem
              dense={true}
              component={Link}
              to={Pages.AccountManagement}
              button
              onClick={handleClose}
            >
              <SettingsIcon className={classes.menuIcon} />
              <ListItemText primary="Key Management" />
            </ListItem>
          )}
          <ListItem
            dense={true}
            component={Link}
            to={Pages.ConnectedSites}
            button
            onClick={handleClose}
          >
            <WebIcon className={classes.menuIcon} />
            <ListItemText primary="Connected Sites" />
          </ListItem>
          {props.accountManager.activeUserAccount && (
            <ListItem
              dense={true}
              button
              onClick={() => {
                props.accountManager.downloadActiveKey();
                handleClose();
              }}
            >
              <CloudDownloadIcon className={classes.menuIcon} />
              <ListItemText primary="Download Active Key" />
            </ListItem>
          )}
          <ListItem
            dense={true}
            component={Link}
            to={Pages.ConfigureTimeout}
            button
            onClick={handleClose}
          >
            <TimerIcon className={classes.menuIcon} />
            <ListItemText primary="Timeout" />
            <Typography variant="overline">
              {props.accountManager.idleTimeoutMins} min
              {props.accountManager.idleTimeoutMins === 1 ? '' : 's'}
            </Typography>
          </ListItem>
          <ListItem
            dense={true}
            button
            onClick={() => {
              props.accountManager.lock();
              handleClose();
            }}
          >
            <LockIcon className={classes.menuIcon} />
            <ListItemText primary="Lock" />
          </ListItem>
        </List>
      </Menu>
    </div>
  );
})
Example #21
Source File: index.tsx    From aqualink-app with MIT License 4 votes vote down vote up
NavBar = ({
  searchLocation,
  geocodingEnabled,
  routeButtons,
  loading,
  classes,
}: NavBarProps) => {
  const user = useSelector(userInfoSelector);
  const storedCollection = useSelector(collectionDetailsSelector);
  const dispatch = useDispatch();
  const theme = useTheme();
  const isTablet = useMediaQuery(theme.breakpoints.up("md"));
  const [registerDialogOpen, setRegisterDialogOpen] = useState<boolean>(false);
  const [signInDialogOpen, setSignInDialogOpen] = useState<boolean>(false);
  const [menuDrawerOpen, setMenuDrawerOpen] = useState<boolean>(false);
  const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null);

  const handleRegisterDialog = (open: boolean) => setRegisterDialogOpen(open);
  const handleSignInDialog = (open: boolean) => setSignInDialogOpen(open);

  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    setAnchorEl(event.currentTarget);
  };

  const handleMenuClose = () => {
    setAnchorEl(null);
  };

  const onUserSignOut = () => {
    // Clear collection if it belongs to the signed in user
    if (storedCollection?.id === user?.collection?.id) {
      dispatch(clearCollection());
    }
    dispatch(signOutUser());
    handleMenuClose();
  };

  const onSiteChange = () => {
    dispatch(unsetSelectedSite());
    dispatch(unsetLiveData());
    dispatch(unsetLatestData());
  };

  return (
    <>
      <AppBar
        className={classNames(classes.appBar, {
          [classes.appBarXs]: searchLocation,
        })}
        position="static"
        color="primary"
      >
        <Toolbar className={classes.toolbar}>
          <MenuDrawer
            open={menuDrawerOpen}
            onClose={() => setMenuDrawerOpen(false)}
          />
          <Grid
            container
            justify="space-between"
            alignItems="center"
            spacing={1}
          >
            <Grid
              item
              xs={5}
              sm={2}
              // eslint-disable-next-line no-nested-ternary
              md={routeButtons ? 2 : searchLocation ? 6 : 4}
            >
              <Box display="flex" flexWrap="nowrap" alignItems="center">
                <IconButton
                  edge="start"
                  color="inherit"
                  onClick={() => setMenuDrawerOpen(true)}
                >
                  <MenuIcon />
                </IconButton>

                <MuiLink className={classes.navBarLink} href="/map">
                  <Typography color="textPrimary" variant="h4">
                    Aqua
                  </Typography>
                  <Typography style={{ color: "#8AC6DE" }} variant="h4">
                    link
                  </Typography>
                </MuiLink>
              </Box>
            </Grid>

            {searchLocation && (
              <Hidden xsDown>
                <Grid item sm={4} md={3}>
                  <Search geocodingEnabled={geocodingEnabled} />
                </Grid>
              </Hidden>
            )}

            {routeButtons && isTablet && <RouteButtons />}

            <Grid
              container
              justify="flex-end"
              item
              xs={7}
              sm={routeButtons && isTablet ? 3 : 4}
              md={searchLocation || (routeButtons && isTablet) ? 3 : 8}
            >
              {user ? (
                <>
                  <Box display="flex" flexWrap="nowrap" alignItems="center">
                    {user.fullName ? user.fullName : "My Profile"}
                    <IconButton
                      className={classes.button}
                      onClick={handleClick}
                    >
                      <ExpandMoreIcon className={classes.expandIcon} />
                    </IconButton>
                    <Menu
                      key="user-menu"
                      className={classes.userMenu}
                      anchorEl={anchorEl}
                      keepMounted
                      open={Boolean(anchorEl)}
                      onClose={handleMenuClose}
                      MenuListProps={{ className: classes.userMenu }}
                      PopoverClasses={{ paper: classes.userMenuWrapper }}
                    >
                      {sortBy(user.administeredSites, "id").map(
                        ({ id, name, region }, index) => {
                          const siteIdentifier = name || region;
                          return (
                            <Link
                              to={`/sites/${id}`}
                              key={`site-link-${id}`}
                              className={classes.menuItemLink}
                            >
                              <MenuItem
                                onClick={() => onSiteChange()}
                                className={classes.menuItem}
                              >
                                {siteIdentifier || `Site ${index + 1}`}
                              </MenuItem>
                            </Link>
                          );
                        }
                      )}
                      <Divider className={classes.userMenuDivider} />
                      <Link to="/dashboard" className={classes.menuItemLink}>
                        <MenuItem
                          key="user-menu-dashboard"
                          className={classes.menuItem}
                        >
                          <Grid container spacing={1}>
                            <Grid item>
                              <DashboardTwoToneIcon fontSize="small" />
                            </Grid>
                            <Grid item>Dashboard</Grid>
                          </Grid>
                        </MenuItem>
                      </Link>
                      <Divider className={classes.userMenuDivider} />
                      <MenuItem
                        key="user-menu-logout"
                        className={classes.menuItem}
                        onClick={onUserSignOut}
                      >
                        <Grid container spacing={1}>
                          <Grid item>
                            <PowerSettingsNewIcon fontSize="small" />
                          </Grid>
                          <Grid item>Logout</Grid>
                        </Grid>
                      </MenuItem>
                    </Menu>
                  </Box>
                </>
              ) : (
                <>
                  <Grid item>
                    <Button onClick={() => handleSignInDialog(true)}>
                      SIGN IN
                    </Button>
                  </Grid>
                  <Grid item>
                    <Button onClick={() => handleRegisterDialog(true)}>
                      SIGN UP
                    </Button>
                  </Grid>
                </>
              )}
            </Grid>

            {searchLocation && (
              <Hidden smUp>
                <Grid item xs={12} style={{ margin: 0, paddingTop: 0 }}>
                  <Search geocodingEnabled={geocodingEnabled} />
                </Grid>
              </Hidden>
            )}
          </Grid>
        </Toolbar>
      </AppBar>
      {loading && <LinearProgress />}
      <RegisterDialog
        open={registerDialogOpen}
        handleRegisterOpen={handleRegisterDialog}
        handleSignInOpen={handleSignInDialog}
      />
      <SignInDialog
        open={signInDialogOpen}
        handleRegisterOpen={handleRegisterDialog}
        handleSignInOpen={handleSignInDialog}
      />
    </>
  );
}
Example #22
Source File: UptimeMonitorActionsMenu.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
UptimeMonitorActionsMenu = ({
  uptimeMonitor,
  onUptimeMonitorChanged,
}: {
  uptimeMonitor: UptimeMonitor;
  onUptimeMonitorChanged?: (uptimeMonitor: UptimeMonitor) => void;
}) => {
  const ilertApi = useApi(ilertApiRef);
  const alertApi = useApi(alertApiRef);
  const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
  const callback = onUptimeMonitorChanged || ((_: UptimeMonitor): void => {});

  const handleClick = (event: React.MouseEvent<HTMLElement>) => {
    setAnchorEl(event.currentTarget);
  };

  const handleCloseMenu = () => {
    setAnchorEl(null);
  };

  const handlePause = async (): Promise<void> => {
    try {
      const newUptimeMonitor = await ilertApi.pauseUptimeMonitor(uptimeMonitor);
      handleCloseMenu();
      alertApi.post({ message: 'Uptime monitor paused.' });

      callback(newUptimeMonitor);
    } catch (err) {
      alertApi.post({ message: err, severity: 'error' });
    }
  };

  const handleResume = async (): Promise<void> => {
    try {
      const newUptimeMonitor = await ilertApi.resumeUptimeMonitor(
        uptimeMonitor,
      );
      handleCloseMenu();
      alertApi.post({ message: 'Uptime monitor resumed.' });

      callback(newUptimeMonitor);
    } catch (err) {
      alertApi.post({ message: err, severity: 'error' });
    }
  };

  const handleOpenReport = async (): Promise<void> => {
    try {
      const um = await ilertApi.fetchUptimeMonitor(uptimeMonitor.id);
      handleCloseMenu();
      window.open(um.shareUrl, '_blank');
    } catch (err) {
      alertApi.post({ message: err, severity: 'error' });
    }
  };

  return (
    <>
      <IconButton
        aria-label="more"
        aria-controls="long-menu"
        aria-haspopup="true"
        onClick={handleClick}
        size="small"
      >
        <MoreVertIcon />
      </IconButton>
      <Menu
        id={`uptime-monitor-actions-menu-${uptimeMonitor.id}`}
        anchorEl={anchorEl}
        keepMounted
        open={Boolean(anchorEl)}
        onClose={handleCloseMenu}
        PaperProps={{
          style: { maxHeight: 48 * 4.5 },
        }}
      >
        {uptimeMonitor.paused ? (
          <MenuItem key="ack" onClick={handleResume}>
            <Typography variant="inherit" noWrap>
              Resume
            </Typography>
          </MenuItem>
        ) : null}

        {!uptimeMonitor.paused ? (
          <MenuItem key="close" onClick={handlePause}>
            <Typography variant="inherit" noWrap>
              Pause
            </Typography>
          </MenuItem>
        ) : null}

        <MenuItem key="report" onClick={handleCloseMenu}>
          <Typography variant="inherit" noWrap>
            <Link to="#" onClick={handleOpenReport}>
              View Report
            </Link>
          </Typography>
        </MenuItem>

        <MenuItem key="details" onClick={handleCloseMenu}>
          <Typography variant="inherit" noWrap>
            <Link to={ilertApi.getUptimeMonitorDetailsURL(uptimeMonitor)}>
              View in iLert
            </Link>
          </Typography>
        </MenuItem>
      </Menu>
    </>
  );
}
Example #23
Source File: IncidentActionsMenu.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
IncidentActionsMenu = ({
  incident,
  onIncidentChanged,
  setIsLoading,
}: {
  incident: Incident;
  onIncidentChanged?: (incident: Incident) => void;
  setIsLoading?: (isLoading: boolean) => void;
}) => {
  const ilertApi = useApi(ilertApiRef);
  const alertApi = useApi(alertApiRef);
  const identityApi = useApi(identityApiRef);
  const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
  const callback = onIncidentChanged || ((_: Incident): void => {});
  const setProcessing = setIsLoading || ((_: boolean): void => {});
  const [isAssignIncidentModalOpened, setIsAssignIncidentModalOpened] =
    React.useState(false);

  const [{ incidentActions, isLoading }] = useIncidentActions(
    incident,
    Boolean(anchorEl),
  );

  const handleClick = (event: React.MouseEvent<HTMLElement>) => {
    setAnchorEl(event.currentTarget);
  };

  const handleCloseMenu = () => {
    setAnchorEl(null);
  };

  const handleAccept = async (): Promise<void> => {
    try {
      handleCloseMenu();
      setProcessing(true);

      const { userEntityRef } = await identityApi.getBackstageIdentity();
      const { name: userName } = parseEntityRef(userEntityRef, {
        defaultKind: 'User',
        defaultNamespace: DEFAULT_NAMESPACE,
      });
      const newIncident = await ilertApi.acceptIncident(incident, userName);
      alertApi.post({ message: 'Incident accepted.' });

      callback(newIncident);
      setProcessing(false);
    } catch (err) {
      setProcessing(false);
      alertApi.post({ message: err, severity: 'error' });
    }
  };

  const handleResolve = async (): Promise<void> => {
    try {
      handleCloseMenu();
      setProcessing(true);
      const { userEntityRef } = await identityApi.getBackstageIdentity();
      const { name: userName } = parseEntityRef(userEntityRef, {
        defaultKind: 'User',
        defaultNamespace: DEFAULT_NAMESPACE,
      });
      const newIncident = await ilertApi.resolveIncident(incident, userName);
      alertApi.post({ message: 'Incident resolved.' });

      callback(newIncident);
      setProcessing(false);
    } catch (err) {
      setProcessing(false);
      alertApi.post({ message: err, severity: 'error' });
    }
  };

  const handleAssign = () => {
    handleCloseMenu();
    setIsAssignIncidentModalOpened(true);
  };

  const handleTriggerAction = (action: IncidentAction) => async () => {
    try {
      handleCloseMenu();
      setProcessing(true);
      await ilertApi.triggerIncidentAction(incident, action);
      alertApi.post({ message: 'Incident action triggered.' });
      setProcessing(false);
    } catch (err) {
      setProcessing(false);
      alertApi.post({ message: err, severity: 'error' });
    }
  };

  const actions: React.ReactNode[] = incidentActions.map(a => {
    const successTrigger = a.history
      ? a.history.find(h => h.success)
      : undefined;
    const triggeredBy =
      successTrigger && successTrigger.actor
        ? `${successTrigger.actor.firstName} ${successTrigger.actor.lastName}`
        : '';
    return (
      <MenuItem
        key={a.webhookId}
        onClick={handleTriggerAction(a)}
        disabled={!!successTrigger}
      >
        <Typography variant="inherit" noWrap>
          {triggeredBy ? `${a.name} (by ${triggeredBy})` : a.name}
        </Typography>
      </MenuItem>
    );
  });

  return (
    <>
      <IconButton
        aria-label="more"
        aria-controls="long-menu"
        aria-haspopup="true"
        onClick={handleClick}
        size="small"
      >
        <MoreVertIcon />
      </IconButton>
      <Menu
        id={`incident-actions-menu-${incident.id}`}
        anchorEl={anchorEl}
        keepMounted
        open={Boolean(anchorEl)}
        onClose={handleCloseMenu}
        PaperProps={{
          style: { maxHeight: 48 * 5.5 },
        }}
      >
        {incident.status === 'PENDING' ? (
          <MenuItem key="ack" onClick={handleAccept}>
            <Typography variant="inherit" noWrap>
              Accept
            </Typography>
          </MenuItem>
        ) : null}

        {incident.status !== 'RESOLVED' ? (
          <MenuItem key="close" onClick={handleResolve}>
            <Typography variant="inherit" noWrap>
              Resolve
            </Typography>
          </MenuItem>
        ) : null}

        {incident.status !== 'RESOLVED' ? (
          <MenuItem key="assign" onClick={handleAssign}>
            <Typography variant="inherit" noWrap>
              Assign
            </Typography>
          </MenuItem>
        ) : null}

        {isLoading ? (
          <MenuItem key="loading">
            <Progress style={{ width: '100%' }} />
          </MenuItem>
        ) : (
          actions
        )}

        <MenuItem key="details" onClick={handleCloseMenu}>
          <Typography variant="inherit" noWrap>
            <Link to={ilertApi.getIncidentDetailsURL(incident)}>
              View in iLert
            </Link>
          </Typography>
        </MenuItem>
      </Menu>
      <IncidentAssignModal
        incident={incident}
        setIsModalOpened={setIsAssignIncidentModalOpened}
        isModalOpened={isAssignIncidentModalOpened}
        onIncidentChanged={onIncidentChanged}
      />
    </>
  );
}
Example #24
Source File: CostInsightsTabs.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
CostInsightsTabs = ({ groups }: CostInsightsTabsProps) => {
  const classes = useStyles();
  const [index] = useState(0); // index is fixed for now until other tabs are added
  const [groupMenuEl, setGroupMenuEl] = useState<Element | null>(null);
  const { group, setGroup } = useFilters(mapFiltersToProps);
  const { loadingActions, dispatchReset } = useLoading(mapLoadingToProps);

  const openGroupMenu = (e: any) => setGroupMenuEl(e.currentTarget as Element);

  const closeGroupMenu = () => setGroupMenuEl(null);

  const updateGroupFilterAndCloseMenu = (g: Group) => () => {
    dispatchReset(loadingActions);
    closeGroupMenu();
    setGroup(g);
  };

  const renderTabLabel = () => (
    <div className={classes.tabLabel}>
      <Typography className={classes.tabLabelText} variant="overline">
        {`${groups.length} teams`}
      </Typography>
      <ExpandMoreIcon fontSize="small" />
    </div>
  );

  const hasAtLeastTwoGroups = groups.length >= 2;

  if (!hasAtLeastTwoGroups) return null;

  return (
    <>
      <Tabs
        className={`cost-insights-tabs ${classes.tabs}`}
        data-testid="cost-insights-tabs"
        classes={{ indicator: classes.indicator }}
        value={index}
      >
        <Tab
          className={classes.tab}
          data-testid="cost-insights-groups-tab"
          key="cost-insights-groups-tab"
          label={renderTabLabel()}
          onClick={openGroupMenu}
          component="button"
        />
      </Tabs>
      <Menu
        id="group-menu"
        data-testid="group-menu"
        className={classes.menu}
        getContentAnchorEl={null}
        anchorEl={groupMenuEl}
        keepMounted
        open={Boolean(groupMenuEl)}
        onClose={closeGroupMenu}
        anchorOrigin={{
          vertical: 'bottom',
          horizontal: 'left',
        }}
        transformOrigin={{
          vertical: 'top',
          horizontal: 'left',
        }}
      >
        {groups.map((g: Group) => (
          <MenuItem
            className={classes.menuItem}
            classes={{ selected: classes.menuItemSelected }}
            selected={g.id === group}
            key={g.id}
            data-testid={g.id}
            onClick={updateGroupFilterAndCloseMenu(g)}
          >
            {g.id}
          </MenuItem>
        ))}
      </Menu>
    </>
  );
}
Example #25
Source File: SidebarThemeSwitcher.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
SidebarThemeSwitcher = () => {
  const appThemeApi = useApi(appThemeApiRef);
  const themeId = useObservable(
    appThemeApi.activeThemeId$(),
    appThemeApi.getActiveThemeId(),
  );
  const themeIds = appThemeApi.getInstalledThemes();
  const activeTheme = themeIds.find(t => t.id === themeId);

  const [anchorEl, setAnchorEl] = useState<Element | undefined>();
  const open = Boolean(anchorEl);

  const handleOpen = (event: React.MouseEvent) => {
    setAnchorEl(event.currentTarget);
  };

  const handleSelectTheme = (newThemeId: string | undefined) => {
    if (themeIds.some(t => t.id === newThemeId)) {
      appThemeApi.setActiveThemeId(newThemeId);
    } else {
      appThemeApi.setActiveThemeId(undefined);
    }

    setAnchorEl(undefined);
  };

  const handleClose = () => {
    setAnchorEl(undefined);
  };

  const ActiveIcon = useCallback(
    () => <ThemeIcon icon={activeTheme?.icon} />,
    [activeTheme],
  );

  return (
    <>
      <SidebarItem
        icon={ActiveIcon}
        text="Switch Theme"
        id="theme-button"
        aria-haspopup="listbox"
        aria-controls="theme-menu"
        aria-label="switch theme"
        aria-expanded={open ? 'true' : undefined}
        onClick={handleOpen}
      />

      <Menu
        id="theme-menu"
        anchorEl={anchorEl}
        open={open}
        onClose={handleClose}
        MenuListProps={{
          'aria-labelledby': 'theme-button',
          role: 'listbox',
        }}
      >
        <MenuItem disabled>Choose a theme</MenuItem>
        <MenuItem
          selected={themeId === undefined}
          onClick={() => handleSelectTheme(undefined)}
        >
          <ListItemIcon>
            <ThemeIcon icon={undefined} active={themeId === undefined} />
          </ListItemIcon>
          <ListItemText>Auto</ListItemText>
        </MenuItem>

        {themeIds.map(theme => {
          const active = theme.id === themeId;
          return (
            <MenuItem
              key={theme.id}
              selected={active}
              aria-selected={active}
              onClick={() => handleSelectTheme(theme.id)}
            >
              <ListItemIcon>
                <ThemeIcon icon={theme.icon} active={active} />
              </ListItemIcon>
              <ListItemText>{theme.title}</ListItemText>
            </MenuItem>
          );
        })}
      </Menu>
    </>
  );
}
Example #26
Source File: index.tsx    From react-app-architecture with Apache License 2.0 4 votes vote down vote up
export default function Header(): ReactElement {
  const classes = useStyles();
  const history = useHistory();
  const { isLoggedIn, data: authData } = useStateSelector(({ authState }) => authState);
  const user = authData?.user;

  const isWriter = checkRole(user, Roles.WRITER);
  const isEditor = checkRole(user, Roles.EDITOR);

  const [openAuthDialog, setOpenAuthDialog] = useState(false);
  const [drawerOpen, setDrawerOpen] = useState(false);
  const [popupMoreAnchorEl, setPopupMoreAnchorEl] = useState<HTMLElement | null>(null);
  const isPopupMenuOpen = Boolean(popupMoreAnchorEl);
  const dispatch = useDispatch();

  function handlePopupMenuClose() {
    setPopupMoreAnchorEl(null);
  }

  function handlePopupMenuOpen(event: MouseEvent<HTMLElement>) {
    setPopupMoreAnchorEl(event.currentTarget);
  }

  function toggleDrawer() {
    setDrawerOpen(!drawerOpen);
  }

  const renderProfileView = (onClick: (event: MouseEvent<HTMLButtonElement>) => void) => {
    if (!user) return null;
    return (
      <CardActionArea onClick={onClick}>
        {user.profilePicUrl ? (
          <CardHeader
            title={user.name.split(' ')[0]}
            avatar={
              <Avatar className={classes.avatar} aria-label={user.name} src={user.profilePicUrl} />
            }
          />
        ) : (
          <CardHeader title={user.name.split(' ')[0]} avatar={<FirstLetter text={user.name} />} />
        )}
      </CardActionArea>
    );
  };

  const popupMenuId = 'menu-popup';
  const popupMenu = (
    <Menu
      anchorEl={popupMoreAnchorEl}
      anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
      id={popupMenuId}
      keepMounted
      transformOrigin={{ vertical: 'top', horizontal: 'right' }}
      open={isPopupMenuOpen}
      onClose={handlePopupMenuClose}
      PopoverClasses={{ paper: classes.paper }}
    >
      {isLoggedIn && renderProfileView(handlePopupMenuClose)}
      {isWriter && (
        <MenuItem
          className={classes.menuItem}
          onClick={() => {
            history.push('/write/blog');
            handlePopupMenuClose();
          }}
        >
          <IconButton color="inherit">
            <CreateIcon />
          </IconButton>
          <p>Write Blog</p>
        </MenuItem>
      )}
      {isWriter && (
        <MenuItem
          className={classes.menuItem}
          onClick={() => {
            history.push('/writer/blogs');
            handlePopupMenuClose();
          }}
        >
          <IconButton color="inherit">
            <ListIcon />
          </IconButton>
          <p>My Blogs</p>
        </MenuItem>
      )}
      {isEditor && (
        <MenuItem
          className={classes.menuItem}
          onClick={() => {
            history.push('/editor/blogs');
            handlePopupMenuClose();
          }}
        >
          <IconButton color="inherit">
            <SupervisorAccountIcon />
          </IconButton>
          <p>Blogs Admin</p>
        </MenuItem>
      )}
      {isLoggedIn && (
        <MenuItem
          className={classes.menuItem}
          onClick={() => {
            dispatch(logout());
            handlePopupMenuClose();
          }}
        >
          <IconButton color="inherit">
            <SvgIcon>
              <path d={mdiLogout} />
            </SvgIcon>
          </IconButton>
          <p>Logout</p>
        </MenuItem>
      )}
    </Menu>
  );

  const mobileDrawerMenu = (
    <Drawer anchor="top" open={drawerOpen} onClose={toggleDrawer}>
      {isLoggedIn && renderProfileView(toggleDrawer)}
      <List component="nav">
        {[
          {
            title: 'About Project',
            href: 'https://github.com/afteracademy/react-app-architecture',
            icon: <InfoIcon />,
          },
          {
            title: 'Contact',
            href: 'https://github.com/afteracademy/react-app-architecture/issues',
            icon: <EmailIcon />,
          },
        ].map(({ title, href, icon }, position) => (
          <ListItem
            key={position}
            className={classes.drawerItem}
            button
            href={href}
            target="_blank"
            onClick={toggleDrawer}
            component="a"
          >
            <ListItemIcon className={classes.drawerIcon}>{icon}</ListItemIcon>
            <ListItemText primary={title} />
          </ListItem>
        ))}
        {[{ title: 'Blogs', link: '/blogs', icon: <WebIcon /> }].map(
          ({ title, link, icon }, position) => (
            <ListItem
              key={position}
              className={classes.drawerItem}
              button
              component={Link}
              to={link}
              onClick={toggleDrawer}
            >
              <ListItemIcon className={classes.drawerIcon}>{icon}</ListItemIcon>
              <ListItemText primary={title} />
            </ListItem>
          ),
        )}
        {isWriter && <Divider />}
        {isWriter &&
          [
            { title: 'Write Blog', link: '/write/blog', icon: <CreateIcon /> },
            { title: 'My Blogs', link: '/writer/blogs', icon: <WebIcon /> },
          ].map(({ title, link, icon }, position) => (
            <ListItem
              key={position}
              className={classes.drawerItem}
              button
              component={Link}
              to={link}
              onClick={toggleDrawer}
            >
              <ListItemIcon className={classes.drawerIcon}>{icon}</ListItemIcon>
              <ListItemText primary={title} />
            </ListItem>
          ))}
        <Divider />
        {isEditor && <Divider />}
        {isEditor &&
          [{ title: 'Blog Admin', link: '/editor/blogs', icon: <SupervisorAccountIcon /> }].map(
            ({ title, link, icon }, position) => (
              <ListItem
                key={position}
                className={classes.drawerItem}
                button
                component={Link}
                to={link}
                onClick={toggleDrawer}
              >
                <ListItemIcon className={classes.drawerIcon}>{icon}</ListItemIcon>
                <ListItemText primary={title} />
              </ListItem>
            ),
          )}
        {isLoggedIn && (
          <ListItem
            className={classes.drawerItem}
            onClick={() => {
              dispatch(logout());
              toggleDrawer();
            }}
            button
          >
            <ListItemIcon className={classes.drawerIcon}>
              <SvgIcon>
                <path d={mdiLogout} />
              </SvgIcon>
            </ListItemIcon>
            <ListItemText primary="Logout" />
          </ListItem>
        )}
        {!isLoggedIn && (
          <ListItem
            className={classes.drawerItem}
            onClick={() => {
              setOpenAuthDialog(true);
              toggleDrawer();
            }}
            button
          >
            <ListItemIcon className={classes.drawerIcon}>
              <SvgIcon>
                <path d={mdiLogin} />
              </SvgIcon>
            </ListItemIcon>
            <ListItemText primary="Login" />
          </ListItem>
        )}
      </List>
      <div className={classes.drawerCloseButtonContainer}>
        <IconButton className={classes.drawerCloseButton} onClick={toggleDrawer}>
          <CloseIcon />
        </IconButton>
      </div>
    </Drawer>
  );

  return (
    <div className={classes.root}>
      <AppBar position="fixed" color="secondary" className={classes.appbar}>
        <Toolbar>
          <Avatar
            alt="Logo"
            src={afterAcademyLogo}
            className={classes.logo}
            component={Link}
            to={'/'}
          />
          <Typography variant="h6" className={classes.brandName}>
            AfterAcademy React
          </Typography>
          <div className={classes.sectionDesktop}>
            {[
              {
                title: 'About Project',
                href: 'https://github.com/afteracademy/react-app-architecture',
              },
              {
                title: 'Contact',
                href: 'https://github.com/afteracademy/react-app-architecture/issues',
              },
            ].map(({ title, href }, position) => (
              <Button
                key={position}
                color="inherit"
                className={classes.button}
                href={href}
                target="_blank"
              >
                {title}
              </Button>
            ))}
            {[{ title: 'Blogs', link: '/blogs' }].map(({ title, link }, position) => (
              <Button
                key={position}
                color="inherit"
                className={classes.button}
                component={Link}
                to={link}
              >
                {title}
              </Button>
            ))}
            {user?.profilePicUrl ? (
              <Avatar alt={user.name} src={user.profilePicUrl} className={classes.avatar} />
            ) : (
              user?.name && <FirstLetter text={user.name} />
            )}
            {isLoggedIn ? (
              <IconButton
                aria-label="show more"
                aria-haspopup="true"
                onClick={handlePopupMenuOpen}
                color="primary"
              >
                <MenuIcon />
              </IconButton>
            ) : (
              <Fab
                variant="extended"
                size="medium"
                color="primary"
                aria-label="login"
                className={classes.loginButton}
                onClick={() => setOpenAuthDialog(true)}
              >
                Login
              </Fab>
            )}
          </div>
          <div className={classes.sectionMobile}>
            <IconButton
              aria-label="show more"
              aria-haspopup="true"
              color="inherit"
              onClick={toggleDrawer}
            >
              <MenuIcon />
            </IconButton>
          </div>
        </Toolbar>
      </AppBar>
      {popupMenu}
      {mobileDrawerMenu}
      <AuthDialog open={openAuthDialog} onClose={() => setOpenAuthDialog(false)} />
    </div>
  );
}
Example #27
Source File: Header.tsx    From frontend with Apache License 2.0 4 votes vote down vote up
Header: FunctionComponent = () => {
  const [avatarMenuRef, setAvatarMenuRef] = React.useState<null | HTMLElement>(null);
  const [helpMenuRef, setHelpMenuRef] = React.useState<null | HTMLElement>(null);
  const { loggedIn, user } = useUserState();
  const authDispatch = useUserDispatch();

  const styleMenuItem = {
    display: "flex",
    alignItems: "center",
  };

  const handleMenuClose = () => {
    setAvatarMenuRef(null);
    setHelpMenuRef(null);
  };

  const closeMenuAndOpenLink = () => {
    handleMenuClose();
    window.open("https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker/issues/new", "_blank");
  };

  const getVRTVersion = (): string => {
    //For cypress tests, window._env_ variable may be undefined, so return a dummy value.
    return window._env_ ? window._env_.VRT_VERSION : "5.0.0";
  };

  const renderHelpMenu = (
    <Menu
      anchorEl={helpMenuRef}
      anchorOrigin={{ vertical: "top", horizontal: "right" }}
      id="headerHelpMenu"
      keepMounted
      transformOrigin={{ vertical: "top", horizontal: "right" }}
      open={!!helpMenuRef}
      onClose={handleMenuClose}
    >
      <MenuItem onClick={handleMenuClose} >
        <GuidedTour />
      </MenuItem>
      <MenuItem onClick={closeMenuAndOpenLink} style={styleMenuItem}>
        <IconButton size="small">
          <GitHub />
        </IconButton>
        Open an issue in GitHub
      </MenuItem>
      <hr />
      <MenuItem style={{
        justifyContent: "center"
      }}>
        VRT Version : {getVRTVersion()}
      </MenuItem>
    </Menu >
  );

  const renderAvatarMenu = (
    <Menu
      anchorEl={avatarMenuRef}
      anchorOrigin={{ vertical: "top", horizontal: "right" }}
      id="headerAvatarMenu"
      keepMounted
      transformOrigin={{ vertical: "top", horizontal: "right" }}
      open={!!avatarMenuRef}
      onClose={handleMenuClose}
    >
      {user?.role === "admin" && (
        <MenuItem
          component={Link}
          to={routes.USER_LIST_PAGE}
          onClick={handleMenuClose}
          style={styleMenuItem}
        >
          <IconButton size="small">
            <People />
          </IconButton>
          Users
        </MenuItem>
      )}
      <MenuItem
        component={Link}
        to={routes.PROJECT_LIST_PAGE}
        onClick={handleMenuClose}
        style={styleMenuItem}
      >
        <IconButton size="small">
          <AllInbox />
        </IconButton>
        Projects
      </MenuItem>
      <MenuItem
        component={Link}
        to={routes.PROFILE_PAGE}
        onClick={handleMenuClose}
        style={styleMenuItem}
      >
        <IconButton size="small">
          <Face />
        </IconButton>
        Profile
      </MenuItem>
      <MenuItem
        onClick={() => {
          handleMenuClose();
          logout(authDispatch);
        }}
        data-testid="logoutBtn"
      >
        <IconButton size="small">
          <SettingsPower />
        </IconButton>
        Logout
      </MenuItem>
    </Menu>
  );

  return (
    <React.Fragment>
      <AppBar position="static" color="default">
        <Toolbar>
          <Grid container justifyContent="space-between" alignItems="center">
            <Grid item>
              <Link to="/">
                <img src={logo} width="60" height="60" alt="logo" />
              </Link>
            </Grid>
            <Grid item>
              <Grid
                container
                justifyContent="space-between"
                alignItems="center"
              >
                <IconButton onClick={(event: React.MouseEvent<HTMLElement>) =>
                  setHelpMenuRef(event.currentTarget)
                }>
                  <Avatar>
                    <HelpOutline />
                  </Avatar>
                </IconButton>
                {loggedIn && (
                  <IconButton
                    onClick={(event: React.MouseEvent<HTMLElement>) =>
                      setAvatarMenuRef(event.currentTarget)
                    }
                  >
                    <Avatar>{`${user?.firstName[0]}${user?.lastName[0]}`}</Avatar>
                  </IconButton>
                )}
              </Grid>
            </Grid>
          </Grid>
        </Toolbar>
      </AppBar>
      {renderAvatarMenu}
      {renderHelpMenu}
    </React.Fragment>
  );
}
Example #28
Source File: index.tsx    From frontend with Apache License 2.0 4 votes vote down vote up
BuildList: FunctionComponent = () => {
  const classes = useStyles();
  const navigate = useNavigate();
  const { buildList, selectedBuild, loading, total, take } = useBuildState();
  const buildDispatch = useBuildDispatch();
  const { enqueueSnackbar } = useSnackbar();
  const { selectedProjectId } = useProjectState();
  const [deleteDialogOpen, setDeleteDialogOpen] = React.useState(false);
  const [editDialogOpen, setEditDialogOpen] = React.useState(false);
  const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
  const [menuBuild, setMenuBuild] = React.useState<Build | null>();
  const [newCiBuildId, setNewCiBuildId] = React.useState("");
  const [paginationPage, setPaginationPage] = React.useState(1);

  const handleMenuClick = (
    event: React.MouseEvent<HTMLElement>,
    build: Build
  ) => {
    event.stopPropagation();
    setAnchorEl(event.currentTarget);
    setMenuBuild(build);
  };

  const handleMenuClose = () => {
    setMenuBuild(null);
  };

  const toggleDeleteDialogOpen = () => {
    setDeleteDialogOpen(!deleteDialogOpen);
  };

  const toggleEditDialogOpen = () => {
    setEditDialogOpen(!editDialogOpen);
  };

  const selectBuildCalback = React.useCallback(
    (id?: string) => navigate(buildTestRunLocation(id)),
    [navigate]
  );

  const handlePaginationChange = React.useCallback(
    (page: number) => {
      setPaginationPage(page);
      if (selectedProjectId) {
        buildDispatch({ type: "request" });
        buildsService
          .getList(selectedProjectId, take, take * (page - 1))
          .then((payload) => {
            buildDispatch({ type: "get", payload });
          })
          .catch((err: string) =>
            enqueueSnackbar(err, {
              variant: "error",
            })
          );
      }
    },
    [buildDispatch, enqueueSnackbar, selectedProjectId, take]
  );

  React.useEffect(() => {
    handlePaginationChange(1);
  }, [handlePaginationChange]);

  return (
    <React.Fragment>
      <Box height="91%" overflow="auto">
        <List>
          {loading ? (
            <SkeletonList />
          ) : buildList.length === 0 ? (
            <Typography variant="h5">No builds</Typography>
          ) : (
            buildList.map((build) => (
              <React.Fragment key={build.id}>
                <ListItem
                  selected={selectedBuild?.id === build.id}
                  button
                  onClick={() => selectBuildCalback(build.id)}
                  classes={{
                    container: classes.listItem,
                  }}
                >
                  <ListItemText
                    disableTypography
                    primary={
                      <Typography
                        variant="subtitle2"
                        style={{
                          wordWrap: "break-word",
                        }}
                      >
                        {`#${build.number} ${build.ciBuildId || ""}`}
                      </Typography>
                    }
                    secondary={
                      <Grid container direction="column">
                        <Grid item>
                          <Typography variant="caption" color="textPrimary">
                            {formatDateTime(build.createdAt)}
                          </Typography>
                        </Grid>
                        <Grid item>
                          <Grid container justifyContent="space-between">
                            <Grid item>
                              <Tooltip title={build.branchName}>
                                <Chip
                                  size="small"
                                  label={build.branchName}
                                  style={{ maxWidth: 180 }}
                                />
                              </Tooltip>
                            </Grid>
                            <Grid item>
                              <BuildStatusChip status={build.status} />
                            </Grid>
                          </Grid>
                        </Grid>
                      </Grid>
                    }
                  />

                  <ListItemSecondaryAction
                    className={classes.listItemSecondaryAction}
                  >
                    <IconButton
                      onClick={(event) => handleMenuClick(event, build)}
                    >
                      <MoreVert />
                    </IconButton>
                  </ListItemSecondaryAction>
                </ListItem>
                {build.isRunning && <LinearProgress />}
              </React.Fragment>
            ))
          )}
        </List>
      </Box>
      <Box height="9%">
        <Grid container justifyContent="center">
          <Grid item>
            <Pagination
              size="small"
              defaultPage={1}
              page={paginationPage}
              count={Math.ceil(total / take)}
              onChange={(event, page) => handlePaginationChange(page)}
            />
          </Grid>
        </Grid>
      </Box>

      {menuBuild && (
        <Menu anchorEl={anchorEl} open={!!menuBuild} onClose={handleMenuClose}>
          {menuBuild.isRunning && (
            <MenuItem
              onClick={() => {
                buildsService
                  .update(menuBuild.id, { isRunning: false })
                  .then((b) =>
                    enqueueSnackbar(`${menuBuild.id} finished`, {
                      variant: "success",
                    })
                  )
                  .catch((err) =>
                    enqueueSnackbar(err, {
                      variant: "error",
                    })
                  );
                handleMenuClose();
              }}
            >
              Stop
            </MenuItem>
          )}
          <MenuItem onClick={toggleEditDialogOpen}>Edit CI Build</MenuItem>
          <MenuItem onClick={toggleDeleteDialogOpen}>Delete</MenuItem>
        </Menu>
      )}
      {menuBuild && (
        <BaseModal
          open={editDialogOpen}
          title={"Edit CI Build ID"}
          submitButtonText={"Edit"}
          onCancel={toggleEditDialogOpen}
          content={
            <React.Fragment>
              <Typography>{`Edit the ci build id for build: #${
                menuBuild.number || menuBuild.id
              }`}</Typography>
              <TextValidator
                name="newCiBuildId"
                validators={["minStringLength:2"]}
                errorMessages={["Enter at least two characters."]}
                margin="dense"
                id="name"
                label="New CI Build Id"
                type="text"
                fullWidth
                required
                value={newCiBuildId}
                inputProps={{
                  onChange: (event: any) =>
                    setNewCiBuildId((event.target as HTMLInputElement).value),
                  "data-testid": "newCiBuildId",
                }}
              />
            </React.Fragment>
          }
          onSubmit={() => {
            buildsService
              .update(menuBuild.id, {
                ciBuildId: newCiBuildId,
              })
              .then((b) => {
                toggleEditDialogOpen();
              })
              .catch((err) =>
                enqueueSnackbar(err, {
                  variant: "error",
                })
              );
            handleMenuClose();
          }}
        />
      )}
      {menuBuild && (
        <BaseModal
          open={deleteDialogOpen}
          title={"Delete Build"}
          submitButtonText={"Delete"}
          onCancel={toggleDeleteDialogOpen}
          content={
            <Typography>{`Are you sure you want to delete build: #${
              menuBuild.number || menuBuild.id
            }?`}</Typography>
          }
          onSubmit={() => {
            deleteBuild(buildDispatch, menuBuild.id)
              .then((build) => {
                toggleDeleteDialogOpen();
                enqueueSnackbar(
                  `Build #${menuBuild.number || menuBuild.id} deleted`,
                  {
                    variant: "success",
                  }
                );
              })
              .then(() => handlePaginationChange(paginationPage))
              .then(() => {
                if (menuBuild.id === selectedBuild?.id) {
                  selectBuildCalback();
                }
              })
              .catch((err) =>
                enqueueSnackbar(err, {
                  variant: "error",
                })
              );
            handleMenuClose();
          }}
        />
      )}
    </React.Fragment>
  );
}
Example #29
Source File: ModActions.tsx    From ow-mod-manager with MIT License 4 votes vote down vote up
ModActions: React.FunctionComponent<Props> = ({ mod }) => {
  const styles = useStyles();
  const setLocalMods = useSetRecoilState(localModList);
  const { owmlPath, alphaPath, owamlPath } = useRecoilValue(settingsState);
  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
  const [isLoading, setIsLoading] = useRecoilState(
    modIsLoadingState(mod.uniqueName)
  );
  const { startLoading, endLoading } = useLoading();
  const setProgress = useSetRecoilState(modProgressState(mod.uniqueName));

  const handleMoreClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    setAnchorEl(event.currentTarget);
  };

  const handleClose = () => {
    setAnchorEl(null);
  };

  const isModBroken = isBroken(mod);
  const isModInstalled = mod !== undefined && isInstalled(mod);
  const isModOutdated = isOutdated(mod);
  const isModInstallable = mod.downloadUrl !== undefined;
  const isModDownloadable =
    !isLoading &&
    isModInstallable &&
    (!isModInstalled || isModBroken || isModOutdated);
  const isInstallHighlighted =
    !isLoading &&
    !isModBroken &&
    (isModOutdated || (mod.isRequired && !isModInstalled));

  const handleActionError = (actionName: string, error: string) => {
    debugConsole.error('error in action', actionName, error);
    setLocalMods(getLocalMods(owmlPath, alphaPath, owamlPath));
  };

  const modActionHandler = (
    handler: ModActionHandler<Promise<void> | void>,
    actionName: string
  ) => async () => {
    handleClose();
    if (mod !== undefined) {
      setIsLoading(true);
      startLoading();
      try {
        await handler(mod, setProgress);
      } catch (error) {
        handleActionError(actionName, `${error}`);
      } finally {
        setProgress(0);
        // TODO try something other than a SetTimeout here.
        setTimeout(() => {
          setIsLoading(false);
        }, 1000);
        endLoading();
      }
    }
  };

  const modActionHandlerSync = (
    handler: ModActionHandlerSync<void>,
    actionName: string
  ) => () => {
    handleClose();
    try {
      handler(mod);
    } catch (error) {
      handleActionError(actionName, `${error}`);
    }
  };

  const getEnableTooltip = () => {
    if (mod.isRequired) {
      return modsText.actions.disableRequired;
    }
    if (mod.isEnabled) {
      return modsText.actions.disable;
    }
    if (isModInstalled) {
      return modsText.actions.enable;
    }
    return '';
  };

  const getInstallTooltip = () => {
    if (isLoading) {
      return modsText.actions.loading;
    }
    if (isModBroken) {
      return mod.remoteVersion
        ? modsText.actions.reinstall
        : modsText.actions.cantReinstall;
    }
    if (isModOutdated && mod.remoteVersion) {
      return modsText.actions.update(mod.remoteVersion);
    }
    if (isModInstalled) {
      return modsText.actions.alreadyInstalled;
    }
    return modsText.actions.install;
  };

  return (
    <Box display="flex" justifyContent="space-between">
      <Tooltip title={getEnableTooltip()}>
        <span>
          <IconButton
            size="small"
            disabled={!isModInstalled || mod.isRequired}
            onClick={modActionHandlerSync(toggleEnabled, 'enable toggle')}
          >
            {mod.isEnabled ? <CheckBoxIcon /> : <CheckboxBlankIcon />}
          </IconButton>
        </span>
      </Tooltip>
      <Tooltip title={getInstallTooltip()}>
        <span>
          <IconButton
            onClick={modActionHandler(
              isModBroken ? reinstall : install,
              'install'
            )}
            disabled={!isModDownloadable}
            size="small"
            className={isInstallHighlighted ? styles.highlightedButton : ''}
          >
            {isLoading && <ModActionProgress modUniqueName={mod.uniqueName} />}
            {!isLoading && (isModOutdated ? <UpdateIcon /> : <SaveIcon />)}
          </IconButton>
        </span>
      </Tooltip>
      <Tooltip title={modsText.actions.readme}>
        <span>
          <IconButton
            disabled={!mod.repo}
            size="small"
            onClick={modActionHandlerSync(openReadme, 'repo open')}
          >
            <DescriptionIcon />
          </IconButton>
        </span>
      </Tooltip>
      <Tooltip title={modsText.actions.more}>
        <span>
          <IconButton size="small" onClick={handleMoreClick}>
            <MoreIcon />
          </IconButton>
        </span>
      </Tooltip>
      <Menu
        id="simple-menu"
        anchorEl={anchorEl}
        keepMounted
        open={Boolean(anchorEl)}
        onClose={handleClose}
        TransitionComponent={undefined}
        transitionDuration={0}
      >
        {mod.prerelease && (
          <MenuItem
            disabled={mod.localVersion === mod.prerelease.version}
            onClick={modActionHandler(installPrerelease, 'install prerelease')}
          >
            <ListItemIcon>
              <SaveIcon />
            </ListItemIcon>
            {modsText.actions.installPrerelease(mod.prerelease.version)}
          </MenuItem>
        )}
        {isModInstalled && (
          <MenuItem
            disabled={!isModInstalled}
            onClick={modActionHandlerSync(openModDirectory, 'directory open')}
          >
            <ListItemIcon>
              <FolderIcon />
            </ListItemIcon>
            {modsText.actions.openDirectory}
          </MenuItem>
        )}
        <MenuItem
          disabled={!mod.repo}
          onClick={modActionHandlerSync(openRepo, 'repo open')}
        >
          <ListItemIcon>
            <GitHubIcon />
          </ListItemIcon>
          {modsText.actions.openRepo}
        </MenuItem>
        <MenuItem
          disabled={mod.isRequired || !isModInstalled}
          onClick={modActionHandlerSync(uninstall, 'uninstall')}
        >
          <ListItemIcon>
            <DeleteIcon />
          </ListItemIcon>
          {modsText.actions.uninstall}
        </MenuItem>
      </Menu>
    </Box>
  );
}