lodash#round TypeScript Examples

The following examples show how to use lodash#round. 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: machine-table.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
ProgressItem = ({ percent, used, total, unit, unitType }: any) => (
  <div className="machine-percent">
    <Tooltip
      placement="top"
      title={
        unitType
          ? `${getFormatter(unitType).format(used)} / ${getFormatter(unitType).format(total)}`
          : `${round(used, 2)} ${unit} / ${round(total, 2)} ${unit}`
      }
    >
      <div
        className={classNames({
          'cursor-pointer': true,
          'machine-percent-bar': true,
          'machine-percent-error-bar': percent >= 100,
        })}
        style={{ width: `${percent}%` }}
      >
        <span>{`${percent}%`}</span>
      </div>
    </Tooltip>
  </div>
)
Example #2
Source File: apiDelayPanel.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
apiDelayPanel = ({ data, reload }: { data: object; reload: Function }) => {
  const list = get(data, 'list');
  const columns = [
    {
      title: 'API Path',
      dataIndex: 'name',
      key: 'name',
      render: (value: string) => (
        <IF check={value.length > 30}>
          <Tooltip title={value}>
            <Copy copyText={value}>{`${value.substr(0, 30)}...`}</Copy>
          </Tooltip>
          <IF.ELSE />
          <Copy>{value}</Copy>
        </IF>
      ),
    },
    {
      title: i18n.t('msp:time-consuming(ms)'),
      dataIndex: 'time',
      key: 'time',
      render: (time: number) => round(time, 3),
    },
  ];

  // only show 10 pieces of data,not need pagination
  return <ErdaTable columns={columns} dataSource={list} pagination={false} onReload={reload} />;
}
Example #3
Source File: apiSizePanel.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
apiSizePanel = ({ data, reload }: { data: object; reload: Function }) => {
  const list = get(data, 'list');
  const columns = [
    {
      title: 'API Path',
      dataIndex: 'name',
      key: 'name',
      render: (value: string) => (
        <IF check={value.length > 30}>
          <Tooltip title={value}>
            <Copy copyText={value}>{`${value.substr(0, 30)}...`}</Copy>
          </Tooltip>
          <IF.ELSE />
          <Copy>{value}</Copy>
        </IF>
      ),
    },
    {
      title: i18n.t('msp:transport bytes(KBytes)'),
      dataIndex: 'size',
      key: 'size',
      render: (size: number) => round(size, 3),
    },
  ];
  // only show 10 pieces of data,not need pagination
  return <ErdaTable columns={columns} dataSource={list} pagination={false} onReload={reload} />;
}
Example #4
Source File: formatQCMissingData.ts    From nextclade with MIT License 6 votes vote down vote up
export function formatQCMissingData<TFunction extends TFunctionInterface>(
  t: TFunction,
  missingData?: DeepReadonly<QcResultMissingData>,
) {
  if (!missingData || missingData.status === QcStatus.good) {
    return undefined
  }

  const { score, totalMissing, missingDataThreshold, status } = missingData

  let message = t('Missing data found')
  if (status === QcStatus.bad) {
    message = t('Too much missing data found')
  }

  return t('{{message}}. Total Ns: {{total}} ({{allowed}} allowed). QC score: {{score}}', {
    message,
    total: totalMissing,
    allowed: missingDataThreshold,
    score: round(score),
  })
}
Example #5
Source File: formatQCMixedSites.ts    From nextclade with MIT License 6 votes vote down vote up
export function formatQCMixedSites<TFunction extends TFunctionInterface>(
  t: TFunction,
  mixedSites?: DeepReadonly<QcResultMixedSites>,
) {
  if (!mixedSites || mixedSites.status === QcStatus.good) {
    return undefined
  }

  const { score, totalMixedSites, mixedSitesThreshold, status } = mixedSites

  let message = t('Mixed sites found')
  if (status === QcStatus.bad) {
    message = t('Too many mixed sites found')
  }

  return t('{{message}}: total {{total}} ({{allowed}} allowed). QC score: {{score}}', {
    message,
    total: totalMixedSites,
    allowed: mixedSitesThreshold,
    score: round(score),
  })
}
Example #6
Source File: formatQCPrivateMutations.ts    From nextclade with MIT License 6 votes vote down vote up
export function formatQCPrivateMutations<TFunction extends TFunctionInterface>(
  t: TFunction,
  privateMutations?: DeepReadonly<QcResultPrivateMutations>,
) {
  if (!privateMutations || privateMutations.status === QcStatus.good) {
    return undefined
  }

  const {
    score,
    numReversionSubstitutions,
    numLabeledSubstitutions,
    numUnlabeledSubstitutions,
    totalDeletionRanges,
    weightedTotal,
  } = privateMutations

  return t(
    'QC score: {{score}}. ' +
      'Reverted substitutions: {{numReversionSubstitutions}}, ' +
      'Labeled substitutions: {{numLabeledSubstitutions}}, ' +
      'Unlabeled substitutions: {{numUnlabeledSubstitutions}}, ' +
      'Deletion ranges: {{totalDeletionRanges}}. ' +
      'Weighted total: {{weightedTotal}}',
    {
      score: round(score),
      numReversionSubstitutions,
      numLabeledSubstitutions,
      numUnlabeledSubstitutions,
      totalDeletionRanges,
      weightedTotal,
    },
  )
}
Example #7
Source File: formatQCSNPClusters.ts    From nextclade with MIT License 6 votes vote down vote up
export function formatQCSNPClusters<TFunction extends TFunctionInterface>(
  t: TFunction,
  snpClusters?: DeepReadonly<QcResultSnpClusters>,
) {
  if (!snpClusters || snpClusters.status === QcStatus.good) {
    return undefined
  }

  const { score, clusteredSNPs, totalSNPs, status } = snpClusters

  let message = t('Mutation clusters found')
  if (status === QcStatus.bad) {
    message = t('Too many mutation clusters found')
  }

  return t('{{message}}. Seen {{nClusters}} mutation clusters with total of {{total}} mutations. QC score: {{score}}', {
    message,
    total: totalSNPs,
    nClusters: clusteredSNPs.length,
    score: round(score),
  })
}
Example #8
Source File: index.tsx    From nebula-dashboard with Apache License 2.0 6 votes vote down vote up
updateChart = () => {
    const { data } = this.state;
    if (data.length > 0) {
      const total = last(data)!.count;
      const chartData = data.slice(0, data.length - 1).map(item => ({
        type: item.name,
        value: round(item.count / total, 2),
      }));
      this.chartInstance.data(chartData).render();
    }
  };
Example #9
Source File: index.tsx    From nebula-dashboard with Apache License 2.0 6 votes vote down vote up
updateChart = () => {
    const { data } = this.state;
    if (data.length > 0) {
      const total = sum(data.map(i => i.count));
      const chartData = data.map(item => ({
        type: item.name,
        value: round(item.count / total, 2),
      }));
      this.chartInstance.data(chartData).render();
    }
  };
Example #10
Source File: index.tsx    From nebula-dashboard with Apache License 2.0 6 votes vote down vote up
updateChart = () => {
    const { data } = this.props;
    const versionList = groupBy(data, 'version');
    const chartData = Object.keys(versionList).map(key => ({
      type: key,
      value: round(versionList[key].length / data.length, 2),
    }));
    this.chartInstance.data(chartData).render();
  };
Example #11
Source File: analyzer.ts    From CIAnalyzer with MIT License 5 votes vote down vote up
secRound = (sec: number) => {
  const PRECISION = 3
  return round(sec, PRECISION)
}
Example #12
Source File: machine-table.tsx    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
DoubleProgressItem = ({ usedPercent, requestPercent, usage, request, total, unit, unitType }: any) => {
  const requestEle = (
    <Tooltip
      key="request"
      placement="top"
      title={
        unitType
          ? `${i18n.t('cmp:allocation')}: ${getFormatter(unitType).format(request)} / ${getFormatter(unitType).format(
              total,
            )}`
          : `${i18n.t('cmp:allocation')}: ${round(request, 2)} ${unit} / ${round(total, 2)} ${unit}`
      }
    >
      <div
        className="machine-percent-bar machine-percent-bottom-bar cursor-pointer"
        style={{ width: `${requestPercent}%` }}
      >
        <span>{`${requestPercent}%`}</span>
      </div>
    </Tooltip>
  );
  const usedEle = (
    <Tooltip
      key="used"
      placement="top"
      title={
        unitType
          ? `${i18n.t('usage')}${getFormatter(unitType).format(usage)} / ${getFormatter(unitType).format(total)}`
          : `${i18n.t('usage')}${round(usage, 2)} ${unit} / ${round(total, 2)} ${unit}`
      }
    >
      <div
        className={classNames({
          'cursor-pointer': true,
          'machine-percent-bar': true,
          'machine-percent-top-bar': true,
          'machine-percent-error-bar': usedPercent >= requestPercent,
        })}
        style={{ width: `${usedPercent}%` }}
      >
        <span>{`${usedPercent}%`}</span>
      </div>
    </Tooltip>
  );
  return (
    <div className="machine-percent double-machine-percent">
      {usedPercent > requestPercent ? [usedEle, requestEle] : [requestEle, usedEle]}
    </div>
  );
}
Example #13
Source File: service-list.tsx    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
countPercent = (used: number, total: number) => {
  const percent = total && round((used / total) * 100, 2);
  const statusClass = compareClass(percent || 0);
  return { percent, statusClass };
}
Example #14
Source File: ListOfQcIsuues.tsx    From nextclade with MIT License 5 votes vote down vote up
export function ListOfQcIssues({ qc }: ListOfQcIssuesProps) {
  const { t } = useTranslation()
  const {
    overallScore,
    overallStatus,
    privateMutations,
    snpClusters,
    mixedSites,
    missingData,
    frameShifts,
    stopCodons,
  } = qc

  const rules = [
    { name: t('Missing Data'), shortName: 'N', value: missingData, message: formatQCMissingData(t, missingData) }, // prettier-ignore
    { name: t('Mixed Sites'), shortName: 'M', value: mixedSites, message: formatQCMixedSites(t, mixedSites) }, // prettier-ignore
    { name: t('Private Mutations'), shortName: 'P', value: privateMutations, message: formatQCPrivateMutations(t, privateMutations) }, // prettier-ignore
    { name: t('Mutation Clusters'), shortName: 'C', value: snpClusters, message: formatQCSNPClusters(t, snpClusters) }, // prettier-ignore
    { name: t('Frame shifts'), shortName: 'F', value: frameShifts, message: formatQCFrameShifts(t, frameShifts) }, // prettier-ignore
    { name: t('Stop codons'), shortName: 'S', value: stopCodons, message: formatQCStopCodons(t, stopCodons) }, // prettier-ignore
  ].filter((value) => notUndefined(value))

  const issues = rules.map(({ name, shortName, value, message }) => {
    if (!value) {
      return undefined
    }

    return (
      <QcListItem key={name} status={value.status} text={shortName}>
        <div>
          <span>
            <b>{name}</b>
          </span>
          <span>{': '}</span>
          <span>{value.status}</span>
        </div>
        {message ?? t('No issues')}
      </QcListItem>
    )
  })

  return (
    <>
      <div>{t('Overall QC score: {{score}}', { score: round(overallScore) })}</div>
      <div>{t('Overall QC status: {{status}}', { status: overallStatus })}</div>
      <div>
        {t('Detailed QC assessment:')}
        <QcList>{issues}</QcList>
      </div>
    </>
  )
}
Example #15
Source File: index.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
ClusterDashboard = () => {
  const [filterGroup, groupInfos, unGroupInfo, clusterList, selectedGroups] = clusterDashboardStore.useStore((s) => [
    s.filterGroup,
    s.groupInfos,
    s.unGroupInfo,
    s.clusterList,
    s.selectedGroups,
  ]);
  const { getFilterTypes, getGroupInfos } = clusterDashboardStore.effects;
  const { setSelectedGroups, clearClusterList } = clusterDashboardStore.reducers;
  const [loading] = useLoading(clusterDashboardStore, ['getGroupInfos']);

  const [groupContainerWidthHolder, groupContainerWidth] = useComponentWidth();
  const [machineContainerWidthHolder, machineContainerWidth] = useComponentWidth();
  const [groupGridClass, setGroupGridClass] = useState();
  const [machineGridClass, setMachineGridClass] = useState('machine-list-ct-g4');
  const [selectedFilters, setSelectedFilters] = useState();
  const [selectedColour, setSelectedColour] = useState('load');
  const [activeMachine, setActiveMachine] = useState({ ip: '', clusterName: '' });
  const [activeMachineTab, setActiveMachineTab] = useState();
  const [activeGroup, setActiveGroup] = useState('');
  const [groupMap, setGroupMap] = useState({});
  const [allMachines, setAllMachines] = useState();
  const [activeMachineList, setActiveMachineList] = useState();
  const [colorMark, setColorMark] = React.useState('load');
  const [isClickState, setIsClickState] = React.useState(false);
  const [isMounted, setIsMounted] = React.useState(false);

  useMount(async () => {
    await getFilterTypes();
    setIsMounted(true);
  });

  useUnmount(() => {
    setSelectedGroups([]);
    clearClusterList();
  });

  useEffect(() => {
    if (!isEmpty(clusterList)) {
      getGroupInfos({
        groups: selectedGroups,
        clusters: map(clusterList, (cl: any) => ({ clusterName: cl.name })),
        filters: getFilters(selectedFilters),
      });
    }
  }, [clusterList, getGroupInfos, selectedFilters, selectedGroups]);

  const unitGroups = React.useMemo(() => {
    const tempList = [...selectedGroups, '', ''].slice(0, 2);
    return tempList.map((item) => UNIT_MAP[item] || '');
  }, [selectedGroups]);

  useEffect(() => {
    const groupInfoMap = reduce(
      groupInfos,
      (result, item) => {
        let subResult = {};
        if (item.groups) {
          subResult = reduce(
            item.groups,
            (acc, subItem) => ({
              ...acc,
              [`${item.name + unitGroups[0]}-${subItem.name + unitGroups[1]}`]: subItem,
            }),
            {},
          );
        }

        return {
          ...result,
          ...subResult,
          [item.name + unitGroups[0]]: item,
        };
      },
      {},
    );
    setGroupMap(groupInfoMap);
  }, [groupInfos, unitGroups]);

  useEffect(() => {
    if (isEmpty(groupInfos)) {
      setAllMachines(unGroupInfo.machines || []);
    } else {
      let results: any[] = [];
      const getAllMachines = (groups: ORG_DASHBOARD.IGroupInfo[]) => {
        forEach(groups, ({ groups: subGroups, machines }: ORG_DASHBOARD.IGroupInfo) => {
          if (isEmpty(subGroups)) {
            results = [...results, ...(machines || [])];
          } else {
            getAllMachines(subGroups || []);
          }
        });
      };

      getAllMachines(groupInfos);
      setAllMachines(results);
    }
  }, [unGroupInfo.machines, groupInfos]);

  useEffect(() => {
    if (!activeGroup) {
      return setActiveMachineList(allMachines);
    }

    let machineList: any[] = [];
    if (isEmpty(groupMap[activeGroup])) {
      setActiveMachineList(machineList);
      return;
    }
    const { groups, machines } = groupMap[activeGroup];
    if (!isEmpty(groups)) {
      machineList = reduce(groups, (result, { machines: subMachines }) => [...result, ...subMachines], []);
    } else {
      machineList = machines || [];
    }
    setActiveMachineList(machineList);
  }, [activeGroup, allMachines, groupMap]);

  useEffect(() => {
    if (groupContainerWidth !== Infinity) {
      setGroupGridClass(getGroupGridClass(groupContainerWidth as number));
    }
  }, [groupContainerWidth]);

  useEffect(() => {
    if (machineContainerWidth !== Infinity) {
      setMachineGridClass(getMachineGridClass(machineContainerWidth as number));
    }
  }, [machineContainerWidth]);

  const getFilters = (filters: string[]) => {
    const filterMap = {};
    forEach(filters, (item) => {
      const [key, value] = item.split(':');
      filterMap[key] = filterMap[key] ? [...filterMap[key], value] : [value];
    });
    return map(filterMap, (values, key) => ({ key, values }));
  };

  const getMachineColourValue = ({
    cpuUsage,
    cpuAllocatable,
    memUsage,
    memAllocatable,
    diskUsage,
    diskTotal,
    cpuRequest,
    memRequest,
    load5,
  }: any) => {
    const getPercent = (used: number, total: number) => round((used / total) * 100, 2);
    const machineColourNameMap = {
      load: {
        name: COLOUR_MAP.load,
        value: load5,
      },
      cpu: {
        name: COLOUR_MAP.cpu,
        value: getPercent(cpuUsage, cpuAllocatable),
      },
      mem: {
        name: COLOUR_MAP.mem,
        value: getPercent(memUsage, memAllocatable),
      },
      disk: {
        name: COLOUR_MAP.disk,
        value: getPercent(diskUsage, diskTotal),
      },
      scheduledCPU: {
        name: COLOUR_MAP.scheduledCPU,
        value: getPercent(cpuRequest, cpuAllocatable),
      },
      scheduledMEM: {
        name: COLOUR_MAP.scheduledMEM,
        value: getPercent(memRequest, memAllocatable),
      },
    };
    return machineColourNameMap[selectedColour];
  };

  const getMachineColourClass = (machineInfo: Partial<ORG_MACHINE.IMachine>) =>
    getDegreeColourClass(getMachineColourValue(machineInfo).value, selectedColour);

  const handleChangeGroups = (groups: string[]) => {
    if (groups.length > 2) {
      message.warning(i18n.t('cmp:up to 2 optional groups'));
      return;
    }
    setActiveGroup('');
    setSelectedGroups(groups);
  };

  const handleChangeFilters = (filters: string[]) => {
    setSelectedFilters(filters);
  };

  const handleActiveMachine = (record: any, key?: string) => {
    setActiveMachine(record);
    setActiveMachineTab(key);
  };

  const getMachineGroupContent = (item: ORG_DASHBOARD.IGroupInfo) => {
    if (isEmpty(item)) return null;

    const { name, displayName, machines, metric, groups, clusterStatus }: ORG_DASHBOARD.IGroupInfo = item;
    const groupName = displayName || name;
    const activeGroupItem = clusterList.find((c) => c.name === name);
    const activeGroupDisplayName = activeGroupItem?.displayName || activeGroupItem?.name;

    const {
      machines: machineNum,
      cpuUsage,
      cpuAllocatable,
      memUsage,
      memAllocatable,
      diskUsage,
      diskTotal,
    } = metric || {};

    const isClusterGroup = selectedGroups?.[0] === 'cluster';

    return (
      <Holder when={isEmpty(machines) && isEmpty(groups)}>
        <IF check={selectedGroups.length}>
          <div className="group-header flex justify-between items-center">
            <h3
              className={`group-title ${isClusterGroup ? 'cluster-group' : ''}`}
              onClick={() => {
                isClusterGroup && goTo(goTo.pages.cmpClustersDetail, { clusterName: name });
              }}
            >
              {activeGroupDisplayName || groupName + unitGroups[0]}
            </h3>
            <IF check={activeGroup}>
              <span className="group-unactived-op hover-active">
                <CustomIcon type="shink" />
              </span>
              <IF.ELSE />
              <span className="group-actived-op hover-active">
                <CustomIcon type="grow" />
              </span>
            </IF>
          </div>
        </IF>
        <p className="group-info">
          <span>
            {i18n.t('Machines')}:{machineNum}
          </span>
          <span>CPU:{round((cpuUsage / cpuAllocatable) * 100, 2)}%</span>
          <span>
            {i18n.t('memory')}:{round((memUsage / memAllocatable) * 100, 2)}%
          </span>
          <span>
            {i18n.t('Disk')}:{round((diskUsage / diskTotal) * 100, 2)}%
          </span>
        </p>
        <IF check={!isEmpty(groups)}>
          <SubMachineGroup
            groups={groups}
            isClusterGroup={selectedGroups?.[1] === 'cluster'}
            unitGroups={unitGroups}
            groupName={groupName}
            activeMachine={activeMachine}
            setActiveMachine={setActiveMachine}
            setActiveGroup={setActiveGroup}
            getMachineColourClass={getMachineColourClass}
            getMachineColourValue={getMachineColourValue}
          />
          <IF.ELSE />
          <div
            className={classnames({
              'machine-list-ct': true,
              [`${machineGridClass}`]: true,
              'machine-actived': !!activeMachine.ip,
            })}
          >
            {map(machines, ({ ip, clusterName, ...rest }) => {
              const { name: colourName, value: colourValue } = getMachineColourValue(rest);
              return (
                <Tooltip
                  placement="bottom"
                  title={`${ip} (${colourName}: ${colourValue}%)`}
                  key={`${clusterName}-${ip}`}
                >
                  <div
                    className={classnames({
                      'machine-item': true,
                      'hover-active': true,
                      [`${getMachineColourClass(rest)}`]: true,
                      active: ip === activeMachine.ip && clusterName === activeMachine.clusterName,
                    })}
                    onClick={(e) => {
                      e.stopPropagation();
                      setActiveMachine({ ip, clusterName, ...rest });
                    }}
                  >
                    <span
                      className="cancel-active"
                      onClick={(e) => {
                        e.stopPropagation();
                        setActiveMachine({});
                      }}
                    >
                      <CustomIcon type="gb" />
                    </span>
                  </div>
                </Tooltip>
              );
            })}
          </div>
        </IF>
      </Holder>
    );
  };

  const getMachineGroupWrapper = (item?: ORG_DASHBOARD.IGroupInfo, gridClass?: any) => {
    return (
      <div
        key={item && item.name ? item.name : ''}
        className={classnames({
          'machine-group': true,
          'actived-machine-group': !!activeGroup,
          'no-machine-group': isEmpty(selectedGroups) || isEmpty(item),
        })}
        onClick={(e) => {
          if (isEmpty(selectedGroups) || isEmpty(item)) return;
          e.stopPropagation();
          setIsClickState(!isClickState);
          setActiveGroup(activeGroup ? '' : (item && item.name ? item.name : '') + unitGroups[0]);
        }}
      >
        <Holder when={isEmpty(item) || !gridClass}>{getMachineGroupContent(item)}</Holder>
        {machineContainerWidthHolder}
      </div>
    );
  };

  const Bottom = React.useMemo(
    () => (
      <IF check={activeMachine.ip}>
        <div className="content-title mb-2">{activeMachine.ip}</div>
        <MachineTabs activeMachine={activeMachine} activeMachineTab={activeMachineTab} />
        <IF.ELSE />
        <GroupTabs
          activedGroup={activeGroup}
          machineList={activeMachineList}
          onActiveMachine={handleActiveMachine}
          isClickState={isClickState}
        />
      </IF>
    ),
    [activeMachine, activeMachineList, activeMachineTab, activeGroup, isClickState],
  );

  const handleOptionMouseEnter = (value: string, e: React.MouseEvent<HTMLDivElement>) => {
    e.stopPropagation();
    setColorMark(value);
  };

  const handleSelectedColour = (val: string) => {
    setSelectedColour(val);
    setColorMark(val);
  };

  return (
    <>
      {groupContainerWidthHolder}
      <Choose>
        <When condition={!clusterList.length && !loading && isMounted}>
          <div className="flex flex-col justify-center items-center h-full">
            <div className="font-medium text-2xl mb-2">{i18n.t('cmp:quick start')}</div>
            <div className="text-desc">
              {interpolationComp(
                i18n.t(
                  'cmp:no cluster currently exists, you can click <CreateClusterLink />, you can also view <DocumentationHref /> to learn more',
                ),
                {
                  CreateClusterLink: (
                    <Link to={`${goTo.resolve.cmpClusters()}?autoOpen=true`}>{i18n.t('cmp:create cluster')}</Link>
                  ),
                  DocumentationHref: (
                    <a href={DOC_CMP_CLUSTER_CREATE} target="__blank">
                      {i18n.t('documentation')}
                    </a>
                  ),
                },
              )}
            </div>
            <img className="w-80 h-80" src={noClusterPng} />
          </div>
        </When>
        <Otherwise>
          <div className="cluster-dashboard-top mb-4">
            <div className="filter-group-ct mb-4">
              <Row gutter={20}>
                <Col span={8} className="filter-item flex justify-between items-center">
                  <div className="filter-item-label">{i18n.t('Group')}</div>
                  <Select
                    value={selectedGroups}
                    placeholder={firstCharToUpper(i18n.t('cmp:no more than 2 groups'))}
                    className="filter-item-content"
                    style={{ width: '100%' }}
                    showArrow
                    allowClear
                    mode="multiple"
                    onChange={handleChangeGroups}
                  >
                    {map(
                      filter(filterGroup, ({ key: group }) => ['cpus', 'mem', 'cluster'].includes(group)),
                      ({ key, name }) => (
                        <Option key={key}>{name}</Option>
                      ),
                    )}
                  </Select>
                </Col>
                <Col span={8} className="filter-item flex justify-between items-center">
                  <div className="filter-item-label">{i18n.t('Filter')}</div>
                  <TreeSelect
                    className="filter-item-content"
                    style={{ width: '100%' }}
                    value={selectedFilters}
                    dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
                    allowClear
                    multiple
                    // treeDefaultExpandAll
                    placeholder={firstCharToUpper(i18n.t('cmp:input to search'))}
                    onChange={handleChangeFilters}
                  >
                    {map(filterGroup, ({ name, key, values, unit, prefix }: ORG_DASHBOARD.IFilterType) => (
                      <TreeNode className="filter-item-node" title={`${name}(${key})`} key={key} value={key} disabled>
                        {map(values, (subItem: any) => (
                          <TreeNode
                            value={`${key}:${subItem}`}
                            title={`${prefix ? `${prefix}:` : ''}${subItem} ${unit || ''}`}
                            key={`${key}-${subItem}`}
                          />
                        ))}
                      </TreeNode>
                    ))}
                  </TreeSelect>
                </Col>
                <Col span={8} className="filter-item flex justify-between items-center">
                  <div className="filter-item-label">{i18n.t('Colour')}</div>
                  <Select
                    className="filter-item-content"
                    style={{ width: '100%' }}
                    value={selectedColour}
                    onChange={handleSelectedColour}
                    dropdownRender={(menu) => {
                      return (
                        <div className="colour-select-dropdown">
                          <div className="menu">{menu}</div>
                          <div className="comments">
                            <ul className="colour-comment-list">
                              {map(DEGREE_COLOUR_MAP[colorMark], (value, color) => (
                                <li className="colour-comment-item flex justify-between items-center" key={color}>
                                  <span className="colour-comment-value">{value.text}</span>
                                  <div className={`color-block ${color}`} />
                                </li>
                              ))}
                            </ul>
                          </div>
                        </div>
                      );
                    }}
                  >
                    {map(COLOUR_MAP, (name, key) => (
                      <Option value={key} key={key}>
                        <div
                          onMouseEnter={(e) => {
                            handleOptionMouseEnter(key, e);
                          }}
                        >
                          {name}
                        </div>
                      </Option>
                    ))}
                  </Select>
                </Col>
              </Row>
            </div>
            <Spin spinning={!groupGridClass || loading}>
              <Choose>
                <When condition={isEmpty(selectedGroups) || isEmpty(groupInfos)}>
                  <div className="machine-group-ct machine-group-ct-g1">
                    {getMachineGroupWrapper(unGroupInfo, groupGridClass)}
                  </div>
                </When>
                <Otherwise>
                  <div className={`machine-group-ct ${activeGroup ? 'machine-group-ct-g1' : groupGridClass}`}>
                    <Choose>
                      <When condition={!!activeGroup}>
                        {getMachineGroupWrapper(groupMap[activeGroup], groupGridClass)}
                      </When>
                      <When condition={!groupGridClass}>{getMachineGroupWrapper()}</When>
                      <Otherwise>{map(groupInfos, (item) => getMachineGroupWrapper(item, true))}</Otherwise>
                    </Choose>
                  </div>
                </Otherwise>
              </Choose>
            </Spin>
          </div>
          <div className="cluster-dashboard-bottom">{Bottom}</div>
        </Otherwise>
      </Choose>
    </>
  );
}
Example #16
Source File: service-list.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
function ServiceList({
  containerList,
  serviceList,
  depth,
  into,
  haveMetrics,
  onReload,
  haveHost = true,
  haveStatus = true,
  slot,
  extraQuery,
}: IProps) {
  const [renderOp, drawer] = useInstanceOperation<IInstance>({
    log: true,
    console: true,
    monitor: true,
    getProps(type, record) {
      return {
        console: {
          host: record.host_private_addr || record.host,
          clusterName: extraQuery.filter_cluster_name,
        },
        log: {
          fetchApi: '/api/orgCenter/logs',
          extraQuery: { clusterName: record.clusterName },
        },
        monitor: {
          api: '/api/orgCenter/metrics',
          extraQuery,
        },
      }[type];
    },
  });

  let list = [];
  let cols = [];
  if (depth && depth < 5) {
    list = serviceList;
    // 设一个id作为rocord的key,避免切换rowKey时新数据还没拿到引起rowKey和record的key不一致
    list.forEach((item: any, i: number) => {
      set(item, 'id', item.id || item.name || i);
    });
    const titleMap = ['', 'PROJECT', 'APPLICATION', 'RUNTIME', 'SERVICE', 'CONTAINER'];
    const titleCnMap = {
      '': '',
      PROJECT: i18n.t('project'),
      APPLICATION: i18n.t('application'),
      RUNTIME: i18n.t('App instance'),
      SERVICE: i18n.t('Microservice'),
      CONTAINER: 'CONTAINER',
    };
    const iconMap = ['', 'project', 'wenjianjia', 'fengchao', 'atom'];

    cols = [
      {
        title: titleCnMap[titleMap[depth]],
        dataIndex: 'id',
        key: 'id',
        // width: 320,
        render: (text: string, record: any) => (
          <span className="font-bold hover-table-text" onClick={() => into({ q: text, name: record.name })}>
            <CustomIcon type={iconMap[depth]} />
            {record.name}
          </span>
        ),
      },
      {
        title: i18n.t('Number of instances'),
        dataIndex: 'instance',
        key: 'instance',
        width: 176,
      },
      {
        title: 'CPU',
        dataIndex: 'cpu',
        key: 'cpu',
        width: 125,
        sorter: (a: any, b: any) => {
          if (!haveMetrics) return Number(a.cpu) - Number(b.cpu);
          const use_a = getMetricsInfo(a, 'cpuUsagePercent') || 0;
          const use_b = getMetricsInfo(b, 'cpuUsagePercent') || 0;
          return Number(use_a / a.cpu) - Number(use_b / b.cpu);
        },
        render: (total: number, record: any) => {
          if (!haveMetrics) return `${total} ${i18n.t('core')}`;
          const parsedTotal = total * 1000;
          const used = +(getMetricsInfo(record, 'cpuUsagePercent') || 0).toFixed(4) * 1000;
          const { percent, statusClass } = countPercent(used, parsedTotal);
          return ProgressItem(percent, round(used, 2), parsedTotal, i18n.t('millicore'), statusClass);
        },
      },
      {
        title: i18n.t('memory'),
        dataIndex: 'memory',
        key: 'memory',
        width: 125,
        sorter: (a: any, b: any) => {
          if (!haveMetrics) return Number(a.memory) - Number(b.memory);
          const use_a = getMetricsInfo(a, 'memUsage') || 0;
          const use_b = getMetricsInfo(b, 'memUsage') || 0;
          return Number(use_a / 1048576 / a.memory) - Number(use_b / 1048576 / b.memory);
        },
        render: (total: number, record: any) => {
          if (!haveMetrics) return `${total} MB`;
          const used = +((getMetricsInfo(record, 'memUsage') || 0) / 1048576).toFixed(2);
          const { percent, statusClass } = countPercent(used, total);
          return ProgressItem(percent, used, total, 'MB', statusClass);
        },
      },
      {
        title: i18n.t('Disk'),
        dataIndex: 'disk',
        key: 'disk',
        width: 125,
        sorter: (a: any, b: any) => Number(a.disk) - Number(b.disk),
        render: (size: string) => getFormatter('STORAGE', 'MB').format(size),
      },
      {
        title: i18n.t('Status'),
        dataIndex: 'unhealthy',
        width: 100,
        align: 'center',
        render: (num: number) => (
          <span>
            <IF check={!!num}>
              <span>
                <Badge status="error" /> {num}
              </span>
              <IF.ELSE />
              <Badge status="success" />
            </IF>
          </span>
        ),
      },
    ];
    // if (includes(['RUNTIME', 'SERVICE'], titleMap[depth])) { // runtime和service级别,需要展示状态
    //   (cols as any[]).push({
    //     title: '状态',
    //     dataIndex: 'status',
    //     width: 100,
    //     align: 'center',
    //     render: (text: string) => {
    //       const stateObj = get(statusConfig, `${titleMap[depth]}.${text}`) || statusConfig.Unknown;
    //       return <Tooltip title={text || 'Unknown'}><Badge status={stateObj.state} /></Tooltip>;
    //     },
    //   });
    // }
  } else {
    list = containerList;
    cols = [
      {
        title: 'IP',
        key: 'ip_addr',
        width: 120,
        sorter: (a: any, b: any) =>
          Number((a.ip_addr || a.ipAddress || '').replace(/\./g, '')) -
          Number((b.ip_addr || b.ipAddress || '').replace(/\./g, '')),
        render: (record: any) => record.ip_addr || record.ipAddress || i18n.t('cmp:no ip address'),
      },
      haveHost
        ? {
            title: i18n.t('cmp:Host address'),
            key: 'host_private_addr',
            width: 120,
            render: (record: any) => record.host_private_addr || record.host || i18n.t('cmp:no host address'),
          }
        : null,
      {
        title: i18n.t('Image'),
        key: 'image',
        // width: 400,
        className: 'item-image',
        render: (record: any) => {
          const text = record.image_name || record.image;
          if (!text) {
            return null;
          }
          return (
            <Tooltip title={`${i18n.t('click to copy')}:${text}`} overlayClassName="tooltip-word-break">
              <span
                className="image-name for-copy-image w-[400px]"
                data-clipboard-tip={i18n.t('Image name')}
                data-clipboard-text={text}
              >
                {getImageText(text)}
              </span>
            </Tooltip>
          );
        },
      },
      {
        title: 'CPU',
        dataIndex: 'cpu',
        key: 'cpu',
        width: 120,
        sorter: (a: any, b: any) => {
          if (!haveMetrics) return Number(a.cpu) - Number(b.cpu);
          const use_a = getMetricsInfo(a, 'cpuUsagePercent') || 0;
          const use_b = getMetricsInfo(b, 'cpuUsagePercent') || 0;
          return Number(use_a / a.cpu) - Number(use_b / b.cpu);
        },
        render: (total: number, record: any) => {
          if (!haveMetrics) return total;
          const parsedTotal = total * 1000;
          const used = +(getMetricsInfo(record, 'cpuUsagePercent') || 0).toFixed(4) * 1000;
          const { percent, statusClass } = countPercent(used, parsedTotal);
          return ProgressItem(percent, round(used, 2), parsedTotal, i18n.t('millicore'), statusClass);
        },
      },
      {
        title: i18n.t('memory'),
        dataIndex: 'memory',
        key: 'memory',
        width: 120,
        sorter: (a: any, b: any) => {
          if (!haveMetrics) return Number(a.memory) - Number(b.memory);
          const use_a = getMetricsInfo(a, 'memUsage') || 0;
          const use_b = getMetricsInfo(b, 'memUsage') || 0;
          return Number(use_a / 1048576 / a.memory) - Number(use_b / 1048576 / b.memory);
        },
        render: (total: number, record: any) => {
          if (!haveMetrics) return getFormatter('STORAGE', 'MB').format(total);
          const used = getMetricsInfo(record, 'memUsage') || 0;
          const { percent, statusClass } = countPercent(used, total);
          return ProgressItem(percent, used, total, '', statusClass);
        },
      },
      {
        title: i18n.t('Disk'),
        dataIndex: 'disk',
        key: 'disk',
        width: 120,
        sorter: (a: any, b: any) => Number(a.disk) - Number(b.disk),
        render: (size: number) => getFormatter('STORAGE', 'MB').format(size),
      },
      // TODO: 集群组件目前无状态,3.5暂时去除,后续提供后打开
      haveStatus
        ? {
            title: i18n.t('Status'),
            dataIndex: 'status',
            key: 'status',
            width: 100,
            align: 'center',
            render: (text: string) => {
              const stateObj = get(statusConfig, `CONTAINER.${text}`) || statusConfig.Unknown;
              return (
                <Tooltip title={text || 'Unknown'}>
                  <Badge status={stateObj.state} />
                </Tooltip>
              );
            },
          }
        : null,
      {
        title: i18n.t('operations'),
        key: 'operation',
        width: 180,
        render: renderOp,
      },
    ];
    if (depth === 3) {
      cols.splice(2, {
        title: i18n.t('Number of instances'),
        dataIndex: 'instance',
        key: 'instance',
      } as any);
    }
  }
  remove(cols as any, (item) => item === null);

  return (
    <div className="service-table">
      <ErdaTable
        slot={slot}
        rowKey={(record: any, i: number) => `${i}${record.id}`}
        pagination={false}
        columns={cols as Array<ColumnProps<any>>}
        dataSource={list}
        onReload={onReload}
        scroll={{ x: 1100 }}
      />
      <Copy selector=".for-copy-image" />
      {drawer}
    </div>
  );
}