types#BoardMember TypeScript Examples

The following examples show how to use types#BoardMember. 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: CommentItem.test.tsx    From knboard with MIT License 5 votes vote down vote up
member: BoardMember = {
  id: 1,
  username: "foobar",
  email: "[email protected]",
  first_name: "Foo",
  last_name: "Bar",
  avatar: null,
}
Example #2
Source File: MemberSlice.tsx    From knboard with MIT License 5 votes vote down vote up
memberAdapter = createEntityAdapter<BoardMember>({
  sortComparer: (a, b) => a.username.localeCompare(b.username),
})
Example #3
Source File: Task.tsx    From knboard with MIT License 5 votes vote down vote up
TaskFooter = ({ task }: { task: ITask }) => {
  const membersByIds = useSelector(selectMembersEntities);
  const assignees = task.assignees.map(
    (assigneeId) => membersByIds[assigneeId]
  ) as BoardMember[];

  return (
    <Footer>
      <CardIcon data-testid="task-priority">
        <FontAwesomeIcon icon={faArrowUp} color={PRIO_COLORS[task.priority]} />
      </CardIcon>
      {assignees.length > 0 && (
        <Assignees>
          <AvatarGroup
            max={3}
            css={css`
              & .MuiAvatarGroup-avatar {
                height: 1.25rem;
                width: 1.25rem;
                font-size: 8px;
                margin-left: -4px;
                border: none;
              }
            `}
          >
            {assignees.map((assignee) => (
              <Avatar
                key={assignee.id}
                css={css`
                  height: 1.25rem;
                  width: 1.25rem;
                  font-size: 8px;
                  margin-left: -12px;
                `}
                src={assignee.avatar?.photo}
                alt={assignee.avatar?.name}
              >
                {assignee.username.charAt(0)}
              </Avatar>
            ))}
          </AvatarGroup>
        </Assignees>
      )}
    </Footer>
  );
}
Example #4
Source File: MemberDialog.tsx    From knboard with MIT License 4 votes vote down vote up
MemberDialog = ({ board }: Props) => {
  const theme = useTheme();
  const dispatch = useDispatch();
  const memberId = useSelector((state: RootState) => state.member.dialogMember);
  const members = useSelector(selectMembersEntities);
  const boardOwner = useSelector((state: RootState) =>
    currentBoardOwner(state)
  );
  const xsDown = useMediaQuery(theme.breakpoints.down("xs"));
  const [confirmDelete, setConfirmDelete] = useState(false);
  const member = memberId === null ? null : members[memberId];
  const memberIsOwner = member?.id === board.owner;
  const open = member !== null;

  if (!member) {
    return null;
  }

  const handleClose = () => {
    dispatch(setDialogMember(null));
    setConfirmDelete(false);
  };

  const handleRemoveMember = async () => {
    try {
      const response = await api.post(
        `${API_BOARDS}${board.id}/remove_member/`,
        { username: member.username }
      );
      const removedMember = response.data as BoardMember;
      dispatch(removeBoardMember(removedMember.id));
      dispatch(createSuccessToast(`Removed ${removedMember.username}`));
      handleClose();
    } catch (err) {
      dispatch(createErrorToast(err.toString()));
    }
  };

  return (
    <Dialog
      open={open}
      onClose={handleClose}
      maxWidth="xs"
      fullWidth
      fullScreen={xsDown}
    >
      <Close onClose={handleClose} />
      <DialogTitle id="member-detail">Member</DialogTitle>
      <Container theme={theme}>
        {confirmDelete ? (
          <div>
            <Alert
              severity="error"
              css={css`
                margin-bottom: 2rem;
              `}
            >
              Are you sure you want to remove this member? This member will be
              removed from all cards.
            </Alert>
            <ConfirmAction>
              <Fab
                size="small"
                onClick={() => setConfirmDelete(false)}
                css={css`
                  box-shadow: none;
                  &.MuiFab-sizeSmall {
                    width: 32px;
                    height: 32px;
                  }
                `}
              >
                <FontAwesomeIcon icon={faAngleLeft} color="#555" />
              </Fab>
              <Button
                size="small"
                color="secondary"
                variant="contained"
                onClick={handleRemoveMember}
                css={css`
                  font-size: 0.625rem;
                `}
              >
                Remove member
              </Button>
            </ConfirmAction>
          </div>
        ) : (
          <>
            <Avatar
              css={css`
                height: 6rem;
                width: 6rem;
                font-size: 36px;
                margin-bottom: 1rem;
              `}
              src={member?.avatar?.photo}
              alt={member?.avatar?.name}
            >
              {member.username.charAt(0)}
            </Avatar>
            <Main>
              <PrimaryText>
                {member.first_name} {member.last_name}
              </PrimaryText>
              <SecondaryText>
                username: <b>{member.username}</b>
              </SecondaryText>
              <SecondaryText
                css={css`
                  margin-bottom: 1.5rem;
                `}
              >
                email: <b>{member?.email || "-"}</b>
              </SecondaryText>
              {memberIsOwner && (
                <Alert severity="info">Owner of this board</Alert>
              )}
              {boardOwner && !memberIsOwner && (
                <Button
                  size="small"
                  css={css`
                    color: #333;
                    font-size: 0.625rem;
                  `}
                  variant="outlined"
                  onClick={() => setConfirmDelete(true)}
                >
                  Remove from board
                </Button>
              )}
            </Main>
          </>
        )}
      </Container>
    </Dialog>
  );
}
Example #5
Source File: MemberFilter.tsx    From knboard with MIT License 4 votes vote down vote up
MemberFilter = ({ boardId }: Props) => {
  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
  const [filteredAssignee, setFilteredAssignee] = useState<BoardMember[]>([]);
  const dispatch = useDispatch();
  const members = useSelector(selectAllMembers);

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

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

  const postFilterMember = async (users: BoardMember[]) => {
    dispatch(
      fetchBoardById({
        boardId: boardId,
        assigneeIds: users.map((m) => m.id),
      })
    );
    handleClose();
  };

  const handleClickFilter = () => {
    postFilterMember(filteredAssignee);
  };

  const handleClickClearFilter = () => {
    setFilteredAssignee([]);
    postFilterMember([]);
  };

  const ClearFilterButton = () => (
    <FilterButton>
      <Button
        variant="outlined"
        size="small"
        css={css`
          text-transform: none;
        `}
        onClick={handleClickClearFilter}
        data-testid="clear-filter"
      >
        Clear Filters
      </Button>
    </FilterButton>
  );

  return (
    <>
      <FilterButton>
        <Button
          variant="outlined"
          size="small"
          css={css`
            text-transform: none;
          `}
          onClick={handleClick}
          aria-controls="member-filter-menu"
          aria-haspopup="true"
          data-testid="member-filter"
        >
          Filter
        </Button>
      </FilterButton>
      {filteredAssignee.length > 0 ? <ClearFilterButton /> : null}
      <Popover
        id="member-filter-menu"
        anchorEl={anchorEl}
        getContentAnchorEl={null}
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "left",
        }}
        transformOrigin={{
          vertical: "top",
          horizontal: "left",
        }}
        open={Boolean(anchorEl)}
        onClose={handleClose}
        transitionDuration={0}
      >
        <Content>
          <Description>Filter tasks by assignees</Description>
          <Box display="flex" alignItems="center">
            <Box flexGrow={1}>
              <AssigneeAutoComplete
                assignee={filteredAssignee}
                members={members}
                controlId={"assignee-filter"}
                dataTestId={"filter-assignees"}
                setAssignee={setFilteredAssignee}
              />
            </Box>
            <Button
              color="primary"
              variant="contained"
              css={css`
                font-size: 0.625rem;
                margin-left: 0.5rem;
              `}
              onClick={handleClickFilter}
              data-testid="filter-selected"
              disabled={filteredAssignee.length === 0}
            >
              Filter
            </Button>
          </Box>
        </Content>
      </Popover>
    </>
  );
}
Example #6
Source File: MemberInvite.tsx    From knboard with MIT License 4 votes vote down vote up
MemberInvite = ({ boardId }: Props) => {
  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
  const [tagsValue, setTagsValue] = useState<UserOption[]>([]);
  const dispatch = useDispatch();

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

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

  const postInviteMember = async (users: number[]) => {
    try {
      const response = await api.post(
        `${API_BOARDS}${boardId}/invite_member/`,
        { users }
      );
      const newMembers = response.data as BoardMember[];
      dispatch(addBoardMembers(newMembers));
      dispatch(
        createSuccessToast(
          `Invited ${newMembers.map((m) => m.username).join(", ")}`
        )
      );
      handleClose();
      setTagsValue([]);
    } catch (err) {
      dispatch(createErrorToast(err.toString()));
    }
  };

  const handleClickInvite = () => {
    postInviteMember(tagsValue.map((v) => v.id));
  };

  return (
    <>
      <InviteMember>
        <Button
          variant="outlined"
          size="small"
          css={css`
            text-transform: none;
          `}
          onClick={handleClick}
          aria-controls="member-invite-menu"
          aria-haspopup="true"
          data-testid="member-invite"
        >
          Invite
        </Button>
      </InviteMember>
      <Popover
        id="member-invite-menu"
        anchorEl={anchorEl}
        getContentAnchorEl={null}
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "left",
        }}
        transformOrigin={{
          vertical: "top",
          horizontal: "left",
        }}
        open={Boolean(anchorEl)}
        onClose={handleClose}
        transitionDuration={0}
      >
        <Content>
          <Description>Invite to this board</Description>
          <Box display="flex" alignItems="center">
            <UserSearch
              boardId={boardId}
              tagsValue={tagsValue}
              setTagsValue={setTagsValue}
            />
            <Button
              color="primary"
              variant="contained"
              css={css`
                font-size: 0.625rem;
                margin-left: 0.5rem;
              `}
              onClick={handleClickInvite}
              data-testid="invite-selected"
              disabled={tagsValue.length === 0}
            >
              Invite
            </Button>
          </Box>
        </Content>
      </Popover>
    </>
  );
}
Example #7
Source File: CreateTaskDialog.tsx    From knboard with MIT License 4 votes vote down vote up
CreateTaskDialog = () => {
  const theme = useTheme();
  const dispatch = useDispatch();
  const labelsOptions = useSelector(selectAllLabels);
  const members = useSelector(selectAllMembers);
  const open = useSelector((state: RootState) => state.task.createDialogOpen);
  const columnId = useSelector(
    (state: RootState) => state.task.createDialogColumn
  );
  const createLoading = useSelector(
    (state: RootState) => state.task.createLoading
  );
  const [titleTouched, setTitleTouched] = useState<boolean>(false);
  const [title, setTitle] = useState<string>("");
  const [description, setDescription] = useState<string>("");
  const [assignees, setAssignees] = useState<BoardMember[]>([]);
  const [priority, setPriority] = useState<Priority | null>({
    value: "M",
    label: "Medium",
  });
  const [labels, setLabels] = useState<Label[]>([]);
  const xsDown = useMediaQuery(theme.breakpoints.down("xs"));

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

  const setInitialValues = () => {
    if (columnId) {
      setTitleTouched(false);
      setTitle("");
      setDescription("");
      setAssignees([]);
      setPriority(PRIORITY_2);
      setLabels([]);
    }
  };

  useEffect(() => {
    setInitialValues();
  }, [open]);

  const handleClose = () => {
    if (window.confirm("Are you sure? Any progress made will be lost.")) {
      dispatch(setCreateDialogOpen(false));
    }
  };

  const handleCreate = async () => {
    setTitleTouched(true);
    if (columnId && priority) {
      const newTask = {
        title,
        description,
        column: columnId,
        labels: labels.map((l) => l.id),
        assignees: assignees.map((a) => a.id),
        priority: priority.value,
      };
      dispatch(createTask(newTask));
    }
  };

  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.keyCode == Key.Enter && e.metaKey) {
      handleCreate();
    }
  };

  return (
    <Dialog
      open={open}
      onClose={handleClose}
      maxWidth="sm"
      fullWidth
      keepMounted={false}
      fullScreen={xsDown}
    >
      <Content onKeyDown={handleKeyDown}>
        <DialogTitle>New issue</DialogTitle>

        <TextField
          autoFocus
          id="create-task-title"
          data-testid="create-task-title"
          label="Title"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          variant="outlined"
          fullWidth
          size="small"
          onBlur={() => setTitleTouched(true)}
          error={titleTouched && !title}
        />

        <EditorWrapper>
          <MdEditor
            plugins={MD_EDITOR_PLUGINS}
            config={MD_EDITOR_CONFIG}
            value={description}
            renderHTML={(text) => mdParser.render(text)}
            onChange={handleEditorChange}
            placeholder="Describe the issue..."
          />
        </EditorWrapper>

        <Autocomplete
          multiple
          filterSelectedOptions
          disableClearable
          openOnFocus
          id="create-assignee-select"
          size="small"
          options={members}
          getOptionLabel={(option) => option.username}
          value={assignees}
          onChange={(_event, value) => setAssignees(value)}
          renderOption={(option) => <AvatarOption option={option} />}
          renderInput={(params) => (
            <TextField {...params} label="Assignees" variant="outlined" />
          )}
          renderTags={(value, getTagProps) =>
            value.map((option, index) => (
              <AvatarTag
                key={option.id}
                option={option}
                {...getTagProps({ index })}
              />
            ))
          }
          css={css`
            width: 100%;
            margin-top: 1rem;
          `}
        />

        <Autocomplete
          id="create-priority-select"
          size="small"
          autoHighlight
          options={PRIORITY_OPTIONS}
          getOptionLabel={(option) => option.label}
          value={priority}
          onChange={(_: any, value: Priority | null) => setPriority(value)}
          renderOption={(option) => <PriorityOption option={option} />}
          renderInput={(params) => (
            <TextField {...params} label="Priority" variant="outlined" />
          )}
          openOnFocus
          disableClearable
          css={css`
            width: 100%;
            margin-top: 1rem;
          `}
        />

        <Autocomplete
          multiple
          id="create-labels-select"
          size="small"
          filterSelectedOptions
          autoHighlight
          openOnFocus
          options={labelsOptions}
          getOptionLabel={(option) => option.name}
          value={labels}
          onChange={(_, newLabels) => setLabels(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`
            margin-top: 1rem;
            width: 100%;
          `}
        />
      </Content>

      <Footer theme={theme}>
        <Button
          startIcon={
            createLoading ? (
              <CircularProgress color="inherit" size={16} />
            ) : (
              <FontAwesomeIcon icon={faRocket} />
            )
          }
          variant="contained"
          color="primary"
          size="small"
          onClick={handleCreate}
          disabled={createLoading}
          data-testid="task-create"
          css={css`
            ${theme.breakpoints.down("xs")} {
              flex-grow: 1;
            }
          `}
        >
          Create issue ({getMetaKey()}+⏎)
        </Button>
        <Button
          css={css`
            margin-left: 1rem;
          `}
          onClick={handleClose}
        >
          Cancel (Esc)
        </Button>
      </Footer>
    </Dialog>
  );
}
Example #8
Source File: TaskAssignees.tsx    From knboard with MIT License 4 votes vote down vote up
TaskAssignees = ({ task }: Props) => {
  const dispatch = useDispatch();
  const membersById = useSelector(selectMembersEntities);
  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
  const [pendingAssignees, setPendingAssignees] = useState<BoardMember[]>([]);
  const members = useSelector(selectAllMembers);
  const assignees = task.assignees.map(
    (id) => membersById[id]
  ) as BoardMember[];

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

  const handleClose = () => {
    const currentIds = assignees.map((a) => a.id);
    const pendingIds = pendingAssignees.map((member) => member.id);
    if (
      !(
        pendingIds.length === currentIds.length &&
        pendingIds
          .sort()
          .every((value, index) => value === currentIds.sort()[index])
      )
    ) {
      dispatch(
        patchTask({
          id: task.id,
          fields: { assignees: pendingIds },
        })
      );
    }
    setAnchorEl(null);
  };

  const open = Boolean(anchorEl);
  const popoverId = open ? "task-assignees-popover" : undefined;

  return (
    <Container>
      <Label>Assignees</Label>
      {assignees.map((assignee) => (
        <List key={assignee.id}>
          <div>
            <Avatar
              css={css`
                height: 2rem;
                width: 2rem;
                margin-right: 0.5rem;
              `}
              src={assignee.avatar?.photo}
              alt={assignee.avatar?.name}
            >
              {assignee.username.charAt(0)}
            </Avatar>
          </div>
          <div>{assignee.username}</div>
        </List>
      ))}
      <Button
        size="small"
        onClick={handleClick}
        data-testid="open-edit-assignees"
        css={css`
          color: ${PRIMARY};
          font-size: 0.7rem;
        `}
      >
        Change
      </Button>
      <Popover
        id={popoverId}
        open={open}
        anchorEl={anchorEl}
        transitionDuration={0}
        style={{ zIndex: modalPopperIndex }}
        onClose={handleClose}
        css={css`
          .MuiPaper-rounded {
            border-radius: 0;
          }
        `}
      >
        <Content>
          <Close onClose={handleClose} onPopper />
          <ContentTitle>Assigned board members</ContentTitle>
          <AssigneeContainer>
            <AssigneeAutoComplete
              assignee={pendingAssignees}
              members={members}
              setAssignee={setPendingAssignees}
              controlId="assignee-select"
              dataTestId={"edit-assignees"}
            />
          </AssigneeContainer>
        </Content>
      </Popover>
    </Container>
  );
}