@material-ui/core#Collapse TypeScript Examples

The following examples show how to use @material-ui/core#Collapse. 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: ExpandableElement.tsx    From bee-dashboard with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
export default function ExpandableElement({ children, expandable, defaultOpen }: Props): ReactElement | null {
  const classes = useStyles()
  const [open, setOpen] = useState<boolean>(Boolean(defaultOpen))

  const handleClick = () => {
    setOpen(!open)
  }

  return (
    <div className={`${classes.root} ${classes.rootLevel2}`}>
      <ListItem button onClick={handleClick} className={classes.header}>
        {children}
        {open ? <ExpandLess /> : <ExpandMore />}
      </ListItem>
      <Collapse in={open} timeout="auto" unmountOnExit>
        <div className={classes.contentLevel12}>{expandable}</div>
      </Collapse>
    </div>
  )
}
Example #2
Source File: ProductInsightsCardList.tsx    From backstage with Apache License 2.0 6 votes vote down vote up
ProductInsightsCardList = ({
  initialStates,
  onSelectAsync,
}: ProductInsightsCardListProps) => {
  if (!initialStates.length) {
    return (
      <Box display="flex" justifyContent="space-around" alignItems="center">
        <CircularProgress />
      </Box>
    );
  }

  return (
    <Collapse in timeout={1000}>
      {initialStates.map(({ product, entity, duration }) => (
        <Box
          key={product.kind}
          mb={6}
          position="relative"
          data-testid={`product-list-item-${product.kind}`}
        >
          <ProductInsightsCard
            product={product}
            onSelectAsync={onSelectAsync}
            initialState={{ entity: entity, duration: duration }}
          />
        </Box>
      ))}
    </Collapse>
  );
}
Example #3
Source File: NotificationButton.tsx    From clearflask with Apache License 2.0 6 votes vote down vote up
render() {
    if (!this.props.isLoggedIn) return null;

    return (
      <Collapse
        in={!!this.props.notifications?.length}
        className={this.props.className}
      >
        <IconButton
          aria-label='Notifications'
          onClick={e => this.setState({ notificationAnchorEl: !!this.state.notificationAnchorEl ? undefined : e.currentTarget })}
        >
          <Badge
            badgeContent={this.props.notifications ? this.props.notifications.length : 0}
            color='secondary'
            variant={this.props.showCount ? 'standard' : 'dot'}
            max={999}
          >
            <NotificationsIcon fontSize='inherit' />
            <NotificationPopup
              server={this.props.server}
              anchorEl={this.state.notificationAnchorEl}
              onClose={() => this.setState({ notificationAnchorEl: undefined })}
            />
          </Badge>
        </IconButton>
      </Collapse>
    );
  }
Example #4
Source File: alert-component.tsx    From react-spring-messenger-project with MIT License 6 votes vote down vote up
AlertComponent: React.FunctionComponent<AlertComponentType> = () => {
    const {alerts, setAlerts} = useAlertContext();

    function closeAlert(id: string) {
        const indexToDelete = alerts.findIndex((elt) => elt.id === id);
        const alertsCopy = [...alerts];
        const eltToDelete = alertsCopy[indexToDelete];
        eltToDelete.isOpen = false;
        alertsCopy[indexToDelete] = eltToDelete;
        setAlerts(alertsCopy);
        setTimeout(() => {
            alertsCopy.splice(indexToDelete, 1)
            setAlerts(alertsCopy);
        }, 3000)
    }

    return (
        <div style={{position: "absolute", bottom: "2%", left: "1%"}}>
            {
                alerts.map((value) => (
                    <div key={value.id} style={{margin: "5px"}}>
                        <Collapse in={value.isOpen}>
                            <Alert onClose={() => closeAlert(value.id)}
                                   severity={value.alert}
                                   variant={"standard"}>
                                {value.text}
                            </Alert>
                        </Collapse>
                    </div>
                ))
            }
        </div>
    )
}
Example #5
Source File: ControlsSubtabChangelog.tsx    From Teyvat.moe with GNU General Public License v3.0 6 votes vote down vote up
ControlsSubtabChangelogVersion: FunctionComponent<ControlsSubtabChangelogVersionProps> = memo(
  ({ version, date, description }) => {
    const classes = useStyles();

    const [open, setOpen] = useState(false);
    const toggleOpen = () => setOpen((value) => !value);

    return (
      <>
        <ListItem disableGutters button onClick={toggleOpen}>
          <ListItemText primary={version} secondary={date} />
          {open ? <ExpandLessIcon /> : <ExpandMoreIcon />}
        </ListItem>
        <Collapse in={open}>
          <List dense disablePadding>
            {_.map(description, (descriptionLine, index) => (
              <ListItem
                key={`${index}/${descriptionLine}`}
                disableGutters
                className={classes.subItem}
              >
                <ListItemText primary={descriptionLine} />
              </ListItem>
            ))}
          </List>
        </Collapse>
      </>
    );
  }
)
Example #6
Source File: TableExpandable.tsx    From frontegg-react with MIT License 6 votes vote down vote up
TableExpandable: FC<TableExpandableProps<any>> = <T extends object>(props: TableExpandableProps<T>) => {
  const { isExpanded, renderExpandedComponent, row } = props;

  return (
    <TableRow>
      <TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={row.cells.length}>
        <Collapse in={isExpanded} timeout='auto' unmountOnExit>
          <Box margin='0 auto'>{renderExpandedComponent?.(row.original, row.index)}</Box>
        </Collapse>
      </TableCell>
    </TableRow>
  );
}
Example #7
Source File: PDF.tsx    From binaural-meet with GNU General Public License v3.0 6 votes vote down vote up
PDF: React.FC<ContentProps> = (props:ContentProps) => {
  assert(props.content.type === 'pdf')
  const memberRef = useRef<Member>(new Member())
  const member = memberRef.current
  member.newProps = props
  const refCanvas = useRef<HTMLCanvasElement>(null)
  const refTextDiv = useRef<HTMLDivElement>(null)
  const refAnnotationDiv = useRef<HTMLDivElement>(null)
  const editing = useObserver(() => props.stores.contents.editing === props.content.id)

  useEffect(()=>{
    member.canvas = refCanvas.current
    member.textDiv = refTextDiv.current
    member.annotationDiv = refAnnotationDiv.current
  // eslint-disable-next-line  react-hooks/exhaustive-deps
  }, [refCanvas.current])

  useEffect(()=>{
    member.updateProps()
  })

  return <div style={{overflow: 'hidden', pointerEvents: 'auto', userSelect: editing? 'text':'none'}}
    onDoubleClick = {(ev) => { if (!editing) {
      ev.stopPropagation()
      ev.preventDefault()
      props.stores.contents.setEditing(props.content.id)
    } }} >
    <canvas style={{ width:`${CANVAS_SCALE*100}%`, height:`${CANVAS_SCALE*100}%`,
      transformOrigin:'top left', transform:`scale(${1/CANVAS_SCALE})`}} ref={refCanvas} />
    <div ref={refTextDiv} style={{position:'absolute', left:0, top:0,
      width:`${CANVAS_SCALE*100}%`, height:`${CANVAS_SCALE*100}%`,
      transformOrigin:'top left', transform:`scale(${1/CANVAS_SCALE})`, lineHeight: 1,
      overflow:'hidden'}} />
    <div ref={refAnnotationDiv} />
    <div style={{position:'absolute', top:0, left:0, width:'100%', height:40}}
      onPointerEnter={()=>{member.showTop = true}} onPointerLeave={()=>{member.showTop = false}}>
      <Observer>{()=>
        <Collapse in={member.showTop} style={{position:'absolute', top:0, left:0, width:'100%'}}>
          <Grid container alignItems="center">
            <Grid item >
              <IconButton size="small" color={member.pageNum>0?'primary':'default'} {...stopper}
                onClick={(ev) => { ev.stopPropagation(); member.updateUrl(member.pageNum - 1) }}
                onDoubleClick={(ev) => {ev.stopPropagation() }} >
              <NavigateBeforeIcon />
              </IconButton>
            </Grid>
            <Grid item xs={1}>
              <TextField value={member.pageText} {...stopper}
                inputProps={{min: 0, style: { textAlign: 'center' }}}
                onChange={(ev)=> { member.pageText = ev.target.value}}
                onBlur={(ev) => {
                  const num = Number(member.pageText)
                  if (num > 0) { member.updateUrl(num-1) }
                }}
                onKeyPress={(ev)=>{
                  if (ev.key === 'Enter'){
                    const num = Number(member.pageText)
                    if (num > 0) { member.updateUrl(num-1) }
                  }
                }}
              />
            </Grid>
            <Grid item style={{fontSize:15}}>/ {member.numPages}</Grid>
            <Grid item >
              <IconButton size="small" color={member.pageNum<member.numPages-1?'primary':'default'} {...stopper}
                onClick={(ev) => { ev.stopPropagation(); member.updateUrl(member.pageNum + 1) }}
                onDoubleClick={(ev) => {ev.stopPropagation() }} >
              <NavigateNextIcon />
              </IconButton>
            </Grid>
          </Grid>
        </Collapse>
      }</Observer>
    </div>
  </div>
}
Example #8
Source File: Scores.tsx    From dashboard with Apache License 2.0 5 votes vote down vote up
Scores = ({ score, nested }: ScoreProps) => {
  const [show, setShow] = useState(false)
  const { palette } = useTheme()

  let { op_name, operands, value, ref_id, description } = score
  if (!op_name && !operands && !value) {
    return <></>
  }

  const toggleShow = () => {
    setShow((prev) => !prev)
  }

  const canToggle = operands && operands.length > 0

  return (
    <div style={{ paddingLeft: nested ? "10px" : "0px" }}>
      <ListItem button={canToggle} onClick={toggleShow}>
        <Grid container>
          <Grid item xs={4}>
            <Box paddingTop="0.75rem">
              <Typography noWrap fontSize="1.25rem">
                {score.value || "0.12341234"}
              </Typography>
            </Box>
          </Grid>
          <Grid item xs={7}>
            <Box>
              <Typography noWrap>{op_name}</Typography>
              <Typography noWrap color={palette.grey[500]}>
                {description}
              </Typography>
            </Box>
          </Grid>
        </Grid>
        {canToggle && (
          <ListItemSecondaryAction>
            ({operands?.length}){show ? <ExpandLess /> : <ExpandMore />}
          </ListItemSecondaryAction>
        )}
      </ListItem>
      {operands && (
        <div>
          <Collapse in={show}>
            <List>
              {operands.map((operand, index) => (
                <Scores score={operand} key={`${index}-${ref_id}`} nested />
              ))}
            </List>
          </Collapse>
        </div>
      )}
    </div>
  )
}
Example #9
Source File: ExpandableList.tsx    From bee-dashboard with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
export default function ExpandableList({ children, label, level, defaultOpen, info }: Props): ReactElement | null {
  const classes = useStyles()
  const [open, setOpen] = useState<boolean>(Boolean(defaultOpen))

  const handleClick = () => {
    setOpen(!open)
  }

  let rootLevelClass = ''
  let typographyVariant: 'h1' | 'h2' | 'h3' = 'h1'
  let contentLevelClass = classes.contentLevel0

  if (level === 1) {
    rootLevelClass = classes.rootLevel1
    typographyVariant = 'h2'
    contentLevelClass = classes.contentLevel12
  } else if (level === 2) {
    rootLevelClass = classes.rootLevel2
    typographyVariant = 'h3'
    contentLevelClass = classes.contentLevel12
  }

  return (
    <div className={`${classes.root} ${rootLevelClass}`}>
      <ListItem button onClick={handleClick} className={classes.header}>
        <ListItemText primary={<Typography variant={typographyVariant}>{label}</Typography>} />
        <div style={{ display: 'flex' }}>
          {!open && (
            <Typography variant="body2" className={classes.infoText}>
              {info}
            </Typography>
          )}
          {open ? <ExpandLess /> : <ExpandMore />}
        </div>
      </ListItem>
      <Collapse in={open} timeout="auto" unmountOnExit>
        <div className={contentLevelClass}>{children}</div>
      </Collapse>
    </div>
  )
}
Example #10
Source File: CommentReply.tsx    From clearflask with Apache License 2.0 5 votes vote down vote up
render() {
    return (
      <Collapse
        mountOnEnter
        in={this.props.collapseIn !== false}
        className={classNames(this.props.className, this.props.classes.addCommentFormOuter)}
      >
        <div className={this.props.classes.addCommentForm}>
          <RichEditor
            uploadImage={(file) => this.richEditorImageUploadRef.current!.uploadImage(file)}
            variant='outlined'
            size='small'
            id='createComment'
            className={this.props.classes.addCommentField}
            label={this.props.inputLabel}
            iAgreeInputIsSanitized
            minInputHeight={60}
            value={this.state.newCommentInput || ''}
            onChange={e => this.setState({ newCommentInput: e.target.value })}
            multiline
            rowsMax={10}
            InputProps={{
              inputRef: this.inputRef,
              // onBlurAndEmpty after a while, fixes issue where pasting causes blur.
              onBlur: () => setTimeout(() => !this.state.newCommentInput && this.props.onBlurAndEmpty && this.props.onBlurAndEmpty(), 200),
            }}
          />
          <RichEditorImageUpload
            ref={this.richEditorImageUploadRef}
            server={this.props.server}
          />
          <Collapse in={!!this.state.newCommentInput}>
            <Button
              color='primary'
              variant='contained'
              disableElevation
              className={this.props.classes.addCommentSubmitButton}
              disabled={!this.state.newCommentInput}
              onClick={e => {
                this.props.logIn().then(() => this.props.server.dispatch().then(d => d.commentCreate({
                  projectId: this.props.server.getProjectId(),
                  ideaId: this.props.ideaId,
                  commentCreate: {
                    content: this.state.newCommentInput!,
                    parentCommentId: this.props.mergedPostId === this.props.parentCommentId ? undefined : this.props.parentCommentId,
                    mergedPostId: this.props.mergedPostId,
                  },
                }))).then(comment => {
                  this.setState({ newCommentInput: undefined })
                  this.props.onSubmitted && this.props.onSubmitted();
                });
              }}
            >
              Post
            </Button>
          </Collapse>
          {!!this.props.focusOnIn && (
            <ScrollAnchor scrollNow={this.props.collapseIn} />
          )}
        </div>
      </Collapse>
    );
  }
Example #11
Source File: SwaggerView.tsx    From dashboard with Apache License 2.0 5 votes vote down vote up
SwaggerView = () => {
  const [show, setShow] = useState(false)
  const [host, setHost] = useState(getInitialHostAndPort().host)
  const [port, setPort] = useState(getInitialHostAndPort().port)
  const [endpoint, setEndpoint] = useState(DEFAULT_ENDPOINT)
  const [url, setURL] = useState(formatURL(host, port, endpoint))

  const updateURL = () => {
    setURL(formatURL(host, port, endpoint))
  }

  const toggleShow = () => {
    setShow((prev) => !prev)
  }

  return (
    <Container data-name="debuggingTool">
      <Box textAlign="right">
        <Button onClick={toggleShow}>
          endpoint settings {show ? <ExpandLess /> : <ExpandMore />}
        </Button>
      </Box>
      <Collapse in={show}>
        <Box paddingTop="2.5em">
          <Grid container spacing={2}>
            <Grid item xs={4}>
              <TextInput
                label="Host"
                variant="outlined"
                value={host}
                onChange={(e) => setHost(e.target.value)}
              />
            </Grid>
            <Grid item xs={2}>
              <TextInput
                label="Port"
                variant="outlined"
                value={port}
                onChange={(e) => setPort(e.target.value)}
              />
            </Grid>
            <Grid item xs={4}>
              <TextInput
                label="OpenAPI Schema"
                variant="outlined"
                value={endpoint}
                onChange={(e) => setEndpoint(e.target.value)}
              />
            </Grid>
            <Grid item xs={2}>
              <FullSizeButton onClick={updateURL} variant="contained">
                Set
              </FullSizeButton>
            </Grid>
          </Grid>
        </Box>
      </Collapse>
      <SwaggerUIReact
        url={url}
        presets={[WrappedComponents]}
        requestInterceptor={(r) => console.log("request:", r)}
        responseInterceptor={(r) => console.log("response:", r)}
      />
    </Container>
  )
}
Example #12
Source File: PostEdit.tsx    From clearflask with Apache License 2.0 5 votes vote down vote up
PostSaveButton = (props: {
  children?: any;
  open?: boolean;
  showNotify?: boolean;
  isSubmitting?: boolean;
  onSave: (doNotify: boolean) => void;
  onCancel?: () => void;
}) => {
  const classes = useStyles();
  const [doNotify, setNotify] = useState<boolean>(!!props.showNotify);
  return (
    <>
      {props.children}
      <Collapse mountOnEnter in={!!props.open}>
        <div className={classes.saveButtonActions}>
          {props.showNotify && (
            <FormControlLabel
              disabled={props.isSubmitting}
              control={(
                <Checkbox
                  checked={doNotify}
                  onChange={(e, checked) => setNotify(!doNotify)}
                  color='default'
                  size='small'
                />
              )}
              label='Notify subscribers'
            />
          )}
          <div className={classes.grow} />
          {!!props.onCancel && (
            <Button
              disabled={props.isSubmitting}
              className={classes.saveButtonAction}
              onClick={() => props.onCancel?.()}
            >Cancel</Button>
          )}
          <SubmitButton
            variant='contained'
            disableElevation
            className={classes.saveButtonAction}
            isSubmitting={props.isSubmitting}
            color='primary'
            onClick={() => props.onSave(doNotify)}
          >Save</SubmitButton>
        </div>
      </Collapse>
    </>
  );
}
Example #13
Source File: NavDrawerItem.tsx    From firetable with Apache License 2.0 5 votes vote down vote up
export default function NavDrawerItem({
  section,
  tables,
  currentSection,
  currentTable,
}: INavDrawerItemProps) {
  const classes = useStyles();

  const [open, setOpen] = useState(section === currentSection);

  return (
    <li>
      <ListItem
        button
        classes={{
          root: clsx(
            classes.listItem,
            !open && currentSection === section && classes.listItemSelected
          ),
        }}
        selected={!open && currentSection === section}
        onClick={() => setOpen((o) => !o)}
      >
        <ListItemText
          primary={section}
          classes={{ primary: classes.listItemText }}
        />

        <ArrowDropDownIcon
          className={clsx(
            classes.dropdownIcon,
            open && classes.dropdownIconOpen
          )}
        />
      </ListItem>

      <Collapse in={open}>
        <List>
          {tables.map((table) => (
            <li key={table.collection}>
              <ListItem
                button
                selected={table.collection === currentTable}
                classes={{
                  root: clsx(classes.listItem, classes.childListItem),
                  selected: classes.listItemSelected,
                }}
                component={Link}
                to={
                  table.isCollectionGroup
                    ? `${routes.tableGroup}/${table.collection}`
                    : `${routes.table}/${table.collection.replace(
                        /\//g,
                        "~2F"
                      )}`
                }
              >
                <ListItemText
                  primary={table.name}
                  classes={{ primary: classes.childListItemText }}
                />
              </ListItem>
            </li>
          ))}
        </List>
      </Collapse>
    </li>
  );
}
Example #14
Source File: MobileMenu.tsx    From ra-enterprise-demo with MIT License 5 votes vote down vote up
SubMenu: FC<{
    dense?: boolean;
    handleToggle: () => void;
    icon: ReactElement;
    isOpen: boolean;
    label: string;
    sidebarIsOpen: boolean;
}> = ({
    handleToggle,
    sidebarIsOpen,
    isOpen,
    icon,
    label,
    children,
    dense = false,
}) => {
    const classes = useStyles();

    const header = (
        <MUIMenuItem
            className={classes.subMenuItem}
            dense={dense}
            button
            onClick={handleToggle}
        >
            <ListItemIcon className={classes.icon}>
                {isOpen ? <ExpandMore /> : icon}
            </ListItemIcon>
            <Typography variant="inherit" color="textSecondary">
                {label}
            </Typography>
        </MUIMenuItem>
    );

    return (
        <>
            {sidebarIsOpen || isOpen ? (
                header
            ) : (
                <Tooltip title={label} placement="right">
                    {header}
                </Tooltip>
            )}
            <Collapse in={isOpen} timeout="auto" unmountOnExit>
                <List
                    className={classes.subMenuContainer}
                    dense={dense}
                    component="div"
                    disablePadding
                >
                    {children}
                </List>
            </Collapse>
        </>
    );
}
Example #15
Source File: Row.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
export function Row({ baseVersion, releaseStat }: RowProps) {
  const [open, setOpen] = useState(false);
  const classes = useRowStyles();

  return (
    <>
      <TableRow className={classes.root}>
        <TableCell>
          <IconButton
            aria-label="expand row"
            size="small"
            onClick={() => setOpen(!open)}
          >
            {open ? <KeyboardArrowDownIcon /> : <ChevronRightIcon />}
          </IconButton>
        </TableCell>

        <TableCell component="th" scope="row">
          <Link to={releaseStat.htmlUrl} target="_blank">
            {baseVersion}
            {releaseStat.versions.length === 0 ? ' (prerelease)' : ''}
          </Link>
        </TableCell>

        <TableCell>
          {releaseStat.createdAt
            ? DateTime.fromISO(releaseStat.createdAt).toFormat('yyyy-MM-dd')
            : '-'}
        </TableCell>

        <TableCell>{releaseStat.candidates.length}</TableCell>

        <TableCell>{Math.max(0, releaseStat.versions.length - 1)}</TableCell>
      </TableRow>

      <TableRow>
        <TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
          <Collapse in={open} timeout="auto" unmountOnExit>
            <RowCollapsed releaseStat={releaseStat} />
          </Collapse>
        </TableCell>
      </TableRow>
    </>
  );
}
Example #16
Source File: Facets.tsx    From cognitive-search-static-web-apps-sample-ui with MIT License 5 votes vote down vote up
render(): JSX.Element {

        const state = this.props.state;

        return (<FacetList component="nav">

            {state.facets.map(facetState => {

                var facetComponent: JSX.Element = null;
                switch (facetState.facetType) {
                    case FacetTypeEnum.BooleanFacet:
                        facetComponent = (<BooleanFacet state={facetState.state as BooleanFacetState} inProgress={this.props.inProgress} />);
                    break;
                    case FacetTypeEnum.NumericFacet:
                        facetComponent = (<NumericFacet state={facetState.state as NumericFacetState} inProgress={this.props.inProgress} />);
                    break;
                    case FacetTypeEnum.DateFacet:
                        facetComponent = (<DateFacet state={facetState.state as DateFacetState} inProgress={this.props.inProgress} />);
                    break;
                    case FacetTypeEnum.StringFacet:
                        facetComponent = (<StringFacet state={facetState.state as StringFacetState} inProgress={this.props.inProgress} />);
                    break;
                    case FacetTypeEnum.StringCollectionFacet:
                        facetComponent = (<StringCollectionFacet state={facetState.state as StringCollectionFacetState} inProgress={this.props.inProgress} />);
                    break;
                }

                // Getting reference to a proper getHintText method in this a bit unusual and not very strongly typed way
                const getHintTextFunc = facetComponent?.type.getHintText;

                return (<div key={facetState.displayName}>
                
                    <FacetListItem disableRipple={true} button onClick={() => state.toggleExpand(facetState.fieldName)}>
                        <ListItemText
                            primary={facetState.displayName}
                            secondary={getHintTextFunc ? getHintTextFunc(facetState.state) : ''}
                        />
                        {!!facetState.isExpanded ? <ExpandLess /> : <ExpandMore />}
                    </FacetListItem>

                    <Collapse in={facetState.isExpanded} timeout={200} unmountOnExit>
                        {facetComponent}
                    </Collapse>
                    
                </div>);
            })}
                
        </FacetList>);
    }
Example #17
Source File: CostInsightsNavigation.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
CostInsightsNavigation = React.memo(
  ({ alerts, products }: CostInsightsNavigationProps) => {
    const classes = useStyles();
    const { icons } = useConfig();
    const [isOpen, setOpen] = useState(false);

    const defaultNavigationItems = getDefaultNavigationItems(alerts);
    const productNavigationItems: NavigationItem[] =
      products?.map(product => ({
        title: product.name,
        navigation: product.kind,
        icon: findAlways(icons, i => i.kind === product.kind).component,
      })) ?? [];

    useEffect(
      function toggleProductMenuItems() {
        if (products?.length) {
          setOpen(true);
        } else {
          setOpen(false);
        }
      },
      [products],
    );

    return (
      <MenuList className={classes.menuList}>
        {defaultNavigationItems.map(item => (
          <NavigationMenuItem
            key={`navigation-menu-item-${item.navigation}`}
            navigation={item.navigation}
            title={item.title}
            icon={
              item.navigation === DefaultNavigation.AlertInsightsHeader ? (
                <Badge badgeContent={alerts} color="secondary">
                  {React.cloneElement(item.icon, {
                    className: classes.navigationIcon,
                  })}
                </Badge>
              ) : (
                React.cloneElement(item.icon, {
                  className: classes.navigationIcon,
                })
              )
            }
          />
        ))}
        <Collapse in={isOpen} timeout={850}>
          {productNavigationItems.map((item: NavigationItem) => (
            <NavigationMenuItem
              key={`navigation-menu-item-${item.navigation}`}
              navigation={item.navigation}
              icon={React.cloneElement(item.icon, {
                className: classes.navigationIcon,
              })}
              title={item.title}
            />
          ))}
        </Collapse>
      </MenuList>
    );
  },
)
Example #18
Source File: SideDrawerField.tsx    From firetable with Apache License 2.0 5 votes vote down vote up
export default function Color({
  column,
  control,
  disabled,
}: ISideDrawerFieldProps) {
  const classes = useStyles();
  const fieldClasses = useFieldStyles();

  const [showPicker, setShowPicker] = useState(false);
  const toggleOpen = () => setShowPicker((s) => !s);

  return (
    <Controller
      control={control}
      name={column.key}
      render={({ onChange, onBlur, value }) => (
        <>
          <Grid
            container
            alignItems="center"
            spacing={1}
            className={classes.root}
            onClick={() => {
              toggleOpen();
              onBlur();
            }}
            component={ButtonBase}
            focusRipple
            disabled={disabled}
          >
            <Grid item>
              <div
                className={classes.colorIndicator}
                style={{ backgroundColor: value?.hex }}
              />
            </Grid>

            <Grid item xs>
              <Typography
                variant="body1"
                color={value?.hex ? "textPrimary" : "textSecondary"}
              >
                {value?.hex ?? "Choose a color…"}
              </Typography>
            </Grid>
          </Grid>

          <Collapse in={showPicker}>
            <ChromePicker color={value?.rgb} onChangeComplete={onChange} />
          </Collapse>
        </>
      )}
    />
  );
}
Example #19
Source File: Form.tsx    From aqualink-app with MIT License 5 votes vote down vote up
SurveyForm = ({ siteId, timeZone, changeTab }: SurveyFormProps) => {
  const user = useSelector(userInfoSelector);
  const surveyError = useSelector(surveyErrorSelector);

  const dispatch = useDispatch();

  const onSubmit = useCallback(
    (
      diveDateTime: string,
      diveLocation: SurveyState["diveLocation"],
      weatherConditions: SurveyData["weatherConditions"],
      comments: string
    ) => {
      const surveyData: SurveyData = {
        site: siteId,
        diveDate: diveDateTime,
        diveLocation,
        weatherConditions,
        comments,
        token: user?.token,
      };
      dispatch(
        surveyAddRequest({
          siteId: `${siteId}`,
          surveyData,
          changeTab,
        })
      );
    },
    [dispatch, changeTab, siteId, user]
  );

  return (
    <>
      <Grid item xs={12}>
        <Collapse in={!!surveyError}>
          <Alert
            severity="error"
            action={
              <IconButton aria-label="close" color="inherit" size="small" />
            }
          >
            There was an error creating the survey.
          </Alert>
        </Collapse>
      </Grid>
      <Form siteId={siteId} timeZone={timeZone} onSubmit={onSubmit} />
    </>
  );
}
Example #20
Source File: Header.tsx    From storefront with MIT License 5 votes vote down vote up
Header: React.VFC = () => {
  const styles = useStyles();
  const { settings } = useSettings();
  const [open, toggleOpen] = useToggle(false);

  const { data: menu } = useMenuQuery({
    variables: { location: MenuLocationEnum.PRIMARY_NAVIGATION },
  });

  const { data: { cart } = { cart: undefined } } = useCartQuery({
    fetchPolicy: 'no-cache',
    ssr: false,
  });

  return (
    <AppBar color="default" position="relative">
      <Toolbar
        className={styles.toolbar}
        sx={{
          minHeight: { xs: 60, md: 110 },
          mx: 'auto',
          width: '100%',
        }}
      >
        <Box sx={{ display: { md: 'none' }, flexGrow: 1 }}>
          <IconButton aria-label="Menu" onClick={toggleOpen}>
            <Menu />
          </IconButton>
        </Box>
        <Link href="/" underline="none">
          <Logo className={styles.logo} aria-label={settings.title} />
        </Link>
        <Box
          sx={{
            alignItems: 'center',
            alignSelf: 'stretch',
            display: 'flex',
            flexGrow: 1,
            justifyContent: 'flex-end',
          }}
        >
          <Box sx={{ display: { xs: 'none', md: 'flex' }, height: '100%' }}>
            <HeaderMenu menu={menu} />
          </Box>
          <IconButton href="/cart" color="inherit" aria-label="Cart">
            <Badge badgeContent={cart?.contents?.itemCount}>
              <Cart />
            </Badge>
          </IconButton>
        </Box>
      </Toolbar>
      <Collapse unmountOnExit in={open} timeout="auto">
        <HeaderMenu menu={menu} />
      </Collapse>
    </AppBar>
  );
}
Example #21
Source File: AlertStatusSummary.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
AlertStatusSummary = ({
  open,
  snoozed,
  accepted,
  dismissed,
}: AlertStatusSummaryProps) => {
  const isSnoozedListDisplayed = !!snoozed.length;
  const isAcceptedListDisplayed = !!accepted.length;
  const isDismissedListDisplayed = !!dismissed.length;

  return (
    <Collapse in={open}>
      {isAcceptedListDisplayed && (
        <AlertGroup
          title="Accepted"
          alerts={accepted}
          status={AlertStatus.Accepted}
          icon={
            <AcceptIcon
              role="img"
              aria-hidden={false}
              aria-label={AlertStatus.Accepted}
            />
          }
        />
      )}
      {isSnoozedListDisplayed && (
        <AlertGroup
          title="Snoozed"
          alerts={snoozed}
          status={AlertStatus.Snoozed}
          icon={
            <SnoozeIcon
              role="img"
              aria-hidden={false}
              aria-label={AlertStatus.Snoozed}
            />
          }
        />
      )}
      {isDismissedListDisplayed && (
        <AlertGroup
          title="Dismissed"
          alerts={dismissed}
          status={AlertStatus.Dismissed}
          icon={
            <DismissIcon
              role="img"
              aria-hidden={false}
              aria-label={AlertStatus.Dismissed}
            />
          }
        />
      )}
    </Collapse>
  );
}
Example #22
Source File: index.tsx    From aqualink-app with MIT License 4 votes vote down vote up
DeleteButton = ({
  header,
  content,
  onConfirm,
  onSuccess,
  onError,
  classes,
}: DeleteButtonProps) => {
  const [open, setOpen] = useState<boolean>(false);
  const [alertOpen, setAlertOpen] = useState<boolean>(false);
  const [alertText, setAlertText] = useState<string>("");
  const [loading, setLoading] = useState<boolean>(false);

  const handleClickOpen = () => {
    setOpen(true);
  };

  const handleClose = () => {
    setOpen(false);
    setAlertOpen(false);
    setAlertText("");
  };

  const onDelete = async () => {
    setLoading(true);
    try {
      await onConfirm();
      onSuccess?.();
    } catch (error) {
      onError?.();
      setAlertOpen(true);
      setAlertText((error as any)?.message);
    }
    setLoading(false);
  };

  const dialogActions: Action[] = [
    {
      size: "small",
      variant: "outlined",
      color: "secondary",
      text: "Cancel",
      action: handleClose,
    },
    {
      size: "small",
      variant: "outlined",
      color: "primary",
      text: "Yes",
      action: onDelete,
    },
  ];

  return (
    <>
      <IconButton onClick={handleClickOpen}>
        <DeleteOutlineIcon color="primary" />
      </IconButton>
      <DeleteDialog
        open={open}
        onClose={handleClose}
        header={header}
        content={
          <>
            {content}
            {loading && <LinearProgress />}
            <Collapse className={classes.alert} in={alertOpen}>
              <Alert
                severity="error"
                action={
                  <IconButton
                    color="inherit"
                    size="small"
                    onClick={() => {
                      setAlertOpen(false);
                    }}
                  >
                    <CloseIcon fontSize="inherit" />
                  </IconButton>
                }
              >
                {alertText}
              </Alert>
            </Collapse>
          </>
        }
        actions={dialogActions}
      />
    </>
  );
}
Example #23
Source File: AccountEnterPage.tsx    From clearflask with Apache License 2.0 4 votes vote down vote up
render() {
    if (this.props.plansStatus === undefined) {
      ServerAdmin.get().dispatchAdmin({ debounce: true, ssr: true }).then(d => d
        .plansGet());
    }
    const isLoggedIn = this.props.accountStatus === Status.FULFILLED && !!this.props.account;
    if (this.props.type === 'invitation') {
      return this.renderInvitation(isLoggedIn);
    }
    if (this.props.type === 'coupon') {
      return this.renderCoupon(isLoggedIn);
    }

    if (this.props.accountStatus === Status.FULFILLED && !!this.props.account
      // Only redirect once submission is over (and redirectTo and accountWasCreated is set appropriately)
      && !this.state.isSubmitting) {
      if (this.props.cfJwt && this.cfReturnUrl) {
        windowIso.location.href = `${this.cfReturnUrl}?${SSO_TOKEN_PARAM_NAME}=${this.props.cfJwt}`;
        return (<ErrorPage msg={this.props.t('redirecting-you-back')} variant='success' />);
      }
      return (<RedirectIso to={this.state.redirectTo
        || this.props.location.state?.[ADMIN_LOGIN_REDIRECT_TO]
        || (this.state.accountWasCreated
          ? '/dashboard/welcome' :
          '/dashboard')} />);
    }

    const selectedPlanId = this.props.location.state?.[PRE_SELECTED_BASE_PLAN_ID]
      || (this.props.plans ? this.props.plans[0].basePlanId : undefined);

    if (this.props.type === 'signup') {
      if (!selectedPlanId && !this.props.plans) {
        return <LoadingPage />
      }
      if (!selectedPlanId || !SIGNUP_PROD_ENABLED && isProd() && new URL(windowIso.location.href).searchParams.get('please') !== undefined) {
        return <ErrorPage variant='warning' msg={(
          <div style={{ display: 'flex', alignItems: 'baseline', flexWrap: 'wrap', }} >
            Direct sign ups are currently disabled. Instead,&nbsp;
            <NavLink to='/contact/demo' className={this.props.classes.link}>schedule a demo</NavLink>
            &nbsp;with us.
          </div>
        )} />
      }
    }

    const isSingleCustomer = detectEnv() === Environment.PRODUCTION_SELF_HOST;
    const isOauthEnabled = !isSingleCustomer;
    const signUpOrLogIn = this.props.type === 'signup' ? this.props.t('sign-up-with') : this.props.t('log-in-with');

    return (
      <EnterTemplate
        title={(
          <>
            {(this.props.type === 'signup' ? this.props.t('get-started-with') : this.props.t('welcome-back-to')) + ' '}
            <span className={this.props.classes.titleClearFlask}>ClearFlask</span>
          </>
        )}
        renderContent={submitButton => (
          <>
            {this.state.couponPlan && (
              <Alert className={this.props.classes.alert} severity='info'>
                Redeeming <span className={this.props.classes.bold}>{this.state.couponPlan.title}</span> plan.
              </Alert>
            )}
            {this.props.invitation?.projectName && (
              <Alert className={this.props.classes.alert} severity='info'>
                Invitation to <span className={this.props.classes.bold}>{this.props.invitation?.projectName}</span>.
              </Alert>
            )}
            {isOauthEnabled && (
              <>
                <Button
                  className={this.props.classes.oauthEnter}
                  variant='outlined'
                  fullWidth
                  size='large'
                  onClick={e => !!selectedPlanId && this.onOauth('google', selectedPlanId)}
                  disabled={this.state.isSubmitting}
                >
                  <GoogleIcon />
                  &nbsp;&nbsp;{signUpOrLogIn}&nbsp;Google
                </Button>
                <Button
                  className={this.props.classes.oauthEnter}
                  variant='outlined'
                  fullWidth
                  size='large'
                  onClick={e => !!selectedPlanId && this.onOauth('github', selectedPlanId)}
                  disabled={this.state.isSubmitting}
                >
                  <GithubIcon />
                  &nbsp;&nbsp;{signUpOrLogIn}&nbsp;GitHub
                </Button>
                {!isProd() && (
                  <Button
                    className={this.props.classes.oauthEnter}
                    variant='outlined'
                    fullWidth
                    size='large'
                    onClick={e => !!selectedPlanId && this.onOauth('bathtub', selectedPlanId)}
                    disabled={this.state.isSubmitting}
                  >
                    <BathtubIcon />
                    &nbsp;&nbsp;{signUpOrLogIn}&nbsp;Bathtub
                  </Button>
                )}
              </>
            )}
            <Collapse in={!this.state.useEmail}>
              <Button
                className={this.props.classes.oauthEnter}
                variant='outlined'
                fullWidth
                size='large'
                onClick={e => this.setState({ useEmail: true })}
                disabled={this.state.isSubmitting}
              >
                <EmailIcon />
                &nbsp;&nbsp;{signUpOrLogIn}&nbsp;{this.props.t('email')}
              </Button>
            </Collapse>
            <Collapse in={this.state.useEmail}>
              <div>
                {isOauthEnabled && (
                  <Hr isInsidePaper length={120} margins={15}>{this.props.t('or')}</Hr>
                )}
                <Collapse in={this.props.type === 'signup'}>
                  <TextField
                    variant='outlined'
                    fullWidth
                    margin='normal'
                    placeholder={this.props.t('your-name-organization')}
                    required
                    value={this.state.name || ''}
                    onChange={e => this.setState({ name: e.target.value })}
                    disabled={this.state.isSubmitting}
                  />
                </Collapse>
                <TextField
                  variant='outlined'
                  fullWidth
                  required
                  value={this.state.email || ''}
                  onChange={e => {
                    const newEmail = e.target.value;
                    this.setState({ email: newEmail });
                    if (this.props.type === 'signup') {
                      import(/* webpackChunkName: "emailDisposableList" */'../common/util/emailDisposableList')
                        .then(eu => this.setState({
                          emailIsFreeOrDisposable: eu.isDisposable(newEmail),
                        }));
                    }
                  }}
                  placeholder={this.props.type === 'login' ? this.props.t('email') : this.props.t('business-email')}
                  type='email'
                  margin='normal'
                  disabled={this.state.isSubmitting}
                />
                <Collapse in={this.props.type === 'signup' && !!this.state.emailIsFreeOrDisposable}>
                  <Message severity='warning' message={(
                    <div style={{ display: 'flex', alignItems: 'baseline', flexWrap: 'wrap', }} >
                      {this.props.t('cannot-use-a-disposable-email')} Is this a mistake?&nbsp;
                      <NavLink to='/contact/demo' className={this.props.classes.link}>Schedule a demo</NavLink>
                      &nbsp;with us.
                    </div>
                  )} />
                </Collapse>
                <TextField
                  variant='outlined'
                  fullWidth
                  required
                  value={this.state.pass || ''}
                  onChange={e => this.setState({ pass: e.target.value })}
                  placeholder={this.props.t('password')}
                  type={this.state.revealPassword ? 'text' : 'password'}
                  InputProps={{
                    endAdornment: (
                      <InputAdornment position='end'>
                        <IconButton
                          aria-label='Toggle password visibility'
                          onClick={() => this.setState({ revealPassword: !this.state.revealPassword })}
                        >
                          {this.state.revealPassword ? <VisibilityIcon fontSize='small' /> : <VisibilityOffIcon fontSize='small' />}
                        </IconButton>
                      </InputAdornment>
                    )
                  }}
                  margin='normal'
                  disabled={this.state.isSubmitting}
                />
                {this.props.type === 'signup' && (
                  <AcceptTerms />
                )}
                {submitButton}
              </div>
            </Collapse>
          </>
        )}
        submitTitle={this.props.type === 'signup' ? this.props.t('create-account') : this.props.t('continue')}
        submitDisabled={
          !this.state.email || !this.state.pass
          || this.props.type === 'signup' && (
            !this.state.name
            || !!this.state.emailIsFreeOrDisposable)}
        isSubmitting={this.state.isSubmitting}
        onSubmit={this.props.type === 'signup' ? this.signUp.bind(this, selectedPlanId!) : this.onLogin.bind(this)}
        footer={this.props.type === 'signup' ? {
          text: this.props.t('have-an-account'),
          actionText: this.props.t('log-in-here'),
          linkTo: {
            pathname: '/login',
            state: this.props.location.state,
          },
        } : {
          text: this.props.t('no-account'),
          actionText: this.props.t('sign-up-here'),
          linkTo: {
            pathname: '/signup',
            state: this.props.location.state,
          },
        }}
        layout={this.props.type}
      />
    );
  }
Example #24
Source File: index.tsx    From aqualink-app with MIT License 4 votes vote down vote up
SignInDialog = ({
  open,
  handleRegisterOpen,
  handleSignInOpen,
  classes,
}: SignInDialogProps) => {
  const dispatch = useDispatch();
  const user = useSelector(userInfoSelector);
  const loading = useSelector(userLoadingSelector);
  const error = useSelector(userErrorSelector);
  const [errorAlertOpen, setErrorAlertOpen] = useState<boolean>(false);
  const [passwordResetEmail, setPasswordResetEmail] = useState<string>("");
  const { register, errors, handleSubmit } = useForm<SignInFormFields>({
    reValidateMode: "onSubmit",
  });

  useEffect(() => {
    if (user) {
      handleSignInOpen(false);
    }
    if (error) {
      setErrorAlertOpen(true);
    }
  }, [user, handleSignInOpen, error]);

  const onSubmit = (
    data: SignInFormFields,
    event?: BaseSyntheticEvent<object, HTMLElement, HTMLElement>
  ) => {
    if (event) {
      event.preventDefault();
    }
    const registerInfo: UserSignInParams = {
      email: data.emailAddress.toLowerCase(),
      password: data.password,
    };
    dispatch(signInUser(registerInfo));
  };

  const onResetPassword = (
    { emailAddress }: SignInFormFields,
    event?: BaseSyntheticEvent<object, HTMLElement, HTMLElement>
  ) => {
    if (event) {
      event.preventDefault();
    }
    dispatch(resetPassword({ email: emailAddress.toLowerCase() }));
    setPasswordResetEmail(emailAddress.toLowerCase());
  };

  const clearUserError = () => dispatch(clearError());

  return (
    <Dialog
      onEnter={() => {
        clearUserError();
        setPasswordResetEmail("");
      }}
      open={open}
      maxWidth="xs"
    >
      <Card elevation={0}>
        <CardHeader
          className={classes.dialogHeader}
          title={
            <Grid container alignItems="center" justify="space-between">
              <Grid item>
                <Grid container>
                  <Typography variant="h4">Aqua</Typography>
                  <Typography
                    className={classes.dialogHeaderSecondPart}
                    variant="h4"
                  >
                    link
                  </Typography>
                </Grid>
              </Grid>
              <Grid item>
                <IconButton
                  className={classes.closeButton}
                  size="small"
                  onClick={() => handleSignInOpen(false)}
                >
                  <CloseIcon />
                </IconButton>
              </Grid>
            </Grid>
          }
        />
        {loading && <LinearProgress />}
        {error && (
          <Collapse in={errorAlertOpen}>
            <Alert
              severity="error"
              action={
                <IconButton
                  aria-label="close"
                  color="inherit"
                  size="small"
                  onClick={() => {
                    setErrorAlertOpen(false);
                  }}
                >
                  <CloseIcon fontSize="inherit" />
                </IconButton>
              }
            >
              {error}
            </Alert>
          </Collapse>
        )}
        <Collapse in={passwordResetEmail !== ""}>
          <Alert
            severity="success"
            action={
              <IconButton
                aria-label="close"
                color="inherit"
                size="small"
                onClick={() => {
                  setPasswordResetEmail("");
                }}
              >
                <CloseIcon fontSize="inherit" />
              </IconButton>
            }
          >
            {`Password reset email sent to ${passwordResetEmail}.`}
          </Alert>
        </Collapse>
        <CardContent>
          <Grid container justify="center" item xs={12}>
            <Grid className={classes.dialogContentTitle} container item xs={10}>
              <Grid item>
                <Typography variant="h5" color="textSecondary">
                  Sign In
                </Typography>
              </Grid>
            </Grid>
            <Grid container item xs={10}>
              <form className={classes.form} onSubmit={handleSubmit(onSubmit)}>
                <Grid className={classes.textFieldWrapper} item xs={12}>
                  {/* TODO: ADD THIS WHEN WE ENABLE GOOGLE LOGIN */}
                  {/* <Typography className={classes.formText} variant="subtitle2">
                    or login with email address
                  </Typography> */}
                  <TextField
                    id="emailAddress"
                    name="emailAddress"
                    placeholder="Email Address"
                    helperText={
                      (errors.emailAddress &&
                        (errors.emailAddress.type === "validate"
                          ? "Invalid email address"
                          : errors.emailAddress.message)) ||
                      ""
                    }
                    label="Email Address"
                    inputRef={register({
                      required: "This is a required field",
                      validate: (value) => isEmail(value),
                    })}
                    error={!!errors.emailAddress}
                    inputProps={{ className: classes.textField }}
                    fullWidth
                    variant="outlined"
                  />
                </Grid>
                <Grid className={classes.textFieldWrapper} item xs={12}>
                  <TextField
                    id="password"
                    name="password"
                    type="password"
                    placeholder="Password"
                    helperText={errors.password ? errors.password.message : ""}
                    label="Password"
                    inputRef={register({
                      required: "This is a required field",
                    })}
                    error={!!errors.password}
                    inputProps={{ className: classes.textField }}
                    fullWidth
                    variant="outlined"
                  />
                </Grid>
                <Grid container item xs={12}>
                  <Button
                    className={classes.forgotPasswordButton}
                    onClick={handleSubmit(onResetPassword)}
                  >
                    <Typography variant="subtitle2" color="textSecondary">
                      Forgot your password?
                    </Typography>
                  </Button>
                </Grid>
                <Grid className={classes.button} item xs={12}>
                  <Button
                    fullWidth
                    type="submit"
                    color="primary"
                    variant="contained"
                    size="large"
                  >
                    SIGN IN
                  </Button>
                </Grid>
                <Grid container item xs={12}>
                  <Typography
                    className={classes.formText}
                    variant="subtitle1"
                    color="textSecondary"
                  >
                    Don&#39;t have an account?{" "}
                    <Button
                      onClick={() => {
                        handleRegisterOpen(true);
                        handleSignInOpen(false);
                      }}
                      color="primary"
                    >
                      SIGN UP
                    </Button>
                  </Typography>
                </Grid>
              </form>
            </Grid>
          </Grid>
        </CardContent>
      </Card>
    </Dialog>
  );
}
Example #25
Source File: BillingPage.tsx    From clearflask with Apache License 2.0 4 votes vote down vote up
render() {
    if (!this.props.account) {
      return 'Need to login to see this page';
    }

    const status = this.props.accountStatus === Status.FULFILLED ? this.props.accountBillingStatus : this.props.accountStatus;
    if (!this.props.accountBilling || status !== Status.FULFILLED) {
      return (
        <Loader skipFade status={status} />
      );
    }

    var cardNumber, cardExpiry, cardStateIcon;
    if (!!this.props.accountBilling?.payment) {
      cardNumber = (
        <>
          <span className={this.props.classes.blurry}>5200&nbsp;8282&nbsp;8282&nbsp;</span>
          {this.props.accountBilling.payment.last4}
        </>
      );
      var expiryColor;
      if (new Date().getFullYear() % 100 >= this.props.accountBilling.payment.expiryYear % 100) {
        if (new Date().getMonth() + 1 === this.props.accountBilling.payment.expiryMonth) {
          expiryColor = this.props.theme.palette.warning.main;
        } else if (new Date().getMonth() + 1 > this.props.accountBilling.payment.expiryMonth) {
          expiryColor = this.props.theme.palette.error.main;
        }
      }
      cardExpiry = (
        <span style={expiryColor && { color: expiryColor }}>
          {this.props.accountBilling.payment.expiryMonth}
          &nbsp;/&nbsp;
          {this.props.accountBilling.payment.expiryYear % 100}
        </span>
      );
    } else {
      cardNumber = (<span className={this.props.classes.blurry}>5200&nbsp;8282&nbsp;8282&nbsp;8210</span>);
      cardExpiry = (<span className={this.props.classes.blurry}>06 / 32</span>);
    }
    var hasAvailablePlansToSwitch: boolean = (this.props.accountBilling?.availablePlans || [])
      .filter(p => p.basePlanId !== this.props.accountBilling?.plan.basePlanId)
      .length > 0;
    var cardState: 'active' | 'warn' | 'error' = 'active';
    var paymentTitle, paymentDesc, showContactSupport, showSetPayment, setPaymentTitle, setPaymentAction, showCancelSubscription, showResumePlan, resumePlanDesc, planTitle, planDesc, showPlanChange, endOfTermChangeToPlanTitle, endOfTermChangeToPlanDesc, switchPlanTitle;
    switch (this.props.account.subscriptionStatus) {
      case Admin.SubscriptionStatus.Active:
        if (this.props.accountBilling?.plan.basePlanId === TeammatePlanId) {
          paymentTitle = 'No payment required';
          paymentDesc = 'While you only access external projects, payments are made by the project owner. No payment is required from you at this time.';
          cardState = 'active';
          showSetPayment = false;
          showCancelSubscription = false;
          planTitle = 'You are not on a plan';
          planDesc = 'While you only access external projects, you are not required to be on a plan. If you decide to create a project under your account, you will be able to choose a plan and your trial will begin.';
          if (hasAvailablePlansToSwitch) {
            showPlanChange = true;
            switchPlanTitle = 'Choose plan'
          }
        } else {
          paymentTitle = 'Automatic renewal is active';
          paymentDesc = 'You will be automatically billed at the next cycle and your plan will be renewed.';
          cardState = 'active';
          showSetPayment = true;
          setPaymentTitle = 'Update payment method';
          showCancelSubscription = true;
          planTitle = 'Your plan is active';
          planDesc = `You have full access to your ${this.props.accountBilling.plan.title} plan.`;
          if (hasAvailablePlansToSwitch) {
            planDesc += ' If you upgrade your plan, changes will reflect immediately. If you downgrade your plan, changes will take effect at the end of the term.';
            showPlanChange = true;
          }
        }
        break;
      case Admin.SubscriptionStatus.ActiveTrial:
        if (this.props.accountBilling?.payment) {
          paymentTitle = 'Automatic renewal is active';
          if (this.props.accountBilling?.billingPeriodEnd) {
            paymentDesc = (
              <>
                Your first payment will be automatically billed at the end of the trial period in&nbsp;<TimeAgo date={this.props.accountBilling?.billingPeriodEnd} />.
              </>
            );
          } else {
            paymentDesc = `Your first payment will be automatically billed at the end of the trial period.`;
          }
          cardState = 'active';
          showSetPayment = true;
          setPaymentTitle = 'Update payment method';
          planTitle = 'Your plan is active';
          planDesc = `You have full access to your ${this.props.accountBilling.plan.title} plan.`;
          if (hasAvailablePlansToSwitch) {
            planDesc += ' If you switch plans now, your first payment at the end of your trial will reflect your new plan.';
            showPlanChange = true;
          }
        } else {
          paymentTitle = 'Automatic renewal requires a payment method';
          paymentDesc = 'To continue using our service beyond the trial period, add a payment method to enable automatic renewal.';
          cardState = 'warn';
          showSetPayment = true;
          setPaymentTitle = 'Add payment method';
          planTitle = 'Your plan is active until your trial ends';
          if (this.props.accountBilling?.billingPeriodEnd) {
            planDesc = (
              <>
                You have full access to your {this.props.accountBilling.plan.title} plan until your trial expires in&nbsp;<TimeAgo date={this.props.accountBilling?.billingPeriodEnd} />. Add a payment method to continue using our service beyond the trial period.
              </>
            );
          } else {
            planDesc = `You have full access to your ${this.props.accountBilling.plan.title} plan until your trial expires. Add a payment method to continue using our service beyond the trial period.`;
          }
          if (hasAvailablePlansToSwitch) {
            showPlanChange = true;
          }
        }
        break;
      case Admin.SubscriptionStatus.ActivePaymentRetry:
        paymentTitle = 'Automatic renewal is having issues with your payment method';
        paymentDesc = 'We are having issues charging your payment method. We will retry your payment method again soon and we may block your service if unsuccessful.';
        cardState = 'error';
        showSetPayment = true;
        if (this.props.accountBilling?.payment) {
          setPaymentTitle = 'Update payment method';
        } else {
          setPaymentTitle = 'Add payment method';
        }
        showCancelSubscription = true;
        planTitle = 'Your plan is active';
        planDesc = `You have full access to your ${this.props.accountBilling.plan.title} plan; however, there is an issue with your payment. Please resolve it before you can change your plan.`;
        break;
      case Admin.SubscriptionStatus.ActiveNoRenewal:
        paymentTitle = 'Automatic renewal is inactive';
        paymentDesc = 'Resume automatic renewal to continue using our service beyond the next billing cycle.';
        cardState = 'warn';
        showSetPayment = true;
        setPaymentTitle = 'Resume with new payment method';
        setPaymentAction = 'Add and resume subscription';
        showResumePlan = true;
        resumePlanDesc = 'Your subscription will no longer be cancelled. You will be automatically billed for our service at the next billing cycle.';
        if (this.props.accountBilling?.billingPeriodEnd) {
          planTitle = (
            <>
              Your plan is active until&nbsp;<TimeAgo date={this.props.accountBilling?.billingPeriodEnd} />
            </>
          );
        } else {
          planTitle = 'Your plan is active until the end of the billing cycle';
        }
        planDesc = `You have full access to your ${this.props.accountBilling.plan.title} plan until it cancels. Please resume your payments to continue using our service beyond next billing cycle.`;
        break;
      case Admin.SubscriptionStatus.Limited:
        paymentTitle = 'Automatic renewal is active';
        paymentDesc = 'You will be automatically billed at the next cycle and your plan will be renewed.';
        cardState = 'active';
        showSetPayment = true;
        setPaymentTitle = 'Update payment method';
        showCancelSubscription = true;
        planTitle = 'Your plan is limited';
        planDesc = `You have limited access to your ${this.props.accountBilling.plan.title} plan due to going over your plan limits. Please resolve all issues to continue using our service.`;
        if (hasAvailablePlansToSwitch) {
          planDesc += ' If you upgrade your plan, changes will reflect immediately. If you downgrade your plan, changes will take effect at the end of the term.';
          showPlanChange = true;
        }
        break;
      case Admin.SubscriptionStatus.NoPaymentMethod:
        paymentTitle = 'Automatic renewal is inactive';
        paymentDesc = 'Your trial has expired. To continue using our service, add a payment method to enable automatic renewal.';
        cardState = 'error';
        showSetPayment = true;
        setPaymentTitle = 'Add payment method';
        planTitle = 'Your trial plan has expired';
        planDesc = `To continue using your ${this.props.accountBilling.plan.title} plan, please add a payment method.`;
        break;
      case Admin.SubscriptionStatus.Blocked:
        paymentTitle = 'Payments are blocked';
        paymentDesc = 'Contact support to reinstate your account.';
        showContactSupport = true;
        cardState = 'error';
        planTitle = 'Your plan is inactive';
        planDesc = `You have limited access to your ${this.props.accountBilling.plan.title} plan due to a payment issue. Please resolve all issues to continue using our service.`;
        break;
      case Admin.SubscriptionStatus.Cancelled:
        paymentTitle = 'Automatic renewal is inactive';
        paymentDesc = 'Resume automatic renewal to continue using our service.';
        cardState = 'error';
        showSetPayment = true;
        setPaymentTitle = 'Update payment method';
        if (this.props.accountBilling?.payment) {
          showResumePlan = true;
          resumePlanDesc = 'Your subscription will no longer be cancelled. You will be automatically billed for our service starting now.';
        }
        planTitle = 'Your plan is cancelled';
        planDesc = `You have limited access to your ${this.props.accountBilling.plan.title} plan since you cancelled your subscription. Please resume payment to continue using our service.`;
        break;
    }
    if (this.props.accountBilling?.endOfTermChangeToPlan) {
      endOfTermChangeToPlanTitle = `Pending plan change to ${this.props.accountBilling.endOfTermChangeToPlan.title}`;
      endOfTermChangeToPlanDesc = `Your requested change of plans to ${this.props.accountBilling.endOfTermChangeToPlan.title} plan will take effect at the end of the term.`;
    }
    switch (cardState) {
      case 'active':
        cardStateIcon = (<ActiveIcon color='primary' />);
        break;
      case 'warn':
        cardStateIcon = (<WarnIcon style={{ color: this.props.theme.palette.warning.main }} />);
        break;
      case 'error':
        cardStateIcon = (<ErrorIcon color='error' />);
        break;
    }
    const creditCard = (
      <TourAnchor anchorId='settings-credit-card' placement='bottom'>
        <CreditCard
          className={this.props.classes.creditCard}
          brand={cardStateIcon}
          numberInput={cardNumber}
          expiryInput={cardExpiry}
          cvcInput={(<span className={this.props.classes.blurry}>642</span>)}
        />
      </TourAnchor>
    );

    const paymentStripeAction: PaymentStripeAction | undefined = this.props.accountBilling?.paymentActionRequired?.actionType === 'stripe-next-action'
      ? this.props.accountBilling?.paymentActionRequired as PaymentStripeAction : undefined;
    const paymentActionOnClose = () => {
      this.setState({
        paymentActionOpen: undefined,
        paymentActionUrl: undefined,
        paymentActionMessage: undefined,
        paymentActionMessageSeverity: undefined,
      });
      if (this.refreshBillingAfterPaymentClose) {
        ServerAdmin.get().dispatchAdmin().then(d => d.accountBillingAdmin({
          refreshPayments: true,
        }));
      }
    };
    const paymentAction = paymentStripeAction ? (
      <>
        <Message
          className={this.props.classes.paymentActionMessage}
          message='One of your payments requires additional information'
          severity='error'
          action={(
            <SubmitButton
              isSubmitting={!!this.state.paymentActionOpen && !this.state.paymentActionUrl && !this.state.paymentActionMessage}
              onClick={() => {
                this.setState({ paymentActionOpen: true });
                this.loadActionIframe(paymentStripeAction);
              }}
            >Open</SubmitButton>
          )}
        />
        <Dialog
          open={!!this.state.paymentActionOpen}
          onClose={paymentActionOnClose}
        >
          {this.state.paymentActionMessage ? (
            <>
              <DialogContent>
                <Message
                  message={this.state.paymentActionMessage}
                  severity={this.state.paymentActionMessageSeverity || 'info'}
                />
              </DialogContent>
              <DialogActions>
                <Button onClick={paymentActionOnClose}>Dismiss</Button>
              </DialogActions>
            </>
          ) : (this.state.paymentActionUrl ? (
            <iframe
              title='Complete outstanding payment action'
              width={this.getFrameActionWidth()}
              height={400}
              src={this.state.paymentActionUrl}
            />
          ) : (
            <div style={{
              minWidth: this.getFrameActionWidth(),
              minHeight: 400,
            }}>
              <LoadingPage />
            </div>
          ))}
        </Dialog>
      </>
    ) : undefined;

    const hasPayable = (this.props.accountBilling?.accountPayable || 0) > 0;
    const hasReceivable = (this.props.accountBilling?.accountReceivable || 0) > 0;
    const payment = (
      <Section
        title='Payment'
        preview={(
          <div className={this.props.classes.creditCardContainer}>
            {creditCard}
            <Box display='grid' gridTemplateAreas='"payTtl payAmt" "rcvTtl rcvAmt"' alignItems='center' gridGap='10px 10px'>
              {hasPayable && (
                <>
                  <Box gridArea='payTtl'><Typography component='div'>Credits:</Typography></Box>
                  <Box gridArea='payAmt' display='flex'>
                    <Typography component='div' variant='h6' color='textSecondary' style={{ alignSelf: 'flex-start' }}>{'$'}</Typography>
                    <Typography component='div' variant='h4' color={hasPayable ? 'primary' : undefined}>
                      {this.props.accountBilling?.accountPayable || 0}
                    </Typography>
                  </Box>
                </>
              )}
              {(hasReceivable || !hasPayable) && (
                <>
                  <Box gridArea='rcvTtl'><Typography component='div'>Overdue:</Typography></Box>
                  <Box gridArea='rcvAmt' display='flex'>
                    <Typography component='div' variant='h6' color='textSecondary' style={{ alignSelf: 'flex-start' }}>{'$'}</Typography>
                    <Typography component='div' variant='h4' color={hasReceivable ? 'error' : undefined}>
                      {this.props.accountBilling?.accountReceivable || 0}
                    </Typography>
                  </Box>
                </>
              )}
            </Box>
          </div>
        )}
        content={(
          <div className={this.props.classes.actionContainer}>
            <p><Typography variant='h6' color='textPrimary' component='div'>{paymentTitle}</Typography></p>
            <Typography color='textSecondary'>{paymentDesc}</Typography>
            <div className={this.props.classes.sectionButtons}>
              {showContactSupport && (
                <Button
                  disabled={this.state.isSubmitting || this.state.showAddPayment}
                  component={Link}
                  to='/contact/support'
                >Contact support</Button>
              )}
              {showSetPayment && (
                <TourAnchor anchorId='settings-add-payment-open' placement='bottom'>
                  {(next, isActive, anchorRef) => (
                    <SubmitButton
                      buttonRef={anchorRef}
                      isSubmitting={this.state.isSubmitting}
                      disabled={this.state.showAddPayment}
                      onClick={() => {
                        trackingBlock(() => {
                          ReactGA.event({
                            category: 'billing',
                            action: this.props.accountBilling?.payment ? 'click-payment-update-open' : 'click-payment-add-open',
                            label: this.props.accountBilling?.plan.basePlanId,
                          });
                        });
                        this.setState({ showAddPayment: true });
                        next();
                      }}
                    >
                      {setPaymentTitle}
                    </SubmitButton>
                  )}
                </TourAnchor>
              )}
              {showCancelSubscription && (
                <SubmitButton
                  isSubmitting={this.state.isSubmitting}
                  disabled={this.state.showCancelSubscription}
                  style={{ color: this.props.theme.palette.error.main }}
                  onClick={() => this.setState({ showCancelSubscription: true })}
                >
                  Cancel payments
                </SubmitButton>
              )}
              {showResumePlan && (
                <SubmitButton
                  isSubmitting={this.state.isSubmitting}
                  disabled={this.state.showResumePlan}
                  color='primary'
                  onClick={() => this.setState({ showResumePlan: true })}
                >
                  Resume payments
                </SubmitButton>
              )}
            </div>
            {paymentAction}
            <Dialog
              open={!!this.state.showAddPayment}
              onClose={() => this.setState({ showAddPayment: undefined })}
            >
              <ElementsConsumer>
                {({ elements, stripe }) => (
                  <TourAnchor anchorId='settings-add-payment-popup' placement='top'>
                    {(next, isActive, anchorRef) => (
                      <div ref={anchorRef}>
                        <DialogTitle>{setPaymentTitle || 'Add new payment method'}</DialogTitle>
                        <DialogContent className={this.props.classes.center}>
                          <StripeCreditCard onFilledChanged={(isFilled) => this.setState({ stripePaymentFilled: isFilled })} />
                          <Collapse in={!!this.state.stripePaymentError}>
                            <Message message={this.state.stripePaymentError} severity='error' />
                          </Collapse>
                        </DialogContent>
                        <DialogActions>
                          <Button onClick={() => this.setState({ showAddPayment: undefined })}>
                            Cancel
                          </Button>
                          <SubmitButton
                            isSubmitting={this.state.isSubmitting}
                            disabled={!this.state.stripePaymentFilled || !elements || !stripe}
                            color='primary'
                            onClick={async () => {
                              const success = await this.onPaymentSubmit(elements!, stripe!);
                              if (success) {
                                next();
                                tourSetGuideState('add-payment', TourDefinitionGuideState.Completed);
                              }
                            }}
                          >{setPaymentAction || 'Add'}</SubmitButton>
                        </DialogActions>
                      </div>
                    )}
                  </TourAnchor>
                )}
              </ElementsConsumer>
            </Dialog>
            <Dialog
              open={!!this.state.showCancelSubscription}
              onClose={() => this.setState({ showCancelSubscription: undefined })}
            >
              <DialogTitle>Stop subscription</DialogTitle>
              <DialogContent className={this.props.classes.center}>
                <DialogContentText>Stop automatic billing of your subscription. Any ongoing subscription will continue to work until it expires.</DialogContentText>
              </DialogContent>
              <DialogActions>
                <Button onClick={() => this.setState({ showCancelSubscription: undefined })}>
                  Cancel
                </Button>
                <SubmitButton
                  isSubmitting={this.state.isSubmitting}
                  style={{ color: this.props.theme.palette.error.main }}
                  onClick={() => {
                    this.setState({ isSubmitting: true });
                    ServerAdmin.get().dispatchAdmin().then(d => d.accountUpdateAdmin({
                      accountUpdateAdmin: {
                        cancelEndOfTerm: true,
                      },
                    }).then(() => d.accountBillingAdmin({})))
                      .then(() => this.setState({ isSubmitting: false, showCancelSubscription: undefined }))
                      .catch(er => this.setState({ isSubmitting: false }));
                  }}
                >Stop subscription</SubmitButton>
              </DialogActions>
            </Dialog>
            <Dialog
              open={!!this.state.showResumePlan}
              onClose={() => this.setState({ showResumePlan: undefined })}
            >
              <DialogTitle>Resume subscription</DialogTitle>
              <DialogContent className={this.props.classes.center}>
                <DialogContentText>{resumePlanDesc}</DialogContentText>
              </DialogContent>
              <DialogActions>
                <Button onClick={() => this.setState({ showResumePlan: undefined })}>
                  Cancel
                </Button>
                <SubmitButton
                  isSubmitting={this.state.isSubmitting}
                  color='primary'
                  onClick={() => {
                    this.setState({ isSubmitting: true });
                    ServerAdmin.get().dispatchAdmin().then(d => d.accountUpdateAdmin({
                      accountUpdateAdmin: {
                        resume: true,
                      },
                    }).then(() => d.accountBillingAdmin({})))
                      .then(() => this.setState({ isSubmitting: false, showResumePlan: undefined }))
                      .catch(er => this.setState({ isSubmitting: false }));
                  }}
                >Resume subscription</SubmitButton>
              </DialogActions>
            </Dialog>
          </div>
        )}
      />
    );

    const nextInvoicesCursor = this.state.invoices === undefined
      ? this.props.accountBilling?.invoices.cursor
      : this.state.invoicesCursor;
    const invoicesItems = [
      ...(this.props.accountBilling?.invoices.results || []),
      ...(this.state.invoices || []),
    ];
    const invoices = invoicesItems.length <= 0 ? undefined : (
      <Section
        title='Invoices'
        content={(
          <>
            <Table>
              <TableHead>
                <TableRow>
                  <TableCell key='due'>Due</TableCell>
                  <TableCell key='status'>Status</TableCell>
                  <TableCell key='amount'>Amount</TableCell>
                  <TableCell key='desc'>Description</TableCell>
                  <TableCell key='invoiceLink'>Invoice</TableCell>
                </TableRow>
              </TableHead>
              <TableBody>
                {invoicesItems.map((invoiceItem, index) => (
                  <TableRow key={index}>
                    <TableCell key='due'><Typography>{new Date(invoiceItem.date).toLocaleDateString()}</Typography></TableCell>
                    <TableCell key='status' align='center'><Typography>{invoiceItem.status}</Typography></TableCell>
                    <TableCell key='amount' align='right'><Typography>{invoiceItem.amount}</Typography></TableCell>
                    <TableCell key='desc'><Typography>{invoiceItem.description}</Typography></TableCell>
                    <TableCell key='invoiceLink'>
                      <Button onClick={() => this.onInvoiceClick(invoiceItem.invoiceId)}>View</Button>
                    </TableCell>
                  </TableRow>
                ))}
              </TableBody>
            </Table>
            {nextInvoicesCursor && (
              <Button
                style={{ margin: 'auto', display: 'block' }}
                onClick={() => ServerAdmin.get().dispatchAdmin()
                  .then(d => d.invoicesSearchAdmin({ cursor: nextInvoicesCursor }))
                  .then(results => this.setState({
                    invoices: [
                      ...(this.state.invoices || []),
                      ...results.results,
                    ],
                    invoicesCursor: results.cursor,
                  }))}
              >
                Show more
              </Button>
            )}
          </>
        )}
      />
    );

    const plan = (
      <Section
        title='Plan'
        preview={(
          <div className={this.props.classes.planContainer}>
            <TourAnchor anchorId='settings-billing-plan' placement='bottom' disablePortal>
              <PricingPlan
                selected
                className={this.props.classes.plan}
                plan={this.props.accountBilling.plan}
              />
            </TourAnchor>
            {(this.props.accountBilling?.trackedUsers !== undefined) && (
              <Box display='grid' gridTemplateAreas='"mauLbl mauAmt"' alignItems='baseline' gridGap='10px 10px'>
                <Box gridArea='mauLbl'><Typography component='div'>Tracked users:</Typography></Box>
                <Box gridArea='mauAmt' display='flex'>
                  <Typography component='div' variant='h5'>
                    {this.props.accountBilling.trackedUsers}
                  </Typography>
                </Box>
              </Box>
            )}
            {(this.props.accountBilling?.postCount !== undefined) && (
              <Box display='grid' gridTemplateAreas='"postCountLbl postCountAmt"' alignItems='baseline' gridGap='10px 10px'>
                <Box gridArea='postCountLbl'><Typography component='div'>Post count:</Typography></Box>
                <Box gridArea='postCountAmt' display='flex'>
                  <Typography component='div' variant='h5' color={
                    this.props.account.basePlanId === 'starter-unlimited'
                      && this.props.accountBilling.postCount > StarterMaxPosts
                      ? 'error' : undefined}>
                    {this.props.accountBilling.postCount}
                  </Typography>
                </Box>
              </Box>
            )}
          </div>
        )}
        content={(
          <div className={this.props.classes.actionContainer}>
            <p><Typography variant='h6' component='div' color='textPrimary'>{planTitle}</Typography></p>
            <Typography color='textSecondary'>{planDesc}</Typography>
            {(endOfTermChangeToPlanTitle || endOfTermChangeToPlanDesc) && (
              <>
                <p><Typography variant='h6' component='div' color='textPrimary' className={this.props.classes.sectionSpacing}>{endOfTermChangeToPlanTitle}</Typography></p>
                <Typography color='textSecondary'>{endOfTermChangeToPlanDesc}</Typography>
              </>
            )}
            {showPlanChange && (
              <div className={this.props.classes.sectionButtons}>
                <Button
                  disabled={this.state.isSubmitting || this.state.showPlanChange}
                  onClick={() => {
                    trackingBlock(() => {
                      ReactGA.event({
                        category: 'billing',
                        action: 'click-plan-switch-open',
                        label: this.props.accountBilling?.plan.basePlanId,
                      });
                    });

                    this.setState({ showPlanChange: true });
                  }}
                >
                  {switchPlanTitle || 'Switch plan'}
                </Button>
              </div>
            )}
            {showPlanChange && (
              <div className={this.props.classes.sectionButtons}>
                <Button
                  disabled={this.state.isSubmitting || this.state.showPlanChange}
                  onClick={() => this.props.history.push('/coupon')}
                >
                  Redeem coupon
                </Button>
              </div>
            )}
            {this.props.isSuperAdmin && (
              <>
                <Dialog
                  open={!!this.state.showFlatYearlyChange}
                  onClose={() => this.setState({ showFlatYearlyChange: undefined })}
                  scroll='body'
                  maxWidth='md'
                >
                  <DialogTitle>Switch to yearly plan</DialogTitle>
                  <DialogContent>
                    <TextField
                      variant='outlined'
                      type='number'
                      label='Yearly flat price'
                      value={this.state.flatYearlyPrice !== undefined ? this.state.flatYearlyPrice : ''}
                      onChange={e => this.setState({ flatYearlyPrice: parseInt(e.target.value) >= 0 ? parseInt(e.target.value) : undefined })}
                    />
                  </DialogContent>
                  <DialogActions>
                    <Button onClick={() => this.setState({ showFlatYearlyChange: undefined })}
                    >Cancel</Button>
                    <SubmitButton
                      isSubmitting={this.state.isSubmitting}
                      disabled={this.state.flatYearlyPrice === undefined}
                      color='primary'
                      onClick={() => {

                        this.setState({ isSubmitting: true });
                        ServerAdmin.get().dispatchAdmin().then(d => d.accountUpdateSuperAdmin({
                          accountUpdateSuperAdmin: {
                            changeToFlatPlanWithYearlyPrice: this.state.flatYearlyPrice || 0,
                          },
                        }).then(() => d.accountBillingAdmin({})))
                          .then(() => this.setState({ isSubmitting: false, showFlatYearlyChange: undefined }))
                          .catch(er => this.setState({ isSubmitting: false }));
                      }}
                    >Change</SubmitButton>
                  </DialogActions>
                </Dialog>
                <div className={this.props.classes.sectionButtons}>
                  <Button
                    disabled={this.state.isSubmitting}
                    onClick={() => this.setState({ showFlatYearlyChange: true })}
                  >Flatten</Button>
                </div>
              </>
            )}
            {this.props.isSuperAdmin && (
              <>
                <Dialog
                  open={!!this.state.showAddonsChange}
                  onClose={() => this.setState({ showAddonsChange: undefined })}
                  scroll='body'
                  maxWidth='md'
                >
                  <DialogTitle>Manage addons</DialogTitle>
                  <DialogContent className={this.props.classes.addonsContainer}>
                    <TextField
                      label='Extra projects'
                      variant='outlined'
                      type='number'
                      value={this.state.extraProjects !== undefined ? this.state.extraProjects : (this.props.account.addons?.[AddonExtraProject] || 0)}
                      onChange={e => this.setState({ extraProjects: parseInt(e.target.value) >= 0 ? parseInt(e.target.value) : undefined })}
                    />
                    <FormControlLabel
                      control={(
                        <Switch
                          checked={this.state.whitelabel !== undefined ? this.state.whitelabel : !!this.props.account.addons?.[AddonWhitelabel]}
                          onChange={(e, checked) => this.setState({ whitelabel: !!checked })}
                          color='default'
                        />
                      )}
                      label={(<FormHelperText>Whitelabel</FormHelperText>)}
                    />
                    <FormControlLabel
                      control={(
                        <Switch
                          checked={this.state.privateProjects !== undefined ? this.state.privateProjects : !!this.props.account.addons?.[AddonPrivateProjects]}
                          onChange={(e, checked) => this.setState({ privateProjects: !!checked })}
                          color='default'
                        />
                      )}
                      label={(<FormHelperText>Private projects</FormHelperText>)}
                    />
                  </DialogContent>
                  <DialogActions>
                    <Button onClick={() => this.setState({ showAddonsChange: undefined })}
                    >Cancel</Button>
                    <SubmitButton
                      isSubmitting={this.state.isSubmitting}
                      disabled={this.state.whitelabel === undefined
                        && this.state.privateProjects === undefined
                        && this.state.extraProjects === undefined}
                      color='primary'
                      onClick={() => {
                        if (this.state.whitelabel === undefined
                          && this.state.privateProjects === undefined
                          && this.state.extraProjects === undefined) return;

                        this.setState({ isSubmitting: true });
                        ServerAdmin.get().dispatchAdmin().then(d => d.accountUpdateSuperAdmin({
                          accountUpdateSuperAdmin: {
                            addons: {
                              ...(this.state.whitelabel === undefined ? {} : {
                                [AddonWhitelabel]: this.state.whitelabel ? 'true' : ''
                              }),
                              ...(this.state.privateProjects === undefined ? {} : {
                                [AddonPrivateProjects]: this.state.privateProjects ? 'true' : ''
                              }),
                              ...(this.state.extraProjects === undefined ? {} : {
                                [AddonExtraProject]: `${this.state.extraProjects}`
                              }),
                            },
                          },
                        }).then(() => d.accountBillingAdmin({})))
                          .then(() => this.setState({ isSubmitting: false, showAddonsChange: undefined }))
                          .catch(er => this.setState({ isSubmitting: false }));
                      }}
                    >Change</SubmitButton>
                  </DialogActions>
                </Dialog>
                <div className={this.props.classes.sectionButtons}>
                  <Button
                    disabled={this.state.isSubmitting}
                    onClick={() => this.setState({ showAddonsChange: true })}
                  >Addons</Button>
                </div>
              </>
            )}
            {this.props.isSuperAdmin && (
              <>
                <Dialog
                  open={!!this.state.showCreditAdjustment}
                  onClose={() => this.setState({ showCreditAdjustment: undefined })}
                  scroll='body'
                  maxWidth='md'
                >
                  <DialogTitle>Credit adjustment</DialogTitle>
                  <DialogContent className={this.props.classes.addonsContainer}>
                    <TextField
                      label='Amount'
                      variant='outlined'
                      type='number'
                      value={this.state.creditAmount || 0}
                      onChange={e => this.setState({ creditAmount: parseInt(e.target.value) })}
                    />
                    <TextField
                      label='Description'
                      variant='outlined'
                      value={this.state.creditDescription || ''}
                      onChange={e => this.setState({ creditDescription: e.target.value })}
                    />
                  </DialogContent>
                  <DialogActions>
                    <Button onClick={() => this.setState({ showCreditAdjustment: undefined })}
                    >Cancel</Button>
                    <SubmitButton
                      isSubmitting={this.state.isSubmitting}
                      disabled={!this.props.account
                        || !this.state.creditAmount
                        || !this.state.creditDescription}
                      color='primary'
                      onClick={() => {
                        if (!this.props.account
                          || !this.state.creditAmount
                          || !this.state.creditDescription) return;

                        this.setState({ isSubmitting: true });
                        ServerAdmin.get().dispatchAdmin().then(d => d.accountCreditAdjustmentSuperAdmin({
                          accountCreditAdjustment: {
                            accountId: this.props.account!.accountId,
                            amount: this.state.creditAmount!,
                            description: this.state.creditDescription!,
                          },
                        }).then(() => d.accountBillingAdmin({})))
                          .then(() => this.setState({ isSubmitting: false, showCreditAdjustment: undefined, creditAmount: undefined, creditDescription: undefined }))
                          .catch(er => this.setState({ isSubmitting: false }));
                      }}
                    >Change</SubmitButton>
                  </DialogActions>
                </Dialog>
                <div className={this.props.classes.sectionButtons}>
                  <Button
                    disabled={this.state.isSubmitting}
                    onClick={() => this.setState({ showCreditAdjustment: true })}
                  >Credit</Button>
                </div>
              </>
            )}
            <BillingChangePlanDialog
              open={!!this.state.showPlanChange}
              onClose={() => this.setState({ showPlanChange: undefined })}
              onSubmit={basePlanId => {
                trackingBlock(() => {
                  ReactGA.event({
                    category: 'billing',
                    action: 'click-plan-switch-submit',
                    label: basePlanId,
                  });
                });

                this.setState({ isSubmitting: true });
                ServerAdmin.get().dispatchAdmin().then(d => d.accountUpdateAdmin({
                  accountUpdateAdmin: {
                    basePlanId,
                  },
                }).then(() => d.accountBillingAdmin({})))
                  .then(() => this.setState({ isSubmitting: false, showPlanChange: undefined }))
                  .catch(er => this.setState({ isSubmitting: false }));
              }}
              isSubmitting={!!this.state.isSubmitting}
            />
          </div>
        )}
      />
    );

    return (
      <ProjectSettingsBase title='Billing'>
        {plan}
        {payment}
        {invoices}
      </ProjectSettingsBase>
    );
  }
Example #26
Source File: LocationDetails.tsx    From covid19testing-map with GNU General Public License v3.0 4 votes vote down vote up
LocationDetails = ({ location, expanded, details }: DetailsProps) => {
function renderLocationTestingDetails(locationToRender: any): any {
    if (locationToRender.location_specific_testing_criteria !== null) {
      if (
        locationToRender.location_specific_testing_criteria.substring(0, 4) !==
          'http' &&
        locationToRender.location_specific_testing_criteria.length > 3
      ) {
        return (
          <Grid key={1} item md={12} xs={12}>
            <div style={{ marginTop: '10px' }}>
              {location.location_specific_testing_criteria.split('\\n').map((i: any, key: number) => {
                return <Typography key={key} paragraph variant="body2" component="p">{i}</Typography>;
              })}
            </div>
          </Grid>
        );
      }
    }

    const trackLocationWebsiteClick = () => {
      trackUiClick('Location Website', locationToRender.location_id + '|' + locationToRender.location_latitude + '|' + locationToRender.location_longitude);
    };

    // If there are specific instructions involving testing, then display those, and move on.
    if (
      locationToRender.location_specific_testing_criteria !== null &&
      locationToRender.location_specific_testing_criteria.substring(0, 4) !==
        'http'
    ) {
      return (
      <Grid key={2} item md={5} xs={12}>
        <div style={{ marginTop: '10px' }}>
          {location.location_specific_testing_criteria.split('\\n').map((i: any, key: number) => {
            return <Typography key={key} paragraph variant="body1" component="p">{i}</Typography>;
          })}
        </div>
      </Grid>
      );
    }
    
    // Otherwise, figure out which URL to display
    let urlToRender = '';
    
    // If location does NOT require/apply testing criteria then we're done
    if (
      locationToRender.is_ordering_tests_only_for_those_who_meeting_criteria !== true &&
      (locationToRender.reference_publisher_of_criteria === null ||
      locationToRender.reference_publisher_of_criteria.length < 3)
      ) {
      return (
        <Grid key={3} item md={5} xs={12}>
          <Typography style={{ marginTop: '10px' }}>
            {'Published testing criteria that is specific to this location could not be found. '}
            {'This is common when CDC guidelines are in effect, but we recommend calling ahead to confirm.'}
          </Typography>
        </Grid>
      ); 
    }
    
    if (
      locationToRender.location_specific_testing_criteria !== null &&
      locationToRender.location_specific_testing_criteria.substring(0, 4) === 'http'
    ) {
      urlToRender = locationToRender.location_specific_testing_criteria;
    } else if (
      locationToRender.location_contact_url_covid_info !== null &&
      locationToRender.location_contact_url_covid_info.substring(0, 4) === 'http'
    ) {
      urlToRender = locationToRender.location_contact_url_covid_info;
    } else if (
      locationToRender.location_contact_url_main !== null &&
      locationToRender.location_contact_url_main.substring(0, 4) === 'http'
    ) {
      urlToRender = locationToRender.location_contact_url_main;
    } else {
      urlToRender =
        'https://www.cdc.gov/coronavirus/2019-ncov/symptoms-testing/index.html#';
    }
    return (
      <Grid key={4} item md={5} xs={12}>
        <Typography style={{ marginTop: '10px' }}>
          {'Testing at this location is only offered to individuals that '}
          <Link
            onClick={trackLocationWebsiteClick}
            href={urlToRender}
            target="_blank"
            rel="noopener"
          >
            meet specific criteria
          </Link>
          {
            '. All others will be turned away.'
          }
        </Typography>
      </Grid>
    );
  }

  return (
    <Collapse in={expanded} timeout="auto" unmountOnExit>
      <Divider />
      <CardContent>
        <Grid container spacing={2}>
          <Grid key={5} item md={12} xs={12}>
            <div style={{ paddingTop: '20px' }}>
              {location.additional_information_for_patients.split('\\n').map((i: any, key: number) => {
                return <Typography key={key} paragraph variant="body1" component="p">{i}</Typography>;
              })}
            </div>
          </Grid>
          
          {renderLocationTestingDetails(location)}
          
          <Divider orientation="horizontal" flexItem />
          <Grid item md={12} xs={12}>
            <Typography color="textPrimary" variant="caption" style={{ paddingTop: '20px',paddingBottom: '20px' }}>
              {'\nSource: '}
              <ReactGA.OutboundLink
                  eventLabel={'OutboundLink | Source | ' + location.location_address_locality + ' | ' + location.location_address_region + ' | ' + location.location_contact_url_main }
                  to={location.location_contact_url_main}
                  target="_blank"
                >
                  {location.location_contact_url_main}
              </ReactGA.OutboundLink>
            </Typography>
          </Grid>
        </Grid>
      </CardContent>
    </Collapse>
  );
}
Example #27
Source File: Settings.tsx    From back-home-safe with GNU General Public License v3.0 4 votes vote down vote up
Settings = () => {
  const { t } = useTranslation("main_screen");
  const { hasCameraSupport } = useCamera();
  const { autoRemoveRecordDay, setAutoRemoveRecordDay } = useTravelRecord();
  const { incognito, setIncognito, value } = useData();
  const [languageOpen, setLanguageOpen] = useState(false);
  const { language, setLanguage } = useI18n();

  const handleLanguageClick = () => {
    setLanguageOpen(!languageOpen);
  };

  const handleExportData = () => {
    const dataStr =
      "data:text/json;charset=utf-8," +
      encodeURIComponent(JSON.stringify(value));
    const downloadAnchorNode = document.createElement("a");
    downloadAnchorNode.setAttribute("href", dataStr);
    downloadAnchorNode.setAttribute("download", "export.json");
    document.body.appendChild(downloadAnchorNode); // required for firefox
    downloadAnchorNode.click();
    downloadAnchorNode.remove();
  };

  return (
    <PageWrapper>
      <Header name={t("setting.name")} />
      <ContentWrapper>
        <StyledList
          subheader={
            <ListSubheader>{t("setting.section.common")}</ListSubheader>
          }
        >
          {hasCameraSupport ? (
            <StyledLink to="/cameraSetting">
              <ListItem button>
                <ListItemText primary={t("setting.item.camera_setting")} />
              </ListItem>
            </StyledLink>
          ) : (
            <ListItem button disabled>
              <ListItemText primary={t("setting.item.camera_setting")} />
            </ListItem>
          )}
          <StyledLink to="/confirmPageSetting">
            <ListItem button>
              <ListItemText primary={t("setting.item.confirm_page_setting")} />
            </ListItem>
          </StyledLink>
          <ListItem>
            <ListItemText primary={t("setting.item.auto_delete_record")} />
            <ListItemSecondaryAction>
              <Select
                labelId="cameraId"
                id="demo-simple-select"
                value={autoRemoveRecordDay}
                onChange={(e) => {
                  setAutoRemoveRecordDay(e.target.value as number);
                }}
              >
                {range(1, 100).map((day) => (
                  <MenuItem value={day} key={day}>
                    {day}{" "}
                    {day === 1 ? t("setting.form.day") : t("setting.form.days")}
                  </MenuItem>
                ))}
              </Select>
            </ListItemSecondaryAction>
          </ListItem>
          <ListItem>
            <ListItemText
              primary={t("setting.item.incognito_mode.name")}
              secondary={t("setting.item.incognito_mode.explanation")}
            />
            <ListItemSecondaryAction>
              <Switch
                checked={incognito}
                onChange={(e) => {
                  setIncognito(e.target.checked);
                }}
                color="primary"
              />
            </ListItemSecondaryAction>
          </ListItem>
          <ListItem button onClick={handleLanguageClick}>
            <ListItemText primary={t("setting.item.language")} />
            {languageOpen ? <ExpandLess /> : <ExpandMore />}
          </ListItem>
          <Collapse in={languageOpen} timeout="auto" unmountOnExit>
            <ListItem>
              <RadioGroup
                aria-label="language"
                name="language"
                value={language}
                onChange={(event) => {
                  setLanguage(event.target.value as languageType);
                }}
              >
                <FormControlLabel
                  value={languageType["ZH-HK"]}
                  control={<Radio />}
                  label="繁體中文"
                />
                <FormControlLabel
                  value={languageType.EN}
                  control={<Radio />}
                  label="English"
                />
              </RadioGroup>
            </ListItem>
          </Collapse>
        </StyledList>
        <Divider />
        <StyledList
          subheader={<ListSubheader>{t("setting.section.lab")}</ListSubheader>}
        >
          <StyledLink to="/qrGenerator">
            <ListItem button>
              <ListItemText primary={t("setting.item.qr_generator")} />
            </ListItem>
          </StyledLink>
          <StyledLink to="/vaccinationQRReader">
            <ListItem button>
              <ListItemText primary={t("setting.item.vaccinationQRReader")} />
            </ListItem>
          </StyledLink>
          <ListItem onClick={handleExportData}>
            <ListItemText primary={t("setting.item.export_data")} />
          </ListItem>
          <ListItem button>
            <ListItemText
              primary={t("setting.item.reset")}
              onClick={clearAllData}
            />
          </ListItem>
        </StyledList>
        <Divider />
        <StyledList
          subheader={
            <ListSubheader>
              {t("setting.section.version")}: {__APP_VERSION__}
            </ListSubheader>
          }
        >
          <StyledExternalLink
            href="https://gitlab.com/codogo-b/back-home-safe"
            target="_blank"
          >
            <ListItem button>
              <ListItemText primary={t("setting.item.about_us")} />
            </ListItem>
          </StyledExternalLink>
          <StyledLink to="/disclaimer">
            <ListItem button>
              <ListItemText primary={t("setting.item.disclaimer")} />
            </ListItem>
          </StyledLink>
          <StyledExternalLink
            href="https://gitlab.com/codogo-b/back-home-safe/-/blob/master/CHANGELOG.md"
            target="_blank"
          >
            <ListItem button>
              <ListItemText primary={t("setting.item.change_log")} />
            </ListItem>
          </StyledExternalLink>
          <StyledExternalLink
            href="https://gitlab.com/codogo-b/back-home-safe/-/issues"
            target="_blank"
          >
            <ListItem button>
              <ListItemText primary={t("setting.item.report_issue")} />
            </ListItem>
          </StyledExternalLink>
        </StyledList>
      </ContentWrapper>
    </PageWrapper>
  );
}
Example #28
Source File: ProposalsList.tsx    From homebase-app with MIT License 4 votes vote down vote up
ProposalsList: React.FC<Props> = ({
  currentLevel,
  proposals,
  title,
  showFooter,
  rightItem,
}) => {
  const [open, setopen] = useState(true);

  return (
    <TableContainer item>
      <Grid container direction="column" wrap={"nowrap"}>
        <TableHeader item container justifyContent="space-between">
          <Grid item>
            <Typography variant="body2" style={{fontWeight: "500"}} color="textPrimary">
              {title}
            </Typography>
          </Grid>
          {proposals.length ? (
            <Grid item>
              <IconButton
                aria-label="expand row"
                size="small"
                onClick={() => setopen(!open)}
              >
                {open ? (
                  <KeyboardArrowUpIcon htmlColor="#FFF" />
                ) : (
                  <KeyboardArrowDownIcon htmlColor="#FFF" />
                )}
              </IconButton>
            </Grid>
          ) : null}
        </TableHeader>
        {proposals.length ? (
            <Grid
              item
              container
              wrap={"nowrap"}
              component={Collapse}
              in={open}
              timeout="auto"
              unmountOnExit
              direction="column"
            >
              {proposals.map((p, i) => (
                <Grid item key={`proposal-${i}`}>
                  <Link to={`proposal/${p.id}`}>
                    <ProposalItem
                      proposal={p}
                      status={p.getStatus(currentLevel).status}
                    >
                      {rightItem ? rightItem(p) : null}
                    </ProposalItem>
                  </Link>
                </Grid>
              ))}
            </Grid>
        ) : (
          <ProposalsFooter
            item
            container
            direction="column"
            justifyContent="center"
          >
            <Grid item>
              <Typography color="textPrimary" align="center">
                No items
              </Typography>
            </Grid>
          </ProposalsFooter>
        )}
        {showFooter && (
          <ProposalsFooter
            item
            container
            direction="column"
            justifyContent="center"
          >
            <Grid item>
              <Link to="proposals">
                <Typography color="secondary" variant="body2" align="center">
                  View All Proposals
                </Typography>
              </Link>
            </Grid>
          </ProposalsFooter>
        )}
      </Grid>
    </TableContainer>
  );
}
Example #29
Source File: index.tsx    From aqualink-app with MIT License 4 votes vote down vote up
RegisterDialog = ({
  open,
  handleRegisterOpen,
  handleSignInOpen,
  classes,
}: RegisterDialogProps) => {
  const dispatch = useDispatch();
  const user = useSelector(userInfoSelector);
  const loading = useSelector(userLoadingSelector);
  const error = useSelector(userErrorSelector);
  const [errorAlertOpen, setErrorAlertOpen] = useState<boolean>(false);
  const [readTerms, setReadTerms] = useState<boolean>(false);

  const { register, errors, handleSubmit } = useForm<RegisterFormFields>({
    reValidateMode: "onSubmit",
  });

  const onSubmit = (
    data: RegisterFormFields,
    event?: BaseSyntheticEvent<object, HTMLElement, HTMLElement>
  ) => {
    if (event) {
      event.preventDefault();
    }
    const registerInfo: UserRegisterParams = {
      fullName: `${data.firstName} ${data.lastName}`,
      organization: data.organization,
      email: data.emailAddress.toLowerCase(),
      password: data.password,
    };
    dispatch(createUser(registerInfo));
  };

  const clearUserError = () => dispatch(clearError());

  useEffect(() => {
    if (user) {
      handleRegisterOpen(false);
    }
    if (error) {
      setErrorAlertOpen(true);
    }
  }, [user, handleRegisterOpen, error]);

  return (
    <Dialog
      onEnter={() => {
        clearUserError();
        setReadTerms(false);
      }}
      scroll="body"
      open={open}
      maxWidth="xs"
    >
      <Card>
        <CardHeader
          className={classes.dialogHeader}
          title={
            <Grid container alignItems="center" justify="space-between">
              <Grid item>
                <Grid container>
                  <Typography variant="h4">Aqua</Typography>
                  <Typography
                    className={classes.dialogHeaderSecondPart}
                    variant="h4"
                  >
                    link
                  </Typography>
                </Grid>
              </Grid>
              <Grid item>
                <IconButton
                  className={classes.closeButton}
                  size="small"
                  onClick={() => {
                    handleRegisterOpen(false);
                  }}
                >
                  <CloseIcon />
                </IconButton>
              </Grid>
            </Grid>
          }
        />
        {loading && <LinearProgress />}
        {error && (
          <Collapse in={errorAlertOpen}>
            <Alert
              severity="error"
              action={
                <IconButton
                  aria-label="close"
                  color="inherit"
                  size="small"
                  onClick={() => {
                    setErrorAlertOpen(false);
                  }}
                >
                  <CloseIcon fontSize="inherit" />
                </IconButton>
              }
            >
              {error}
            </Alert>
          </Collapse>
        )}
        <CardContent>
          <Grid container justify="center" item xs={12}>
            <Grid className={classes.dialogContentTitle} container item xs={10}>
              <Grid item>
                <Typography variant="h5" color="textSecondary">
                  Create an account
                </Typography>
              </Grid>
            </Grid>
            <Grid container item xs={10}>
              <form className={classes.form} onSubmit={handleSubmit(onSubmit)}>
                <Grid className={classes.textFieldWrapper} item xs={12}>
                  <TextField
                    id="firstName"
                    name="firstName"
                    placeholder="First Name"
                    helperText={
                      errors.firstName ? errors.firstName.message : ""
                    }
                    label="First Name"
                    inputRef={register({
                      required: "This is a required field",
                    })}
                    error={!!errors.firstName}
                    inputProps={{ className: classes.textField }}
                    fullWidth
                    variant="outlined"
                  />
                </Grid>
                <Grid className={classes.textFieldWrapper} item xs={12}>
                  <TextField
                    id="lastName"
                    name="lastName"
                    placeholder="Last Name"
                    helperText={errors.lastName ? errors.lastName.message : ""}
                    label="Last Name"
                    inputRef={register({
                      required: "This is a required field",
                    })}
                    error={!!errors.lastName}
                    inputProps={{ className: classes.textField }}
                    fullWidth
                    variant="outlined"
                  />
                </Grid>
                <Grid className={classes.textFieldWrapper} item xs={12}>
                  <TextField
                    id="organization"
                    name="organization"
                    placeholder="Organization"
                    helperText={
                      errors.organization ? errors.organization.message : ""
                    }
                    label="Organization"
                    inputRef={register({
                      required: "This is a required field",
                    })}
                    error={!!errors.organization}
                    inputProps={{ className: classes.textField }}
                    fullWidth
                    variant="outlined"
                  />
                </Grid>
                <Grid className={classes.textFieldWrapper} item xs={12}>
                  <TextField
                    id="emailAddress"
                    name="emailAddress"
                    placeholder="Email Address"
                    helperText={
                      (errors.emailAddress &&
                        (errors.emailAddress.type === "validate"
                          ? "Invalid email address"
                          : errors.emailAddress.message)) ||
                      ""
                    }
                    label="Email Address"
                    inputRef={register({
                      required: "This is a required field",
                      validate: (value) => isEmail(value),
                    })}
                    error={!!errors.emailAddress}
                    inputProps={{ className: classes.textField }}
                    fullWidth
                    variant="outlined"
                  />
                </Grid>
                <Grid className={classes.textFieldWrapper} item xs={12}>
                  <TextField
                    id="password"
                    name="password"
                    type="password"
                    placeholder="Password"
                    helperText={errors.password ? errors.password.message : ""}
                    label="Password"
                    inputRef={register({
                      required: "This is a required field",
                      minLength: {
                        value: 8,
                        message: "Password must be at least 8 characters",
                      },
                    })}
                    error={!!errors.password}
                    inputProps={{ className: classes.textField }}
                    fullWidth
                    variant="outlined"
                  />
                </Grid>
                <Grid
                  container
                  justify="space-between"
                  alignItems="center"
                  item
                  xs={12}
                >
                  <Grid item xs={1}>
                    <Checkbox
                      className={classes.termsCheckbox}
                      checked={readTerms}
                      onChange={() => setReadTerms(!readTerms)}
                      color="primary"
                    />
                  </Grid>
                  <Grid item xs={10} sm={11}>
                    <Box>
                      <Typography
                        className={classes.formText}
                        variant="subtitle1"
                        color="textSecondary"
                      >
                        I have read the{" "}
                        <Link className={classes.termsLink} to="/terms">
                          Terms and Conditions
                        </Link>
                      </Typography>
                    </Box>
                  </Grid>
                </Grid>
                <Grid className={classes.button} item xs={12}>
                  <Button
                    fullWidth
                    type="submit"
                    color="primary"
                    variant="contained"
                    size="large"
                    disabled={!readTerms}
                  >
                    CREATE ACCOUNT
                  </Button>
                </Grid>
                <Grid container item xs={12}>
                  <Typography
                    className={classes.formText}
                    variant="subtitle1"
                    color="textSecondary"
                  >
                    Have an account?{" "}
                    <Button
                      onClick={() => {
                        handleRegisterOpen(false);
                        handleSignInOpen(true);
                      }}
                      color="primary"
                    >
                      SIGN IN
                    </Button>
                  </Typography>
                </Grid>
              </form>
            </Grid>
          </Grid>
        </CardContent>
      </Card>
    </Dialog>
  );
}