@material-ui/core#TextareaAutosize TypeScript Examples

The following examples show how to use @material-ui/core#TextareaAutosize. 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: DaoSettings.tsx    From homebase-app with MIT License 6 votes vote down vote up
CustomTextarea = styled(withTheme(TextareaAutosize))((props) => ({
  minHeight: 152,
  boxSizing: "border-box",
  width: "100%",
  border: `1px solid ${props.theme.palette.primary.light}`,
  marginTop: 14,
  fontWeight: 300,
  padding: "21px 20px",
  fontFamily: "system-ui",
  fontSize: 16,
  background: props.theme.palette.primary.main,
  color: props.theme.palette.text.secondary,
  paddingRight: 40,
  wordBreak: "break-word",
  "&:hover": {
    background: "rgba(129, 254, 183, 0.03)",
    borderLeft: `2px solid ${props.theme.palette.secondary.light}`,
  },
}))
Example #2
Source File: JSONChart.tsx    From neodash with Apache License 2.0 6 votes vote down vote up
NeoJSONChart = (props: ChartProps) => {
    const records = props.records;

    return <div style={{marginTop: "0px"}}>

        <TextareaAutosize
            style={{ width: "100%", border: "1px solid lightgray" }}
            className={"textinput-linenumbers"}
            value={JSON.stringify(records, null, 2)}
            aria-label=""
            placeholder="Query output should be rendered here." />
    </div >;
}
Example #3
Source File: CodeViewerComponent.tsx    From neodash with Apache License 2.0 6 votes vote down vote up
NeoCodeViewerComponent = ({ value = "", placeholder = "" }) => {
    return (
        <div style={{ overflowY: "auto", marginLeft: "10px", marginRight: "10px", height: "100%" }}>
            <TextareaAutosize
                style={{ width: "100%", overflowY: "hidden", scrollbarWidth: "auto", paddingLeft: "10px", background: "none",  overflow: "scroll !important", border: "1px solid lightgray" }}
                className={"textinput-linenumbers"}
                aria-label=""
                value={value}
                placeholder={placeholder} />
        </div>
    );
}
Example #4
Source File: TextAreaAutosize.tsx    From shadowsocks-electron with GNU General Public License v3.0 6 votes vote down vote up
StyledTextareaAutosize = React.memo((props: TextAreaProps) => {
  const useStyles = makeStyles((theme: Theme) => createStyles({
    textarea: {
      width: '100%',
      border: `solid 1px ${theme.palette.type === 'dark' ? grey[700] : 'lightgrey'}`,
      outline: 'none',
      backgroundColor: theme.palette.background.paper,
      color: theme.palette.type === 'dark' ? grey[500] : grey[900],
    }
  }));

  const onInnerChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
    if (props.onTextChange) {
      return props.onTextChange("acl", event);
    }
  }

  const TextArea = (props: TextAreaProps) => {
    const { onTextChange, ...rest } = props;
    const classes = useStyles();

    return <TextareaAutosize {...rest} className={classes.textarea} onChange={onInnerChange}/>
  };

  return <TextArea {...props}/>;
})
Example #5
Source File: CommentTextarea.tsx    From knboard with MIT License 6 votes vote down vote up
CommentTextarea = (props: TextareaAutosizeProps) => {
  const theme = useTheme();

  return (
    <TextareaAutosize
      aria-label="comment"
      placeholder="Add a comment..."
      rowsMin={4}
      css={css`
        line-height: 1.25rem;
        font-size: 0.875rem;
        color: ${N800};
        width: ${commentBoxWidth}px;
        border: 1px solid #bcbcbc;
        border-radius: 4px;
        padding: 12px 16px 0;
        resize: none;
        margin-bottom: 8px;
        ${theme.breakpoints.down("sm")} {
          width: ${commentBoxWidthMobile}px !important;
        }
        &:focus {
          outline: none;
          border: 1px solid #999;
        }
      `}
      {...props}
    />
  );
}
Example #6
Source File: UpgradeOldDashboardModal.tsx    From neodash with Apache License 2.0 5 votes vote down vote up
NeoUpgradeOldDashboardModal = ({ open, text, clearOldDashboard, loadDashboard }) => {
    return (
        <div>
            <Dialog maxWidth={"lg"} open={open == true} aria-labelledby="form-dialog-title">
                <DialogTitle id="form-dialog-title">
                    Old Dashboard Found
                </DialogTitle>
                <DialogContent>
                    We've found a dashboard built with an old version of NeoDash.
                    Would you like to attempt an upgrade, or start from scratch?
                    <br />
                    <b>Make sure you back up this dashboard first!</b><br />
                    <Button onClick={() => {
                        localStorage.removeItem("neodash-dashboard");
                        clearOldDashboard();
                    }}
                        style={{ marginTop: "20px", marginBottom: "20px", marginRight: "20px" }}
                        color="default"
                        variant="contained"
                        size="large"
                        endIcon={<DeleteIcon color={"white"} />}>
                        Delete Old Dashboard
                    </Button>
                    <Button onClick={() => {
                        localStorage.removeItem("neodash-dashboard");
                        loadDashboard(text);
                        clearOldDashboard();
                    }}
                        style={{ marginTop: "20px", marginRight: "6px", marginBottom: "20px", backgroundColor: "white" }}
                        color="default"
                        variant="contained"
                        size="large">
                        Upgrade
                    </Button>
                    <TextareaAutosize
                        style={{ minHeight: "200px", width: "100%", border: "1px solid lightgray" }}
                        className={"textinput-linenumbers"}
                        onChange={(e) => { }}
                        value={text ? text : ""}
                        aria-label=""
                        placeholder="" />
                </DialogContent>
            </Dialog>
        </div >
    );
}
Example #7
Source File: BoardName.tsx    From knboard with MIT License 5 votes vote down vote up
BoardName = ({ id, name, isOwner, ...props }: Props) => {
  const dispatch = useDispatch();
  const [pendingName, setPendingName] = useState<string>(name);
  const [editing, setEditing] = useState<boolean>(false);
  const nameTextAreaRef = useRef<HTMLTextAreaElement>(null);

  useEffect(() => {
    if (!editing && name === pendingName) {
      nameTextAreaRef?.current?.blur();
    }
  }, [pendingName, editing]);

  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.keyCode === Key.Enter) {
      e.preventDefault();
      if (pendingName.length > 0) {
        nameTextAreaRef?.current?.blur();
      }
    }
    if (e.keyCode === Key.Escape) {
      e.preventDefault();
      setPendingName(name);
      setEditing(false);
      // blur via useEffect
    }
  };

  const handleSave = () => {
    if (editing && pendingName.length > 0) {
      setEditing(false);
      if (pendingName !== name) {
        dispatch(patchBoard({ id, fields: { name: pendingName } }));
      }
    }
  };

  const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
    setPendingName(e.target.value);
  };

  const handleFocus = (e: React.FocusEvent<HTMLTextAreaElement>) => {
    e.target.select();
  };

  return (
    <Container {...props}>
      {editing ? (
        <div>
          <TextareaAutosize
            ref={nameTextAreaRef}
            value={pendingName}
            onChange={handleChange}
            onBlur={handleSave}
            onKeyDown={handleKeyDown}
            data-testid="board-name-textarea"
            onFocus={handleFocus}
            autoFocus
          />
        </div>
      ) : (
        <div
          css={css`
            ${isOwner && `&:hover {cursor: pointer;}`}
          `}
          onClick={() => setEditing(isOwner)}
        >
          {pendingName}
        </div>
      )}
    </Container>
  );
}
Example #8
Source File: index.tsx    From react-app-architecture with Apache License 2.0 4 votes vote down vote up
export default function WritingPad(): ReactElement {
  const classes = useStyles();
  const dispatch = useDispatch();

  const [preventBack, setPreventBack] = useState(false);
  const [showPreview, setShowPreview] = useState(false);

  const { hydrationBlog, data, isFetchingBlog, isSavingBlog, message } = useStateSelector(
    ({ writingPadState }) => writingPadState,
  );

  const [localState, setLocalState] = useState<LocalState>({
    isForSubmission: false,
    isAllDataSentToServer: false,
    isBlogDetailsFormToShow: false,
    title: '',
    description: '',
    imgUrl: '',
    blogUrl: '',
    tags: [],
    isWriting: false,
    isTitleError: false,
    isDescriptionError: false,
    isImgUrlError: false,
    isBlogUrlError: false,
    isTagsError: false,
  });

  useEffect(() => {
    if (hydrationBlog?._id && !isFetchingBlog) dispatch(fetchBlog(hydrationBlog._id));
    if (hydrationBlog)
      setLocalState({
        ...localState,
        title: hydrationBlog.title,
        description: hydrationBlog.description,
        imgUrl: hydrationBlog.imgUrl,
        blogUrl: hydrationBlog.blogUrl,
        tags: hydrationBlog.tags,
      });
    return () => {
      dispatch(clearPad.action());
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const handleDoneClick = () => {
    setLocalState({
      ...localState,
      isBlogDetailsFormToShow: true,
      isForSubmission: true,
    });
  };

  const handleSaveClick = () => {
    if (!data?._id) {
      setLocalState({ ...localState, isBlogDetailsFormToShow: true });
    } else {
      setLocalState({ ...localState, isAllDataSentToServer: true });
      data?._id &&
        dispatch(
          saveBlog(data._id, {
            text: data.draftText,
            title: localState.title,
            description: localState.description,
            tags: localState.tags,
            imgUrl: localState.imgUrl,
          }),
        );
    }
    setPreventBack(false);
  };

  const handleCreateClick = () => {
    data &&
      dispatch(
        createBlog({
          text: data.draftText,
          title: localState.title,
          blogUrl: localState.blogUrl,
          description: localState.description,
          tags: localState.tags,
          imgUrl: localState.imgUrl,
        }),
      );
  };

  const handleSubmitClick = () => {
    setLocalState({ ...localState, isBlogDetailsFormToShow: false });
    data?._id && dispatch(submitBlog(data._id));
    dispatch(clearEditorPage.action());
  };

  const handleWithdrawClick = () => {
    setLocalState({ ...localState, isBlogDetailsFormToShow: false });
    data?._id && dispatch(withdrawBlog(data._id));
    dispatch(clearEditorPage.action());
  };

  const renderMenu = () => {
    if (!data) return null;
    return (
      <SpeedDial
        direction="down"
        ariaLabel="Blog Editor Menu"
        className={classes.speedDial}
        hidden={true}
        icon={<ArrowDropDownIcon />}
        open={true}
      >
        <SpeedDialAction
          FabProps={{
            disabled: !(data && data._id && data.isDraft),
          }}
          key="Done"
          icon={<DoneAllIcon />}
          tooltipTitle="Done"
          onClick={handleDoneClick}
        />
        <SpeedDialAction
          key="Unsubmit"
          FabProps={{
            disabled: !(data && data._id && data.isSubmitted),
          }}
          icon={<CloseIcon />}
          tooltipTitle="Remove Submission"
          onClick={handleWithdrawClick}
        />
        <SpeedDialAction
          key="Submit"
          FabProps={{
            disabled: !(data && data._id && !data.isSubmitted),
          }}
          icon={<SendIcon />}
          tooltipTitle="Submit Blog"
          onClick={handleSubmitClick}
        />
        <SpeedDialAction
          FabProps={{
            disabled: !(data?.draftText?.trim().length > 0),
          }}
          key="Blog Preview"
          icon={<VisibilityIcon />}
          tooltipTitle="Blog Preview"
          onClick={() => setShowPreview(true)}
        />
        <SpeedDialAction
          FabProps={{
            disabled: !(data?.draftText?.trim().length > 0),
          }}
          key="Save Blog"
          icon={<SaveIcon />}
          tooltipTitle="Save Blog"
          onClick={handleSaveClick}
        />
      </SpeedDial>
    );
  };

  return (
    <div className={classes.root}>
      <Prompt
        when={preventBack}
        message={() => 'Are you sure you want to go without saving your work.'}
      />
      {(isFetchingBlog || isSavingBlog) && <LinearProgress className={classes.progress} />}
      <Grid className={classes.content} container justify="center">
        <Grid item xs={12} sm={12} md={7}>
          <TextareaAutosize
            className={classes.pad}
            aria-label="blog writing pad"
            rowsMin={15}
            value={data?.draftText}
            onChange={(e) => {
              dispatch(editBlog.action({ draftText: e.target.value }));
              if (!preventBack) setPreventBack(true);
            }}
            placeholder="Write something awesome today.."
          />
        </Grid>
      </Grid>
      {data && <Preview blog={data} open={showPreview} onClose={() => setShowPreview(false)} />}
      <BlogDetailsForm
        blog={data}
        localState={localState}
        setLocalState={setLocalState}
        onSubmit={handleSubmitClick}
        onCreate={handleCreateClick}
        onSave={handleSaveClick}
      />
      {renderMenu()}
      {message && (
        <Snackbar
          message={message.text}
          variant={message.type}
          onClose={() => dispatch(removeMessage.action())}
        />
      )}
    </div>
  );
}
Example #9
Source File: Dashboard.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 4 votes vote down vote up
DashboardUI: React.FC<ContextType & { location: any }> = (
  props
) => {
  const {
    current,
    setCurrent,
    resultsPerPage,
    setResultsPerPage,
    filters,
    addFilter,
    removeFilter,
    results,
    facets,
    clearFilters,
    sortDirection,
    sortField,
    setSort,
    totalPages,
    totalResults,
    setSearchTerm,
    searchTerm,
    noResults
  } = props;
  const classes = useStyles();
  const [selectedDomain, setSelectedDomain] = useState('');
  const [resultsScrolled, setResultsScrolled] = useState(false);
  const {
    apiPost,
    apiPut,
    setLoading,
    showAllOrganizations,
    currentOrganization
  } = useAuthContext();

  const search:
    | (SavedSearch & {
        editing?: boolean;
      })
    | undefined = localStorage.getItem('savedSearch')
    ? JSON.parse(localStorage.getItem('savedSearch')!)
    : undefined;

  const [showSaveSearch, setShowSaveSearch] = useState<Boolean>(
    search && search.editing ? true : false
  );

  const [savedSearchValues, setSavedSearchValues] = useState<
    Partial<SavedSearch> & {
      name: string;
      vulnerabilityTemplate: Partial<Vulnerability>;
    }
  >(
    search
      ? search
      : {
          name: '',
          vulnerabilityTemplate: {},
          createVulnerabilities: false
        }
  );

  const onTextChange: React.ChangeEventHandler<
    HTMLInputElement | HTMLSelectElement
  > = (e) => onChange(e.target.name, e.target.value);

  const onChange = (name: string, value: any) => {
    setSavedSearchValues((values) => ({
      ...values,
      [name]: value
    }));
  };

  const onVulnerabilityTemplateChange = (e: any) => {
    (savedSearchValues.vulnerabilityTemplate as any)[e.target.name] =
      e.target.value;
    setSavedSearchValues(savedSearchValues);
  };

  const handleResultScroll = (e: React.UIEvent<HTMLElement>) => {
    if (e.currentTarget.scrollTop > 0) {
      setResultsScrolled(true);
    } else {
      setResultsScrolled(false);
    }
  };

  useEffect(() => {
    if (props.location.search === '') {
      // Search on initial load
      setSearchTerm('');
    }
    return () => {
      localStorage.removeItem('savedSearch');
    };
  }, [setSearchTerm, props.location.search]);

  useBeforeunload((event) => {
    localStorage.removeItem('savedSearch');
  });

  const fetchDomainsExport = async (): Promise<string> => {
    try {
      const body: any = {
        current,
        filters,
        resultsPerPage,
        searchTerm,
        sortDirection,
        sortField
      };
      if (!showAllOrganizations && currentOrganization) {
        if ('rootDomains' in currentOrganization)
          body.organizationId = currentOrganization.id;
        else body.tagId = currentOrganization.id;
      }
      const { url } = await apiPost('/search/export', {
        body
      });
      return url!;
    } catch (e) {
      console.error(e);
      return '';
    }
  };

  return (
    <div className={classes.root}>
      <FilterDrawer
        addFilter={addFilter}
        removeFilter={removeFilter}
        filters={filters}
        facets={facets}
        clearFilters={filters.length > 0 ? () => clearFilters([]) : undefined}
      />
      <div className={classes.contentWrapper}>
        <Subnav
          items={[
            { title: 'Search Results', path: '/inventory', exact: true },
            { title: 'All Domains', path: '/inventory/domains' },
            { title: 'All Vulnerabilities', path: '/inventory/vulnerabilities' }
          ]}
          styles={{
            paddingLeft: '0%'
          }}
        >
          <FilterTags filters={filters} removeFilter={removeFilter} />
        </Subnav>
        <SortBar
          sortField={sortField}
          sortDirection={sortDirection}
          setSort={setSort}
          isFixed={resultsScrolled}
          saveSearch={
            filters.length > 0 || searchTerm
              ? () => setShowSaveSearch(true)
              : undefined
          }
          existingSavedSearch={search}
        />
        {noResults && (
          <NoResults
            message={"We don't see any results that match your criteria."}
          ></NoResults>
        )}
        <div className={classes.content}>
          <div className={classes.panel} onScroll={handleResultScroll}>
            {results.map((result) => (
              <ResultCard
                key={result.id.raw}
                {...result}
                onDomainSelected={(id) => setSelectedDomain(id)}
                selected={result.id.raw === selectedDomain}
              />
            ))}
          </div>
          <div className={classes.panel}>
            {selectedDomain && <DomainDetails domainId={selectedDomain} />}
          </div>
        </div>
        <Paper classes={{ root: classes.pagination }}>
          <span>
            <strong>
              {(totalResults === 0
                ? 0
                : (current - 1) * resultsPerPage + 1
              ).toLocaleString()}{' '}
              -{' '}
              {Math.min(
                (current - 1) * resultsPerPage + resultsPerPage,
                totalResults
              ).toLocaleString()}
            </strong>{' '}
            of <strong>{totalResults.toLocaleString()}</strong>
          </span>
          <Pagination
            count={totalPages}
            page={current}
            onChange={(_, page) => setCurrent(page)}
            color="primary"
            size="small"
          />
          <FormControl
            variant="outlined"
            className={classes.pageSize}
            size="small"
          >
            <Typography id="results-per-page-label">
              Results per page:
            </Typography>
            <Select
              id="teststa"
              labelId="results-per-page-label"
              value={resultsPerPage}
              onChange={(e) => setResultsPerPage(e.target.value as number)}
            >
              {[15, 45, 90].map((perPage) => (
                <MenuItem key={perPage} value={perPage}>
                  {perPage}
                </MenuItem>
              ))}
            </Select>
          </FormControl>
          <USWDSButton
            className={classes.exportButton}
            outline
            type="button"
            onClick={() =>
              exportCSV(
                {
                  name: 'domains',
                  getDataToExport: fetchDomainsExport
                },
                setLoading
              )
            }
          >
            Export Results
          </USWDSButton>
        </Paper>
      </div>

      {showSaveSearch && (
        <div>
          <Overlay />
          <ModalContainer>
            <Modal
              className={classes.saveSearchModal}
              actions={
                <>
                  <USWDSButton
                    outline
                    type="button"
                    onClick={() => {
                      setShowSaveSearch(false);
                    }}
                  >
                    Cancel
                  </USWDSButton>
                  <USWDSButton
                    type="button"
                    onClick={async () => {
                      const body = {
                        body: {
                          ...savedSearchValues,
                          searchTerm,
                          filters,
                          count: totalResults,
                          searchPath: window.location.search,
                          sortField,
                          sortDirection
                        }
                      };
                      if (search) {
                        await apiPut('/saved-searches/' + search.id, body);
                      } else {
                        await apiPost('/saved-searches/', body);
                      }
                      setShowSaveSearch(false);
                    }}
                  >
                    Save
                  </USWDSButton>
                </>
              }
              title={search ? <h2>Update Search</h2> : <h2>Save Search</h2>}
            >
              <FormGroup>
                <Label htmlFor="name">Name Your Search</Label>
                <TextInput
                  required
                  id="name"
                  name="name"
                  type="text"
                  value={savedSearchValues.name}
                  onChange={onTextChange}
                />
                <p>When a new result is found:</p>
                {/* <FormControlLabel
                  control={
                    <Checkbox
                      // checked={gilad}
                      // onChange={handleChange}
                      name="email"
                    />
                  }
                  label="Email me"
                /> */}
                <FormControlLabel
                  control={
                    <Checkbox
                      checked={savedSearchValues.createVulnerabilities}
                      onChange={(e) =>
                        onChange(e.target.name, e.target.checked)
                      }
                      id="createVulnerabilities"
                      name="createVulnerabilities"
                    />
                  }
                  label="Create a vulnerability"
                />
                {savedSearchValues.createVulnerabilities && (
                  <>
                    <Label htmlFor="title">Title</Label>
                    <TextInput
                      required
                      id="title"
                      name="title"
                      type="text"
                      value={savedSearchValues.vulnerabilityTemplate.title}
                      onChange={onVulnerabilityTemplateChange}
                    />
                    <Label htmlFor="description">Description</Label>
                    <TextareaAutosize
                      required
                      id="description"
                      name="description"
                      style={{ padding: 10 }}
                      rowsMin={2}
                      value={
                        savedSearchValues.vulnerabilityTemplate.description
                      }
                      onChange={onVulnerabilityTemplateChange}
                    />
                    <Label htmlFor="description">Severity</Label>
                    <Dropdown
                      id="severity"
                      name="severity"
                      onChange={onVulnerabilityTemplateChange}
                      value={
                        savedSearchValues.vulnerabilityTemplate
                          .severity as string
                      }
                      style={{ display: 'inline-block', width: '150px' }}
                    >
                      <option value="None">None</option>
                      <option value="Low">Low</option>
                      <option value="Medium">Medium</option>
                      <option value="High">High</option>
                      <option value="Critical">Critical</option>
                    </Dropdown>
                  </>
                )}
                {/* <h3>Collaborators</h3>
                <p>
                  Collaborators can view vulnerabilities, and domains within
                  this search. Adding a team will make all members
                  collaborators.
                </p>
                <button className={classes.addButton} >
                  <AddCircleOutline></AddCircleOutline> ADD
                </button> */}
              </FormGroup>
            </Modal>
          </ModalContainer>
        </div>
      )}
    </div>
  );
}
Example #10
Source File: Vulnerability.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 4 votes vote down vote up
Vulnerability: React.FC = () => {
  const { vulnerabilityId } = useParams();
  const { apiGet, apiPut } = useAuthContext();
  const [vulnerability, setVulnerability] = useState<VulnerabilityType>();
  const [comment, setComment] = useState<string>('');
  const [showCommentForm, setShowCommentForm] = useState<boolean>(false);
  const [menuAnchor, setMenuAnchor] = React.useState<null | HTMLElement>(null);
  const classes = useStyles();
  const history = useHistory();

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

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

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

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

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

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

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

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

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

                    <TimelineItem>
                      <TimelineSeparator>
                        <TimelineDot />
                      </TimelineSeparator>
                      <TimelineContent>
                        Vulnerability opened<br></br>
                        <span
                          style={{
                            color: '#A9AEB1'
                          }}
                        >
                          {formatDate(vulnerability.createdAt)}
                        </span>
                      </TimelineContent>
                    </TimelineItem>
                  </Timeline>
                </div>
              </Paper>
              <Paper className={classes.cardRoot}>
                <div className={classes.inner}>
                  <div className={classes.section}>
                    <h2 className={classes.subtitle}>Provenance</h2>
                    <p>
                      <strong>Root Domain: </strong>
                      {vulnerability.domain.fromRootDomain}
                    </p>
                    <p>
                      <strong>Subdomain: </strong>
                      {vulnerability.domain.name} (
                      {vulnerability.domain.subdomainSource})
                    </p>
                    {vulnerability.service && (
                      <p>
                        <strong>Service/Port: </strong>
                        {vulnerability.service.service
                          ? vulnerability.service.service
                          : vulnerability.service.port}{' '}
                        ({vulnerability.service.serviceSource})
                      </p>
                    )}
                    {vulnerability.cpe && (
                      <>
                        <p>
                          <strong>Product: </strong>
                          {vulnerability.cpe}
                        </p>
                      </>
                    )}
                    <p>
                      <strong>Vulnerability: </strong>
                      {vulnerability.title} ({vulnerability.source})
                    </p>
                  </div>
                </div>
              </Paper>
              {vulnerability.source === 'hibp' && (
                <Paper className={classes.cardRoot}>
                  <div className={classes.inner}>
                    <div className={classes.section}>
                      <h2 className={classes.subtitle}>Breaches</h2>
                      <Table aria-label="simple table">
                        <TableHead>
                          <TableRow>
                            <TableCell>Breach Name</TableCell>
                            <TableCell align="right">Date Added</TableCell>
                          </TableRow>
                        </TableHead>
                        <TableBody>
                          {Object.keys(vulnerability.structuredData['breaches'])
                            .sort(
                              (a, b) =>
                                parseISO(
                                  vulnerability.structuredData['breaches'][b][
                                    'AddedDate'
                                  ]
                                ).getTime() -
                                parseISO(
                                  vulnerability.structuredData['breaches'][a][
                                    'AddedDate'
                                  ]
                                ).getTime()
                            )
                            .map((keyName, keyIndex) => (
                              <TableRow key={keyName}>
                                <TableCell component="th" scope="row">
                                  {keyName}
                                </TableCell>
                                <TableCell align="right">
                                  {formatDistanceToNow(
                                    parseISO(
                                      vulnerability.structuredData['breaches'][
                                        keyName
                                      ]['AddedDate']
                                    )
                                  ) + ' ago'}
                                </TableCell>
                              </TableRow>
                            ))}
                        </TableBody>
                      </Table>
                    </div>
                  </div>
                </Paper>
              )}
            </div>
          </div>
        </div>
      </div>
    </>
  );
}
Example #11
Source File: ImportFeedDialog.tsx    From bee-dashboard with BSD 3-Clause "New" or "Revised" License 4 votes vote down vote up
export function ImportFeedDialog({ onClose }: Props): ReactElement {
  const [textareaValue, setTextareaValue] = useState('')
  const [name, setName] = useState('')
  const fileInputRef = useRef(null)

  const { identities, setIdentities } = useContext(Context)

  const { enqueueSnackbar } = useSnackbar()

  const classes = useStyles()

  async function onImport() {
    const feed = await importIdentity(name, textareaValue)

    if (feed) {
      onFeedReady(feed)
    } else {
      enqueueSnackbar('Feed is not valid', { variant: 'error' })
    }
  }

  function onUploadIdentityFile() {
    if (fileInputRef.current) {
      const input = fileInputRef.current as HTMLInputElement
      input.click()
    }
  }

  function onIdentityFileSelected(event: React.ChangeEvent<HTMLInputElement>) {
    const fileReader = new FileReader()
    const file = event.target?.files?.[0]
    fileReader.onload = async event => {
      const string = event.target?.result

      if (string) {
        const feed = await importIdentity(name, string as string)

        if (feed) {
          onFeedReady(feed)
        } else {
          enqueueSnackbar('Feed is not valid', { variant: 'error' })
        }
      }
    }

    if (file) {
      fileReader.readAsText(file)
    }
  }

  function onFeedReady(identity: Identity) {
    persistIdentity(identities, identity)
    setIdentities(identities)
    enqueueSnackbar('Feed imported successfully', { variant: 'success' })
    onClose()
  }

  return (
    <SwarmDialog>
      <input onChange={onIdentityFileSelected} ref={fileInputRef} className={classes.displayNone} type="file" />
      <Box mb={4}>
        <TitleWithClose onClose={onClose}>Import</TitleWithClose>
      </Box>
      <Box mb={2}>
        <SwarmTextInput label="Identity Name" name="name" onChange={event => setName(event.target.value)} />
      </Box>
      <Box mb={4}>
        <TextareaAutosize
          className={classes.textarea}
          minRows={5}
          onChange={event => setTextareaValue(event.target.value)}
        />
      </Box>
      <ExpandableListItemActions>
        <SwarmButton iconType={Upload} onClick={onUploadIdentityFile}>
          Upload Json File
        </SwarmButton>
        <SwarmButton iconType={Check} onClick={onImport}>
          Use Pasted Text
        </SwarmButton>
      </ExpandableListItemActions>
    </SwarmDialog>
  )
}
Example #12
Source File: LoadModal.tsx    From neodash with Apache License 2.0 4 votes vote down vote up
NeoLoadModal = ({ loadDashboard, loadDatabaseListFromNeo4j, loadDashboardFromNeo4j, loadDashboardListFromNeo4j }) => {
    const [loadModalOpen, setLoadModalOpen] = React.useState(false);
    const [loadFromNeo4jModalOpen, setLoadFromNeo4jModalOpen] = React.useState(false);
    const [text, setText] = React.useState("");
    const [rows, setRows] = React.useState([]);
    const { driver } = useContext<Neo4jContextState>(Neo4jContext);
    const [dashboardDatabase, setDashboardDatabase] = React.useState("neo4j");
    const [databases, setDatabases] = React.useState(["neo4j"]);

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

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


    const handleCloseAndLoad = () => {
        setLoadModalOpen(false);
        loadDashboard(text);
        setText("");
    };

    function handleDashboardLoadedFromNeo4j(result) {
        setText(result);
        setLoadFromNeo4jModalOpen(false);
    }

    const reader = new FileReader();
    reader.onload = async (e) => {
        setText(e.target.result);
    };

    const uploadDashboard = async (e) => {
        e.preventDefault();
        reader.readAsText(e.target.files[0]);
    }

    const columns = [
        { field: 'id', hide: true, headerName: 'ID', width: 150 },
        { field: 'date', headerName: 'Date', width: 200 },
        { field: 'title', headerName: 'Title', width: 270 },
        { field: 'author', headerName: 'Author', width: 160 },
        { field: 'version', headerName: 'Version', width: 95 },
        {
            field: 'load', headerName: 'Select', renderCell: (c) => {
                return <Button onClick={(e) => { loadDashboardFromNeo4j(driver, dashboardDatabase, c.id, handleDashboardLoadedFromNeo4j) }} style={{ float: "right", backgroundColor: "white" }} variant="contained" size="medium" endIcon={<PlayArrow />}>Select</Button>
            }, width: 120
        },
    ]


    return (
        <div>
            <ListItem button onClick={handleClickOpen}>
                <ListItemIcon>
                    <IconButton style={{ padding: "0px" }} >
                        <SystemUpdateAltIcon />
                    </IconButton>
                </ListItemIcon>
                <ListItemText primary="Load" />
            </ListItem>

            <Dialog maxWidth={"lg"} open={loadModalOpen == true} onClose={handleClose} aria-labelledby="form-dialog-title">
                <DialogTitle id="form-dialog-title">
                    <SystemUpdateAltIcon style={{
                        height: "30px",
                        paddingTop: "4px",
                        marginBottom: "-8px",
                        marginRight: "5px",
                        paddingBottom: "5px"
                    }} />   Load Dashboard
                    <IconButton onClick={handleClose} style={{ padding: "3px", float: "right" }}>
                        <Badge badgeContent={""} >
                            <CloseIcon />
                        </Badge>
                    </IconButton>

                </DialogTitle>
                <DialogContent style={{ width: "1000px" }}>
                    {/* <DialogContentText> Paste your dashboard file here to load it into NeoDash.</DialogContentText> */}
                    <div>
                        <Button
                            component="label"
                            onClick={(e) => {
                                loadDashboardListFromNeo4j(driver, dashboardDatabase, (result) => { setRows(result) });
                                setLoadFromNeo4jModalOpen(true);
                                loadDatabaseListFromNeo4j(driver, (result) => { setDatabases(result) });
                            }}
                            style={{ marginBottom: "10px", backgroundColor: "white" }}
                            color="default"
                            variant="contained"
                            size="medium"
                            endIcon={<StorageIcon />}>

                            Select From Neo4j
                        </Button>
                        <Button
                            component="label"
                            // onClick={(e)=>uploadDashboard(e)}
                            style={{ marginLeft: "10px", backgroundColor: "white", marginBottom: "10px" }}
                            color="default"
                            variant="contained"
                            size="medium"
                            endIcon={<PostAddIcon />}>
                            <input
                                type="file"
                                onChange={(e) => uploadDashboard(e)}
                                hidden
                            />
                            Select From File
                        </Button>

                        <Button onClick={(text.length > 0) ? handleCloseAndLoad : null}
                            style={{ color: text.length > 0 ? "white" : "lightgrey", float: "right", marginLeft: "10px", marginBottom: "10px", backgroundColor: text.length > 0 ? "green" : "white" }}
                            color="default"
                            variant="contained"
                            size="medium"
                            endIcon={<PlayArrow />}>
                            Load Dashboard
                        </Button>
                    </div>


                    <TextareaAutosize
                        style={{ minHeight: "500px", width: "100%", border: "1px solid lightgray" }}
                        className={"textinput-linenumbers"}
                        onChange={(e) => setText(e.target.value)}
                        value={text}
                        aria-label=""
                        placeholder="Select a dashboard first, then preview it here..." />

                </DialogContent>
                {/* <DialogActions> */}
                {/* </DialogActions> */}
            </Dialog>
            <Dialog maxWidth={"lg"} open={loadFromNeo4jModalOpen == true} onClose={(e) => { setLoadFromNeo4jModalOpen(false) }} aria-labelledby="form-dialog-title">
                <DialogTitle id="form-dialog-title">
                    Select From Neo4j
                    <IconButton onClick={(e) => { setLoadFromNeo4jModalOpen(false) }} style={{ padding: "3px", float: "right" }}>
                        <Badge badgeContent={""} >
                            <CloseIcon />
                        </Badge>
                    </IconButton>
                </DialogTitle>
                <DialogContent style={{ width: "900px" }}>
                    <DialogContentText>If dashboards are saved in your current database, choose a dashboard below.
                    </DialogContentText>

                    <div style={{ height: "380px", borderBottom: "1px solid lightgrey" }}>
                        <DataGrid
                            rows={rows}
                            columns={columns}

                            pageSize={5}
                            rowsPerPageOptions={[5]}
                            disableSelectionOnClick
                            components={{
                                ColumnSortedDescendingIcon: () => <></>,
                                ColumnSortedAscendingIcon: () => <></>,
                            }}
                        /></div>
                    <FormControl style={{ marginTop: "-58px", marginLeft: "10px" }}>
                        <InputLabel id="demo-simple-select-label">Database</InputLabel>
                        <Select
                            labelId="demo-simple-select-label"
                            id="demo-simple-select"
                            style={{ width: "150px" }}
                            value={dashboardDatabase}
                            onChange={(e) => {
                                setRows([]);
                                setDashboardDatabase(e.target.value);
                                loadDashboardListFromNeo4j(driver, e.target.value, (result) => {  setRows(result); });
                            }}
                        >
                            {databases.map(database => {
                                return <MenuItem value={database}>{database}</MenuItem>
                            })}
                        </Select>
                    </FormControl>
                </DialogContent>
            </Dialog>
        </div>
    );
}
Example #13
Source File: SaveModal.tsx    From neodash with Apache License 2.0 4 votes vote down vote up
NeoSaveModal = ({ dashboard, connection, saveDashboardToNeo4j, loadDatabaseListFromNeo4j }) => {
    const [saveModalOpen, setSaveModalOpen] = React.useState(false);
    const [saveToNeo4jModalOpen, setSaveToNeo4jModalOpen] = React.useState(false);
    const [overwriteExistingDashboard, setOverwriteExistingDashboard] = React.useState(false);
    const [dashboardDatabase, setDashboardDatabase] = React.useState("neo4j");
    const [databases, setDatabases] = React.useState(["neo4j"]);

    const { driver } = useContext<Neo4jContextState>(Neo4jContext);

    useEffect(() => {
        loadDatabaseListFromNeo4j(driver, (result) => { setDatabases(result) });
    }, [])


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

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

    const filteredDashboard = filterNestedDict(dashboard, ["fields", "settingsOpen", "advancedSettingsOpen", "collapseTimeout"]);
    const dashboardString = JSON.stringify(filteredDashboard, null, 2);
    const downloadDashboard = () => {
        const element = document.createElement("a");
        const file = new Blob([dashboardString], { type: 'text/plain' });
        element.href = URL.createObjectURL(file);
        element.download = "dashboard.json";
        document.body.appendChild(element); // Required for this to work in FireFox
        element.click();
    }

    return (
        <div>
            <ListItem button onClick={handleClickOpen}>
                <ListItemIcon>
                    <IconButton style={{ padding: "0px" }} >
                        <SaveIcon />
                    </IconButton>
                </ListItemIcon>
                <ListItemText primary="Save" />
            </ListItem>

            <Dialog maxWidth={"lg"} open={saveModalOpen == true} onClose={handleClose} aria-labelledby="form-dialog-title">
                <DialogTitle id="form-dialog-title">
                    <SaveIcon style={{
                        height: "30px",
                        paddingTop: "4px",
                        marginBottom: "-8px",
                        marginRight: "5px",
                        paddingBottom: "5px"
                    }} />
                    Save Dashboard

                    <IconButton onClick={handleClose} style={{ padding: "3px", float: "right" }}>
                        <Badge badgeContent={""} >
                            <CloseIcon />
                        </Badge>
                    </IconButton>
                </DialogTitle>
                <DialogContent style={{ width: "1000px" }}>
                    <Button
                        component="label"
                        onClick={(e) => { setSaveToNeo4jModalOpen(true) }}
                        style={{ backgroundColor: "white" }}
                        color="default"
                        variant="contained"
                        size="medium"
                        endIcon={<StorageIcon />}>
                        Save to Neo4j
                    </Button>
                    <Button
                        component="label"
                        onClick={downloadDashboard}
                        style={{ backgroundColor: "white", marginLeft: "10px" }}
                        color="default"
                        variant="contained"
                        size="medium"
                        endIcon={<GetAppIcon />}>
                        Save to File
                    </Button>
                    <br /><br />
                    <TextareaAutosize
                        style={{ minHeight: "500px", width: "100%", border: "1px solid lightgray" }}
                        className={"textinput-linenumbers"}
                        value={dashboardString}
                        aria-label=""
                        placeholder="Your dashboard JSON should show here" />
                </DialogContent>
                <DialogActions>

                </DialogActions>
            </Dialog>

            <Dialog maxWidth={"lg"} open={saveToNeo4jModalOpen == true} onClose={(e) => { setSaveToNeo4jModalOpen(false) }} aria-labelledby="form-dialog-title">
                <DialogTitle id="form-dialog-title">

                    Save to Neo4j

                    <IconButton onClick={(e) => { setSaveToNeo4jModalOpen(false) }} style={{ padding: "3px", float: "right" }}>
                        <Badge badgeContent={""} >
                            <CloseIcon />
                        </Badge>
                    </IconButton>
                </DialogTitle>
                <DialogContent style={{ width: "800px" }}>
                    <DialogContentText>This will save your current dashboard as a node to your active Neo4j database.
                        <br />Ensure you have write permissions to the database to use this feature.
                    </DialogContentText>

                    <TextareaAutosize
                        style={{ width: "100%", border: "1px solid lightgray" }}
                        className={"textinput-linenumbers"}
                        value={"{\n    title: '" + dashboard.title + "',\n" +
                            "    date: '" + new Date().toISOString() + "',\n" +
                            "    user: '" + connection.username + "',\n" +
                            "    content: " + "{...}" + "\n}"}
                        aria-label=""
                        placeholder="" />

                    <FormControl style={{ marginTop: "10px" }}>
                        <InputLabel id="demo-simple-select-label">Save to Database</InputLabel>


                        <Select
                            labelId="demo-simple-select-label"
                            id="demo-simple-select"
                            style={{ width: "150px" }}
                            value={dashboardDatabase}
                            onChange={(e) => setDashboardDatabase(e.target.value)}
                        >
                            {databases.map(database => {
                                return <MenuItem value={database}>{database}</MenuItem>
                            })}
                        </Select>

                    </FormControl>

                    <FormControl style={{ marginTop: "20px", marginLeft: "10px" }}>
                        <Tooltip title="Overwrite dashboard(s) with the same name." aria-label="">
                            <FormControlLabel
                                control={<Checkbox style={{ fontSize: "small", color: "grey" }} checked={overwriteExistingDashboard} onChange={e => setOverwriteExistingDashboard(!overwriteExistingDashboard)} name="overwrite" />}
                                label="Overwrite"
                            />
                        </Tooltip>
                    </FormControl>

                    <Button
                        component="label"
                        onClick={e => {
                            saveDashboardToNeo4j(driver, dashboardDatabase, dashboard, new Date().toISOString(), connection.username, overwriteExistingDashboard);
                            setSaveToNeo4jModalOpen(false);
                            setSaveModalOpen(false);
                        }}
                        style={{ backgroundColor: "white", marginTop: "20px", float: "right" }}
                        color="default"
                        variant="contained"
                        endIcon={<SaveIcon />}
                        size="medium">
                        Save
                    </Button>
                    <Button
                        component="label"
                        onClick={(e) => { setSaveToNeo4jModalOpen(false) }}
                        style={{ float: "right", marginTop: "20px", marginRight: "10px", backgroundColor: "white" }}
                        color="default"
                        variant="contained"
                        size="medium">
                        Cancel
                    </Button>
                </DialogContent>
                <DialogActions>

                </DialogActions>
            </Dialog>
        </div>
    );
}
Example #14
Source File: ColumnTitle.tsx    From knboard with MIT License 4 votes vote down vote up
ColumnTitle = ({ id, title, tasksCount, ...props }: Props) => {
  const dispatch = useDispatch();
  const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(
    null
  );
  const [pendingTitle, setPendingTitle] = useState<string>(title);
  const [editing, setEditing] = useState<boolean>(false);
  const titleTextAreaRef = useRef<HTMLTextAreaElement>(null);

  useEffect(() => {
    if (!editing && title === pendingTitle) {
      titleTextAreaRef?.current?.blur();
    }
  }, [pendingTitle, editing]);

  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.keyCode === Key.Enter) {
      e.preventDefault();
      if (pendingTitle.length > 0) {
        titleTextAreaRef?.current?.blur();
      }
    }
    if (e.keyCode === Key.Escape) {
      e.preventDefault();
      setPendingTitle(title);
      setEditing(false);
      // blur via useEffect
    }
  };

  const handleSave = () => {
    if (editing && pendingTitle.length > 0) {
      setEditing(false);
      if (pendingTitle !== title) {
        dispatch(patchColumn({ id, fields: { title: pendingTitle } }));
      }
    }
  };

  const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
    setPendingTitle(e.target.value);
  };

  const handleFocus = (e: React.FocusEvent<HTMLTextAreaElement>) => {
    e.target.select();
  };

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

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

  const handleDelete = () => {
    if (
      window.confirm(
        "Are you sure? Deleting the column will also delete related tasks and this cannot be undone."
      )
    ) {
      dispatch(deleteColumn(id));
      handleOptionsClose();
    }
  };

  const open = Boolean(anchorEl);
  const popoverId = open ? `col-${id}options-popover` : undefined;

  return (
    <Container {...props}>
      {editing ? (
        <InputTitle>
          <TextareaAutosize
            ref={titleTextAreaRef}
            value={pendingTitle}
            onChange={handleChange}
            onBlur={handleSave}
            onKeyDown={handleKeyDown}
            data-testid="column-title-textarea"
            onFocus={handleFocus}
            autoFocus
          />
        </InputTitle>
      ) : (
        <RegularTitle onClick={() => setEditing(true)}>
          {pendingTitle}
        </RegularTitle>
      )}
      <Extra>
        <InnerExtra>
          <Count>{tasksCount}</Count>
          <Button
            onClick={handleOptionsClick}
            data-testid="col-options"
            css={css`
              margin-left: 0.25rem;
              min-width: 0;
              padding: 2px 8px;
              height: 22px;
            `}
          >
            <FontAwesomeIcon icon={faEllipsisV} />
          </Button>
        </InnerExtra>
        <Popover
          id={popoverId}
          open={open}
          anchorEl={anchorEl}
          onClose={handleOptionsClose}
          anchorOrigin={{
            vertical: "bottom",
            horizontal: "left",
          }}
          transformOrigin={{
            vertical: "top",
            horizontal: "left",
          }}
        >
          <OptionsContent>
            <Button
              startIcon={<FontAwesomeIcon fixedWidth icon={faTrash} />}
              onClick={handleDelete}
              data-testid="delete-column"
              size="small"
              css={css`
                font-size: 12px;
                font-weight: bold;
                color: ${ACTION_G};
              `}
            >
              Delete column
            </Button>
          </OptionsContent>
        </Popover>
      </Extra>
    </Container>
  );
}
Example #15
Source File: EditTaskDialog.tsx    From knboard with MIT License 4 votes vote down vote up
EditTaskDialog = () => {
  const theme = useTheme();
  const dispatch = useDispatch();
  const columns = useSelector(selectAllColumns);
  const labels = useSelector(selectAllLabels);
  const labelsById = useSelector(selectLabelEntities);
  const columnsById = useSelector(selectColumnsEntities);
  const tasksByColumn = useSelector((state: RootState) => state.task.byColumn);
  const taskId = useSelector((state: RootState) => state.task.editDialogOpen);
  const tasksById = useSelector((state: RootState) => state.task.byId);
  const [title, setTitle] = useState("");
  const [description, setDescription] = useState("");
  const [editingDescription, setEditingDescription] = useState(false);
  const titleTextAreaRef = useRef<HTMLTextAreaElement>(null);
  const wrapperRef = useRef<HTMLDivElement>(null);
  const editorRef = useRef<MdEditor>(null);
  const cancelRef = useRef<HTMLButtonElement>(null);
  const xsDown = useMediaQuery(theme.breakpoints.down("xs"));
  const open = taskId !== null;

  useEffect(() => {
    if (taskId && tasksById[taskId]) {
      setDescription(tasksById[taskId].description);
      setTitle(tasksById[taskId].title);
    }
  }, [open, taskId]);

  const handleSaveTitle = () => {
    if (taskId) {
      dispatch(patchTask({ id: taskId, fields: { title } }));
    }
  };

  const handleSaveDescription = () => {
    if (taskId) {
      dispatch(patchTask({ id: taskId, fields: { description } }));
      setEditingDescription(false);
    }
  };

  const handleCancelDescription = () => {
    if (taskId && tasksById[taskId]) {
      setDescription(tasksById[taskId].description);
      setEditingDescription(false);
    }
  };

  useEffect(() => {
    const handleClickOutside = (event: any) => {
      if (
        wrapperRef.current &&
        !wrapperRef.current.contains(event.target) &&
        cancelRef.current &&
        !cancelRef.current?.contains(event.target)
      ) {
        handleSaveDescription();
      }
    };

    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, [wrapperRef, taskId, description]);

  useEffect(() => {
    if (editingDescription && editorRef && editorRef.current) {
      editorRef.current.setSelection({
        start: 0,
        end: description.length,
      });
    }
  }, [editingDescription]);

  const findTaskColumnId = () => {
    for (const columnId in tasksByColumn) {
      for (const id of tasksByColumn[columnId]) {
        if (id === taskId) {
          return columnId;
        }
      }
    }
    return null;
  };

  const columnId = findTaskColumnId();

  if (!taskId || !tasksById[taskId] || !columnId) {
    return null;
  }

  const task = tasksById[taskId];
  const column = columnsById[columnId];

  const handleEditorKeyDown = (e: React.KeyboardEvent) => {
    if (e.keyCode == Key.Enter && e.metaKey) {
      handleSaveDescription();
    }
    if (e.keyCode === Key.Escape) {
      // Prevent propagation from reaching the Dialog
      e.stopPropagation();
      handleCancelDescription();
    }
  };

  const handleTitleKeyDown = (e: React.KeyboardEvent) => {
    if (e.keyCode === Key.Enter) {
      e.preventDefault();
      titleTextAreaRef?.current?.blur();
    }
    if (e.keyCode === Key.Escape) {
      // Prevent propagation from reaching the Dialog
      e.stopPropagation();
    }
  };

  const handleClose = () => {
    dispatch(setEditDialogOpen(null));
    setEditingDescription(false);
  };

  const handleTitleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
    setTitle(e.target.value);
  };

  const handleColumnChange = (_: any, value: IColumn | null) => {
    if (!column || !value || column.id === value.id) {
      return;
    }
    const current: Id[] = [...tasksByColumn[column.id]];
    const next: Id[] = [...tasksByColumn[value.id]];

    const currentId = current.indexOf(task.id);
    const newPosition = 0;

    // remove from original
    current.splice(currentId, 1);
    // insert into next
    next.splice(newPosition, 0, task.id);

    const updatedTasksByColumn: TasksByColumn = {
      ...tasksByColumn,
      [column.id]: current,
      [value.id]: next,
    };
    dispatch(updateTasksByColumn(updatedTasksByColumn));
    handleClose();
  };

  const handlePriorityChange = (_: any, priority: Priority | null) => {
    if (priority) {
      dispatch(patchTask({ id: taskId, fields: { priority: priority.value } }));
    }
  };

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

  const handleDelete = () => {
    if (window.confirm("Are you sure? Deleting a task cannot be undone.")) {
      dispatch(deleteTask(task.id));
      handleClose();
    }
  };

  const handleDescriptionClick = () => {
    setEditingDescription(true);
  };

  const handleEditorChange = ({ text }: any) => {
    setDescription(text);
  };

  const handleLabelsChange = (newLabels: Label[]) => {
    dispatch(
      patchTask({
        id: taskId,
        fields: { labels: newLabels.map((label) => label.id) },
      })
    );
  };

  const handleKeyDown = (e: React.KeyboardEvent) => {
    // don't listen for input when inputs are focused
    if (
      document.activeElement instanceof HTMLInputElement ||
      document.activeElement instanceof HTMLTextAreaElement
    ) {
      return;
    }

    if (e.key === "Backspace" && e.metaKey) {
      handleDelete();
    }

    if (e.key === "Escape" && e.metaKey) {
      handleClose();
    }

    if (e.key === "l" && e.metaKey) {
      e.preventDefault();
      handleNotImplemented();
    }
  };

  return (
    <Dialog
      open={open}
      onClose={handleClose}
      onKeyDown={handleKeyDown}
      fullWidth
      keepMounted={false}
      fullScreen={xsDown}
      css={css`
        .MuiDialog-paper {
          max-width: 920px;
        }
      `}
    >
      <Content theme={theme}>
        <Close onClose={handleClose} />
        <Main>
          <Header>id: {task.id}</Header>
          <Title>
            <FontAwesomeIcon icon={faArrowUp} />
            <TextareaAutosize
              ref={titleTextAreaRef}
              value={title}
              onChange={handleTitleChange}
              onBlur={handleSaveTitle}
              onKeyDown={handleTitleKeyDown}
              data-testid="task-title"
            />
          </Title>
          <DescriptionHeader>
            <FontAwesomeIcon icon={faAlignLeft} />
            <h3>Description</h3>
          </DescriptionHeader>
          <Description
            key={`${taskId}${editingDescription}`}
            data-testid="task-description"
          >
            <EditorWrapper
              onDoubleClick={
                editingDescription ? undefined : handleDescriptionClick
              }
              editing={editingDescription}
              ref={wrapperRef}
              theme={theme}
              onKeyDown={handleEditorKeyDown}
            >
              <MdEditor
                ref={editorRef}
                plugins={MD_EDITOR_PLUGINS}
                config={
                  editingDescription ? MD_EDITING_CONFIG : MD_READ_ONLY_CONFIG
                }
                value={
                  editingDescription
                    ? description
                    : description || DESCRIPTION_PLACEHOLDER
                }
                renderHTML={(text) => mdParser.render(text)}
                onChange={handleEditorChange}
                placeholder={DESCRIPTION_PLACEHOLDER}
              />
            </EditorWrapper>
            {editingDescription && (
              <DescriptionActions>
                <Button
                  variant="contained"
                  data-testid="save-description"
                  onClick={handleSaveDescription}
                  color="primary"
                  size="small"
                >
                  Save ({getMetaKey()}+⏎)
                </Button>
                <Button
                  variant="outlined"
                  data-testid="cancel-description"
                  onClick={handleCancelDescription}
                  ref={cancelRef}
                  size="small"
                  css={css`
                    margin-left: 0.5rem;
                  `}
                >
                  Cancel (Esc)
                </Button>
              </DescriptionActions>
            )}
          </Description>
          <CommentSection taskId={task.id} />
        </Main>
        <Side theme={theme}>
          <TaskAssignees task={task} />
          <Autocomplete
            id="column-select"
            size="small"
            options={columns}
            getOptionLabel={(option) => option.title}
            renderInput={(params) => (
              <TextField {...params} label="Column" variant="outlined" />
            )}
            value={column}
            onChange={handleColumnChange}
            disableClearable
            openOnFocus
            data-testid="edit-column"
            css={css`
              width: 100%;
            `}
          />
          <Autocomplete
            id="priority-select"
            size="small"
            blurOnSelect
            autoHighlight
            options={PRIORITY_OPTIONS}
            getOptionLabel={(option) => option.label}
            value={PRIORITY_MAP[task.priority]}
            onChange={handlePriorityChange}
            renderInput={(params) => (
              <TextField {...params} label="Priority" variant="outlined" />
            )}
            renderOption={(option) => <PriorityOption option={option} />}
            openOnFocus
            disableClearable
            data-testid="edit-priority"
            css={css`
              width: 100%;
              margin-top: 1rem;
            `}
          />
          <Autocomplete
            multiple
            id="labels-select"
            data-testid="edit-labels"
            size="small"
            filterSelectedOptions
            autoHighlight
            openOnFocus
            blurOnSelect
            disableClearable
            options={labels}
            getOptionLabel={(option) => option.name}
            value={
              tasksById[taskId].labels.map(
                (labelId) => labelsById[labelId]
              ) as Label[]
            }
            onChange={(_, newLabels) => handleLabelsChange(newLabels)}
            renderInput={(params) => (
              <TextField {...params} label="Labels" variant="outlined" />
            )}
            renderTags={(value, getTagProps) =>
              value.map((option, index) => (
                <LabelChip
                  key={option.id}
                  label={option}
                  size="small"
                  {...getTagProps({ index })}
                />
              ))
            }
            renderOption={(option) => <LabelChip label={option} size="small" />}
            css={css`
              width: 100%;
              margin-top: 1rem;
              margin-bottom: 2rem;
            `}
          />
          <ButtonsContainer>
            <Button
              startIcon={<FontAwesomeIcon fixedWidth icon={faLock} />}
              onClick={handleNotImplemented}
              size="small"
              css={css`
                font-size: 12px;
                font-weight: bold;
                color: ${TASK_G};
              `}
            >
              Lock task ({getMetaKey()}+L)
            </Button>
            <Button
              startIcon={<FontAwesomeIcon fixedWidth icon={faTrash} />}
              onClick={handleDelete}
              data-testid="delete-task"
              size="small"
              css={css`
                font-size: 12px;
                font-weight: bold;
                color: ${TASK_G};
                margin-bottom: 2rem;
              `}
            >
              Delete task ({getMetaKey()}+⌫)
            </Button>
          </ButtonsContainer>
          <Text>
            Updated {formatDistanceToNow(new Date(task.modified))} ago
          </Text>
          <Text
            css={css`
              margin-bottom: 1rem;
            `}
          >
            Created {formatDistanceToNow(new Date(task.created))} ago
          </Text>
        </Side>
      </Content>
    </Dialog>
  );
}