@ant-design/icons#DownOutlined TypeScript Examples

The following examples show how to use @ant-design/icons#DownOutlined. 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: SelectDownIcon.tsx    From posthog-foss with MIT License 6 votes vote down vote up
SelectDownIcon = (props: React.HTMLAttributes<HTMLSpanElement>): JSX.Element => {
    return (
        <span {...props}>
            <DownOutlined
                style={{
                    paddingLeft: '0.6em',
                    fontSize: '90%',
                    opacity: 0.5,
                }}
            />
        </span>
    )
}
Example #2
Source File: general-custom-buttons.editor.tsx    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
export function DropdownBtn({
  isMoreButton,
}: dropdownBtnProps): React.ReactElement {
  return (
    <>
      <BaseButton>
        {isMoreButton ? (
          <EllipsisOutlined />
        ) : (
          <>
            <SettingOutlined style={{ marginRight: 5 }} /> 管理{" "}
            <DownOutlined style={{ marginLeft: 5 }} />
          </>
        )}
      </BaseButton>
    </>
  );
}
Example #3
Source File: FormList.tsx    From condo with MIT License 6 votes vote down vote up
function ExtraDropdownActionsMenu ({ actions }) {
    const actionsLine = actions.filter(identity)
    const [popConfirmProps, setPopConfirmProps] = useState({ visible: false, title: null, icon: null })

    function handleAction ({ key }) {
        const action = actionsLine[key]
        if (action.confirm) {
            setPopConfirmProps({
                visible: true,
                onConfirm: action.action,
                onCancel: () => setPopConfirmProps({ visible: false, title: null, icon: null }),
                ...action.confirm,
            })
        } else {
            setPopConfirmProps({ visible: false, title: null, icon: null })
            action.action()
        }
    }

    return <Popconfirm {...popConfirmProps}>
        <Dropdown overlay={<Menu onClick={handleAction}>
            {actionsLine.map((action, i) => <Menu.Item key={i}>{action.label}</Menu.Item>)}
        </Menu>}>
            <a> ... <DownOutlined /></a>
        </Dropdown>
    </Popconfirm>
}
Example #4
Source File: Panel.tsx    From fe-v5 with Apache License 2.0 6 votes vote down vote up
export default function Panel(props: IProps) {
  const [isActive, setIsActive] = useState<boolean>(props.isActive || true);
  return (
    <div
      className={classnames({
        'n9e-collapse-item': true,
        'n9e-collapse-item-active': isActive,
        'n9e-collapse-item-inner': props.isInner,
      })}
    >
      <div
        className='n9e-collapse-header'
        onClick={() => {
          setIsActive(!isActive);
        }}
      >
        {isActive ? <DownOutlined className='n9e-collapse-arrow' /> : <RightOutlined className='n9e-collapse-arrow' />}
        {props.header}
        <div
          className='n9e-collapse-extra'
          onClick={(e) => {
            e.stopPropagation();
            e.nativeEvent.stopImmediatePropagation();
          }}
        >
          {props.extra}
        </div>
      </div>
      <div
        className={classnames({
          'n9e-collapse-content': true,
          'n9e-collapse-content-hidden': !isActive,
        })}
      >
        <div className='n9e-collapse-content-box'>{props.children}</div>
      </div>
    </div>
  );
}
Example #5
Source File: index.tsx    From metaplex with Apache License 2.0 6 votes vote down vote up
export function TokenButton({
  mint,
  onClick,
}: {
  mint: PublicKey;
  onClick: () => void;
}) {
  const tokenMap = useTokenList().subscribedTokens;
  const tokenInfo = tokenMap.filter(t => t.address == mint.toBase58())[0];

  return (
    <Row onClick={onClick} className={'token-button'} justify="space-between">
      <Col>
        <Row>
          <TokenCircle
            iconSize={40}
            iconFile={tokenInfo?.logoURI}
            style={{ marginTop: 2.5 }}
          />
          <TokenName mint={mint} />
        </Row>
      </Col>
      <Col>
        <DownOutlined style={{ marginLeft: 10, fontWeight: 700 }} />
      </Col>
    </Row>
  );
}
Example #6
Source File: TextCollapse.tsx    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
export function TextCollapse(props: TextCollapseProps): React.ReactElement {
  const [ellipsis, setEllipsis] = useState(true);

  const { Paragraph } = Typography;

  const handleClick = () => {
    setEllipsis(!ellipsis);
  };

  return (
    <div className={styles.main}>
      <Paragraph
        data-testid="main-text"
        ellipsis={ellipsis ? { rows: props.line, expandable: false } : false}
      >
        {props.text}
      </Paragraph>
      <div className={styles.icons} onClick={handleClick} data-testid="icons">
        {ellipsis ? <DownOutlined /> : <UpOutlined />}
      </div>
    </div>
  );
}
Example #7
Source File: index.tsx    From fe-v5 with Apache License 2.0 5 votes vote down vote up
function Refresh(props: IProps, ref) {
  const [intervalSeconds, setIntervalSeconds] = useState(intervalSecondsCache);
  const intervalRef = useRef<NodeJS.Timeout>();

  useEffect(() => {
    if (intervalRef.current) {
      clearInterval(intervalRef.current);
    }
    if (intervalSeconds) {
      intervalRef.current = setInterval(() => {
        props.onRefresh();
      }, intervalSeconds * 1000);
    }
  }, [intervalSeconds]);

  useEffect(() => {
    return () => {
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
      }
    };
  }, []);

  useImperativeHandle(ref, () => ({
    closeRefresh() {
      setIntervalSeconds(0);
      window.localStorage.setItem('refresh-interval-seconds', '0');
    },
  }));

  return (
    <div className='refresh-container'>
      <Button className='refresh-btn' icon={<SyncOutlined />} onClick={props.onRefresh} />
      <Dropdown
        trigger={['click']}
        overlay={
          <Menu
            onClick={(e) => {
              setIntervalSeconds(_.toNumber(e.key));
              window.localStorage.setItem('refresh-interval-seconds', _.toString(e.key));
            }}
          >
            {_.map(refreshMap, (text, value) => {
              return <Menu.Item key={value}>{text}</Menu.Item>;
            })}
          </Menu>
        }
      >
        <Button>
          {refreshMap[intervalSeconds]} <DownOutlined />
        </Button>
      </Dropdown>
    </div>
  );
}
Example #8
Source File: SearchableTree.tsx    From next-basics with GNU General Public License v3.0 5 votes vote down vote up
export function SearchableTree({
  list,
  defaultSelectedKeys,
  icon,
  field = "title",
  searchPlaceholder,
  onSelect,
  onQChange,
  customClassName,
}: SearchableTreeProps): React.ReactElement {
  const [q, setQ] = useState<string>("");
  const [selectedKeys, setSelectedKeys] = useState(defaultSelectedKeys);

  const handleSearch = (value: string): void => {
    setQ(value);
  };

  useEffect(() => {
    onQChange?.(q);
  }, [q]);

  const handleClick = (selectedKeys: React.Key[], value: any): void => {
    const selectedProps = value.node.props;
    const select = selectedProps.id ? [selectedProps.id] : [];
    setSelectedKeys(select);
    onSelect?.(selectedProps);
  };

  const titleRender = (nodeData: Record<string, any>): React.ReactElement => {
    const titleText = get(nodeData, field) as string;
    let title = <span>{titleText}</span>;
    if (q) {
      const trimQ = q.trim();
      const index = titleText.toLowerCase().indexOf(trimQ.toLowerCase());
      if (index !== -1) {
        const [beforeStr, matchStr, afterStr] = [
          titleText.substr(0, index),
          titleText.substr(index, trimQ.length),
          titleText.substr(index + trimQ.length),
        ];
        title = (
          <span>
            {beforeStr}
            {!!matchStr && (
              <span className={styles.matchedStr}>{matchStr}</span>
            )}
            {afterStr}
          </span>
        );
      }
    }
    return <span title={nodeData.path}>{title}</span>;
  };

  return (
    <div className={classNames(styles.container, customClassName)}>
      <SearchComponent
        placeholder={searchPlaceholder}
        onSearch={handleSearch}
      />
      {list?.length > 0 && (
        <div className={styles.treeWrapper}>
          <Tree
            showIcon
            defaultExpandAll={true}
            treeData={list as any}
            selectedKeys={selectedKeys}
            switcherIcon={<DownOutlined />}
            onSelect={handleClick}
            titleRender={titleRender}
            icon={icon}
            blockNode={true}
          ></Tree>
        </div>
      )}
    </div>
  );
}
Example #9
Source File: index.tsx    From ant-design-pro-V4 with MIT License 5 votes vote down vote up
Person = (props:any) => {
    console.log('props:',props);

    useEffect(()=>{
        //调用model,更新数据
        dispatch({
            type:'person/fetchPersons',
            payload:null
        })
    },[])
    const {dispatch} = props
    const personList = async () => {
        //发起请求获取数据
        // const data = await getPerson();
        // return {data}
    

        const data = props.person.persons
        return {data}
    }
    const actionRef = useRef<ActionType>();
    return (
        <PageContainer>
            <Card>
                <ProTable<TableListItem>
                    columns={columns}
                    dataSource={props.person.persons}
                    // request={(params, sorter, filter) =>
                    //     personList()
                    // }
                    rowKey="key"
                    pagination={{
                        showQuickJumper: true,
                    }}
                    search={{
                        // optionRender: false,
                        // collapsed: false,
                    }}
                    dateFormatter="string"
                    headerTitle="表格标题"
                    toolBarRender={() => [
                        <Button key="show">查看日志</Button>,
                        <Button key="out">
                            导出数据
                            <DownOutlined />
                        </Button>,
                        <Button type="primary" key="primary">
                            创建应用
                        </Button>,
                    ]}
                />
            </Card>
        </PageContainer>
    );
}
Example #10
Source File: index.tsx    From jetlinks-ui-antd with MIT License 5 votes vote down vote up
ArrayComponents = {
  CircleButton,
  TextButton,
  AdditionIcon: () => <PlusOutlined />,
  RemoveIcon: () => <DeleteOutlined />,
  MoveDownIcon: () => <DownOutlined />,
  MoveUpIcon: () => <UpOutlined />
}
Example #11
Source File: index.tsx    From fe-v5 with Apache License 2.0 5 votes vote down vote up
export default function index(props: IProps) {
  const { editable = true, value } = props;
  return (
    <Space align='baseline'>
      <Dropdown
        overlay={
          <Menu>
            {_.isEmpty(value) ? (
              <div style={{ textAlign: 'center' }}>暂无数据</div>
            ) : (
              _.map(value, (item, idx) => {
                return (
                  <Menu.Item key={idx}>
                    <a href={item.url} target={item.targetBlank ? '_blank' : '_self'}>
                      {item.title}
                    </a>
                  </Menu.Item>
                );
              })
            )}
          </Menu>
        }
      >
        <Button>
          大盘链接 <DownOutlined />
        </Button>
      </Dropdown>
      {editable && (
        <EditOutlined
          style={{ fontSize: 18 }}
          className='icon'
          onClick={() => {
            Edit({
              initialValues: value,
              onOk: (newValue) => {
                props.onChange(newValue);
              },
            });
          }}
        />
      )}
    </Space>
  );
}
Example #12
Source File: StatsCard.tsx    From condo with MIT License 5 votes vote down vote up
StatsCard: React.FC<IStatsCardProps> = (props) => {
    const intl = useIntl()
    const extraTitle = intl.formatMessage({ id: 'component.statscard.ExtraTitle' })
    const SELECTED_PERIOD: SelectedPeriod = {
        calendarWeek: intl.formatMessage({ id: 'component.statscard.periodtypes.Week' }),
        month: intl.formatMessage({ id: 'component.statscard.periodtypes.Month' }),
        quarter: intl.formatMessage({ id: 'component.statscard.periodtypes.Quarter' }),
        year: intl.formatMessage({ id: 'component.statscard.periodtypes.Year' }),
    }

    const { title, children, link, loading = false, onFilterChange, dependencyArray } = props

    const [selectedPeriod, setSelectedPeriod] = useState<string>(Object.keys(SELECTED_PERIOD)[0])
    const updateDependencies = [selectedPeriod, ...dependencyArray]

    useEffect(() => {
        onFilterChange(selectedPeriod)
    }, updateDependencies)

    const menuClick = useCallback(({ key }) => { setSelectedPeriod(key)}, [])
    const linkClick = useCallback(() => { Router.push(link) }, [link])

    const menuOverlay = (
        <Menu onClick={menuClick} disabled={loading}>
            {
                Object.keys(SELECTED_PERIOD).map((period) => (
                    <Menu.Item key={period}>{SELECTED_PERIOD[period]}</Menu.Item>
                ))
            }
        </Menu>
    )

    const cardTitle = (
        <Space css={cardTitleCss}>
            {title}
            <Dropdown overlay={menuOverlay} >
                <span style={DROPDOWN_TEXT_STYLE}>{SELECTED_PERIOD[selectedPeriod]} <DownOutlined /></span>
            </Dropdown>
        </Space>
    )

    const cardExtra = (
        <Button style={CARD_EXTRA_STYLE} type={'inlineLink'} onClick={linkClick}>
            {extraTitle}{<RightOutlined />}
        </Button>
    )

    return (
        <Row gutter={STATS_CARD_ROW_GUTTER} align={'middle'}>
            <Col span={24}>
                <Card
                    title={cardTitle}
                    bordered={false}
                    headStyle={CARD_HEAD_STYLE}
                    extra={cardExtra}
                >
                    {loading ? <Skeleton active round paragraph={{ rows: 1 }} /> : children}
                </Card>
            </Col>
        </Row>
    )
}
Example #13
Source File: index.tsx    From amiya with MIT License 5 votes vote down vote up
export default function AyCard(props: AyCardProps) {
  const { collapsible, onCollapse, defaultCollapsed, children, title, extra, collapsePosition = 'extra' } = props
  const [collapsed, setCollapsed] = useState<boolean>(props.collapsed || defaultCollapsed || false)
  const params = { ...props }

  delete params.collapsible
  delete params.defaultCollapsed
  delete params.collapsePosition
  delete params.collapsed
  delete params.onCollapse

  useEffect(() => {
    setCollapsed(props.collapsed || false)
  }, [props.collapsed])

  const toggleCollapsed = () => {
    setCollapsed(!collapsed)
    if (onCollapse) {
      onCollapse(!collapsed)
    }
  }

  const icon = collapsible ? (
    <DownOutlined className={`ay-card-collapsible-icon ${collapsePosition}`} onClick={toggleCollapsed} />
  ) : null

  return (
    <Card
      {...params}
      className={`ay-card ${collapsed ? 'collapsed' : ''}`}
      title={
        title ? (
          <>
            {collapsePosition === 'title' ? icon : null}
            {title}
          </>
        ) : null
      }
      extra={
        title ? (
          <>
            {extra}
            {collapsePosition === 'extra' ? icon : null}
          </>
        ) : null
      }
    >
      {children}
    </Card>
  )
}
Example #14
Source File: property.tsx    From LogicFlow with Apache License 2.0 5 votes vote down vote up
// @ts-ignore
export default function PropertyPanel(nodeData, updateproperty, hidePropertyPanel) {
  const getApproveList = () => {
    const approveUserOption: JSX.Element[] = []
    approveUser.forEach((item: IApproveUser) => {
      approveUserOption.push(<Select.Option value={item.value}>{item.label}</Select.Option>);
    });
    const approveSelect = <Form.Item className="form-property" label="审核节点类型" name="approveType">
      <Select>
        {approveUserOption}
    </Select>
    </Form.Item>
    return approveSelect;
  }
  const getApiUrl = () => {
    const Api = <Form.Item label="API" name="api">
      <Input />
    </Form.Item>
    return Api;
  }
  const onFormLayoutChange = (value: any, all: any) => {
    approveUser.forEach(item => {
      if (item.value === value.approveType) {
        value['approveTypeLabel'] = item.label;
      }
    })
    updateproperty(nodeData.id, value,);
  }
  return (
    <div>
      <h2>属性面板</h2>
      <Form
        key={nodeData.id}
        layout="inline"
        initialValues={nodeData.properties}
        onValuesChange={onFormLayoutChange}
      >
        <span className="form-property">类型:<span>{nodeData.type}</span></span>
        <span className="form-property">文案:<span>{nodeData.text?.value}</span></span>
        {nodeData.type==="approver" ? getApproveList() : ''}
        {nodeData.type === "jugement" ? getApiUrl() : ''}
      </Form>
      <div>
        <h3>......</h3>
        <h3>业务属性可根据需要进行自定义扩展</h3>
      </div>
      <div className="property-panel-footer">
        <Button
          className="property-panel-footer-hide"
          type="primary"
          icon={<DownOutlined/>}
          onClick={hidePropertyPanel}>
          收起
        </Button>
      </div>
    </div>
  )
}
Example #15
Source File: TeamMembers.tsx    From posthog-foss with MIT License 5 votes vote down vote up
function LevelComponent(member: FusedTeamMemberType): JSX.Element | null {
    const { user } = useValues(userLogic)
    const { currentTeam } = useValues(teamLogic)
    const { changeUserAccessLevel } = useActions(teamMembersLogic)

    const myMembershipLevel = currentTeam ? currentTeam.effective_membership_level : null

    if (!user) {
        return null
    }

    function generateHandleClick(listLevel: TeamMembershipLevel): (event: React.MouseEvent) => void {
        return function handleClick(event: React.MouseEvent) {
            event.preventDefault()
            changeUserAccessLevel(member.user, listLevel)
        }
    }

    const isImplicit = member.organization_level >= OrganizationMembershipLevel.Admin
    const levelName = membershipLevelToName.get(member.level) ?? `unknown (${member.level})`

    const levelButton = (
        <Button
            data-attr="change-membership-level"
            icon={member.level === OrganizationMembershipLevel.Owner ? <CrownFilled /> : undefined}
            // Org admins have implicit access anyway, so it doesn't make sense to edit them
            disabled={isImplicit}
        >
            {levelName}
        </Button>
    )

    const allowedLevels = teamMembershipLevelIntegers.filter(
        (listLevel) => !getReasonForAccessLevelChangeProhibition(myMembershipLevel, user, member, listLevel)
    )
    const disallowedReason = isImplicit
        ? `This user is a member of the project implicitly due to being an organization ${levelName}.`
        : getReasonForAccessLevelChangeProhibition(myMembershipLevel, user, member, allowedLevels)

    return disallowedReason ? (
        <Tooltip title={disallowedReason}>{levelButton}</Tooltip>
    ) : (
        <Dropdown
            overlay={
                <Menu>
                    {allowedLevels.map((listLevel) => (
                        <Menu.Item key={`${member.user.uuid}-level-${listLevel}`}>
                            <a href="#" onClick={generateHandleClick(listLevel)} data-test-level={listLevel}>
                                {listLevel > member.level ? (
                                    <>
                                        <UpOutlined style={{ marginRight: '0.5rem' }} />
                                        Upgrade to project {membershipLevelToName.get(listLevel)}
                                    </>
                                ) : (
                                    <>
                                        <DownOutlined style={{ marginRight: '0.5rem' }} />
                                        Downgrade to project {membershipLevelToName.get(listLevel)}
                                    </>
                                )}
                            </a>
                        </Menu.Item>
                    ))}
                </Menu>
            }
        >
            {levelButton}
        </Dropdown>
    )
}
Example #16
Source File: index.tsx    From fe-v5 with Apache License 2.0 4 votes vote down vote up
index = (_props: any) => {
  const { t, i18n } = useTranslation();
  const searchRef = useRef<Input>(null);
  const [query, setQuery] = useState('');
  const { curBusiItem } = useSelector<RootState, CommonStoreState>((state) => state.common);
  const busiId = curBusiItem.id;
  const [selectedIds, setSelectedIds] = useState([] as any[]);
  const { tableProps, refresh } = useAntdTable<any, any>((options) => getTableData(options, busiId, query), { refreshDeps: [busiId, query] });

  function handleTagClick(tag: string) {
    if (!_.includes(query, tag)) {
      const newQuery = query ? `${query} ${tag}` : tag;
      setQuery(newQuery);
      searchRef.current?.setValue(newQuery);
    }
  }

  function handleDelBtnClick(id: number) {
    if (busiId) {
      request(`${api.tasktpl(busiId)}/${id}`, {
        method: 'DELETE',
      }).then(() => {
        message.success(t('msg.delete.success'));
        refresh();
      });
    }
  }

  function handleBatchBindTags() {
    if (!_.isEmpty(selectedIds)) {
      BindTags({
        language: i18n.language,
        selectedIds,
        busiId,
        onOk: () => {
          refresh();
        },
      });
    }
  }

  function handleBatchUnBindTags() {
    if (!_.isEmpty(selectedIds)) {
      let uniqueTags = [] as any[];
      _.each(tableProps.dataSource, (item) => {
        const tags = item.tags;
        uniqueTags = _.union(uniqueTags, tags);
      });
      UnBindTags({
        language: i18n.language,
        selectedIds,
        uniqueTags,
        busiId,
        onOk: () => {
          refresh();
        },
      });
    }
  }

  const columns: ColumnProps<Tpl>[] = [
    {
      title: 'ID',
      dataIndex: 'id',
    },
    {
      title: t('tpl.title'),
      dataIndex: 'title',
      render: (text, record) => {
        return <Link to={{ pathname: `/job-tpls/${record.id}/detail` }}>{text}</Link>;
      },
    },
    {
      title: t('tpl.tags'),
      dataIndex: 'tags',
      render: (text) => {
        return _.map(text, (item) => (
          <Tag color='blue' key={item} onClick={() => handleTagClick(item)}>
            {item}
          </Tag>
        ));
      },
    },
    {
      title: t('tpl.creator'),
      dataIndex: 'create_by',
      width: 100,
    },
    {
      title: t('tpl.last_updated'),
      dataIndex: 'update_at',
      width: 160,
      render: (text) => {
        return moment.unix(text).format('YYYY-MM-DD HH:mm:ss');
      },
    },
    {
      title: t('table.operations'),
      width: 220,
      render: (_text, record) => {
        return (
          <span>
            <Link to={{ pathname: `/job-tpls/add/task`, search: `tpl=${record.id}` }}>{t('task.create')}</Link>
            <Divider type='vertical' />
            <Link to={{ pathname: `/job-tpls/${record.id}/modify` }}>{t('table.modify')}</Link>
            <Divider type='vertical' />
            <Link to={{ pathname: `/job-tpls/${record.id}/clone` }}>{t('table.clone')}</Link>
            <Divider type='vertical' />
            <Popconfirm
              title={<div style={{ width: 100 }}>{t('table.delete.sure')}</div>}
              onConfirm={() => {
                handleDelBtnClick(record.id);
              }}
            >
              <a style={{ color: 'red' }}>{t('table.delete')}</a>
            </Popconfirm>
          </span>
        );
      },
    },
  ];
  return (
    <PageLayout
      hideCluster
      title={
        <>
          <CodeOutlined />
          {t('自愈脚本')}
        </>
      }
    >
      <div style={{ display: 'flex' }}>
        <LeftTree></LeftTree>
        {busiId ? (
          <div style={{ flex: 1, padding: 20 }}>
            <Row>
              <Col span={14} className='mb10'>
                <Input
                  style={{ width: 200 }}
                  ref={searchRef}
                  prefix={<SearchOutlined />}
                  defaultValue={query}
                  onPressEnter={(e) => {
                    setQuery(e.currentTarget.value);
                  }}
                  placeholder='搜索标题、标签'
                />
              </Col>
              <Col span={10} className='textAlignRight'>
                <Link to={{ pathname: `/job-tpls/add` }}>
                  <Button icon={<PlusOutlined />} style={{ marginRight: 10 }} type='primary' ghost>
                    {t('tpl.create')}
                  </Button>
                </Link>
                <Dropdown
                  overlay={
                    <Menu>
                      <Menu.Item>
                        <Button
                          type='link'
                          disabled={selectedIds.length === 0}
                          onClick={() => {
                            handleBatchBindTags();
                          }}
                        >
                          {t('tpl.tag.bind')}
                        </Button>
                      </Menu.Item>
                      <Menu.Item>
                        <Button
                          type='link'
                          disabled={selectedIds.length === 0}
                          onClick={() => {
                            handleBatchUnBindTags();
                          }}
                        >
                          {t('tpl.tag.unbind')}
                        </Button>
                      </Menu.Item>
                    </Menu>
                  }
                >
                  <Button icon={<DownOutlined />}>{t('table.batch.operations')}</Button>
                </Dropdown>
              </Col>
            </Row>
            <Table
              rowKey='id'
              columns={columns}
              {...(tableProps as any)}
              rowSelection={{
                selectedRowKeys: selectedIds,
                onChange: (selectedRowKeys) => {
                  setSelectedIds(selectedRowKeys);
                },
              }}
              pagination={
                {
                  ...tableProps.pagination,
                  showSizeChanger: true,
                  pageSizeOptions: ['10', '50', '100', '500', '1000'],
                  showTotal: (total) => {
                    return i18n.language == 'en' ? `Total ${total} items` : `共 ${total} 条`;
                  },
                } as any
              }
            />
          </div>
        ) : (
          <BlankBusinessPlaceholder text='自愈脚本' />
        )}
      </div>
    </PageLayout>
  );
}
Example #17
Source File: TicketChartView.tsx    From condo with MIT License 4 votes vote down vote up
TicketChartView: React.FC<ITicketAnalyticsPageChartProps> = (props) => {
    const {
        children,
        data,
        viewMode,
        loading = false,
        onChartReady,
        chartConfig,
        mapperInstance,
        mainGroup = 'ticket',
    } = props

    const intl = useIntl()
    const NoData = intl.formatMessage({ id: 'NoData' })
    const LoadMoreTitle = intl.formatMessage(
        { id: 'pages.condo.analytics.TicketAnalyticsPage.TicketChartView.LoadMoreTitle' },
        { entity: intl.formatMessage({ id: `component.TicketWarningModal.Entity.${mainGroup}` }) }
    )

    let legend = [], tooltip = null, color = CHART_COLOR_SET
    const [chartReadyCounter, setChartReadyCounter] = useState<number>(0)
    // Start from 1 because used as multiplier with TICKET_CHART_PAGE_SIZE
    const [chartPage, setChartPage] = useState<number>(1)
    // Store pie chart refs because we need to access api for every chart component
    const chartRefs = useRef([])
    // Cache series result for client side paging
    const seriesRef = useRef<EchartsSeries[]>([])
    const seriesCacheRef = useRef<EchartsSeries[]>([])
    const axisDataRef = useRef<ChartConfigResult['axisData'] | null>(null)

    if (data !== null) {
        const mapperResult = mapperInstance.getChartConfig(viewMode, data)
        seriesCacheRef.current = mapperResult.series
        const pageSize = TICKET_CHART_PAGE_SIZE * chartPage
        seriesRef.current = mapperResult.series.slice(0, pageSize)
        // If component used as report widget, we should use page separation. Otherwise we load all data (for pdf page)
        legend = mapperResult.legend
        axisDataRef.current = mapperResult.axisData
        tooltip = mapperResult.tooltip
        if (mapperResult.color) {
            color = mapperResult.color
        }
    }

    useEffect(() => {
        setChartPage(1)
    }, [viewMode, mainGroup])

    useEffect(() => {
        // Clean chart refs if input data was changed
        if (viewMode === 'pie') {
            if (seriesRef.current.length !== chartRefs.current.length) {
                // It means that filters were changed & total chart count may be changed too, so just reset refs & page
                setChartReadyCounter(0)
                setChartPage(1)
            }
            chartRefs.current = chartRefs.current.slice(0, seriesRef.current.length)
        }
    }, [data, viewMode])

    // Way to await moment when all pie chart instance were rendered (needed for client side pdf generation)
    useLayoutEffect(() => {
        if (viewMode === 'pie' && onChartReady !== undefined) {
            if (seriesRef.current.length !== 0 && seriesCacheRef.current.length !== 0 && seriesRef.current.length === seriesCacheRef.current.length) {
                onChartReady()
                return
            }

            if (chartReadyCounter < seriesRef.current.length) {
                let animationFrameId
                const nextAnimationFrame = () => {
                    setChartPage(pieChartPage => pieChartPage + 1)
                    animationFrameId = requestAnimationFrame(nextAnimationFrame)
                }

                animationFrameId = requestAnimationFrame(nextAnimationFrame)
                return () => cancelAnimationFrame(animationFrameId)
            }
        }
    }, [chartReadyCounter])

    const loadMore = useCallback(() => { setChartPage(chartPage + 1) }, [chartPage])

    if (data === null || loading) {
        return <Skeleton loading={loading} active paragraph={{ rows: 12 }} />
    }
    const { animationEnabled, chartOptions } = chartConfig
    const isEmptyDataSet = data.every(ticketStatus => ticketStatus.count === 0)

    if (viewMode !== 'pie') {
        const pieChartDataStep = chartPage * TICKET_CHART_PAGE_SIZE
        const axisData = axisDataRef.current
        const hasMore = axisData !== null && pieChartDataStep < get(axisData, 'yAxis.data.length', 0)
        const barChartPagedRenderEnabled = animationEnabled && hasMore && viewMode === 'bar'

        if (barChartPagedRenderEnabled) {
            axisData['yAxis']['data'] = get(axisData, 'yAxis.data', []).slice(-pieChartDataStep)
            seriesCacheRef.current.forEach(series => {
                series.data = series.data.slice(-pieChartDataStep)
            })
        }
        const { opts, option } = getChartOptions({
            legend,
            axisData,
            tooltip,
            series: seriesCacheRef.current,
            chartOptions,
            viewMode,
            animationEnabled,
            color,
        })
        const chartHeight = get(opts, 'height', 'auto')
        const chartStyle = {
            height: chartHeight,
        }

        return <ChartViewContainer>
            {isEmptyDataSet ? (
                <>
                    <BasicEmptyListView>
                        <Typography.Text>{NoData}</Typography.Text>
                    </BasicEmptyListView>
                    {children}
                </>
            ) : (
                <>
                    <ReactECharts
                        opts={opts}
                        onChartReady={onChartReady}
                        notMerge
                        style={{ ...chartStyle }}
                        option={option}/>
                    {barChartPagedRenderEnabled ? (
                        <Button style={{ width: '100%', marginTop: 16 }} type={'ghost'} onClick={loadMore}>
                            {LoadMoreTitle} <DownOutlined />
                        </Button>
                    ) : null}
                    {children}
                </>
            )}
        </ChartViewContainer>
    }

    const hasMore = chartPage * TICKET_CHART_PAGE_SIZE <= seriesRef.current.length
    let infiniteScrollContainerHeight = seriesRef.current.length > 2 ? '660px' : '340px'
    // onChartReadyCallback is used only for pdf generation page to make sure that chart component was rendered at DOM
    if (onChartReady !== undefined) {
        infiniteScrollContainerHeight = '100%'
    }

    return <ChartViewContainer>
        {isEmptyDataSet ? (
            <>
                <BasicEmptyListView>
                    <Typography.Text>{NoData}</Typography.Text>
                </BasicEmptyListView>
                {children}
            </>
        ) : (
            <>
                <ReactECharts
                    onEvents={{
                        legendselectchanged: function (params) {
                            chartRefs.current.forEach(chartRef => {
                                const chartInstance = chartRef.getEchartsInstance()
                                chartInstance._api.dispatchAction({
                                    type: 'legendToggleSelect',
                                    name: params.name,
                                })
                            })
                        },
                    }}
                    opts={{ renderer: 'svg' }}
                    notMerge
                    option={
                        getChartOptions({
                            legend,
                            color,
                            series: [{ ...seriesRef.current[0], top: -1000, left: -1000, labelLayout: {}, label: { show: false } }],
                            viewMode: 'pie',
                            animationEnabled,
                            chartOptions: { renderer: 'svg' },
                            showTitle: false,
                        }).option
                    }
                    style={{ height: 40, overflow: 'hidden' }}
                />
                <ScrollContainer height={infiniteScrollContainerHeight}>
                    <InfiniteScroll
                        initialLoad={false}
                        loadMore={loadMore}
                        hasMore={hasMore}
                        useWindow={false}>
                        <List
                            grid={{ gutter: 24, xs: 1, sm: 1, md: 1, lg: 1, xl: 2, xxl: 2 }}
                            dataSource={seriesRef.current}
                            renderItem={(chartSeries, index) => {
                                const { option, opts } = getChartOptions({
                                    series: [chartSeries],
                                    legend,
                                    viewMode,
                                    chartOptions,
                                    animationEnabled,
                                    color,
                                })
                                return (
                                    <List.Item key={`pie-${index}`} style={{ width: 620 }}>
                                        <ReactECharts
                                            ref={element => chartRefs.current[index] = element}
                                            opts={opts}
                                            onChartReady={() => setChartReadyCounter(chartReadyCounter + 1)}
                                            notMerge
                                            style={{
                                                border: '1px solid',
                                                borderColor: colors.lightGrey[6],
                                                borderRadius: 8,
                                            }}
                                            option={option}
                                        />
                                    </List.Item>
                                )
                            }}
                            style={{ paddingRight: 20, width: '100%' }}
                        />
                    </InfiniteScroll>
                </ScrollContainer>
                {children}
            </>
        )}
    </ChartViewContainer>
}
Example #18
Source File: VerificationPanel.tsx    From posthog-foss with MIT License 4 votes vote down vote up
export function VerificationPanel(): JSX.Element {
    const { loadCurrentTeam } = useActions(teamLogic)
    const { currentTeam } = useValues(teamLogic)
    const { setVerify, completeOnboarding } = useActions(ingestionLogic)
    const { index, totalSteps } = useValues(ingestionLogic)
    const [isPopConfirmShowing, setPopConfirmShowing] = useState(false)
    const [isHelpMenuShowing, setHelpMenuShowing] = useState(false)
    const { showInviteModal } = useActions(inviteLogic)

    useInterval(() => {
        if (!currentTeam?.ingested_event && !isPopConfirmShowing && !isHelpMenuShowing) {
            loadCurrentTeam()
        }
    }, 2000)

    function HelperButtonRow(): JSX.Element {
        function HelpButton(): JSX.Element {
            const menu = (
                <Menu selectable>
                    <Menu.Item key="0" data-attr="ingestion-help-item-docs">
                        <a href="https://posthog.com/docs/integrate/ingest-live-data" target="_blank">
                            <Button type="link">
                                <ReadOutlined />
                                Read the ingestion docs
                            </Button>
                        </a>
                    </Menu.Item>
                    <Menu.Item key="1" data-attr="ingestion-help-item-invite">
                        <Button type="link" onClick={showInviteModal}>
                            <UserAddOutlined />
                            Invite team member
                        </Button>
                    </Menu.Item>
                    <Menu.Item key="2" data-attr="ingestion-help-item-slack">
                        <a href="https://posthog.com/slack?s=app" target="_blank">
                            <Button type="link">
                                <SlackSquareOutlined />
                                Ask us in Slack
                            </Button>
                        </a>
                    </Menu.Item>
                </Menu>
            )
            return (
                <Dropdown
                    overlay={menu}
                    trigger={['click']}
                    visible={isHelpMenuShowing}
                    onVisibleChange={(v) => {
                        setHelpMenuShowing(v)
                    }}
                >
                    <Button type="primary" data-attr="ingestion-help-button" onClick={() => setHelpMenuShowing(true)}>
                        Need help? <DownOutlined />
                    </Button>
                </Dropdown>
            )
        }

        const popoverTitle = (
            <Space direction="vertical">
                <Text strong>Are you sure you want to continue without data?</Text>
                <Text>You won't be able to conduct analysis or use most tools without event data.</Text>
                <Text>For the best experience, we recommend adding event data before continuing.</Text>
            </Space>
        )
        return (
            <Space style={{ float: 'right' }}>
                <Popconfirm
                    title={popoverTitle}
                    okText="Yes, I know what I'm doing."
                    okType="danger"
                    cancelText="No, go back."
                    onVisibleChange={(v) => {
                        setPopConfirmShowing(v)
                    }}
                    onCancel={() => {
                        setPopConfirmShowing(false)
                    }}
                    visible={isPopConfirmShowing}
                    onConfirm={completeOnboarding}
                    cancelButtonProps={{ type: 'primary' }}
                >
                    <Button
                        type="dashed"
                        data-attr="ingestion-continue-anyway"
                        onClick={() => {
                            setPopConfirmShowing(true)
                        }}
                    >
                        Continue without verifying
                    </Button>
                </Popconfirm>
                <HelpButton />
            </Space>
        )
    }

    return (
        <CardContainer index={index} totalSteps={totalSteps} onBack={() => setVerify(false)}>
            {!currentTeam?.ingested_event ? (
                <>
                    <Row className="flex-center">
                        <Spinner style={{ marginRight: 4 }} />
                        <h2 className="ml-3" style={{ marginBottom: 0, color: 'var(--primary-alt)' }}>
                            Listening for events...
                        </h2>
                    </Row>
                    <p className="prompt-text mt-05">
                        Once you have integrated the snippet and sent an event, we will verify it was properly received
                        and continue.
                    </p>
                    <HelperButtonRow />
                </>
            ) : (
                <>
                    <h2>Successfully sent events!</h2>
                    <p className="prompt-text">
                        You will now be able to explore PostHog and take advantage of all its features to understand
                        your users.
                    </p>
                    <Button
                        data-attr="wizard-complete-button"
                        type="primary"
                        style={{ float: 'right' }}
                        onClick={completeOnboarding}
                    >
                        Complete
                    </Button>
                </>
            )}
        </CardContainer>
    )
}
Example #19
Source File: PageTable.tsx    From fe-v5 with Apache License 2.0 4 votes vote down vote up
PageTable: React.FC<Props> = ({ bgid }) => {
  const [severity, setSeverity] = useState<number>();
  const [clusters, setClusters] = useState<string[]>([]);
  const { t, i18n } = useTranslation();
  const history = useHistory();
  const [modalType, setModalType] = useState<ModalStatus>(ModalStatus.None);
  const [selectRowKeys, setSelectRowKeys] = useState<React.Key[]>([]);
  const [selectedRows, setSelectedRows] = useState<strategyItem[]>([]);
  const [exportData, setExportData] = useState<string>('');
  const { curBusiItem } = useSelector<RootState, CommonStoreState>((state) => state.common);

  const [query, setQuery] = useState<string>('');
  const [isModalVisible, setisModalVisible] = useState<boolean>(false);

  const [currentStrategyDataAll, setCurrentStrategyDataAll] = useState([]);
  const [currentStrategyData, setCurrentStrategyData] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    if (bgid) {
      getAlertRules();
    }
  }, [bgid, severity]);

  useEffect(() => {
    filterData();
  }, [query, clusters, currentStrategyDataAll]);

  const getAlertRules = async () => {
    if (!bgid) {
      return;
    }
    setLoading(true);
    const { success, dat } = await getStrategyGroupSubList({ id: bgid });
    if (success) {
      setCurrentStrategyDataAll(dat.filter((item) => !severity || item.severity === severity) || []);
      setLoading(false);
    }
  };

  const filterData = () => {
    const data = JSON.parse(JSON.stringify(currentStrategyDataAll));
    const res = data.filter((item) => {
      const lowerCaseQuery = query.toLowerCase();
      return (
        (item.name.toLowerCase().indexOf(lowerCaseQuery) > -1 || item.append_tags.join(' ').toLowerCase().indexOf(lowerCaseQuery) > -1) &&
        ((clusters && clusters?.indexOf(item.cluster) > -1) || clusters?.length === 0)
      );
    });
    setCurrentStrategyData(res || []);
  };

  const goToAddWarningStrategy = () => {
    curBusiItem?.id && history.push(`/alert-rules/add/${curBusiItem.id}`);
  };

  const handleClickEdit = (id, isClone = false) => {
    curBusiItem?.id && history.push(`/alert-rules/edit/${id}${isClone ? '?mode=clone' : ''}`);
  };

  const refreshList = () => {
    getAlertRules();
  };

  const columns: ColumnType<strategyItem>[] = [
    {
      title: t('集群'),
      dataIndex: 'cluster',
      render: (data) => {
        return <div>{data}</div>;
      },
    },
    {
      title: t('级别'),
      dataIndex: 'severity',
      render: (data) => {
        return <Tag color={priorityColor[data - 1]}>S{data}</Tag>;
      },
    },
    {
      title: t('名称'),
      dataIndex: 'name',
      render: (data, record) => {
        return (
          <div
            className='table-active-text'
            onClick={() => {
              handleClickEdit(record.id);
            }}
          >
            {data}
          </div>
        );
      },
    },
    {
      title: t('告警接收者'),
      dataIndex: 'notify_groups_obj',
      width: 100,
      render: (data, record) => {
        return (
          (data.length &&
            data.map(
              (
                user: {
                  nickname: string;
                  username: string;
                } & { name: string },
                index: number,
              ) => {
                return <ColorTag text={user.nickname || user.username || user.name} key={index}></ColorTag>;
              },
            )) || <div></div>
        );
      },
    },
    {
      title: t('附加标签'),
      dataIndex: 'append_tags',
      render: (data) => {
        const array = data || [];
        return (
          (array.length &&
            array.map((tag: string, index: number) => {
              return <ColorTag text={tag} key={index}></ColorTag>;
            })) || <div></div>
        );
      },
    },
    {
      title: t('更新时间'),
      dataIndex: 'update_at',
      width: 120,
      render: (text: string) => dayjs(Number(text) * 1000).format('YYYY-MM-DD HH:mm:ss'),
    },
    {
      title: t('启用'),
      dataIndex: 'disabled',
      render: (disabled, record) => (
        <Switch
          checked={disabled === strategyStatus.Enable}
          size='small'
          onChange={() => {
            const { id, disabled } = record;
            updateAlertRules(
              {
                ids: [id],
                fields: {
                  disabled: !disabled ? 1 : 0,
                },
              },
              curBusiItem.id,
            ).then(() => {
              refreshList();
            });
          }}
        />
      ),
    },
    {
      title: t('操作'),
      dataIndex: 'operator',
      width: 100,
      render: (data, record) => {
        return (
          <div className='table-operator-area'>
            <div
              className='table-operator-area-normal'
              onClick={() => {
                handleClickEdit(record.id, true);
              }}
            >
              {t('克隆')}
            </div>
            <div
              className='table-operator-area-warning'
              onClick={() => {
                confirm({
                  title: t('是否删除该告警规则?'),
                  onOk: () => {
                    deleteStrategy([record.id], curBusiItem.id).then(() => {
                      message.success(t('删除成功'));
                      refreshList();
                    });
                  },

                  onCancel() {},
                });
              }}
            >
              {t('删除')}
            </div>
          </div>
        );
      },
    },
  ];

  const toOneArr = (arr, res, name) => {
    arr.forEach((ele) => {
      if (Array.isArray(ele)) {
        toOneArr(ele, res, name);
      } else {
        res.push(ele[name]);
      }
    });
  };
  const openModel = (title, item) => {
    if (selectRowKeys.length == 0) {
      message.warning(t('请先选择策略'));
      return;
    }
    setisModalVisible(true);
  };

  const menu = useMemo(() => {
    return (
      <ul className='ant-dropdown-menu'>
        <li className='ant-dropdown-menu-item' onClick={() => setModalType(ModalStatus.BuiltIn)}>
          <span>{t('导入告警规则')}</span>
        </li>
        <li
          className='ant-dropdown-menu-item'
          onClick={() => {
            if (selectedRows.length) {
              const exportData = selectedRows.map((item) => {
                return { ...item, ...exportIgnoreAttrsObj };
              });
              setExportData(JSON.stringify(exportData, null, 2));
              setModalType(ModalStatus.Export);
            } else {
              message.warning(t('未选择任何规则'));
            }
          }}
        >
          <span>{t('导出告警规则')}</span>
        </li>
        <li
          className='ant-dropdown-menu-item'
          onClick={() => {
            if (selectRowKeys.length) {
              confirm({
                title: t('是否批量删除告警规则?'),
                onOk: () => {
                  deleteStrategy(selectRowKeys as number[], curBusiItem?.id).then(() => {
                    message.success(t('删除成功'));
                    refreshList();
                  });
                },

                onCancel() {},
              });
            } else {
              message.warning(t('未选择任何规则'));
            }
          }}
        >
          <span>{t('批量删除规则')}</span>
        </li>
        <li
          className='ant-dropdown-menu-item'
          onClick={() => {
            openModel(t('批量更新规则'), 1);
          }}
        >
          <span>{t('批量更新规则')}</span>
        </li>
      </ul>
    );
  }, [selectRowKeys, t]);

  const handleImportStrategy = async (data) => {
    const { dat } = await addOrEditStrategy(data, curBusiItem.id, 'Post');
    return dat || {};
  };

  const editModalFinish = async (isOk, fieldsData?) => {
    if (isOk) {
      const res = await updateAlertRules(
        {
          ids: selectRowKeys,
          fields: fieldsData,
        },
        curBusiItem.id,
      );
      if (!res.err) {
        message.success('修改成功!');
        refreshList();
        setisModalVisible(false);
      } else {
        message.error(res.err);
      }
    } else {
      setisModalVisible(false);
    }
  };

  return (
    <div className='strategy-table-content'>
      <div className='strategy-table-search table-handle'>
        <div className='strategy-table-search-left'>
          <RefreshIcon
            className='strategy-table-search-left-refresh'
            onClick={() => {
              refreshList();
            }}
          />
          <ColumnSelect noLeftPadding noRightPadding={false} onSeverityChange={(e) => setSeverity(e)} onClusterChange={(e) => setClusters(e)} />
          <SearchInput className={'searchInput'} placeholder={t('搜索名称或标签')} onSearch={setQuery} allowClear />
        </div>
        <div className='strategy-table-search-right'>
          <Button type='primary' onClick={goToAddWarningStrategy} className='strategy-table-search-right-create' ghost>
            {t('新增告警规则')}
          </Button>
          <div className={'table-more-options'}>
            <Dropdown overlay={menu} trigger={['click']}>
              <Button onClick={(e) => e.stopPropagation()}>
                {t('更多操作')}
                <DownOutlined
                  style={{
                    marginLeft: 2,
                  }}
                />
              </Button>
            </Dropdown>
          </div>
        </div>
      </div>

      <Table
        rowKey='id'
        // sticky
        pagination={{
          total: currentStrategyData.length,
          showQuickJumper: true,
          showSizeChanger: true,
          showTotal: (total) => {
            return `共 ${total} 条数据`;
          },
          pageSizeOptions: pageSizeOptionsDefault,
          defaultPageSize: 30,
        }}
        loading={loading}
        dataSource={currentStrategyData}
        rowSelection={{
          selectedRowKeys: selectedRows.map((item) => item.id),
          onChange: (selectedRowKeys: React.Key[], selectedRows: strategyItem[]) => {
            setSelectRowKeys(selectedRowKeys);
            setSelectedRows(selectedRows);
          },
        }}
        columns={columns}
      />
      <ImportAndDownloadModal
        bgid={bgid}
        status={modalType}
        fetchBuiltinFunc={getBuiltinAlerts}
        submitBuiltinFunc={createBuiltinAlerts}
        onClose={() => {
          setModalType(ModalStatus.None);
        }}
        onSuccess={() => {
          getAlertRules();
        }}
        onSubmit={handleImportStrategy}
        label='告警规则'
        title={
          ModalStatus.Export === modalType ? (
            '告警规则'
          ) : (
            <Tabs defaultActiveKey={ModalStatus.BuiltIn} onChange={(e: ModalStatus) => setModalType(e)} className='custom-import-alert-title'>
              <TabPane tab=' 导入内置告警规则' key={ModalStatus.BuiltIn}></TabPane>
              <TabPane tab='导入告警规则JSON' key={ModalStatus.Import}></TabPane>
            </Tabs>
          )
        }
        exportData={exportData}
      />
      {isModalVisible && <EditModal isModalVisible={isModalVisible} editModalFinish={editModalFinish} />}
    </div>
  );
}
Example #20
Source File: Telemetry.tsx    From nanolooker with MIT License 4 votes vote down vote up
Telemetry: React.FC = () => {
  const { t } = useTranslation();
  const [currentPercentile, setCurrentPercentile] = React.useState(
    Percentiles.P95,
  );
  const {
    telemetry,
    versions,
    status: { nodeCount, bandwidthCapGroups, date } = {},
    isLoading: isTelemetryLoading,
  } = useTelemetry();
  const [
    formattedMedianBandwidthCap,
    setFormattedMedianBandwidthCap,
  ] = React.useState(formatBytes(0));
  const [
    unlimitedBandwidthCapCount,
    setUnlimitedBandwidthCapCount,
  ] = React.useState<number>();
  const [
    limitedBandwidthCapCount,
    setLimitedBandwidthCapCount,
  ] = React.useState<number>();
  const [limitedBandwidthCap, setLimitedBandwidthCap] = React.useState(
    formatBytes(0),
  );

  React.useEffect(() => {
    if (!telemetry[currentPercentile]) return;
    setFormattedMedianBandwidthCap(
      formatBytes(telemetry[currentPercentile].bandwidthCap),
    );
  }, [telemetry, currentPercentile]);

  const onPercentileClick = ({ key }: any) => {
    setCurrentPercentile(key);
  };

  React.useEffect(() => {
    if (!bandwidthCapGroups) return;
    setUnlimitedBandwidthCapCount(bandwidthCapGroups[0].count);
    setLimitedBandwidthCapCount(bandwidthCapGroups[1]?.count);
    setLimitedBandwidthCap(formatBytes(bandwidthCapGroups[1]?.bandwidthCap));
  }, [bandwidthCapGroups]);

  return (
    <Row gutter={12}>
      <Col xs={24} md={16}>
        <div
          style={{
            display: "flex",
            alignItems: "baseline",
            marginBottom: "12px",
          }}
        >
          <Title level={3} style={{ margin: 0 }}>
            {t("pages.status.telemetry")}
          </Title>
          <div style={{ marginLeft: "12px" }}>
            <Dropdown
              overlay={
                <Menu onClick={onPercentileClick}>
                  {Object.values(Percentiles).map(percentile => (
                    <Menu.Item key={percentile}>{percentile}</Menu.Item>
                  ))}
                </Menu>
              }
            >
              <Button>
                {currentPercentile} <DownOutlined />
              </Button>
            </Dropdown>
          </div>
          <Tooltip placement="right" title={t("tooltips.telemetry")}>
            <QuestionCircle />
          </Tooltip>
        </div>

        <Card
          size="small"
          bordered={false}
          className="detail-layout"
          style={{ marginBottom: "12px" }}
        >
          <div style={{ marginBottom: "12px", fontSize: "12px" }}>
            {date ? (
              <>
                {t("common.executionTimeAgo")}{" "}
                <TimeAgo
                  locale={i18next.language}
                  datetime={date}
                  live={false}
                  style={{ fontWeight: "bold" }}
                />
              </>
            ) : null}
            {nodeCount ? (
              <>
                <br />
                <Trans i18nKey="pages.status.nodeCount">
                  <strong>{{ nodeCount }}</strong>
                </Trans>
              </>
            ) : null}
            {unlimitedBandwidthCapCount ? (
              <>
                <br />
                <Trans i18nKey="pages.status.unlimitedBandwidthCap">
                  <strong>{{ unlimitedBandwidthCapCount }}</strong>
                </Trans>
              </>
            ) : null}
            {limitedBandwidthCapCount && limitedBandwidthCap.value ? (
              <>
                {" "}
                <Trans i18nKey="pages.status.limitedBandwidthCap">
                  <strong>{{ limitedBandwidthCapCount }}</strong>
                  <strong>
                    {{
                      limitedBandwidthCap: `${limitedBandwidthCap.value} ${limitedBandwidthCap.suffix}`,
                    }}
                  </strong>
                </Trans>
              </>
            ) : null}
          </div>
          <Row gutter={6}>
            <Col xs={12} lg={8} xl={6} style={{ margin: "12px 0" }}>
              <LoadingStatistic
                isLoading={isTelemetryLoading}
                title={t("common.blocks")}
                value={telemetry[currentPercentile]?.blockCount}
              />
            </Col>
            <Col xs={12} lg={8} xl={6} style={{ margin: "12px 0" }}>
              <LoadingStatistic
                isLoading={isTelemetryLoading}
                title={t("pages.status.cemented")}
                tooltip={t("tooltips.cemented")}
                value={telemetry[currentPercentile]?.cementedCount}
              />
            </Col>
            <Col xs={12} lg={8} xl={6} style={{ margin: "12px 0" }}>
              <LoadingStatistic
                isLoading={isTelemetryLoading}
                title={t("pages.status.unchecked")}
                tooltip={t("tooltips.unchecked")}
                value={telemetry[currentPercentile]?.uncheckedCount}
              />
            </Col>
            <Col xs={12} lg={8} xl={6} style={{ margin: "12px 0" }}>
              <LoadingStatistic
                isLoading={isTelemetryLoading}
                title={t("common.accounts")}
                value={telemetry[currentPercentile]?.accountCount}
              />
            </Col>
            <Col xs={12} lg={8} xl={6} style={{ margin: "12px 0" }}>
              <LoadingStatistic
                isLoading={isTelemetryLoading}
                title={t("pages.status.bandwidthCap")}
                tooltip={t("tooltips.bandwidthCap")}
                value={formattedMedianBandwidthCap.value || "∞"}
                suffix={
                  formattedMedianBandwidthCap.value
                    ? formattedMedianBandwidthCap.suffix
                    : null
                }
              />
            </Col>
            <Col xs={12} lg={8} xl={6} style={{ margin: "12px 0" }}>
              <LoadingStatistic
                isLoading={isTelemetryLoading}
                title={t("pages.status.peers")}
                value={telemetry[currentPercentile]?.peerCount}
              />
            </Col>
            <Col xs={12} lg={8} xl={6} style={{ margin: "12px 0" }}>
              <LoadingStatistic
                isLoading={isTelemetryLoading}
                title={t("pages.status.uptime")}
                tooltip={t("tooltips.uptime")}
                value={secondsToTime(telemetry[currentPercentile]?.uptime || 0)}
              />
            </Col>
          </Row>
        </Card>
      </Col>
      <Col xs={24} md={8}>
        <PieChart versions={versions} />
      </Col>
    </Row>
  );
}
Example #21
Source File: index.tsx    From fe-v5 with Apache License 2.0 4 votes vote down vote up
export default function Dashboard() {
  const { t } = useTranslation();
  const [form] = Form.useForm();
  const ref = useRef(null);
  const history = useHistory();
  const [isModalVisible, setIsModalVisible] = useState(false);
  const [modalType, setModalType] = useState<ModalStatus>(ModalStatus.None);
  const [selectRowKeys, setSelectRowKeys] = useState<number[]>([]);
  const [exportData, setExportData] = useState<string>('');
  const [editing, setEditing] = useState(false);
  const [query, setQuery] = useState<string>('');
  const [searchVal, setsearchVal] = useState<string>('');
  const [dashboardList, setDashboardList] = useState<DashboardType[]>();
  const [busiId, setBusiId] = useState<number>();

  const showModal = () => {
    setIsModalVisible(true);
  };

  const handleOk = async () => {
    await form.validateFields();

    if (editing) {
      await edit();
      message.success(t('编辑大盘成功'));
    } else {
      await create();
      message.success(t('新建大盘成功'));
    }

    (ref?.current as any)?.refreshList();
    setIsModalVisible(false);
    setEditing(false);
  };

  useEffect(() => {
    if (busiId) {
      getDashboard(busiId).then((res) => {
        if (searchVal && res.dat) {
          const filters = searchVal.split(' ');
          for (var i = 0; i < filters.length; i++) {
            res.dat = res.dat.filter((item) => item.name.includes(filters[i]) || item.tags.includes(filters[i]));
          }
        }
        setDashboardList(res.dat);
      });
    }
  }, [busiId, searchVal]);

  const create = async () => {
    let { name, tags } = form.getFieldsValue();
    return (
      busiId &&
      createDashboard(busiId, {
        name,
        tags,
      })
    );
  };

  const edit = async () => {
    let { name, tags, id } = form.getFieldsValue();
    return (
      busiId &&
      updateSingleDashboard(busiId, id, {
        name,
        tags,
        pure: true,
      })
    );
  };

  const handleEdit = (record: DashboardType) => {
    const { id, name, tags } = record;
    form.setFieldsValue({
      name,
      tags,
      id,
    });
    setIsModalVisible(true);
    setEditing(true);
  };

  const handleTagClick = (tag) => {
    const queryItem = query.length > 0 ? query.split(' ') : [];
    if (queryItem.includes(tag)) return;
    setQuery((query) => query + ' ' + tag);
    setsearchVal((searchVal) => searchVal + ' ' + tag);
  };

  const layout = {
    labelCol: {
      span: 4,
    },
    wrapperCol: {
      span: 16,
    },
  };
  const dashboardColumn: ColumnsType = [
    {
      title: t('大盘名称'),
      dataIndex: 'name',
      render: (text: string, record: DashboardType) => {
        const { t } = useTranslation();
        return (
          <div className='table-active-text' onClick={() => history.push(`/dashboard/${busiId}/${record.id}`)}>
            {text}
          </div>
        );
      },
    },
    {
      title: t('分类标签'),
      dataIndex: 'tags',
      render: (text: string[]) => (
        <>
          {text.map((tag, index) => {
            return tag ? (
              <Tag
                color='blue'
                key={index}
                style={{
                  cursor: 'pointer',
                }}
                onClick={() => handleTagClick(tag)}
              >
                {tag}
              </Tag>
            ) : null;
          })}
        </>
      ),
    },
    {
      title: t('更新时间'),
      dataIndex: 'update_at',
      render: (text: number) => dayjs(text * 1000).format('YYYY-MM-DD HH:mm:ss'),
    },
    {
      title: t('发布人'),
      dataIndex: 'create_by',
    },
    {
      title: t('操作'),
      width: '240px',
      render: (text: string, record: DashboardType) => (
        <div className='table-operator-area'>
          <div className='table-operator-area-normal' onClick={() => handleEdit(record)}>
            {t('编辑')}
          </div>
          <div
            className='table-operator-area-normal'
            onClick={async () => {
              confirm({
                title: `${t('是否克隆大盘')}${record.name}?`,
                onOk: async () => {
                  await cloneDashboard(busiId as number, record.id);
                  message.success(t('克隆大盘成功'));
                  (ref?.current as any)?.refreshList();
                },

                onCancel() {},
              });
            }}
          >
            {t('克隆')}
          </div>
          <div
            className='table-operator-area-warning'
            onClick={async () => {
              confirm({
                title: `${t('是否删除大盘')}:${record.name}?`,
                onOk: async () => {
                  await removeDashboard(busiId as number, record.id);
                  message.success(t('删除大盘成功'));
                  (ref?.current as any)?.refreshList();
                },

                onCancel() {},
              });
            }}
          >
            {t('删除')}
          </div>
        </div>
      ),
    },
  ];

  const onSearchQuery = (e) => {
    let val = e.target.value;
    setsearchVal(val);
  };

  const handleImportDashboard = async (data) => {
    const { dat } = await importDashboard(busiId as number, data);
    return dat || {};
  };

  return (
    <PageLayout title={t('监控大盘')} icon={<FundViewOutlined />} hideCluster={true}>
      <div style={{ display: 'flex' }}>
        <LeftTree busiGroup={{ onChange: (id) => setBusiId(id) }}></LeftTree>
        {busiId ? (
          <div className='dashboard' style={{ flex: 1, overflow: 'auto' }}>
            <div className='table-handle'>
              <div className='table-handle-search'>
                <Input
                  onPressEnter={onSearchQuery}
                  className={'searchInput'}
                  value={query}
                  onChange={(e) => setQuery(e.target.value)}
                  prefix={<SearchOutlined />}
                  placeholder={t('大盘名称、分类标签')}
                />
              </div>
              <div className='table-handle-buttons'>
                <Button type='primary' onClick={showModal} ghost>
                  {t('新建大盘')}
                </Button>
                <div className={'table-more-options'}>
                  <Dropdown
                    overlay={
                      <ul className='ant-dropdown-menu'>
                        <li className='ant-dropdown-menu-item' onClick={() => setModalType(ModalStatus.BuiltIn)}>
                          <span>{t('导入监控大盘')}</span>
                        </li>
                        <li
                          className='ant-dropdown-menu-item'
                          onClick={async () => {
                            if (selectRowKeys.length) {
                              let exportData = await exportDashboard(busiId as number, selectRowKeys);
                              setExportData(JSON.stringify(exportData.dat, null, 2));
                              setModalType(ModalStatus.Export);
                            } else {
                              message.warning(t('未选择任何大盘'));
                            }
                          }}
                        >
                          <span>{t('导出监控大盘')}</span>
                        </li>
                        <li
                          className='ant-dropdown-menu-item'
                          onClick={() => {
                            if (selectRowKeys.length) {
                              confirm({
                                title: '是否批量删除大盘?',
                                onOk: async () => {
                                  const reuqests = selectRowKeys.map((id) => {
                                    console.log(id);
                                    return removeDashboard(busiId as number, id);
                                  });
                                  Promise.all(reuqests).then(() => {
                                    message.success(t('批量删除大盘成功'));
                                  });
                                  // TODO: 删除完后立马刷新数据有时候不是实时的,这里暂时间隔0.5s后再刷新列表
                                  setTimeout(() => {
                                    (ref?.current as any)?.refreshList();
                                  }, 500);
                                },
                                onCancel() {},
                              });
                            } else {
                              message.warning(t('未选择任何大盘'));
                            }
                          }}
                        >
                          <span>{t('批量删除大盘')}</span>
                        </li>
                      </ul>
                    }
                    trigger={['click']}
                  >
                    <Button onClick={(e) => e.stopPropagation()}>
                      {t('更多操作')}
                      <DownOutlined
                        style={{
                          marginLeft: 2,
                        }}
                      />
                    </Button>
                  </Dropdown>
                </div>
              </div>
            </div>
            <Table
              dataSource={dashboardList}
              className='dashboard-table'
              columns={dashboardColumn}
              pagination={{
                total: dashboardList?.length,
                showTotal(total: number) {
                  return `共 ${total} 条数据`;
                },
                pageSizeOptions: [30, 50, 100, 300],
                defaultPageSize: 30,
                showSizeChanger: true,
              }}
              rowKey='id'
              rowSelection={{
                selectedRowKeys: selectRowKeys,
                onChange: (selectedRowKeys: number[]) => {
                  setSelectRowKeys(selectedRowKeys);
                },
              }}
            ></Table>
          </div>
        ) : (
          <BlankBusinessPlaceholder text='监控大盘' />
        )}
      </div>
      <Modal
        title={editing ? t('编辑监控大盘') : t('创建新监控大盘')}
        visible={isModalVisible}
        onOk={handleOk}
        onCancel={() => {
          setIsModalVisible(false);
        }}
        destroyOnClose
      >
        <Form {...layout} form={form} preserve={false}>
          <Form.Item
            label={t('大盘名称')}
            name='name'
            wrapperCol={{
              span: 24,
            }}
            rules={[
              {
                required: true,
                message: t('请输入大盘名称'),
              },
            ]}
          >
            <Input />
          </Form.Item>
          <Form.Item
            wrapperCol={{
              span: 24,
            }}
            label={t('分类标签')}
            name='tags'
          >
            <Select
              mode='tags'
              dropdownStyle={{
                display: 'none',
              }}
              placeholder={t('请输入分类标签(请用回车分割)')}
            ></Select>
          </Form.Item>
          <Form.Item name='id' hidden>
            <Input />
          </Form.Item>
        </Form>
      </Modal>
      <ImportAndDownloadModal
        bgid={busiId}
        status={modalType}
        crossCluster={false}
        fetchBuiltinFunc={getBuiltinDashboards}
        submitBuiltinFunc={createBuiltinDashboards}
        onClose={() => {
          setModalType(ModalStatus.None);
        }}
        onSuccess={() => {
          (ref?.current as any)?.refreshList();
        }}
        onSubmit={handleImportDashboard}
        label='大盘'
        title={
          ModalStatus.Export === modalType ? (
            '大盘'
          ) : (
            <Tabs defaultActiveKey={ModalStatus.BuiltIn} onChange={(e: ModalStatus) => setModalType(e)} className='custom-import-alert-title'>
              <TabPane tab=' 导入内置大盘模块' key={ModalStatus.BuiltIn}></TabPane>
              <TabPane tab='导入大盘JSON' key={ModalStatus.Import}></TabPane>
            </Tabs>
          )
        }
        exportData={exportData}
      />
    </PageLayout>
  );
}
Example #22
Source File: Members.tsx    From posthog-foss with MIT License 4 votes vote down vote up
function LevelComponent(member: OrganizationMemberType): JSX.Element | null {
    const { user } = useValues(userLogic)
    const { currentOrganization } = useValues(organizationLogic)
    const { changeMemberAccessLevel } = useActions(membersLogic)

    const myMembershipLevel = currentOrganization ? currentOrganization.membership_level : null

    if (!user) {
        return null
    }

    function generateHandleClick(listLevel: OrganizationMembershipLevel): (event: React.MouseEvent) => void {
        return function handleClick(event: React.MouseEvent) {
            event.preventDefault()
            if (!user) {
                throw Error
            }
            if (listLevel === OrganizationMembershipLevel.Owner) {
                Modal.confirm({
                    centered: true,
                    title: `Transfer organization ownership to ${member.user.first_name}?`,
                    content: `You will no longer be the owner of ${user.organization?.name}. After the transfer you will become an administrator.`,
                    icon: <SwapOutlined />,
                    okType: 'danger',
                    okText: 'Transfer Ownership',
                    onOk() {
                        changeMemberAccessLevel(member, listLevel)
                    },
                })
            } else {
                changeMemberAccessLevel(member, listLevel)
            }
        }
    }

    const levelButton = (
        <Button
            data-attr="change-membership-level"
            icon={member.level === OrganizationMembershipLevel.Owner ? <CrownFilled /> : undefined}
        >
            {membershipLevelToName.get(member.level) ?? `unknown (${member.level})`}
        </Button>
    )

    const allowedLevels = organizationMembershipLevelIntegers.filter(
        (listLevel) => !getReasonForAccessLevelChangeProhibition(myMembershipLevel, user, member, listLevel)
    )
    const disallowedReason = getReasonForAccessLevelChangeProhibition(myMembershipLevel, user, member, allowedLevels)

    return disallowedReason ? (
        <Tooltip title={disallowedReason}>{levelButton}</Tooltip>
    ) : (
        <Dropdown
            overlay={
                <Menu>
                    {allowedLevels.map((listLevel) => (
                        <Menu.Item key={`${member.user.uuid}-level-${listLevel}`}>
                            <a href="#" onClick={generateHandleClick(listLevel)} data-test-level={listLevel}>
                                {listLevel === OrganizationMembershipLevel.Owner ? (
                                    <>
                                        <CrownFilled style={{ marginRight: '0.5rem' }} />
                                        Transfer organization ownership
                                    </>
                                ) : listLevel > member.level ? (
                                    <>
                                        <UpOutlined style={{ marginRight: '0.5rem' }} />
                                        Upgrade to {membershipLevelToName.get(listLevel)}
                                    </>
                                ) : (
                                    <>
                                        <DownOutlined style={{ marginRight: '0.5rem' }} />
                                        Downgrade to {membershipLevelToName.get(listLevel)}
                                    </>
                                )}
                            </a>
                        </Menu.Item>
                    ))}
                </Menu>
            }
        >
            {levelButton}
        </Dropdown>
    )
}
Example #23
Source File: index.tsx    From surveyo with Apache License 2.0 4 votes vote down vote up
function FormCreator() {
  const [questions, setQuestions] = useState<any>([]);
  const [formSubmitted, setFormSubmitState] = useState(false);
  const [formURL, setFormURL] = useState('');
  const [surveyTitle, setSurveyTitle] = useState('');
  const [formHook] = useForm();
  const {user} = useAuth0();

  const [sendToClient] = useMutation(ADD_FORM);

  const getCard = (i: number) => {
    const question = questions[i];
    const params = {
      question: question,
      updateQuestion: (question: any) =>
        setQuestions(update(questions, {$splice: [[i, 1, question]]})),
      deleteQuestion: () =>
        setQuestions(update(questions, {$splice: [[i, 1]]})),
    };
    return <QuestionCard {...params} />;
  };
  const menu = (
    <Menu onClick={e => setQuestions(questions.concat({type: e.key}))}>
      <Menu.Item key="Text">Short Answer</Menu.Item>
      <Menu.Item key="SingleChoice">Multiple Choice</Menu.Item>
      <Menu.Item key="Date">Date</Menu.Item>
      <Menu.Item key="Rating">Rating</Menu.Item>
      <Menu.Item key="NetPromoterScore">Net Promoter Score</Menu.Item>
    </Menu>
  );

  if (formSubmitted) {
    return (
      <Card type="inner">
        <Result
          status="success"
          title="Thank you!"
          subTitle={
            <Anchor>
              <Anchor.Link href={formURL} title="Your form is live." />
            </Anchor>
          }
        />
      </Card>
    );
  } else
    return (
      <PageHeader ghost={true} title="Create a survey">
        <Form form={formHook}>
          <Card
            actions={[
              <Dropdown overlay={menu}>
                <Button>
                  Add Question <DownOutlined />
                </Button>
              </Dropdown>,
              <Button
                type="primary"
                onClick={async () => {
                  const values = await formHook.validateFields();
                  console.log('validation ' + values.name);
                  for (let index = 0; index < questions.length; index++) {
                    if ('options' in questions[index]) {
                      let newOptions = questions[index].options.map(
                        (value: any, index: any) => {
                          return {order: index, title: value};
                        }
                      );
                      questions[index].options = newOptions;
                    }
                    questions[index].order = index;
                    if (!('required' in questions[index])) {
                      questions[index].required = false;
                    }
                  }
                  var form = {
                    title: surveyTitle,
                    fields: questions,
                    creator: {email: user.email},
                  };

                  console.log('Form: ', form);

                  try {
                    var result = await sendToClient({
                      variables: {
                        form: form,
                      },
                    });

                    console.log(result);
                    let id = result.data.addForm.form[0].id;
                    let url =
                      window.location.href.replace('/create', '') +
                      '/form/' +
                      id;
                    setFormURL(url);
                    setFormSubmitState(true);
                  } catch (error) {
                    console.log(error);
                  }
                }}
              >
                Create
              </Button>,
            ]}
          >
            <Form.Item
              label="Survey Title"
              name="survey title"
              rules={[{required: true, message: 'Please input Survey title'}]}
            >
              <Input
                placeholder="Enter your survey title"
                onChange={e => {
                  setSurveyTitle(e.target.value);
                }}
              />
            </Form.Item>
            {questions.map((question: any, index: number) => (
              <div key={index}>
                <Card>{getCard(index)}</Card>
              </div>
            ))}
          </Card>
        </Form>
      </PageHeader>
    );
}
Example #24
Source File: index.tsx    From nanolooker with MIT License 4 votes vote down vote up
LargeTransactions: React.FC = () => {
  const { t } = useTranslation();
  const history = useHistory();
  const { sortBy: paramSortBy = SORT_BY.LATEST } = useParams<PageParams>();
  const { largeTransactions, isLoading } = useLargeTransactions();
  const [currentPage, setCurrentPage] = React.useState<number>(1);
  const [orderedTransactions, setOrderedTransactions] = React.useState<
    Transaction[]
  >();
  const defaultSortBy = Object.values(SORT_BY).includes(paramSortBy)
    ? paramSortBy
    : SORT_BY.LATEST;
  const [sortBy, setSortBy] = React.useState(defaultSortBy);

  const showPaginate = largeTransactions.length > TRANSACTIONS_PER_PAGE;

  React.useEffect(() => {
    const start = 0 + (currentPage - 1) * TRANSACTIONS_PER_PAGE;
    const data = largeTransactions.map(
      ({ timestamp, block, hash, amount }) => ({
        ...block,
        amount,
        hash,
        largest: new BigNumber(rawToRai(amount)).toNumber(),
        latest: timestamp,
        local_timestamp: Math.floor(timestamp / 1000),
      }),
    );
    const orderedData = orderBy(data, [sortBy.toLowerCase()], ["desc"]);
    const pageData = orderedData?.slice(start, start + TRANSACTIONS_PER_PAGE);

    // @ts-ignore
    setOrderedTransactions(pageData);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [largeTransactions, sortBy, currentPage]);

  const handleSortBy = ({ key }: any) => {
    history.replace(`/large-transactions/${key}`);
    setCurrentPage(1);
    setSortBy(key);
  };

  return (
    <>
      <Helmet>
        <title>Nano {t("menu.largeTransactions")}</title>
      </Helmet>
      <Title level={3}>{t("menu.largeTransactions")}</Title>

      <div style={{ marginBottom: "12px" }}>
        <Text>{t("pages.largeTransactions.description")}</Text>
        <Tooltip placement="right" title={t("tooltips.largeTransactions")}>
          <QuestionCircle />
        </Tooltip>
      </div>
      <div style={{ marginBottom: "12px" }}>
        <Dropdown
          overlay={
            <Menu onClick={handleSortBy}>
              {Object.values(SORT_BY).map(sortBy => (
                <Menu.Item key={sortBy}>
                  {t(`pages.largeTransactions.${sortBy}`)}
                </Menu.Item>
              ))}
            </Menu>
          }
        >
          <Button>
            {t(`pages.largeTransactions.${sortBy}`)} <DownOutlined />
          </Button>
        </Dropdown>
      </div>

      <TransactionsTable
        data={orderedTransactions}
        isLoading={isLoading}
        isPaginated={true}
        showPaginate={showPaginate}
        pageSize={TRANSACTIONS_PER_PAGE}
        currentPage={currentPage}
        totalPages={largeTransactions.length}
        setCurrentPage={setCurrentPage}
      />
    </>
  );
}
Example #25
Source File: LabelsValues.tsx    From fe-v5 with Apache License 2.0 4 votes vote down vote up
export default function LabelsValues(props: IProps) {
  const { value, range, onChange } = props;
  const { id, refreshFlag, filters, dynamicLabels, dimensionLabels } = value;
  const [labelValues, setLabelValues] = useState<{ [key: string]: string[] }>({});
  const [dimensionLabelsValues, setDimensionLabelsValues] = useState<{ [key: string]: string[] }>({});
  const [dimensionLabelsSearch, setDimensionLabelsSearch] = useState({});
  const filtersStr = getFiltersStr(filters);
  const dynamicLabelsStr = getDynamicLabelsStr(dynamicLabels);
  const [expaned, setExpaned] = useState({
    filters: false,
    dynamicLabels: true,
  });

  useEffect(() => {
    const dynamicLabelsRequests = _.map(dynamicLabels, (item) => {
      return getLabelValues(item.label, range, filtersStr ? `{${filtersStr}}` : '');
    });
    Promise.all(dynamicLabelsRequests).then((res) => {
      const _labelValues = {};
      _.forEach(res, (item, idx) => {
        _labelValues[dynamicLabels[idx].label] = item;
      });
      setLabelValues(_labelValues);
    });
  }, [filtersStr]);

  useEffect(() => {
    const matchStr = _.join(_.compact(_.concat(filtersStr, dynamicLabelsStr)), ',');
    const dimensionLabelsRequests = _.map(dimensionLabels, (item) => {
      return getLabelValues(item.label, range, matchStr ? `{${matchStr}}` : '');
    });
    Promise.all(dimensionLabelsRequests).then((res) => {
      const _labelValues = {};
      _.forEach(res, (item, idx) => {
        _labelValues[dimensionLabels[idx].label] = item;
      });
      setDimensionLabelsValues(_labelValues);
      if (_.every(dimensionLabels, (item) => _.isEmpty(item.value))) {
        onChange({
          ...value,
          dimensionLabels: _.map(dimensionLabels, (item, idx) => {
            if (idx === 0) {
              return {
                label: item.label,
                value: _.slice(_labelValues[item.label], 0, 10),
              };
            }
            return item;
          }),
        });
      }
    });
  }, [filtersStr, dynamicLabelsStr, id, refreshFlag]);

  return (
    <div className='n9e-metric-views-labels-values'>
      {!_.isEmpty(filtersStr) && (
        <div className='n9e-metric-views-labels-values-item'>
          <div
            className='page-title'
            style={{ cursor: 'pointer' }}
            onClick={() => {
              setExpaned({
                ...expaned,
                filters: !expaned.filters,
              });
            }}
          >
            前置过滤条件 {expaned.filters ? <UpOutlined /> : <DownOutlined />}
          </div>
          {expaned.filters && <div className='n9e-metric-views-filters'>{filtersStr ? filtersStr : '暂无数据'}</div>}
        </div>
      )}
      {!_.isEmpty(dynamicLabels) && (
        <div className='n9e-metric-views-labels-values-item'>
          <div
            className='page-title'
            style={{ cursor: 'pointer' }}
            onClick={() => {
              setExpaned({
                ...expaned,
                dynamicLabels: !expaned.dynamicLabels,
              });
            }}
          >
            动态过滤条件 {expaned.dynamicLabels ? <UpOutlined /> : <DownOutlined />}
          </div>
          {expaned.dynamicLabels && (
            <div className='n9e-metric-views-dynamicLabels'>
              {_.isEmpty(dynamicLabels) ? (
                <div style={{ marginBottom: 10 }}>暂无数据</div>
              ) : (
                _.map(dynamicLabels, (item) => {
                  return (
                    <div key={item.label} className='n9e-metric-views-dynamicLabels-item'>
                      <div className='n9e-metric-views-dynamicLabels-item-label'>{item.label}:</div>
                      <Select
                        allowClear
                        showSearch
                        style={{ width: '100%' }}
                        value={item.value}
                        onChange={(val) => {
                          const _dynamicLabels = _.map(dynamicLabels, (obj) => {
                            if (item.label === obj.label) {
                              return {
                                ...obj,
                                value: val,
                              };
                            }
                            return obj;
                          });
                          onChange({
                            ...value,
                            dynamicLabels: _dynamicLabels,
                            dimensionLabels: _.map(dimensionLabels, (item, idx) => {
                              if (idx === 0) {
                                return {
                                  label: item.label,
                                  value: [],
                                };
                              }
                              return item;
                            }),
                          });
                        }}
                      >
                        {_.map(labelValues[item.label], (value) => {
                          return (
                            <Select.Option key={value} value={value}>
                              {value}
                            </Select.Option>
                          );
                        })}
                      </Select>
                    </div>
                  );
                })
              )}
            </div>
          )}
        </div>
      )}
      {_.map(dimensionLabels, (dimensionLabel) => {
        const dimensionLabelValues = dimensionLabelsValues[dimensionLabel.label];
        return (
          <div key={dimensionLabel.label} className='n9e-metric-views-labels-values-item'>
            <div className='page-title'>
              <div
                style={{
                  display: 'flex',
                  flexWrap: 'wrap',
                  justifyContent: 'space-between',
                  alignItems: 'center',
                  width: 220,
                }}
              >
                <Tooltip title={dimensionLabel.label} placement='left'>
                  <div
                    style={{
                      overflow: 'hidden',
                      textOverflow: 'ellipsis',
                      maxWidth: 'calc(100% - 30px)',
                    }}
                  >
                    {dimensionLabel.label}
                  </div>
                </Tooltip>
                <a
                  style={{ fontSize: 12, fontWeight: 'normal' }}
                  onClick={() => {
                    onChange({
                      ...value,
                      dimensionLabels: _.map(dimensionLabels, (item) => {
                        if (item.label === dimensionLabel.label) {
                          return {
                            ...item,
                            value: dimensionLabelValues,
                          };
                        }
                        return item;
                      }),
                    });
                  }}
                >
                  全选
                </a>
              </div>
            </div>
            <div className='n9e-metric-views-dimensionLabel'>
              <Input.Group compact>
                <Input
                  style={{ width: 'calc(100% - 32px)' }}
                  prefix={<SearchOutlined />}
                  value={dimensionLabelsSearch[dimensionLabel.label]}
                  onChange={(e) => {
                    setDimensionLabelsSearch({
                      ...dimensionLabelsSearch,
                      [dimensionLabel.label]: e.target.value,
                    });
                  }}
                />
                <Tooltip title='清空已选的值' placement='right' getTooltipContainer={() => document.body}>
                  <Button
                    icon={<ClearOutlined />}
                    onClick={() => {
                      onChange({
                        ...value,
                        dimensionLabels: _.map(dimensionLabels, (item) => {
                          if (item.label === dimensionLabel.label) {
                            return {
                              ...item,
                              value: [],
                            };
                          }
                          return item;
                        }),
                      });
                    }}
                  />
                </Tooltip>
              </Input.Group>

              <div className='n9e-metric-views-dimensionLabel-content'>
                {_.isEmpty(dimensionLabelValues) ? (
                  '暂无数据'
                ) : (
                  <div>
                    {_.map(
                      _.filter(dimensionLabelValues, (item) => {
                        let result = true;
                        if (dimensionLabelsSearch[dimensionLabel.label]) {
                          try {
                            const reg = new RegExp(dimensionLabelsSearch[dimensionLabel.label], 'gi');
                            result = reg.test(item);
                          } catch (e) {
                            console.log(e);
                          }
                        }
                        return result;
                      }),
                      (item: string) => {
                        return (
                          <div
                            key={item}
                            className={classNames({
                              'n9e-metric-views-dimensionLabel-content-item': true,
                              active: _.includes(dimensionLabel.value, item),
                            })}
                            onClick={() => {
                              const dimensionLabelValue = _.includes(dimensionLabel.value, item) ? _.without(dimensionLabel.value, item) : _.concat(dimensionLabel.value, item);
                              const newDimensionLabels = _.map(dimensionLabels, (item) => {
                                if (item.label === dimensionLabel.label) {
                                  return {
                                    ...item,
                                    value: _.compact(dimensionLabelValue),
                                  };
                                }
                                return {
                                  ...item,
                                  value: [],
                                };
                              });
                              onChange({
                                ...value,
                                dimensionLabels: newDimensionLabels,
                              });
                            }}
                          >
                            {item}
                          </div>
                        );
                      },
                    )}
                  </div>
                )}
              </div>
            </div>
          </div>
        );
      })}
    </div>
  );
}
Example #26
Source File: index.tsx    From nanolooker with MIT License 4 votes vote down vote up
NewsPage: React.FC = () => {
  const { t } = useTranslation();
  const history = useHistory();
  const { feed = "" } = useParams<PageParams>();
  const [posts, setPosts] = React.useState([] as (MediumPost | YoutubePost)[]);
  const [authors, setAuthors] = React.useState([""]);
  const {
    isLoading: isMediumLoading,
    posts: mediumPosts,
    authors: mediumAuthors,
  } = useMedium();
  const {
    isLoading: isYoutubeLoading,
    posts: youtubePosts,
    authors: youtubeAuthors,
  } = useYoutube();
  const [feedFilter, setFeedFilter] = React.useState<MEDIUM_FEEDS | string>(
    ALL,
  );
  const [authorFilter, setAuthorFilter] = React.useState<string>(ALL);
  const [sourceFilter, setSourceFilter] = React.useState<string>(ALL);

  React.useEffect(() => {
    const orderedPosts: (MediumPost | YoutubePost)[] = orderBy(
      [...mediumPosts, ...youtubePosts],
      ["pubDate"],
      ["desc"],
    );

    setPosts(orderedPosts);
  }, [mediumPosts, youtubePosts]);

  React.useEffect(() => {
    const orderedAuthors: string[] = [
      ...mediumAuthors,
      ...youtubeAuthors,
    ].sort();

    setAuthors(orderedAuthors);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [mediumAuthors.length, youtubeAuthors.length]);

  React.useEffect(() => {
    if (feed && posts.find(({ feed: postFeed }) => postFeed === feed)) {
      handleFeedFilter({ key: feed });
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [posts]);

  const handleFeedFilter = ({ key }: any) => {
    history.replace(`/news${key !== ALL ? `/${key}` : ""}`);
    setFeedFilter(key);
  };

  const handleAuthorFilter = ({ key }: any) => {
    setAuthorFilter(key);
  };

  const handleSourceFilter = ({ key }: any) => {
    setSourceFilter(key);
  };

  const filteredPosts = posts
    .filter(({ feed }) => (feedFilter !== ALL ? feedFilter === feed : true))
    .filter(({ author }) =>
      authorFilter !== ALL ? authorFilter === author : true,
    )
    .filter(({ source }) =>
      sourceFilter !== ALL ? sourceFilter === source : true,
    );

  return (
    <>
      <Helmet>
        <title>Nano {t("menu.news")}</title>
      </Helmet>
      <Space style={{ marginBottom: "12px" }}>
        <Dropdown
          overlay={
            <Menu onClick={handleFeedFilter}>
              <Menu.Item key={ALL}>{t("pages.news.allFeeds")}</Menu.Item>
              {Object.values(MEDIUM_FEEDS).map(feed => (
                <Menu.Item key={feed}>{feed}</Menu.Item>
              ))}
            </Menu>
          }
        >
          <Button>
            {feedFilter !== ALL ? feedFilter : t("pages.news.allFeeds")}{" "}
            <DownOutlined />
          </Button>
        </Dropdown>

        <Dropdown
          overlay={
            <Menu onClick={handleAuthorFilter}>
              <Menu.Item key={ALL}>{t("pages.news.allAuthors")}</Menu.Item>
              {authors.map(author => (
                <Menu.Item key={author}>{author}</Menu.Item>
              ))}
            </Menu>
          }
        >
          <Button>
            {authorFilter !== ALL ? authorFilter : t("pages.news.allAuthors")}{" "}
            <DownOutlined />
          </Button>
        </Dropdown>

        <Dropdown
          overlay={
            <Menu onClick={handleSourceFilter}>
              <Menu.Item key={ALL}>{t("pages.news.allSources")}</Menu.Item>
              {Object.keys(PostSource).map(source => (
                <Menu.Item key={source}>{source}</Menu.Item>
              ))}
            </Menu>
          }
        >
          <Button>
            {sourceFilter !== ALL ? sourceFilter : t("pages.news.allSources")}{" "}
            <DownOutlined />
          </Button>
        </Dropdown>
      </Space>

      <Row gutter={[12, 0]}>
        {!filteredPosts.length && (isMediumLoading || isYoutubeLoading)
          ? Array.from({ length: 6 }).map((_value, index) => (
              <Col xs={24} md={12} lg={8} key={index}>
                <Card
                  size="small"
                  bodyStyle={{
                    minHeight: "240px",
                  }}
                >
                  <Skeleton active loading={true}></Skeleton>
                </Card>
              </Col>
            ))
          : null}

        {filteredPosts.map((post: MediumPost | YoutubePost, index) => (
          <Col xs={24} md={12} lg={8} key={index}>
            {post.source === PostSource.MEDIUM ? <Medium post={post} /> : null}
            {post.source === PostSource.YOUTUBE ? (
              <Youtube post={post} />
            ) : null}
          </Col>
        ))}
      </Row>

      {!filteredPosts?.length && !isMediumLoading && !isYoutubeLoading ? (
        <Text style={{ display: "block" }}>{t("common.noResults")}</Text>
      ) : null}
    </>
  );
}
Example #27
Source File: ChartDraggableElementField.tsx    From datart with Apache License 2.0 4 votes vote down vote up
ChartDraggableElementField: FC<{
  modalSize;
  config;
  columnConfig;
  ancestors;
  aggregation;
  availableSourceFunctions;
  onConfigChanged;
  handleOpenActionModal;
}> = memo(
  ({
    modalSize,
    config,
    columnConfig,
    ancestors,
    aggregation,
    availableSourceFunctions,
    onConfigChanged,
    handleOpenActionModal,
  }) => {
    const renderActionExtensionMenu = (uid: string, type: string, category) => {
      return (
        <ChartDataConfigSectionActionMenu
          uid={uid}
          type={type}
          category={category}
          ancestors={ancestors}
          config={config}
          modalSize={modalSize}
          availableSourceFunctions={availableSourceFunctions}
          onConfigChanged={onConfigChanged}
          onOpenModal={handleOpenActionModal}
        />
      );
    };

    const enableActionsIcons = col => {
      const icons = [] as any;
      if (col.alias) {
        icons.push(<DiffOutlined key="alias" />);
      }
      if (col.sort) {
        if (col.sort.type === SortActionType.ASC) {
          icons.push(<SortAscendingOutlined key="sort" />);
        }
        if (col.sort.type === SortActionType.DESC) {
          icons.push(<SortDescendingOutlined key="sort" />);
        }
      }
      if (col.format) {
        icons.push(<FormatPainterOutlined key="format" />);
      }
      if (col.aggregate) {
        icons.push(<GroupOutlined key="aggregate" />);
      }
      if (col.filter) {
        icons.push(<FilterOutlined key="filter" />);
      }
      if (col.color) {
        icons.push(<BgColorsOutlined key="color" />);
      }
      if (col.size) {
        icons.push(<FontSizeOutlined key="size" />);
      }
      return icons;
    };

    return (
      <Dropdown
        key={columnConfig.uid}
        disabled={!config?.actions}
        destroyPopupOnHide={true}
        overlay={renderActionExtensionMenu(
          columnConfig.uid!,
          columnConfig.type,
          columnConfig.category,
        )}
        overlayClassName="datart-data-section-dropdown"
        trigger={['click']}
      >
        <div>
          {config?.actions && <DownOutlined style={{ marginRight: '10px' }} />}
          <span>
            {aggregation === false
              ? columnConfig.colName
              : getColumnRenderName(columnConfig)}
          </span>
          <div style={{ display: 'inline-block', marginLeft: '5px' }}>
            {enableActionsIcons(columnConfig)}
          </div>
        </div>
      </Dropdown>
    );
  },
)
Example #28
Source File: NavBar.tsx    From office-hours with GNU General Public License v3.0 4 votes vote down vote up
export default function NavBar({ courseId }: NavBarProps): ReactElement {
  const profile = useProfile();
  if (!courseId) {
    courseId = profile?.courses[0]?.course?.id;
  }

  const [_defaultCourse, setDefaultCourse] = useLocalStorage(
    "defaultCourse",
    null
  );
  const [visible, setVisible] = useState<boolean>(false);
  const { pathname } = useRouter();
  const { course } = useCourse(courseId);
  const role = useRoleInCourse(courseId);

  const openQueues = course?.queues?.filter((queue) => queue.isOpen);

  const showDrawer = () => {
    setVisible(true);
  };
  const onClose = () => {
    setVisible(false);
  };

  const courseSelector = (
    <Menu>
      {profile?.courses?.map((c) => (
        <CoursesMenuItem
          key={c.course.id}
          onClick={() => setDefaultCourse(c.course)}
        >
          <Link href="/course/[cid]/today" as={`/course/${c.course.id}/today`}>
            <a>{c.course.name}</a>
          </Link>
        </CoursesMenuItem>
      ))}
    </Menu>
  );

  const tabs: NavBarTabsItem[] = [
    {
      href: "/course/[cid]/today",
      as: `/course/${courseId}/today`,
      text: "Today",
    },
    {
      href: "/course/[cid]/schedule",
      as: `/course/${courseId}/schedule`,
      text: "Schedule",
    },
  ];

  if (role === Role.PROFESSOR) {
    tabs.push({
      href: "/course/[cid]/course_admin_panel",
      as: `/course/${courseId}/course_admin_panel`,
      text: "Admin Panel",
    });
  }

  if (openQueues?.length > 0) {
    tabs.push({
      text: "Queue",
      queues: openQueues,
      courseId: courseId,
    });
  }

  if (role === Role.PROFESSOR) {
    tabs.push({
      href: "/course/[cid]/insights",
      as: `/course/${courseId}/insights`,
      text: "Insights",
    });
  }

  return courseId ? (
    <>
      <NavBG />
      <AlertsContainer courseId={courseId} />
      <Nav>
        <LogoContainer>
          {profile?.courses.length > 1 ? (
            <Dropdown
              overlay={courseSelector}
              trigger={["click"]}
              placement="bottomLeft"
            >
              <a>
                <Logo>
                  <span>{course?.name}</span>
                  <DownOutlined
                    style={{
                      fontSize: "16px",
                      verticalAlign: "-0.125em",
                      marginLeft: "5px",
                    }}
                  />
                </Logo>
              </a>
            </Dropdown>
          ) : (
            <Logo>
              <span>{course?.name}</span>
            </Logo>
          )}
        </LogoContainer>
        <MenuCon>
          <LeftMenu>
            <NavBarTabs horizontal currentHref={pathname} tabs={tabs} />
          </LeftMenu>
          <RightMenu>
            <ProfileDrawer courseId={courseId} />
          </RightMenu>
        </MenuCon>
        <BarsMenu type="primary" onClick={showDrawer}>
          <BarsButton />
        </BarsMenu>
        <Drawer
          title="Course"
          placement="right"
          visible={visible}
          closable={false}
          onClose={onClose}
          bodyStyle={{ padding: "12px" }}
        >
          <NavBarTabs currentHref={pathname} tabs={tabs} />
          <ProfileDrawer courseId={courseId} />
        </Drawer>
      </Nav>
    </>
  ) : null;
}
Example #29
Source File: HeadAccounts.tsx    From subscan-multisig-react with Apache License 2.0 4 votes vote down vote up
HeadAccounts = () => {
  const { t } = useTranslation();
  const { network, accounts, extensions } = useApi();
  const { contacts, queryContacts } = useContacts();
  const [popoverVisible, setPopoverVisible] = useState(false);
  const [drawerVisible, setDrawerVisible] = useState(false);
  const [addContactModalVisible, setAddContactModalVisible] = useState(false);

  const headerLinkStyle = useMemo(() => genHeaderLinkStyle`${network}`, [network]);

  const mainColor = useMemo(() => {
    return getThemeColor(network);
  }, [network]);

  useEffect(() => {
    if (popoverVisible) {
      queryContacts();
    }
  }, [popoverVisible, queryContacts]);

  const renderAccountContent = () => {
    if (extensions && extensions.length === 0) {
      return <div className="mx-5 my-3">{t('extension not found')}</div>;
    }
    if (accounts && accounts.length === 0) {
      return <div className="mx-5 my-3">{t('extension account empty')}</div>;
    }
    return accounts?.map((item) => (
      <AccountItem key={item.address} address={item.address} name={item.meta?.name} type="injected" />
    ));
  };

  return (
    <div>
      <span className={`${headerLinkStyle} inline md:hidden`} onClick={() => setDrawerVisible(true)}>
        {t('accounts')}

        <DownOutlined style={{ marginTop: '4px' }} />
      </span>

      <Drawer
        className="block md:hidden account-drawer"
        placement="top"
        visible={drawerVisible}
        onClose={() => setDrawerVisible(false)}
      >
        <div>
          <Tabs defaultActiveKey="1">
            <TabPane tab={t('My Account')} key="1">
              <div className="account-list">{renderAccountContent()}</div>
            </TabPane>

            <TabPane tab={t('Contact Account')} key="2">
              <div>
                <div className="account-list">
                  {contacts?.map((item) => (
                    <AccountItem
                      key={item.address}
                      address={item.address}
                      name={item.meta?.name}
                      type="contact"
                      refreshContacts={queryContacts}
                    />
                  ))}
                </div>

                <div className="flex justify-end md:mt-2">
                  <Button
                    type="default"
                    size="large"
                    className="flex justify-center items-center w-full"
                    style={{ color: mainColor }}
                    onClick={() => {
                      setAddContactModalVisible(true);
                      setPopoverVisible(false);
                    }}
                  >
                    {t('contact.Add Contact')}
                  </Button>
                </div>
              </div>
            </TabPane>
          </Tabs>
        </div>
      </Drawer>

      <Popover
        title={null}
        trigger="click"
        visible={popoverVisible}
        onVisibleChange={setPopoverVisible}
        overlayInnerStyle={{
          borderRadius: '0.15rem',
        }}
        overlayStyle={{
          width: '600px',
        }}
        content={
          <div>
            <Tabs defaultActiveKey="1">
              <TabPane tab={t('My Account')} key="1">
                <div className="account-list">{renderAccountContent()}</div>
              </TabPane>

              <TabPane tab={t('Contact Account')} key="2">
                <div className="">
                  <div className="account-list">
                    {contacts?.map((item) => (
                      <AccountItem
                        key={item.address}
                        address={item.address}
                        name={item.meta?.name}
                        type="contact"
                        refreshContacts={queryContacts}
                      />
                    ))}
                  </div>

                  <div className="flex justify-end md:mt-2">
                    <Button
                      type="default"
                      size="large"
                      className="flex justify-center items-center w-full"
                      style={{ color: mainColor }}
                      onClick={() => {
                        setAddContactModalVisible(true);
                        setPopoverVisible(false);
                      }}
                    >
                      {t('contact.Add Contact')}
                    </Button>
                  </div>
                </div>
              </TabPane>
            </Tabs>
          </div>
        }
        placement="bottom"
      >
        <span className={`${headerLinkStyle} hidden md:block`}>
          {t('accounts')}

          <DownOutlined style={{ marginTop: '4px' }} />
        </span>
      </Popover>
      <AddContactModal visible={addContactModalVisible} handleVisibleChange={setAddContactModalVisible} />
    </div>
  );
}