antd#Progress TypeScript Examples

The following examples show how to use antd#Progress. 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: PasswordStrength.tsx    From posthog-foss with MIT License 6 votes vote down vote up
export default function PasswordStrength({ password = '' }: { password: string }): JSX.Element {
    // passwordScore is 0 if no password input
    // passwordScore is 20, 40, 60, 80, or 100 if password input, based on zxcvbn score (which is 0, 1, 2, 3, or 4)
    const passwordScore: number = password.length && zxcvbn(password).score * 20 + 20

    return (
        <Progress
            percent={passwordScore}
            size="small"
            strokeColor={
                passwordScore <= 20
                    ? red.primary
                    : passwordScore <= 40
                    ? volcano.primary
                    : passwordScore <= 60
                    ? orange.primary
                    : passwordScore <= 80
                    ? yellow.primary
                    : green.primary
            }
            showInfo={false}
        />
    )
}
Example #2
Source File: ModalConfigs.tsx    From condo with MIT License 6 votes vote down vote up
getUploadProgressModalConfig = (
    title: string,
    processingMessage: string,
    okText: string,
    onButtonClick: () => void
) => {
    return {
        title: title,
        closable: false,
        content: (
            <ModalContext.Consumer>
                {
                    ({ progress }) => {
                        return (
                            <>
                                <Progress
                                    format={(percent) => Math.floor(percent) + '%'}
                                    percent={progress}
                                    status={'active'}
                                />
                                <Alert
                                    style={{ marginTop: 16 }}
                                    message={processingMessage}
                                    type='info'
                                />
                            </>
                        )
                    }
                }
            </ModalContext.Consumer>
        ),
        okText: okText,
        okButtonProps: {
            type: 'primary',
            danger: true,
        },
        onOk: onButtonClick,
    }
}
Example #3
Source File: useIpcRendererHandle.tsx    From Aragorn with MIT License 6 votes vote down vote up
useFileDownloadHandle = () => {
  useEffect(() => {
    ipcRenderer.on('file-download-reply', (_, errMessage?: string) => {
      if (errMessage) {
        message.error(errMessage);
      } else {
        message.success('下载成功');
      }
    });
    ipcRenderer.on('file-download-progress', (_, res: { name: string; progress: number; key: string }) => {
      const percent = Math.floor(res.progress * 100);
      const isFinish = percent === 100;
      notification.open({
        type: isFinish ? 'success' : 'info',
        message: isFinish ? '下载完成' : `正在下载${res.name}`,
        description: <Progress percent={percent} />,
        key: res.key,
        duration: isFinish ? 1.5 : null
      });
    });
  }, []);
}
Example #4
Source File: H5UploadButton.tsx    From whiteboard-demo with MIT License 6 votes vote down vote up
private renderProgress = () => {
        const { t } = this.props;
        return (
            <div style={{ display: "flex", justifyContent: "center" }}>
                <Spin tip={t('upload-finished-loading')} spinning={this.state.deploying} size="large">
                    <Progress
                        type="circle"
                        strokeColor={{
                            '0%': '#108ee9',
                            '100%': '#87d068',
                        }}
                        percent={this.state.percent}>
                    </Progress>
                </Spin>
            </div>
        )
    }
Example #5
Source File: index.tsx    From metaplex with Apache License 2.0 6 votes vote down vote up
WaitingStep = (props: {
  createAuction: () => Promise<void>;
  confirm: () => void;
}) => {
  const [progress, setProgress] = useState<number>(0);

  useEffect(() => {
    const func = async () => {
      const inte = setInterval(
        () => setProgress(prog => Math.min(prog + 1, 99)),
        600,
      );
      await props.createAuction();
      clearInterval(inte);
      props.confirm();
    };
    func();
  }, []);

  return (
    <div
      style={{
        marginTop: 70,
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
      }}
    >
      <Progress type="circle" percent={progress} />
      <div className="waiting-title">
        Your creation is being listed with Metaplex...
      </div>
      <div className="waiting-subtitle">This can take up to 30 seconds.</div>
    </div>
  );
}
Example #6
Source File: GaugeChart.tsx    From nebula-dashboard with Apache License 2.0 6 votes vote down vote up
render() {
    const { percent } = this.props;
    const color = getWhichColor(percent);
    const _percent =
      percent < 1 ? Number(percent.toFixed(2)) : Math.round(percent);
    return (
      <Progress
        className="nebula-chart nebula-chart-gauge"
        strokeLinecap="square"
        strokeColor={color}
        type="dashboard"
        percent={_percent}
        width={140}
        strokeWidth={20}
      />
    );
  }
Example #7
Source File: index.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
// 放在更新人前面
planDetailColumns.splice(
  4,
  0,
  {
    title: i18n.t('dop:Interface pass rate'),
    dataIndex: 'apiPassedCount',
    key: 'apiPass',
    width: 140,
    render: (_text: any, record: TEST_CASE.CaseTableRecord) => {
      const { total, passed } = record.apiCount || {};
      const percent = record.apiCount ? ((passed || 0) * 100) / total : 0;
      return (
        record.id && (
          <div className="mr-6">
            <Progress percent={Math.round(percent)} format={() => `${passed || 0}/${total || 0}`} />
          </div>
        )
      );
    },
  },
  {
    title: i18n.t('dop:Executor'),
    dataIndex: 'executorID',
    key: 'executorID',
    width: 100,
    render: (text) => <UserInfo id={text} render={(data) => data.nick || data.name} />,
  },
);
Example #8
Source File: time-trace.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
TimeTraceBar = React.forwardRef(
  ({ logged, spent, remain, estimate, active = false, onClick }: IZTraceBarProps, ref) => {
    const _logged = numberOr(logged, 0);
    const _spent = numberOr(spent, 0);
    const _remain = numberOr(remain, 0);
    const _estimate = numberOr(estimate, 0);
    const [blue, yellow] = calculatePercent(_logged, _spent, _remain, _estimate);
    return (
      <div className={`time-trace ${active ? 'active-hover' : ''}`} onClick={onClick} ref={ref}>
        <Progress strokeColor="#f47201" showInfo={false} success={{ percent: blue }} percent={blue + yellow} />
      </div>
    );
  },
)
Example #9
Source File: summary-detail.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
renderProgress = (data: IData, i: number, shiftingLeft?: any) => (
  <div key={i} className="progress-row">
    <div className="progress-left">
      <span>{data.name}</span>
      <span>{getFormatter('TIME').format(data.value, 2)}</span>
    </div>
    <div className="progress-right">
      <div style={shiftingLeft ? { left: shiftingLeft[i], position: 'absolute', width: '100%' } : {}}>
        <Progress strokeLinecap="square" percent={data.percent} showInfo={false} />
      </div>
    </div>
  </div>
)
Example #10
Source File: circular.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
Circular: React.FC<IProps> = ({ width = 100, stroke, percent = 0, strokeWidth = 5, className, children }) => {
  const [bgColor, frontColor] = stroke;
  const wrapperWidth = width + strokeWidth;
  return (
    <div className="relative" style={{ width: wrapperWidth, height: wrapperWidth }}>
      <Progress
        width={width + strokeWidth}
        strokeWidth={strokeWidth}
        strokeColor={bgColor}
        percent={100}
        success={{ percent, strokeColor: frontColor }}
        type="circle"
        format={() => ''}
      />
      {children ? (
        <div className="absolute top-0 left-0" style={{ width: wrapperWidth, height: wrapperWidth }}>
          {children}
        </div>
      ) : null}
    </div>
  );
}
Example #11
Source File: test-list.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
CodeCoverage = ({ data }: { data: Array<{ value: number[] }> }) => {
  let rowsTotal = 0;
  let rowsCover = 0;

  data.forEach((item) => {
    rowsTotal += item.value[0] || 0;
    rowsCover += item.value[9] || 0;
  });

  const percent = rowsTotal ? +((rowsCover * 100) / rowsTotal).toFixed(2) : 0;

  const title = (
    <>
      <div>
        {i18n.t('dop:Total number of rows')}: {rowsTotal}
      </div>
      <div>
        {i18n.t('dop:Covering the number of rows')}: {rowsCover}
      </div>
      <div>
        {i18n.t('dop:Line coverage')}: {percent}%
      </div>
    </>
  );
  return (
    <Tooltip title={title} placement="right">
      <Progress
        percent={100}
        success={{
          percent: percent,
        }}
        strokeColor={themeColor['default-02']}
        format={(_percent: number, successPercent: number) => `${Math.floor(successPercent)}%`}
      />
    </Tooltip>
  );
}
Example #12
Source File: test-list.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
ExecuteResult = ({ totals }: { totals: { tests: number; statuses: TEST.Statuses } }) => {
  if (!totals) {
    return null;
  }
  const { failed, error, passed, skipped } = totals.statuses;
  const { tests } = totals;
  const passedPercent = tests ? floor(((passed + skipped) * 100) / tests, 2) : 0;
  const title = (
    <div>
      <div>
        {i18n.t('failed')}: {failed}
      </div>
      <div>
        {i18n.t('Error')}: {error}
      </div>
      <div>
        {i18n.t('dop:pass')}: {passed}
      </div>
      <div>
        {i18n.t('dop:Skipped')}: {skipped}
      </div>
    </div>
  );
  return (
    <Tooltip title={title} placement="right">
      <Progress
        percent={100}
        success={{
          percent: passedPercent,
        }}
        strokeColor={themeColor['default-02']}
        format={(_percent: number, successPercent: number) => `${Math.floor(successPercent)}%`}
      />
    </Tooltip>
  );
}
Example #13
Source File: index.tsx    From RareCamp with Apache License 2.0 6 votes vote down vote up
export default function GlobalLoadingIndicator() {
  const isFetching = useIsFetching()
  const isMutating = useIsMutating()

  return isFetching || isMutating ? (
    <Progress
      style={{ position: 'fixed', top: -12, left: 0, zIndex: 99999 }}
      strokeColor={{
        from: '#66bb6a',
        to: '#5c6bc0',
      }}
      percent={100}
      showInfo={false}
      status="active"
    />
  ) : null
}
Example #14
Source File: utils.tsx    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
getRender = (val: Obj, record: Obj, extra?: Extra) => {
  const { type, data, key } = val || {};
  let Comp: React.ReactNode = null;
  const { userMap = {} } = extra || {};

  if (!data) {
    return '-';
  }

  switch (type) {
    case 'multiple':
      {
        const { props } = extra || ({} as Extra);
        const render = get(props, `columnsRender.${key}`) || defaultMultiple;

        const CompMap = {};
        map(data, (v, k) => {
          CompMap[k] = getRender(v, record, extra);
        });
        Comp = <div>{render(val, record, CompMap)}</div>;
      }
      break;
    case 'icon':
      {
        const { type: iconType, url } = data;
        if (url) {
          Comp = <img src={url} />;
        } else if (iconType) {
          Comp = <ErdaIcon isConfigPageIcon size={16} type={iconType} />;
        } else {
          Comp = '';
        }
      }
      break;
    case 'duration':
      {
        const { value, tip } = data;
        if (!value) {
          Comp = '0s';
        } else if (value === -1) {
          Comp = '-';
        } else {
          const _duration = moment.duration(value, 'seconds');
          const duration = [
            { value: _duration.years(), unit: 'y' },
            { value: _duration.months(), unit: 'm' },
            { value: _duration.days(), unit: 'd' },
            { value: _duration.hours(), unit: 'h' },
            { value: _duration.minutes(), unit: 'min' },
            { value: _duration.seconds(), unit: 's' },
          ];
          const durationArr = duration.filter((item) => item.value) || [];
          const durationStr = durationArr.map((item) => `${item.value}${item.unit}`).join('');
          Comp = tip ? <Tooltip title={tip}>{durationStr}</Tooltip> : durationStr;
        }
      }
      break;
    case 'progressBar':
      {
        const { barCompletedNum, barTotalNum, barPercent, text, status, tip } = data;
        const value = barPercent || (barCompletedNum / barTotalNum) * 100 || 0;

        const content = (
          <>
            <Progress
              percent={value}
              type="circle"
              width={20}
              strokeWidth={18}
              format={() => null}
              strokeColor={statusColorMap[status]}
            />
            <span className="text-black-8  ml-2">{text}</span>
          </>
        );
        Comp = tip ? <Tooltip title={tip}>{content}</Tooltip> : content;
      }
      break;
    case 'user':
      const curUsers = [];
      if (isArray(data.id)) {
        data.id.forEach((vItem: any) => {
          curUsers.push(userMap[vItem] || {});
        });
      } else {
        curUsers.push(userMap[data.id] || {});
      }
      if (data.showIcon === false) {
        Comp = map(curUsers, (item) => item.nick || item.name || item.id || i18n.t('common:None')).join(', ');
      } else {
        Comp = (
          <div>
            {map(curUsers, (cU, idx) => {
              return (
                <span key={idx}>
                  {data.showIcon === false ? null : (
                    <Avatar src={cU.avatar} size="small">
                      {cU.nick ? getAvatarChars(cU.nick) : i18n.t('None')}
                    </Avatar>
                  )}
                  <span className="ml-0.5 mr-1" title={cU.name}>
                    {cU.nick || cU.name || data.value || i18n.t('common:None')}
                  </span>
                </span>
              );
            })}
          </div>
        );
      }
      break;
    case 'dropDownMenu':
      // {
      // const {menus, operations} = data || {};
      // Comp = <DropdownSelectNew options={} />
      // }
      break;
    case 'text':
      if (typeof data === 'string') {
        Comp = data;
      } else if (typeof data === 'object') {
        const { text, enableCopy, status, showDot = false, tip, onlyText = false } = data;
        let value = status ? <Badge text={text} status={status} showDot={showDot} onlyText={onlyText} /> : text;

        if (tip) {
          value = (
            <Tooltip overlayClassName="whitespace-pre" title={tip} className="truncate w-full inline-block">
              {value}
            </Tooltip>
          );
        } else {
          value = <Ellipsis title={value} />;
        }

        Comp = enableCopy ? (
          <span className="flex group">
            <span className="ant-table-cell-ellipsis group-hover:text-purple-deep" title={text}>
              {text}
            </span>
            <Copy>
              <ErdaIcon
                type="fz1"
                size={12}
                data-clipboard-text={text}
                onClick={(e) => e.stopPropagation()}
                className="ml-1 cursor-copy text-desc opacity-0 group-hover:text-purple-deep group-hover:opacity-100"
              />
            </Copy>
          </span>
        ) : (
          value
        );
      }
      break;
    case 'labels':
      {
        const { labels, showCount } = data;
        // TODO showCount should be calculated based on the container width
        Comp = (
          <TagsRow
            labels={labels.map((item: { id: string; title?: string; color?: string; group?: string }) => ({
              ...item,
              label: item.title || item.id,
            }))}
            showCount={showCount ?? 2}
          />
        );
      }
      break;
    case 'moreOperations':
      {
        const { ops } = data;

        const getIcon = (icon: { type?: string; url?: string }) => {
          if (icon.type) {
            return <ErdaIcon type={icon.type} color="currentColor" className="mr-1 text-white-6" />;
          }
          if (icon.url) {
            return <img src={icon.url} />;
          }
          return null;
        };

        const getTableOperationItem = (op: CP_COMMON.Operation, key: string, record: Obj) => {
          const { confirm, disabled, disabledTip, text, icon, operations } = op;
          const { click } = operations || {};
          const { serverData } = click || {};
          if (disabled === true) {
            // 无权限操作
            return (
              <Menu.Item key={key} className="p-0">
                <WithAuth noAuthTip={disabledTip} key={key} pass={false}>
                  <span className="table-operations-btn px-4 py-1 block flex-h-center">
                    {icon ? getIcon(icon) : null}
                    {text}
                  </span>
                </WithAuth>
              </Menu.Item>
            );
          } else if (confirm) {
            // 需要确认的操作
            return (
              <Menu.Item key={key} className="p-0 bg-transparent">
                <Popconfirm
                  title={confirm}
                  onConfirm={(e) => {
                    e && e.stopPropagation();
                    extra?.execOperation({
                      key: 'click',
                      ...click,
                      clientData: {
                        dataRef: op,
                        parentDataRef: record,
                      },
                      serverData,
                    });
                    const customFunc = get(extra, `customOp.operations.${key}`);
                    if (customFunc) {
                      customFunc(op, record);
                    }
                  }}
                  key={key}
                  onCancel={(e: any) => e && e.stopPropagation()}
                  zIndex={1100}
                >
                  <span
                    className="table-operations-btn px-4 py-1 block flex-h-center text-white-9"
                    onClick={(e: any) => e.stopPropagation()}
                  >
                    {icon ? getIcon(icon) : null}
                    {text}
                  </span>
                </Popconfirm>
              </Menu.Item>
            );
          } else {
            // 普通的操作
            return (
              <Menu.Item key={key} className="p-0">
                <span
                  className="table-operations-btn px-4 py-1 block flex-h-center text-white-9"
                  key={key}
                  onClick={(e: any) => {
                    e.stopPropagation();
                    extra?.execOperation({
                      key: 'click',
                      ...click,
                      clientData: {
                        dataRef: op,
                        parentDataRef: record,
                      },
                      serverData,
                    });
                    const customFunc = get(extra, `customOp.operations.${key}`);
                    if (customFunc) {
                      customFunc(op, record);
                    }
                  }}
                >
                  {icon ? getIcon(icon) : null}
                  {text}
                </span>
              </Menu.Item>
            );
          }
        };

        const operationList = [] as any[];
        if (ops) {
          // 根据配置的operations展示
          const operations = sortBy(
            filter(map(ops) || [], (item: CP_COMMON.Operation) => item.show !== false),
            'showIndex',
          );

          map(operations, (item: CP_COMMON.Operation) => {
            if (item) {
              operationList.push(getTableOperationItem(item, item.id, record));
            }
          });
        }

        if (!operationList.length) {
          Comp = null;
        } else {
          Comp = (
            <div className="table-operations">
              <Dropdown
                overlay={
                  <Menu theme="dark" style={{ minWidth: 160, padding: '8px 0' }}>
                    {operationList}
                  </Menu>
                }
                align={{ offset: [0, 5] }}
                trigger={['click']}
                overlayStyle={{ zIndex: 1040 }}
              >
                <ErdaIcon
                  type="more"
                  className="cursor-pointer p-1 bg-hover rounded-sm"
                  onClick={(e) => e.stopPropagation()}
                />
              </Dropdown>
            </div>
          );
        }
      }
      break;
    default:
      Comp = (typeof data === 'string' ? data : data?.text) || '-';
      break;
  }
  return Comp;
}
Example #15
Source File: ApplyPage.tsx    From office-hours with GNU General Public License v3.0 5 votes vote down vote up
ProgressBar = styled(Progress)`
  padding-bottom: 2.5em;

  & .ant-progress-inner {
    border: 1px solid #d9d9d9;
  }
`
Example #16
Source File: ProgressBar.tsx    From jmix-frontend with Apache License 2.0 5 votes vote down vote up
export function ProgressBar({ ...props }: ProgressProps) {
  return <Progress {...props} />;
}
Example #17
Source File: Storage.tsx    From whiteboard-demo with MIT License 5 votes vote down vote up
private renderZipCell(downloader: DownloadLogic, pptState: PPTState): React.ReactNode {
        const { t } = this.props
        const displayProgress = (
            pptState.phase === TaskPhase.Downloading
        );
        const enableRemoveCache = (
            this.state.mode === DownloadingMode.Freedom &&
            pptState.phase === TaskPhase.Cached
        );
        return (
            <div key={pptState.uuid}>
                <div className="room-cell-box">
                    <div className="room-cell-left">
                        <div className="room-cell-image">
                            <img src={zip_icon} alt={"cover"} />
                            {displayProgress &&
                                <div className="room-cell-image-cover">
                                    <Progress
                                        width={42}
                                        style={{color: "white"}}
                                        strokeWidth={6}
                                        type="circle"
                                        trailColor={"white"}
                                        percent={pptState.progress} />
                                </div>
                            }
                        </div>
                        <div>
                            <div className="room-cell-text">{pptState.name}</div>
                        </div>
                    </div>
                    <div className="room-download-cell-right">
                        {this.renderDownloadButton(downloader, pptState)}
                        <Button
                            onClick={() => downloader.removeTask(pptState.uuid)}
                            disabled={!enableRemoveCache}
                            style={{width: 96}}>
                            {t('delete')}
                        </Button>
                    </div>
                </div>
                <div className="room-cell-cut-line" />
            </div>
        );
    }
Example #18
Source File: StatsPageContent.tsx    From disco-cube-admin with MIT License 5 votes vote down vote up
StatsPageContent: React.FC<Props> = ({
  isConnected,
  statusChangedAt,
  cpuLoadsPercent,
  allSystemInfo,
  memUsagePercent,
  cpuTemperature,
}) => {
  return (
    <Segment spacing={10} width="100%" maxWidth={500} height={"100%"} scroll="vertical">
      <h1>Disco Cube</h1>
      <Vertical spacing={30}>
        <Horizontal spacing={40}>
          <Stat label="Status">
            <div>
              <span style={{ color: isConnected ? "#3f8600" : "#cf1322" }}>
                {isConnected ? "Connected" : "Disconnected"}
              </span>
              <span style={{ color: "rgba(0, 0, 0, 0.45)", fontSize: "0.75em" }}>
                {statusChangedAt ? " " + formatDistanceToNow(statusChangedAt) + " ago" : ""}
              </span>
            </div>
          </Stat>
          <Stat label="CPU temperature">
            <span>{cpuTemperature}</span>
          </Stat>
        </Horizontal>
        <Horizontal spacing={40}>
          <Stat label="CPU LOADS">
            <Vertical>
              {cpuLoadsPercent.map((c, i) => (
                <Progress
                  key={i}
                  percent={Math.round(c)}
                  strokeColor={{
                    from: getProgressStrokeColorFromPercentage(c),
                    to: getProgressStrokeColorFromPercentage(c),
                  }}
                />
              ))}
            </Vertical>
          </Stat>
          <Stat label="Mem usage">
            <Progress
              type="circle"
              width={80}
              percent={Math.round(memUsagePercent)}
              strokeColor={{
                from: getProgressStrokeColorFromPercentage(memUsagePercent),
                to: getProgressStrokeColorFromPercentage(memUsagePercent),
              }}
            />
          </Stat>
          <Stretch />
        </Horizontal>

        <Horizontal spacing={40}>
          <Stat label="All System Info" width={360}>
            <SystemInfoTree info={allSystemInfo} />
          </Stat>
        </Horizontal>
      </Vertical>
    </Segment>
  );
}
Example #19
Source File: components.tsx    From jitsu with MIT License 5 votes vote down vote up
render() {
    return <Progress type="circle" percent={this.state.progressPercents} />
  }
Example #20
Source File: form.tsx    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
CustomUpload = ({ form, onChange }: { form: { form: FormInstance }; onChange: (value?: string) => void }) => {
  const [fileList, setFileList] = React.useState<UploadFile[]>([]);

  const fileType = 'application/zip';

  const props: UploadProps = {
    fileList,
    accept: '.zip',
    beforeUpload: (file) => {
      const reg = new RegExp(/^.*\.zip$/);
      if (!reg.test(file.name)) {
        onChange('type-error');
        return false;
      }

      return true;
    },
    onChange: async ({ file }) => {
      if (file.type !== fileType) {
        setFileList([{ ...file, status: 'error', percent: 100, name: file.name }]);
      } else {
        setFileList([file]);
        if (file.response?.success) {
          const diceFileID = file.response?.data.uuid;
          const res = await parseVersion.fetch({ diceFileID });
          if (res.success) {
            onChange(diceFileID);
            form.form.setFieldsValue({ version: res.data?.version });
          }
        }
      }
    },
  };

  const remove = () => {
    setFileList([]);
    onChange();
  };

  return (
    <div className="w-1/2">
      <Dragger {...getUploadProps(props, 10)}>
        <div className="flex-all-center py-1">
          <img src={EmptySVG} style={{ height: 80 }} />
          <div className="ml-2.5">
            <div className="text-left text-default text-base">{i18n.t('dop:Upload ZIP file')}</div>
            <div className="text-xs text-default-6 leading-5">{i18n.t('dop:Click to browse and upload')}</div>
          </div>
        </div>
      </Dragger>
      {fileList.map((item) => (
        <div className="mt-4">
          <div className="flex-h-center justify-between">
            {item.name}
            <ErdaIcon type="guanbi" className="text-default-4 cursor-pointer" onClick={remove} />
          </div>
          <Progress showInfo={false} percent={item.percent || 0} status={progressStatusMap[item.status]} />
        </div>
      ))}
    </div>
  );
}
Example #21
Source File: common-comp.tsx    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
BaseInfo = () => {
  const planItemDetail = testPlanStore.useStore((s) => s.planItemDetail);
  const partnerIDs = planItemDetail.partnerIDs || [];
  const percent = React.useMemo(() => {
    const { succ, total } = planItemDetail.relsCount;
    return Math.floor((succ / total) * 100 || 0);
  }, [planItemDetail.relsCount]);
  const createTime = planItemDetail.createdAt ? moment(planItemDetail.createdAt).format('YYYY-MM-DD HH:mm:ss') : '';
  const content = (
    <div>
      <span className="text-normal font-medium mb-2">{i18n.t('dop:Participant')}</span>
      <div className="flex flex-wrap items-center participant-items justify-start">
        {partnerIDs.map((value, index) => {
          return (
            <span key={`${String(index)}-${value}`} className="mr-2 mb-2">
              <UserInfo.RenderWithAvatar id={value} />
            </span>
          );
        })}
      </div>
    </div>
  );
  return (
    <div className="common-list-item px-0">
      <div>
        <div className="title">
          {planItemDetail.id} - {planItemDetail.name}
        </div>
        <div className="sub member">
          <span className="ml-1">{i18n.t('dop:Principal')}:</span>
          <UserInfo.RenderWithAvatar id={planItemDetail.ownerID} />
          <span className="ml-6">{i18n.t('dop:Participant')}:</span>
          <Popover overlayStyle={{ width: 280 }} overlayClassName="participant-popover" content={content}>
            <span className="participant flex justify-between items-center hover-active">
              {partnerIDs.slice(0, 4).map((p, index) => (
                <UserInfo.RenderWithAvatar showName={false} id={p} key={`${String(index)}-${p}`} />
              ))}
              {partnerIDs.length > 4 ? <span className="count px-1 font-medium">+{partnerIDs.length - 4}</span> : null}
            </span>
          </Popover>
          {/* <span>{planItemDetail.relatedIterative} 迭代</span> */}
        </div>
      </div>
      <div>
        <div className="text-normal">
          <Progress strokeWidth={12} style={{ width: '230px' }} percent={percent} showInfo={false} />{' '}
          {i18n.t('dop:Pass rate')} {percent}%
        </div>
        <div className="sub float-right">
          {<UserInfo id={planItemDetail.creatorID} />} {i18n.t('dop:built in')} {createTime}
        </div>
      </div>
    </div>
  );
}
Example #22
Source File: BasicProgress.tsx    From next-basics with GNU General Public License v3.0 5 votes vote down vote up
export function BasicProgress(props: BasicProgressProps): React.ReactElement {
  const [color, setColor] = useState(props.configProps?.strokeColor || "blue");

  useEffect(() => {
    if (props.colorMap) {
      const curObj = props.colorMap.find((item) => {
        return +item.progress >= +props.value;
      });
      const color = Object.values(Color).includes(curObj?.color)
        ? curObj.color
        : "";
      setColor(color);
    }
  }, [props.colorMap, props.value]);

  const format = (percent) => {
    return (
      <div className={style.showContainer} style={{ fontSize: props.fontSize }}>
        <span
          style={{ color: props.textColor ?? `var(--theme-${color}-color)` }}
          className={style.showValue}
        >
          {props.text}
        </span>
        <span className={style.showDescription}>{props.description}</span>
      </div>
    );
  };

  return (
    <div className={style.mainContainer}>
      <Progress
        type={props.type}
        strokeColor={`var(--theme-${color}-color)`}
        percent={props.value}
        format={format}
        {...props.configProps}
      />
    </div>
  );
}
Example #23
Source File: index.tsx    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
CP_TopN: React.FC<CP_DATA_RANK.Props> = (props) => {
  const {
    customOp,
    props: configProps,
    data: { list },
  } = props || {};
  const handleClick = (item: CP_DATA_RANK.IItem) => {
    customOp?.clickRow?.(item);
  };

  return (
    <div className="cp-data-rank h-full">
      <Row gutter={8} {...configProps.rowsProps} className="h-full">
        {(list ?? []).map((listItem, index) => {
          const { color = themeColor.default, titleIcon, backgroundIcon } = configProps.theme?.[index] || {};
          const { title, items, span } = listItem;
          return (
            <Col key={title} span={span} className="my-1">
              <div
                className="px-4 py-3 relative h-full items-wrapper"
                style={{ backgroundColor: colorToRgb(color, 0.04) }}
              >
                <div
                  className="absolute top-0 right-0 bg-icon-wrapper flex justify-center items-center"
                  style={{ color: colorToRgb(color, 0.1) }}
                >
                  {titleIcon || backgroundIcon ? <ErdaIcon size={44} type={backgroundIcon ?? titleIcon} /> : null}
                </div>
                <div className="mb-2 flex justify-start items-center">
                  {titleIcon ? (
                    <span style={{ color }} className="mr-2 flex items-center">
                      <ErdaIcon size={16} type={titleIcon} />
                    </span>
                  ) : null}

                  <p className="mb-0 flex-1 text-purple-dark font-medium overflow-hidden overflow-ellipsis whitespace-nowrap text-purple-dark ">
                    {title}
                  </p>
                </div>
                <div>
                  {!items?.length ? (
                    <EmptyHoder relative />
                  ) : (
                    items?.map((item) => {
                      const { name, value, unit, id, percent } = item;
                      return (
                        <div
                          key={id}
                          className={`${customOp?.clickRow ? 'cursor-pointer' : ''} flex flex-col mb-2 last:mb-0`}
                          onClick={() => {
                            handleClick(item);
                          }}
                        >
                          <div className="flex py-1">
                            <Ellipsis className="flex-1 text-purple-dark" title={name} />
                            <div className="ml-2">
                              <span className="text-purple-dark">{value}</span>
                              {unit ? <span className="text-sub text-xs ml-0.5">{unit}</span> : null}
                            </div>
                          </div>
                          <Progress strokeColor={color} percent={percent} showInfo={false} strokeWidth={4} />
                        </div>
                      );
                    })
                  )}
                </div>
              </div>
            </Col>
          );
        })}
      </Row>
    </div>
  );
}
Example #24
Source File: index.tsx    From nebula-studio with Apache License 2.0 4 votes vote down vote up
TaskItem = (props: IProps) => {
  const { 
    data: { 
      space,
      id, 
      name, 
      stats: { totalImportedBytes, totalBytes, numFailed, numReadFailed }, 
      status, 
      message,
      updateTime, 
      createTime 
    }, 
    showConfigDownload,
    onViewLog,
    onConfigDownload,
    onTaskStop, 
    onTaskDelete } = props;
  const [progressStatus, setStatus] = useState<'success' | 'active' | 'normal' | 'exception' | undefined>(undefined);
  const [extraMsg, setExtraMsg] = useState('');
  const addMsg = () => {
    const info: string[] = [];
    if(numFailed > 0) {
      info.push(intl.get('import.notImported', { total: numFailed }));
    }
    if(numReadFailed > 0) {
      info.push(intl.get('import.readFailed', { total: numReadFailed }));
    }
    info.length > 0 && setExtraMsg(info.join(', '));
  };
  useEffect(() => {
    if(status === ITaskStatus.StatusFinished) {
      setStatus('success');
      addMsg();
    } else if(status === ITaskStatus.StatusProcessing) {
      setStatus('active');
      addMsg();
    } else {
      setStatus('exception');
      if(message) {
        setExtraMsg(message);
      }
    }
  }, [status]);
  return (
    <div className={styles.taskItem}>
      <div className={styles.row}>
        <span>{intl.get('common.space')}: {space}</span>
        {showConfigDownload && <Button type="link" size="small" onClick={() => onConfigDownload(id)}>
          <Icon type="icon-studio-btn-download" />
          {intl.get('import.downloadConfig')}
        </Button>}
      </div>
      <div className={styles.row}>
        <div className={styles.progress}>
          <div className={styles.progressInfo}>
            <span className={styles.taskName}>
              {name}
              {status === ITaskStatus.StatusFinished && <span className={styles.completeInfo}>
                <CheckCircleFilled />
                {intl.get('import.importCompleted')}
                <span className={styles.red}>{extraMsg && ` (${extraMsg})`}</span>
              </span>}
              {status === ITaskStatus.StatusAborted && <span className={styles.errInfo}>
                {intl.get('import.importFailed')}
                {extraMsg && ` (${extraMsg})`}
              </span>}
              {status === ITaskStatus.StatusStoped && <span className={styles.errInfo}>
                {intl.get('import.importStopped')}
              </span>}
            </span>
            <div className={styles.moreInfo}>
              <span>
                {status !== ITaskStatus.StatusFinished && `${getFileSize(totalImportedBytes)} / `}
                {getFileSize(totalBytes)}{' '}
              </span>
              <span>{dayjs.duration(dayjs.unix(updateTime).diff(dayjs.unix(createTime))).format('HH:mm:ss')}</span>
            </div>
          </div>
          <Progress 
            format={percent => `${percent}%`}
            status={progressStatus} 
            percent={status !== ITaskStatus.StatusFinished ? floor(totalImportedBytes / totalBytes * 100, 2) : 100} 
            strokeColor={progressStatus && COLOR_MAP[progressStatus]} />
        </div>
        <div className={styles.operations}>
          <Button className="primaryBtn" onClick={() => onViewLog(id, space, status)}>{intl.get('import.viewLogs')}</Button>
          {status === ITaskStatus.StatusProcessing && 
          <Popconfirm
            placement="left"
            title={intl.get('import.endImport')}
            onConfirm={() => onTaskStop(id)}
            okText={intl.get('common.confirm')}
            cancelText={intl.get('common.cancel')}
          >
            <Button className="cancelBtn">{intl.get('import.endImport')}</Button>
          </Popconfirm>}
          {status !== ITaskStatus.StatusProcessing && 
          <Popconfirm
            placement="left"
            title={intl.get('common.ask')}
            onConfirm={() => onTaskDelete(id)}
            okText={intl.get('common.confirm')}
            cancelText={intl.get('common.cancel')}
          >
            <Button danger={true}>{intl.get('common.delete')}</Button>
          </Popconfirm>}
        </div>
      </div>
    </div>
  );
}
Example #25
Source File: BatchExecuteByFilter.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
BatchExecuteByFilter: React.FC<BatchExecuteByFilterProp> = React.memo((props) => {
    const [total, setTotal] = useState(0);
    const [loading, setLoading] = useState(false);
    const [token, setToken] = useState(randomString(20))
    const [executing, setExecuting] = useState(false);
    const [percent, setPercent] = useState(0);

    // 执行任务历史列表
    const [taskHistory, setTaskHistory] = useState<NewTaskHistoryProps[]>([])

    // 计算插件数量
    useEffect(() => {
        setLoading(true)
        const result = simpleQueryToFull(props.isAll, props.simpleQuery, props.allTag);

        ipcRenderer.invoke("QueryYakScript", result).then((data: QueryYakScriptsResponse) => {
            setTotal(data.Total)
        }).catch(e => console.info(e))
            .finally(() => setTimeout(() => setLoading(false), 300))
    }, [
        useDebounce(props.simpleQuery, {wait: 500}),
        useDebounce(props.isAll, {wait: 500})
    ])

    // 回复缓存
    useEffect(() => {
        setLoading(true)
        ipcRenderer
            .invoke("get-value", ExecuteTaskHistory)
            .then((res: any) => {
                setTaskHistory(res ? JSON.parse(res) : [])
            })
            .catch(() => {
            })
            .finally(() => {
                setTimeout(() => setLoading(false), 300)
            })
    }, [])

    // 执行批量任务
    const run = useMemoizedFn((t: TargetRequest) => {
        setPercent(0)

        //@ts-ignore
        const time = Date.parse(new Date()) / 1000
        const obj: NewTaskHistoryProps = {
            target: t,
            simpleQuery: props.simpleQuery,
            isAll: props.isAll,
            time: formatTimestamp(time)
        }
        const arr = [...taskHistory]
        if (taskHistory.length === 10) arr.pop()
        arr.unshift(obj)
        setTaskHistory(arr)
        ipcRenderer.invoke("set-value", ExecuteTaskHistory, JSON.stringify(arr))

        const tokens = randomString(40)
        setToken(tokens)
        StartExecBatchYakScriptWithFilter(
            t, simpleQueryToFull(props.isAll, props.simpleQuery, props.allTag),
            tokens).then(() => {
            setExecuting(true)
        }).catch(e => {
            failed(`启动批量执行插件失败:${e}`)
        })
    });

    const cancel = useMemoizedFn(() => {
        CancelBatchYakScript(token).then()
    })
    useEffect(() => {
        return cancel()
    }, [])

    const executeHistory = useMemoizedFn((item: NewTaskHistoryProps) => {
        setLoading(true)
        props.executeHistory(item)
        setTimeout(() => setLoading(false), 300);
    })

    useEffect(() => {
        if (!token) return

        ipcRenderer.on(`${token}-end`, async (e) => {
            console.info("call finished by token filter")
            setTimeout(() => setExecuting(false), 300)
        })
        return () => {
            ipcRenderer.removeAllListeners(`${token}-end`)
        }
    }, [token])

    return <AutoCard
        title={<Space>
            {"已选插件"}
            <Tag>{`${total}`}</Tag>
        </Space>}
        size={"small"} bordered={false}
        extra={<Space>
            {(percent > 0 || executing) && <div style={{width: 200}}>
                <Progress status={executing ? "active" : undefined} percent={
                    parseInt((percent * 100).toFixed(0))
                }/>
            </div>}
        </Space>}
        bodyStyle={{display: "flex", flexDirection: "column", padding: '0 5px', overflow: "hidden"}}
    >
        <ExecSelectedPlugins
            disableStartButton={total <= 0}
            onSubmit={run}
            onCancel={cancel}
            executing={executing}
            loading={loading}
            history={taskHistory}
            executeHistory={executeHistory}
        />
        <Divider style={{margin: 4}}/>
        <div style={{flex: '1', overflow: "hidden"}}>
            <BatchExecutorResultByFilter token={token} executing={executing} setPercent={setPercent}/>
        </div>
    </AutoCard>
})
Example #26
Source File: Entries.tsx    From subscan-multisig-react with Apache License 2.0 4 votes vote down vote up
// eslint-disable-next-line complexity
export function Entries({
  source,
  isConfirmed,
  isCancelled,
  account,
  loading,
  totalCount,
  currentPage,
  onChangePage,
}: EntriesProps) {
  const { t } = useTranslation();
  const isInjected = useIsInjected();
  const { network } = useApi();

  const renderAction = useCallback(
    // eslint-disable-next-line complexity
    (row: Entry) => {
      if (row.status) {
        return <span>{t(`status.${row.status}`)}</span>;
      }

      const actions: TxActionType[] = [];
      // eslint-disable-next-line react/prop-types
      const pairs = (account.meta?.addressPair ?? []) as AddressPair[];
      const injectedAccounts: string[] = pairs.filter((pair) => isInjected(pair.address)).map((pair) => pair.address);

      if (injectedAccounts.includes(row.depositor)) {
        actions.push('cancel');
      }

      const localAccountInMultisigPairList = intersection(
        injectedAccounts,
        pairs.map((pair) => pair.address)
      );
      const approvedLocalAccounts = intersection(localAccountInMultisigPairList, row.approvals);

      if (approvedLocalAccounts.length !== localAccountInMultisigPairList.length) {
        actions.push('approve');
      }

      if (actions.length === 0) {
        // eslint-disable-next-line react/prop-types
        if (row.approvals && row.approvals.length === account.meta.threshold) {
          actions.push('pending');
        }
      }

      return (
        <Space>
          {actions.map((action) => {
            if (action === 'pending') {
              return (
                <Button key={action} disabled>
                  {t(action)}
                </Button>
              );
            } else if (action === 'approve') {
              return <TxApprove key={action} entry={row} />;
            } else {
              return <TxCancel key={action} entry={row} />;
            }
          })}
        </Space>
      );
    },
    [account.meta?.addressPair, account.meta.threshold, isInjected, t]
  );

  const columns: ColumnsType<Entry> = [
    {
      title: t(isConfirmed || isCancelled ? 'extrinsic_index' : 'call_data'),
      dataIndex: isConfirmed || isCancelled ? 'extrinsicIdx' : 'hexCallData',
      width: 300,
      align: 'left',
      // eslint-disable-next-line complexity
      render(data: string) {
        let extrinsicHeight = '';
        let extrinsicIndex = '';
        if ((isConfirmed || isCancelled) && data.split('-').length > 1) {
          extrinsicHeight = data.split('-')[0];
          extrinsicIndex = data.split('-')[1];
        }

        return !(isConfirmed || isCancelled) ? (
          <>
            <Typography.Text copyable={!isEmpty(data) && { text: data }}>
              {!isEmpty(data)
                ? // ? `${data.substring(0, CALL_DATA_LENGTH)}${data.length > CALL_DATA_LENGTH ? '...' : ''}`
                  toShortString(data, CALL_DATA_LENGTH)
                : '-'}
            </Typography.Text>
          </>
        ) : (
          <SubscanLink extrinsic={{ height: extrinsicHeight, index: extrinsicIndex }}>{data}</SubscanLink>
        );
      },
    },
    {
      title: t('actions'),
      dataIndex: 'callDataJson',
      align: 'left',
      render: renderMethod,
    },
    {
      title: t('progress'),
      dataIndex: 'approvals',
      align: 'left',
      render(approvals: string[]) {
        const cur = (approvals && approvals.length) || 0;

        return cur + '/' + account.meta.threshold;
      },
    },
    {
      title: t('status.index'),
      key: 'status',
      align: 'left',
      render: (_, row) => renderAction(row),
    },
  ];

  const expandedRowRender = (entry: Entry) => {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const progressColumnsNested: ColumnsType<any> = [
      { dataIndex: 'name', width: 100 },
      {
        width: 400,
        dataIndex: 'address',
        render: (address) => (
          <Space size="middle">
            <BaseIdentityIcon theme="polkadot" size={32} value={address} />
            <SubscanLink address={address} copyable />
          </Space>
        ),
      },
      {
        width: 250,
        key: 'status',
        render: (_, pair) => renderMemberStatus(entry, pair, network, !isCancelled && !isConfirmed),
      },
    ];
    // const callDataJson = entry.callData?.toJSON() ?? {};
    const args: Required<ArgObj>[] = ((entry.meta?.args ?? []) as Required<ArgObj>[]).map((arg) => {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const value = (entry.callDataJson?.args as any)[arg?.name ?? ''];

      return { ...arg, value };
    });

    return (
      <div className="record-expand bg-gray-100 py-3 px-5">
        <div className=" text-black-800 text-base leading-none mb-3">{t('progress')}</div>
        <div className="members">
          <Table
            columns={progressColumnsNested}
            dataSource={account.meta.addressPair as { key: string; name: string; address: string }[]}
            pagination={false}
            bordered
            rowKey="address"
            showHeader={false}
            className="mb-4 mx-4"
          />
        </div>
        <div className=" text-black-800 text-base leading-none my-3">{t('parameters')}</div>

        <Args
          args={args}
          className="mb-4 mx-4"
          section={entry.callDataJson?.section}
          method={entry.callDataJson?.method}
        />
      </div>
    );
  };

  return (
    <div className="record-table">
      <Table
        loading={loading}
        dataSource={source}
        columns={columns}
        rowKey={(record) => record.callHash ?? (record.blockHash as string)}
        pagination={
          isConfirmed || isCancelled
            ? {
                total: totalCount,
                pageSize: 10,
                current: currentPage,
                onChange: onChangePage,
              }
            : false
        }
        expandable={{
          expandedRowRender,
          expandIcon: genExpandIcon(),
          expandIconColumnIndex: 4,
        }}
        className="lg:block hidden"
      ></Table>

      <Space direction="vertical" className="lg:hidden block">
        {source.map((data) => {
          const { address, hash, callData, approvals } = data;
          const approvedCount = approvals.length || 0;
          const threshold = (account.meta.threshold as number) || 1;

          return (
            <Collapse key={address} expandIcon={() => <></>} className="wallet-collapse">
              <Panel
                header={
                  <Space direction="vertical" className="w-full mb-4">
                    <Typography.Text className="mr-4" copyable>
                      {hash}
                    </Typography.Text>

                    <div className="flex items-center">
                      <Typography.Text>{renderMethod(callData)}</Typography.Text>

                      <Progress
                        /* eslint-disable-next-line no-magic-numbers */
                        percent={parseInt(String((approvedCount / threshold) * 100), 10)}
                        steps={threshold}
                        className="ml-4"
                      />
                    </div>
                  </Space>
                }
                key={address}
                extra={renderAction(data)}
                className="overflow-hidden mb-4"
              >
                <MemberList
                  data={account}
                  statusRender={(pair) => renderMemberStatus(data, pair, network, !isCancelled && !isConfirmed)}
                />
              </Panel>
            </Collapse>
          );
        })}

        {!source.length && <Empty />}
      </Space>
    </div>
  );
}
Example #27
Source File: YakitUpgrade.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
YakitUpgrade: React.FC<YakitUpgradeProp> = (props) => {
    const [currentVersion, setCurrentVersion] = useState("")
    const [loading, setLoading] = useState(false);
    const [latestLoading, setLatestLoading] = useState(false);
    const [latestVersion, setLatestVersion] = useState("");
    const [downloading, setDownloading] = useState(false);
    const [downloadProgress, setDownloadProgress] = useState<DownloadingState>();

    const queryLatestVersion = () => {
        setLatestLoading(true)
        ipcRenderer.invoke("query-latest-yakit-version").then((data: string) => {
            if (data.startsWith("v")) {
                data = data.substr(1)
            }
            setLatestVersion(data)
        }).catch((e: any) => {
            failed(`${e}`)
        }).finally(
            () => setTimeout(() => setLatestLoading(false), 300)
        )
    }

    const updateCurrent = () => {
        setLoading(true)
        ipcRenderer.invoke("yakit-version").then((data: string) => {
            setCurrentVersion(data)
        }).catch((e: any) => {
            failed(`${e}`)
        }).finally(
            () => setTimeout(() => setLoading(false), 300)
        )
    }

    useEffect(() => {
        ipcRenderer.on("download-yakit-engine-progress", async (e: any, state: DownloadingState) => {
            setDownloadProgress(state);
        })
        return () => {
            ipcRenderer.removeAllListeners("download-yakit-engine-progress")
        }
    }, [])


    useEffect(() => {
        updateCurrent()
        queryLatestVersion()

        // ipcRenderer.invoke("get-windows-install-dir").then(setWinPath).catch(() => {
        // }).finally()
    }, [])

    const install = (version: string) => {
        Modal.confirm({
            title: "Yakit 下载完毕",
            width: "50%",
            content: <>
                <Space direction={"vertical"}>
                    <Tag color={"purple"}>Yakit 安装包下载完毕</Tag>
                    <p/>
                    <Tag>选择 Ok/确定 允许打开 Yakit 安装包下载目录,用户双击安装</Tag>
                    <Tag>选择 Cancel 用户自行找到安装包</Tag>
                    <br/>
                    <Tag>linux/macOs 安装包存储在:~/yakit-projects/yak-engine</Tag>
                    <Tag>windows 安装包存储在:%HOME%/yakit-projects/yak-engine</Tag>
                </Space>
            </>,
            onOk: () => {
                ipcRenderer.invoke("install-yakit", latestVersion).then(() => {
                }).catch((err: any) => {
                })
            }

        })
    }

    const isLatest = currentVersion === latestVersion;
    const color = isLatest ? "green" : "red";
    return <Card
        size={"small"} bodyStyle={{padding: 0}} bordered={false}
    >
        <Space direction={"vertical"} style={{width: "100%"}}>
            <Spin spinning={loading}>
                <Alert message={<Space>
                    当前 Yakit 版本:
                    <Tag
                        color={color}
                    >{currentVersion}</Tag>
                    {isLatest ? <Tag color={"green"}>已是最新</Tag> : <Tag
                        color={"red"}
                    >Yakit 需要更新</Tag>}
                </Space>}/>
            </Spin>
            <Spin spinning={loading}>
                <Alert
                    type={"success"}
                    message={<Space>
                        Yakit 最新版本为:
                        <Tag color={"green"}>{latestVersion}</Tag>
                    </Space>}/>
            </Spin>
            <Spin spinning={downloading}>
                <Space>
                    <Popconfirm
                        visible={(isLatest || loading || latestLoading) ? false : undefined}
                        title={`确定要更新版本: ${latestVersion}`}
                        onConfirm={e => {
                            setDownloading(true)
                            ipcRenderer.invoke("download-latest-yakit", latestVersion).then(() => {
                                success("下载完毕")
                                install(latestVersion)
                            }).catch((e: any) => {
                                failed("下载失败")
                            }).finally(() => {
                                setTimeout(() => setDownloading(false), 100)
                            })
                        }}
                    >
                        <Button
                            type={"primary"} disabled={isLatest || loading || latestLoading}
                        >
                            一键下载最新版 Yakit
                        </Button>
                    </Popconfirm>
                    <Button type={"link"} onClick={() => {
                        install(latestVersion)
                    }}>我已经下载,点此安装</Button>
                </Space>
            </Spin>
            {downloadProgress && <Progress percent={
                downloading ? Math.floor((downloadProgress?.percent || 0) * 100) : 100
            }/>}
            {downloadProgress && downloading && <Space>
                <Tag>剩余时间:{downloadProgress?.time.remaining}</Tag>
                <Tag>已下载用时:{downloadProgress?.time.elapsed}</Tag>
                <Tag>
                    下载速度:约{((downloadProgress?.speed || 0) / 1000000).toFixed(2)}M/s
                </Tag>
            </Space>}
        </Space>
    </Card>
}
Example #28
Source File: BatchExecutorPage.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
BatchExecutorPage: React.FC<BatchExecutorPageProp> = (props) => {
    const [loading, setLoading] = useState(false);
    const [pluginType, setPluginType] = useState<"yak" | "nuclei" | string>("yak");
    const [limit, setLimit] = useState(200);
    const [keyword, setKeyword] = useState("");
    const [scripts, setScripts, getScripts] = useGetState<YakScript[]>([]);
    const [total, setTotal] = useState(0);
    const [selected, setSelected] = useState<string[]>([]);
    const [indeterminate, setIndeterminate] = useState(false);
    const [checked, setChecked] = useState(false)
    const [executing, setExecuting] = useState(false);
    const [token, setToken] = useState<string>(randomString(40));
    const [percent, setPercent] = useState(0.0);

    // 处理性能问题
    const containerRef = useRef();
    const wrapperRef = useRef();
    const [list] = useVirtualList(getScripts(), {
        containerTarget: containerRef,
        wrapperTarget: wrapperRef,
        itemHeight: 50, overscan: 20,
    })
    const [vlistHeigth, setVListHeight] = useState(600);

    // 执行任务历史列表
    const [taskHistory, setTaskHistory] = useState<TaskHistoryProps[]>([])

    useEffect(() => {
        setLoading(true)
        ipcRenderer
            .invoke("get-value", ExecuteTaskHistory)
            .then((res: any) => {
                setTaskHistory(res ? JSON.parse(res) : [])
            })
            .catch(() => {
            })
            .finally(() => {
                setTimeout(() => setLoading(false), 300)
            })
    }, [])

    useEffect(() => {
        const totalYakScript = scripts.length;
        const filterArr = scripts.filter((item) => selected.indexOf(item.ScriptName) > -1)

        const IndeterminateFlag =
            (filterArr.length > 0 && filterArr.length < totalYakScript && selected.length !== 0) ||
            (filterArr.length === 0 && selected.length !== 0)
        const checkedFlag = filterArr.length === totalYakScript && selected.length !== 0

        setIndeterminate(IndeterminateFlag)
        setChecked(checkedFlag)
    }, [selected, scripts])

    const search = useMemoizedFn(() => {
        setLoading(true)
        queryYakScriptList(
            pluginType,
            (data, total) => {
                setTotal(total || 0)
                setScripts(data)
            }, () => setTimeout(() => setLoading(false), 300),
            limit, undefined, keyword,
            (pluginType === "yak" ? {
                IsBatch: true
            } : {
                ExcludeNucleiWorkflow: true,
            }) as any,
        )
    })

    useEffect(() => {
        setSelected([]);
        if (!pluginType) return;
        search()
    }, [pluginType])

    const selectYakScript = useMemoizedFn((y: YakScript) => {
        if (!selected.includes(y.ScriptName)) {
            setSelected([...selected, y.ScriptName])
        }
    });
    const unselectYakScript = useMemoizedFn((y: YakScript) => {
        setSelected(selected.filter(i => i !== y.ScriptName))
    })
    const renderListItem = useMemoizedFn((y: YakScript) => {
        return <YakScriptWithCheckboxLine
            key={y.ScriptName}
            selected={selected.includes(y.ScriptName)} plugin={y} onSelected={selectYakScript}
            onUnselected={unselectYakScript}
        />
    });

    const run = useMemoizedFn((t: TargetRequest) => {
        setPercent(0)

        //@ts-ignore
        const time = Date.parse(new Date()) / 1000
        const obj: TaskHistoryProps = {
            target: t,
            selected: selected,
            pluginType: pluginType,
            limit: limit,
            keyword: keyword || "",
            time: formatTimestamp(time)
        }
        const arr = [...taskHistory]
        if (taskHistory.length === 10) arr.pop()
        arr.unshift(obj)
        setTaskHistory(arr)
        ipcRenderer.invoke("set-value", ExecuteTaskHistory, JSON.stringify(arr))

        const tokens = randomString(40)
        setToken(tokens)
        StartExecBatchYakScript(t, selected, tokens).then(() => {
            setExecuting(true)
        }).catch(e => {
            failed(`启动批量执行插件失败:${e}`)
        })
    });
    const cancel = useMemoizedFn(() => {
        CancelBatchYakScript(token).then()
    });

    useEffect(() => {
        ipcRenderer.on(`${token}-data`, async (e, data: any) => {
            try {
                if (data.ProgressMessage) {
                    setPercent(data.ProgressPercent)
                    return
                }
            } catch (e) {
                console.info(e)
            }

        })
        ipcRenderer.on(`${token}-error`, async (e, data) => {
            failed(`批量执行插件遇到问题: ${data}`)
        })
        ipcRenderer.on(`${token}-end`, async (e) => {
            setTimeout(() => setExecuting(false), 300)
        })
        return () => {
            ipcRenderer.removeAllListeners(`${token}-data`)
            ipcRenderer.removeAllListeners(`${token}-error`)
            ipcRenderer.removeAllListeners(`${token}-end`)
        }
    }, [token])

    const executeHistory = useMemoizedFn((item: TaskHistoryProps) => {
        setLoading(true)
        setLimit(item.limit)
        setKeyword(item.keyword)

        if (item.pluginType === pluginType) setTimeout(() => search(), 300);
        else setPluginType(item.pluginType)

        setTimeout(() => {
            setSelected(item.selected)
            setLoading(false)
        }, 300);
    })

    return <div style={{width: "100%", height: "100%", display: "flex", overflowY: "hidden"}}>
        <div style={{width: 470, height: "100%"}}>
            {/*<AutoSpin*/}
            {/*    spinning={loading}*/}
            {/*>*/}
            <AutoCard
                size={"small"}
                bordered={false}
                title={<Space>
                    <SelectOne label={"插件"} formItemStyle={{marginBottom: 0}} size={"small"} data={[
                        {text: "YAK 插件", value: "yak"},
                        {text: "YAML POC", value: "nuclei"},
                    ]} value={pluginType} setValue={setPluginType}/>
                </Space>}
                bodyStyle={{
                    paddingLeft: 4,
                    paddingRight: 4,
                    overflow: "hidden", display: "flex", flexDirection: "column",
                }}
                extra={<Space>
                    <Popover title={"额外设置"} trigger={["click"]} content={<div>
                        <Form size={"small"} onSubmitCapture={e => {
                            e.preventDefault()

                            search()
                        }}>
                            <InputInteger
                                label={"插件展示数量"} value={limit} setValue={setLimit}
                                formItemStyle={{marginBottom: 4}}
                            />
                            <Form.Item colon={false} label={""} style={{marginBottom: 10}}>
                                <Button type="primary" htmlType="submit">刷新</Button>
                            </Form.Item>
                        </Form>
                    </div>}>
                        <Button size={"small"} icon={<SettingOutlined/>} type={"link"}/>
                    </Popover>
                    <Popover title={"搜索插件关键字"} trigger={["click"]} content={<div>
                        <Form size={"small"} onSubmitCapture={e => {
                            e.preventDefault()

                            search()
                        }}>
                            <InputItem
                                label={""}
                                extraFormItemProps={{style: {marginBottom: 4}, colon: false}}
                                value={keyword}
                                setValue={setKeyword}
                            />
                            <Form.Item colon={false} label={""} style={{marginBottom: 10}}>
                                <Button type="primary" htmlType="submit">搜索</Button>
                            </Form.Item>
                        </Form>
                    </div>}>
                        <Button size={"small"} type={!!keyword ? "primary" : "link"} icon={<SearchOutlined/>}/>
                    </Popover>
                    <Checkbox indeterminate={indeterminate} onChange={(r) => {
                        if (r.target.checked) {
                            const newSelected = [...scripts.map(i => i.ScriptName), ...selected];
                            setSelected(newSelected.filter((e, index) => newSelected.indexOf(e) === index));
                        } else {
                            setSelected([]);
                        }
                    }} checked={checked}>
                        全选
                    </Checkbox>
                </Space>}
            >
                <div style={{flex: "1", overflow: "hidden"}}>
                    <ReactResizeDetector
                        onResize={(width, height) => {
                            if (!width || !height) {
                                return
                            }
                            setVListHeight(height)
                        }}
                        handleWidth={true} handleHeight={true} refreshMode={"debounce"} refreshRate={50}
                    />
                    <div ref={containerRef as any} style={{height: vlistHeigth, overflow: "auto"}}>
                        <div ref={wrapperRef as any}>
                            {list.map(i => renderListItem(i.data))}
                        </div>
                    </div>
                </div>
            </AutoCard>
            {/*</AutoSpin>*/}
        </div>
        <div style={{marginLeft: 12, flex: 1, backgroundColor: "#fff", overflow: "hidden"}}>
            <AutoCard
                title={<Space>
                    {"已选插件 / 当页插件 / 插件总量"}
                    <Tag>{`${selected.length} / ${scripts.length} / ${total}`}</Tag>
                </Space>}
                size={"small"} bordered={false}
                extra={<Space>
                    {(percent > 0 || executing) && <div style={{width: 200}}>
                        <Progress status={executing ? "active" : undefined} percent={
                            parseInt((percent * 100).toFixed(0))
                        }/>
                    </div>}
                </Space>}
                bodyStyle={{display: "flex", flexDirection: "column", padding: '0 5px', overflow: "hidden"}}
            >
                {/* <ExecSelectedPlugins
                    disableStartButton={selected.length === 0}
                    onSubmit={run}
                    onCancel={cancel}
                    executing={executing}
                    loading={loading}
                    history={taskHistory}
                    executeHistory={executeHistory}
                /> */}
                <Divider style={{margin: 4}}/>
                <div style={{flex: '1', overflow: "hidden"}}>
                    <AutoCard style={{padding: 4}} bodyStyle={{padding: 4, overflow: "hidden"}} bordered={false}>
                        <BatchExecutorResultUI token={token} executing={executing}/>
                    </AutoCard>
                </div>
            </AutoCard>
        </div>
    </div>
}
Example #29
Source File: YakUpgrade.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
YakUpgrade: React.FC<YakUpgradeProp> = (props) => {
    const [currentVersion, setCurrentVersion] = useState("")
    const [loading, setLoading] = useState(false);
    const [latestLoading, setLatestLoading] = useState(false);
    const [latestVersion, setLatestVersion] = useState("");
    const [downloading, setDownloading] = useState(false);
    const [downloadProgress, setDownloadProgress] = useState<DownloadingState>();
    const [winPath, setWinPath] = useState("");
    const [platformArch, setPlatformArch] = useState("");

    const latestVersionWithoutV = latestVersion.startsWith("v") ? latestVersion.slice(1) : latestVersion;

    const queryLatestVersion = () => {
        setLatestLoading(true)
        ipcRenderer.invoke("query-latest-yak-version").then((data: string) => {
            setLatestVersion(data)
        }).catch((e: any) => {
            failed(`${e}`)
        }).finally(
            () => setTimeout(() => setLatestLoading(false), 300)
        )
    }

    const updateCurrent = () => {
        setLoading(true)
        ipcRenderer.invoke("get-current-yak").then((data: string) => {
            setCurrentVersion(data)
        }).catch((e: any) => {
            setCurrentVersion("")
            failed(<>
                获取 Yak 引擎当前版本失败,请按提示安装即可<Popover content={`${e}`}>
                <Button size={"small"} type={"link"}>错误详情</Button>
            </Popover>
            </>)
        }).finally(
            () => setTimeout(() => setLoading(false), 300)
        )
    }

    useEffect(() => {
        ipcRenderer.invoke("get-platform-and-arch").then((e: string) => {
            setPlatformArch(e)
        })

        ipcRenderer.on("download-yak-engine-progress", async (e: any, state: DownloadingState) => {
            setDownloadProgress(state);
        })
        return () => {
            ipcRenderer.removeAllListeners("download-yak-engine-progress")
        }
    }, [])


    useEffect(() => {
        updateCurrent()
        queryLatestVersion();

        ipcRenderer.invoke("get-windows-install-dir").then(setWinPath).catch(() => {
        }).finally()
    }, [])

    useEffect(() => {
        (props.existed || []).forEach(i => {
            ipcRenderer.invoke("kill-yak-grpc", i.pid).then(() => {
                info(`KILL yak PROCESS: ${i.pid}`)
            })
        })
    }, [props.existed])

    const install = (version: string) => {
        Modal.confirm({
            title: "Yak 核心引擎下载完毕,将会自动更新到系统目录",
            width: "40%",
            content: <>
                <Space direction={"vertical"}>
                    {(platformArch.startsWith("darwin-") || platformArch === "") &&
                    <Tag color={"purple"}>*nix 系统下会安装在 /usr/local/bin/yak </Tag>}
                    {platformArch.startsWith("darwin-arm64") &&
                    <Space direction={"vertical"}>
                        <Tag color={"purple"}>macOS m1(pro/max) 用户需要检查 Rosetta 2 环境,如需要手动安装,如下:</Tag>
                        <Text mark={false} code={true} copyable={true}>softwareupdate --install-rosetta</Text>
                    </Space>
                    }
                    {platformArch.startsWith("win") && <Tag color={""}>windows 系统下会安装在 {winPath} </Tag>}
                    <p/>
                    <Tag>选择 Ok 允许 Yakit 操作</Tag>
                    <Tag>选择 Cancel 用户可以手动更新 %PATH%</Tag>
                </Space>
            </>,
            onOk: () => {
                ipcRenderer.invoke("install-yak-engine", latestVersion).then(() => {
                    success("安装成功,如未生效,重启 Yakit 即可")
                }).catch((err: any) => {
                    failed(`安装失败: ${err}`)
                }).finally(updateCurrent)
            }

        })
    }

    const isLatest = currentVersion === latestVersion;
    const color = isLatest ? "green" : "red";
    return <Card
        size={"small"} bodyStyle={{padding: 0}} bordered={false}
    >
        <Space direction={"vertical"} style={{width: "100%"}}>
            {platformArch === "darwin-arm64" && <Alert
                type={"error"}
                message={<>
                    当前系统为({platformArch}),如果未安装 Rosetta 2, 无法运行 Yak 核心引擎
                    <br/>
                    <br/>
                    <div>运行以下命令可手动安装 Rosetta,如已安装可忽略</div>
                    <Text mark={false} code={true} copyable={true}>softwareupdate --install-rosetta</Text>
                </>}
            />}
            <Spin spinning={loading}>
                <Alert message={<Space>
                    当前本地安装的 Yak 核心引擎版本为:
                    <Tag
                        color={color}
                    >{currentVersion}</Tag>
                    {isLatest ? <Tag color={"green"}>已是最新</Tag> : <Tag
                        color={"red"}
                    >Yak 引擎需要更新</Tag>}
                </Space>}/>
            </Spin>
            <Spin spinning={loading}>
                <Alert
                    type={"success"}
                    message={<Space direction={"vertical"}>
                        <Space>
                            当前最新的 Yak 引擎版本为
                            <Tag color={"green"}>{latestVersion}</Tag>
                        </Space>
                    </Space>}/>
            </Spin>
            <Spin spinning={downloading}>
                <Space>
                    <Popconfirm
                        visible={(isLatest || loading || latestLoading) ? false : undefined}
                        title={`确定要更新版本: ${latestVersion}`}
                        onConfirm={e => {
                            setDownloading(true)
                            ipcRenderer.invoke("download-latest-yak", latestVersion).then(() => {
                                success("下载完毕")
                                install(latestVersion)
                            }).catch((e: any) => {
                                failed("下载失败")
                            }).finally(() => {
                                setTimeout(() => setDownloading(false), 100)
                            })
                        }}
                    >
                        <Button
                            type={"primary"} disabled={isLatest || loading || latestLoading}
                        >
                            一键更新 Yak 引擎
                        </Button>
                    </Popconfirm>
                    <Button type={"link"} onClick={() => {
                        install(latestVersion)
                    }}>我已经下载,点此安装</Button>
                    <Button danger={true} size={"small"} onClick={() => {
                        showModal({
                            title: "yak 核心引擎下载链接",
                            width: "60%",
                            content: <Space direction={"vertical"}>
                                <Space>
                                    Windows(x64) 下载:
                                    <div>https://yaklang.oss-cn-beijing.aliyuncs.com/yak/{latestVersionWithoutV || "latest"}/yak_windows_amd64.exe</div>
                                </Space>
                                <Space>
                                    MacOS(intel/m1) 下载:
                                    <div>https://yaklang.oss-cn-beijing.aliyuncs.com/yak/{latestVersionWithoutV || "latest"}/yak_darwin_amd64</div>
                                </Space>
                                <Space>
                                    Linux(x64) 下载:
                                    <div>https://yaklang.oss-cn-beijing.aliyuncs.com/yak/{latestVersionWithoutV || "latest"}/yak_linux_amd64</div>
                                </Space>
                                <Alert message={<div>
                                    手动下载完成后 Windows 用户可以把引擎放在 %HOME%/yakit-projects/yak-engine/yak.exe 即可识别
                                    <br/>
                                    MacOS / Linux 用户可以把引擎放在 ~/yakit-projects/yak-engine/yak 即可识别
                                </div>}>

                                </Alert>
                            </Space>
                        })
                    }}>
                        网络问题无法下载?手动下载
                    </Button>
                </Space>
            </Spin>
            {downloadProgress && <Progress percent={
                downloading ? Math.floor((downloadProgress?.percent || 0) * 100) : 100
            }/>}
            {downloadProgress && downloading && <Space>
                <Tag>剩余时间:{downloadProgress?.time.remaining}</Tag>
                <Tag>已下载用时:{downloadProgress?.time.elapsed}</Tag>
                <Tag>
                    下载速度:约{((downloadProgress?.speed || 0) / 1000000).toFixed(2)}M/s
                </Tag>
            </Space>}
        </Space>
    </Card>
}