@ant-design/icons#LeftOutlined TypeScript Examples

The following examples show how to use @ant-design/icons#LeftOutlined. 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: Storage.tsx    From whiteboard-demo with MIT License 6 votes vote down vote up
private renderHeadView(downloader: DownloadLogic): React.ReactNode {
        const { t } = this.props
        const shouldDisplaySpaceTag = (
            typeof this.state.space === "number" &&
            typeof this.state.availableSpace === "number"
        );
        return (
            <div className="page-history-head">
                <div className="page-history-head-left">
                    <Link to={"/"}>
                        <div className="page-history-back">
                            <LeftOutlined /> <div>{t('back')}</div>
                        </div>
                    </Link>
                    {shouldDisplaySpaceTag && <Tag
                        color={"blue"}
                        style={{marginLeft: 8}}>{this.state.space}(mb) / {this.state.availableSpace}(mb)
                    </Tag>}
                </div>
                <div>
                    {this.renderAddTaskButton(downloader)}
                    {this.renderDownloadOneByOneButton(downloader)}
                    {this.renderCleanAllCacheButton(downloader)}
                </div>
            </div>
        );
    }
Example #2
Source File: ProviderGroupMenu.spec.tsx    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
describe("ProviderGroupMenu", () => {
  it("should work", () => {
    const props = {
      itemList: [
        "providers-of-admin.nameservice-api-list-name-service",
        "providers-of-admin.agent-management-api-get-agent-status",
        "providers-of-admin.nameservice-api-get-name-service",
      ],
      onFold: jest.fn(),
    };
    const wrapper = shallow(<ProviderGroupMenu {...props} />);

    expect(wrapper.find(".item").length).toEqual(3);
    expect(wrapper.find(LeftOutlined).length).toEqual(0);

    wrapper.find(".customIcon").invoke("onClick")(null);

    wrapper.update();

    expect(wrapper.find(LeftOutlined).length).toEqual(1);
  });
});
Example #3
Source File: HistoryPage.tsx    From whiteboard-demo with MIT License 6 votes vote down vote up
public render(): React.ReactNode {
        const { t } = this.props 
        return (
            <div className="page-index-box">
                <FloatLink />
                <div className="page-index-mid-box">
                    <div className="page-history-head">
                        <Link to={"/"}>
                            <div className="page-history-back">
                                <LeftOutlined /> <div>{t('back')}</div>
                            </div>
                        </Link>
                        <Button
                            type="link"
                            size={"small"}
                            style={{ marginRight: 20, fontSize: 14 }}
                            onClick={() => {
                                localStorage.removeItem("rooms");
                                this.setState({ rooms: [] });
                            }}
                        >
                            {t('clear')}
                        </Button>
                    </div>
                    {this.state.rooms.length === 0 ? (
                        <div className="page-history-body-empty">
                            <img src={empty_box} alt={"empty_box"} />
                        </div>
                    ) : (
                        <div className="page-history-body">{this.renderCells()}</div>
                    )}
                </div>
            </div>
        );
    }
Example #4
Source File: ProviderGroupMenu.tsx    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
export function ProviderGroupMenu(
  props: ProviderGroupMenuProps
): React.ReactElement {
  const { itemList, onFold, containerStyle = {} } = props;
  const [fold, setFold] = useState(false);

  const handleClick = (): void => {
    const curFold = !fold;
    setFold(curFold);
    onFold?.(curFold);
  };

  return (
    <div style={{ position: "relative" }}>
      <span
        onClick={handleClick}
        className={classNames(styles.customIcon, { [styles.fasten]: fold })}
      >
        {fold ? <LeftOutlined /> : <RightOutlined />}
      </span>

      <div
        className={styles.container}
        style={{
          display: fold ? "none" : "block",
          ...containerStyle,
        }}
      >
        {itemList?.map((name) => (
          <div key={name} className={styles.itemWrapper} title={name}>
            <HashLink to={`#${name}`} className={styles.item}>
              {name}
            </HashLink>
          </div>
        ))}
      </div>
    </div>
  );
}
Example #5
Source File: index.tsx    From dashboard with Apache License 2.0 6 votes vote down vote up
MemberDetailInfo = (props: any) => {
    console.log(props.location.query)
    return (
        <PageContainer
            onBack={() => history.goBack()}
            backIcon={<LeftOutlined />}
            header={{
                title: '成员详情',
            }}
        >
        </PageContainer>
    )
}
Example #6
Source File: DesktopSideNav.tsx    From condo with MIT License 5 votes vote down vote up
DesktopSideNav: React.FC<ISideNavProps> = (props) => {
    const { onLogoClick, menuData } = props
    const { link } = useOrganization()
    const { isSmall, toggleCollapsed, isCollapsed } = useLayoutContext()

    const isEmployeeBlocked = get(link, 'isBlocked', false)

    if (isEmployeeBlocked) {
        return null
    }

    // TODO: (Dimitreee) implement mobile nav later
    if (isSmall) {
        return null
    }

    return (
        <>
            <Layout.Sider
                collapsed={isCollapsed}
                theme='light'
                css={SIDE_NAV_STYLES}
                width={SIDE_MENU_WIDTH}
                collapsedWidth={COLLAPSED_SIDE_MENU_WIDTH}
            >
                <LogoContainer>
                    <Logo onClick={onLogoClick} minified={isCollapsed}/>
                </LogoContainer>
                <LayoutTriggerWrapper>
                    <Button
                        onClick={toggleCollapsed}
                        size={'small'}
                        shape={'circle'}
                        icon={isCollapsed ? <RightOutlined style={{ fontSize: '13px' }} /> : <LeftOutlined style={{ fontSize: '13px' }}/>}
                    />
                </LayoutTriggerWrapper>
                <ActionsContainer minified={isCollapsed}>
                    <ResidentActions minified={isCollapsed}/>
                </ActionsContainer>
                <MenuItemsContainer>
                    {menuData}
                </MenuItemsContainer>
                <ServiceSubscriptionIndicator/>
            </Layout.Sider>
            <Layout.Sider
                collapsed={isCollapsed}
                width={SIDE_MENU_WIDTH}
                collapsedWidth={COLLAPSED_SIDE_MENU_WIDTH}
            />
        </>
    )
}
Example #7
Source File: create.tsx    From dashboard with Apache License 2.0 5 votes vote down vote up
CreateContactWay: React.FC = () => {
  const [allStaffs, setAllStaffs] = useState<StaffOption[]>([]);
  const [allTagGroups, setAllTagGroups] = useState<CustomerTagGroupItem[]>([]);

  useEffect(() => {
    QueryCustomerTagGroups({page_size: 5000}).then((res) => {
      if (res.code === 0) {
        setAllTagGroups(res?.data?.items);
      } else {
        message.error(res.message);
      }
    });
  }, []);

  useEffect(() => {
    QuerySimpleStaffs({ page_size: 5000 }).then((res) => {
      if (res.code === 0) {
        setAllStaffs(
          res?.data?.items?.map((item: SimpleStaffInterface) => {
            return {
              label: item.name,
              value: item.ext_id,
              ...item,
            };
          }) || [],
        );
      } else {
        message.error(res.message);
      }
    });
  }, []);

  return (
    <PageContainer
      onBack={() => history.goBack()}
      backIcon={<LeftOutlined />}
      header={{
        title: '创建渠道活码',
      }}
    >
      <ProCard>
        <ContactWayForm
          mode={'create'}
          onFinish={async (values) => {
            const params = { ...values };
            const hide = message.loading('处理中');
            const res: CommonResp = await Create(params);
            hide();
            if (res.code === 0) {
              history.push('/staff-admin/customer-growth/contact-way');
              message.success('添加成功');
              return true;
            }

            if (res.message) {
              message.error(res.message);
              return false;
            }

            message.error('添加失败');
            return false;
          }}
          staffs={allStaffs}
          tagGroups={allTagGroups}
        />
      </ProCard>
    </PageContainer>
  );
}
Example #8
Source File: index.tsx    From fe-v5 with Apache License 2.0 5 votes vote down vote up
LeftTree: React.FC<LeftTreeProps> = ({ clusterGroup = {}, busiGroup = {}, eventLevelGroup = {}, eventTypeGroup = {} }) => {
  const history = useHistory();
  const [collapse, setCollapse] = useState(localStorage.getItem('leftlist') === '1');
  const groupItems: IGroupItemProps[] = [
    clustersGroupContent(clusterGroup),
    busiGroupContent(busiGroup),
    {
      title: '事件级别',
      isShow: eventLevelGroup.isShow,
      render() {
        return (
          <SelectList
            dataSource={[
              { label: '一级告警', value: 1 },
              { label: '二级告警', value: 2 },
              { label: '三级告警', value: 3 },
            ]}
            defaultSelect={eventLevelGroup.defaultSelect}
            allowNotSelect={true}
            onChange={eventLevelGroup?.onChange}
          />
        );
      },
    },
    {
      title: '事件类别',
      isShow: eventTypeGroup.isShow,
      render() {
        return (
          <SelectList
            dataSource={[
              { label: 'Triggered', value: 0 },
              { label: 'Recovered', value: 1 },
            ]}
            defaultSelect={eventTypeGroup.defaultSelect}
            allowNotSelect={true}
            onChange={eventTypeGroup?.onChange}
          />
        );
      },
    },
  ];

  return (
    <div className={collapse ? 'left-area collapse' : 'left-area'}>
      <div
        className='collapse-btn'
        onClick={() => {
          localStorage.setItem('leftlist', !collapse ? '1' : '0');
          setCollapse(!collapse);
        }}
      >
        {!collapse ? <LeftOutlined /> : <RightOutlined />}
      </div>
      {/* 遍历渲染左侧栏内容 */}
      {groupItems.map(
        ({ title, isShow, shrink = false, render }: IGroupItemProps, i) =>
          isShow && (
            <div key={i} className={`left-area-group ${shrink ? 'group-shrink' : ''}`} style={typeof shrink === 'object' ? shrink.style : {}}>
              <div className='left-area-group-title'>
                {title}
                {title === '业务组' && <SettingOutlined onClick={() => history.push(`/busi-groups`)} />}
              </div>
              {render()}
            </div>
          ),
      )}
    </div>
  );
}
Example #9
Source File: create.tsx    From dashboard with Apache License 2.0 5 votes vote down vote up
CreateCustomerMassMsg: React.FC = () => {
  const [currentCustomerMassMsg] = useState<CustomerMassMsgItem>();
  const formRef = useRef<FormInstance>();

  return (
    <PageContainer
      onBack={() => history.goBack()}
      backIcon={<LeftOutlined />}
      header={{
        title: '创建群发',
      }}
    >
      <ProCard>
        <CustomerMassMsgForm
          formRef={formRef}
          mode={'create'}
          onFinish={async (values) => {
            const params = { ...values };
            const hide = message.loading('处理中');
            const res: CommonResp = await Create(params);
            hide();
            if (res.code === 0) {
              history.push('/staff-admin/customer-conversion/customer-mass-msg');
              message.success('添加成功');
              return true;
            }

            if (res.message) {
              message.error(res.message);
              return false;
            }

            message.error('添加失败');
            return false;
          }}
          initialValues={currentCustomerMassMsg}
        />
      </ProCard>
    </PageContainer>
  );
}
Example #10
Source File: SecondaryList.tsx    From ant-simple-draw with MIT License 5 votes vote down vote up
SecondaryList: FC<SecondaryListType> = memo(({ data, fatherData }) => {
  const [oneModuleAll, setOneModuleAll] = useSetState<oneModuleAllType>({
    isShow: false,
    componentInfo: {},
  });

  return (
    <>
      <div
        className={styles.contentContainer}
        style={{
          display: !oneModuleAll.isShow ? 'block' : 'none',
        }}
      >
        {data.map((child, k) => (
          <React.Fragment key={k}>
            <>
              <div className={styles.head}>
                <h2 className={styles.title}>{child.title}</h2>
                <button
                  className={styles.more}
                  onClick={() => setOneModuleAll({ isShow: true, componentInfo: child })}
                >
                  <span>全部</span>
                  <RightOutlined />
                </button>
              </div>
              <Drag list={child.componentList} category={fatherData.category} />
            </>
          </React.Fragment>
        ))}
      </div>
      <div
        className={styles.contentContainer}
        style={{
          display: oneModuleAll.isShow ? 'block' : 'none',
        }}
      >
        <div className={styles.moreList}>
          <button className={styles.more} onClick={() => setOneModuleAll({ isShow: false })}>
            <LeftOutlined />
            <span>{oneModuleAll.componentInfo.title}</span>
          </button>
          <Drag list={oneModuleAll.componentInfo.componentList!} category={fatherData.category} />
        </div>
      </div>
    </>
  );
})
Example #11
Source File: GeneralCarousel.tsx    From next-basics with GNU General Public License v3.0 5 votes vote down vote up
export function GeneralCarousel({
  speed,
  slidesToShow,
  slidesToScroll,
  autoplay,
  dots,
  components,
  carouselStyle,
  pauseOnDotsHover,
  adaptiveHeight,
  infinite,
  responsive,
  onHandleClick,
  noDataDesc,
  arrows,
  dotsTheme,
  useBrick,
  dataSource,
  autoplaySpeed,
}: GeneralCarouselProps): React.ReactElement {
  const comps = Array.isArray(components) ? components : compact([components]);
  const data = Array.isArray(dataSource) ? dataSource : compact([dataSource]);

  const carousel = (
    <Carousel
      className={classNames({
        "carousel-dots-dark": dotsTheme === "dark",
      })}
      style={carouselStyle}
      autoplay={autoplay}
      dots={dots}
      speed={speed}
      autoplaySpeed={autoplaySpeed}
      slidesToShow={slidesToShow}
      slidesToScroll={slidesToScroll}
      pauseOnDotsHover={pauseOnDotsHover}
      arrows={arrows}
      infinite={infinite}
      adaptiveHeight={adaptiveHeight}
      responsive={responsive}
      prevArrow={<LeftOutlined />}
      nextArrow={<RightOutlined />}
    >
      {useBrick
        ? renderCustomBrick(useBrick, data, onHandleClick)
        : renderCustomComp(comps, onHandleClick)}
    </Carousel>
  );

  return (
    <div className={style.generalCarousel}>
      {useBrick ? (
        data.length !== 0 ? (
          carousel
        ) : (
          <Empty description={noDataDesc} />
        )
      ) : comps.length !== 0 ? (
        carousel
      ) : (
        <Empty description={noDataDesc} />
      )}
    </div>
  );
}
Example #12
Source File: EditorHeader.tsx    From datart with Apache License 2.0 5 votes vote down vote up
EditorHeader: FC = memo(({ children }) => {
  const dispatch = useDispatch();
  const history = useHistory();
  const t = useI18NPrefix(`viz.action`);
  const { updateBoard } = useContext(BoardActionContext);
  const { onEditClearActiveWidgets } = useContext(WidgetActionContext);
  const { name, status } = useContext(BoardContext);
  const { saving } = useContext(BoardInfoContext);
  const title = useStatusTitle(name, status);
  const onCloseBoardEditor = () => {
    const pathName = history.location.pathname;
    const prePath = pathName.split('/boardEditor')[0];
    history.push(`${prePath}`);
    dispatch(clearEditBoardState());
  };
  const onUpdateBoard = () => {
    onEditClearActiveWidgets();
    setImmediate(() => {
      updateBoard?.(onCloseBoardEditor);
    });
  };

  return (
    <Wrapper onClick={onEditClearActiveWidgets}>
      <h1 className={classnames({ disabled: status < 2 })}>
        <LeftOutlined onClick={onCloseBoardEditor} />
        {title}
      </h1>
      <Space>
        {children}
        <>
          <Button
            key="cancel"
            icon={<CloseOutlined />}
            onClick={onCloseBoardEditor}
          >
            {t('common.cancel')}
          </Button>

          <Button
            key="update"
            type="primary"
            loading={saving}
            icon={<SaveOutlined />}
            onClick={onUpdateBoard}
          >
            {t('common.save')}
          </Button>
        </>
      </Space>
    </Wrapper>
  );
})
Example #13
Source File: Pagination.tsx    From wildduck-ui with MIT License 5 votes vote down vote up
Pagination: React.FC<IPagination> = ({
	limit,
	previous,
	next,
	page,
	setLimit,
	setPrevious,
	setNext,
	setPage,
}: IPagination) => {
	return (
		<Space>
			<Tooltip title={'Previous'}>
				<Button
					className='ant-btn-icon'
					disabled={_.isEmpty(previous)}
					onClick={() => {
						setPrevious(previous);
						setPage(Math.max(page - 1, 1));
					}}
				>
					<LeftOutlined className='blue-color' />
				</Button>
			</Tooltip>
			<Select
				style={{ width: 120 }}
				defaultValue={limit}
				onSelect={(value) => {
					setLimit(value);
				}}
			>
				{_.map([10, 20, 30, 50, 100, 250], (value) => (
					<Select.Option key={value} value={value}>
						{value + ' / page'}
					</Select.Option>
				))}
			</Select>
			<Tooltip title={'Next'}>
				<Button
					className='ant-btn-icon'
					disabled={_.isEmpty(next)}
					onClick={() => {
						setNext(next);
						setPage(page + 1);
					}}
				>
					<RightOutlined className='blue-color' />
				</Button>
			</Tooltip>
		</Space>
	);
}
Example #14
Source File: header-renderer.tsx    From electron-playground with MIT License 5 votes vote down vote up
AnchorRender = () => {
  const [show, setShow] = useState<boolean>(false)
  useEffect(() => {
    return () => {
      TOC = []
    }
  }, [])

  const handleAnchor = (
    e: React.MouseEvent<HTMLElement>,
    link: { title: React.ReactNode; href: string },
  ) => {
    const dom = document.getElementById(link.href)
    if (dom) {
      dom.scrollIntoView({ behavior: 'smooth' })
      dom.scrollTop -= 60
    }
    e.preventDefault()
  }

  return (
    <>
      <div className={style.contents} onMouseOver={() => setShow(true)}>
        <LeftOutlined className={style.icon} />
        目录
      </div>

      <Drawer title={null} placement='right' closable={false} mask={false} visible={show}>
        <div onMouseLeave={() => setShow(false)} style={{height:'100%'}}>
          <Anchor onClick={handleAnchor} bounds={0}>
            {TOC.map(({ content, level }) => (
              <div key={content} style={{ marginLeft: (level - 1) * 10 }}>
                <Link href={content} title={content} key={content} />
              </div>
            ))}
          </Anchor>
        </div>
      </Drawer>
    </>
  )
}
Example #15
Source File: Icon.tsx    From html2sketch with MIT License 4 votes vote down vote up
IconSymbol: FC = () => {
  return (
    <Row>
      {/*<CaretUpOutlined*/}
      {/*  className="icon"*/}
      {/*  symbolName={'1.General/2.Icons/1.CaretUpOutlined'}*/}
      {/*/>*/}
      {/*  className="icon"*/}
      {/*  symbolName={'1.General/2.Icons/2.MailOutlined'}*/}
      {/*/>*/}
      {/*<StepBackwardOutlined*/}
      {/*  className="icon"*/}
      {/*  symbolName={'1.General/2.Icons/2.StepBackwardOutlined'}*/}
      {/*/>*/}
      {/*<StepForwardOutlined*/}
      {/*  className="icon"*/}
      {/*  symbolName={'1.General/2.Icons/2.StepBackwardOutlined'}*/}
      {/*/>*/}
      <StepForwardOutlined />
      <ShrinkOutlined />
      <ArrowsAltOutlined />
      <DownOutlined />
      <UpOutlined />
      <LeftOutlined />
      <RightOutlined />
      <CaretUpOutlined />
      <CaretDownOutlined />
      <CaretLeftOutlined />
      <CaretRightOutlined />
      <VerticalAlignTopOutlined />
      <RollbackOutlined />
      <FastBackwardOutlined />
      <FastForwardOutlined />
      <DoubleRightOutlined />
      <DoubleLeftOutlined />
      <VerticalLeftOutlined />
      <VerticalRightOutlined />
      <VerticalAlignMiddleOutlined />
      <VerticalAlignBottomOutlined />
      <ForwardOutlined />
      <BackwardOutlined />
      <EnterOutlined />
      <RetweetOutlined />
      <SwapOutlined />
      <SwapLeftOutlined />
      <SwapRightOutlined />
      <ArrowUpOutlined />
      <ArrowDownOutlined />
      <ArrowLeftOutlined />
      <ArrowRightOutlined />
      <LoginOutlined />
      <LogoutOutlined />
      <MenuFoldOutlined />
      <MenuUnfoldOutlined />
      <BorderBottomOutlined />
      <BorderHorizontalOutlined />
      <BorderInnerOutlined />
      <BorderOuterOutlined />
      <BorderLeftOutlined />
      <BorderRightOutlined />
      <BorderTopOutlined />
      <BorderVerticleOutlined />
      <PicCenterOutlined />
      <PicLeftOutlined />
      <PicRightOutlined />
      <RadiusBottomleftOutlined />
      <RadiusBottomrightOutlined />
      <RadiusUpleftOutlined />
      <RadiusUprightOutlined />
      <FullscreenOutlined />
      <FullscreenExitOutlined />
      <QuestionOutlined />
      <PauseOutlined />
      <MinusOutlined />
      <PauseCircleOutlined />
      <InfoOutlined />
      <CloseOutlined />
      <ExclamationOutlined />
      <CheckOutlined />
      <WarningOutlined />
      <IssuesCloseOutlined />
      <StopOutlined />
      <EditOutlined />
      <CopyOutlined />
      <ScissorOutlined />
      <DeleteOutlined />
      <SnippetsOutlined />
      <DiffOutlined />
      <HighlightOutlined />
      <AlignCenterOutlined />
      <AlignLeftOutlined />
      <AlignRightOutlined />
      <BgColorsOutlined />
      <BoldOutlined />
      <ItalicOutlined />
      <UnderlineOutlined />
      <StrikethroughOutlined />
      <RedoOutlined />
      <UndoOutlined />
      <ZoomInOutlined />
      <ZoomOutOutlined />
      <FontColorsOutlined />
      <FontSizeOutlined />
      <LineHeightOutlined />
      <SortAscendingOutlined />
      <SortDescendingOutlined />
      <DragOutlined />
      <OrderedListOutlined />
      <UnorderedListOutlined />
      <RadiusSettingOutlined />
      <ColumnWidthOutlined />
      <ColumnHeightOutlined />
      <AreaChartOutlined />
      <PieChartOutlined />
      <BarChartOutlined />
      <DotChartOutlined />
      <LineChartOutlined />
      <RadarChartOutlined />
      <HeatMapOutlined />
      <FallOutlined />
      <RiseOutlined />
      <StockOutlined />
      <BoxPlotOutlined />
      <FundOutlined />
      <SlidersOutlined />
      <AndroidOutlined />
      <AppleOutlined />
      <WindowsOutlined />
      <IeOutlined />
      <ChromeOutlined />
      <GithubOutlined />
      <AliwangwangOutlined />
      <DingdingOutlined />
      <WeiboSquareOutlined />
      <WeiboCircleOutlined />
      <TaobaoCircleOutlined />
      <Html5Outlined />
      <WeiboOutlined />
      <TwitterOutlined />
      <WechatOutlined />
      <AlipayCircleOutlined />
      <TaobaoOutlined />
      <SkypeOutlined />
      <FacebookOutlined />
      <CodepenOutlined />
      <CodeSandboxOutlined />
      <AmazonOutlined />
      <GoogleOutlined />
      <AlipayOutlined />
      <AntDesignOutlined />
      <AntCloudOutlined />
      <ZhihuOutlined />
      <SlackOutlined />
      <SlackSquareOutlined />
      <BehanceSquareOutlined />
      <DribbbleOutlined />
      <DribbbleSquareOutlined />
      <InstagramOutlined />
      <YuqueOutlined />
      <AlibabaOutlined />
      <YahooOutlined />
      <RedditOutlined />
      <SketchOutlined />
      <AccountBookOutlined />
      <AlertOutlined />
      <ApartmentOutlined />
      <ApiOutlined />
      <QqOutlined />
      <MediumWorkmarkOutlined />
      <GitlabOutlined />
      <MediumOutlined />
      <GooglePlusOutlined />
      <AppstoreAddOutlined />
      <AppstoreOutlined />
      <AudioOutlined />
      <AudioMutedOutlined />
      <AuditOutlined />
      <BankOutlined />
      <BarcodeOutlined />
      <BarsOutlined />
      <BellOutlined />
      <BlockOutlined />
      <BookOutlined />
      <BorderOutlined />
      <BranchesOutlined />
      <BuildOutlined />
      <BulbOutlined />
      <CalculatorOutlined />
      <CalendarOutlined />
      <CameraOutlined />
      <CarOutlined />
      <CarryOutOutlined />
      <CiCircleOutlined />
      <CiOutlined />
      <CloudOutlined />
      <ClearOutlined />
      <ClusterOutlined />
      <CodeOutlined />
      <CoffeeOutlined />
      <CompassOutlined />
      <CompressOutlined />
      <ContactsOutlined />
      <ContainerOutlined />
      <ControlOutlined />
      <CopyrightCircleOutlined />
      <CopyrightOutlined />
      <CreditCardOutlined />
      <CrownOutlined />
      <CustomerServiceOutlined />
      <DashboardOutlined />
      <DatabaseOutlined />
      <DeleteColumnOutlined />
      <DeleteRowOutlined />
      <DisconnectOutlined />
      <DislikeOutlined />
      <DollarCircleOutlined />
      <DollarOutlined />
      <DownloadOutlined />
      <EllipsisOutlined />
      <EnvironmentOutlined />
      <EuroCircleOutlined />
      <EuroOutlined />
      <ExceptionOutlined />
      <ExpandAltOutlined />
      <ExpandOutlined />
      <ExperimentOutlined />
      <ExportOutlined />
      <EyeOutlined />
      <FieldBinaryOutlined />
      <FieldNumberOutlined />
      <FieldStringOutlined />
      <DesktopOutlined />
      <DingtalkOutlined />
      <FileAddOutlined />
      <FileDoneOutlined />
      <FileExcelOutlined />
      <FileExclamationOutlined />
      <FileOutlined />
      <FileImageOutlined />
      <FileJpgOutlined />
      <FileMarkdownOutlined />
      <FilePdfOutlined />
      <FilePptOutlined />
      <FileProtectOutlined />
      <FileSearchOutlined />
      <FileSyncOutlined />
      <FileTextOutlined />
      <FileUnknownOutlined />
      <FileWordOutlined />
      <FilterOutlined />
      <FireOutlined />
      <FlagOutlined />
      <FolderAddOutlined />
      <FolderOutlined />
      <FolderOpenOutlined />
      <ForkOutlined />
      <FormatPainterOutlined />
      <FrownOutlined />
      <FunctionOutlined />
      <FunnelPlotOutlined />
      <GatewayOutlined />
      <GifOutlined />
      <GiftOutlined />
      <GlobalOutlined />
      <GoldOutlined />
      <GroupOutlined />
      <HddOutlined />
      <HeartOutlined />
      <HistoryOutlined />
      <HomeOutlined />
      <HourglassOutlined />
      <IdcardOutlined />
      <ImportOutlined />
      <InboxOutlined />
      <InsertRowAboveOutlined />
      <InsertRowBelowOutlined />
      <InsertRowLeftOutlined />
      <InsertRowRightOutlined />
      <InsuranceOutlined />
      <InteractionOutlined />
      <KeyOutlined />
      <LaptopOutlined />
      <LayoutOutlined />
      <LikeOutlined />
      <LineOutlined />
      <LinkOutlined />
      <Loading3QuartersOutlined />
      <LoadingOutlined />
      <LockOutlined />
      <MailOutlined />
      <ManOutlined />
      <MedicineBoxOutlined />
      <MehOutlined />
      <MenuOutlined />
      <MergeCellsOutlined />
      <MessageOutlined />
      <MobileOutlined />
      <MoneyCollectOutlined />
      <MonitorOutlined />
      <MoreOutlined />
      <NodeCollapseOutlined />
      <NodeExpandOutlined />
      <NodeIndexOutlined />
      <NotificationOutlined />
      <NumberOutlined />
      <PaperClipOutlined />
      <PartitionOutlined />
      <PayCircleOutlined />
      <PercentageOutlined />
      <PhoneOutlined />
      <PictureOutlined />
      <PoundCircleOutlined />
      <PoundOutlined />
      <PoweroffOutlined />
      <PrinterOutlined />
      <ProfileOutlined />
      <ProjectOutlined />
      <PropertySafetyOutlined />
      <PullRequestOutlined />
      <PushpinOutlined />
      <QrcodeOutlined />
      <ReadOutlined />
      <ReconciliationOutlined />
      <RedEnvelopeOutlined />
      <ReloadOutlined />
      <RestOutlined />
      <RobotOutlined />
      <RocketOutlined />
      <SafetyCertificateOutlined />
      <SafetyOutlined />
      <ScanOutlined />
      <ScheduleOutlined />
      <SearchOutlined />
      <SecurityScanOutlined />
      <SelectOutlined />
      <SendOutlined />
      <SettingOutlined />
      <ShakeOutlined />
      <ShareAltOutlined />
      <ShopOutlined />
      <ShoppingCartOutlined />
      <ShoppingOutlined />
      <SisternodeOutlined />
      <SkinOutlined />
      <SmileOutlined />
      <SolutionOutlined />
      <SoundOutlined />
      <SplitCellsOutlined />
      <StarOutlined />
      <SubnodeOutlined />
      <SyncOutlined />
      <TableOutlined />
      <TabletOutlined />
      <TagOutlined />
      <TagsOutlined />
      <TeamOutlined />
      <ThunderboltOutlined />
      <ToTopOutlined />
      <ToolOutlined />
      <TrademarkCircleOutlined />
      <TrademarkOutlined />
      <TransactionOutlined />
      <TrophyOutlined />
      <UngroupOutlined />
      <UnlockOutlined />
      <UploadOutlined />
      <UsbOutlined />
      <UserAddOutlined />
      <UserDeleteOutlined />
      <UserOutlined />
      <UserSwitchOutlined />
      <UsergroupAddOutlined />
      <UsergroupDeleteOutlined />
      <VideoCameraOutlined />
      <WalletOutlined />
      <WifiOutlined />
      <BorderlessTableOutlined />
      <WomanOutlined />
      <BehanceOutlined />
      <DropboxOutlined />
      <DeploymentUnitOutlined />
      <UpCircleOutlined />
      <DownCircleOutlined />
      <LeftCircleOutlined />
      <RightCircleOutlined />
      <UpSquareOutlined />
      <DownSquareOutlined />
      <LeftSquareOutlined />
      <RightSquareOutlined />
      <PlayCircleOutlined />
      <QuestionCircleOutlined />
      <PlusCircleOutlined />
      <PlusSquareOutlined />
      <MinusSquareOutlined />
      <MinusCircleOutlined />
      <InfoCircleOutlined />
      <ExclamationCircleOutlined />
      <CloseCircleOutlined />
      <CloseSquareOutlined />
      <CheckCircleOutlined />
      <CheckSquareOutlined />
      <ClockCircleOutlined />
      <FormOutlined />
      <DashOutlined />
      <SmallDashOutlined />
      <YoutubeOutlined />
      <CodepenCircleOutlined />
      <AliyunOutlined />
      <PlusOutlined />
      <LinkedinOutlined />
      <AimOutlined />
      <BugOutlined />
      <CloudDownloadOutlined />
      <CloudServerOutlined />
      <CloudSyncOutlined />
      <CloudUploadOutlined />
      <CommentOutlined />
      <ConsoleSqlOutlined />
      <EyeInvisibleOutlined />
      <FileGifOutlined />
      <DeliveredProcedureOutlined />
      <FieldTimeOutlined />
      <FileZipOutlined />
      <FolderViewOutlined />
      <FundProjectionScreenOutlined />
      <FundViewOutlined />
      <MacCommandOutlined />
      <PlaySquareOutlined />
      <OneToOneOutlined />
      <RotateLeftOutlined />
      <RotateRightOutlined />
      <SaveOutlined />
      <SwitcherOutlined />
      <TranslationOutlined />
      <VerifiedOutlined />
      <VideoCameraAddOutlined />
      <WhatsAppOutlined />

      {/*</Col>*/}
    </Row>
  );
}
Example #16
Source File: GeneralSignup.tsx    From next-basics with GNU General Public License v3.0 4 votes vote down vote up
export function GeneralSignup(props: GeneralSignupProps): React.ReactElement {
  const [form] = Form.useForm();
  const runtime = getRuntime();
  const brand = runtime.getBrandSettings();
  const enabledFeatures = runtime.getFeatureFlags();
  const { t } = useTranslation(NS_GENERAL_AUTH);
  const [, setForceUpdate] = useState<any>();

  const passwordConfigMap = {
    default: {
      regex: /^.{6,20}$/,
      description: "请输入6至20位密码",
    },
    strong: {
      regex: /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[^a-zA-Z0-9]).{8,20}$/,
      description: "请输入8至20位密码,且同时包含大小写字母、数字、特殊字符",
    },
    backend: {},
  };
  let passwordLevel: keyof typeof passwordConfigMap = "default"; //特性开关
  useEffect(() => {
    if (enabledFeatures["enable-backend-password-config"]) {
      (async () => {
        passwordLevel = "backend";
        passwordConfigMap[passwordLevel] =
          await UserAdminApi_getPasswordConfig();
      })();
    }
  }, []);

  const MIN_USERNAME_LENGTH = 3; //特性开关
  const MAX_USERNAME_LENGTH = 32; //特性开关
  const usernamePattern = new RegExp(
    `^[A-Za-z0-9][A-Za-z0-9|_\\-\\.]{${MIN_USERNAME_LENGTH - 1},${
      MAX_USERNAME_LENGTH - 1
    }}$`
  );

  const iniviteCodePattern = /^[0-9a-zA-Z]{9}$/;
  const hideInvite = iniviteCodePattern.test(getInviteCode());
  const [isCommonSignup, setIsCommonSignup] = useState(true);

  const [isTermsVisible, setIsTermsVisible] = useState(false);

  function showTerms(): void {
    setIsTermsVisible(true);
  }
  function hideTerms(): void {
    setIsTermsVisible(false);
  }
  function agreeTerms(): void {
    form.setFieldsValue({
      terms: true,
    });
    hideTerms();
  }
  function disagreeTerms(): void {
    form.setFieldsValue({
      terms: false,
    });
    hideTerms();
  }

  const [imageHeight, setImageHeight] = useState(window.innerHeight);
  const onWindowResized = () => {
    if (imageHeight < window.innerHeight) {
      setImageHeight(window.innerHeight);
    }
  };
  useEffect(() => {
    const handleWindowResized = debounce(onWindowResized, 500, {
      leading: false,
    });
    window.addEventListener("resize", handleWindowResized);
    return () => {
      window.removeEventListener("resize", handleWindowResized);
    };
  }, []);

  const timer = useRef<any>();
  const count = useRef<number>(duration);
  const [verifyBtnDisabled, setVerifyBtnDisabled] = useState(true);
  const [content, setContent] = useState(t(K.GET_VERIFY_CODE));
  const [messageId, setMessageId] = useState("");
  const handleVerifyBtnClick = async (
    e: React.MouseEvent<HTMLElement, MouseEvent>
  ) => {
    if (timer.current) return;
    count.current -= 1;
    setContent(t(K.GET_VERIFY_CODE_TIPS, { count: count.current }));
    setVerifyBtnDisabled(true);
    timer.current = setInterval(() => {
      count.current -= 1;
      setContent(t(K.GET_VERIFY_CODE_TIPS, { count: count.current }));
      if (count.current === 0) {
        clearInterval(timer.current);
        timer.current = null;
        count.current = duration;
        setVerifyBtnDisabled(false);
        setContent(t(K.GET_VERIFY_CODE));
      }
    }, 1000);
    const result = await CustomerApi_sendApplicationVerificationCode({
      phone_number: form.getFieldValue("phone"),
    });
    result.message_id && setMessageId(result.message_id);
  };

  const redirect = async (result: Record<string, any>): Promise<void> => {
    runtime.reloadSharedData();
    await runtime.reloadMicroApps();
    resetLegacyIframe();
    authenticate({
      org: result.org,
      username: result.username,
      userInstanceId: result.userInstanceId,
      accessRule: result.accessRule,
    });
    const { state } = getHistory().location;
    const from =
      state && state.from
        ? state.from
        : {
            pathname: "/",
          };
    const redirect = createLocation(from);
    getHistory().push(redirect);
  };

  const onFinish = async (values: Record<string, any>): Promise<void> => {
    values.password = encryptValue(values.password);
    try {
      let result: Record<string, any>;
      if (isCommonSignup && !hideInvite) {
        result = await OrgApi_saaSOrgRegister(
          assign(omit(values, ["terms", "password2"]), {
            message_id: messageId,
          }) as OrgApi_SaaSOrgRegisterRequestBody
        );
      } else {
        result = await AuthApi_register(
          assign(
            omit(values, ["terms", "password2", "username"]),
            hideInvite
              ? { invite: getInviteCode(), name: values["username"] }
              : { name: values["username"] }
          ) as AuthApi_RegisterRequestBody
        );
      }
      if (result.loggedIn) {
        redirect(result);
      }
      message.success(t(K.REGISTER_SUCCESS));
    } catch (error) {
      Modal.error({
        title: t(K.REGISTER_FAILED),
        content:
          isCommonSignup && !hideInvite
            ? t(K.WRONG_VERIFICATION_CODE)
            : t(K.WRONG_INVITE_CODE),
      });
    }
  };

  return (
    <>
      <div className={styles.signupWrapper}>
        <div className={styles.signupHeader}>
          <div className={styles.logoBar}>
            <Link to="/">
              {brand.auth_logo_url ? (
                <img
                  src={brand.auth_logo_url}
                  style={{ height: 32, verticalAlign: "middle" }}
                />
              ) : (
                <Logo height={32} style={{ verticalAlign: "middle" }} />
              )}
            </Link>
          </div>
        </div>
        <div className={styles.signupImg}>
          <img src={loginPng} style={{ height: imageHeight }} />
        </div>
        <div className={styles.signupForm}>
          <Card bordered={false}>
            {!hideInvite &&
              (isCommonSignup ? (
                <a
                  onClick={() => {
                    setIsCommonSignup(false);
                  }}
                  style={{ alignSelf: "flex-end" }}
                  id="JumpToJoinFormLink"
                >
                  {t(K.JOIN_THE_ORGANIZATION)} <RightOutlined />
                </a>
              ) : (
                <a
                  onClick={() => {
                    setIsCommonSignup(true);
                  }}
                  id="JumpToCommonFormLink"
                >
                  <LeftOutlined /> {t(K.REGISTER_COMMONLY)}
                </a>
              ))}
            {!hideInvite && isCommonSignup ? (
              <div className={styles.title}>{t(K.REGISTER_ACCOUNT)}</div>
            ) : (
              <div className={styles.title}>{t(K.REGISTER_AND_JOIN)}</div>
            )}
            <Form name="signupForm" form={form} onFinish={onFinish}>
              <Form.Item
                validateFirst={true}
                name="username"
                rules={[
                  {
                    required: true,
                    message: t(K.USERNAME_TIPS, {
                      minLength: 3,
                      maxLength: 32,
                    }),
                  },
                  {
                    pattern: usernamePattern,
                    message: t(K.USERNAME_TIPS, {
                      minLength: 3,
                      maxLength: 32,
                    }),
                  },
                  {
                    validator: (
                      _: any,
                      value: any,
                      callback: (value?: string) => void
                    ) =>
                      validateMap["airNameValidator"](
                        value,
                        callback,
                        setForceUpdate
                      ),
                  },
                ]}
              >
                <Input
                  prefix={<UserOutlined className={styles.inputPrefixIcon} />}
                  placeholder={t(K.USERNAME)}
                />
              </Form.Item>
              {enabledFeatures["enable-nickname-config"] && hideInvite && (
                <Form.Item validateFirst={false} name="nickname">
                  <Input
                    prefix={
                      <SolutionOutlined className={styles.inputPrefixIcon} />
                    }
                    placeholder={t(K.NICKNAME)}
                  />
                </Form.Item>
              )}
              <Form.Item
                name="email"
                validateFirst={true}
                rules={[
                  { required: true, message: t(K.PLEASE_ENTER_VALID_EMAIL) },
                  { type: "email", message: t(K.PLEASE_ENTER_VALID_EMAIL) },
                  {
                    validator: (
                      _: any,
                      value: any,
                      callback: (value?: string) => void
                    ) =>
                      validateMap["airEmailValidator"](
                        value,
                        callback,
                        setForceUpdate
                      ),
                  },
                ]}
              >
                <Input
                  prefix={<MailOutlined className={styles.inputPrefixIcon} />}
                  type="email"
                  placeholder={t(K.EMAIL)}
                />
              </Form.Item>
              <Form.Item
                validateFirst={true}
                name="password"
                rules={[
                  { required: true, message: t(K.PLEASE_INPUT_PASSWORD) },
                  {
                    pattern: passwordConfigMap[passwordLevel].regex,
                    message: passwordConfigMap[passwordLevel].description,
                  },
                ]}
              >
                <Input
                  prefix={<LockOutlined className={styles.inputPrefixIcon} />}
                  type="password"
                  placeholder={t(K.PASSWORD)}
                />
              </Form.Item>
              <Form.Item
                dependencies={["password"]}
                name="password2"
                rules={[
                  { required: true, message: t(K.PLEASE_INPUT_PASSWORD) },
                  ({ getFieldValue }) => ({
                    validator(_, value) {
                      if (!value || getFieldValue("password") === value) {
                        return Promise.resolve();
                      }
                      return Promise.reject(
                        new Error(t(K.TWO_PASSWORDS_ARE_INCONSISTENT))
                      );
                    },
                  }),
                ]}
              >
                <Input
                  prefix={<LockOutlined className={styles.inputPrefixIcon} />}
                  type="password"
                  placeholder={t(K.PASSWORD_CONFIRM)}
                />
              </Form.Item>
              {!hideInvite &&
                (isCommonSignup ? (
                  <>
                    <Form.Item
                      validateFirst={true}
                      rules={[
                        {
                          required: true,
                          message: t(K.PLEASE_FILL_IN_VALID_PHONE_NUMBER),
                        },
                        {
                          validator: (_, value) => {
                            if (
                              /^(?=\d{11}$)^1(?:3\d|4[57]|5[^4\D]|7[^249\D]|8\d)\d{8}$/.test(
                                value
                              )
                            ) {
                              setVerifyBtnDisabled(false);
                              return Promise.resolve();
                            }
                            setVerifyBtnDisabled(true);
                            return Promise.reject(
                              new Error(t(K.PLEASE_FILL_IN_VALID_PHONE_NUMBER))
                            );
                          },
                        },
                      ]}
                      name="phone"
                    >
                      <Input
                        prefix={
                          <PhoneOutlined
                            className={styles.inputPrefixIcon}
                            rotate={90}
                          />
                        }
                        suffix={
                          <Button
                            disabled={verifyBtnDisabled}
                            type="text"
                            onClick={handleVerifyBtnClick}
                            id="verifyBtn"
                          >
                            {content}
                          </Button>
                        }
                        placeholder={t(K.PHONE)}
                      />
                    </Form.Item>
                    <Form.Item
                      rules={[
                        {
                          required: true,
                          message: t(K.PLEASE_INPUT_PHRASE),
                        },
                        {
                          pattern: /^\d{6}$/,
                          message: t(K.PLEASE_INPUT_VALID_PHRASE),
                        },
                      ]}
                      name="verification_code"
                    >
                      <Input
                        prefix={
                          <SafetyOutlined className={styles.inputPrefixIcon} />
                        }
                        placeholder={t(K.VERIFY_CODE)}
                      ></Input>
                    </Form.Item>
                  </>
                ) : (
                  <Form.Item
                    validateFirst={true}
                    name="invite"
                    rules={[
                      {
                        required: true,
                        message: t([K.PLEASE_FILL_IN_INVITE_CODE]),
                      },
                      {
                        pattern: iniviteCodePattern,
                        message: t([K.PLEASE_FILL_IN_INVITE_CODE]),
                      },
                    ]}
                  >
                    <Input
                      prefix={
                        <GeneralIcon
                          icon={{
                            lib: "easyops",
                            icon: "release-management",
                            category: "menu",
                            color: "rgba(0,0,0,.25)",
                          }}
                        />
                      }
                      type="text"
                      placeholder={t(K.INVITE_CODE)}
                    />
                  </Form.Item>
                ))}
              <Form.Item
                name="terms"
                valuePropName="checked"
                rules={[
                  {
                    validator: (_, value) =>
                      value
                        ? Promise.resolve()
                        : Promise.reject(new Error(t(K.AGREE_TERMS_TIPS))),
                  },
                ]}
              >
                <Checkbox>
                  {t(K.AGREE_TERMS)}
                  <a
                    onClick={() => {
                      showTerms();
                    }}
                    id="TermsLink"
                  >
                    {t(K.UWINTECH_TERMS)}
                  </a>
                </Checkbox>
              </Form.Item>
              <Form.Item>
                <Button
                  type="primary"
                  htmlType="submit"
                  style={{
                    width: "100%",
                    height: 34,
                  }}
                  id="submitBtn"
                >
                  {t(K.REGISTER)}
                </Button>
              </Form.Item>
              <Form.Item>
                <div style={{ textAlign: "center" }}>
                  {t(K.ALREADY_HAVE_AN_ACCOUNT)}
                  <a
                    id="LogInLink"
                    onClick={() => {
                      getHistory().push(
                        createLocation({
                          pathname: props.loginUrl ?? "/auth/login",
                        })
                      );
                    }}
                  >
                    {t(K.LOGIN_IMMEDIATELY)}
                  </a>
                </div>
              </Form.Item>
            </Form>
          </Card>
          <Modal
            visible={isTermsVisible}
            title={t(K.UWINTECH_TERMS)}
            width={598}
            okType="default"
            cancelText={t(K.DISAGREE)}
            okText={t(K.AGREE)}
            closable={false}
            onCancel={() => {
              disagreeTerms();
            }}
            onOk={() => {
              agreeTerms();
            }}
          >
            <Terms />
          </Modal>
        </div>
      </div>
    </>
  );
}
Example #17
Source File: SessionRecordingsTable.tsx    From posthog-foss with MIT License 4 votes vote down vote up
export function SessionRecordingsTable({ personUUID, isPersonPage = false }: SessionRecordingsTableProps): JSX.Element {
    const sessionRecordingsTableLogicInstance = sessionRecordingsTableLogic({ personUUID })
    const {
        sessionRecordings,
        sessionRecordingsResponseLoading,
        sessionRecordingId,
        entityFilters,
        propertyFilters,
        hasNext,
        hasPrev,
        fromDate,
        toDate,
        durationFilter,
        showFilters,
    } = useValues(sessionRecordingsTableLogicInstance)
    const {
        openSessionPlayer,
        closeSessionPlayer,
        setEntityFilters,
        setPropertyFilters,
        loadNext,
        loadPrev,
        setDateRange,
        setDurationFilter,
        enableFilter,
    } = useActions(sessionRecordingsTableLogicInstance)
    const { preflight } = useValues(preflightLogic)

    const columns: LemonTableColumns<SessionRecordingType> = [
        {
            title: 'Start time',
            render: function RenderStartTime(_: any, sessionRecording: SessionRecordingType) {
                return <TZLabel time={sessionRecording.start_time} formatString="MMMM DD, YYYY h:mm" />
            },
        },
        {
            title: 'Duration',
            render: function RenderDuration(_: any, sessionRecording: SessionRecordingType) {
                return <span>{humanFriendlyDuration(sessionRecording.recording_duration)}</span>
            },
        },
        {
            title: 'Person',
            key: 'person',
            render: function RenderPersonLink(_: any, sessionRecording: SessionRecordingType) {
                return <PersonHeader withIcon person={sessionRecording.person} />
            },
        },

        {
            render: function RenderPlayButton(_: any, sessionRecording: SessionRecordingType) {
                return (
                    <div className="play-button-container">
                        <Button
                            className={sessionRecording.viewed ? 'play-button viewed' : 'play-button'}
                            data-attr="session-recordings-button"
                            icon={<PlayCircleOutlined />}
                        >
                            Watch recording
                        </Button>
                    </div>
                )
            },
        },
    ]
    return (
        <div className="session-recordings-table" data-attr="session-recordings-table">
            <Row className="filter-row">
                <div className="filter-container" style={{ display: showFilters ? undefined : 'none' }}>
                    <div>
                        <Typography.Text strong>
                            {`Filter by events and actions `}
                            <Tooltip title="Show recordings where all of the events or actions listed below happen.">
                                <InfoCircleOutlined className="info-icon" />
                            </Tooltip>
                        </Typography.Text>
                        <ActionFilter
                            fullWidth={true}
                            filters={entityFilters}
                            setFilters={(payload) => {
                                setEntityFilters(payload)
                            }}
                            typeKey={isPersonPage ? `person-${personUUID}` : 'session-recordings'}
                            hideMathSelector={true}
                            buttonCopy="Add another filter"
                            horizontalUI
                            stripeActionRow={false}
                            propertyFilterWrapperClassName="session-recording-action-property-filter"
                            customRowPrefix=""
                            hideRename
                            showOr
                            renderRow={(props) => <FilterRow {...props} />}
                            showNestedArrow={false}
                            actionsTaxonomicGroupTypes={[
                                TaxonomicFilterGroupType.Actions,
                                TaxonomicFilterGroupType.Events,
                            ]}
                            propertiesTaxonomicGroupTypes={[
                                TaxonomicFilterGroupType.EventProperties,
                                TaxonomicFilterGroupType.Elements,
                            ]}
                        />
                    </div>
                    {!isPersonPage && preflight?.is_clickhouse_enabled && (
                        <div className="mt-2">
                            <Typography.Text strong>
                                {`Filter by persons and cohorts `}
                                <Tooltip title="Show recordings by persons who match the set criteria">
                                    <InfoCircleOutlined className="info-icon" />
                                </Tooltip>
                            </Typography.Text>
                            <PropertyFilters
                                popoverPlacement="bottomRight"
                                pageKey={isPersonPage ? `person-${personUUID}` : 'session-recordings'}
                                taxonomicGroupTypes={[
                                    TaxonomicFilterGroupType.PersonProperties,
                                    TaxonomicFilterGroupType.Cohorts,
                                ]}
                                propertyFilters={propertyFilters}
                                onChange={(properties) => {
                                    setPropertyFilters(properties)
                                }}
                            />
                        </div>
                    )}
                </div>
                <Button
                    style={{ display: showFilters ? 'none' : undefined }}
                    onClick={() => {
                        enableFilter()
                        if (isPersonPage) {
                            const entityFilterButtons = document.querySelectorAll('.entity-filter-row button')
                            if (entityFilterButtons.length > 0) {
                                ;(entityFilterButtons[0] as HTMLElement).click()
                            }
                        }
                    }}
                >
                    <FilterOutlined /> Filter recordings
                </Button>

                <Row className="time-filter-row">
                    <Row className="time-filter">
                        <DateFilter
                            makeLabel={(key) => (
                                <>
                                    <CalendarOutlined />
                                    <span> {key}</span>
                                </>
                            )}
                            defaultValue="Last 7 days"
                            bordered={true}
                            dateFrom={fromDate ?? undefined}
                            dateTo={toDate ?? undefined}
                            onChange={(changedDateFrom, changedDateTo) => {
                                setDateRange(changedDateFrom, changedDateTo)
                            }}
                            dateOptions={{
                                Custom: { values: [] },
                                'Last 24 hours': { values: ['-24h'] },
                                'Last 7 days': { values: ['-7d'] },
                                'Last 21 days': { values: ['-21d'] },
                            }}
                        />
                    </Row>
                    <Row className="time-filter">
                        <Typography.Text className="filter-label">Duration</Typography.Text>
                        <DurationFilter
                            onChange={(newFilter) => {
                                setDurationFilter(newFilter)
                            }}
                            initialFilter={durationFilter}
                            pageKey={isPersonPage ? `person-${personUUID}` : 'session-recordings'}
                        />
                    </Row>
                </Row>
            </Row>

            <LemonTable
                dataSource={sessionRecordings}
                columns={columns}
                loading={sessionRecordingsResponseLoading}
                onRow={(sessionRecording) => ({
                    onClick: (e) => {
                        // Lets the link to the person open the person's page and not the session recording
                        if (!(e.target as HTMLElement).closest('a')) {
                            openSessionPlayer(sessionRecording.id, RecordingWatchedSource.RecordingsList)
                        }
                    },
                })}
                rowClassName="cursor-pointer"
                data-attr="session-recording-table"
            />
            {(hasPrev || hasNext) && (
                <Row className="pagination-control">
                    <Button
                        type="link"
                        disabled={!hasPrev}
                        onClick={() => {
                            loadPrev()
                            window.scrollTo(0, 0)
                        }}
                    >
                        <LeftOutlined /> Previous
                    </Button>
                    <Button
                        type="link"
                        disabled={!hasNext}
                        onClick={() => {
                            loadNext()
                            window.scrollTo(0, 0)
                        }}
                    >
                        Next <RightOutlined />
                    </Button>
                </Row>
            )}
            <div style={{ marginBottom: 64 }} />
            {!!sessionRecordingId && <SessionPlayerDrawer isPersonPage={isPersonPage} onClose={closeSessionPlayer} />}
        </div>
    )
}
Example #18
Source File: SearchMsgView.tsx    From dashboard with Apache License 2.0 4 votes vote down vote up
SearchMsgView: React.FC<SearchMsgViewProps> = (props) => {
  const {searchMsgList, setShowSearchMessageView, currentChatSession, currentStaff, total: msgTotal} = props
  const [chatMessageList, setChatMessageList] = useState<ChatMessage.Item[]>([]) // 消息
  const [showContextMsgList, setShowContextMsgList] = useState(false)
  const [page, setPage] = useState(1)
  const [loadingMoreEarlierData, setLoadingMoreEarlierData] = useState(false)
  const [loadingMoreNearData, setLoadingMoreNearData] = useState(false)

  const queryChatMessages = (action: string, min_id?: string, max_id?: string, pageSize?: number, pageNth?: number) => {
    return QueryChatMessages({
      page_size: pageSize,
      page: pageNth,
      ext_staff_id: currentStaff?.ext_staff_id || 'none',
      receiver_id: currentChatSession.peer_ext_id || 'none',
      max_id,
      min_id
    }).then(res => {
      if (res?.code === 0) {
        setChatMessageList([])
        let newMsgList: any[] = []
        if (action === 'getEarlier') {
          newMsgList = [...res.data.items]?.concat([...chatMessageList]).filter((m: any) => m !== null)
          setLoadingMoreEarlierData(false)
        } else if (action === 'getNear') {
          newMsgList = [...chatMessageList]?.concat([...res.data.items]).filter((m: any) => m !== null)
          setLoadingMoreNearData(false)
        }
        newMsgList?.map((msg: any) => {
          return {...msg, showTime: false}
        })
        for (let i = 0; i < newMsgList.length; i += 1) {
          if (Math.floor(Number(newMsgList[i]?.msgtime) / 10800 / 1000) !== Math.floor(Number(newMsgList[i + 1]?.msgtime) / 10800 / 1000)) {
            newMsgList[i].showTime = true
          }
        }
        setChatMessageList(newMsgList || [])
        setPage(() => page + 1)
      } else {
        message.error(res?.message)
      }
    })
  }

  // 下拉 min_id
  const loadEarlierData = () => {
    setLoadingMoreEarlierData(true)
    queryChatMessages('getEarlier', chatMessageList?.[0]?.id, undefined, 10, page)
  }

  // 上拉 max_id
  const loadNearData = () => {
    setLoadingMoreNearData(true)
    queryChatMessages('getNear', undefined, chatMessageList?.[chatMessageList?.length - 1]?.id, 10, page)
  }

  // 获取初始的上下文列表
  const getInitialChatMessageList = (msg: SearchMessage.Item) => {
    QueryChatMessages({
      page_size: 10,
      page: 1,
      ext_staff_id: currentStaff?.ext_staff_id || 'none',
      receiver_id: currentChatSession.peer_ext_id || 'none',
      max_id: msg.id
    }).then(maxRes => {
      if (maxRes?.code === 0) {
        QueryChatMessages({
          page_size: 10,
          page: 1,
          ext_staff_id: currentStaff?.ext_staff_id || 'none',
          receiver_id: currentChatSession.peer_ext_id || 'none',
          min_id: msg.id
        }).then(minRes => {
          // 查询结果按时间从小到大排序。用min_id查询比目标时间更早的数据
          if (minRes?.code === 0) {
            const initialChatMessageList = [...minRes.data.items].concat([msg]).concat([...maxRes.data.items]).filter((m: any) => m !== null) || []
            initialChatMessageList?.map((initialMsg: any) => {
              return {...initialMsg, showTime: false}
            })
            for (let i = 0; i < initialChatMessageList.length; i += 1) {
              if (Math.floor(Number(initialChatMessageList[i]?.msgtime) / 10800 / 1000) !== Math.floor(Number(initialChatMessageList[i + 1]?.msgtime) / 10800 / 1000)) {
                initialChatMessageList[i].showTime = true
              }
            }
            setChatMessageList(initialChatMessageList || [])
            setPage(() => page + 1)
          }
        })
      }

    })
  }

  return (
    <div className={styles.searchViewContainerOuter}>
      <div className={styles.back}>
        <Button type={'link'} onClick={() => {
          if (showContextMsgList) {
            setShowContextMsgList(false)
            setPage(1)
          } else {
            setShowSearchMessageView(false)
          }
        }}><LeftOutlined/>返回</Button>
      </div>
      <div className={styles.searchViewContainer}>
        <div className={styles.searchMessageView} style={{display: showContextMsgList ? 'none' : 'block'}}>
          {
            searchMsgList.map(msg => (
              <div className={styles.msgItem}>
                <img src={msg.sender_avatar}/>
                <div className={styles.textBox}>
                  <div className={styles.nameAndTime}>
                    <span>{msg.sender_name}</span>
                    <span>{parseTime(msg.msgtime)}</span>
                  </div>
                  <div className={styles.textContent}>
                    <div>
                      {msg.content_text}
                    </div>
                    <Button type={'link'} onClick={() => {
                      getInitialChatMessageList(msg)
                      setShowContextMsgList(true)
                    }}>查看上下文</Button>
                  </div>
                </div>
              </div>
            ))
          }
          {
            searchMsgList.length > 0 && <p className={styles.noMore}>没有更多了</p>
          }
        </div>
        <div className={styles.contextMsgView} style={{display: showContextMsgList ? 'block' : 'none'}}>
          <MessageView chatMessageList={chatMessageList || []} currentStaff={currentStaff}
                       loadEarlierData={loadEarlierData} loadNearData={loadNearData}
                       loadingMoreEarlierData={loadingMoreEarlierData}
                       loadingMoreNearData={loadingMoreNearData} currentChatSession={currentChatSession}
                       total={msgTotal} isContextView={true}/>
        </div>
      </div>

    </div>
  )
}
Example #19
Source File: HorizontalScrollable.tsx    From oxen-website with GNU General Public License v3.0 4 votes vote down vote up
function HorizontalScrollableInner(props: Props) {
  const { onItemClick, children } = props;

  const scrollRef = useRef(null);
  const innerContentRef = useRef(null);

  const { x } = useScroll(scrollRef);
  const pageWidth = useWindowSize().width;
  const scrollDistance = pageWidth > 1400 ? 450 : pageWidth / 3;

  const [rightScrollHidden, setRightScrollHidden] = useState(false);

  const { isDesktop } = useContext(ScreenContext);

  const handleLeftScroll = () => {
    scrollRef.current.scrollBy({
      left: -scrollDistance,
      behavior: 'smooth',
    });
  };

  const handleRightScroll = () => {
    scrollRef.current.scrollBy({
      left: scrollDistance,
      behavior: 'smooth',
    });
  };

  function handleItemClick() {
    if (onItemClick) {
      onItemClick();
    }
  }

  useEffect(() => {
    const isFullRight =
      scrollRef.current.scrollWidth - scrollRef.current.clientWidth ===
      scrollRef.current.scrollLeft;

    const tooSmallToScroll =
      innerContentRef.current.clientWidth < scrollRef.current.clientWidth;

    setRightScrollHidden(tooSmallToScroll || isFullRight);
  }, [x, children]);

  return (
    <div className="relative flex w-full">
      <div
        className={classNames(
          'absolute left-0 flex items-center justify-between h-full w-full',
          !isDesktop && 'hidden',
        )}
      >
        <div
          className={classNames(
            'flex flex-col justify-center h-full z-50 duration-300 -ml-8',
            x <= 1 && 'opacity-0',
          )}
        >
          <LeftOutlined
            onClick={handleLeftScroll}
            className={classNames('h-20 mt-1 cursor-pointer')}
          />
        </div>

        <div
          className={classNames(
            'flex flex-col justify-center h-full z-50 duration-300 -mr-8',
            rightScrollHidden && 'opacity-0',
          )}
        >
          <RightOutlined
            onClick={handleRightScroll}
            className="h-20 mt-1 cursor-pointer"
          />
        </div>
      </div>
      <div
        ref={scrollRef}
        className={classNames(
          'relative',
          'w-full',
          'hide_scroll',
          'scrolling-touch hide-scroll',
          isDesktop ? 'overflow-x-scroll' : 'overflow-x-scroll',
        )}
      >
        <div
          ref={innerContentRef}
          className={classNames('flex space-x-8 overflow-y-visible')}
          style={{
            width: 'min-content',
            marginLeft: `${!isDesktop ? UI.PAGE_CONTAINED_PADDING_VW : 0}vw`,
            paddingRight: `${!isDesktop ? UI.PAGE_CONTAINED_PADDING_VW : 0}vw`,
          }}
        >
          {children}
        </div>
      </div>
    </div>
  );
}
Example #20
Source File: index.tsx    From datart with Apache License 2.0 4 votes vote down vote up
export function ListTitle({
  title,
  subTitle,
  search,
  add,
  more,
  back,
  className,
  onSearch,
  onPrevious,
  onNext,
}: ListTitleProps) {
  const [searchbarVisible, setSearchbarVisible] = useState(false);
  const t = useI18NPrefix('components.listTitle');
  const toggleSearchbar = useCallback(() => {
    setSearchbarVisible(!searchbarVisible);
  }, [searchbarVisible]);

  const moreMenuClick = useCallback(
    ({ key }) => {
      more?.callback(key, onPrevious, onNext);
    },
    [more, onPrevious, onNext],
  );

  const backClick = useCallback(() => {
    onPrevious && onPrevious();
  }, [onPrevious]);

  return (
    <Wrapper className={className}>
      <Title className="title">
        {back && (
          <span className="back" onClick={backClick}>
            <LeftOutlined />
          </span>
        )}
        {title && <h3>{title}</h3>}
        {subTitle && <h5>{subTitle}</h5>}
        <Space size={SPACE_UNIT}>
          {search && (
            <Tooltip title={t('search')} placement="bottom">
              <ToolbarButton
                size="small"
                icon={<SearchOutlined />}
                color={searchbarVisible ? PRIMARY : void 0}
                onClick={toggleSearchbar}
              />
            </Tooltip>
          )}
          {add && <AddButton dataSource={add} />}
          {more && (
            <Popup
              getPopupContainer={triggerNode =>
                triggerNode.parentElement as HTMLElement
              }
              trigger={['click']}
              placement="bottomRight"
              content={
                <MenuWrapper
                  prefixCls="ant-dropdown-menu"
                  selectable={false}
                  onClick={moreMenuClick}
                >
                  {more.items.map(({ key, text, prefix, suffix }) => (
                    <MenuListItem
                      key={key}
                      {...(prefix && { prefix })}
                      {...(suffix && { suffix })}
                    >
                      {text}
                    </MenuListItem>
                  ))}
                </MenuWrapper>
              }
            >
              <ToolbarButton size="small" icon={<MoreOutlined />} />
            </Popup>
          )}
        </Space>
      </Title>
      <Searchbar visible={searchbarVisible}>
        <Input
          className="search-input"
          prefix={<SearchOutlined className="icon" />}
          placeholder={t('searchValue')}
          bordered={false}
          onChange={onSearch}
        />
      </Searchbar>
    </Wrapper>
  );
}
Example #21
Source File: PopularTimes.tsx    From office-hours with GNU General Public License v3.0 4 votes vote down vote up
export default function PopularTimes({ heatmap }: HeatmapProps): ReactElement {
  const [currentDayOfWeek, setCurrentDayOfWeek] = useState(new Date().getDay());
  const [firstHour, lastHour] = findWeekMinAndMax(heatmap);
  const dailyAvgWaitTimes: number[] = chunk(heatmap, 24).map((hours) => {
    const filteredOfficeHours = hours.filter((v) => v !== -1);
    return filteredOfficeHours.length > 0 ? mean(filteredOfficeHours) : -1;
  });

  return (
    <div className="hide-in-percy">
      <TitleRow>
        <h2>Wait Times on</h2>
        <Dropdown
          trigger={["click"]}
          overlay={
            <Menu>
              {DAYS_OF_WEEK.map((dayName, i) => (
                <Menu.Item key={dayName}>
                  <a onClick={() => setCurrentDayOfWeek(i)}>{dayName}</a>
                </Menu.Item>
              ))}
            </Menu>
          }
        >
          <WeekdayDropdown>
            {DAYS_OF_WEEK[currentDayOfWeek]}
            <DownOutlined />
          </WeekdayDropdown>
        </Dropdown>
      </TitleRow>
      <GraphWithArrow>
        <GraphArrowButtons
          onClick={() => setCurrentDayOfWeek((7 + currentDayOfWeek - 1) % 7)}
        >
          <LeftOutlined />
        </GraphArrowButtons>
        <GraphContainer>
          <ParentSize>
            {({ width }) => (
              <TimeGraph
                values={heatmap
                  .slice(currentDayOfWeek * 24, (currentDayOfWeek + 1) * 24 - 1)
                  .map((i) => (i < 0 ? 0 : Math.floor(i)))}
                maxTime={Math.max(...heatmap)}
                firstHour={firstHour}
                lastHour={lastHour}
                width={width}
                height={220}
              />
            )}
          </ParentSize>
        </GraphContainer>
        <GraphArrowButtons
          onClick={() => setCurrentDayOfWeek((currentDayOfWeek + 1) % 7)}
        >
          <RightOutlined />
        </GraphArrowButtons>
      </GraphWithArrow>
      {dailyAvgWaitTimes[currentDayOfWeek] >= 0 && (
        <GraphNotes>
          <ClockCircleOutlined /> {DAYS_OF_WEEK[currentDayOfWeek]}s have{" "}
          <strong>
            {generateBusyText(currentDayOfWeek, dailyAvgWaitTimes)}
          </strong>{" "}
          wait times.
        </GraphNotes>
      )}
      {new Date().getDay() === currentDayOfWeek &&
        heatmap[currentDayOfWeek * 24 + new Date().getHours()] >= 0 && (
          <GraphNotes>
            <HourglassOutlined /> At {formatDateHour(new Date().getHours())},
            people generally wait{" "}
            <strong>
              {formatWaitTime(
                heatmap[currentDayOfWeek * 24 + new Date().getHours()]
              )}
            </strong>
            .
          </GraphNotes>
        )}
    </div>
  );
}
Example #22
Source File: AddCustomNetwork.tsx    From subscan-multisig-react with Apache License 2.0 4 votes vote down vote up
AddCustomNetwork = (props: AddCustomNetworkProps) => {
  const { t } = useTranslation();
  const { network } = useApi();
  const mainColor = useMemo(() => {
    return getThemeColor(network);
  }, [network]);

  const [rpcName, setRpcName] = useState('');
  const [rpcUrl, setRpcUrl] = useState('');
  const [explorerHostName, setExplorerHostName] = useState('');
  // const [explorerUrl, setExplorerUrl] = useState('');

  const networks = useMemo(() => _.values(chains), []);

  const [addNetworkLoading, setAddNetworkLoading] = useState(false);
  const [addNetworkErrorDialogVisible, setAddNetworkErrorDialogVisible] = useState(false);
  const [addNetworkErrorContent, setAddNetworkErrorContent] = useState('');

  // eslint-disable-next-line complexity
  useEffect(() => {
    if (props.editNetwork) {
      setRpcName(props.editNetwork?.displayName || '');
      setRpcUrl(props.editNetwork?.rpc || '');
      setExplorerHostName(props.editNetwork?.explorerHostName || '');
    } else {
      setRpcName('');
      setRpcUrl('');
      setExplorerHostName('');
    }
  }, [props.editNetwork]);

  const saveNewCustomNetwork = () => {
    if (!rpcName.trim() || !rpcUrl.trim()) {
      return;
    }

    setAddNetworkLoading(true);
    try {
      const provider = new WsProvider(rpcUrl);
      const api = new ApiPromise({
        provider,
      });

      const onReady = async () => {
        setAddNetworkLoading(false);
        api.off('ready', onReady);
        api.off('error', onError);
        api.off('disconnected', onError);

        if (!api.query.multisig) {
          setAddNetworkErrorDialogVisible(true);
          setAddNetworkErrorContent(t('network not supported'));
          return;
        }

        const storage = readStorage();
        const oldCustomNetworks = storage.addedCustomNetworks || [];

        let duplicate = false;
        networks.forEach((networkItem) => {
          if (networkItem?.rpc === rpcUrl.trim()) {
            duplicate = true;
            return;
          }
        });
        oldCustomNetworks.forEach((networkItem) => {
          if (networkItem.rpc === rpcUrl.trim()) {
            duplicate = true;
            return;
          }
        });

        if (duplicate) {
          message.warn(t('custom.duplicate websocket url'));
          return;
        }

        const newNetwork = {
          name: rpcName.trim(),
          displayName: rpcName.trim(),
          rpc: rpcUrl.trim(),
          explorerHostName: explorerHostName.trim(),
        };
        oldCustomNetworks.push(newNetwork);
        updateStorage({ addedCustomNetworks: oldCustomNetworks });
        setRpcName('');
        setRpcUrl('');

        selectCustomNetwork(newNetwork);
        props.onCancel();
      };

      const onError = async () => {
        setAddNetworkLoading(false);
        setAddNetworkErrorDialogVisible(true);
        setAddNetworkErrorContent(t('unavailable endpoint'));

        api.disconnect();
        api.off('ready', onReady);
        api.off('error', onError);
        api.off('disconnected', onError);
      };

      api.on('ready', onReady);
      api.on('error', onError);
      api.on('disconnected', onError);
    } catch {
      setAddNetworkLoading(false);
      setAddNetworkErrorDialogVisible(true);
      setAddNetworkErrorContent(t('unavailable endpoint'));
    }
  };

  const updateCustomNetwork = () => {
    if (!rpcName.trim() || !rpcUrl.trim()) {
      return;
    }

    setAddNetworkLoading(true);
    try {
      const provider = new WsProvider(rpcUrl.trim());
      const api = new ApiPromise({
        provider,
      });

      const onReady = async () => {
        setAddNetworkLoading(false);

        if (!api.query.multisig) {
          setAddNetworkErrorDialogVisible(true);
          setAddNetworkErrorContent(t('network not supported'));
          return;
        }

        api.off('ready', onReady);

        const storage = readStorage();
        const oldCustomNetworks = storage.addedCustomNetworks || [];

        let duplicate = false;
        networks.forEach((networkItem) => {
          if (networkItem?.rpc === rpcUrl.trim()) {
            duplicate = true;
            return;
          }
        });
        oldCustomNetworks.forEach((networkItem) => {
          if (networkItem.rpc === rpcUrl.trim() && networkItem.rpc !== props.editNetwork?.rpc) {
            duplicate = true;
            return;
          }
        });

        if (duplicate) {
          message.warn(t('custom.duplicate websocket url'));
          return;
        }

        const networkIndex = oldCustomNetworks.findIndex((networkItem) => networkItem.rpc === props.editNetwork?.rpc);
        oldCustomNetworks.splice(networkIndex, 1, {
          name: rpcName,
          displayName: rpcName,
          rpc: rpcUrl,
          explorerHostName: explorerHostName.trim(),
        });

        updateStorage({ addedCustomNetworks: oldCustomNetworks });
        props.onCancel();
      };

      api.on('ready', onReady);
    } catch {
      setAddNetworkLoading(false);
      setAddNetworkErrorDialogVisible(true);
      setAddNetworkErrorContent('unavailable endpoint');
    }
  };

  const selectCustomNetwork = (netConfig: NetConfigV2) => {
    if (!netConfig.displayName || !netConfig.rpc) {
      return;
    }

    updateStorage({
      customNetwork: {
        name: netConfig.name,
        displayName: netConfig.displayName,
        rpc: netConfig.rpc,
      },
    });
    props.onCancel();
    changeUrlHash(netConfig.rpc);
    updateStorage({
      selectedRpc: netConfig.rpc,
    });
  };

  return (
    <div>
      <div className="overflow-auto hide-scrollbar" style={{ maxHeight: '500px' }}>
        <div className="flex items-center justify-center relative">
          <div
            className="font-bold capitalize text-black-800"
            style={{ fontSize: '16px', textTransform: 'capitalize' }}
          >
            {props.editNetwork ? t('custom.edit custom endpoint') : t('custom.add custom endpoint')}
          </div>

          <LeftOutlined
            className="absolute cursor-pointer left-0"
            style={{ color: '#666666' }}
            onClick={props.onCancel}
          />
        </div>

        <div className="mt-6 font-bold text-black-800">{t('remark')}</div>

        <div className="mt-2">
          <Input
            value={rpcName}
            onChange={(e) => {
              setRpcName(e.target.value);
            }}
          />
        </div>

        <div className="mt-6 font-bold text-black-800">{t('endpoint')}</div>

        <div className="mt-2">
          <Input
            value={rpcUrl}
            onChange={(e) => {
              setRpcUrl(e.target.value);
            }}
          />
        </div>

        <div className="mt-6 font-bold text-black-800">{t('explorer optional')}</div>

        <div className="mt-2 flex items-center text-black-800">
          <div>https://</div>
          <div className="mx-1 w-24">
            <Input
              value={explorerHostName}
              size="small"
              onChange={(e) => {
                setExplorerHostName(e.target.value);
              }}
            />
          </div>

          <div>.subscan.io/</div>
        </div>

        <Row gutter={16} className={classNames('mt-10')}>
          <Col span={24}>
            <Button
              loading={addNetworkLoading}
              block
              style={{
                color: mainColor,
              }}
              onClick={props.editNetwork ? updateCustomNetwork : saveNewCustomNetwork}
            >
              {props.editNetwork ? t('save') : t('add')}
            </Button>
          </Col>
        </Row>
      </div>

      <ConfirmDialog
        title="Error"
        content={addNetworkErrorContent}
        visible={addNetworkErrorDialogVisible}
        onCancel={() => setAddNetworkErrorDialogVisible(false)}
        onConfirm={() => setAddNetworkErrorDialogVisible(false)}
      />
    </div>
  );
}
Example #23
Source File: HTTPFlowDetail.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
HTTPFlowDetail: React.FC<HTTPFlowDetailProp> = (props) => {
    const [flow, setFlow] = useState<HTTPFlow>();
    const [loading, setLoading] = useState(false);

    const actionFuzzer = [
        {
            id: 'send-fuzzer-info',
            label: '发送到Fuzzer',
            contextMenuGroupId: 'send-fuzzer-info',
            run: () => {
                ipcRenderer.invoke("send-to-tab", {
                    type: "fuzzer",
                    data: {
                        isHttps: flow?.IsHTTPS,
                        request: Buffer.from(flow?.Request || []).toString("utf8")
                    }
                })
                if (props.onClose) props.onClose()
            }
        },
        {
            id: 'send-to-plugin',
            label: '发送到数据包扫描',
            contextMenuGroupId: 'send-fuzzer-info',
            run: () => ipcRenderer.invoke("send-to-packet-hack", {
                request: flow?.Request,
                ishttps: flow?.IsHTTPS,
                response: flow?.Response
            })

        }
    ]

    useEffect(() => {
        if (!props.hash) {
            return
        }
        //
        // ipcRenderer.on(props.hash, (e: any, data: HTTPFlow) => {
        //     setFlow(data)
        //     setTimeout(() => setLoading(false), 300)
        // })
        // ipcRenderer.on(`ERROR:${props.hash}`, (e: any, details: any) => {
        //     failed(`查询该请求失败[${props.hash}]: ` + details)
        // })

        setLoading(true)
        ipcRenderer.invoke("GetHTTPFlowByHash", {Hash: props.hash}).then((data: HTTPFlow) => {
            setFlow(data)
        }).catch(e => {
            failed(`GetHTTPFlow ByHash[${props.hash}] failed`)
        }).finally(() => setTimeout(() => setLoading(false), 300))
        // ipcRenderer.invoke("get-http-flow", props.hash)

        return () => {
            // ipcRenderer.removeAllListeners(props.hash)
            // ipcRenderer.removeAllListeners(`ERROR:${props.hash}`)
        }
    }, [props.hash])

    return <Spin spinning={loading} style={{width: "100%", marginBottom: 24}}>
        {flow ? <>
            {props.noHeader ? undefined : <PageHeader
                title={`请求详情`} subTitle={props.hash}
                extra={
                    props.fetchRequest ?
                        <Space>
                            <Tooltip title={"上一个请求"}>
                                <Button type="link" disabled={!!props.isFront}
                                        icon={<LeftOutlined/>} onClick={() => {
                                    props?.fetchRequest!(1)
                                }}></Button>
                            </Tooltip>
                            <Tooltip title={"下一个请求"}>
                                <Button type="link" disabled={!!props.isBehind}
                                        icon={<RightOutlined/>} onClick={() => {
                                    props?.fetchRequest!(2)
                                }}></Button>
                            </Tooltip>
                        </Space>
                        :
                        <></>
                }/>
            }
            <Space direction={"vertical"} style={{width: "100%"}}>
                <Descriptions column={4} bordered={true} size={"small"}>
                    <Descriptions.Item key={"method"} span={1} label={"HTTP 方法"}><Tag color={"geekblue"}><Text
                        style={{maxWidth: 500}}>{flow.Method}</Text></Tag></Descriptions.Item>
                    <Descriptions.Item key={"url"} span={3} label={"请求 URL"}>
                        <Text style={{maxWidth: 500}} copyable={true}>{flow.Url}</Text>
                    </Descriptions.Item>
                    <Descriptions.Item key={"https"} span={1} label={"HTTPS"}><Tag color={"geekblue"}>
                        <div
                            style={{maxWidth: 500}}>{flow.IsHTTPS ? "True" : "False"}</div>
                    </Tag></Descriptions.Item>
                    <Descriptions.Item key={"status"} span={1} label={"StatusCode"}><Tag
                        color={"geekblue"}>{flow.StatusCode}</Tag></Descriptions.Item>
                    <Descriptions.Item key={"size"} span={1} label={"Body大小"}><Tag color={"geekblue"}>
                        <div style={{maxWidth: 500}}>{flow.BodySizeVerbose}</div>
                    </Tag></Descriptions.Item>
                    <Descriptions.Item key={"type"} span={1} label={"Content-Type"}><Tag color={"geekblue"}>
                        <div style={{maxWidth: 500}}>{flow.ContentType}</div>
                    </Tag></Descriptions.Item>
                </Descriptions>
                <div style={{width: "100%", overflow: "auto"}}>
                    {flow.GetParams.length > 0 || flow.PostParams.length > 0 || flow.CookieParams.length > 0 ? <Tabs>
                        {flow.GetParams.length > 0 && <Tabs.TabPane key={"get"} tab={"GET 参数"}>
                            <FuzzableParamList data={flow.GetParams} sendToWebFuzzer={() => {
                                if (props.onClose) props.onClose()
                            }}/>
                        </Tabs.TabPane>}
                        {flow.PostParams.length > 0 && <Tabs.TabPane key={"post"} tab={"POST 参数"}>
                            <FuzzableParamList data={flow.PostParams} sendToWebFuzzer={() => {
                                if (props.onClose) props.onClose()
                            }}/>
                        </Tabs.TabPane>}
                        {flow.CookieParams.length > 0 && <Tabs.TabPane key={"cookie"} tab={"Cookie 参数"}>
                            <FuzzableParamList data={flow.CookieParams} sendToWebFuzzer={() => {
                                if (props.onClose) props.onClose()
                            }}/>
                        </Tabs.TabPane>}
                    </Tabs> : ""}
                </div>

                <Row gutter={8}>
                    <Col span={12}>
                        <Card title={"原始 HTTP 请求"} size={"small"} bodyStyle={{padding: 0}}>
                            <div style={{height: 350}}>
                                <YakEditor readOnly={true} type={"http"}//theme={"fuzz-http-theme"}
                                           value={new Buffer(flow.Request).toString("utf-8")}
                                           actions={[...actionFuzzer]}/>
                            </div>
                        </Card>
                    </Col>
                    <Col span={12}>
                        <Card title={"原始 HTTP 响应"} size={"small"} bodyStyle={{padding: 0}}>
                            <div style={{height: 350}}>
                                <YakEditor readOnly={true} type={"http"}// theme={"fuzz-http-theme"}
                                           value={new Buffer(flow.Response).toString("utf-8")}
                                />
                            </div>
                        </Card>
                    </Col>
                </Row>

                {/*<Collapse>*/}
                {/*    <Collapse.Panel key={"request-raw"} header={"原始 HTTP 请求数据包内容"}>*/}

                {/*    </Collapse.Panel>*/}
                {/*    <Collapse.Panel key={"response-raw"} header={"原始 HTTP 响应数据包内容"}>*/}

                {/*    </Collapse.Panel>*/}
                {/*</Collapse>*/}
                <Row gutter={8}>
                    <Col span={12}>
                        <Collapse defaultActiveKey={"request"}>
                            <Collapse.Panel key={"request"} header={"Request Headers"}>
                                <Descriptions bordered={true} column={1} size={"small"}>
                                    {(flow?.RequestHeader || []).sort((i, e) => {
                                        return i.Header.localeCompare(e.Header)
                                    }).map(i => {
                                        return <Descriptions.Item key={i.Header} label={<Text style={{width: 240}}>
                                            <Tag>{i.Header}</Tag>
                                        </Text>}>
                                            <Text
                                                copyable={true}
                                                style={{maxWidth: 500}}
                                                ellipsis={{tooltip: true}}>{i.Value}</Text>
                                        </Descriptions.Item>
                                    })}
                                </Descriptions>
                            </Collapse.Panel>
                        </Collapse>
                    </Col>
                    <Col span={12}>
                        <Collapse defaultActiveKey={"response"}>
                            <Collapse.Panel key={"response"} header={"Response Headers"}>
                                <Descriptions bordered={true} column={1} size={"small"}>
                                    {(flow?.ResponseHeader || []).sort((i, e) => {
                                        return i.Header.localeCompare(e.Header)
                                    }).map(i => {
                                        return <Descriptions.Item key={i.Header} label={<Text style={{width: 240}}>
                                            <Tag>{i.Header}</Tag>
                                        </Text>}>
                                            <Text
                                                copyable={true}
                                                style={{maxWidth: 500}}
                                                ellipsis={{tooltip: true}}>{i.Value}</Text>
                                        </Descriptions.Item>
                                    })}
                                </Descriptions>
                            </Collapse.Panel>
                        </Collapse>
                    </Col>
                </Row>
            </Space>
        </> : ""}
    </Spin>
}
Example #24
Source File: App.tsx    From pcap2socks-gui with MIT License 4 votes vote down vote up
render() {
    return (
      <Layout className="layout">
        <Content className="content-wrapper">
          <div className="content">
            {(() => {
              switch (this.state.stage) {
                case STAGE_WELCOME:
                  return this.renderWelcome();
                case STAGE_INTERFACE:
                  return this.renderInterface();
                case STAGE_DEVICE:
                  return this.renderDevice();
                case STAGE_PROXY:
                  return this.renderProxy();
                case STAGE_RUNNING:
                  return this.renderRunning();
                default:
                  return;
              }
            })()}
          </div>
          <div className="footer">
            {(() => {
              if (this.state.stage > STAGE_WELCOME && this.state.stage <= STAGE_PROXY) {
                return (
                  <Button
                    className="button"
                    disabled={this.state.loading > 0}
                    icon={<LeftOutlined />}
                    onClick={() => this.setState({ stage: this.state.stage - 1 })}
                  >
                    上一步
                  </Button>
                );
              }
            })()}
            {(() => {
              if (this.state.stage === STAGE_INTERFACE) {
                return (
                  <Button
                    className="button"
                    disabled={this.state.loading > 0 && this.state.loading !== 1}
                    icon={<ReloadOutlined />}
                    onClick={this.updateInterfaces}
                  >
                    刷新网卡列表
                  </Button>
                );
              }
            })()}
            {(() => {
              if (this.state.stage === STAGE_PROXY) {
                return (
                  <Button
                    className="button"
                    disabled={this.state.loading > 0}
                    icon={<FolderOpenOutlined />}
                    onClick={() => {
                      const node = document.getElementById("open");
                      if (node) {
                        node.click();
                      }
                    }}
                  >
                    导入代理配置
                    <input id="open" type="file" onChange={this.import} style={{ display: "none" }} />
                  </Button>
                );
              }
            })()}
            {(() => {
              if (this.state.stage === STAGE_PROXY) {
                return (
                  <Button className="button" icon={<ExportOutlined />} onClick={this.export}>
                    导出代理配置
                  </Button>
                );
              }
            })()}
            {(() => {
              if (this.state.stage === STAGE_PROXY) {
                return (
                  <Button
                    className="button"
                    disabled={this.state.loading > 0 && this.state.loading !== 2}
                    loading={this.state.loading === 2}
                    icon={<ExperimentOutlined />}
                    onClick={this.test}
                  >
                    测试代理服务器
                  </Button>
                );
              }
            })()}
            {(() => {
              if (this.state.stage === STAGE_WELCOME && this.state.ready) {
                return (
                  <Tooltip title={this.state.destination}>
                    <Button
                      className="button"
                      type="primary"
                      disabled={this.state.loading > 0 && this.state.loading !== 3}
                      loading={this.state.loading === 3}
                      icon={<PlayCircleOutlined />}
                      onClick={this.run}
                    >
                      以上次的配置运行
                    </Button>
                  </Tooltip>
                );
              }
            })()}
            {(() => {
              if (this.state.stage >= STAGE_WELCOME && this.state.stage < STAGE_PROXY) {
                return (
                  <Button
                    className="button"
                    disabled={this.state.loading > 0}
                    icon={<RightOutlined />}
                    type="primary"
                    onClick={() => this.setState({ stage: this.state.stage + 1 })}
                  >
                    下一步
                  </Button>
                );
              }
            })()}
            {(() => {
              if (this.state.stage === STAGE_PROXY) {
                return (
                  <Button
                    className="button"
                    type="primary"
                    disabled={this.state.loading > 0 && this.state.loading !== 3}
                    loading={this.state.loading === 3}
                    icon={<PoweroffOutlined />}
                    onClick={this.run}
                  >
                    运行
                  </Button>
                );
              }
            })()}
            {(() => {
              if (this.state.stage === STAGE_RUNNING) {
                return (
                  <Button className="button" icon={<GlobalOutlined />} onClick={this.notifyNetwork}>
                    显示网络设置
                  </Button>
                );
              }
            })()}
            {(() => {
              if (this.state.stage === STAGE_RUNNING) {
                return (
                  <Button
                    className="button"
                    type="primary"
                    danger
                    disabled={this.state.loading > 0 && this.state.loading !== 4}
                    loading={this.state.loading === 4}
                    icon={<PoweroffOutlined />}
                    onClick={this.stopConfirm}
                  >
                    停止
                  </Button>
                );
              }
            })()}
          </div>
        </Content>
      </Layout>
    );
  }
Example #25
Source File: CarMultiSelectionTable.tsx    From jmix-frontend with Apache License 2.0 4 votes vote down vote up
CarMultiSelectionTable = observer((props: EntityListProps<Car>) => {
  const { entityList, onEntityListChange } = props;
  const onOpenScreenError = useOpenScreenErrorCallback();
  const onEntityDelete = useEntityDeleteCallback();
  const {
    items,
    count,
    relationOptions,
    executeListQuery,
    listQueryResult: { loading, error },
    handleSelectionChange,
    handleFilterChange,
    handleSortOrderChange,
    handlePaginationChange,
    handleDeleteBtnClick,
    goToParentScreen,
    entityListState,
    multiSelectionTableStore
  } = useMultiSelectionTable<Car>({
    listQuery: SCR_CAR_LIST,
    entityName: ENTITY_NAME,
    routingPath: ROUTING_PATH,
    entityList,
    onEntityListChange,
    onPagination: saveHistory,
    onEntityDelete,
    onOpenScreenError,
    lazyLoading: true
  });

  if (error != null) {
    console.error(error);
    return <RetryDialog onRetry={executeListQuery} />;
  }

  const buttons = [
    <EntityPermAccessControl
      entityName={ENTITY_NAME}
      operation="delete"
      key="delete"
    >
      <Button
        htmlType="button"
        style={{ margin: "0 12px 12px 0" }}
        disabled={
          multiSelectionTableStore.selectedEntityIds == null ||
          multiSelectionTableStore.selectedEntityIds.length === 0
        }
        onClick={handleDeleteBtnClick}
        key="remove"
        type="default"
      >
        <FormattedMessage id="common.remove" />
      </Button>
    </EntityPermAccessControl>
  ];

  if (entityList != null) {
    buttons.unshift(
      <Tooltip title={<FormattedMessage id="common.back" />}>
        <Button
          htmlType="button"
          style={{ margin: "0 12px 12px 0" }}
          icon={<LeftOutlined />}
          onClick={goToParentScreen}
          key="back"
          type="default"
          shape="circle"
        />
      </Tooltip>
    );
  }

  return (
    <DataTable
      tableId={ROUTING_PATH + ENTITY_NAME}
      items={items}
      count={count}
      relationOptions={relationOptions}
      current={entityListState.pagination?.current}
      pageSize={entityListState.pagination?.pageSize}
      entityName={ENTITY_NAME}
      loading={loading}
      error={error}
      enableFiltersOnColumns={entityList != null ? [] : undefined}
      enableSortingOnColumns={entityList != null ? [] : undefined}
      columnDefinitions={[
        "manufacturer",
        "model",
        "regNumber",
        "purchaseDate",
        "manufactureDate",
        "wheelOnRight",
        "carType",
        "ecoRank",
        "maxPassengers",
        "price",
        "mileage",
        "garage",
        "technicalCertificate"
      ]}
      rowSelectionMode="multi"
      onRowSelectionChange={handleSelectionChange}
      onFilterChange={handleFilterChange}
      onSortOrderChange={handleSortOrderChange}
      onPaginationChange={handlePaginationChange}
      buttons={buttons}
      executeListQuery={entityList == null ? executeListQuery : undefined}
    />
  );
})