@ant-design/icons#DownloadOutlined TypeScript Examples

The following examples show how to use @ant-design/icons#DownloadOutlined. 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: index.tsx    From surveyo with Apache License 2.0 6 votes vote down vote up
function DownloadCsv(props: any) {
  const [getCsv, {loading, error, data}] = useLazyQuery<
    GetCsvResponses,
    GetCsvResponsesVariables
  >(GET_CSV);

  if (error) {
    console.error(error);
    message.error('Internal error: could not generate CSV');
  }

  return data ? (
    <Tooltip title="Download CSV">
      <CSVLink data={makeCsv(data)} filename={`${props.title}.csv`}>
        <Button type="link" icon={<DownloadOutlined />} />
      </CSVLink>
    </Tooltip>
  ) : (
    <Tooltip title="Generate CSV">
      <Button
        type="link"
        icon={loading ? <LoadingOutlined /> : <FileExcelOutlined />}
        onClick={() => getCsv({variables: {id: props.id}})}
      />
    </Tooltip>
  );
}
Example #2
Source File: out.tsx    From web-pdm with Apache License 2.0 6 votes vote down vote up
IconRenders = {

    undo: <RollbackOutlined />,
    redo: <RollbackOutlined style={{ transform: 'scaleX(-1)' }} />,
    min: <ZoomOutOutlined />,
    max: <ZoomInOutlined />,
    full: <BorderOutlined />,
    miniMap: <PictureFilled />,
    miniMapNo: <PictureOutlined />,
    dagreLayout: <PartitionOutlined />,
    relationLayout: <UngroupOutlined />,
    reload: <ReloadOutlined />,
    image: <DownloadOutlined />,
    darkness: <SnippetsFilled />,
    light: <SnippetsOutlined />,
    colorClose: <BgColorsOutlined />,
    colorOpen: <BgColorsOutlined />
}
Example #3
Source File: seeds.tsx    From dendron with GNU Affero General Public License v3.0 6 votes vote down vote up
/**
 * Component for Button to Add Seed to Workspace on a seed browser card
 * @param existsInWorkspace - does the seed already exist in the users workspace?
 * @param seedId - seed unique ID
 * @returns
 */
export function AddToWorkspaceButton({
  existsInWorkspace,
  seedId,
}: {
  existsInWorkspace: boolean;
  seedId: string;
}) {
  function onClick() {
    postVSCodeMessage({
      type: SeedBrowserMessageType.onSeedAdd,
      data: { data: seedId },
      source: DMessageSource.webClient,
    } as SeedBrowserMessage);
  }

  if (!existsInWorkspace) {
    return (
      <Tooltip placement="top" title="Add to Workspace">
        <DownloadOutlined key="download" onClick={onClick} />
      </Tooltip>
    );
  }
  return (
    <Tooltip placement="top" title="Already in Workspace">
      <CheckCircleOutlined key="installed" disabled />
    </Tooltip>
  );
}
Example #4
Source File: Seeds.tsx    From dendron with GNU Affero General Public License v3.0 6 votes vote down vote up
/**
 * Component for Button to Add Seed to Workspace on a seed browser card
 * @param existsInWorkspace - does the seed already exist in the users workspace?
 * @param seedId - seed unique ID
 * @returns
 */
export function AddToWorkspaceButton({
  existsInWorkspace,
  seedId,
}: {
  existsInWorkspace: boolean;
  seedId: string;
}) {
  function onClick() {
    postVSCodeMessage({
      type: SeedBrowserMessageType.onSeedAdd,
      data: { data: seedId },
      source: DMessageSource.webClient,
    } as SeedBrowserMessage);
  }

  if (!existsInWorkspace) {
    return (
      <Tooltip placement="top" title="Add to Workspace">
        <DownloadOutlined key="download" onClick={onClick} />
      </Tooltip>
    );
  }
  return (
    <Tooltip placement="top" title="Already in Workspace">
      <CheckCircleOutlined key="installed" disabled />
    </Tooltip>
  );
}
Example #5
Source File: index.tsx    From LogicFlow with Apache License 2.0 6 votes vote down vote up
export default function SnapshotExample() {

  useEffect(() => {
    LogicFlow.use(Snapshot);
    lf = new LogicFlow({
      ...config,
      container: document.querySelector('#graph') as HTMLElement
    });
    lf.render(data)
  }, []);

  const handleDoenload = () => {
    lf.getSnapshot();
  }

  return (
    <>
      <ExampleHeader
        contentStyle={contentStyle}
        githubPath="/extension/snapshot/index.tsx"
      >
        导出图片:
        <Button
          shape="round"
          icon={<DownloadOutlined />}
          onClick={handleDoenload}
        />
      </ExampleHeader>
      <div id="graph" className="viewport" />
    </>
  )
}
Example #6
Source File: index.tsx    From web-pdm with Apache License 2.0 6 votes vote down vote up
IconRenders = {

    undo: <RollbackOutlined />,
    redo: <RollbackOutlined style={{ transform: 'scaleX(-1)' }} />,
    min: <ZoomOutOutlined />,
    max: <ZoomInOutlined />,
    full: <BorderOutlined />,
    miniMap: <PictureFilled />,
    miniMapNo: <PictureOutlined />,
    dagreLayout: <PartitionOutlined />,
    relationLayout: <UngroupOutlined />,
    reload: <ReloadOutlined />,
    image: <DownloadOutlined />,
    darkness: <SnippetsFilled />,
    light: <SnippetsOutlined />,
    colorClose: <BgColorsOutlined />,
    colorOpen: <BgColorsOutlined />
}
Example #7
Source File: PrintButton.tsx    From next-basics with GNU General Public License v3.0 5 votes vote down vote up
export function PrintButton(props: PrintButtonProps): React.ReactElement {
  const [visible, setVisible] = useState(false);

  const content = (
    <span>
      查看{" "}
      <a onClick={preRemind}>
        帮助{" "}
        <InfoCircleOutlined
          style={{ transform: "translateY(1px)" }}
        ></InfoCircleOutlined>
      </a>
    </span>
  );

  const handleVisible = (isVisible: boolean) => {
    setVisible(isVisible);
  };

  function preRemind() {
    const padding = 32;
    const pngWidth = 800;
    Modal.info({
      className: styles.printModalBody,
      maskClosable: true,
      title: "打印操作指引",
      width: pngWidth + padding * 2,
      okText: "关闭",
      content: (
        <div style={{ overflowY: "scroll", width: "100%", height: "500px" }}>
          <img src={printHelperUrl} alt="打印操作指导图片" />
        </div>
      ),
    });
  }

  function invokePrint() {
    const originTitle = window.parent.document.title;
    // in iframe, maybe
    window.parent.document.title =
      props.prefixTitle +
      "-" +
      new Date().toISOString().substr(0, 10).replace(/-/g, "");
    window.print();
    window.parent.document.title = originTitle;
  }

  return (
    <div>
      <Popover
        title={null}
        content={content}
        visible={visible}
        onVisibleChange={handleVisible}
        overlayClassName="print-hide"
        placement="left"
      >
        <Button
          className="print-hide"
          icon={<DownloadOutlined />}
          style={{
            backgroundColor: props.backgroundColor,
            color: props.color,
            border: props.border,
            width: props.size || 24,
            height: props.size || 24,
          }}
          onClick={invokePrint}
        ></Button>
      </Popover>
    </div>
  );
}
Example #8
Source File: Index.tsx    From condo with MIT License 5 votes vote down vote up
ColumnsInfoBox: React.FC<IColumnsInfoBoxProps> = ({ columns, domainTranslate, exampleTemplateLink }) => {
    const intl = useIntl()
    const ColumnsFormatMessage = intl.formatMessage({ id: 'ImportRequiredColumnsFormat' }, {
        domain: domainTranslate,
    })
    const RequiredFieldsMessage = intl.formatMessage({ id: 'ImportRequiredFields' })
    const DownloadExampleTitle = intl.formatMessage({ id: 'ImportDownloadExampleTitle' })

    const downloadExample = useCallback(
        (event) => {
            event.stopPropagation()
            const link = document.createElement('a')
            link.href = exampleTemplateLink
            link.target = '_blank'
            link.hidden = true
            document.body.appendChild(link)
            link.click()
            link.parentNode.removeChild(link)
        },
        [exampleTemplateLink],
    )

    const fieldsString = columns.filter(({ required }) => required).map(column => column.label).join(', ')

    return (
        <Space direction={'vertical'} size={10} style={{ maxWidth: 300 }}>
            <Typography.Text>
                {ColumnsFormatMessage}
            </Typography.Text>
            <Typography.Text>{RequiredFieldsMessage}: {fieldsString}</Typography.Text>
            {
                exampleTemplateLink !== null && (
                    <Button
                        onClick={downloadExample}
                        icon={<DownloadOutlined />}
                        type={'inlineLink'}
                    >{DownloadExampleTitle}</Button>
                )
            }
        </Space>
    )
}
Example #9
Source File: TitleForShare.tsx    From datart with Apache License 2.0 5 votes vote down vote up
TitleForShare: FC<TitleHeaderProps> = memo(
  ({ name, onShareDownloadData, loadVizData, children }) => {
    const t = useI18NPrefix(`viz.action`);
    const [showTitle, setShowTitle] = useState<boolean>(false);

    if (!showTitle) {
      return (
        <FixedButton onClick={() => setShowTitle(true)}>
          <MoreOutlined />
        </FixedButton>
      );
    }
    return (
      <Wrapper>
        <Space>
          {children}
          <Popconfirm
            placement="left"
            title={t('common.confirm')}
            okText={t('common.ok')}
            cancelText={t('common.cancel')}
            onConfirm={() => {
              onShareDownloadData?.();
            }}
          >
            <Tooltip title={t('share.downloadData')} placement="bottom">
              <DownloadOutlined style={{ fontSize: 20 }} />
            </Tooltip>
          </Popconfirm>
          <Tooltip title={t('syncData')} placement="bottom">
            <ReloadOutlined style={IconStyle} onClick={loadVizData} />
          </Tooltip>
          <CloseOutlined
            onClick={() => setShowTitle(false)}
            style={IconStyle}
          />
        </Space>
      </Wrapper>
    );
  },
)
Example #10
Source File: index.tsx    From visual-layout with MIT License 5 votes vote down vote up
Code: React.FC<{ project: ProjectService }> = ({ project }) => {
  const [codeConfig, setCodeConfig] = useState<CodeConfig>(
    getInitCodeConfig(project),
  );

  return (
    <Visible>
      {({ visible, setVisible }) => (
        <>
          <Drawer
            title="代码"
            placement="right"
            width={'calc(100% - 420px)'}
            onClose={() => setVisible(false)}
            visible={visible}
            destroyOnClose
          >
            <div className={styles.container}>
              <div className={styles.leftContainer}>
                <div className={styles.download}>
                  <Button
                    type="primary"
                    shape="round"
                    icon={<DownloadOutlined />}
                    onClick={() => exportCode(project, codeConfig)}
                  >
                    导出
                  </Button>
                </div>
                <BaseInfo project={project} />
                <Config setCodeConfig={setCodeConfig} codeConfig={codeConfig} />
              </div>
              <div className={styles.rightContainer}>
                <CodeEdit project={project} codeConfig={codeConfig} />
              </div>
            </div>
          </Drawer>
          <Tooltip placement="top" title="代码">
            <CodeOutlined
              style={{ fontSize: 20 }}
              onClick={() => setVisible(true)}
            />
          </Tooltip>
        </>
      )}
    </Visible>
  );
}
Example #11
Source File: Args.tsx    From subscan-multisig-react with Apache License 2.0 5 votes vote down vote up
export function Args({ args, className, section, method }: ArgsProps) {
  const { t } = useTranslation();
  const { chain } = useApi();
  const columns: ColumnsType<ArgObj> = [
    {
      key: 'name',
      dataIndex: 'name',
      render(name: string, record) {
        return name || record.type;
      },
    },
    {
      key: 'value',
      dataIndex: 'value',
      className: 'value-column',
      // eslint-disable-next-line complexity
      render(value, record) {
        const { type, name } = record;
        const isAddr = type ? isAddressType(type) : isSS58Address(value);

        if (isObject(value)) {
          return (
            <Args
              args={Object.entries(value).map(([prop, propValue]) => ({ name: prop, value: propValue }))}
              section={section}
              method={method}
            />
          );
          // return JSON.stringify(value);
        }

        if (isAddr) {
          return formatAddressValue(value, chain);
        }

        // balances(transfer) kton(transfer)
        if (isBalanceType(type || name) || isCrabValue(name) || section === 'balances' || method === 'transfer') {
          const formatValue = toString(value).replaceAll(',', '');
          return formatBalance(formatValue, +chain.tokens[0].decimal, {
            noDecimal: false,
            withThousandSplit: true,
          }); // FIXME: decimal issue;
        }

        if (isDownloadType(value)) {
          return (
            <a href={value}>
              {t('download')} <DownloadOutlined />
            </a>
          );
        }

        if (isValueType(name)) {
          return value;
        }

        return <div style={{ wordBreak: 'break-all' }}>{value}</div>;
      },
    },
  ];

  return (
    <Table
      columns={columns}
      /* antd form data source require object array */
      dataSource={args.map((arg) => (isObject(arg) ? arg : { value: arg }))}
      pagination={false}
      bordered
      rowKey="name"
      showHeader={false}
      className={className}
    />
  );
}
Example #12
Source File: MessageDetails.tsx    From wildduck-ui with MIT License 4 votes vote down vote up
MessageDetails: React.FC = () => {
	const { id }: any = useParams();

	const { mailboxId, mailboxName } = useValues(mailboxesLogic);
	const { messageId, attachmentId } = useValues(messagesLogic);

	const attachment = useDownloadAttachment({
		userId: id,
		attachment: attachmentId,
		mailboxId: mailboxId,
		messageId: messageId,
	});

	const { data, isLoading, isError } = useMessageDetails({ userId: id, mailboxId: mailboxId, messageId: messageId });

	const { setMessageDetailsToggle, setAttachmentId } = useActions(messagesLogic);
	const { setUpdateMailboxToggle, setShowMailboxMessagesTable } = useActions(mailboxesLogic);

	const pageBreadcrumb = (
		<Breadcrumb>
			<Breadcrumb.Item>
				<a
					onClick={() => {
						setUpdateMailboxToggle(false);
						setShowMailboxMessagesTable(false);
					}}
				>
					Mailboxes
				</a>
			</Breadcrumb.Item>
			<Breadcrumb.Item>
				<a
					onClick={(event) => {
						event.stopPropagation();
						setMessageDetailsToggle(false);
					}}
				>
					{mailboxName}
				</a>
			</Breadcrumb.Item>
			<Breadcrumb.Item>Message</Breadcrumb.Item>
		</Breadcrumb>
	);

	const MessageTitle = (
		<div>
			<p>From : {`${_.get(data, 'from.name', '')}<${_.get(data, 'from.address', '')}>`}</p>
			<p>To : {`${_.get(data, 'to.0.name', '')}<${_.get(data, 'to.0.address', '')}>`}</p>
			<p>Subject : {_.get(data, 'subject', '')}</p>
		</div>
	);
	return (
		<Page title={pageBreadcrumb} loading={isLoading} error={isError}>
			{_.isUndefined(data) ? null : (
				<Card title={MessageTitle} extra={<>{moment(data.date).format(DATE_TIME_FORMAT_AP)}</>}>
					<BraftEditor language='en' controls={[]} value={BraftEditor.createEditorState(data.html[0])} />
					{_.isEmpty(data.attachments) ? null : (
						<Descriptions size='small' bordered column={1}>
							<Descriptions.Item label='Attachments'>
								{_.map(data.attachments, (attachment, index) => {
									return (
										<Row key={index}>
											<Col>
												<p>
													Filename: {_.get(attachment, 'filename', '')}
													<br />
													Content Type: {_.get(attachment, 'contentType', '')}
													<br />
													Size: {_.get(attachment, 'sizeKb', '')} KB
												</p>
											</Col>
											<Col offset={1}>
												<Tooltip title={'Download file'}>
													<Button
														className='ant-btn-icon'
														shape='circle'
														onClick={() => {
															setAttachmentId(attachment);
														}}
													>
														<DownloadOutlined className={'blue-color'} />
													</Button>
												</Tooltip>
											</Col>
										</Row>
									);
								})}
							</Descriptions.Item>
						</Descriptions>
					)}
				</Card>
			)}
		</Page>
	);
}
Example #13
Source File: YakitStorePage.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
YakitStorePage: React.FC<YakitStorePageProp> = (props) => {
    const [script, setScript] = useState<YakScript>()
    const [trigger, setTrigger] = useState(false)
    const [keyword, setKeyword] = useState("")
    const [ignored, setIgnored] = useState(false)
    const [history, setHistory] = useState(false)

    const refresh = useMemoizedFn(() => {
        setTrigger(!trigger)
    })

    const [loading, setLoading] = useState(false)
    const [localPluginDir, setLocalPluginDir] = useState("");
    const [pluginType, setPluginType] = useState<"yak" | "mitm" | "nuclei" | "codec" | "packet-hack" | "port-scan">("yak");

    useEffect(() => {
        getValue(YAKIT_DEFAULT_LOAD_LOCAL_PATH).then(e => {
            if (e) {
                setLocalPluginDir(`${e}`)
            }
        })
    }, [])

    useEffect(() => {
        setLoading(true)
        setTimeout(() => {
            setLoading(false)
        }, 200)
    }, [script])

    return (
        <div style={{height: "100%", display: "flex", flexDirection: "row"}}>
            <div style={{width: 470}}>
                <AutoCard
                    bodyStyle={{padding: 0, overflow: "auto"}}
                    bordered={false}
                    style={{height: "100%"}}
                    title={
                        <Space size={0}>
                            类型:
                            <ManySelectOne
                                size={"small"}
                                data={[
                                    {text: "YAK 插件", value: "yak"},
                                    {text: "MITM 插件", value: "mitm"},
                                    {text: "数据包扫描", value: "packet-hack"},
                                    {text: "端口扫描插件", value: "port-scan"},
                                    {text: "CODEC插件", value: "codec"},
                                    {text: "YAML POC", value: "nuclei"},
                                ]}
                                value={pluginType} setValue={setPluginType}
                                formItemStyle={{marginBottom: 0, width: 115}}
                            />
                            <Button size={"small"} type={"link"} onClick={(e) => setTrigger(!trigger)}>
                                <ReloadOutlined/>
                            </Button>
                            <Button
                                size={"small"}
                                type={"link"}
                                onClick={(e) => {
                                    let m = showModal({
                                        title: "设置 Keyword",
                                        content: (
                                            <>
                                                <KeywordSetter
                                                    defaultIgnore={ignored}
                                                    defaultHistory={history}
                                                    onFinished={(e, ignored, history) => {
                                                        setKeyword(e)
                                                        setIgnored(ignored)
                                                        setHistory(history)
                                                        m.destroy()
                                                    }}
                                                    defaultValue={keyword}
                                                />
                                            </>
                                        )
                                    })
                                }}
                            >
                                <SearchOutlined/>
                            </Button>
                        </Space>
                    }
                    size={"small"}
                    extra={
                        <Space size={1}>
                            <Popover
                                title={"额外设置"}
                                content={<>
                                    <Form onSubmitCapture={e => {
                                        e.preventDefault()

                                        saveValue(YAKIT_DEFAULT_LOAD_LOCAL_PATH, localPluginDir)
                                    }} size={"small"}>
                                        <InputItem
                                            label={"本地插件仓库"} value={localPluginDir} setValue={setLocalPluginDir}
                                            extraFormItemProps={{style: {marginBottom: 4}}}
                                        />
                                        <Form.Item colon={false} label={" "} style={{marginBottom: 12}}>
                                            <Button type="primary" htmlType="submit"> 设置 </Button>
                                        </Form.Item>
                                    </Form>
                                </>}
                                trigger={["click"]}
                            >
                                <Button
                                    icon={<SettingOutlined/>} size={"small"} type={"link"}
                                    style={{marginRight: 3}}
                                />
                            </Popover>
                            <Button
                                size={"small"}
                                type={"primary"}
                                icon={<DownloadOutlined/>}
                                onClick={() => {
                                    showModal({
                                        width: 800,
                                        title: "导入插件方式",
                                        content: (
                                            <>
                                                <div style={{width: 800}}>
                                                    <LoadYakitPluginForm onFinished={refresh}/>
                                                </div>
                                            </>
                                        )
                                    })
                                }}
                            >
                                导入
                            </Button>
                            {/*<Popconfirm*/}
                            {/*    title={"更新模块数据库?"}*/}
                            {/*    onConfirm={e => {*/}
                            {/*        showModal({*/}
                            {/*            title: "自动更新 Yak 模块", content: <>*/}
                            {/*                <AutoUpdateYakModuleViewer/>*/}
                            {/*            </>, width: "60%",*/}
                            {/*        })*/}
                            {/*    }}*/}
                            {/*>*/}
                            {/*    <Button size={"small"} type={"primary"} icon={<DownloadOutlined/>}>*/}
                            {/*        导入*/}
                            {/*    </Button>*/}
                            {/*</Popconfirm>*/}
                            <Button
                                size={"small"}
                                type={"link"}
                                icon={<PlusOutlined/>}
                                onClick={() => {
                                    let m = showDrawer({
                                        title: "创建新插件",
                                        width: "100%",
                                        content: (
                                            <>
                                                <YakScriptCreatorForm
                                                    onChanged={(e) => setTrigger(!trigger)}
                                                    onCreated={() => {
                                                        m.destroy()
                                                    }}
                                                />
                                            </>
                                        ),
                                        keyboard: false
                                    })
                                }}
                            >
                                新插件
                            </Button>
                        </Space>
                    }
                >
                    <YakModuleList
                        currentId={script?.Id}
                        Keyword={keyword}
                        Type={pluginType}
                        onClicked={setScript}
                        trigger={trigger}
                        isHistory={history}
                        isIgnored={ignored}
                    />
                    {/*<Tabs*/}
                    {/*    className='yak-store-list'*/}
                    {/*    tabPosition={"left"}*/}
                    {/*    size={"small"}*/}
                    {/*    tabBarStyle={{*/}
                    {/*        padding: 0,*/}
                    {/*        margin: 0,*/}
                    {/*        width: 70,*/}
                    {/*        marginLeft: -20*/}
                    {/*    }}*/}
                    {/*    direction={"ltr"}*/}
                    {/*>*/}
                    {/*    {[*/}
                    {/*        {tab: "YAK", key: "yak"},*/}
                    {/*        {tab: "YAML", key: "nuclei"},*/}
                    {/*        {tab: "MITM", key: "mitm"},*/}
                    {/*        {tab: "Packet", key: "packet-hack"},*/}
                    {/*        {tab: "CODEC", key: "codec"},*/}
                    {/*    ].map((e) => {*/}
                    {/*        return (*/}
                    {/*            <Tabs.TabPane tab={e.tab} key={e.key}>*/}
                    {/*                <YakModuleList*/}
                    {/*                    currentId={script?.Id}*/}
                    {/*                    Keyword={keyword}*/}
                    {/*                    Type={e.key as any}*/}
                    {/*                    onClicked={setScript}*/}
                    {/*                    trigger={trigger}*/}
                    {/*                    isHistory={history}*/}
                    {/*                    isIgnored={ignored}*/}
                    {/*                />*/}
                    {/*            </Tabs.TabPane>*/}
                    {/*        )*/}
                    {/*    })}*/}
                    {/*</Tabs>*/}
                </AutoCard>
            </div>
            <div style={{flex: 1, overflowX: "hidden"}}>
                {script ? (
                    <AutoCard
                        loading={loading}
                        title={
                            <Space>
                                <div>Yak[{script?.Type}] 模块详情</div>
                            </Space>
                        }
                        bordered={false}
                        size={"small"}
                    >
                        <PluginOperator yakScriptId={script.Id} setTrigger={() => setTrigger(!trigger)}
                                        setScript={setScript}/>
                    </AutoCard>
                ) : (
                    <Empty style={{marginTop: 100}}>在左侧所选模块查看详情</Empty>
                )}
            </div>
        </div>
    )
}
Example #14
Source File: FileManager.tsx    From anew-server with MIT License 4 votes vote down vote up
FileManager: React.FC<FileManagerProps> = (props) => {
    const { modalVisible, handleChange, connectId } = props;
    const [columnData, setColumnData] = useState<API.SSHFileList[]>([]);
    const [showHidden, setShowHidden] = useState<boolean>(false);
    const [childrenDrawer, setChildrenDrawer] = useState<boolean>(false);
    const [currentPathArr, setCurrentPathArr] = useState<string[]>([]);
    const [initPath, setInitPath] = useState<string>('');

    const _dirSort = (item: API.SSHFileList) => {
        return item.isDir;
    };

    const getFileData = (key: string, path: string) => {
        querySSHFile(key, path).then((res) => {
            const obj = lds.orderBy(res.data, [_dirSort, 'name'], ['desc', 'asc']);
            showHidden ? setColumnData(obj) : setColumnData(obj.filter((x) => !x.name.startsWith('.')));
            try {
                // 获取服务器的当前路径
                let pathb = obj[0].path;
                const index = pathb.lastIndexOf('/');
                pathb = pathb.substring(0, index + 1);
                setCurrentPathArr(pathb.split('/').filter((x: any) => x !== ''));
                setInitPath(pathb); // 保存当前路径,刷新用
            } catch (exception) {
                setCurrentPathArr(path.split('/').filter((x) => x !== ''));
                setInitPath(path);
            }
        });
    };

    const getChdirDirData = (key: string, path: string) => {
        const index = currentPathArr.indexOf(path);
        const currentDir = '/' + currentPathArr.splice(0, index + 1).join('/');
        getFileData(key, currentDir);
    };

    const handleDelete = (key: string, path: string) => {
        if (!path) return;
        const index = path.lastIndexOf('/');
        const currentDir = path.substring(0, index + 1);
        const currentFile = path.substring(index + 1, path.length);
        const content = `您是否要删除 ${currentFile}?`;
        Modal.confirm({
            title: '注意',
            content,
            onOk: () => {
                deleteSSHFile(key, path).then((res) => {
                    if (res.code === 200 && res.status === true) {
                        message.success(res.message);
                        getFileData(key, currentDir);
                    }
                });
            },
            onCancel() { },
        });
    };

    const handleDownload = (key: string, path: string) => {
        if (!path) return;
        const index = path.lastIndexOf('/');
        const currentFile = path.substring(index + 1, path.length);
        const content = `您是否要下载 ${currentFile}?`;
        Modal.confirm({
            title: '注意',
            content,
            onOk: () => {
                const token = localStorage.getItem('token');
                const link = document.createElement('a');
                link.href = `/api/v1/host/ssh/download?key=${key}&path=${path}&token=${token}`;
                document.body.appendChild(link);
                const evt = document.createEvent('MouseEvents');
                evt.initEvent('click', false, false);
                link.dispatchEvent(evt);
                document.body.removeChild(link);
            },
            onCancel() { },
        });
    };

    const uploadProps = {
        name: 'file',
        action: `/api/v1/host/ssh/upload?key=${connectId}&path=${initPath}`,
        multiple: true,
        headers: {
            Authorization: `Bearer ${localStorage.getItem('token')}`,
        },
        // showUploadList: {
        //   removeIcon: false,
        //   showRemoveIcon: false,
        // },
        onChange(info: any) {
            // if (info.file.status !== 'uploading') {
            //   console.log(info.file, info.fileList);
            // }
            //console.log(info);
            if (info.file.status === 'done') {
                message.success(`${info.file.name} file uploaded successfully`);
                getFileData(connectId, initPath as string); // 刷新数据
            } else if (info.file.status === 'error') {
                message.error(`${info.file.name} file upload failed.`);
            }
        },
        progress: {
            strokeColor: {
                '0%': '#108ee9',
                '100%': '#87d068',
            },
            strokeWidth: 3,
            format: (percent: any) => `${parseFloat(percent.toFixed(2))}%`,
        },
    };


    const columns: ProColumns<API.SSHFileList>[] = [
        {
            title: '名称',
            dataIndex: 'name',
            render: (_, record) =>
                record.isDir ? (
                    <div onClick={() => getFileData(connectId, record.path)} style={{ cursor: 'pointer' }}>
                        <FolderTwoTone />
                        <span style={{ color: '#1890ff', paddingLeft: 5 }}>{record.name}</span>
                    </div>
                ) : (
                    <React.Fragment>
                        {record.isLink ? (
                            <div>
                                <LinkOutlined />
                                <Tooltip title="Is Link">
                                    <span style={{ color: '#3cb371', paddingLeft: 5 }}>{record.name}</span>
                                </Tooltip>
                            </div>
                        ) : (
                            <div>
                                <FileOutlined />
                                <span style={{ paddingLeft: 5 }}>{record.name}</span>
                            </div>
                        )}
                    </React.Fragment>
                ),
        },
        {
            title: '大小',
            dataIndex: 'size',
        },
        {
            title: '修改时间',
            dataIndex: 'mtime',
        },
        {
            title: '属性',
            dataIndex: 'mode',
        },
        {
            title: '操作',
            dataIndex: 'option',
            valueType: 'option',
            render: (_, record) =>
                !record.isDir && !record.isLink ? (
                    <>
                        <Tooltip title="下载文件">
                            <DownloadOutlined
                                style={{ fontSize: '17px', color: 'blue' }}
                                onClick={() => handleDownload(connectId, record.path)}
                            />
                        </Tooltip>
                        <Divider type="vertical" />
                        <Tooltip title="删除文件">
                            <DeleteOutlined
                                style={{ fontSize: '17px', color: 'red' }}
                                onClick={() => handleDelete(connectId, record.path)}
                            />
                        </Tooltip>
                    </>
                ) : null,
        },
    ];

    useEffect(() => {
        // 是否显示隐藏文件
        getFileData(connectId, initPath as string); // 刷新数据
    }, [showHidden]);

    const { Dragger } = Upload;
    return (
        <Drawer
            title="文件管理器"
            placement="right"
            width={800}
            visible={modalVisible}
            onClose={()=>handleChange(false)}
            getContainer={false}
        >
            {/* <input style={{ display: 'none' }} type="file" ref={(ref) => (this.input = ref)} /> */}
            <div className={styles.drawerHeader}>
                <Breadcrumb>
                    <Breadcrumb.Item href="#" onClick={() => getFileData(connectId, '/')}>
                        <ApartmentOutlined />
                    </Breadcrumb.Item>
                    <Breadcrumb.Item href="#" onClick={() => getFileData(connectId, '')}>
                        <HomeOutlined />
                    </Breadcrumb.Item>
                    {currentPathArr.map((item) => (
                        <Breadcrumb.Item key={item} href="#" onClick={() => getChdirDirData(connectId, item)}>
                            <span>{item}</span>
                        </Breadcrumb.Item>
                    ))}
                </Breadcrumb>
                <div style={{ display: 'flex', alignItems: 'center' }}>
                    <span>显示隐藏文件:</span>
                    <Switch
                        checked={showHidden}
                        checkedChildren="开启"
                        unCheckedChildren="关闭"
                        onChange={(v) => {
                            setShowHidden(v);
                        }}
                    />

                    <Button
                        style={{ marginLeft: 10 }}
                        size="small"
                        type="primary"
                        icon={<UploadOutlined />}
                        onClick={() => setChildrenDrawer(true)}
                    >
                        上传文件
                    </Button>
                </div>
            </div>
            <Drawer
                title="上传文件"
                width={320}
                closable={false}
                onClose={() => setChildrenDrawer(false)}
                visible={childrenDrawer}
            >
                <div style={{ height: 150 }}>
                    <Dragger {...uploadProps}>
                        <p className="ant-upload-drag-icon">
                            <InboxOutlined />
                        </p>
                        <p className="ant-upload-text">单击或拖入上传</p>
                        <p className="ant-upload-hint">支持多文件</p>
                    </Dragger>
                </div>
            </Drawer>
            <ProTable
                pagination={false}
                search={false}
                toolBarRender={false}
                rowKey="name"
                dataSource={columnData}
                columns={columns}
            />
        </Drawer>
    );
}
Example #15
Source File: ChartHeaderPanel.tsx    From datart with Apache License 2.0 4 votes vote down vote up
ChartHeaderPanel: FC<{
  chartName?: string;
  orgId?: string;
  container?: string;
  onSaveChart?: () => void;
  onGoBack?: () => void;
  onSaveChartToDashBoard?: (dashboardId, dashboardType) => void;
}> = memo(
  ({
    chartName,
    orgId,
    container,
    onSaveChart,
    onGoBack,
    onSaveChartToDashBoard,
  }) => {
    const t = useI18NPrefix(`viz.workbench.header`);
    const hasVizFetched = useSelector(selectHasVizFetched);
    const [isModalVisible, setIsModalVisible] = useState<boolean>(false);
    const backendChart = useSelector(backendChartSelector);
    const downloadPolling = useSelector(selectChartEditorDownloadPolling);
    const dispatch = useDispatch();
    const { actions } = useWorkbenchSlice();

    const handleModalOk = useCallback(
      (dashboardId: string, dashboardType: string) => {
        onSaveChartToDashBoard?.(dashboardId, dashboardType);
        setIsModalVisible(true);
      },
      [onSaveChartToDashBoard],
    );

    const handleModalCancel = useCallback(() => {
      setIsModalVisible(false);
    }, []);

    const onSetPolling = useCallback(
      (polling: boolean) => {
        dispatch(actions.setChartEditorDownloadPolling(polling));
      },
      [dispatch, actions],
    );

    useMount(() => {
      if (!hasVizFetched) {
        // Request data when there is no data
        dispatch(getFolders(orgId as string));
      }
    });

    return (
      <Wrapper>
        <h1>{chartName}</h1>
        <Space>
          <DownloadListPopup
            polling={downloadPolling}
            setPolling={onSetPolling}
            onLoadTasks={loadTasks}
            onDownloadFile={item => {
              if (item.id) {
                downloadFile(item.id).then(() => {
                  dispatch(actions.setChartEditorDownloadPolling(true));
                });
              }
            }}
            renderDom={
              <Button icon={<DownloadOutlined />}>{t('downloadList')}</Button>
            }
          />
          <Button onClick={onGoBack}>{t('cancel')}</Button>
          <Button type="primary" onClick={onSaveChart}>
            {t('save')}
          </Button>
          {!(container === 'widget') && (
            <Button
              type="primary"
              onClick={() => {
                setIsModalVisible(true);
              }}
            >
              {t('saveToDashboard')}
            </Button>
          )}
          <SaveToDashboard
            orgId={orgId as string}
            title={t('saveToDashboard')}
            isModalVisible={isModalVisible}
            handleOk={handleModalOk}
            handleCancel={handleModalCancel}
            backendChartId={backendChart?.id}
          ></SaveToDashboard>
        </Space>
      </Wrapper>
    );
  },
)
Example #16
Source File: HTTPFuzzerPage.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
HTTPFuzzerPage: React.FC<HTTPFuzzerPageProp> = (props) => {
    // params
    const [isHttps, setIsHttps, getIsHttps] = useGetState<boolean>(props.fuzzerParams?.isHttps || props.isHttps || false)
    const [noFixContentLength, setNoFixContentLength] = useState(false)
    const [request, setRequest, getRequest] = useGetState(props.fuzzerParams?.request || props.request || defaultPostTemplate)
    const [concurrent, setConcurrent] = useState(props.fuzzerParams?.concurrent || 20)
    const [forceFuzz, setForceFuzz] = useState<boolean>(props.fuzzerParams?.forceFuzz || true)
    const [timeout, setParamTimeout] = useState(props.fuzzerParams?.timeout || 10.0)
    const [proxy, setProxy] = useState(props.fuzzerParams?.proxy || "")
    const [actualHost, setActualHost] = useState(props.fuzzerParams?.actualHost || "")
    const [advancedConfig, setAdvancedConfig] = useState(false)
    const [redirectedResponse, setRedirectedResponse] = useState<FuzzerResponse>()
    const [historyTask, setHistoryTask] = useState<HistoryHTTPFuzzerTask>();
    const [hotPatchCode, setHotPatchCode] = useState<string>("");

    // filter
    const [_, setFilter, getFilter] = useGetState<FuzzResponseFilter>({
        Keywords: [],
        MaxBodySize: 0,
        MinBodySize: 0,
        Regexps: [],
        StatusCode: []
    });
    const [droppedCount, setDroppedCount] = useState(0);

    // state
    const [loading, setLoading] = useState(false)
    const [content, setContent] = useState<FuzzerResponse[]>([])
    const [reqEditor, setReqEditor] = useState<IMonacoEditor>()
    const [fuzzToken, setFuzzToken] = useState("")
    const [search, setSearch] = useState("")
    const [targetUrl, setTargetUrl] = useState("")

    const [refreshTrigger, setRefreshTrigger] = useState(false)
    const refreshRequest = () => {
        setRefreshTrigger(!refreshTrigger)
    }

    // history
    const [history, setHistory] = useState<string[]>([])
    const [currentHistoryIndex, setCurrentHistoryIndex] = useState<number>()

    const [urlPacketShow, setUrlPacketShow] = useState<boolean>(false)

    // filter
    const [keyword, setKeyword] = useState<string>("")
    const [filterContent, setFilterContent] = useState<FuzzerResponse[]>([])
    const [timer, setTimer] = useState<any>()

    useEffect(() => {
        getValue(WEB_FUZZ_HOTPATCH_CODE).then((data: any) => {
            if (!data) {
                return
            }
            setHotPatchCode(`${data}`)
        })
    }, [])

    // 定时器
    const sendTimer = useRef<any>(null)

    const withdrawRequest = useMemoizedFn(() => {
        const targetIndex = history.indexOf(request) - 1
        if (targetIndex >= 0) {
            setRequest(history[targetIndex])
            setCurrentHistoryIndex(targetIndex)
        }
    })
    const forwardRequest = useMemoizedFn(() => {
        const targetIndex = history.indexOf(request) + 1
        if (targetIndex < history.length) {
            setCurrentHistoryIndex(targetIndex)
            setRequest(history[targetIndex])
        }
    })

    const sendToFuzzer = useMemoizedFn((isHttps: boolean, request: string) => {
        ipcRenderer.invoke("send-to-tab", {
            type: "fuzzer",
            data: {isHttps: isHttps, request: request}
        })
    })
    const sendToPlugin = useMemoizedFn((request: Uint8Array, isHTTPS: boolean, response?: Uint8Array) => {
        let m = showDrawer({
            width: "80%",
            content: <HackerPlugin request={request} isHTTPS={isHTTPS} response={response}></HackerPlugin>
        })
    })

    // 从历史记录中恢复
    useEffect(() => {
        if (!historyTask) {
            return
        }

        setRequest(historyTask.Request)
        setIsHttps(historyTask.IsHTTPS)
        setProxy(historyTask.Proxy)
        refreshRequest()
    }, [historyTask])

    useEffect(() => {
        // 缓存全局参数
        getValue(WEB_FUZZ_PROXY).then(e => {
            if (!e) {
                return
            }
            setProxy(`${e}`)
        })

    }, [])

    useEffect(() => {
        if (currentHistoryIndex === undefined) {
            return
        }
        refreshRequest()
    }, [currentHistoryIndex])

    useEffect(() => {
        setIsHttps(!!props.isHttps)
        if (props.request) {
            setRequest(props.request)
            setContent([])
        }
    }, [props.isHttps, props.request])

    const loadHistory = useMemoizedFn((id: number) => {
        setLoading(true)
        setHistory([])
        ipcRenderer.invoke(
            "HTTPFuzzer",
            {HistoryWebFuzzerId: id}, fuzzToken
        ).then(() => {
            ipcRenderer.invoke("GetHistoryHTTPFuzzerTask", {Id: id}).then((data: { OriginRequest: HistoryHTTPFuzzerTask }) => {
                setHistoryTask(data.OriginRequest)
            })
        })
    })

    const submitToHTTPFuzzer = useMemoizedFn(() => {
        // 清楚历史任务的标记
        setHistoryTask(undefined);

        saveValue(WEB_FUZZ_PROXY, proxy)
        setLoading(true)
        if (history.includes(request)) {
            history.splice(history.indexOf(request), 1)
        }
        history.push(request)
        setHistory([...history])

        setDroppedCount(0)
        ipcRenderer.invoke(
            "HTTPFuzzer",
            {
                Request: request,
                ForceFuzz: forceFuzz,
                IsHTTPS: isHttps,
                Concurrent: concurrent,
                PerRequestTimeoutSeconds: timeout,
                NoFixContentLength: noFixContentLength,
                Proxy: proxy,
                ActualAddr: actualHost,
                HotPatchCode: hotPatchCode,
                Filter: getFilter(),
            },
            fuzzToken
        )
    })

    const cancelCurrentHTTPFuzzer = useMemoizedFn(() => {
        ipcRenderer.invoke("cancel-HTTPFuzzer", fuzzToken)
    })

    useEffect(() => {
        const token = randomString(60)
        setFuzzToken(token)

        const dataToken = `${token}-data`
        const errToken = `${token}-error`
        const endToken = `${token}-end`

        ipcRenderer.on(errToken, (e, details) => {
            notification["error"]({
                message: `提交模糊测试请求失败 ${details}`,
                placement: "bottomRight"
            })
        })
        let buffer: FuzzerResponse[] = []
        let droppedCount = 0;
        let count: number = 0
        const updateData = () => {
            if (buffer.length <= 0) {
                return
            }
            if (JSON.stringify(buffer) !== JSON.stringify(content)) setContent([...buffer])
        }

        ipcRenderer.on(dataToken, (e: any, data: any) => {
            if (data["MatchedByFilter"] !== true && !filterIsEmpty(getFilter())) {
                // 不匹配的
                droppedCount++
                setDroppedCount(droppedCount)
                return
            }
            // const response = new Buffer(data.ResponseRaw).toString(fixEncoding(data.GuessResponseEncoding))
            buffer.push({
                StatusCode: data.StatusCode,
                Ok: data.Ok,
                Reason: data.Reason,
                Method: data.Method,
                Host: data.Host,
                ContentType: data.ContentType,
                Headers: (data.Headers || []).map((i: any) => {
                    return {Header: i.Header, Value: i.Value}
                }),
                DurationMs: data.DurationMs,
                BodyLength: data.BodyLength,
                UUID: data.UUID,
                Timestamp: data.Timestamp,
                ResponseRaw: data.ResponseRaw,
                RequestRaw: data.RequestRaw,
                Payloads: data.Payloads,
                IsHTTPS: data.IsHTTPS,
                Count: count,
                BodySimilarity: data.BodySimilarity,
                HeaderSimilarity: data.HeaderSimilarity,
            } as FuzzerResponse)
            count++
            // setContent([...buffer])
        })
        ipcRenderer.on(endToken, () => {
            updateData()
            buffer = []
            count = 0
            droppedCount = 0
            setLoading(false)
        })

        const updateDataId = setInterval(() => {
            updateData()
        }, 200)

        return () => {
            ipcRenderer.invoke("cancel-HTTPFuzzer", token)

            clearInterval(updateDataId)
            ipcRenderer.removeAllListeners(errToken)
            ipcRenderer.removeAllListeners(dataToken)
            ipcRenderer.removeAllListeners(endToken)
        }
    }, [])

    const searchContent = (keyword: string) => {
        if (timer) {
            clearTimeout(timer)
            setTimer(null)
        }
        setTimer(
            setTimeout(() => {
                try {
                    const filters = content.filter((item) => {
                        return Buffer.from(item.ResponseRaw).toString("utf8").match(new RegExp(keyword, "g"))
                    })
                    setFilterContent(filters)
                } catch (error) {
                }
            }, 500)
        )
    }

    const downloadContent = useMemoizedFn(() => {
        if (!keyword) {
            failed('请先输入需要搜索的关键词')
            return
        }

        const strs = []
        try {
            const reg = new RegExp(keyword)
            for (let info of filterContent) {
                let str = Buffer.from(info.ResponseRaw).toString('utf8')
                let temp: any
                while ((temp = reg.exec(str)) !== null) {
                    // @ts-ignore
                    if (temp[1]) {
                        // @ts-ignore
                        strs.push(temp[1])
                        str = str.substring(temp['index'] + 1)
                        reg.lastIndex = 0
                    } else {
                        break
                    }
                }
            }
        } catch (error) {
            failed("正则有问题,请检查后重新输入")
            return
        }

        if (strs.length === 0) {
            failed('未捕获到关键词信息')
            return
        }

        ipcRenderer.invoke("show-save-dialog", 'fuzzer列表命中内容.txt').then((res) => {
            if (res.canceled) return

            ipcRenderer
                .invoke("write-file", {
                    route: res.filePath,
                    data: strs.join('\n\r')
                })
                .then(() => {
                    success('下载完成')
                    ipcRenderer.invoke("open-specified-file", res.filePath)
                })
        })
    })

    useEffect(() => {
        if (!!keyword) {
            searchContent(keyword)
        } else {
            setFilterContent([])
        }
    }, [keyword])

    useEffect(() => {
        if (keyword && content.length !== 0) {
            const filters = content.filter(item => {
                return Buffer.from(item.ResponseRaw).toString("utf8").match(new RegExp(keyword, 'g'))
            })
            setFilterContent(filters)
        }
    }, [content])

    const onlyOneResponse = !loading && (content || []).length === 1

    const filtredResponses =
        search === ""
            ? content || []
            : (content || []).filter((i) => {
                return Buffer.from(i.ResponseRaw).toString().includes(search)
            })
    const successResults = filtredResponses.filter((i) => i.Ok)
    const failedResults = filtredResponses.filter((i) => !i.Ok)

    const sendFuzzerSettingInfo = useMemoizedFn(() => {
        const info: fuzzerInfoProp = {
            time: new Date().getTime().toString(),
            isHttps: isHttps,
            forceFuzz: forceFuzz,
            concurrent: concurrent,
            proxy: proxy,
            actualHost: actualHost,
            timeout: timeout,
            request: request
        }
        if (sendTimer.current) {
            clearTimeout(sendTimer.current)
            sendTimer.current = null
        }
        sendTimer.current = setTimeout(() => {
            ipcRenderer.invoke('send-fuzzer-setting-data', {key: props.order || "", param: JSON.stringify(info)})
        }, 1000);
    })
    useEffect(() => {
        sendFuzzerSettingInfo()
    }, [isHttps, forceFuzz, concurrent, proxy, actualHost, timeout, request])

    const responseViewer = useMemoizedFn((rsp: FuzzerResponse) => {
        return (
            <HTTPPacketEditor
                system={props.system}
                originValue={rsp.ResponseRaw}
                bordered={true}
                hideSearch={true}
                emptyOr={
                    !rsp?.Ok && (
                        <Result
                            status={"error"}
                            title={"请求失败"}
                            // no such host
                            subTitle={(() => {
                                const reason = content[0]!.Reason
                                if (reason.includes("tcp: i/o timeout")) {
                                    return "网络超时"
                                }
                                if (reason.includes("no such host")) {
                                    return "DNS 错误或主机错误"
                                }
                                return undefined
                            })()}
                        >
                            <>详细原因:{rsp.Reason}</>
                        </Result>
                    )
                }
                readOnly={true}
                extra={
                    (
                        <Space>
                            {loading && <Spin size={"small"} spinning={loading}/>}
                            {onlyOneResponse ? (
                                <Space>
                                    {content[0].IsHTTPS && <Tag>{content[0].IsHTTPS ? "https" : ""}</Tag>}
                                    <Tag>{content[0].DurationMs}ms</Tag>
                                    <Space key='single'>
                                        <Button
                                            size={"small"}
                                            onClick={() => {
                                                analyzeFuzzerResponse(
                                                    rsp,
                                                    (bool, r) => {
                                                        // setRequest(r)
                                                        // refreshRequest()
                                                    }
                                                )
                                            }}
                                            type={"primary"}
                                            icon={<ProfileOutlined/>}
                                        >
                                            详情
                                        </Button>
                                        <Button
                                            type={"primary"}
                                            size={"small"}
                                            onClick={() => {
                                                setContent([])
                                            }}
                                            danger={true}
                                            icon={<DeleteOutlined/>}
                                        />
                                    </Space>
                                </Space>
                            ) : (
                                <Space key='list'>
                                    <Tag color={"green"}>成功:{successResults.length}</Tag>
                                    <Input
                                        size={"small"}
                                        value={search}
                                        onChange={(e) => {
                                            setSearch(e.target.value)
                                        }}
                                    />
                                    {/*<Tag>当前请求结果数[{(content || []).length}]</Tag>*/}
                                    <Button
                                        size={"small"}
                                        onClick={() => {
                                            setContent([])
                                        }}
                                    >
                                        清除数据
                                    </Button>
                                </Space>
                            )}

                        </Space>
                    )
                }
            />
        )
    })

    const hotPatchTrigger = useMemoizedFn(() => {
        let m = showModal({
            title: "调试 / 插入热加载代码",
            width: "60%",
            content: (
                <div>
                    <HTTPFuzzerHotPatch initialHotPatchCode={hotPatchCode || ""} onInsert={tag => {
                        if (reqEditor) monacoEditorWrite(reqEditor, tag);
                        m.destroy()
                    }} onSaveCode={code => {
                        setHotPatchCode(code)
                        saveValue(WEB_FUZZ_HOTPATCH_CODE, code)
                    }}/>
                </div>
            )
        })
    })

    return (
        <div style={{height: "100%", width: "100%", display: "flex", flexDirection: "column", overflow: "hidden"}}>
            <Row gutter={8} style={{marginBottom: 8}}>
                <Col span={24} style={{textAlign: "left", marginTop: 4}}>
                    <Space>
                        {loading ? (
                            <Button
                                style={{width: 150}}
                                onClick={() => {
                                    cancelCurrentHTTPFuzzer()
                                }}
                                // size={"small"}
                                danger={true}
                                type={"primary"}
                            >
                                强制停止
                            </Button>
                        ) : (
                            <Button
                                style={{width: 150}}
                                onClick={() => {
                                    setContent([])
                                    setRedirectedResponse(undefined)
                                    sendFuzzerSettingInfo()
                                    submitToHTTPFuzzer()
                                }}
                                // size={"small"}
                                type={"primary"}
                            >
                                发送数据包
                            </Button>
                        )}
                        <Space>
                            <Button
                                onClick={() => {
                                    withdrawRequest()
                                }}
                                type={"link"}
                                icon={<LeftOutlined/>}
                            />
                            <Button
                                onClick={() => {
                                    forwardRequest()
                                }}
                                type={"link"}
                                icon={<RightOutlined/>}
                            />
                            {history.length > 1 && (
                                <Dropdown
                                    trigger={["click"]}
                                    overlay={() => {
                                        return (
                                            <Menu>
                                                {history.map((i, index) => {
                                                    return (
                                                        <Menu.Item
                                                            style={{width: 120}}
                                                            onClick={() => {
                                                                setRequest(i)
                                                                setCurrentHistoryIndex(index)
                                                            }}
                                                        >{`${index}`}</Menu.Item>
                                                    )
                                                })}
                                            </Menu>
                                        )
                                    }}
                                >
                                    <Button size={"small"} type={"link"} onClick={(e) => e.preventDefault()}>
                                        History <DownOutlined/>
                                    </Button>
                                </Dropdown>
                            )}
                        </Space>
                        <Checkbox defaultChecked={isHttps} value={isHttps} onChange={() => setIsHttps(!isHttps)}>强制
                            HTTPS</Checkbox>
                        <SwitchItem
                            label={"高级配置"}
                            formItemStyle={{marginBottom: 0}}
                            value={advancedConfig}
                            setValue={setAdvancedConfig}
                            size={"small"}
                        />
                        {droppedCount > 0 && <Tag color={"red"}>已丢弃[{droppedCount}]个响应</Tag>}
                        {onlyOneResponse && content[0].Ok && (
                            <Form.Item style={{marginBottom: 0}}>
                                <Button
                                    onClick={() => {
                                        setLoading(true)
                                        ipcRenderer
                                            .invoke("RedirectRequest", {
                                                Request: request,
                                                Response: new Buffer(content[0].ResponseRaw).toString("utf8"),
                                                IsHttps: isHttps,
                                                PerRequestTimeoutSeconds: timeout,
                                                NoFixContentLength: noFixContentLength,
                                                Proxy: proxy
                                            })
                                            .then((rsp: FuzzerResponse) => {
                                                setRedirectedResponse(rsp)
                                            })
                                            .catch((e) => {
                                                failed(`"ERROR in: ${e}"`)
                                            })
                                            .finally(() => {
                                                setTimeout(() => setLoading(false), 300)
                                            })
                                    }}
                                >
                                    跟随重定向
                                </Button>
                            </Form.Item>
                        )}
                        {loading && (
                            <Space>
                                <Spin size={"small"}/>
                                <div style={{color: "#3a8be3"}}>sending packets</div>
                            </Space>
                        )}
                        {proxy && <Tag>代理:{proxy}</Tag>}
                        {/*<Popover*/}
                        {/*    trigger={"click"}*/}
                        {/*    content={*/}
                        {/*    }*/}
                        {/*>*/}
                        {/*    <Button type={"link"} size={"small"}>*/}
                        {/*        配置请求包*/}
                        {/*    </Button>*/}
                        {/*</Popover>*/}
                        {actualHost !== "" && <Tag color={"red"}>请求 Host:{actualHost}</Tag>}
                    </Space>
                </Col>
                {/*<Col span={12} style={{textAlign: "left"}}>*/}
                {/*</Col>*/}
            </Row>

            {advancedConfig && (
                <Row style={{marginBottom: 8}} gutter={8}>
                    <Col span={16}>
                        {/*高级配置*/}
                        <Card bordered={true} size={"small"} bodyStyle={{height: 106}}>
                            <Spin style={{width: "100%"}} spinning={!reqEditor}>
                                <Form
                                    onSubmitCapture={(e) => e.preventDefault()}
                                    // layout={"horizontal"}
                                    size={"small"}
                                    // labelCol={{span: 8}}
                                    // wrapperCol={{span: 16}}
                                >
                                    <Row gutter={8}>
                                        <Col span={12} xl={8}>
                                            <Form.Item
                                                label={<OneLine width={68}>Intruder</OneLine>}
                                                style={{marginBottom: 4}}
                                            >
                                                <Button
                                                    style={{backgroundColor: "#08a701"}}
                                                    size={"small"}
                                                    type={"primary"}
                                                    onClick={() => {
                                                        const m = showModal({
                                                            width: "70%",
                                                            content: (
                                                                <>
                                                                    <StringFuzzer
                                                                        advanced={true}
                                                                        disableBasicMode={true}
                                                                        insertCallback={(template: string) => {
                                                                            if (!template) {
                                                                                Modal.warn({
                                                                                    title: "Payload 为空 / Fuzz 模版为空"
                                                                                })
                                                                            } else {
                                                                                if (reqEditor && template) {
                                                                                    reqEditor.trigger(
                                                                                        "keyboard",
                                                                                        "type",
                                                                                        {
                                                                                            text: template
                                                                                        }
                                                                                    )
                                                                                } else {
                                                                                    Modal.error({
                                                                                        title: "BUG: 编辑器失效"
                                                                                    })
                                                                                }
                                                                                m.destroy()
                                                                            }
                                                                        }}
                                                                    />
                                                                </>
                                                            )
                                                        })
                                                    }}
                                                >
                                                    插入 yak.fuzz 语法
                                                </Button>
                                            </Form.Item>
                                        </Col>
                                        <Col span={12} xl={8}>
                                            <SwitchItem
                                                label={<OneLine width={68}>渲染 fuzz</OneLine>}
                                                setValue={(e) => {
                                                    if (!e) {
                                                        Modal.confirm({
                                                            title: "确认关闭 Fuzz 功能吗?关闭之后,所有的 Fuzz 标签将会失效",
                                                            onOk: () => {
                                                                setForceFuzz(e)
                                                            }
                                                        })
                                                        return
                                                    }
                                                    setForceFuzz(e)
                                                }}
                                                size={"small"}
                                                value={forceFuzz}
                                                formItemStyle={{marginBottom: 4}}
                                            />
                                        </Col>
                                        <Col span={12} xl={8}>
                                            <InputInteger
                                                label={<OneLine width={68}>并发线程</OneLine>}
                                                size={"small"}
                                                setValue={(e) => {
                                                    setConcurrent(e)
                                                }}
                                                formItemStyle={{marginBottom: 4}} // width={40}
                                                width={50}
                                                value={concurrent}
                                            />
                                        </Col>
                                        <Col span={12} xl={8}>
                                            <SwitchItem
                                                label={<OneLine width={68}>HTTPS</OneLine>}
                                                setValue={(e) => {
                                                    setIsHttps(e)
                                                }}
                                                size={"small"}
                                                value={isHttps}
                                                formItemStyle={{marginBottom: 4}}
                                            />
                                        </Col>
                                        <Col span={12} xl={8}>
                                            <SwitchItem
                                                label={<OneLine width={70}>
                                                    <Tooltip title={"不修复 Content-Length: 常用发送多个数据包"}>
                                                        不修复长度
                                                    </Tooltip>
                                                </OneLine>}
                                                setValue={(e) => {
                                                    setNoFixContentLength(e)
                                                }}
                                                size={"small"}
                                                value={noFixContentLength}
                                                formItemStyle={{marginBottom: 4}}
                                            />
                                        </Col>
                                        <Col span={12} xl={8}>
                                            <ItemSelects
                                                item={{
                                                    style: {marginBottom: 4},
                                                    label: <OneLine width={68}>设置代理</OneLine>,
                                                }}
                                                select={{
                                                    style: {width: "100%"},
                                                    allowClear: true,
                                                    autoClearSearchValue: true,
                                                    maxTagTextLength: 8,
                                                    mode: "tags",
                                                    data: [
                                                        {text: "http://127.0.0.1:7890", value: "http://127.0.0.1:7890"},
                                                        {text: "http://127.0.0.1:8080", value: "http://127.0.0.1:8080"},
                                                        {text: "http://127.0.0.1:8082", value: "http://127.0.0.1:8082"}
                                                    ],
                                                    value: proxy ? proxy.split(",") : [],
                                                    setValue: (value) => setProxy(value.join(",")),
                                                    maxTagCount: "responsive",
                                                }}
                                            ></ItemSelects>
                                            {/* <ManyMultiSelectForString
                                                formItemStyle={{marginBottom: 4}}
                                                label={<OneLine width={68}>设置代理</OneLine>}
                                                data={[
                                                    "http://127.0.0.1:7890",
                                                    "http://127.0.0.1:8080",
                                                    "http://127.0.0.1:8082"
                                                ].map((i) => {
                                                    return {label: i, value: i}
                                                })}
                                                mode={"tags"}
                                                defaultSep={","}
                                                value={proxy}
                                                setValue={(r) => {
                                                    setProxy(r.split(",").join(","))
                                                }}
                                            /> */}
                                        </Col>
                                        <Col span={12} xl={8}>
                                            <InputItem
                                                extraFormItemProps={{
                                                    style: {marginBottom: 0}
                                                }}
                                                label={<OneLine width={68}>请求 Host</OneLine>}
                                                setValue={setActualHost}
                                                value={actualHost}
                                            />
                                        </Col>
                                        <Col span={12} xl={8}>
                                            <InputFloat
                                                formItemStyle={{marginBottom: 4}}
                                                size={"small"}
                                                label={<OneLine width={68}>超时时间</OneLine>}
                                                setValue={setParamTimeout}
                                                value={timeout}
                                            />
                                        </Col>
                                    </Row>
                                </Form>
                            </Spin>
                        </Card>
                    </Col>
                    <Col span={8}>
                        <AutoCard title={<Tooltip title={"通过过滤匹配,丢弃无用数据包,保证界面性能!"}>
                            设置过滤器
                        </Tooltip>}
                                  bordered={false} size={"small"} bodyStyle={{paddingTop: 4}}
                                  style={{marginTop: 0, paddingTop: 0}}
                        >
                            <Form size={"small"} onSubmitCapture={e => e.preventDefault()}>
                                <Row gutter={20}>
                                    <Col span={12}>
                                        <InputItem
                                            label={"状态码"} placeholder={"200,300-399"}
                                            disable={loading}
                                            value={getFilter().StatusCode.join(",")}
                                            setValue={e => {
                                                setFilter({...getFilter(), StatusCode: e.split(",").filter(i => !!i)})
                                            }}
                                            extraFormItemProps={{style: {marginBottom: 0}}}
                                        />
                                    </Col>
                                    <Col span={12}>
                                        <InputItem
                                            label={"关键字"} placeholder={"Login,登录成功"}
                                            value={getFilter().Keywords.join(",")}
                                            disable={loading}
                                            setValue={e => {
                                                setFilter({...getFilter(), Keywords: e.split(",").filter(i => !!i)})
                                            }}
                                            extraFormItemProps={{style: {marginBottom: 0}}}
                                        />
                                    </Col>
                                    <Col span={12}>
                                        <InputItem
                                            label={"正则"} placeholder={`Welcome\\s+\\w+!`}
                                            value={getFilter().Regexps.join(",")}
                                            disable={loading}
                                            setValue={e => {
                                                setFilter({...getFilter(), Regexps: e.split(",").filter(i => !!i)})
                                            }}
                                            extraFormItemProps={{style: {marginBottom: 0, marginTop: 2}}}
                                        />
                                    </Col>
                                </Row>
                            </Form>
                        </AutoCard>
                    </Col>
                </Row>
            )}
            {/*<Divider style={{marginTop: 6, marginBottom: 8, paddingTop: 0}}/>*/}
            <ResizeBox
                firstMinSize={350} secondMinSize={360}
                style={{overflow: "hidden"}}
                firstNode={<HTTPPacketEditor
                    system={props.system}
                    refreshTrigger={refreshTrigger}
                    hideSearch={true}
                    bordered={true}
                    originValue={new Buffer(request)}
                    actions={[
                        {
                            id: "packet-from-url",
                            label: "URL转数据包",
                            contextMenuGroupId: "1_urlPacket",
                            run: () => {
                                setUrlPacketShow(true)
                            }
                        },
                        {
                            id: "copy-as-url",
                            label: "复制为 URL",
                            contextMenuGroupId: "1_urlPacket",
                            run: () => {
                                copyAsUrl({Request: getRequest(), IsHTTPS: getIsHttps()})
                            }
                        },
                        {
                            id: "insert-intruder-tag",
                            label: "插入模糊测试字典标签",
                            contextMenuGroupId: "1_urlPacket",
                            run: (editor) => {
                                showDictsAndSelect(i => {
                                    monacoEditorWrite(editor, i, editor.getSelection())
                                })
                            }
                        },
                        {
                            id: "insert-hotpatch-tag",
                            label: "插入热加载标签",
                            contextMenuGroupId: "1_urlPacket",
                            run: (editor) => {
                                hotPatchTrigger()
                            }
                        },
                    ]}
                    onEditor={setReqEditor}
                    onChange={(i) => setRequest(new Buffer(i).toString("utf8"))}
                    extra={
                        <Space size={2}>
                            <Button
                                style={{marginRight: 1}}
                                size={"small"} type={"primary"}
                                onClick={() => {
                                    hotPatchTrigger()
                                }}
                            >热加载标签</Button>
                            <Popover
                                trigger={"click"}
                                title={"从 URL 加载数据包"}
                                content={
                                    <div style={{width: 400}}>
                                        <Form
                                            layout={"vertical"}
                                            onSubmitCapture={(e) => {
                                                e.preventDefault()

                                                ipcRenderer
                                                    .invoke("Codec", {
                                                        Type: "packet-from-url",
                                                        Text: targetUrl
                                                    })
                                                    .then((e) => {
                                                        if (e?.Result) {
                                                            setRequest(e.Result)
                                                            refreshRequest()
                                                        }
                                                    })
                                                    .finally(() => {
                                                    })
                                            }}
                                            size={"small"}
                                        >
                                            <InputItem
                                                label={"从 URL 构造请求"}
                                                value={targetUrl}
                                                setValue={setTargetUrl}
                                                extraFormItemProps={{style: {marginBottom: 8}}}
                                            ></InputItem>
                                            <Form.Item style={{marginBottom: 8}}>
                                                <Button type={"primary"} htmlType={"submit"}>
                                                    构造请求
                                                </Button>
                                            </Form.Item>

                                        </Form>
                                    </div>
                                }
                            >
                                <Button size={"small"} type={"primary"}>
                                    URL
                                </Button>
                            </Popover>
                            <Popover
                                trigger={"click"}
                                placement={"bottom"}
                                destroyTooltipOnHide={true}
                                content={
                                    <div style={{width: 400}}>
                                        <HTTPFuzzerHistorySelector onSelect={e => {
                                            loadHistory(e)
                                        }}/>
                                    </div>
                                }
                            >
                                <Button size={"small"} type={"primary"} icon={<HistoryOutlined/>}>
                                    历史
                                </Button>
                            </Popover>
                        </Space>
                    }
                />}
                secondNode={<AutoSpin spinning={false}>
                    {onlyOneResponse ? (
                        <>{redirectedResponse ? responseViewer(redirectedResponse) : responseViewer(content[0])}</>
                    ) : (
                        <>
                            {(content || []).length > 0 ? (
                                <HTTPFuzzerResultsCard
                                    onSendToWebFuzzer={sendToFuzzer}
                                    sendToPlugin={sendToPlugin}
                                    setRequest={(r) => {
                                        setRequest(r)
                                        refreshRequest()
                                    }}
                                    extra={
                                        <div>
                                            <Input
                                                value={keyword}
                                                style={{maxWidth: 200}}
                                                allowClear
                                                placeholder="输入字符串或正则表达式"
                                                onChange={e => setKeyword(e.target.value)}
                                                addonAfter={
                                                    <DownloadOutlined style={{cursor: "pointer"}}
                                                                      onClick={downloadContent}/>
                                                }></Input>
                                        </div>
                                    }
                                    failedResponses={failedResults}
                                    successResponses={filterContent.length !== 0 ? filterContent : keyword ? [] : successResults}
                                />
                            ) : (
                                <Result
                                    status={"info"}
                                    title={"请在左边编辑并发送一个 HTTP 请求/模糊测试"}
                                    subTitle={
                                        "本栏结果针对模糊测试的多个 HTTP 请求结果展示做了优化,可以自动识别单个/多个请求的展示"
                                    }
                                />
                            )}
                        </>
                    )}
                </AutoSpin>}/>
            <Modal
                visible={urlPacketShow}
                title='从 URL 加载数据包'
                onCancel={() => setUrlPacketShow(false)}
                footer={null}
            >
                <Form
                    layout={"vertical"}
                    onSubmitCapture={(e) => {
                        e.preventDefault()

                        ipcRenderer
                            .invoke("Codec", {
                                Type: "packet-from-url",
                                Text: targetUrl
                            })
                            .then((e) => {
                                if (e?.Result) {
                                    setRequest(e.Result)
                                    refreshRequest()
                                    setUrlPacketShow(false)
                                }
                            })
                            .finally(() => {
                            })
                    }}
                    size={"small"}
                >
                    <InputItem
                        label={"从 URL 构造请求"}
                        value={targetUrl}
                        setValue={setTargetUrl}
                        extraFormItemProps={{style: {marginBottom: 8}}}
                    ></InputItem>
                    <Form.Item style={{marginBottom: 8}}>
                        <Button type={"primary"} htmlType={"submit"}>
                            构造请求
                        </Button>
                    </Form.Item>
                </Form>
            </Modal>
        </div>
    )
}
Example #17
Source File: palette.tsx    From jmix-frontend with Apache License 2.0 4 votes vote down vote up
palette = () => (
  <Palette>
    <Category name="Text">
      <Component name="Formatted Message">
        <Variant>
          <FormattedMessage />
        </Variant>
      </Component>
      <Component name="Heading">
        <Variant name="h1">
          <Typography.Title></Typography.Title>
        </Variant>
        <Variant name="h2">
          <Typography.Title level={2}></Typography.Title>
        </Variant>
        <Variant name="h3">
          <Typography.Title level={3}></Typography.Title>
        </Variant>
        <Variant name="h4">
          <Typography.Title level={4}></Typography.Title>
        </Variant>
        <Variant name="h5">
          <Typography.Title level={5}></Typography.Title>
        </Variant>
      </Component>
      <Component name="Text">
        <Variant>
          <Typography.Text></Typography.Text>
        </Variant>
        <Variant name="Secondary">
          <Typography.Text type="secondary"></Typography.Text>
        </Variant>
        <Variant name="Success">
          <Typography.Text type="success"></Typography.Text>
        </Variant>
        <Variant name="Warning">
          <Typography.Text type="warning"></Typography.Text>
        </Variant>
        <Variant name="Danger">
          <Typography.Text type="danger"></Typography.Text>
        </Variant>
        <Variant name="Disabled">
          <Typography.Text disabled></Typography.Text>
        </Variant>
      </Component>
    </Category>
    <Category name="Layout">
      <Component name="Divider">
        <Variant>
          <Divider />
        </Variant>
      </Component>

      <Component name="Grid">
        <Variant name="Simple Row">
          <Row></Row>
        </Variant>
        <Variant name="Two columns">
          <Row>
            <Col span={12}></Col>
            <Col span={12}></Col>
          </Row>
        </Variant>
        <Variant name="Three columns">
          <Row>
            <Col span={8}></Col>
            <Col span={8}></Col>
            <Col span={8}></Col>
          </Row>
        </Variant>
      </Component>

      <Component name="Space">
        <Variant>
          <Space />
        </Variant>
        <Variant name="Small">
          <Space size={"small"} />
        </Variant>
        <Variant name="Large">
          <Space size={"large"} />
        </Variant>
      </Component>
    </Category>
    <Category name="Controls">
      <Component name="Autocomplete">
        <Variant>
          <AutoComplete placeholder="input here" />
        </Variant>
      </Component>

      <Component name="Button">
        <Variant>
          <Button></Button>
        </Variant>
        <Variant name="Primary">
          <Button type="primary"></Button>
        </Variant>
        <Variant name="Link">
          <Button type="link"></Button>
        </Variant>
        <Variant name="Dropdown">
          <Dropdown
            trigger={["click"]}
            overlay={
              <Menu>
                <Menu.Item></Menu.Item>
                <Menu.Item></Menu.Item>
                <Menu.Item></Menu.Item>
              </Menu>
            }
          >
            <Button></Button>
          </Dropdown>
        </Variant>
      </Component>

      <Component name="Checkbox">
        <Variant>
          <Checkbox />
        </Variant>
      </Component>

      <Component name="Switch">
        <Variant>
          <Switch />
        </Variant>
      </Component>

      <Component name="Radio Group">
        <Variant>
          <Radio.Group>
            <Radio value={1}>A</Radio>
            <Radio value={2}>B</Radio>
            <Radio value={3}>C</Radio>
            <Radio value={4}>D</Radio>
          </Radio.Group>
        </Variant>
        <Variant name="Button">
          <Radio.Group>
            <Radio.Button value={1}>A</Radio.Button>
            <Radio.Button value={2}>B</Radio.Button>
            <Radio.Button value={3}>C</Radio.Button>
            <Radio.Button value={4}>D</Radio.Button>
          </Radio.Group>
        </Variant>
      </Component>

      <Component name="DatePicker">
        <Variant>
          <DatePicker />
        </Variant>
        <Variant name="Range">
          <DatePicker.RangePicker />
        </Variant>
      </Component>

      <Component name="TimePicker">
        <Variant>
          <TimePicker />
        </Variant>
        <Variant name="Range">
          <TimePicker.RangePicker />
        </Variant>
      </Component>

      <Component name="Input">
        <Variant>
          <Input />
        </Variant>
        <Variant name="Number">
          <InputNumber />
        </Variant>
      </Component>

      <Component name="Select">
        <Variant>
          <Select defaultValue="1">
            <Select.Option value="1">1</Select.Option>
            <Select.Option value="2">2</Select.Option>
          </Select>
        </Variant>
        <Variant name="Multiple">
          <Select defaultValue={["1"]} mode="multiple" allowClear>
            <Select.Option value="1">1</Select.Option>
            <Select.Option value="2">2</Select.Option>
          </Select>
        </Variant>
      </Component>

      <Component name="Link">
        <Variant>
          <Typography.Link href="" target="_blank"></Typography.Link>
        </Variant>
      </Component>

      <Component name="Slider">
        <Variant>
          <Slider defaultValue={30} />
        </Variant>
        <Variant name="Range">
          <Slider range defaultValue={[20, 50]} />
        </Variant>
      </Component>
    </Category>
    <Category name="Data Display">
      <Component name="Field">
        <Variant>
          <Field
            entityName={ENTITY_NAME}
            disabled={readOnlyMode}
            propertyName=""
            formItemProps={{
              style: { marginBottom: "12px" }
            }}
          />
        </Variant>
      </Component>
      <Component name="Card">
        <Variant>
          <Card />
        </Variant>
        <Variant name="With Title">
          <Card>
            <Card title="Card title">
              <p>Card content</p>
            </Card>
          </Card>
        </Variant>
        <Variant name="My custom card">
          <Card>
            <Card title="Card title">
              <p>Card content</p>
              <Avatar />
            </Card>
          </Card>
        </Variant>
      </Component>
      <Component name="Tabs">
        <Variant>
          <Tabs defaultActiveKey="1">
            <Tabs.TabPane tab="Tab 1" key="1">
              Content of Tab Pane 1
            </Tabs.TabPane>
            <Tabs.TabPane tab="Tab 2" key="2">
              Content of Tab Pane 2
            </Tabs.TabPane>
            <Tabs.TabPane tab="Tab 3" key="3">
              Content of Tab Pane 3
            </Tabs.TabPane>
          </Tabs>
        </Variant>
        <Variant name="Tab Pane">
          <Tabs.TabPane></Tabs.TabPane>
        </Variant>
      </Component>
      <Component name="Collapse">
        <Variant>
          <Collapse defaultActiveKey="1">
            <Collapse.Panel
              header="This is panel header 1"
              key="1"
            ></Collapse.Panel>
            <Collapse.Panel
              header="This is panel header 2"
              key="2"
            ></Collapse.Panel>
            <Collapse.Panel
              header="This is panel header 3"
              key="3"
            ></Collapse.Panel>
          </Collapse>
        </Variant>
      </Component>
      <Component name="Image">
        <Variant>
          <Image width={200} src="" />
        </Variant>
      </Component>
      <Component name="Avatar">
        <Variant>
          <Avatar icon={<UserOutlined />} />
        </Variant>
        <Variant name="Image">
          <Avatar src="https://joeschmoe.io/api/v1/random" />
        </Variant>
      </Component>
      <Component name="Badge">
        <Variant>
          <Badge count={1}></Badge>
        </Variant>
      </Component>
      <Component name="Statistic">
        <Variant>
          <Statistic title="Title" value={112893} />
        </Variant>
      </Component>
      <Component name="Alert">
        <Variant name="Success">
          <Alert message="Text" type="success" />
        </Variant>
        <Variant name="Info">
          <Alert message="Text" type="info" />
        </Variant>
        <Variant name="Warning">
          <Alert message="Text" type="warning" />
        </Variant>
        <Variant name="Error">
          <Alert message="Text" type="error" />
        </Variant>
      </Component>
      <Component name="List">
        <Variant>
          <List
            bordered
            dataSource={[]}
            renderItem={item => <List.Item></List.Item>}
          />
        </Variant>
      </Component>
    </Category>
    <Category name="Icons">
      <Component name="Arrow">
        <Variant name="Up">
          <ArrowUpOutlined />
        </Variant>
        <Variant name="Down">
          <ArrowDownOutlined />
        </Variant>
        <Variant name="Left">
          <ArrowLeftOutlined />
        </Variant>
        <Variant name="Right">
          <ArrowRightOutlined />
        </Variant>
      </Component>
      <Component name="Question">
        <Variant>
          <QuestionOutlined />
        </Variant>
        <Variant name="Circle">
          <QuestionCircleOutlined />
        </Variant>
      </Component>
      <Component name="Plus">
        <Variant>
          <PlusOutlined />
        </Variant>
        <Variant name="Circle">
          <PlusCircleOutlined />
        </Variant>
      </Component>
      <Component name="Info">
        <Variant>
          <InfoOutlined />
        </Variant>
        <Variant name="Circle">
          <InfoCircleOutlined />
        </Variant>
      </Component>
      <Component name="Exclamation">
        <Variant>
          <ExclamationOutlined />
        </Variant>
        <Variant name="Circle">
          <ExclamationCircleOutlined />
        </Variant>
      </Component>
      <Component name="Close">
        <Variant>
          <CloseOutlined />
        </Variant>
        <Variant name="Circle">
          <CloseCircleOutlined />
        </Variant>
      </Component>
      <Component name="Check">
        <Variant>
          <CheckOutlined />
        </Variant>
        <Variant name="Circle">
          <CheckCircleOutlined />
        </Variant>
      </Component>
      <Component name="Edit">
        <Variant>
          <EditOutlined />
        </Variant>
      </Component>
      <Component name="Copy">
        <Variant>
          <CopyOutlined />
        </Variant>
      </Component>
      <Component name="Delete">
        <Variant>
          <DeleteOutlined />
        </Variant>
      </Component>
      <Component name="Bars">
        <Variant>
          <BarsOutlined />
        </Variant>
      </Component>
      <Component name="Bell">
        <Variant>
          <BellOutlined />
        </Variant>
      </Component>
      <Component name="Clear">
        <Variant>
          <ClearOutlined />
        </Variant>
      </Component>
      <Component name="Download">
        <Variant>
          <DownloadOutlined />
        </Variant>
      </Component>
      <Component name="Upload">
        <Variant>
          <UploadOutlined />
        </Variant>
      </Component>
      <Component name="Sync">
        <Variant>
          <SyncOutlined />
        </Variant>
      </Component>
      <Component name="Save">
        <Variant>
          <SaveOutlined />
        </Variant>
      </Component>
      <Component name="Search">
        <Variant>
          <SearchOutlined />
        </Variant>
      </Component>
      <Component name="Settings">
        <Variant>
          <SettingOutlined />
        </Variant>
      </Component>
      <Component name="Paperclip">
        <Variant>
          <PaperClipOutlined />
        </Variant>
      </Component>
      <Component name="Phone">
        <Variant>
          <PhoneOutlined />
        </Variant>
      </Component>
      <Component name="Mail">
        <Variant>
          <MailOutlined />
        </Variant>
      </Component>
      <Component name="Home">
        <Variant>
          <HomeOutlined />
        </Variant>
      </Component>
      <Component name="Contacts">
        <Variant>
          <ContactsOutlined />
        </Variant>
      </Component>
      <Component name="User">
        <Variant>
          <UserOutlined />
        </Variant>
        <Variant name="Add">
          <UserAddOutlined />
        </Variant>
        <Variant name="Remove">
          <UserDeleteOutlined />
        </Variant>
      </Component>
      <Component name="Team">
        <Variant>
          <TeamOutlined />
        </Variant>
      </Component>
    </Category>
    <Category name="Screens">
      <Component name="ExampleCustomScreen">
        <Variant>
          <ExampleCustomScreen />
        </Variant>
      </Component>
      <Component name="CustomEntityFilterTest">
        <Variant>
          <CustomEntityFilterTest />
        </Variant>
      </Component>
      <Component name="CustomFormControls">
        <Variant>
          <CustomFormControls />
        </Variant>
      </Component>
      <Component name="CustomDataDisplayComponents">
        <Variant>
          <CustomDataDisplayComponents />
        </Variant>
      </Component>
      <Component name="CustomAppLayouts">
        <Variant>
          <CustomAppLayouts />
        </Variant>
      </Component>
      <Component name="CustomControls">
        <Variant>
          <CustomControls />
        </Variant>
      </Component>
      <Component name="ErrorBoundaryTests">
        <Variant>
          <ErrorBoundaryTests />
        </Variant>
      </Component>
      <Component name="TestBlankScreen">
        <Variant>
          <TestBlankScreen />
        </Variant>
      </Component>
      <Component name="CarEditor">
        <Variant>
          <CarEditor />
        </Variant>
      </Component>
      <Component name="CarBrowserCards">
        <Variant>
          <CarBrowserCards />
        </Variant>
      </Component>
      <Component name="CarBrowserList">
        <Variant>
          <CarBrowserList />
        </Variant>
      </Component>
      <Component name="CarBrowserTable">
        <Variant>
          <CarBrowserTable />
        </Variant>
      </Component>
      <Component name="CarCardsGrid">
        <Variant>
          <CarCardsGrid />
        </Variant>
      </Component>
      <Component name="FavoriteCars">
        <Variant>
          <FavoriteCars />
        </Variant>
      </Component>
      <Component name="CarCardsWithDetails">
        <Variant>
          <CarCardsWithDetails />
        </Variant>
      </Component>
      <Component name="CarTableWithFilters">
        <Variant>
          <CarTableWithFilters />
        </Variant>
      </Component>
      <Component name="CarMasterDetail">
        <Variant>
          <CarMasterDetail />
        </Variant>
      </Component>
      <Component name="FormWizardCompositionO2O">
        <Variant>
          <FormWizardCompositionO2O />
        </Variant>
      </Component>
      <Component name="FormWizardEditor">
        <Variant>
          <FormWizardEditor />
        </Variant>
      </Component>
      <Component name="FormWizardBrowserTable">
        <Variant>
          <FormWizardBrowserTable />
        </Variant>
      </Component>
      <Component name="CarMultiSelectionTable">
        <Variant>
          <CarMultiSelectionTable />
        </Variant>
      </Component>
      <Component name="DatatypesTestEditor">
        <Variant>
          <DatatypesTestEditor />
        </Variant>
      </Component>
      <Component name="DatatypesTestBrowserCards">
        <Variant>
          <DatatypesTestBrowserCards />
        </Variant>
      </Component>
      <Component name="DatatypesTestBrowserList">
        <Variant>
          <DatatypesTestBrowserList />
        </Variant>
      </Component>
      <Component name="DatatypesTestBrowserTable">
        <Variant>
          <DatatypesTestBrowserTable />
        </Variant>
      </Component>
      <Component name="DatatypesTestCards">
        <Variant>
          <DatatypesTestCards />
        </Variant>
      </Component>
      <Component name="AssociationO2OEditor">
        <Variant>
          <AssociationO2OEditor />
        </Variant>
      </Component>
      <Component name="AssociationO2OBrowserTable">
        <Variant>
          <AssociationO2OBrowserTable />
        </Variant>
      </Component>
      <Component name="AssociationO2MEditor">
        <Variant>
          <AssociationO2MEditor />
        </Variant>
      </Component>
      <Component name="AssociationO2MBrowserTable">
        <Variant>
          <AssociationO2MBrowserTable />
        </Variant>
      </Component>
      <Component name="AssociationM2OEditor">
        <Variant>
          <AssociationM2OEditor />
        </Variant>
      </Component>
      <Component name="AssociationM2OBrowserTable">
        <Variant>
          <AssociationM2OBrowserTable />
        </Variant>
      </Component>
      <Component name="AssociationM2MEditor">
        <Variant>
          <AssociationM2MEditor />
        </Variant>
      </Component>
      <Component name="AssociationM2MBrowserTable">
        <Variant>
          <AssociationM2MBrowserTable />
        </Variant>
      </Component>
      <Component name="CompositionO2OEditor">
        <Variant>
          <CompositionO2OEditor />
        </Variant>
      </Component>
      <Component name="CompositionO2OBrowserTable">
        <Variant>
          <CompositionO2OBrowserTable />
        </Variant>
      </Component>
      <Component name="CompositionO2MEditor">
        <Variant>
          <CompositionO2MEditor />
        </Variant>
      </Component>
      <Component name="CompositionO2MBrowserTable">
        <Variant>
          <CompositionO2MBrowserTable />
        </Variant>
      </Component>
      <Component name="DeeplyNestedTestEntityEditor">
        <Variant>
          <DeeplyNestedTestEntityEditor />
        </Variant>
      </Component>
      <Component name="DeeplyNestedO2MTestEntityTable">
        <Variant>
          <DeeplyNestedO2MTestEntityTable />
        </Variant>
      </Component>
      <Component name="DeeplyNestedO2MTestEntityEditor">
        <Variant>
          <DeeplyNestedO2MTestEntityEditor />
        </Variant>
      </Component>
      <Component name="IntIdEditor">
        <Variant>
          <IntIdEditor />
        </Variant>
      </Component>
      <Component name="IntIdBrowserTable">
        <Variant>
          <IntIdBrowserTable />
        </Variant>
      </Component>
      <Component name="IntIdBrowserCards">
        <Variant>
          <IntIdBrowserCards />
        </Variant>
      </Component>
      <Component name="IntIdBrowserList">
        <Variant>
          <IntIdBrowserList />
        </Variant>
      </Component>
      <Component name="IntIdentityIdCards">
        <Variant>
          <IntIdentityIdCards />
        </Variant>
      </Component>
      <Component name="IntIdentityIdEditor">
        <Variant>
          <IntIdentityIdEditor />
        </Variant>
      </Component>
      <Component name="IntIdentityIdBrowserTable">
        <Variant>
          <IntIdentityIdBrowserTable />
        </Variant>
      </Component>
      <Component name="IntIdentityIdBrowserCards">
        <Variant>
          <IntIdentityIdBrowserCards />
        </Variant>
      </Component>
      <Component name="IntIdentityIdBrowserList">
        <Variant>
          <IntIdentityIdBrowserList />
        </Variant>
      </Component>
      <Component name="StringIdCards">
        <Variant>
          <StringIdCards />
        </Variant>
      </Component>
      <Component name="StringIdMgtCardsEdit">
        <Variant>
          <StringIdMgtCardsEdit />
        </Variant>
      </Component>
      <Component name="StringIdBrowserCards">
        <Variant>
          <StringIdBrowserCards />
        </Variant>
      </Component>
      <Component name="StringIdBrowserList">
        <Variant>
          <StringIdBrowserList />
        </Variant>
      </Component>
      <Component name="StringIdBrowserTable">
        <Variant>
          <StringIdBrowserTable />
        </Variant>
      </Component>
      <Component name="WeirdStringIdEditor">
        <Variant>
          <WeirdStringIdEditor />
        </Variant>
      </Component>
      <Component name="WeirdStringIdBrowserCards">
        <Variant>
          <WeirdStringIdBrowserCards />
        </Variant>
      </Component>
      <Component name="WeirdStringIdBrowserList">
        <Variant>
          <WeirdStringIdBrowserList />
        </Variant>
      </Component>
      <Component name="WeirdStringIdBrowserTable">
        <Variant>
          <WeirdStringIdBrowserTable />
        </Variant>
      </Component>
      <Component name="BoringStringIdEditor">
        <Variant>
          <BoringStringIdEditor />
        </Variant>
      </Component>
      <Component name="BoringStringIdBrowserTable">
        <Variant>
          <BoringStringIdBrowserTable />
        </Variant>
      </Component>
      <Component name="TrickyIdEditor">
        <Variant>
          <TrickyIdEditor />
        </Variant>
      </Component>
      <Component name="TrickyIdBrowserTable">
        <Variant>
          <TrickyIdBrowserTable />
        </Variant>
      </Component>
    </Category>
  </Palette>
)
Example #18
Source File: index.tsx    From Aragorn with MIT License 4 votes vote down vote up
FileManage = () => {
  const {
    state: {
      uploaderProfiles,
      configuration: { defaultUploaderProfileId }
    }
  } = useAppContext();

  const [windowHeight, setWindowHeight] = useState(window.innerHeight);

  useEffect(() => {
    function handleResize() {
      setWindowHeight(window.innerHeight);
    }
    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []);

  const { id } = useParams<{ id: string }>();

  const [hasFileManageFeature, setHasFileManageFeature] = useState(false);

  const [uploaderProfile, setUploaderProfile] = useState({} as UploaderProfile);

  useEffect(() => {
    const currentId = id || defaultUploaderProfileId;
    setCurrentProfile(currentId as string);
  }, []);

  useEffect(() => {
    if (uploaderProfile?.id) {
      getList();
    }
  }, [uploaderProfile]);

  const [list, setList] = useState([] as ListFile[]);
  const [listLoading, setListLoading] = useState(false);

  const getList = (directoryPath?: string) => {
    setListLoading(true);
    ipcRenderer.send('file-list-get', uploaderProfile.id, directoryPath);
  };

  const [dirPath, setDirPath] = useState([] as string[]);

  useEffect(() => {
    function handleListGetReply(_, res?: FileListResponse) {
      setListLoading(false);
      if (res === undefined) {
        setHasFileManageFeature(false);
        setList([]);
        message.info(`${uploaderProfile.uploaderName}暂不支持文件管理功能`);
        return;
      }
      setHasFileManageFeature(true);
      if (res.success) {
        setList(res.data);
      } else {
        message.error(`文件列表获取失败 ${res.desc || ''}`);
      }
    }

    function handleFileDeleteReply(_, res?: DeleteFileResponse) {
      if (res === undefined) {
        return;
      }
      if (res.success) {
        message.success({ content: '文件删除成功', key: 'file-manage-delete' });
        getList(dirPath.join('/'));
      } else {
        message.error({ content: `文件删除失败 ${res.desc || ''}`, key: 'file-manage-delete' });
      }
    }

    function handleFileUploadReply() {
      getList(dirPath.join('/'));
    }

    function handleDirectoryCreateReply(_, res?: CreateDirectoryResponse) {
      if (res === undefined) {
        return;
      }
      if (res.success) {
        message.success('目录创建成功');
        setModalVisible(false);
        getList(dirPath.join('/'));
      } else {
        message.error(`目录创建失败 ${res.desc || ''}`);
      }
    }

    function handleExportReplay(_, res) {
      setExportLoading(false);
      if (res) {
        shell.showItemInFolder(res);
        setRowKeys([]);
        setSelectRows([]);
      }
    }

    ipcRenderer.on('file-list-get-reply', handleListGetReply);
    ipcRenderer.on('file-delete-reply', handleFileDeleteReply);
    ipcRenderer.on('file-upload-reply', handleFileUploadReply);
    ipcRenderer.on('directory-create-reply', handleDirectoryCreateReply);
    ipcRenderer.on('export-reply', handleExportReplay);

    return () => {
      ipcRenderer.removeListener('file-list-get-reply', handleListGetReply);
      ipcRenderer.removeListener('file-delete-reply', handleFileDeleteReply);
      ipcRenderer.removeListener('file-upload-reply', handleFileUploadReply);
      ipcRenderer.removeListener('directory-create-reply', handleDirectoryCreateReply);
      ipcRenderer.removeListener('export-reply', handleExportReplay);
    };
  }, [uploaderProfile, dirPath]);

  const handleNameClick = (record: ListFile) => {
    if (record.type === 'directory') {
      const newPath = [...dirPath, formatFileName(record.name)];
      setDirPath(newPath);
      getList(newPath.join('/'));
    } else {
      clipboard.writeText(record.url as string);
      message.success('链接已复制到粘贴板');
    }
  };

  const handlePathClick = (index: number) => {
    if (index === -1) {
      setDirPath([]);
      getList();
    } else {
      const newPath = dirPath.slice(0, index + 1);
      setDirPath(newPath);
      getList(newPath.join('/'));
    }
  };

  const setCurrentProfile = (uploaderProfileId: string) => {
    setDirPath([]);
    const uploaderProfile = uploaderProfiles.find(item => item.id === uploaderProfileId);
    setUploaderProfile(uploaderProfile as UploaderProfile);
  };

  const formatFileName = (name: string) => {
    if (dirPath.length > 0) {
      const pathPrefix = dirPath.join('/') + '/';
      return name.split(pathPrefix).pop() || '';
    } else {
      return name;
    }
  };

  const [selectRowKeys, setRowKeys] = useState([] as string[]);
  const [selectRows, setSelectRows] = useState([] as ListFile[]);

  const handleTableRowChange = (selectedRowKeys, selectedRows: ListFile[]) => {
    setRowKeys(selectedRowKeys);
    setSelectRows(selectedRows);
  };

  const handleRefresh = () => {
    getList(dirPath.join('/'));
  };

  const handleBatchDelete = () => {
    Modal.confirm({
      title: '确认删除',
      onOk: () => {
        const names = selectRows.map(item => [...dirPath, formatFileName(item.name)].join('/'));
        message.info({ content: '正在删除,请稍后...', key: 'file-manage-delete' });
        ipcRenderer.send('file-delete', uploaderProfile.id, names);
      }
    });
  };

  const handleDelete = (record: ListFile) => {
    let name = record.name;
    Modal.confirm({
      title: '确认删除',
      content: name,
      onOk: () => {
        let name = record.name;
        if (record.type === 'directory') {
          name = `${[...dirPath, record.name].join('/')}/`;
        } else {
          name = [...dirPath, formatFileName(record.name)].join('/');
        }
        message.info({ content: '正在删除,请稍后...', key: 'file-manage-delete' });
        ipcRenderer.send('file-delete', uploaderProfile.id, [name]);
      }
    });
  };

  const uploadRef = useRef<HTMLInputElement>(null);

  const handleFileUpload = (event: React.FormEvent<HTMLInputElement>) => {
    const fileList = event.currentTarget.files || [];
    const filesPath = Array.from(fileList).map(file => file.path);
    const pathPrefix = dirPath.join('/');
    ipcRenderer.send('file-upload', uploaderProfile.id, filesPath, pathPrefix);
    event.currentTarget.value = '';
  };

  const [modalVisible, setModalVisible] = useState(false);

  const [form] = Form.useForm();

  const handleCreateDirectory = () => {
    form.validateFields().then(values => {
      ipcRenderer.send('directory-create', uploaderProfile.id, values?.directoryPath || '');
    });
  };

  const handleDownload = (record: ListFile) => {
    ipcRenderer.send('file-download', record.name, record.url);
  };

  const [exportLoading, setExportLoading] = useState(false);

  const handleExport = () => {
    const data = selectRows.map(item => {
      const fileNameArr = item.name.split('.');
      fileNameArr.pop();
      return {
        name: fileNameArr.join('.'),
        url: item.url
      };
    });
    setExportLoading(true);
    ipcRenderer.send('export', data);
  };

  const columns: ColumnsType<ListFile> = [
    {
      title: '文件名',
      dataIndex: 'name',
      ellipsis: true,
      render: (val: string, record: ListFile) => (
        <div style={{ display: 'flex', alignItems: 'center' }}>
          {record.type === 'directory' ? (
            <FolderFilled style={{ fontSize: 16 }} />
          ) : (
            <FileOutlined style={{ fontSize: 16 }} />
          )}
          {record.type === 'directory' ? (
            <a
              title={val}
              onClick={() => handleNameClick(record)}
              className="table-filename"
              style={{ marginLeft: 10, overflow: 'hidden', textOverflow: 'ellipsis' }}
            >
              {formatFileName(val)}
            </a>
          ) : (
            <Popover
              placement="topLeft"
              content={() =>
                /(jpg|png|gif|jpeg)$/.test(val) ? (
                  <Image
                    style={{ maxWidth: 500 }}
                    src={record.url}
                    fallback="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg=="
                  />
                ) : (
                  val
                )
              }
              trigger="hover"
            >
              <a
                title={val}
                onClick={() => handleNameClick(record)}
                className="table-filename"
                style={{ marginLeft: 10, overflow: 'hidden', textOverflow: 'ellipsis' }}
              >
                {formatFileName(val)}
              </a>
            </Popover>
          )}
        </div>
      )
    },
    {
      title: '文件大小',
      dataIndex: 'size',
      ellipsis: true,
      width: 120,
      render: val => (val ? filesize(val) : '-')
    },
    {
      title: '更新时间',
      dataIndex: 'lastModified',
      ellipsis: true,
      width: 200,
      render: val => (val ? dayjs(val).format('YYYY-MM-DD HH:mm:ss') : '-')
    },
    {
      title: '操作',
      width: 120,
      render: (_, record) => (
        <Space>
          {record.type !== 'directory' && (
            <>
              <DownloadOutlined onClick={() => handleDownload(record)} />
              <CopyOutlined onClick={() => handleNameClick(record)} />
            </>
          )}
          <DeleteOutlined onClick={() => handleDelete(record)} />
        </Space>
      )
    }
  ];

  return (
    <div className="storage-page">
      <header>
        <span>文件管理</span>
        <Divider />
      </header>
      <Space style={{ marginBottom: 10 }}>
        <Select style={{ minWidth: 120 }} value={uploaderProfile?.id} onChange={setCurrentProfile}>
          {uploaderProfiles.map(item => (
            <Select.Option key={item.name} value={item.id}>
              {item.name}
            </Select.Option>
          ))}
        </Select>
        <Button
          title="上传"
          icon={<UploadOutlined />}
          disabled={!hasFileManageFeature}
          type="primary"
          onClick={() => {
            uploadRef.current?.click();
          }}
        />
        <Button title="刷新" icon={<ReloadOutlined />} disabled={!hasFileManageFeature} onClick={handleRefresh} />
        <Button
          title="创建文件夹"
          icon={<FolderAddOutlined />}
          disabled={!hasFileManageFeature}
          onClick={() => {
            setModalVisible(true);
          }}
        />
        <Button
          title="导出"
          icon={<ExportOutlined />}
          disabled={selectRows.length === 0}
          onClick={handleExport}
          loading={exportLoading}
        />
        <Button title="删除" icon={<DeleteOutlined />} disabled={selectRows.length === 0} onClick={handleBatchDelete} />
      </Space>
      <Breadcrumb style={{ marginBottom: 10 }}>
        <Breadcrumb.Item>
          <a onClick={() => handlePathClick(-1)}>全部文件</a>
        </Breadcrumb.Item>
        {dirPath.map((item, index) => (
          <Breadcrumb.Item key={item}>
            <a onClick={() => handlePathClick(index)}>{item}</a>
          </Breadcrumb.Item>
        ))}
      </Breadcrumb>
      <div className="table-wrapper">
        <Table
          size="small"
          rowKey="name"
          scroll={{ y: windowHeight - 270 }}
          dataSource={list}
          columns={columns}
          pagination={{
            size: 'small',
            defaultPageSize: 100,
            pageSizeOptions: ['50', '100', '200'],
            hideOnSinglePage: true
          }}
          loading={listLoading}
          rowSelection={{
            onChange: handleTableRowChange,
            selectedRowKeys: selectRowKeys,
            getCheckboxProps: record => ({ disabled: record?.type === 'directory' })
          }}
        />
      </div>
      <input ref={uploadRef} type="file" multiple hidden onChange={handleFileUpload} />
      <Modal
        title="创建目录"
        visible={modalVisible}
        onCancel={() => setModalVisible(false)}
        onOk={handleCreateDirectory}
        destroyOnClose={true}
      >
        <Form form={form} preserve={false}>
          <Form.Item
            label="目录名称"
            name="directoryPath"
            rules={[{ required: true }, { pattern: domainPathRegExp, message: '目录名不能以 / 开头或结尾' }]}
          >
            <Input autoFocus />
          </Form.Item>
        </Form>
      </Modal>
    </div>
  );
}
Example #19
Source File: index.tsx    From fe-v5 with Apache License 2.0 4 votes vote down vote up
Indicator: React.FC = () => {
  const { t } = useTranslation();
  // 数据定义
  const [form] = Form.useForm();
  const tableRef = useRef(null as any);
  const exportTextRef = useRef(null as any);
  const [modal, setModal] = useState<boolean>(false);
  const [modalType, setModalType] = useState('create');
  const [exportList, setExportList] = useState([] as string[]);
  const [editingKey, setEditingKey] = useState<Partial<InterfaceItem>>({});
  const [query, setQuery] = useState<string>('');
  const history = useHistory();
  const moreOperations: MoreOptions = {
    导入指标: 'import',
    导出指标: 'export',
  };
  const optMap = {
    import: t('导入'),
    edit: t('编辑'),
    export: t('导出'),
    create: t('创建'),
  }; // 弹窗操作

  const handleOpen = (type: string = 'create') => {
    setModalType(type);
    setModal(true);
  };

  const handleCancel = () => {
    setModal(false);
  }; // 接口操作

  const handleEdit = (item: InterfaceItem) => {
    setEditingKey(item);
  };

  const handleDelete = (item) => {
    deleteIndicator([item.id]).then(async (res) => {
      await tableRef.current.refreshList();
      message.success(t('删除指标成功'));
    });
  };

  const handleAdd = async () => {
    try {
      const values = await form.validateFields();
      addIndicator(values.metrics || '').then(async (res) => {
        if (res && res.success) {
          await tableRef.current.refreshList();
          setModal(false);
          message.success(t('创建指标成功'));
        }
      });
    } catch (e) {}
  }; // 更多操作

  const [oType, setOType] = useState<string>(t('更多操作'));

  const onSelect = (val: string): void => {
    switch (val) {
      case 'import':
        setModal(true);
        setModalType('import');
        break;

      case 'export':
        if (exportList.length <= 0) {
          message.warning(t('请勾选需要导出的指标'));
        } else {
          setModal(true);
          setModalType('export');
          setTimeout(() => {
            exportTextRef.current && exportTextRef.current.focus();
          });
        }

        break;

      default:
        break;
    }

    setOType(val);
  }; // table

  const handleEditChange = (e, record, index, field) => {
    let targetCol = Object.assign({}, record);
    targetCol[field] = e.target.value;
    setEditingKey(targetCol);
  };

  const renderInput = (text, record, index, field) => {
    if (record.id === editingKey.id) {
      return (
        <Input
          defaultValue={text}
          onPressEnter={() => handleEditOption('save')}
          onChange={(e) => handleEditChange(e, record, index, field)}
        />
      );
    } else {
      return <span>{text}</span>;
    }
  };

  const handleEditOption = async (type = '') => {
    if (type === 'save') {
      let id = editingKey.id || -1;
      await editIndicator(id, {
        description: editingKey.description,
        metric: editingKey.metric,
      });
      message.success(t('编辑成功'));
      tableRef.current.refreshList();
    }

    setEditingKey({} as any);
  };

  const columns = [
    {
      title: t('指标名称'),
      dataIndex: 'metric',
      key: 'metric',
      render: (text: string, record: InterfaceItem) => {
        return (
          <a
            onClick={() => {
              let path = {
                pathname: `/metric/explorer`,
                state: { name: text, description: record.description },
              };
              history.push(path);
            }}
          >
            {text}
          </a>
        );
      },
    },
    {
      title: t('释义'),
      dataIndex: 'description',
      width: '40%',
      key: 'description',
      render: (text: string, record: InterfaceItem, index: number) => {
        return renderInput(text, record, index, 'description');
      },
    },
    {
      title: t('操作'),
      dataIndex: '',
      key: 'operations',
      width: '15%',
      render: (_: any, item: any) => {
        return (
          <div className='operations'>
            {item.id !== editingKey.id ? (
              <div>
                <span
                  className='operation-item edit'
                  onClick={() => handleEdit(item)}
                >
                  {t('编辑')}
                </span>
                {/* <Popconfirm
             title='确定删除该指标?'
             onConfirm={() => handleDelete(item)}
             okText='确定'
             cancelText='取消'
            >
             <span className='operation-item delete'>删除</span>
            </Popconfirm> */}
                <div
                  className='table-operator-area-warning'
                  style={{
                    display: 'inline-block',
                    marginLeft: 10,
                  }}
                  onClick={() => {
                    confirm({
                      title: t('确定删除该指标?'),
                      icon: <ExclamationCircleOutlined />,
                      onOk: () => {
                        handleDelete(item);
                      },
                    });
                  }}
                >
                  {t('删除')}
                </div>
              </div>
            ) : (
              <div>
                <span
                  className='operation-item edit-save'
                  onClick={() => handleEditOption('save')}
                >
                  {t('保存')}
                </span>
                <span
                  className='operation-item edit-cancel'
                  onClick={() => handleEditOption('cancel')}
                >
                  {t('取消')}
                </span>
              </div>
            )}
          </div>
        );
      },
    },
  ];

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

  const rowSelection = {
    onChange: (selectedRowKeys, selectedRows) => {
      let list: string[] = [];
      selectedRows.map((item) => {
        list.push(`${item.metric}:${item.description}`);
      });
      setExportList(list);
    },
  };

  const handleExportTxt = () => {
    download(exportList);
  };

  return (
    <PageLayout title={t('指标释义')} icon={<BulbOutlined />}>
      <div className='indicator-index'>
        <div className='indicator-operations'>
          <Input
            className={'searchInput'}
            onPressEnter={onSearchQuery}
            prefix={<SearchOutlined />}
            placeholder={t('指标名称或释义')}
          />
          <div className='operations'>
            <Button.Group>
              <Button
                size='middle'
                type='default'
                icon={<DownloadOutlined />}
                onClick={() => onSelect('import')}
              >
                {t('导入')}
              </Button>
              <Button
                size='middle'
                type='default'
                icon={<UploadOutlined />}
                onClick={() => onSelect('export')}
              >
                {t('导出')}
              </Button>
            </Button.Group>
          </div>
        </div>
        <div className='indicator-main'>
          <BaseTable
            fetchHandle={getIndicatorList}
            ref={tableRef}
            fetchParams={{
              query: query,
            }}
            rowSelection={{ ...rowSelection }}
            rowKey={(record: { id: number }) => {
              return record.id;
            }}
            columns={columns}
          />
        </div>
      </div>
      <Modal
        title={optMap[modalType]}
        destroyOnClose={true}
        footer={
          modalType !== 'export' && [
            <Button key='delete' onClick={handleCancel}>
              {t('取消')}
            </Button>,
            <Button key='submit' type='primary' onClick={handleAdd}>
              {t('确定')}
            </Button>,
          ]
        }
        onCancel={handleCancel}
        visible={modal}
      >
        <p
          style={{
            color: '#999',
          }}
        >
          {modalType === 'export' ? (
            <a onClick={handleExportTxt}>Download.txt</a>
          ) : (
            <span>{t('一行一个')}</span>
          )}
        </p>
        {(() => {
          switch (modalType) {
            case 'export':
              return (
                <div
                  contentEditable='true'
                  suppressContentEditableWarning={true}
                  ref={exportTextRef}
                  style={{
                    height: '200px',
                    border: '1px solid #d9d9d9',
                    overflow: 'auto',
                    padding: '10px',
                  }}
                >
                  {exportList.map((item, index) => {
                    return <div key={index}>{item}</div>;
                  })}
                </div>
              );
              break;

            case 'import':
            case 'create':
              return (
                <Form name={modalType + '-dialog'} form={form} preserve={false}>
                  <Form.Item
                    name={'metrics'}
                    key={'metrics'}
                    rules={[
                      {
                        required: true,
                        message: t('不能为空'),
                        validateTrigger: 'trigger',
                      },
                    ]}
                  >
                    <TextArea
                      placeholder={'name:description'}
                      rows={4}
                    ></TextArea>
                  </Form.Item>
                </Form>
              );
              break;
          }
        })()}
      </Modal>
    </PageLayout>
  );
}
Example #20
Source File: CodeDebugger.tsx    From jitsu with MIT License 4 votes vote down vote up
ControlsComponent: React.FC<ControlsProps> = ({
  inputChecked,
  codeChecked,
  outputChecked,
  codeSaved,
  toggleInput,
  toggleCode,
  toggleOutput,
  handleExit: handleCloseWithoutSaving,
  handleSave,
  handleRun,
}) => {
  const [isClosePopoverVisible, setIsClosePopoverVisible] = useState(false)

  const handleClose = () => {
    if (!codeSaved.current) {
      setIsClosePopoverVisible(true)
      return
    }
    handleCloseWithoutSaving()
  }

  useEffect(() => {
    const handleToggleInput = () => {
      toggleInput()
      return false // to prevent browsers' default behaviour
    }
    const handleToggleCode = () => {
      toggleCode()
      return false
    }
    const handleToggleOutput = () => {
      toggleOutput()
      return false
    }
    const _handleSave = (e: KeyboardEvent) => {
      e.preventDefault()
      handleSave()
      return false
    }
    const _handleRun = (e: KeyboardEvent) => {
      e.stopPropagation()
      handleRun()
      return false
    }
    const handleEscape = e => {
      if (e.key === "Escape") {
        handleClose()
      }
    }

    hotkeys.filter = () => true // to enable hotkeys everywhere, even in input fields

    hotkeys("cmd+i,ctrl+i", handleToggleInput)
    hotkeys("cmd+u,ctrl+u", handleToggleCode)
    hotkeys("cmd+o,ctrl+o", handleToggleOutput)
    hotkeys("cmd+s,ctrl+s", _handleSave)
    hotkeys("cmd+enter,ctrl+enter", _handleRun)
    document.addEventListener("keydown", handleEscape, true)

    return () => {
      hotkeys.unbind("cmd+i,ctrl+i", handleToggleInput)
      hotkeys.unbind("cmd+u,ctrl+u", handleToggleCode)
      hotkeys.unbind("cmd+o,ctrl+o", handleToggleOutput)
      hotkeys.unbind("cmd+s,ctrl+s", _handleSave)
      hotkeys.unbind("cmd+enter,ctrl+enter", _handleRun)
      document.removeEventListener("keydown", handleEscape, true)
    }
  }, [])

  return (
    <div className="flex items-stretch w-full h-full">
      <Popconfirm
        title="You have some unsaved expression code. Do you want to quit?"
        placement="rightBottom"
        className="max-w-xs mr-4"
        visible={isClosePopoverVisible}
        onCancel={() => setIsClosePopoverVisible(false)}
        onConfirm={() => {
          handleCloseWithoutSaving()
          setIsClosePopoverVisible(false)
        }}
      >
        <Button size="middle" className="flex-grow-0" onClick={handleClose}>
          <CloseOutlined className={styles.adaptiveIcon} />
          <span className={`${styles.adaptiveLabel} ${styles.noMargins}`}>{"Close"}</span>
        </Button>
      </Popconfirm>
      <div className="flex justify-center items-center flex-auto min-w-0">
        <Tooltip title={`${OS_CMD_CTRL_KEY}+I`} mouseEnterDelay={1}>
          <Checkbox
            checked={inputChecked}
            className={cn("relative", styles.checkbox, styles.hideAntdCheckbox, styles.checkboxLabel, {
              [styles.checkboxChecked]: inputChecked,
            })}
            onClick={toggleInput}
          >
            <i className="block absolute left-0.5">{inputChecked ? <EyeFilled /> : <EyeInvisibleFilled />}</i>
            <span className={styles.adaptiveIcon}>{"{ }"}</span>
            <span className={`${styles.adaptiveLabel} ${styles.noMargins}`}>{"Input"}</span>
          </Checkbox>
        </Tooltip>
        <Tooltip title={`${OS_CMD_CTRL_KEY}+U`} mouseEnterDelay={1}>
          <Checkbox
            checked={codeChecked}
            className={cn("relative", styles.checkbox, styles.hideAntdCheckbox, styles.checkboxLabel, {
              [styles.checkboxChecked]: codeChecked,
            })}
            onClick={toggleCode}
          >
            <i className="block absolute left-0.5">{codeChecked ? <EyeFilled /> : <EyeInvisibleFilled />}</i>
            <span className={styles.adaptiveIcon}>{"</>"}</span>
            <span className={`${styles.adaptiveLabel} ${styles.noMargins}`}>{"Expression"}</span>
          </Checkbox>
        </Tooltip>
        <Tooltip title={`${OS_CMD_CTRL_KEY}+O`} mouseEnterDelay={1}>
          <Checkbox
            checked={outputChecked}
            className={cn("relative", styles.checkbox, styles.hideAntdCheckbox, styles.checkboxLabel, {
              [styles.checkboxChecked]: outputChecked,
            })}
            onClick={toggleOutput}
          >
            <i className="block absolute left-0.5">{outputChecked ? <EyeFilled /> : <EyeInvisibleFilled />}</i>
            <CodeOutlined className={styles.adaptiveIcon} />
            <span className={`${styles.adaptiveLabel} ${styles.noMargins}`}>{"Result"}</span>
          </Checkbox>
        </Tooltip>
      </div>
      <div className="flex-grow-0 ant-btn-group">
        <Tooltip title={`${OS_CMD_CTRL_KEY}+↵`} mouseEnterDelay={1}>
          <Button
            size="middle"
            type="primary"
            icon={<CaretRightOutlined />}
            className={`${styles.buttonGreen}`}
            onClick={handleRun}
          >
            <span className={`${styles.adaptiveLabel}`}>{"Run"}</span>
          </Button>
        </Tooltip>
        <Tooltip title={`${OS_CMD_CTRL_KEY}+S`} mouseEnterDelay={1}>
          <Button size="middle" type="primary" onClick={handleSave} icon={<DownloadOutlined />}>
            <span className={`${styles.adaptiveLabel}`}>{"Save"}</span>
          </Button>
        </Tooltip>
      </div>
    </div>
  )
}
Example #21
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 #22
Source File: PersonsModal.tsx    From posthog-foss with MIT License 4 votes vote down vote up
export function PersonsModal({
    visible,
    view,
    filters,
    onSaveCohort,
    showModalActions = true,
    aggregationTargetLabel,
}: PersonsModalProps): JSX.Element {
    const {
        people,
        loadingMorePeople,
        firstLoadedPeople,
        searchTerm,
        isInitialLoad,
        clickhouseFeaturesEnabled,
        peopleParams,
        actorLabel,
        sessionRecordingId,
    } = useValues(personsModalLogic)
    const {
        hidePeople,
        loadMorePeople,
        setFirstLoadedActors,
        setPersonsModalFilters,
        setSearchTerm,
        switchToDataPoint,
        openRecordingModal,
        closeRecordingModal,
    } = useActions(personsModalLogic)
    const { preflight } = useValues(preflightLogic)
    const { featureFlags } = useValues(featureFlagLogic)

    const title = useMemo(
        () =>
            isInitialLoad ? (
                `Loading ${aggregationTargetLabel.plural}…`
            ) : filters.shown_as === 'Stickiness' ? (
                <>
                    <PropertyKeyInfo value={people?.label || ''} disablePopover /> stickiness on day {people?.day}
                </>
            ) : filters.display === 'ActionsBarValue' || filters.display === 'ActionsPie' ? (
                <PropertyKeyInfo value={people?.label || ''} disablePopover />
            ) : filters.insight === InsightType.FUNNELS ? (
                <>
                    {(people?.funnelStep ?? 0) >= 0 ? 'Completed' : 'Dropped off at'} step{' '}
                    {Math.abs(people?.funnelStep ?? 0)} • <PropertyKeyInfo value={people?.label || ''} disablePopover />{' '}
                    {!!people?.breakdown_value ? `• ${people.breakdown_value}` : ''}
                </>
            ) : filters.insight === InsightType.PATHS ? (
                <>
                    {people?.pathsDropoff ? 'Dropped off after' : 'Completed'} step{' '}
                    <PropertyKeyInfo value={people?.label.replace(/(^[0-9]+_)/, '') || ''} disablePopover />
                </>
            ) : (
                <>
                    {capitalizeFirstLetter(actorLabel)} list on{' '}
                    <DateDisplay interval={filters.interval || 'day'} date={people?.day?.toString() || ''} />
                </>
            ),
        [filters, people, isInitialLoad]
    )

    const flaggedInsights = featureFlags[FEATURE_FLAGS.NEW_INSIGHT_COHORTS]
    const isDownloadCsvAvailable: boolean = view === InsightType.TRENDS && showModalActions && !!people?.action
    const isSaveAsCohortAvailable =
        clickhouseFeaturesEnabled &&
        (view === InsightType.TRENDS ||
            view === InsightType.STICKINESS ||
            (!!flaggedInsights && (view === InsightType.FUNNELS || view === InsightType.PATHS))) && // make sure flaggedInsights isn't evaluated as undefined
        showModalActions

    const colorList = getChartColors('white', people?.crossDataset?.length)
    const showCountedByTag = !!people?.crossDataset?.find(({ action }) => action?.math && action.math !== 'total')
    const hasMultipleSeries = !!people?.crossDataset?.find(({ action }) => action?.order)
    return (
        <>
            {!!sessionRecordingId && <SessionPlayerDrawer onClose={closeRecordingModal} />}
            <Modal
                title={title}
                visible={visible}
                onCancel={hidePeople}
                footer={
                    people &&
                    people.count > 0 &&
                    (isDownloadCsvAvailable || isSaveAsCohortAvailable) && (
                        <>
                            {isDownloadCsvAvailable && (
                                <Button
                                    icon={<DownloadOutlined />}
                                    href={api.actions.determinePeopleCsvUrl(
                                        {
                                            label: people.label,
                                            action: people.action,
                                            date_from: people.day,
                                            date_to: people.day,
                                            breakdown_value: people.breakdown_value,
                                        },
                                        filters
                                    )}
                                    style={{ marginRight: 8 }}
                                    data-attr="person-modal-download-csv"
                                >
                                    Download CSV
                                </Button>
                            )}
                            {isSaveAsCohortAvailable && (
                                <Button
                                    onClick={onSaveCohort}
                                    icon={<UsergroupAddOutlined />}
                                    data-attr="person-modal-save-as-cohort"
                                >
                                    Save as cohort
                                </Button>
                            )}
                        </>
                    )
                }
                width={600}
                className="person-modal"
            >
                {isInitialLoad ? (
                    <div style={{ padding: 16 }}>
                        <Skeleton active />
                    </div>
                ) : (
                    people && (
                        <>
                            {!preflight?.is_clickhouse_enabled && (
                                <Input.Search
                                    allowClear
                                    enterButton
                                    placeholder="Search for persons by email, name, or ID"
                                    onChange={(e) => {
                                        setSearchTerm(e.target.value)
                                        if (!e.target.value) {
                                            setFirstLoadedActors(firstLoadedPeople)
                                        }
                                    }}
                                    value={searchTerm}
                                    onSearch={(term) =>
                                        term
                                            ? setPersonsModalFilters(term, people, filters)
                                            : setFirstLoadedActors(firstLoadedPeople)
                                    }
                                />
                            )}
                            {featureFlags[FEATURE_FLAGS.MULTI_POINT_PERSON_MODAL] &&
                                !!people.crossDataset?.length &&
                                people.seriesId !== undefined && (
                                    <div className="data-point-selector">
                                        <Select value={people.seriesId} onChange={(_id) => switchToDataPoint(_id)}>
                                            {people.crossDataset.map((dataPoint) => (
                                                <Select.Option
                                                    value={dataPoint.id}
                                                    key={`${dataPoint.action?.id}${dataPoint.breakdown_value}`}
                                                >
                                                    <InsightLabel
                                                        seriesColor={colorList[dataPoint.id]}
                                                        action={dataPoint.action}
                                                        breakdownValue={
                                                            dataPoint.breakdown_value === ''
                                                                ? 'None'
                                                                : dataPoint.breakdown_value?.toString()
                                                        }
                                                        showCountedByTag={showCountedByTag}
                                                        hasMultipleSeries={hasMultipleSeries}
                                                    />
                                                </Select.Option>
                                            ))}
                                        </Select>
                                    </div>
                                )}
                            <div className="user-count-subheader">
                                <IconPersonFilled style={{ fontSize: '1.125rem', marginRight: '0.5rem' }} />
                                <span>
                                    This list contains{' '}
                                    <b>
                                        {people.count} unique {aggregationTargetLabel.plural}
                                    </b>
                                    {peopleParams?.pointValue !== undefined &&
                                        (!peopleParams.action?.math || peopleParams.action?.math === 'total') && (
                                            <>
                                                {' '}
                                                who performed the event{' '}
                                                <b>
                                                    {peopleParams.pointValue} total{' '}
                                                    {pluralize(peopleParams.pointValue, 'time', undefined, false)}
                                                </b>
                                            </>
                                        )}
                                    .
                                </span>
                            </div>
                            {people.count > 0 ? (
                                <LemonTable
                                    columns={
                                        [
                                            {
                                                title: 'Person',
                                                key: 'person',
                                                render: function Render(_, actor: ActorType) {
                                                    return <ActorRow actor={actor} />
                                                },
                                            },
                                            {
                                                width: 0,
                                                title: 'Recordings',
                                                key: 'recordings',
                                                render: function Render(_, actor: ActorType) {
                                                    if (
                                                        actor.matched_recordings?.length &&
                                                        actor.matched_recordings?.length > 0
                                                    ) {
                                                        return (
                                                            <MultiRecordingButton
                                                                sessionRecordings={actor.matched_recordings}
                                                                onOpenRecording={(sessionRecording) => {
                                                                    openRecordingModal(sessionRecording.session_id)
                                                                }}
                                                            />
                                                        )
                                                    }
                                                },
                                            },
                                        ] as LemonTableColumns<ActorType>
                                    }
                                    className="persons-table"
                                    rowKey="id"
                                    expandable={{
                                        expandedRowRender: function RenderPropertiesTable({ properties }) {
                                            return Object.keys(properties).length ? (
                                                <PropertiesTable properties={properties} />
                                            ) : (
                                                'This person has no properties.'
                                            )
                                        },
                                    }}
                                    embedded
                                    showHeader={false}
                                    dataSource={people.people}
                                    nouns={['person', 'persons']}
                                />
                            ) : (
                                <div className="person-row-container person-row">
                                    We couldn't find any matching {aggregationTargetLabel.plural} for this data point.
                                </div>
                            )}
                            {people?.next && (
                                <div
                                    style={{
                                        margin: '1rem',
                                        textAlign: 'center',
                                    }}
                                >
                                    <Button
                                        type="primary"
                                        style={{ color: 'white' }}
                                        onClick={loadMorePeople}
                                        loading={loadingMorePeople}
                                    >
                                        Load more {aggregationTargetLabel.plural}
                                    </Button>
                                </div>
                            )}
                        </>
                    )
                )}
            </Modal>
        </>
    )
}
Example #23
Source File: EventsTable.tsx    From posthog-foss with MIT License 4 votes vote down vote up
export function EventsTable({
    fixedFilters,
    pageKey,
    hidePersonColumn,
    hideTableConfig,
    sceneUrl,
    // Disables all interactivity and polling for filters
    disableActions,
    // How many months of data to fetch?
    fetchMonths,
}: EventsTable): JSX.Element {
    const { currentTeam } = useValues(teamLogic)
    const logic = eventsTableLogic({
        fixedFilters,
        key: pageKey,
        sceneUrl: sceneUrl || urls.events(),
        disableActions,
        fetchMonths,
    })
    const {
        properties,
        eventsFormatted,
        isLoading,
        hasNext,
        isLoadingNext,
        newEvents,
        eventFilter,
        automaticLoadEnabled,
        exportUrl,
        highlightEvents,
        sceneIsEventsPage,
        months,
    } = useValues(logic)
    const { tableWidth, selectedColumns } = useValues(tableConfigLogic)
    const { propertyNames } = useValues(propertyDefinitionsModel)
    const { fetchNextEvents, prependNewEvents, setEventFilter, toggleAutomaticLoad, startDownload, setPollingActive } =
        useActions(logic)
    const { filters } = useValues(propertyFilterLogic({ pageKey }))

    const showLinkToPerson = !fixedFilters?.person_id

    usePageVisibility(setPollingActive)

    const newEventsRender = (
        { date_break, new_events }: EventsTableRowItem,
        colSpan: number
    ): TableCellRepresentation => {
        return {
            children:
                date_break ||
                (new_events ? (
                    <LemonButton
                        icon={<IconSync />}
                        style={{ borderRadius: 0 }}
                        onClick={() => prependNewEvents()}
                        center
                        fullWidth
                    >
                        {newEvents.length === 1
                            ? `There is 1 new event. Click here to load it`
                            : `There are ${newEvents.length || ''} new events. Click here to load them`}
                    </LemonButton>
                ) : (
                    '???'
                )),
            props: {
                colSpan: colSpan + 1,
                style: new_events ? { padding: 0 } : undefined,
            },
        }
    }
    const personColumn: LemonTableColumn<EventsTableRowItem, keyof EventsTableRowItem | undefined> = {
        title: 'Person',
        key: 'person',
        render: function renderPerson(_, { event }: EventsTableRowItem) {
            if (!event) {
                return { props: { colSpan: 0 } }
            }
            return showLinkToPerson && event.person?.distinct_ids?.length ? (
                <Link to={urls.person(event.person.distinct_ids[0])}>
                    <PersonHeader withIcon person={event.person} />
                </Link>
            ) : (
                <PersonHeader withIcon person={event.person} />
            )
        },
    }

    const defaultColumns = useMemo<LemonTableColumns<EventsTableRowItem>>(() => {
        const _localColumns = [
            {
                title: 'Event',
                key: 'event',
                width: '16rem',
                render: function render(_, item: EventsTableRowItem) {
                    if (!item.event) {
                        return newEventsRender(item, tableWidth)
                    }
                    const { event } = item
                    return <PropertyKeyInfo value={autoCaptureEventToDescription(event)} />
                },
                ellipsis: true,
            },
            {
                title: 'URL / Screen',
                key: 'url',
                width: '4rem',
                render: function renderURL(_, { event }: EventsTableRowItem) {
                    if (!event) {
                        return { props: { colSpan: 0 } }
                    }
                    const param = event.properties['$current_url'] ? '$current_url' : '$screen_name'
                    if (!disableActions) {
                        return (
                            <FilterPropertyLink
                                className="ph-no-capture"
                                property={param}
                                value={event.properties[param] as string}
                                filters={{ properties }}
                            />
                        )
                    }
                    return <Property value={event.properties[param]} />
                },
                ellipsis: true,
            },
            {
                title: 'Source',
                key: 'source',
                render: function renderSource(_, { event }: EventsTableRowItem) {
                    if (!event) {
                        return { props: { colSpan: 0 } }
                    }
                    if (!disableActions) {
                        return (
                            <FilterPropertyLink
                                property="$lib"
                                value={event.properties['$lib'] as string}
                                filters={{ properties }}
                            />
                        )
                    }
                    return <Property value={event.properties['$lib']} />
                },
            },
        ] as LemonTableColumns<EventsTableRowItem>
        if (!hidePersonColumn) {
            _localColumns.splice(1, 0, personColumn)
        }
        return _localColumns
    }, [tableWidth])

    const columns = useMemo(() => {
        const columnsSoFar =
            selectedColumns === 'DEFAULT'
                ? defaultColumns
                : selectedColumns.map(
                      (e, index): LemonTableColumn<EventsTableRowItem, keyof EventsTableRowItem | undefined> =>
                          defaultColumns.find((d) => d.key === e) || {
                              title: keyMapping['event'][e] ? keyMapping['event'][e].label : e,
                              key: e,
                              render: function render(_, item: EventsTableRowItem) {
                                  const { event } = item
                                  if (!event) {
                                      if (index === 0) {
                                          return newEventsRender(item, tableWidth)
                                      } else {
                                          return { props: { colSpan: 0 } }
                                      }
                                  }
                                  if (!disableActions) {
                                      return (
                                          <FilterPropertyLink
                                              className="ph-no-capture "
                                              property={e}
                                              value={event.properties[e] as string}
                                              filters={{ properties }}
                                          />
                                      )
                                  }
                                  return <Property value={event.properties[e]} />
                              },
                          }
                  )
        columnsSoFar.push({
            title: 'Time',
            key: 'time',
            render: function renderTime(_, { event }: EventsTableRowItem) {
                if (!event) {
                    return { props: { colSpan: 0 } }
                }
                return <TZLabel time={event.timestamp} showSeconds />
            },
        })
        columnsSoFar.push({
            key: 'actions',
            width: 0,
            render: function renderActions(_, { event }: EventsTableRowItem) {
                if (!event) {
                    return { props: { colSpan: 0 } }
                }

                let insightParams: Partial<FilterType> | undefined
                if (event.event === '$pageview') {
                    insightParams = {
                        insight: InsightType.TRENDS,
                        interval: 'day',
                        display: ChartDisplayType.ActionsLineGraphLinear,
                        actions: [],
                        events: [
                            {
                                id: '$pageview',
                                name: '$pageview',
                                type: 'events',
                                order: 0,
                                properties: [
                                    {
                                        key: '$current_url',
                                        value: event.properties.$current_url,
                                        type: 'event',
                                    },
                                ],
                            },
                        ],
                    }
                } else if (event.event !== '$autocapture') {
                    insightParams = {
                        insight: InsightType.TRENDS,
                        interval: 'day',
                        display: ChartDisplayType.ActionsLineGraphLinear,
                        actions: [],
                        events: [
                            {
                                id: event.event,
                                name: event.event,
                                type: 'events',
                                order: 0,
                                properties: [],
                            },
                        ],
                    }
                }

                return (
                    <More
                        overlay={
                            <>
                                {currentTeam && (
                                    <LemonButton
                                        type="stealth"
                                        onClick={() =>
                                            createActionFromEvent(
                                                currentTeam.id,
                                                event,
                                                0,
                                                currentTeam.data_attributes || []
                                            )
                                        }
                                        fullWidth
                                        data-attr="events-table-create-action"
                                    >
                                        Create action from event
                                    </LemonButton>
                                )}
                                {insightParams && (
                                    <LemonButton
                                        type="stealth"
                                        to={urls.insightNew(insightParams)}
                                        fullWidth
                                        data-attr="events-table-usage"
                                    >
                                        Try out in Insights
                                    </LemonButton>
                                )}
                            </>
                        }
                    />
                )
            },
        })
        return columnsSoFar
    }, [selectedColumns])

    return (
        <div data-attr="manage-events-table">
            <div className="events" data-attr="events-table">
                {!disableActions && <EventPageHeader activeTab={EventsTab.Events} hideTabs={!sceneIsEventsPage} />}
                {!disableActions && (
                    <div
                        className="mb"
                        style={{
                            display: 'flex',
                            gap: '0.75rem',
                            flexWrap: 'wrap',
                            justifyContent: 'space-between',
                            alignItems: 'start',
                        }}
                    >
                        <div style={{ display: 'flex', gap: '0.75rem', flexDirection: 'column', flexGrow: 1 }}>
                            <EventName
                                value={eventFilter}
                                onChange={(value: string) => {
                                    setEventFilter(value || '')
                                }}
                            />
                            <PropertyFilters
                                pageKey={pageKey}
                                style={{ marginBottom: 0 }}
                                eventNames={eventFilter ? [eventFilter] : []}
                            />
                        </div>

                        <div style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: '0.75rem' }}>
                            <LemonSwitch
                                id="autoload-switch"
                                label="Automatically load new events"
                                checked={automaticLoadEnabled}
                                onChange={toggleAutomaticLoad}
                            />
                            {!hideTableConfig && (
                                <TableConfig
                                    availableColumns={propertyNames}
                                    immutableColumns={['event', 'person']}
                                    defaultColumns={defaultColumns.map((e) => e.key || '')}
                                />
                            )}
                            {exportUrl && (
                                <Tooltip title="Export up to 10,000 latest events." placement="left">
                                    <Button icon={<DownloadOutlined />} onClick={startDownload}>
                                        Export events
                                    </Button>
                                </Tooltip>
                            )}
                        </div>
                    </div>
                )}

                <LemonTable
                    dataSource={eventsFormatted}
                    loading={isLoading}
                    columns={columns}
                    key={selectedColumns === 'DEFAULT' ? 'default' : selectedColumns.join('-')}
                    className="ph-no-capture"
                    emptyState={
                        isLoading ? undefined : filters.some((filter) => Object.keys(filter).length) || eventFilter ? (
                            `No events matching filters found in the last ${months} months!`
                        ) : (
                            <>
                                This project doesn't have any events. If you haven't integrated PostHog yet,{' '}
                                <Link to="/project/settings">
                                    click here to instrument analytics with PostHog in your product
                                </Link>
                                .
                            </>
                        )
                    }
                    rowKey={(row) =>
                        row.event ? row.event.id + '-' + row.event.event : row.date_break?.toString() || ''
                    }
                    rowClassName={(row) => {
                        return clsx({
                            'event-row': row.event,
                            highlighted: row.event && highlightEvents[row.event.id],
                            'event-row-is-exception': row.event && row.event.event === '$exception',
                            'event-row-date-separator': row.date_break,
                            'event-row-new': row.new_events,
                        })
                    }}
                    expandable={{
                        expandedRowRender: function renderExpand({ event }) {
                            return event && <EventDetails event={event} />
                        },
                        rowExpandable: ({ event, date_break, new_events }) => (date_break || new_events ? -1 : !!event),
                    }}
                />
                <Button
                    type="primary"
                    onClick={fetchNextEvents}
                    loading={isLoadingNext}
                    style={{
                        display: hasNext || isLoadingNext ? 'block' : 'none',
                        margin: '2rem auto 1rem',
                    }}
                >
                    Load more events
                </Button>
            </div>
        </div>
    )
}
Example #24
Source File: palette.tsx    From jmix-frontend with Apache License 2.0 4 votes vote down vote up
palette = () =>
  <Palette>
    <Category name="Text">
      <Component name="Formatted Message">
        <Variant>
          <FormattedMessage />
        </Variant>
      </Component>
      <Component name="Heading">
        <Variant name='h1'>
          <Typography.Title></Typography.Title>
        </Variant>
        <Variant name='h2'>
          <Typography.Title level = {2}></Typography.Title>
        </Variant>
        <Variant name='h3'>
          <Typography.Title level = {3}></Typography.Title>
        </Variant>
        <Variant name='h4'>
          <Typography.Title level = {4}></Typography.Title>
        </Variant>
        <Variant name='h5'>
          <Typography.Title level = {5}></Typography.Title>
        </Variant>
      </Component>
      <Component name='Text'>
        <Variant>
          <Typography.Text></Typography.Text>
        </Variant>
        <Variant name = 'Secondary'>
          <Typography.Text type="secondary"></Typography.Text>
        </Variant>
        <Variant name = 'Success'>
          <Typography.Text type="success"></Typography.Text>
        </Variant>
        <Variant name = 'Warning'>
          <Typography.Text type="warning"></Typography.Text>
        </Variant>
        <Variant name = 'Danger'>
          <Typography.Text type="danger"></Typography.Text>
        </Variant>
        <Variant name = 'Disabled'>
          <Typography.Text disabled></Typography.Text>
        </Variant>
      </Component>
    </Category>
    <Category name="Layout">
      <Component name="Divider">
        <Variant>
          <Divider />
        </Variant>
      </Component>

      <Component name="Grid">
        <Variant name="Simple Row">
          <Row></Row>
        </Variant>
        <Variant name="Two columns">
          <Row>
            <Col span={12}></Col>
            <Col span={12}></Col>
          </Row>
        </Variant>
        <Variant name="Three columns">
          <Row>
            <Col span={8}></Col>
            <Col span={8}></Col>
            <Col span={8}></Col>
          </Row>
        </Variant>
      </Component>

      <Component name="Space">
        <Variant>
          <Space />
        </Variant>
        <Variant name="Small">
          <Space size={"small"} />
        </Variant>
        <Variant name="Large">
          <Space size={"large"} />
        </Variant>
      </Component>
    </Category>
    <Category name="Controls">
      <Component name="Autocomplete">
        <Variant>
          <AutoComplete placeholder="input here" />
        </Variant>
      </Component>

      <Component name="Button">
        <Variant>
          <Button></Button>
        </Variant>
        <Variant name="Primary">
          <Button type="primary" ></Button>
        </Variant>
        <Variant name="Link">
          <Button type="link" ></Button>
        </Variant>
        <Variant name="Dropdown">
          <Dropdown
            trigger={['click']}
            overlay={<Menu>
              <Menu.Item>
              </Menu.Item>
              <Menu.Item>
              </Menu.Item>
              <Menu.Item>
              </Menu.Item>
            </Menu>}
          >
            <Button></Button>
          </Dropdown>
        </Variant>
      </Component>

      <Component name="Checkbox">
        <Variant>
          <Checkbox />
        </Variant>
      </Component>

      <Component name='Switch'>
        <Variant>
          <Switch />
        </Variant>
      </Component>

      <Component name='Radio Group'>
        <Variant>
          <Radio.Group>
            <Radio value={1}>A</Radio>
            <Radio value={2}>B</Radio>
            <Radio value={3}>C</Radio>
            <Radio value={4}>D</Radio>
          </Radio.Group>
        </Variant>
        <Variant name = 'Button'>
          <Radio.Group>
            <Radio.Button value={1}>A</Radio.Button>
            <Radio.Button value={2}>B</Radio.Button>
            <Radio.Button value={3}>C</Radio.Button>
            <Radio.Button value={4}>D</Radio.Button>
          </Radio.Group>
        </Variant>
      </Component>

      <Component name="DatePicker">
        <Variant>
          <DatePicker />
        </Variant>
        <Variant name="Range">
          <DatePicker.RangePicker />
        </Variant>
      </Component>

      <Component name="TimePicker">
        <Variant>
          <TimePicker />
        </Variant>
        <Variant name="Range">
          <TimePicker.RangePicker />
        </Variant>
      </Component>

      <Component name="Input">
        <Variant>
          <Input />
        </Variant>
        <Variant name='Number'>
          <InputNumber />
        </Variant>
      </Component>

      <Component name='Select'>
        <Variant>
          <Select defaultValue="1">
            <Select.Option value="1">1</Select.Option>
            <Select.Option value="2">2</Select.Option>
          </Select>
        </Variant>
        <Variant name='Multiple'>
          <Select
            defaultValue={["1"]}
            mode="multiple"
            allowClear
          >
            <Select.Option value="1">1</Select.Option>
            <Select.Option value="2">2</Select.Option>
          </Select>
        </Variant>
      </Component>

      <Component name="Link">
        <Variant>
          <Typography.Link href="" target="_blank">
          </Typography.Link>
        </Variant>
      </Component>

      <Component name='Slider'>
        <Variant>
          <Slider defaultValue={30} />
        </Variant>
        <Variant name = 'Range'>
          <Slider range defaultValue={[20, 50]}/>
        </Variant>
      </Component>
    </Category>
    <Category name="Data Display">
    <Component name="Field">
        <Variant>
          <Field
            entityName={ENTITY_NAME}
            disabled={readOnlyMode}
            propertyName=''
            formItemProps={{
              style: { marginBottom: "12px" }
            }}
          />
        </Variant>
      </Component>
      <Component name="Card">
        <Variant>
          <Card />
        </Variant>
        <Variant name="With Title">
          <Card>
            <Card title="Card title">
              <p>Card content</p>
            </Card>
          </Card>
        </Variant>
        <Variant name="My custom card">
          <Card>
            <Card title="Card title">
              <p>Card content</p>
              <Avatar />
            </Card>
          </Card>
        </Variant>
      </Component>
      <Component name="Tabs">
        <Variant>
          <Tabs defaultActiveKey="1">
            <Tabs.TabPane tab="Tab 1" key="1">
              Content of Tab Pane 1
            </Tabs.TabPane>
            <Tabs.TabPane tab="Tab 2" key="2">
              Content of Tab Pane 2
            </Tabs.TabPane>
            <Tabs.TabPane tab="Tab 3" key="3">
              Content of Tab Pane 3
            </Tabs.TabPane>
          </Tabs>
        </Variant>
        <Variant name = "Tab Pane">
          <Tabs.TabPane>
          </Tabs.TabPane>
        </Variant>
      </Component>
      <Component name="Collapse">
        <Variant>
          <Collapse defaultActiveKey='1'>
            <Collapse.Panel header="This is panel header 1" key="1">
            </Collapse.Panel>
            <Collapse.Panel header="This is panel header 2" key="2">
            </Collapse.Panel>
            <Collapse.Panel header="This is panel header 3" key="3">
            </Collapse.Panel>
          </Collapse>
        </Variant>
      </Component>
      <Component name="Image">
        <Variant>
          <Image
            width={200}
            src=""
          />
        </Variant>
      </Component>
      <Component name="Avatar">
        <Variant>
          <Avatar icon={<UserOutlined />} />
        </Variant>
        <Variant name="Image">
          <Avatar src="https://joeschmoe.io/api/v1/random" />
        </Variant>
      </Component>
      <Component name="Badge">
        <Variant>
          <Badge count={1}>
          </Badge>
        </Variant>
      </Component>
      <Component name="Statistic">
        <Variant>
          <Statistic title="Title" value={112893} />
        </Variant>
      </Component>
      <Component name="Alert">
        <Variant name="Success">
          <Alert message="Text" type="success" />
        </Variant>
        <Variant name="Info">
          <Alert message="Text" type="info" />
        </Variant>
        <Variant name="Warning">
          <Alert message="Text" type="warning" />
        </Variant>
        <Variant name="Error">
          <Alert message="Text" type="error" />
        </Variant>
      </Component>
      <Component name='List'>
        <Variant>
          <List
            bordered
            dataSource={[]}
            renderItem={item => (
              <List.Item>
              </List.Item>
            )}
          />
        </Variant>
      </Component>
    </Category>
    <Category name="Icons">
      <Component name="Arrow">
        <Variant name = 'Up'>
          <ArrowUpOutlined />
        </Variant>
        <Variant name = 'Down'>
          <ArrowDownOutlined />
        </Variant>
        <Variant name = 'Left'>
          <ArrowLeftOutlined />
        </Variant>
        <Variant name = 'Right'>
          <ArrowRightOutlined />
        </Variant>
      </Component>
      <Component name = 'Question'>
        <Variant>
          <QuestionOutlined />
        </Variant>
        <Variant name = 'Circle'>
          <QuestionCircleOutlined />
        </Variant>
      </Component>
      <Component name = 'Plus'>
        <Variant>
          <PlusOutlined />
        </Variant>
        <Variant name = 'Circle'>
          <PlusCircleOutlined />
        </Variant>
      </Component>
      <Component name = 'Info'>
        <Variant>
          <InfoOutlined />
        </Variant>
        <Variant name = 'Circle'>
          <InfoCircleOutlined />
        </Variant>
      </Component>
      <Component name = 'Exclamation'>
        <Variant>
          <ExclamationOutlined />
        </Variant>
        <Variant name = 'Circle'>
          <ExclamationCircleOutlined />
        </Variant>
      </Component>
      <Component name = 'Close'>
        <Variant>
          <CloseOutlined />
        </Variant>
        <Variant name = 'Circle'>
          <CloseCircleOutlined />
        </Variant>
      </Component>
      <Component name = 'Check'>
        <Variant>
          <CheckOutlined />
        </Variant>
        <Variant name = 'Circle'>
          <CheckCircleOutlined />
        </Variant>
      </Component>
      <Component name = 'Edit'>
        <Variant>
          <EditOutlined />
        </Variant>
      </Component>
      <Component name = 'Copy'>
        <Variant>
          <CopyOutlined />
        </Variant>
      </Component>
      <Component name = 'Delete'>
        <Variant>
          <DeleteOutlined />
        </Variant>
      </Component>
      <Component name = 'Bars'>
        <Variant>
          <BarsOutlined />
        </Variant>
      </Component>
      <Component name = 'Bell'>
        <Variant>
          <BellOutlined />
        </Variant>
      </Component>
      <Component name = 'Clear'>
        <Variant>
          <ClearOutlined />
        </Variant>
      </Component>
      <Component name = 'Download'>
        <Variant>
          <DownloadOutlined />
        </Variant>
      </Component>
      <Component name = 'Upload'>
        <Variant>
          <UploadOutlined />
        </Variant>
      </Component>
      <Component name = 'Sync'>
        <Variant>
          <SyncOutlined />
        </Variant>
      </Component>
      <Component name = 'Save'>
        <Variant>
          <SaveOutlined />
        </Variant>
      </Component>
      <Component name = 'Search'>
        <Variant>
          <SearchOutlined />
        </Variant>
      </Component>
      <Component name = 'Settings'>
        <Variant>
          <SettingOutlined />
        </Variant>
      </Component>
      <Component name = 'Paperclip'>
        <Variant>
          <PaperClipOutlined />
        </Variant>
      </Component>
      <Component name = 'Phone'>
        <Variant>
          <PhoneOutlined />
        </Variant>
      </Component>
      <Component name = 'Mail'>
        <Variant>
          <MailOutlined />
        </Variant>
      </Component>
      <Component name = 'Home'>
        <Variant>
          <HomeOutlined />
        </Variant>
      </Component>
      <Component name = 'Contacts'>
        <Variant>
          <ContactsOutlined />
        </Variant>
      </Component>
      <Component name = 'User'>
        <Variant>
          <UserOutlined />
        </Variant>
        <Variant name = 'Add'>
          <UserAddOutlined />
        </Variant>
        <Variant name = 'Remove'>
          <UserDeleteOutlined />
        </Variant>
      </Component>
      <Component name = 'Team'>
        <Variant>
          <TeamOutlined />
        </Variant>
      </Component>
    </Category>
  </Palette>