@ant-design/icons#ExclamationCircleOutlined TypeScript Examples

The following examples show how to use @ant-design/icons#ExclamationCircleOutlined. 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: utils.tsx    From posthog-foss with MIT License 7 votes vote down vote up
export function successToast(title?: string, message?: string): void {
    /**
     * Shows a standardized success message.
     * @param title Title message of the toast
     * @param message Body message on the toast
     */
    setTimeout(
        () =>
            toast.success(
                <div data-attr="success-toast">
                    <h1>
                        <ExclamationCircleOutlined /> {title || 'Success!'}
                    </h1>
                    <p>{message || 'Your action was completed successfully.'}</p>
                </div>
            ),
        100
    )
}
Example #2
Source File: App.tsx    From pcap2socks-gui with MIT License 6 votes vote down vote up
stopConfirm = () => {
    notification.close("network");

    const stop = this.stop;
    confirm({
      title: "停止运行",
      icon: <ExclamationCircleOutlined />,
      content: "停止运行可能会导致游戏掉线,确认要继续操作吗?",
      onOk() {
        return stop();
      },
      onCancel() { },
    });
  };
Example #3
Source File: App.tsx    From jitsu with MIT License 6 votes vote down vote up
RouteNotFound: React.FC = () => {
  useEffect(() => {
    currentPageHeaderStore.setBreadcrumbs("Not found")
  })
  return (
    <div className="flex justify-center pt-12">
      <Card bordered={false}>
        <Card.Meta
          description={
            <>
              This page does not exist. If you got here by clicking a link within Jitsu interface, try to contact us:{" "}
              <Typography.Paragraph copyable={{ tooltips: false }} className="inline">
                {"[email protected]"}
              </Typography.Paragraph>
            </>
          }
          avatar={
            <span className="text-error">
              <ExclamationCircleOutlined />
            </span>
          }
          title={"Page Not Found"}
        />
      </Card>
    </div>
  )
}
Example #4
Source File: BasicLayout.tsx    From react_admin with MIT License 6 votes vote down vote up
BasicLayout: React.FC<Iprops> = (props) => {
  const [collapsed, setCollapsed] = useState(false);
  const onLogout = () => {
    confirm({
      title: "你确定要退出登录吗?",
      icon: <ExclamationCircleOutlined />,
      onOk() {
        localStorage.removeItem("userinfo");
        console.log(props.history.replace("/"));
      },
      onCancel() {},
    });
  };

  return (
    <Layout hasSider className="page-basic">
      <MenuDefault collapsed={collapsed} />
      <Layout className="site-layout">
        <Header
          onLogout={onLogout}
          setColl={(coll) => setCollapsed(coll)}
          collapsed={collapsed}
        />
        <Bread />
        <Content
          className="site-layout-background"
          style={{
            margin: "24px 16px",
            padding: 24,
            minHeight: 280,
          }}
        >
          <Routers location={props.location} />
        </Content>
        <Footer />
      </Layout>
    </Layout>
  );
}
Example #5
Source File: index.tsx    From metaplex with Apache License 2.0 6 votes vote down vote up
TransactionErrorModal = ({
  error = '',
  onDismiss,
  open,
  modalView = true,
}: ITransactionErrorModal) => {
  const modalBody = (
    <div className="error-modal-content">
      <div className="warning-icon">
        <ExclamationCircleOutlined width={20} height={20} />
      </div>
      <h4>Transaction error</h4>
      <div className="error-text">
        Your transaction was not completed for{' '}
        {error ? error : 'an unknown reason. Please try again.'}
      </div>
      <Button onClick={onDismiss}>Dismiss</Button>
    </div>
  );
  if (modalView) {
    return (
      <Modal
        className="transaction-error-modal"
        centered
        width={500}
        mask={false}
        visible={open}
        onCancel={onDismiss}
        footer={null}
        closable
      >
        {modalBody}
      </Modal>
    );
  }
  return <div className="create-error-modal">{modalBody}</div>;
}
Example #6
Source File: index.tsx    From fe-v5 with Apache License 2.0 6 votes vote down vote up
export function deleteAlertEventsModal(busiId, ids: number[], onSuccess = () => {}) {
  confirm({
    title: '删除告警事件',
    icon: <ExclamationCircleOutlined />,
    content: '通常只有在确定监控数据永远不再上报的情况下(比如调整了监控数据标签,或者机器下线)才删除告警事件,因为相关告警事件永远无法自动恢复了,您确定要这么做吗?',
    okText: '确认删除',
    maskClosable: true,
    okButtonProps: { danger: true },
    zIndex: 1001,
    onOk() {
      return deleteAlertEvents(busiId, ids).then((res) => {
        message.success('删除成功');
        onSuccess();
      });
    },
    onCancel() {},
  });
}
Example #7
Source File: algo-node.tsx    From XFlow with MIT License 6 votes vote down vote up
AlgoIcon: React.FC<IProps> = props => {
  if (props.hide) {
    return null
  }
  switch (props.status) {
    case NsGraphStatusCommand.StatusEnum.PROCESSING:
      return <RedoOutlined spin style={{ color: '#c1cdf7', fontSize: '16px' }} />
    case NsGraphStatusCommand.StatusEnum.ERROR:
      return <CloseCircleOutlined style={{ color: '#ff4d4f', fontSize: '16px' }} />
    case NsGraphStatusCommand.StatusEnum.SUCCESS:
      return <CheckCircleOutlined style={{ color: '#39ca74cc', fontSize: '16px' }} />
    case NsGraphStatusCommand.StatusEnum.WARNING:
      return <ExclamationCircleOutlined style={{ color: '#faad14', fontSize: '16px' }} />
    case NsGraphStatusCommand.StatusEnum.DEFAULT:
      return <InfoCircleOutlined style={{ color: '#d9d9d9', fontSize: '16px' }} />
    default:
      return null
  }
}
Example #8
Source File: index.tsx    From dnde with GNU General Public License v3.0 6 votes vote down vote up
modalConfirmLoadLocalState = async (okCallback: () => void, cancelCallback: () => void) => {
  return new Promise<boolean>((resolve, reject) => {
    Modal.confirm({
      title: 'Confirm',
      icon: <ExclamationCircleOutlined />,
      content: 'local save found do you want to load it?',
      okText: 'restore',
      cancelText: 'cancel',
      onOk: () => {
        okCallback();
        resolve(true);
      },
      onCancel: () => {
        cancelCallback();
        resolve(false);
      },
    });
  });
}
Example #9
Source File: Invites.tsx    From posthog-foss with MIT License 6 votes vote down vote up
function makeActionsComponent(
    deleteInvite: (invite: OrganizationInviteType) => void
): (_: any, invite: any) => JSX.Element {
    return function ActionsComponent(_, invite: OrganizationInviteType): JSX.Element {
        return (
            <LemonButton
                title="Cancel the invite"
                compact
                data-attr="invite-delete"
                icon={<IconClose />}
                status="danger"
                onClick={() => {
                    invite.is_expired
                        ? deleteInvite(invite)
                        : Modal.confirm({
                              title: `Do you want to cancel the invite for ${invite.target_email}?`,
                              icon: <ExclamationCircleOutlined />,
                              okText: 'Yes, cancel invite',
                              okType: 'danger',
                              onOk() {
                                  deleteInvite(invite)
                              },
                              cancelText: 'No, keep invite',
                          })
                }}
            />
        )
    }
}
Example #10
Source File: index.tsx    From amiya with MIT License 6 votes vote down vote up
/**
 * 注册【批量删除】事件
 */
registerAction('batch-delete', (props, _record, searchTable) => {
  return {
    icon: <DeleteOutlined />,
    tableFooterExtraOnly: true,
    onClick: () => {
      let selection = searchTable?.selection || []
      if (!selection.length) {
        info(locale.action.noSelection)
        return
      }
      if (searchTable?.deleteApi) {
        Modal.confirm({
          title: locale.action.deleteConfirmTitle,
          content: `${locale.action.deleteConfirmBefore} ${selection.length} ${locale.action.deleteConfirmAfter}`,
          icon: <ExclamationCircleOutlined />,
          onOk: () => {
            let params: Array<string> = selection.map((row: any) => getKey(row, searchTable?.rowKey))
            searchTable?.deleteApi(params).then((data: any) => {
              success(props.successMsg || locale.action.deleteConfirmBatchSuccess)
              searchTable?.clearSelection()
              searchTable?.tableRef.current.refresh()
              // 请求完成回调
              if (props.onFinish) {
                props.onFinish({ data, params })
              }
            })
          }
        })
      }
    },
    ...props
  }
})
Example #11
Source File: PersonalAPIKeys.tsx    From posthog-foss with MIT License 6 votes vote down vote up
function RowActionsCreator(
    deleteKey: (key: PersonalAPIKeyType) => void
): (personalAPIKey: PersonalAPIKeyType) => JSX.Element {
    return function RowActions(personalAPIKey: PersonalAPIKeyType) {
        return (
            <Popconfirm
                title={`Permanently delete key "${personalAPIKey.label}"?`}
                okText="Delete Key"
                okType="danger"
                icon={<ExclamationCircleOutlined style={{ color: red.primary }} />}
                placement="left"
                onConfirm={() => {
                    deleteKey(personalAPIKey)
                }}
            >
                <a className="text-danger">Delete</a>
            </Popconfirm>
        )
    }
}
Example #12
Source File: component.tsx    From visual-layout with MIT License 5 votes vote down vote up
Component: React.FC<{ page: PageService }> = ({ page }) => {
  const [codeConfig, setCodeConfig] = useState(DEFAULT.codeConfig);
  const [name, setName] = useState(DEFAULT.codeConfig.componentName);

  useEffect(() => {
    if (page) {
      setCodeConfig(page.currentNode[0]?.codeConfig);
      setName(page.currentNode[0]?.codeConfig.componentName);
    }
    // eslint-disable-next-line
  }, [page?.currentNode, page?.currentNode[0]?.codeConfig]);

  const updateIsComponent = (value: boolean) => {
    if (value !== codeConfig.isComponent) {
      page.setCodeConfig({
        ...codeConfig,
        isComponent: value,
      });
    }
  };

  const updateComponentName = (e: React.FocusEvent<HTMLInputElement>) => {
    if (e.target.value !== codeConfig.componentName) {
      page.setCodeConfig({
        ...codeConfig,
        componentName: e.target.value,
      });
    }
  };

  return (
    <div className={styles.container}>
      <div>
        组件
        <Tooltip placement="topLeft" title="生成独立代码文件">
          <ExclamationCircleOutlined style={{ marginLeft: 5 }} />
        </Tooltip>
      </div>
      <div className={styles.rightContainer}>
        <Switch
          checked={codeConfig?.isComponent}
          onChange={value => updateIsComponent(value)}
        />
        <Input
          placeholder="组件名"
          value={name}
          onChange={e => setName(e.target.value)}
          onBlur={e => updateComponentName(e)}
        />
      </div>
    </div>
  );
}
Example #13
Source File: DestinationCard.tsx    From jitsu with MIT License 5 votes vote down vote up
export function DestinationCard({ dst }: DestinationCardProps) {
  const reference = destinationsReferenceMap[dst._type]
  const rename = async (newName: string) => {
    await flowResult(destinationsStore.patch(dst._uid, { displayName: newName }))
  }
  let deleteAction = () => {
    Modal.confirm({
      title: "Please confirm deletion of destination",
      icon: <ExclamationCircleOutlined />,
      content: "Are you sure you want to delete " + dst._id + " destination?",
      okText: "Delete",
      cancelText: "Cancel",
      onOk: async () => {
        try {
          await flowResult(destinationsStore.delete(dst._uid))
          await connectionsHelper.unconnectDeletedDestination(dst._uid)
        } catch (errors) {
          handleError(errors, "Unable to delete destination at this moment, please try later.")
        }
      },
    })
  }
  let editLink = projectRoute(destinationPageRoutes.editExact, { id: dst._id })
  const statLink = projectRoute(destinationPageRoutes.statisticsExact, { id: dst._id })
  return (
    <ConnectionCard
      title={DestinationsUtils.getDisplayName(dst)}
      icon={reference.ui.icon}
      deleteAction={deleteAction}
      editAction={editLink}
      rename={rename}
      menuOverlay={
        <Menu>
          <Menu.Item icon={<EditOutlined />}>
            <NavLink to={editLink}>Edit</NavLink>
          </Menu.Item>
          <Menu.Item icon={<DeleteOutlined />} onClick={deleteAction}>
            Delete
          </Menu.Item>
          <Menu.Item icon={<CodeOutlined />}>
            <NavLink to={statLink}>Statistics</NavLink>
          </Menu.Item>
        </Menu>
      }
      subtitle={dst._formData?.mode ? <>mode: {dst._formData?.mode}</> : <>{reference.ui.title(dst)}</>}
      status={
        <Tooltip
          overlay={
            dst._connectionTestOk ? "Connection successful" : `Connection failed: ${dst._connectionErrorMessage}`
          }
        >
          <Badge
            size="default"
            status={dst._connectionTestOk ? "success" : "error"}
            text={
              <>
                <span className={`text-${dst._connectionTestOk ? "success" : "error"}`}>
                  {dst._connectionTestOk ? "Active" : "Connection test failed"}
                </span>
                {reference.deprecated ? <span className={"text-warning"}> (Deprecated)</span> : <></>}
              </>
            }
          />
        </Tooltip>
      }
    />
  )
}
Example #14
Source File: DeleteCommentModal.tsx    From foodie with MIT License 5 votes vote down vote up
DeleteCommentModal: React.FC<IProps> = (props) => {
    const [isDeleting, setIsDeleting] = useState(false);
    const [error, setError] = useState<IError | null>(null);
    const dispatch = useDispatch();
    const targetComment = useSelector((state: IRootReducer) => state.helper.targetComment);

    const handleDeleteComment = async () => {
        try {
            setIsDeleting(true);
            targetComment && await deleteComment(targetComment.id);

            closeModal();
            targetComment && props.deleteSuccessCallback(targetComment);
            toast.dark('Comment successfully deleted.', {
                progressStyle: { backgroundColor: '#4caf50' },
                autoClose: 2000
            });
        } catch (e) {
            setIsDeleting(false);
            setError(e);
        }
    };

    const closeModal = () => {
        if (props.isOpen) {
            props.closeModal();
            dispatch(setTargetComment(null));
        }
    }

    return (
        <Modal
            isOpen={props.isOpen}
            onAfterOpen={props.onAfterOpen}
            onRequestClose={closeModal}
            contentLabel="Delete Comment"
            className="modal"
            shouldCloseOnOverlayClick={!isDeleting}
            overlayClassName="modal-overlay"
        >
            <div className="relative">
                <div
                    className="absolute right-2 top-2 p-1 rounded-full flex items-center justify-center cursor-pointer hover:bg-gray-200 dark:hover:bg-indigo-1100"
                    onClick={closeModal}
                >
                    <CloseOutlined className="p-2  outline-none text-gray-500 dark:text-white" />
                </div>
                {error && (
                    <span className="block p-4 bg-red-100 text-red-500 w-full">
                        {error?.error?.message || 'Unable process request. Please try again.'}
                    </span>
                )}
                <div className="p-4 laptop:px-8">
                    <h2 className="dark:text-white">
                        <ExclamationCircleOutlined className="text-red-500 mr-2 pt-2" />
                        Delete Comment
                    </h2>
                    <p className="text-gray-600 my-4 dark:text-white">Are you sure you want to delete this comment?</p>
                    <div className="flex justify-between">
                        <button
                            className="button--muted !rounded-full dark:bg-indigo-1100 dark:text-white dark:hover:bg-indigo-1100"
                            onClick={closeModal}
                        >
                            Cancel
                        </button>
                        <button
                            className="button--danger"
                            disabled={isDeleting}
                            onClick={handleDeleteComment}
                        >
                            Delete
                        </button>
                    </div>
                </div>
            </div>

        </Modal>
    );
}
Example #15
Source File: handleHttpError.tsx    From next-core with GNU General Public License v3.0 5 votes vote down vote up
/**
 * 处理 http 请求错误(使用 AntDesign 模态框弹出错误信息)。
 *
 * @param error - 错误对象。
 */
export function handleHttpError(
  error: Error | HttpFetchError | HttpResponseError | HttpParseError
): ReturnType<ModalFunc> {
  // Redirect to login page if not logged in.
  if (isUnauthenticatedError(error) && !window.NO_AUTH_GUARD) {
    // Do not show multiple confirm modals.
    if (unauthenticatedConfirmModal) {
      return;
    }
    unauthenticatedConfirmModal = Modal.confirm({
      icon: <ExclamationCircleOutlined />,
      content: <LoginTimeoutMessage />,
      okText: i18next.t(`${NS_BRICK_KIT}:${K.MODAL_OK}`),
      cancelText: i18next.t(`${NS_BRICK_KIT}:${K.MODAL_CANCEL}`),
      onOk: () => {
        const ssoEnabled = getRuntime().getFeatureFlags()["sso-enabled"];
        const history = getHistory();
        history.push(ssoEnabled ? "/sso-auth/login" : "/auth/login", {
          from: {
            ...history.location,
            state: undefined,
          },
        });
      },
    });
    return;
  }

  return Modal.error({
    title: i18next.t(`${NS_BRICK_KIT}:${K.REQUEST_FAILED}`),
    content: (
      <div
        style={{
          whiteSpace: "pre-wrap",
        }}
      >
        {httpErrorToString(error)}
      </div>
    ),
    okText: i18next.t(`${NS_BRICK_KIT}:${K.MODAL_OK}`),
  });
}
Example #16
Source File: RestoreAllMessagesModal.tsx    From wildduck-ui with MIT License 5 votes vote down vote up
RestoreAllMessagesModal = () => {
	const { setDateData, setIsModalVisible } = useActions(archiveLogic);
	const { dateData, isModalVisible } = useValues(archiveLogic);

	const { id }: any = useParams();

	const { mutate } = useRestoreArchiveMessages();

	const handleOk = () => {
		mutate({
			userId: id,
			params: {
				start: _.get(dateData, '0', moment('2000-01-01 00:00:00')).format(DATE_TIME_FORMAT),
				end: _.get(dateData, '1', moment()).format(DATE_TIME_FORMAT),
			},
		});
		setIsModalVisible(false);
	};

	const handleCancel = () => {
		setIsModalVisible(false);
		setDateData([]);
	};

	return (
		<Modal visible={isModalVisible} onOk={handleOk} onCancel={handleCancel}>
			<Title level={4}>
				<ExclamationCircleOutlined />
				Are you sure you want to Restore all messages ?
			</Title>
			<p>Select the start and end date (by default it will restores all the mails).</p>
			<RangePicker
				disabledDate={(current) => current && current > moment().endOf('day')}
				onChange={(value) => setDateData(value)}
				showTime
				defaultValue={
					_.isEmpty(dateData) ? ('' as any) : [moment(_.get(dateData, '0')), moment(_.get(dateData, '1'))]
				}
			/>
		</Modal>
	);
}
Example #17
Source File: TeamMembers.tsx    From posthog-foss with MIT License 5 votes vote down vote up
function ActionsComponent(member: FusedTeamMemberType): JSX.Element | null {
    const { user } = useValues(userLogic)
    const { currentTeam } = useValues(teamLogic)
    const { removeMember } = useActions(teamMembersLogic)

    if (!user) {
        return null
    }

    function handleClick(): void {
        Modal.confirm({
            title: `${
                member.user.uuid == user?.uuid
                    ? 'Leave'
                    : `Remove ${member.user.first_name} (${member.user.email}) from`
            } project ${currentTeam?.name}?`,
            icon: <ExclamationCircleOutlined />,
            okText: member.user.uuid == user?.uuid ? 'Leave' : 'Remove',
            okType: 'danger',
            onOk() {
                removeMember({ member })
            },
        })
    }

    const allowDeletion =
        // You can leave, but only project admins can remove others
        ((currentTeam?.effective_membership_level &&
            currentTeam.effective_membership_level >= OrganizationMembershipLevel.Admin) ||
            member.user.uuid === user.uuid) &&
        // Only members without implicit access can leave or be removed
        member.organization_level < MINIMUM_IMPLICIT_ACCESS_LEVEL

    return allowDeletion ? (
        <a className="text-danger" onClick={handleClick} data-attr="delete-team-membership">
            {member.user.uuid !== user.uuid ? (
                <CloseCircleOutlined title="Remove from project" />
            ) : (
                <LogoutOutlined title="Leave project" />
            )}
        </a>
    ) : null
}
Example #18
Source File: index.tsx    From fe-v5 with Apache License 2.0 4 votes vote down vote up
Shield: React.FC = () => {
  const { t } = useTranslation();
  const history = useHistory();
  const dispatch = useDispatch();
  const [query, setQuery] = useState<string>('');
  const { curBusiItem } = useSelector<RootState, CommonStoreState>((state) => state.common);
  const [bgid, setBgid] = useState(undefined);
  const [clusters, setClusters] = useState<string[]>([]);
  const [currentShieldDataAll, setCurrentShieldDataAll] = useState<Array<shieldItem>>([]);
  const [currentShieldData, setCurrentShieldData] = useState<Array<shieldItem>>([]);
  const [loading, setLoading] = useState<boolean>(false);

  const columns: ColumnsType = [
    {
      title: t('集群'),
      dataIndex: 'cluster',
      render: (data) => {
        return <div>{data}</div>;
      },
    },
    {
      title: t('标签'),
      dataIndex: 'tags',
      render: (text: any) => {
        return (
          <>
            {text
              ? text.map((tag, index) => {
                  return tag ? (
                    // <ColorTag text={`${tag.key} ${tag.func} ${tag.func === 'in' ? tag.value.split(' ').join(', ') : tag.value}`} key={index}>
                    // </ColorTag>
                    <div key={index} style={{ lineHeight: '16px' }}>{`${tag.key} ${tag.func} ${tag.func === 'in' ? tag.value.split(' ').join(', ') : tag.value}`}</div>
                  ) : null;
                })
              : ''}
          </>
        );
      },
    },
    {
      title: t('屏蔽原因'),
      dataIndex: 'cause',
      render: (text: string, record: shieldItem) => {
        return (
          <>
            <Tooltip placement='topLeft' title={text}>
              <div
                style={{
                  whiteSpace: 'nowrap',
                  textOverflow: 'ellipsis',
                  overflow: 'hidden',
                  lineHeight: '16px',
                }}
              >
                {text}
              </div>
            </Tooltip>
            by {record.create_by}
          </>
        );
      },
    },
    {
      title: t('屏蔽时间'),
      dataIndex: 'btime',
      render: (text: number, record: shieldItem) => {
        return (
          <div className='shield-time'>
            <div>
              {t('开始:')}
              {dayjs(record?.btime * 1000).format('YYYY-MM-DD HH:mm:ss')}
            </div>
            <div>
              {t('结束:')}
              {dayjs(record?.etime * 1000).format('YYYY-MM-DD HH:mm:ss')}
            </div>
          </div>
        );
      },
    },
    // {
    //   title: t('创建人'),
    //   ellipsis: true,
    //   dataIndex: 'create_by',
    // },
    {
      title: t('操作'),
      width: '98px',
      dataIndex: 'operation',
      render: (text: undefined, record: shieldItem) => {
        return (
          <>
            <div className='table-operator-area'>
              <div
                className='table-operator-area-normal'
                style={{
                  cursor: 'pointer',
                  display: 'inline-block',
                }}
                onClick={() => {
                  dispatch({
                    type: 'shield/setCurShieldData',
                    data: record,
                  });
                  curBusiItem?.id && history.push(`/alert-mutes/edit/${record.id}?mode=clone`);
                }}
              >
                {t('克隆')}
              </div>
              <div
                className='table-operator-area-warning'
                style={{
                  cursor: 'pointer',
                  display: 'inline-block',
                }}
                onClick={() => {
                  confirm({
                    title: t('确定删除该告警屏蔽?'),
                    icon: <ExclamationCircleOutlined />,
                    onOk: () => {
                      dismiss(record.id);
                    },

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

  useEffect(() => {
    getList();
  }, [curBusiItem]);

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

  const dismiss = (id: number) => {
    deleteShields({ ids: [id] }, curBusiItem.id).then((res) => {
      refreshList();
      if (res.err) {
        message.success(res.err);
      } else {
        message.success(t('删除成功'));
      }
    });
  };

  const filterData = () => {
    const data = JSON.parse(JSON.stringify(currentShieldDataAll));
    const res = data.filter((item: shieldItem) => {
      const tagFind = item.tags.find((tag) => {
        return tag.key.indexOf(query) > -1 || tag.value.indexOf(query) > -1 || tag.func.indexOf(query) > -1;
      });
      return (item.cause.indexOf(query) > -1 || !!tagFind) && ((clusters && clusters?.indexOf(item.cluster) > -1) || clusters?.length === 0);
    });
    setCurrentShieldData(res || []);
  };

  const getList = async () => {
    if (curBusiItem.id) {
      setLoading(true);
      const { success, dat } = await getShieldList({ id: curBusiItem.id });
      if (success) {
        setCurrentShieldDataAll(dat || []);
        setLoading(false);
      }
    }
  };

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

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

  const clusterChange = (data) => {
    setClusters(data);
  };

  const busiChange = (data) => {
    setBgid(data);
  };

  return (
    <PageLayout title={t('屏蔽规则')} icon={<CloseCircleOutlined />} hideCluster>
      <div className='shield-content'>
        <LeftTree
          busiGroup={{
            // showNotGroupItem: true,
            onChange: busiChange,
          }}
        ></LeftTree>
        {curBusiItem?.id ? (
          <div className='shield-index'>
            <div className='header'>
              <div className='header-left'>
                <RefreshIcon
                  className='strategy-table-search-left-refresh'
                  onClick={() => {
                    refreshList();
                  }}
                />
                <ColumnSelect onClusterChange={(e) => setClusters(e)} />
                <Input onPressEnter={onSearchQuery} className={'searchInput'} prefix={<SearchOutlined />} placeholder={t('搜索标签、屏蔽原因')} />
              </div>
              <div className='header-right'>
                <Button
                  type='primary'
                  className='add'
                  ghost
                  onClick={() => {
                    history.push('/alert-mutes/add');
                  }}
                >
                  {t('新增屏蔽规则')}
                </Button>
              </div>
            </div>
            <Table
              rowKey='id'
              // sticky
              pagination={{
                total: currentShieldData.length,
                showQuickJumper: true,
                showSizeChanger: true,
                showTotal: (total) => {
                  return `共 ${total} 条数据`;
                },
                pageSizeOptions: pageSizeOptionsDefault,
                defaultPageSize: 30,
              }}
              loading={loading}
              dataSource={currentShieldData}
              columns={columns}
            />
          </div>
        ) : (
          <BlankBusinessPlaceholder text='屏蔽规则' />
        )}
      </div>
    </PageLayout>
  );
}
Example #19
Source File: AddFiltersForm.tsx    From wildduck-ui with MIT License 4 votes vote down vote up
AddFiltersForm: React.FC<Props> = (props: Props) => {
	const { setShowAddFilterForm, setFilterId } = useActions(filtersLogic);
	const { filterId } = useValues(filtersLogic);
	const [form] = Form.useForm();
	const { confirm } = Modal;

	const params: { id: string } = useParams();
	const { data, isLoading } = useFilterDetails(params.id, filterId);
	const { data: mailboxesList } = useMailboxes({ userId: params.id });
	const { mutate: createFilter } = useCreateFilter();
	const { mutate: updateFilter } = useUpdateFilter();

	const onFinish = (values: any) => {
		const query = _.pick(values, ['from', 'to', 'subject', 'listId', 'text', 'ha', 'size']);
		const action = _.pick(values, ['seen', 'flag', 'delete', 'spam', 'mailbox', 'targets']);
		const filter: any = _.pick(values, ['name', 'disabled']);

		_.set(filter, 'query', query);
		_.set(filter, 'action', action);
		showConfirm(filter);
	};

	function showConfirm(filter: CreateFilterRequest) {
		confirm({
			title: 'Are you sure you want to save this Filter ?',
			icon: <ExclamationCircleOutlined />,

			onOk() {
				props.action === 'create'
					? createFilter({ userId: params.id, filterDetails: filter })
					: updateFilter({ userId: params.id, filterId: filterId, filterDetails: filter });
			},
		});
	}

	const reset = () => {
		form.resetFields();
	};

	useEffect(() => {
		form.setFieldsValue({ ...data });
	}, [data]);

	const pageBreadcrumb = (
		<Breadcrumb>
			<Breadcrumb.Item>
				<a
					onClick={(event) => {
						event.stopPropagation();
						setShowAddFilterForm(false);
						setFilterId('');
					}}
				>
					Filters
				</a>
			</Breadcrumb.Item>
			<Breadcrumb.Item>{props.action === 'create' ? 'Add Filter' : 'Update Filter'}</Breadcrumb.Item>
		</Breadcrumb>
	);

	return (
		<Page title={pageBreadcrumb} loading={isLoading}>
			<Form wrapperCol={{ span: 6 }} labelCol={{ span: 3 }} form={form} onFinish={onFinish}>
				<Form.Item label='Filter name' name='name'>
					<Input placeholder='Name of the Filter' />
				</Form.Item>
				<Form.Item label='Query' tooltip={filtersTooltip.query}>
					{_.map(['from', 'to', 'subject', 'listId', 'text'], (query) => {
						return (
							<Form.Item key={query} name={query} label={query} tooltip={_.get(filtersTooltip, query)}>
								<Input />
							</Form.Item>
						);
					})}
					<Form.Item name='ha' label='has attachment' tooltip={filtersTooltip.ha} valuePropName='checked'>
						<Switch />
					</Form.Item>
					<Form.Item name='size' label='size' tooltip={filtersTooltip.size}>
						<Input />
					</Form.Item>
				</Form.Item>
				<Form.Item label='Action' tooltip={filtersTooltip.action}>
					{_.map(['seen', 'flag', 'delete', 'spam'], (query) => {
						return (
							<Form.Item
								name={query}
								key={query}
								label={query}
								tooltip={_.get(filtersTooltip, query)}
								valuePropName='checked'
							>
								<Switch />
							</Form.Item>
						);
					})}
					<Form.Item name='mailbox' label='Mailbox' tooltip={filtersTooltip.mailbox}>
						<Select showSearch>
							{_.map(mailboxesList, (mailbox: any) => {
								return (
									<Select.Option value={mailbox?.id} key={mailbox?.name + mailbox?.id}>
										{mailbox?.name}
									</Select.Option>
								);
							})}
						</Select>
					</Form.Item>
					<Form.Item name='targets' label='Targets' tooltip={filtersTooltip.targets}>
						<Select mode='tags' placeholder='Enter Targets' />
					</Form.Item>
				</Form.Item>
				<Form.Item
					name='disabled'
					label='Disable Filter'
					tooltip={filtersTooltip.disabled}
					valuePropName='checked'
				>
					<Switch />
				</Form.Item>
				<Form.Item {...tailLayout}>
					<Space size='middle'>
						<Button type='default' htmlType='button' onClick={reset}>
							Reset
						</Button>
						<Button type='primary' htmlType='submit'>
							Save
						</Button>
					</Space>
				</Form.Item>
			</Form>
		</Page>
	);
}
Example #20
Source File: index.tsx    From metaplex with Apache License 2.0 4 votes vote down vote up
AdjustQuantitiesStep = ({
  allowedAmountToRedeem,
  distributionType,
  weightByMetadataKey,
  supplyByMetadataKey,
  selectedItems,
  setPackState,
  isUnlimited,
}: AdjustQuantitiesStepProps): ReactElement => {
  const availableDistributionTypes = [
    ...(isUnlimited ? [PackDistributionType.Unlimited] : []),
    PackDistributionType.MaxSupply,
    PackDistributionType.Fixed,
  ];

  const shouldRenderSupplyInput =
    distributionType !== PackDistributionType.Unlimited;
  const shouldRenderWeightInput =
    distributionType !== PackDistributionType.MaxSupply;

  const handleRedeemAmountChange = (value: string): void => {
    setPackState({
      allowedAmountToRedeem: parseInt(value),
    });
  };

  const handleDistributionChange = (
    item: SafetyDepositDraft,
    value: string,
    inputType: InputType,
  ): void => {
    const number = Number(value);
    const pubKey = item.metadata.pubkey;

    if (inputType === InputType.weight && number > 100) {
      return;
    }

    if (inputType === InputType.weight) {
      return setPackState({
        weightByMetadataKey: {
          ...weightByMetadataKey,
          [pubKey]: number,
        },
      });
    }

    const maxSupply = item.masterEdition?.info.maxSupply?.toNumber();
    setPackState({
      supplyByMetadataKey: {
        ...supplyByMetadataKey,
        [pubKey]:
          maxSupply !== undefined && number > maxSupply ? maxSupply : number,
      },
    });
  };

  const handleDistributionTypeChange = (type: PackDistributionType): void => {
    setPackState({ distributionType: type });
  };

  return (
    <div className="quantities-step-wrapper">
      <p className="quantities-step-wrapper__title">
        Set number of cards in pack
      </p>
      <p className="quantities-step-wrapper__subtitle">
        Number of times user can redeem a card using a single voucher.
      </p>
      <Input
        className="quantities-step-wrapper__input"
        type="number"
        value={allowedAmountToRedeem}
        onChange={({ target: { value } }) => handleRedeemAmountChange(value)}
      />

      <p className="quantities-step-wrapper__title">Select distribution type</p>
      <div className="cards-select">
        {availableDistributionTypes.map(type => (
          <SelectCard
            key={type}
            title={DISTRIBUTION_TYPES_DATA[type].title}
            subtitle={DISTRIBUTION_TYPES_DATA[type].subtitle}
            isSelected={distributionType === type}
            onClick={() => handleDistributionTypeChange(type)}
          />
        ))}
      </div>

      <div className="quantities-step-wrapper__table-titles">
        {shouldRenderSupplyInput && <p>NUMBER OF NFTs</p>}
        {shouldRenderWeightInput && (
          <p className="redeem-weight">
            REDEEM WEIGHT{' '}
            <span>
              — Weights must be between 1-100. 1 is least likely, 100 is most
              likely.
            </span>
          </p>
        )}
      </div>

      {Object.values(selectedItems).map(item => (
        <ItemRow key={item.metadata.pubkey} item={item}>
          <>
            {shouldRenderSupplyInput && (
              <div className="input-column">
                <Input
                  type="number"
                  min={0}
                  max={item.masterEdition?.info.maxSupply?.toNumber()}
                  className={classNames({
                    'ant-error-input':
                      !supplyByMetadataKey[item.metadata.pubkey],
                  })}
                  value={supplyByMetadataKey[item.metadata.pubkey]}
                  onChange={({ target: { value } }) =>
                    handleDistributionChange(item, value, InputType.maxSupply)
                  }
                />
              </div>
            )}
            {shouldRenderWeightInput && (
              <div className="input-column">
                <Input
                  type="number"
                  min={0}
                  max={100}
                  value={weightByMetadataKey[item.metadata.pubkey]}
                  onChange={({ target: { value } }) =>
                    handleDistributionChange(item, value, InputType.weight)
                  }
                  className={classNames({
                    'ant-error-input error-redeem':
                      !weightByMetadataKey[item.metadata.pubkey],
                  })}
                />
                {!weightByMetadataKey[item.metadata.pubkey] && (
                  <div className="error-tooltip-container">
                    <Tooltip
                      overlayClassName="creat-pack-redeem-tooltip"
                      placement="top"
                      title="Weight must be between 1-100"
                    >
                      <ExclamationCircleOutlined className="input-info" />
                    </Tooltip>
                  </div>
                )}
              </div>
            )}
          </>
        </ItemRow>
      ))}
    </div>
  );
}
Example #21
Source File: index.tsx    From fe-v5 with Apache License 2.0 4 votes vote down vote up
Shield: React.FC = () => {
  const { t } = useTranslation();
  const history = useHistory();
  const [query, setQuery] = useState<string>('');
  const { curBusiItem } = useSelector<RootState, CommonStoreState>((state) => state.common);
  const [bgid, setBgid] = useState(undefined);
  const [clusters, setClusters] = useState<string[]>([]);
  const [currentShieldDataAll, setCurrentShieldDataAll] = useState<Array<subscribeItem>>([]);
  const [currentShieldData, setCurrentShieldData] = useState<Array<subscribeItem>>([]);
  const [loading, setLoading] = useState<boolean>(false);

  const columns: ColumnsType = [
    {
      title: t('集群'),
      dataIndex: 'cluster',
      render: (data) => {
        return <div>{data}</div>;
      },
    },
    {
      title: t('告警规则'),
      dataIndex: 'rule_name',
      render: (data) => {
        return <div>{data}</div>;
      },
    },
    {
      title: t('订阅标签'),
      dataIndex: 'tags',
      render: (text: any) => {
        return (
          <>
            {text
              ? text.map((tag, index) => {
                  return tag ? <div key={index}>{`${tag.key} ${tag.func} ${tag.func === 'in' ? tag.value.split(' ').join(', ') : tag.value}`}</div> : null;
                })
              : ''}
          </>
        );
      },
    },
    {
      title: t('告警接收组'),
      dataIndex: 'user_groups',
      render: (text: string, record: subscribeItem) => {
        return (
          <>
            {record.user_groups?.map((item) => (
              <ColorTag text={item.name} key={item.id}></ColorTag>
            ))}
          </>
        );
      },
    },

    {
      title: t('编辑人'),
      ellipsis: true,
      dataIndex: 'update_by',
    },
    {
      title: t('操作'),
      width: '128px',
      dataIndex: 'operation',
      render: (text: undefined, record: subscribeItem) => {
        return (
          <>
            <div className='table-operator-area'>
              <div
                className='table-operator-area-normal'
                style={{
                  cursor: 'pointer',
                  display: 'inline-block',
                }}
                onClick={() => {
                  curBusiItem?.id && history.push(`/alert-subscribes/edit/${record.id}`);
                }}
              >
                {t('编辑')}
              </div>
              <div
                className='table-operator-area-normal'
                style={{
                  cursor: 'pointer',
                  display: 'inline-block',
                }}
                onClick={() => {
                  curBusiItem?.id && history.push(`/alert-subscribes/edit/${record.id}?mode=clone`);
                }}
              >
                {t('克隆')}
              </div>
              <div
                className='table-operator-area-warning'
                style={{
                  cursor: 'pointer',
                  display: 'inline-block',
                }}
                onClick={() => {
                  confirm({
                    title: t('确定删除该订阅规则?'),
                    icon: <ExclamationCircleOutlined />,
                    onOk: () => {
                      dismiss(record.id);
                    },

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

  useEffect(() => {
    getList();
  }, [curBusiItem]);

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

  const dismiss = (id: number) => {
    deleteSubscribes({ ids: [id] }, curBusiItem.id).then((res) => {
      refreshList();
      if (res.err) {
        message.success(res.err);
      } else {
        message.success(t('删除成功'));
      }
    });
  };

  const filterData = () => {
    const data = JSON.parse(JSON.stringify(currentShieldDataAll));
    const res = data.filter((item: subscribeItem) => {
      const tagFind = item?.tags?.find((tag) => {
        return tag.key.indexOf(query) > -1 || tag.value.indexOf(query) > -1 || tag.func.indexOf(query) > -1;
      });
      const groupFind = item?.user_groups?.find((item) => {
        return item?.name?.indexOf(query) > -1;
      });
      return (item?.rule_name?.indexOf(query) > -1 || !!tagFind || !!groupFind) && ((clusters && clusters?.indexOf(item.cluster) > -1) || clusters?.length === 0);
    });
    setCurrentShieldData(res || []);
  };

  const getList = async () => {
    if (curBusiItem.id) {
      setLoading(true);
      const { success, dat } = await getSubscribeList({ id: curBusiItem.id });
      if (success) {
        setCurrentShieldDataAll(dat || []);
        setLoading(false);
      }
    }
  };

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

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

  const clusterChange = (data) => {
    setClusters(data);
  };

  const busiChange = (data) => {
    setBgid(data);
  };

  return (
    <PageLayout title={t('订阅规则')} icon={<CopyOutlined />} hideCluster>
      <div className='shield-content'>
        <LeftTree
          busiGroup={{
            // showNotGroupItem: true,
            onChange: busiChange,
          }}
        ></LeftTree>
        {curBusiItem?.id ? (
          <div className='shield-index'>
            <div className='header'>
              <div className='header-left'>
                <RefreshIcon
                  className='strategy-table-search-left-refresh'
                  onClick={() => {
                    refreshList();
                  }}
                />
                <ColumnSelect onClusterChange={(e) => setClusters(e)} />
                <Input onPressEnter={onSearchQuery} className={'searchInput'} prefix={<SearchOutlined />} placeholder={t('搜索规则、标签、接收组')} />
              </div>
              <div className='header-right'>
                <Button
                  type='primary'
                  className='add'
                  ghost
                  onClick={() => {
                    history.push('/alert-subscribes/add');
                  }}
                >
                  {t('新增订阅规则')}
                </Button>
              </div>
            </div>
            <Table
              rowKey='id'
              pagination={{
                total: currentShieldData.length,
                showQuickJumper: true,
                showSizeChanger: true,
                showTotal: (total) => {
                  return `共 ${total} 条数据`;
                },
                pageSizeOptions: pageSizeOptionsDefault,
                defaultPageSize: 30,
              }}
              loading={loading}
              dataSource={currentShieldData}
              columns={columns}
            />
          </div>
        ) : (
          <BlankBusinessPlaceholder text='订阅规则' />
        )}
      </div>
    </PageLayout>
  );
}
Example #22
Source File: MainOperator.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
Main: React.FC<MainProp> = (props) => {
    const [engineStatus, setEngineStatus] = useState<"ok" | "error">("ok")
    const [status, setStatus] = useState<{ addr: string; isTLS: boolean }>()
    const [collapsed, setCollapsed] = useState(false)
    const [hideMenu, setHideMenu] = useState(false)

    const [loading, setLoading] = useState(false)
    const [menuItems, setMenuItems] = useState<MenuItemGroup[]>([])
    const [routeMenuData, setRouteMenuData] = useState<MenuDataProps[]>(RouteMenuData)

    const [notification, setNotification] = useState("")

    const [pageCache, setPageCache] = useState<PageCache[]>([
        {
            verbose: "MITM",
            route: Route.HTTPHacker,
            singleNode: ContentByRoute(Route.HTTPHacker),
            multipleNode: []
        }
    ])
    const [currentTabKey, setCurrentTabKey] = useState<string>(Route.HTTPHacker)

    // 系统类型
    const [system, setSystem] = useState<string>("")
    useEffect(() => {
        ipcRenderer.invoke('fetch-system-name').then((res) => setSystem(res))
    }, [])

    // yakit页面关闭是否二次确认提示
    const [winCloseFlag, setWinCloseFlag] = useState<boolean>(true)
    const [winCloseShow, setWinCloseShow] = useState<boolean>(false)
    useEffect(() => {
        ipcRenderer
            .invoke("get-value", WindowsCloseFlag)
            .then((flag: any) => setWinCloseFlag(flag === undefined ? true : flag))
    }, [])

    // 获取自定义菜单
    const updateMenuItems = () => {
        setLoading(true)
        // Fetch User Defined Plugins
        ipcRenderer
            .invoke("GetAllMenuItem", {})
            .then((data: { Groups: MenuItemGroup[] }) => {
                setMenuItems(data.Groups)
            })
            .catch((e: any) => failed("Update Menu Item Failed"))
            .finally(() => setTimeout(() => setLoading(false), 300))
        // Fetch Official General Plugins
        ipcRenderer
            .invoke("QueryYakScript", {
                Pagination: genDefaultPagination(1000),
                IsGeneralModule: true,
                Type: "yak"
            } as QueryYakScriptRequest)
            .then((data: QueryYakScriptsResponse) => {
                const tabList: MenuDataProps[] = cloneDeep(RouteMenuData)
                for (let item of tabList) {
                    if (item.subMenuData) {
                        if (item.key === Route.GeneralModule) {
                            const extraMenus: MenuDataProps[] = data.Data.map((i) => {
                                return {
                                    icon: <EllipsisOutlined/>,
                                    key: `plugin:${i.Id}`,
                                    label: i.ScriptName,
                                } as unknown as MenuDataProps
                            })
                            item.subMenuData.push(...extraMenus)
                        }
                        item.subMenuData.sort((a, b) => a.label.localeCompare(b.label))
                    }
                }
                setRouteMenuData(tabList)
            })
    }
    useEffect(() => {
        updateMenuItems()
        ipcRenderer.on("fetch-new-main-menu", (e) => {
            updateMenuItems()
        })

        return () => {
            ipcRenderer.removeAllListeners("fetch-new-main-menu")
        }
    }, [])

    useEffect(() => {
        if (engineStatus === "error") props.onErrorConfirmed && props.onErrorConfirmed()
    }, [engineStatus])

    // 整合路由对应名称
    const pluginKey = (item: PluginMenuItem) => `plugin:${item.Group}:${item.YakScriptId}`;
    const routeKeyToLabel = new Map<string, string>();
    routeMenuData.forEach(k => {
        (k.subMenuData || []).forEach(subKey => {
            routeKeyToLabel.set(`${subKey.key}`, subKey.label)
        })

        routeKeyToLabel.set(`${k.key}`, k.label)
    })
    menuItems.forEach((k) => {
        k.Items.forEach((value) => {
            routeKeyToLabel.set(pluginKey(value), value.Verbose)
        })
    })

    // Tabs Bar Operation Function
    const getCacheIndex = (route: string) => {
        const targets = pageCache.filter((i) => i.route === route)
        return targets.length > 0 ? pageCache.indexOf(targets[0]) : -1
    }
    const addTabPage = useMemoizedFn(
        (route: Route, nodeParams?: { time?: string; node: ReactNode; isRecord?: boolean }) => {
            const filterPage = pageCache.filter((i) => i.route === route)
            const filterPageLength = filterPage.length

            if (singletonRoute.includes(route)) {
                if (filterPageLength > 0) {
                    setCurrentTabKey(route)
                } else {
                    const tabName = routeKeyToLabel.get(route) || `${route}`
                    setPageCache([
                        ...pageCache,
                        {
                            verbose: tabName,
                            route: route,
                            singleNode: ContentByRoute(route),
                            multipleNode: []
                        }
                    ])
                    setCurrentTabKey(route)
                }
            } else {
                if (filterPageLength > 0) {
                    const tabName = routeKeyToLabel.get(route) || `${route}`
                    const tabId = `${route}-[${randomString(49)}]`
                    const time = new Date().getTime().toString()
                    const node: multipleNodeInfo = {
                        id: tabId,
                        verbose: `${tabName}-[${filterPage[0].multipleNode.length + 1}]`,
                        node: nodeParams && nodeParams.node ? nodeParams?.node || <></> : ContentByRoute(route),
                        time: nodeParams && nodeParams.node ? nodeParams?.time || time : time
                    }
                    const pages = pageCache.map((item) => {
                        if (item.route === route) {
                            item.multipleNode.push(node)
                            item.multipleCurrentKey = tabId
                            return item
                        }
                        return item
                    })
                    setPageCache([...pages])
                    setCurrentTabKey(route)
                    if (nodeParams && !!nodeParams.isRecord) addFuzzerList(nodeParams?.time || time)
                } else {
                    const tabName = routeKeyToLabel.get(route) || `${route}`
                    const tabId = `${route}-[${randomString(49)}]`
                    const time = new Date().getTime().toString()
                    const node: multipleNodeInfo = {
                        id: tabId,
                        verbose: `${tabName}-[1]`,
                        node: nodeParams && nodeParams.node ? nodeParams?.node || <></> : ContentByRoute(route),
                        time: nodeParams && nodeParams.node ? nodeParams?.time || time : time
                    }
                    setPageCache([
                        ...pageCache,
                        {
                            verbose: tabName,
                            route: route,
                            singleNode: undefined,
                            multipleNode: [node],
                            multipleCurrentKey: tabId
                        }
                    ])
                    setCurrentTabKey(route)
                    if (nodeParams && !!nodeParams.isRecord) addFuzzerList(nodeParams?.time || time)
                }
            }
        }
    )
    const menuAddPage = useMemoizedFn((route: Route) => {
        if (route === "ignore") return

        if (route === Route.HTTPFuzzer) {
            const time = new Date().getTime().toString()
            addTabPage(Route.HTTPFuzzer, {
                time: time,
                node: ContentByRoute(Route.HTTPFuzzer, undefined, {
                    system: system,
                    order: time
                }),
                isRecord: true
            })
        } else addTabPage(route as Route)
    })
    const removePage = (route: string) => {
        const targetIndex = getCacheIndex(route)

        if (targetIndex > 0 && pageCache[targetIndex - 1]) {
            const targetCache = pageCache[targetIndex - 1]
            setCurrentTabKey(targetCache.route)
        }
        if (targetIndex === 0 && pageCache[targetIndex + 1]) {
            const targetCache = pageCache[targetIndex + 1]
            setCurrentTabKey(targetCache.route)
        }
        if (targetIndex === 0 && pageCache.length === 1) setCurrentTabKey("")

        setPageCache(pageCache.filter((i) => i.route !== route))

        if (route === Route.HTTPFuzzer) delFuzzerList(1)
    }
    const updateCacheVerbose = (id: string, verbose: string) => {
        const index = getCacheIndex(id)
        if (index < 0) return

        pageCache[index].verbose = verbose
        setPageCache([...pageCache])
    }
    const setMultipleCurrentKey = useMemoizedFn((key: string, type: Route) => {
        const arr = pageCache.map(item => {
            if (item.route === type) {
                item.multipleCurrentKey = key
                return item
            }
            return item
        })
        setPageCache([...arr])
    })
    const removeMultipleNodePage = useMemoizedFn((key: string, type: Route) => {
        const removeArr: multipleNodeInfo[] = pageCache.filter(item => item.route === type)[0]?.multipleNode || []
        if (removeArr.length === 0) return
        const nodes = removeArr.filter(item => item.id === key)
        const time = nodes[0].time

        let index = 0
        for (let i in removeArr) {
            if (removeArr[i].id === key) {
                index = +i
                break
            }
        }

        if (index === 0 && removeArr.length === 1) {
            removePage(type)
            return
        }

        let current = ""
        let filterArr: multipleNodeInfo[] = []
        if (index > 0 && removeArr[index - 1]) {
            current = removeArr[index - 1].id
            filterArr = removeArr.filter(item => item.id !== key)
        }
        if (index === 0 && removeArr[index + 1]) {
            current = removeArr[index + 1].id
            filterArr = removeArr.filter(item => item.id !== key)
        }

        if (current) {
            const arr = pageCache.map(item => {
                if (item.route === type) {
                    item.multipleNode = [...filterArr]
                    item.multipleCurrentKey = current
                    return item
                }
                return item
            })
            setPageCache([...arr])
            if (type === Route.HTTPFuzzer) delFuzzerList(2, time)
        }
    })
    const removeOtherMultipleNodePage = useMemoizedFn((key: string, type: Route) => {
        const removeArr: multipleNodeInfo[] = pageCache.filter(item => item.route === type)[0]?.multipleNode || []
        if (removeArr.length === 0) return
        const nodes = removeArr.filter(item => item.id === key)
        const time = nodes[0].time

        const arr = pageCache.map(item => {
            if (item.route === type) {
                item.multipleNode = [...nodes]
                item.multipleCurrentKey = key
                return item
            }
            return item
        })
        setPageCache([...arr])
        if (type === Route.HTTPFuzzer) delFuzzerList(3, time)
    })

    // 全局记录鼠标坐标位置(为右键菜单提供定位)
    const coordinateTimer = useRef<any>(null)
    useEffect(() => {
        document.onmousemove = (e) => {
            const {screenX, screenY, clientX, clientY, pageX, pageY} = e
            if (coordinateTimer.current) {
                clearTimeout(coordinateTimer.current)
                coordinateTimer.current = null
            }
            coordinateTimer.current = setTimeout(() => {
                coordinate.screenX = screenX
                coordinate.screenY = screenY
                coordinate.clientX = clientX
                coordinate.clientY = clientY
                coordinate.pageX = pageX
                coordinate.pageY = pageY
            }, 50);
        }
    }, [])

    // 全局注册快捷键功能
    const documentKeyDown = useMemoizedFn((e: any) => {
        // ctrl + w 关闭tab页面
        if (e.code === "KeyW" && (e.ctrlKey || e.metaKey)) {
            e.preventDefault()
            if (pageCache.length === 0) return

            setLoading(true)
            removePage(currentTabKey)
            setTimeout(() => setLoading(false), 300);
            return
        }
    })
    useEffect(() => {
        document.onkeydown = documentKeyDown
    }, [])

    // fuzzer本地缓存
    const fuzzerList = useRef<Map<string, fuzzerInfoProp>>(new Map<string, fuzzerInfoProp>())
    const saveFuzzerList = debounce(() => {
        const historys: fuzzerInfoProp[] = []
        fuzzerList.current.forEach((value) => historys.push(value))
        historys.sort((a, b) => +a.time - +b.time)
        const filters = historys.filter(item => (item.request || "").length < 1000000 && (item.request || "").length > 0)
        ipcRenderer.invoke("set-value", FuzzerCache, JSON.stringify(filters.slice(-5)))
    }, 500)
    const fetchFuzzerList = useMemoizedFn(() => {
        setLoading(true)
        fuzzerList.current.clear()
        ipcRenderer
            .invoke("get-value", FuzzerCache)
            .then((res: any) => {
                const cache = JSON.parse(res || "[]")

                for (let item of cache) {
                    const time = new Date().getTime().toString()
                    fuzzerList.current.set(time, {...item, time: time})
                    addTabPage(Route.HTTPFuzzer, {
                        time: time,
                        node:
                            ContentByRoute(
                                Route.HTTPFuzzer,
                                undefined,
                                {
                                    isHttps: item.isHttps || false,
                                    request: item.request || "",
                                    fuzzerParams: item,
                                    system: system,
                                    order: time
                                }
                            )
                    })
                }
            })
            .catch((e) => console.info(e))
            .finally(() => setTimeout(() => setLoading(false), 300))
    })
    const addFuzzerList = (key: string, request?: string, isHttps?: boolean) => {
        fuzzerList.current.set(key, {request, isHttps, time: key})
    }
    const delFuzzerList = (type: number, key?: string) => {
        if (type === 1) fuzzerList.current.clear()
        if (type === 2 && key) if (fuzzerList.current.has(key)) fuzzerList.current.delete(key)
        if (type === 3 && key) {
            const info = fuzzerList.current.get(key)
            if (info) {
                fuzzerList.current.clear()
                fuzzerList.current.set(key, info)
            }
        }
        saveFuzzerList()
    }
    const updateFuzzerList = (key: string, param: fuzzerInfoProp) => {
        fuzzerList.current.set(key, param)
        saveFuzzerList()
    }
    useEffect(() => {
        ipcRenderer.on("fetch-fuzzer-setting-data", (e, res: any) => updateFuzzerList(res.key, JSON.parse(res.param)))
        // 开发环境不展示fuzzer缓存
        ipcRenderer.invoke("is-dev").then((flag) => {
            if (!flag) fetchFuzzerList()
            // fetchFuzzerList()
        })
        return () => ipcRenderer.removeAllListeners("fetch-fuzzer-setting-data")
    }, [])

    // 加载补全
    useEffect(() => {
        ipcRenderer.invoke("GetYakitCompletionRaw").then((data: { RawJson: Uint8Array }) => {
            const completionJson = Buffer.from(data.RawJson).toString("utf8")
            setCompletions(JSON.parse(completionJson) as CompletionTotal)
            // success("加载 Yak 语言自动补全成功 / Load Yak IDE Auto Completion Finished")
        })
    }, [])

    useEffect(() => {
        ipcRenderer.invoke("yakit-connect-status").then((data) => {
            setStatus(data)
        })

        ipcRenderer.on("client-engine-status-ok", (e, reason) => {
            if (engineStatus !== "ok") setEngineStatus("ok")
        })
        ipcRenderer.on("client-engine-status-error", (e, reason) => {
            if (engineStatus === "ok") setEngineStatus("error")
        })

        const updateEngineStatus = () => {
            ipcRenderer
                .invoke("engine-status")
                .catch((e: any) => {
                    setEngineStatus("error")
                })
                .finally(() => {
                })
        }
        let id = setInterval(updateEngineStatus, 3000)
        return () => {
            ipcRenderer.removeAllListeners("client-engine-status-error")
            ipcRenderer.removeAllListeners("client-engine-status-ok")
            clearInterval(id)
        }
    }, [])

    useHotkeys("Ctrl+Alt+T", () => {
    })

    useEffect(() => {
        ipcRenderer.invoke("query-latest-notification").then((e: string) => {
            setNotification(e)

            if (e) {
                success(
                    <>
                        <Space direction={"vertical"}>
                            <span>来自于 yaklang.io 的通知</span>
                            <Button
                                type={"link"}
                                onClick={() => {
                                    showModal({
                                        title: "Notification",
                                        content: (
                                            <>
                                                <MDEditor.Markdown source={e}/>
                                            </>
                                        )
                                    })
                                }}
                            >
                                点击查看
                            </Button>
                        </Space>
                    </>
                )
            }
        })
    }, [])

    // 新增数据对比页面
    useEffect(() => {
        ipcRenderer.on("main-container-add-compare", (e, params) => {
            const newTabId = `${Route.DataCompare}-[${randomString(49)}]`;
            const verboseNameRaw = routeKeyToLabel.get(Route.DataCompare) || `${Route.DataCompare}`;
            addTabPage(Route.DataCompare, {node: ContentByRoute(Route.DataCompare, undefined, {system: system})})

            // 区分新建对比页面还是别的页面请求对比的情况
            ipcRenderer.invoke("created-data-compare")
        })

        return () => {
            ipcRenderer.removeAllListeners("main-container-add-compare")
        }
    }, [pageCache])

    // Global Sending Function(全局发送功能|通过发送新增功能页面)
    const addFuzzer = useMemoizedFn((res: any) => {
        const {isHttps, request} = res || {}
        if (request) {
            const time = new Date().getTime().toString()
            addTabPage(Route.HTTPFuzzer, {
                time: time,
                node:
                    ContentByRoute(
                        Route.HTTPFuzzer,
                        undefined,
                        {
                            isHttps: isHttps || false,
                            request: request || "",
                            system: system,
                            order: time
                        }
                    )
            })
            addFuzzerList(time, request || "", isHttps || false)
        }
    })
    const addScanPort = useMemoizedFn((res: any) => {
        const {URL = ""} = res || {}
        if (URL) {
            addTabPage(Route.Mod_ScanPort, {
                node: ContentByRoute(Route.Mod_ScanPort, undefined, {scanportParams: URL})
            })
        }
    })
    const addBrute = useMemoizedFn((res: any) => {
        const {URL = ""} = res || {}
        if (URL) {
            addTabPage(Route.Mod_Brute, {
                node: ContentByRoute(Route.Mod_Brute, undefined, {bruteParams: URL})
            })
        }
    })
    // 发送到专项漏洞检测modal-show变量
    const [bugTestShow, setBugTestShow] = useState<boolean>(false)
    const [bugList, setBugList] = useState<BugInfoProps[]>([])
    const [bugTestValue, setBugTestValue] = useState<BugInfoProps[]>([])
    const [bugUrl, setBugUrl] = useState<string>("")
    const addBugTest = useMemoizedFn((type: number, res?: any) => {
        const {URL = ""} = res || {}

        if (type === 1 && URL) {
            setBugUrl(URL)
            ipcRenderer.invoke("get-value", CustomBugList)
                .then((res: any) => {
                    setBugList(res ? JSON.parse(res) : [])
                    setBugTestShow(true)
                })
                .catch(() => {
                })
        }
        if (type === 2) {
            const filter = pageCache.filter(item => item.route === Route.PoC)
            if (filter.length === 0) {
                addTabPage(Route.PoC)
                setTimeout(() => {
                    ipcRenderer.invoke("send-to-bug-test", {type: bugTestValue, data: bugUrl})
                    setBugTestValue([])
                    setBugUrl("")
                }, 300);
            } else {
                ipcRenderer.invoke("send-to-bug-test", {type: bugTestValue, data: bugUrl})
                setCurrentTabKey(Route.PoC)
                setBugTestValue([])
                setBugUrl("")
            }

        }
    })
    const addYakRunning = useMemoizedFn((res: any) => {
        const {name = "", code = ""} = res || {}
        const filter = pageCache.filter(item => item.route === Route.YakScript)

        if (!name || !code) return false

        if ((filter || []).length === 0) {
            addTabPage(Route.YakScript)
            setTimeout(() => {
                ipcRenderer.invoke("send-to-yak-running", {name, code})
            }, 300);
        } else {
            ipcRenderer.invoke("send-to-yak-running", {name, code})
            setCurrentTabKey(Route.YakScript)
        }
    })
    useEffect(() => {
        ipcRenderer.on("fetch-send-to-tab", (e, res: any) => {
            const {type, data = {}} = res
            if (type === "fuzzer") addFuzzer(data)
            if (type === "scan-port") addScanPort(data)
            if (type === "brute") addBrute(data)
            if (type === "bug-test") addBugTest(1, data)
            if (type === "plugin-store") addYakRunning(data)
        })

        return () => {
            ipcRenderer.removeAllListeners("fetch-send-to-tab")
        }
    }, [])

    // Tabs Bar 组件
    const closeAllCache = useMemoizedFn(() => {
        Modal.confirm({
            title: "确定要关闭所有 Tabs?",
            content: "这样将会关闭所有进行中的进程",
            onOk: () => {
                delFuzzerList(1)
                setPageCache([])
            }
        })
    })
    const closeOtherCache = useMemoizedFn((route: string) => {
        Modal.confirm({
            title: "确定要关闭除此之外所有 Tabs?",
            content: "这样将会关闭所有进行中的进程",
            onOk: () => {
                const arr = pageCache.filter((i) => i.route === route)
                setPageCache(arr)
                if (route === Route.HTTPFuzzer) delFuzzerList(1)
            }
        })
    })
    const bars = (props: any, TabBarDefault: any) => {
        return (
            <TabBarDefault
                {...props}
                children={(barNode: React.ReactElement) => {
                    return (
                        <DropdownMenu
                            menu={{
                                data: [
                                    {key: "all", title: "关闭所有Tabs"},
                                    {key: "other", title: "关闭其他Tabs"}
                                ]
                            }}
                            dropdown={{trigger: ["contextMenu"]}}
                            onClick={(key) => {
                                switch (key) {
                                    case "all":
                                        closeAllCache()
                                        break
                                    case "other":
                                        closeOtherCache(barNode.key as Route)
                                        break
                                    default:
                                        break
                                }
                            }}
                        >
                            {barNode}
                        </DropdownMenu>
                    )
                }}
            />
        )
    }

    return (
        <Layout className="yakit-main-layout">
            <AutoSpin spinning={loading}>
                <Header className="main-laytou-header">
                    <Row>
                        <Col span={8}>
                            <Space>
                                <div style={{marginLeft: 18, textAlign: "center", height: 60}}>
                                    <Image src={YakLogoBanner} preview={false} width={130} style={{marginTop: 6}}/>
                                </div>
                                <Divider type={"vertical"}/>
                                <YakVersion/>
                                <YakitVersion/>
                                {!hideMenu && (
                                    <Button
                                        style={{marginLeft: 4, color: "#207ee8"}}
                                        type={"ghost"}
                                        ghost={true}
                                        onClick={(e) => {
                                            setCollapsed(!collapsed)
                                        }}
                                        icon={collapsed ? <MenuUnfoldOutlined/> : <MenuFoldOutlined/>}
                                    />
                                )}
                                <Button
                                    style={{marginLeft: 4, color: "#207ee8"}}
                                    type={"ghost"}
                                    ghost={true}
                                    onClick={(e) => {
                                        updateMenuItems()
                                    }}
                                    icon={<ReloadOutlined/>}
                                />
                            </Space>
                        </Col>
                        <Col span={16} style={{textAlign: "right", paddingRight: 28}}>
                            <PerformanceDisplay/>
                            <RiskStatsTag professionalMode={true}/>
                            <Space>
                                {/* {status?.isTLS ? <Tag color={"green"}>TLS:通信已加密</Tag> : <Tag color={"red"}>通信未加密</Tag>} */}
                                {status?.addr && <Tag color={"geekblue"}>{status?.addr}</Tag>}
                                {/* <Tag color={engineStatus === "ok" ? "green" : "red"}>Yak 引擎状态:{engineStatus}</Tag> */}
                                <ReversePlatformStatus/>
                                <Dropdown forceRender={true} overlay={<Menu>
                                    <Menu.Item key={"update"}>
                                        <AutoUpdateYakModuleButton/>
                                    </Menu.Item>
                                    <Menu.Item key={"reverse-global"}>
                                        <ConfigGlobalReverseButton/>
                                    </Menu.Item>
                                </Menu>} trigger={["click"]}>
                                    <Button icon={<SettingOutlined/>}>
                                        配置
                                    </Button>
                                </Dropdown>
                                <Button type={"link"} danger={true} icon={<PoweroffOutlined/>} onClick={() => {
                                    if (winCloseFlag) setWinCloseShow(true)
                                    else {
                                        success("退出当前 Yak 服务器成功")
                                        setEngineStatus("error")
                                    }
                                }}/>
                            </Space>
                        </Col>
                    </Row>
                </Header>
                <Content
                    style={{
                        margin: 12,
                        backgroundColor: "#fff",
                        overflow: "auto"
                    }}
                >
                    <Layout style={{height: "100%", overflow: "hidden"}}>
                        {!hideMenu && (
                            <Sider
                                style={{backgroundColor: "#fff", overflow: "auto"}}
                                collapsed={collapsed}
                            >
                                <Spin spinning={loading}>
                                    <Space
                                        direction={"vertical"}
                                        style={{
                                            width: "100%"
                                        }}
                                    >
                                        <Menu
                                            theme={"light"}
                                            style={{}}
                                            selectedKeys={[]}
                                            mode={"inline"}
                                            onSelect={(e) => {
                                                if (e.key === "ignore") return

                                                const flag = pageCache.filter(item => item.route === (e.key as Route)).length === 0
                                                if (flag) menuAddPage(e.key as Route)
                                                else setCurrentTabKey(e.key)
                                            }}
                                        >
                                            {menuItems.map((i) => {
                                                if (i.Group === "UserDefined") {
                                                    i.Group = "社区插件"
                                                }
                                                return (
                                                    <Menu.SubMenu icon={<EllipsisOutlined/>} key={i.Group}
                                                                  title={i.Group}>
                                                        {i.Items.map((item) => {
                                                            if (item.YakScriptId > 0) {
                                                                return (
                                                                    <MenuItem icon={<EllipsisOutlined/>}
                                                                              key={`plugin:${item.Group}:${item.YakScriptId}`}>
                                                                        <Text
                                                                            ellipsis={{tooltip: true}}>{item.Verbose}</Text>
                                                                    </MenuItem>
                                                                )
                                                            }
                                                            return (
                                                                <MenuItem icon={<EllipsisOutlined/>}
                                                                          key={`batch:${item.Group}:${item.Verbose}:${item.MenuItemId}`}>
                                                                    <Text
                                                                        ellipsis={{tooltip: true}}>{item.Verbose}</Text>
                                                                </MenuItem>
                                                            )

                                                        })}
                                                    </Menu.SubMenu>
                                                )
                                            })}
                                            {(routeMenuData || []).map((i) => {
                                                if (i.subMenuData) {
                                                    return (
                                                        <Menu.SubMenu icon={i.icon} key={i.key} title={i.label}>
                                                            {(i.subMenuData || []).map((subMenu) => {
                                                                return (
                                                                    <MenuItem icon={subMenu.icon} key={subMenu.key}
                                                                              disabled={subMenu.disabled}>
                                                                        <Text
                                                                            ellipsis={{tooltip: true}}>{subMenu.label}</Text>
                                                                    </MenuItem>
                                                                )
                                                            })}
                                                        </Menu.SubMenu>
                                                    )
                                                }
                                                return (
                                                    <MenuItem icon={i.icon} key={i.key} disabled={i.disabled}>
                                                        {i.label}
                                                    </MenuItem>
                                                )
                                            })}
                                        </Menu>
                                    </Space>
                                </Spin>
                            </Sider>
                        )}
                        <Content style={{
                            overflow: "hidden",
                            backgroundColor: "#fff",
                            marginLeft: 12,
                            height: "100%",
                            display: "flex",
                            flexFlow: "column"
                        }}>
                            <div style={{
                                padding: 12,
                                paddingTop: 8,
                                overflow: "hidden",
                                flex: "1",
                                display: "flex",
                                flexFlow: "column"
                            }}>
                                {pageCache.length > 0 ? (
                                    <Tabs
                                        style={{display: "flex", flex: "1"}}
                                        tabBarStyle={{marginBottom: 8}}
                                        className='main-content-tabs yakit-layout-tabs'
                                        activeKey={currentTabKey}
                                        onChange={setCurrentTabKey}
                                        size={"small"}
                                        type={"editable-card"}
                                        renderTabBar={(props, TabBarDefault) => {
                                            return bars(props, TabBarDefault)
                                        }}
                                        hideAdd={true}
                                        onTabClick={(key, e) => {
                                            const divExisted = document.getElementById("yakit-cursor-menu")
                                            if (divExisted) {
                                                const div: HTMLDivElement = divExisted as HTMLDivElement
                                                const unmountResult = ReactDOM.unmountComponentAtNode(div)
                                                if (unmountResult && div.parentNode) {
                                                    div.parentNode.removeChild(div)
                                                }
                                            }
                                        }}
                                    >
                                        {pageCache.map((i) => {
                                            return (
                                                <Tabs.TabPane
                                                    forceRender={true}
                                                    key={i.route}
                                                    tab={i.verbose}
                                                    closeIcon={
                                                        <Space>
                                                            <Popover
                                                                trigger={"click"}
                                                                title={"修改名称"}
                                                                content={
                                                                    <>
                                                                        <Input
                                                                            size={"small"}
                                                                            defaultValue={i.verbose}
                                                                            onBlur={(e) => updateCacheVerbose(i.route, e.target.value)}
                                                                        />
                                                                    </>
                                                                }
                                                            >
                                                                <EditOutlined className='main-container-cion'/>
                                                            </Popover>
                                                            <CloseOutlined
                                                                className='main-container-cion'
                                                                onClick={() => removePage(i.route)}
                                                            />
                                                        </Space>
                                                    }
                                                >
                                                    <div
                                                        style={{
                                                            overflowY: NoScrollRoutes.includes(i.route)
                                                                ? "hidden"
                                                                : "auto",
                                                            overflowX: "hidden",
                                                            height: "100%",
                                                            maxHeight: "100%"
                                                        }}
                                                    >
                                                        {i.singleNode ? (
                                                            i.singleNode
                                                        ) : (
                                                            <MainTabs
                                                                currentTabKey={currentTabKey}
                                                                tabType={i.route}
                                                                pages={i.multipleNode}
                                                                currentKey={i.multipleCurrentKey || ""}
                                                                isShowAdd={true}
                                                                setCurrentKey={(key, type) => {
                                                                    setMultipleCurrentKey(key, type as Route)
                                                                }}
                                                                removePage={(key, type) => {
                                                                    removeMultipleNodePage(key, type as Route)
                                                                }}
                                                                removeOtherPage={(key, type) => {
                                                                    removeOtherMultipleNodePage(key, type as Route)
                                                                }}
                                                                onAddTab={() => menuAddPage(i.route)}
                                                            ></MainTabs>
                                                        )}
                                                    </div>
                                                </Tabs.TabPane>
                                            )
                                        })}
                                    </Tabs>
                                ) : (
                                    <></>
                                )}
                            </div>
                        </Content>
                    </Layout>
                </Content>
            </AutoSpin>

            <Modal
                visible={winCloseShow}
                onCancel={() => setWinCloseShow(false)}
                footer={[
                    <Button key='link' onClick={() => setWinCloseShow(false)}>
                        取消
                    </Button>,
                    <Button key='back' type='primary' onClick={() => {
                        success("退出当前 Yak 服务器成功")
                        setEngineStatus("error")
                    }}>
                        退出
                    </Button>
                ]}
            >
                <div style={{height: 40}}>
                    <ExclamationCircleOutlined style={{fontSize: 22, color: "#faad14"}}/>
                    <span style={{fontSize: 18, marginLeft: 15}}>提示</span>
                </div>
                <p style={{fontSize: 15, marginLeft: 37}}>是否要退出yakit操作界面,一旦退出,界面内打开内容除fuzzer页外都会销毁</p>
                <div style={{marginLeft: 37}}>
                    <Checkbox
                        defaultChecked={!winCloseFlag}
                        value={!winCloseFlag}
                        onChange={() => {
                            setWinCloseFlag(!winCloseFlag)
                            ipcRenderer.invoke("set-value", WindowsCloseFlag, false)
                        }}
                    ></Checkbox>
                    <span style={{marginLeft: 8}}>不再出现该提示信息</span>
                </div>
            </Modal>
            <Modal
                visible={bugTestShow}
                onCancel={() => setBugTestShow(false)}
                footer={[
                    <Button key='link' onClick={() => setBugTestShow(false)}>
                        取消
                    </Button>,
                    <Button key='back' type='primary' onClick={() => {
                        if ((bugTestValue || []).length === 0) return failed("请选择类型后再次提交")
                        addBugTest(2)
                        setBugTestShow(false)
                    }}>
                        确定
                    </Button>
                ]}
            >
                <ItemSelects
                    item={{
                        label: "专项漏洞类型",
                        style: {marginTop: 20}
                    }}
                    select={{
                        allowClear: true,
                        data: BugList.concat(bugList) || [],
                        optText: "title",
                        optValue: "key",
                        value: (bugTestValue || [])[0]?.key,
                        onChange: (value, option: any) => setBugTestValue(value ? [{
                            key: option?.key,
                            title: option?.title
                        }] : [])
                    }}
                ></ItemSelects>
            </Modal>
        </Layout>
    )
}
Example #23
Source File: ChartEditor.tsx    From datart with Apache License 2.0 4 votes vote down vote up
ChartEditor: FC<ChartEditorProps> = ({
  originChart,
  orgId,
  container,
  dataChartId,
  chartType,
  defaultViewId,
  widgetId,
  onClose,
  onSaveInWidget,
  onSaveInDataChart,
}) => {
  const saveFormContextValue = useSaveFormContext();
  const { actions } = useWorkbenchSlice();
  const dispatch = useDispatch();
  const dataset = useSelector(datasetsSelector);
  const dataview = useSelector(currentDataViewSelector);
  const chartConfig = useSelector(chartConfigSelector);
  const shadowChartConfig = useSelector(shadowChartConfigSelector);
  const backendChart = useSelector(backendChartSelector);
  const aggregation = useSelector(aggregationSelector);
  const availableSourceFunctions = useSelector(selectAvailableSourceFunctions);
  const [chart, setChart] = useState<IChart>();
  const drillOptionRef = useRef<IChartDrillOption>();

  const [allowQuery, setAllowQuery] = useState<boolean>(false);
  const history = useHistory();
  const addVizFn = useAddViz({
    showSaveForm: saveFormContextValue.showSaveForm,
  });
  const tg = useI18NPrefix('global');

  const expensiveQuery = useMemo(() => {
    try {
      return dataview?.config
        ? Boolean(JSON.parse(dataview.config).expensiveQuery)
        : false;
    } catch (error) {
      console.log(error);
      return false;
    }
  }, [dataview]);

  useMount(
    () => {
      if (
        (container === 'dataChart' && !dataChartId) ||
        (container === 'widget' && !originChart)
      ) {
        // Note: add default chart if new to editor
        const currentChart = ChartManager.instance().getDefaultChart();
        handleChartChange(currentChart);
      }

      if (container === 'dataChart') {
        dispatch(
          initWorkbenchAction({
            backendChartId: dataChartId,
            orgId,
          }),
        );
      } else {
        //   container === 'widget'
        if (chartType === 'widgetChart') {
          dispatch(
            initWorkbenchAction({
              orgId,
              backendChart: originChart as ChartDTO,
            }),
          );

          if (!originChart) {
            dispatch(actions.updateChartAggregation(true));
          }
        } else {
          // chartType === 'dataChart'
          dispatch(
            initWorkbenchAction({
              orgId,
              backendChartId: dataChartId,
            }),
          );
        }
      }
    },
    () => {
      dispatch(actions.resetWorkbenchState({}));
    },
  );

  useEffect(() => {
    if (backendChart?.config?.chartGraphId) {
      const currentChart = ChartManager.instance().getById(
        backendChart?.config?.chartGraphId,
      );
      registerChartEvents(currentChart);
      setChart(currentChart);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [backendChart?.config?.chartGraphId]);

  useEffect(() => {
    if (!isEmptyArray(chartConfig?.datas) && !drillOptionRef.current) {
      drillOptionRef.current = getChartDrillOption(chartConfig?.datas);
    }
  }, [chartConfig?.datas, drillOptionRef]);

  useEffect(() => {
    if (dataview?.sourceId) {
      dispatch(fetchAvailableSourceFunctions({ sourceId: dataview.sourceId }));
    }
  }, [dataview?.sourceId, dispatch]);

  const resetOriginalComputedFields = useCallback(
    config => {
      const index = config?.datas?.findIndex(
        v => v.type === ChartDataSectionType.GROUP,
      );
      if (index !== undefined) {
        const groupRows = config?.datas?.[index]?.rows;
        if (groupRows) {
          const dateLevelComputedFields = groupRows.filter(
            v =>
              v.category === ChartDataViewFieldCategory.DateLevelComputedField,
          );

          const computedFields = getRuntimeComputedFields(
            dateLevelComputedFields,
            '',
            dataview?.computedFields,
            chartConfig,
          );

          dispatch(
            workbenchSlice.actions.updateCurrentDataViewComputedFields(
              computedFields,
            ),
          );
        }
      }
    },
    [chartConfig, dataview?.computedFields, dispatch],
  );

  const registerChartEvents = useCallback(
    chart => {
      chart?.registerMouseEvents([
        {
          name: 'click',
          callback: param => {
            if (
              drillOptionRef.current?.isSelectedDrill &&
              !drillOptionRef.current.isBottomLevel
            ) {
              const option = drillOptionRef.current;
              option.drillDown(param.data.rowData);
              drillOptionRef.current = option;
              handleDrillOptionChange?.(option);
              return;
            }
            if (
              param.componentType === 'table' &&
              param.seriesType === 'paging-sort-filter'
            ) {
              dispatch(
                refreshDatasetAction({
                  sorter: {
                    column: param?.seriesName!,
                    operator: param?.value?.direction,
                    aggOperator: param?.value?.aggOperator,
                  },
                  pageInfo: {
                    pageNo: param?.value?.pageNo,
                  },
                }),
              );
              return;
            }
            if (param.seriesName === 'richText') {
              dispatch(updateRichTextAction(param.value));
              return;
            }
          },
        },
      ]);
    },
    [dispatch],
  );

  const clearDataConfig = useCallback(() => {
    const currentChart = chart?.meta?.id
      ? ChartManager.instance().getById(chart?.meta?.id)
      : ChartManager.instance().getDefaultChart();
    let targetChartConfig = CloneValueDeep(currentChart?.config);
    registerChartEvents(currentChart);
    setChart(currentChart);

    const finalChartConfig = transferChartConfigs(
      targetChartConfig,
      targetChartConfig,
    );

    dispatch(workbenchSlice.actions.updateCurrentDataViewComputedFields([]));
    dispatch(workbenchSlice.actions.updateShadowChartConfig({}));
    dispatch(
      workbenchSlice.actions.updateChartConfig({
        type: ChartConfigReducerActionType.INIT,
        payload: {
          init: finalChartConfig,
        },
      }),
    );
    drillOptionRef.current = getChartDrillOption(
      chartConfig?.datas,
      drillOptionRef.current,
    );
  }, [dispatch, chart?.meta?.id, registerChartEvents, chartConfig?.datas]);

  const handleChartChange = (c: IChart) => {
    registerChartEvents(c);
    setChart(c);
    const targetChartConfig = CloneValueDeep(c.config);

    const finalChartConfig = clearRuntimeDateLevelFieldsInChartConfig(
      transferChartConfigs(targetChartConfig, shadowChartConfig || chartConfig),
    );

    resetOriginalComputedFields(finalChartConfig);

    dispatch(
      workbenchSlice.actions.updateChartConfig({
        type: ChartConfigReducerActionType.INIT,
        payload: {
          init: finalChartConfig,
        },
      }),
    );
    drillOptionRef.current = getChartDrillOption(
      finalChartConfig?.datas,
      drillOptionRef.current,
    );
    if (!expensiveQuery) {
      dispatch(refreshDatasetAction({ drillOption: drillOptionRef?.current }));
    } else {
      setAllowQuery(true);
    }
  };

  const handleChartConfigChange = useCallback(
    (type, payload) => {
      if (expensiveQuery) {
        dispatch(
          workbenchSlice.actions.updateChartConfig({
            type,
            payload: payload,
          }),
        );
        dispatch(workbenchSlice.actions.updateShadowChartConfig(null));
        setAllowQuery(payload.needRefresh);
        return true;
      }
      // generate runtime computed fields(date level)
      if (
        payload.value.type === ChartDataSectionType.GROUP ||
        payload.value.type === ChartDataSectionType.MIXED
      ) {
        const dateLevelComputedFields = payload.value.rows.filter(
          v => v.category === ChartDataViewFieldCategory.DateLevelComputedField,
        );

        const replacedColName = payload.value.replacedColName;
        const computedFields = getRuntimeComputedFields(
          dateLevelComputedFields,
          replacedColName,
          dataview?.computedFields,
          chartConfig,
        );

        if (replacedColName) {
          payload = updateBy(payload, draft => {
            delete draft.value.replacedColName;
          });
        }

        if (
          JSON.stringify(computedFields) !==
          JSON.stringify(dataview?.computedFields)
        ) {
          dispatch(
            workbenchSlice.actions.updateCurrentDataViewComputedFields(
              computedFields,
            ),
          );
        }
      }

      dispatch(
        updateChartConfigAndRefreshDatasetAction({
          type,
          payload,
          needRefresh: payload.needRefresh,
          updateDrillOption: config => {
            drillOptionRef.current = getChartDrillOption(
              config?.datas,
              drillOptionRef.current,
            );
            return drillOptionRef.current;
          },
        }),
      );
    },
    [chartConfig, dispatch, expensiveQuery, dataview],
  );

  const handleDataViewChanged = useCallback(() => {
    clearDataConfig();
  }, [clearDataConfig]);

  const handleAggregationState = useCallback(() => {
    clearDataConfig();
  }, [clearDataConfig]);

  const buildDataChart = useCallback(() => {
    const dataChartConfig: DataChartConfig = {
      chartConfig: chartConfig!,
      chartGraphId: chart?.meta.id!,
      computedFields: dataview?.computedFields || [],
      aggregation,
    };

    const dataChart: DataChart = {
      id: dataChartId,
      name: backendChart?.name || '',
      viewId: dataview?.id || '',
      orgId: orgId,
      config: dataChartConfig,
      status: 1,
      description: '',
    };
    return dataChart;
  }, [
    backendChart?.name,
    chart,
    chartConfig,
    dataChartId,
    dataview,
    orgId,
    aggregation,
  ]);

  const saveToWidget = useCallback(() => {
    const dataChart = buildDataChart();
    onSaveInWidget?.(chartType, dataChart, dataview);
  }, [buildDataChart, chartType, dataview, onSaveInWidget]);

  const saveChart = useCallback(async () => {
    resetOriginalComputedFields(chartConfig);

    if (container === 'dataChart') {
      if (dataChartId) {
        await dispatch(
          updateChartAction({
            name: backendChart?.name,
            viewId: dataview?.id,
            graphId: chart?.meta?.id,
            chartId: dataChartId,
            index: 0,
            parentId: 0,
            aggregation: aggregation,
          }),
        );
        onSaveInDataChart?.(orgId, dataChartId);
      } else {
        try {
          addVizFn({
            vizType: 'DATACHART',
            type: CommonFormTypes.Add,
            visible: true,
            initialValues: {
              config: JSON.stringify({
                aggregation,
                chartConfig: chartConfig,
                chartGraphId: chart?.meta?.id,
                computedFields: dataview?.computedFields,
              }),
              viewId: dataview?.id,
              avatar: chart?.meta?.id,
            },
            callback: folder => {
              folder &&
                history.push(`/organizations/${orgId}/vizs/${folder.relId}`);
            },
          });
        } catch (error) {
          throw error;
        }
      }
    } else if (container === 'widget') {
      if (chartType === 'widgetChart') {
        saveToWidget();
      } else {
        // dataChart
        confirm({
          title: '保存修改后不能撤销,确定继续保存吗?',
          icon: <ExclamationCircleOutlined />,
          async onOk() {
            dispatch(
              updateChartAction({
                name: backendChart?.name,
                viewId: dataview?.id,
                graphId: chart?.meta?.id,
                chartId: dataChartId,
                index: 0,
                parentId: 0,
                aggregation,
              }),
            );
            saveToWidget();
          },
          onCancel() {
            console.log('Cancel');
          },
        });
      }
    }
  }, [
    container,
    dispatch,
    backendChart?.name,
    dataview?.id,
    chart?.meta?.id,
    dataChartId,
    onSaveInDataChart,
    orgId,
    chartType,
    saveToWidget,
    aggregation,
    addVizFn,
    chartConfig,
    dataview?.computedFields,
    history,
    resetOriginalComputedFields,
  ]);

  const saveChartToDashBoard = useCallback(
    (dashboardId, dashboardType) => {
      const dataChart = buildDataChart();
      try {
        history.push({
          pathname: `/organizations/${orgId}/vizs/${dashboardId}/boardEditor`,
          state: {
            widgetInfo: JSON.stringify({
              chartType,
              dataChart,
              dataview,
              dashboardType,
            }),
          },
        });
      } catch (error) {
        throw error;
      }
    },
    [history, buildDataChart, chartType, dataview, orgId],
  );

  const handleRefreshDataset = useCallback(async () => {
    await dispatch(
      refreshDatasetAction({ drillOption: drillOptionRef?.current }),
    );
    setAllowQuery(false);
  }, [dispatch, drillOptionRef]);

  const handleCreateDownloadDataTask = useCallback(async () => {
    if (!dataview?.id) {
      return;
    }
    const isWidget = dataChartId.includes('widget');
    const builder = new ChartDataRequestBuilder(
      dataview,
      chartConfig?.datas,
      chartConfig?.settings,
      {},
      true,
      aggregation,
    );
    dispatch(
      makeDownloadDataTask({
        downloadParams: [
          {
            ...builder.build(),
            ...{
              analytics: dataChartId ? false : true,
              vizName: backendChart?.name || 'chart',
              vizId: isWidget ? widgetId : dataChartId,
              vizType: isWidget ? 'widget' : 'dataChart',
            },
          },
        ],
        fileName: backendChart?.name || 'chart',
        downloadType: DownloadFileType.Excel,
        resolve: () => {
          dispatch(actions.setChartEditorDownloadPolling(true));
        },
      }),
    );
  }, [
    aggregation,
    backendChart?.name,
    chartConfig?.datas,
    chartConfig?.settings,
    dataChartId,
    dataview,
    dispatch,
    actions,
    widgetId,
  ]);

  const handleDrillOptionChange = (option: IChartDrillOption) => {
    drillOptionRef.current = option;
    dispatch(refreshDatasetAction({ drillOption: option }));
  };

  const handleDateLevelChange = (type, payload) => {
    const rows = getRuntimeDateLevelFields(payload.value?.rows);
    const dateLevelComputedFields = rows.filter(
      v => v.category === ChartDataViewFieldCategory.DateLevelComputedField,
    );
    const replacedColName = payload.value.replacedColName;
    const computedFields = getRuntimeComputedFields(
      dateLevelComputedFields,
      replacedColName,
      dataview?.computedFields,
      chartConfig,
    );

    dispatch(
      workbenchSlice.actions.updateCurrentDataViewComputedFields(
        computedFields,
      ),
    );

    dispatch(
      updateChartConfigAndRefreshDatasetAction({
        type,
        payload,
        needRefresh: payload.needRefresh,
        updateDrillOption: config => {
          drillOptionRef.current = getChartDrillOption(
            config?.datas,
            drillOptionRef.current,
          );
          return drillOptionRef.current;
        },
      }),
    );
  };

  return (
    <StyledChartWorkbenchPage>
      <SaveFormContext.Provider value={saveFormContextValue}>
        <ChartWorkbench
          header={{
            name: backendChart?.name || originChart?.name,
            orgId,
            container,
            onSaveChart: saveChart,
            onSaveChartToDashBoard: saveChartToDashBoard,
            onGoBack: () => {
              onClose?.();
            },
            onChangeAggregation: handleAggregationState,
          }}
          drillOption={drillOptionRef?.current}
          aggregation={aggregation}
          chart={chart}
          dataset={dataset}
          dataview={dataview}
          chartConfig={chartConfig}
          defaultViewId={defaultViewId}
          expensiveQuery={expensiveQuery}
          allowQuery={allowQuery}
          availableSourceFunctions={availableSourceFunctions}
          onChartChange={handleChartChange}
          onChartConfigChange={handleChartConfigChange}
          onChartDrillOptionChange={handleDrillOptionChange}
          onDataViewChange={handleDataViewChanged}
          onRefreshDataset={handleRefreshDataset}
          onCreateDownloadDataTask={handleCreateDownloadDataTask}
          onDateLevelChange={handleDateLevelChange}
        />
        <SaveForm
          width={400}
          formProps={{
            labelAlign: 'left',
            labelCol: { offset: 1, span: 6 },
            wrapperCol: { span: 15 },
          }}
          okText={tg('button.save')}
        />
      </SaveFormContext.Provider>
    </StyledChartWorkbenchPage>
  );
}
Example #24
Source File: YakExecutor.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
YakExecutor: React.FC<YakExecutorProp> = (props) => {
    const [codePath, setCodePath] = useState<string>("")
    const [loading, setLoading] = useState<boolean>(false)
    const [fileList, setFileList] = useState<tabCodeProps[]>([])
    const [tabList, setTabList] = useState<tabCodeProps[]>([])
    const [activeTab, setActiveTab] = useState<string>("")
    const [unTitleCount, setUnTitleCount] = useState(1)

    const [hintShow, setHintShow] = useState<boolean>(false)
    const [hintFile, setHintFile] = useState<string>("")
    const [hintIndex, setHintIndex] = useState<number>(0)

    const [renameHint, setRenameHint] = useState<boolean>(false)
    const [renameIndex, setRenameIndex] = useState<number>(-1)
    const [renameFlag, setRenameFlag] = useState<boolean>(false)
    const [renameCache, setRenameCache] = useState<string>("")

    const [fullScreen, setFullScreen] = useState<boolean>(false)

    const [errors, setErrors] = useState<string[]>([])
    const [executing, setExecuting] = useState(false)
    const [outputEncoding, setOutputEncoding] = useState<"utf8" | "latin1">("utf8")
    const xtermAsideRef = useRef(null)
    const xtermRef = useRef(null)
    const timer = useRef<any>(null)

    const [extraParams, setExtraParams] = useState("")

    // trigger for updating
    const [triggerForUpdatingHistory, setTriggerForUpdatingHistory] = useState<any>(0)

    const addFileTab = useMemoizedFn((res: any) => {
        const {name, code} = res

        const tab: tabCodeProps = {
            tab: `${name}.yak`,
            code: code,
            suffix: "yak",
            isFile: false
        }
        setActiveTab(`${tabList.length}`)
        setTabList(tabList.concat([tab]))
        setUnTitleCount(unTitleCount + 1)
    })

    useEffect(() => {
        ipcRenderer.on("fetch-send-to-yak-running", (e, res: any) => addFileTab(res))
        return () => ipcRenderer.removeAllListeners("fetch-send-to-yak-running")
    }, [])

    // 自动保存
    const autoSave = useMemoizedFn(() => {
        for (let tabInfo of tabList) {
            if (tabInfo.isFile) {
                ipcRenderer.invoke("write-file", {
                    route: tabInfo.route,
                    data: tabInfo.code
                })
            }
        }
    })
    // 保存近期文件内的15个
    const saveFiliList = useMemoizedFn(() => {
        let files = cloneDeep(fileList).reverse()
        files.splice(14)
        files = files.reverse()
        ipcRenderer.invoke("set-value", RecentFileList, files)
    })

    // 获取和保存近期打开文件信息,同时展示打开默认内容
    useEffect(() => {
        let time: any = null
        let timer: any = null
        setLoading(true)
        ipcRenderer
            .invoke("get-value", RecentFileList)
            .then((value: any) => {
                if ((value || []).length !== 0) {
                    setFileList(value)
                } else {
                    const tab: tabCodeProps = {
                        tab: `Untitle-${unTitleCount}.yak`,
                        code: "# input your yak code\nprintln(`Hello Yak World!`)",
                        suffix: "yak",
                        isFile: false
                    }
                    setActiveTab(`${tabList.length}`)
                    setTabList([tab])
                    setUnTitleCount(unTitleCount + 1)
                }
            })
            .catch(() => {})
            .finally(() => {
                setTimeout(() => setLoading(false), 300)
                time = setInterval(() => {
                    autoSave()
                }, 2000)
                timer = setInterval(() => {
                    saveFiliList()
                }, 5000)
            })

        return () => {
            saveFiliList()
            if (time) clearInterval(time)
            if (timer) clearInterval(timer)
        }
    }, [])

    // 全局监听重命名事件是否被打断
    useEffect(() => {
        document.onmousedown = (e) => {
            // @ts-ignore
            if (e.path[0].id !== "rename-input" && renameFlag) {
                renameCode(renameIndex)
                setRenameFlag(false)
            }
        }
    }, [renameFlag])

    // 打开文件
    const addFile = useMemoizedFn((file: any) => {
        const isExists = fileList.filter((item) => item.tab === file.name && item.route === file.path).length === 1

        if (isExists) {
            for (let index in tabList) {
                const item = tabList[index]
                if (item.tab === file.name && item.route === file.path) {
                    setActiveTab(`${index}`)
                    return false
                }
            }
        }
        ipcRenderer
            .invoke("fetch-file-content", file.path)
            .then((res) => {
                const tab: tabCodeProps = {
                    tab: file.name,
                    code: res,
                    suffix: file.name.split(".").pop() === "yak" ? "yak" : "http",
                    isFile: true,
                    route: file.path,
                    extraParams: file.extraParams
                }
                setActiveTab(`${tabList.length}`)
                if (!isExists) setFileList(fileList.concat([tab]))
                setTabList(tabList.concat([tab]))
            })
            .catch(() => {
                failed("无法获取该文件内容,请检查后后重试!")
                const files = cloneDeep(fileList)
                for (let i in files) if (files[i].route === file.path) files.splice(i, 1)
                setFileList(files)
            })
        return false
    })
    // 新建文件
    const newFile = useMemoizedFn(() => {
        const tab: tabCodeProps = {
            tab: `Untitle-${unTitleCount}.yak`,
            code: "# input your yak code\nprintln(`Hello Yak World!`)",
            suffix: "yak",
            isFile: false
        }
        setActiveTab(`${tabList.length}`)
        setTabList(tabList.concat([tab]))
        setUnTitleCount(unTitleCount + 1)
    })
    //修改文件
    const modifyCode = useMemoizedFn((value: string, index: number) => {
        const tabs = cloneDeep(tabList)
        tabs[index].code = value
        setTabList(tabs)
    })
    // 保存文件
    const saveCode = useMemoizedFn((info: tabCodeProps, index: number) => {
        if (info.isFile) {
            ipcRenderer.invoke("write-file", {
                route: info.route,
                data: info.code
            })
        } else {
            ipcRenderer.invoke("show-save-dialog", `${codePath}${codePath ? '/' : ''}${info.tab}`).then((res) => {
                if (res.canceled) return

                const path = res.filePath
                const name = res.name
                ipcRenderer
                    .invoke("write-file", {
                        route: res.filePath,
                        data: info.code
                    })
                    .then(() => {
                        const suffix = name.split(".").pop()

                        var tabs = cloneDeep(tabList)
                        var active = null
                        tabs = tabs.filter((item) => item.route !== path)
                        tabs = tabs.map((item, index) => {
                            if (!item.route && item.tab === info.tab) {
                                active = index
                                item.tab = name
                                item.isFile = true
                                item.suffix = suffix === "yak" ? suffix : "http"
                                item.route = path
                                return item
                            }
                            return item
                        })
                        if (active !== null) setActiveTab(`${active}`)
                        setTabList(tabs)
                        const file: tabCodeProps = {
                            tab: name,
                            code: info.code,
                            isFile: true,
                            suffix: suffix === "yak" ? suffix : "http",
                            route: res.filePath,
                            extraParams: info.extraParams
                        }
                        for (let item of fileList) {
                            if (item.route === file.route) {
                                return
                            }
                        }
                        setFileList(fileList.concat([file]))
                    })
            })
        }
    })
    //关闭文件
    const closeCode = useMemoizedFn((index, isFileList: boolean) => {
        const tabInfo = isFileList ? fileList[+index] : tabList[+index]
        if (isFileList) {
            for (let i in tabList) {
                if (tabList[i].tab === tabInfo.tab && tabList[i].route === tabInfo.route) {
                    const tabs = cloneDeep(tabList)
                    tabs.splice(i, 1)
                    setTabList(tabs)
                    setActiveTab(tabs.length >= 1 ? `0` : "")
                }
            }
            const files = cloneDeep(fileList)
            files.splice(+index, 1)
            setFileList(files)
        } else {
            setActiveTab(index)

            if (!tabInfo.isFile) {
                setHintFile(tabInfo.tab)
                setHintIndex(index)
                setHintShow(true)
            } else {
                const tabs = cloneDeep(tabList)
                tabs.splice(+index, 1)
                setTabList(tabs)
                setActiveTab(tabs.length >= 1 ? `0` : "")
            }
        }
    })
    // 关闭虚拟文件不保存
    const ownCloseCode = useMemoizedFn(() => {
        const tabs = cloneDeep(tabList)
        tabs.splice(hintIndex, 1)
        setTabList(tabs)
        setHintShow(false)
        setActiveTab(tabs.length >= 1 ? `0` : "")
    })
    // 删除文件
    const delCode = useMemoizedFn((index) => {
        const fileInfo = fileList[index]

        ipcRenderer
            .invoke("delelte-code-file", fileInfo.route)
            .then(() => {
                for (let i in tabList) {
                    if (tabList[i].tab === fileInfo.tab && tabList[i].route === fileInfo.route) {
                        const tabs = cloneDeep(tabList)
                        tabs.splice(i, 1)
                        setTabList(tabs)
                        setActiveTab(tabs.length >= 1 ? `0` : "")
                    }
                }
                const arr = cloneDeep(fileList)
                arr.splice(index === undefined ? hintIndex : index, 1)
                setFileList(arr)
            })
            .catch(() => {
                failed("文件删除失败!")
            })
    })
    //重命名操作
    const renameCode = useMemoizedFn((index: number) => {
        const tabInfo = fileList[index]

        if (renameCache === tabInfo.tab) return
        if (!renameCache) return

        if (!tabInfo.route) return
        const flagStr = tabInfo.route?.indexOf("/") > -1 ? "/" : "\\"
        const routes = tabInfo.route?.split(flagStr)
        routes?.pop()
        ipcRenderer
            .invoke("is-exists-file", routes?.concat([renameCache]).join(flagStr))
            .then(() => {
                const newRoute = routes?.concat([renameCache]).join(flagStr)
                if (!tabInfo.route || !newRoute) return
                renameFile(index, renameCache, tabInfo.route, newRoute)
            })
            .catch(() => {
                setRenameHint(true)
            })
    })
    // 重命名文件
    const renameFile = useMemoizedFn(
        (index: number, rename: string, oldRoute: string, newRoute: string, callback?: () => void) => {
            ipcRenderer.invoke("rename-file", {old: oldRoute, new: newRoute}).then(() => {
                const suffix = rename.split(".").pop()

                var files = cloneDeep(fileList)
                var tabs = cloneDeep(tabList)
                var active = null
                files = files.filter((item) => item.route !== newRoute)
                tabs = tabs.filter((item) => item.route !== newRoute)

                files = files.map((item) => {
                    if (item.route === oldRoute) {
                        item.tab = rename
                        item.suffix = suffix === "yak" ? suffix : "http"
                        item.route = newRoute
                        return item
                    }
                    return item
                })
                tabs = tabs.map((item, index) => {
                    if (item.route === oldRoute) {
                        active = index
                        item.tab = rename
                        item.suffix = suffix === "yak" ? suffix : "http"
                        item.route = newRoute
                        return item
                    }
                    return item
                })
                if (active !== null) setActiveTab(`${active}`)
                setFileList(files)
                setTabList(tabs)

                if (callback) callback()
            })
        }
    )

    const fileFunction = (kind: string, index: string, isFileList: boolean) => {
        const tabCodeInfo = isFileList ? fileList[index] : tabList[index]

        switch (kind) {
            case "own":
                closeCode(index, isFileList)
                return
            case "other":
                const tabInfo: tabCodeProps = cloneDeep(tabList[index])
                for (let i in tabList) {
                    if (i !== index && !tabList[i].isFile) {
                        const arr: tabCodeProps[] =
                            +i > +index
                                ? [tabInfo].concat(tabList.splice(+i, tabList.length))
                                : tabList.splice(+i, tabList.length)
                        const num = +i > +index ? 1 : 0

                        setActiveTab(`${num}`)
                        setTabList(arr)
                        setHintFile(arr[num].tab)
                        setHintIndex(num)
                        setHintShow(true)
                        return
                    }
                }
                const code = cloneDeep(tabList[index])
                setTabList([code])
                setActiveTab(`0`)
                return
            case "all":
                for (let i in tabList) {
                    if (!tabList[i].isFile) {
                        const arr = tabList.splice(+i, tabList.length)
                        setActiveTab("0")
                        setTabList(arr)
                        setHintFile(arr[0].tab)
                        setHintIndex(0)
                        setHintShow(true)
                        return
                    }
                }
                setActiveTab("")
                setTabList([])
                return
            case "remove":
                closeCode(index, isFileList)
                return
            case "delete":
                delCode(index)
                return
            case "rename":
                setRenameIndex(+index)
                setRenameFlag(true)
                setRenameCache(tabCodeInfo.tab)
                return
        }
    }

    const openFileLayout = (file: any) => {
        addFile(file)
    }

    useEffect(() => {
        ipcRenderer.invoke("fetch-code-path")
            .then((path: string) => {
                ipcRenderer.invoke("is-exists-file", path)
                    .then(() => {
                        setCodePath("")
                    })
                    .catch(() => {
                        setCodePath(path)
                    })
            })
    }, [])

    useEffect(() => {
        if (tabList.length === 0) setFullScreen(false)
    }, [tabList])

    useEffect(() => {
        if (!xtermRef) {
            return
        }
        // let buffer = "";
        ipcRenderer.on("client-yak-error", async (e: any, data) => {
            failed(`FoundError: ${JSON.stringify(data)}`)
            if (typeof data === "object") {
                setErrors([...errors, `${JSON.stringify(data)}`])
            } else if (typeof data === "string") {
                setErrors([...errors, data])
            } else {
                setErrors([...errors, `${data}`])
            }
        })
        ipcRenderer.on("client-yak-end", () => {
            info("Yak 代码执行完毕")
            setTriggerForUpdatingHistory(getRandomInt(100000))
            setTimeout(() => {
                setExecuting(false)
            }, 300)
        })
        ipcRenderer.on("client-yak-data", async (e: any, data: ExecResult) => {
            if (data.IsMessage) {
                // alert(Buffer.from(data.Message).toString("utf8"))
            }
            if (data?.Raw) {
                writeExecResultXTerm(xtermRef, data, outputEncoding)
                // writeXTerm(xtermRef, Buffer.from(data.Raw).toString(outputEncoding).replaceAll("\n", "\r\n"))
                // monacoEditorWrite(currentOutputEditor, )
            }
        })
        return () => {
            ipcRenderer.removeAllListeners("client-yak-data")
            ipcRenderer.removeAllListeners("client-yak-end")
            ipcRenderer.removeAllListeners("client-yak-error")
        }
    }, [xtermRef])

    const bars = (props: any, TabBarDefault: any) => {
        return (
            <TabBarDefault
                {...props}
                children={(barNode: React.ReactElement) => {
                    return (
                        <Dropdown
                            overlay={CustomMenu(barNode.key, false, tabMenu, fileFunction)}
                            trigger={["contextMenu"]}
                        >
                            {barNode}
                        </Dropdown>
                    )
                }}
            />
        )
    }

    return (
        <AutoCard
            className={"yak-executor-body"}
            // title={"Yak Runner"}
            headStyle={{minHeight: 0}}
            bodyStyle={{padding: 0, overflow: "hidden"}}
        >
            <div
                style={{width: "100%", height: "100%", display: "flex", backgroundColor: "#E8E9E8"}}
                tabIndex={0}
                onKeyDown={(e) => {
                    if (e.keyCode === 78 && (e.ctrlKey || e.metaKey)) {
                        newFile()
                    }
                    if (e.keyCode === 83 && (e.ctrlKey || e.metaKey) && activeTab) {
                        saveCode(tabList[+activeTab], +activeTab)
                    }
                }}
            >
                <div style={{width: `${fullScreen ? 0 : 15}%`}}>
                    <AutoSpin spinning={loading}>
                        <ExecutorFileList
                            lists={fileList}
                            activeFile={tabList[+activeTab]?.route || ""}
                            renameFlag={renameFlag}
                            renameIndex={renameIndex}
                            renameCache={renameCache}
                            setRenameCache={setRenameCache}
                            addFile={addFile}
                            newFile={newFile}
                            openFile={openFileLayout}
                            fileFunction={fileFunction}
                        />
                    </AutoSpin>
                </div>
                <div style={{width: `${fullScreen ? 100 : 85}%`}} className='executor-right-body'>
                    {tabList.length > 0 && (
                        <ResizeBox
                            isVer
                            firstNode={
                                <Tabs
                                    className={"right-editor"}
                                    style={{height: "100%"}}
                                    type='editable-card'
                                    activeKey={activeTab}
                                    hideAdd={true}
                                    onChange={(activeTab) => setActiveTab(activeTab)}
                                    onEdit={(key, event: "add" | "remove") => {
                                        switch (event) {
                                            case "remove":
                                                closeCode(key, false)
                                                return
                                            case "add":
                                                return
                                        }
                                    }}
                                    renderTabBar={(props, TabBarDefault) => {
                                        return bars(props, TabBarDefault)
                                    }}
                                    tabBarExtraContent={
                                        tabList.length && (
                                            <Space style={{marginRight: 5}} size={0}>
                                                <Button
                                                    style={{height: 25}}
                                                    type={"link"}
                                                    size={"small"}
                                                    disabled={
                                                        tabList[+activeTab] && tabList[+activeTab].suffix !== "yak"
                                                    }
                                                    onClick={(e) => {
                                                        let m = showDrawer({
                                                            width: "60%",
                                                            placement: "left",
                                                            title: "选择你的 Yak 模块执行特定功能",
                                                            content: (
                                                                <>
                                                                    <YakScriptManagerPage
                                                                        type={"yak"}
                                                                        onLoadYakScript={(s) => {
                                                                            const tab: tabCodeProps = {
                                                                                tab: `Untitle-${unTitleCount}.yak`,
                                                                                code: s.Content,
                                                                                suffix: "yak",
                                                                                isFile: false
                                                                            }
                                                                            info(`加载 Yak 模块:${s.ScriptName}`)
                                                                            xtermClear(xtermRef)
                                                                            setActiveTab(`${tabList.length}`)
                                                                            setTabList(tabList.concat([tab]))
                                                                            setUnTitleCount(unTitleCount + 1)
                                                                            m.destroy()
                                                                        }}
                                                                    />
                                                                </>
                                                            )
                                                        })
                                                    }}
                                                >
                                                    Yak脚本模板
                                                </Button>

                                                <Button
                                                    icon={
                                                        fullScreen ? (
                                                            <FullscreenExitOutlined style={{fontSize: 15}} />
                                                        ) : (
                                                            <FullscreenOutlined style={{fontSize: 15}} />
                                                        )
                                                    }
                                                    type={"link"}
                                                    size={"small"}
                                                    style={{width: 30, height: 25}}
                                                    onClick={() => setFullScreen(!fullScreen)}
                                                />
                                                <Popover
                                                    trigger={["click"]}
                                                    title={"设置命令行额外参数"}
                                                    placement="bottomRight"
                                                    content={
                                                        <Space style={{width: 400}}>
                                                            <div>yak {tabList[+activeTab]?.tab || "[file]"}</div>
                                                            <Divider type={"vertical"} />
                                                            <Paragraph
                                                                style={{width: 200, marginBottom: 0}}
                                                                editable={{
                                                                    icon: <Space>
                                                                        <EditOutlined />
                                                                        <SaveOutlined onClick={(e) => {
                                                                            e.stopPropagation()
                                                                            tabList[+activeTab].extraParams = extraParams
                                                                            setTabList(tabList)
                                                                            if(tabList[+activeTab].isFile){
                                                                                const files = fileList.map(item => {
                                                                                    if(item.route === tabList[+activeTab].route){
                                                                                        item.extraParams = extraParams
                                                                                        return item
                                                                                    }
                                                                                    return item
                                                                                })
                                                                                setFileList(files)
                                                                            }
                                                                            success("保存成功")
                                                                        }} 
                                                                    /></Space>,
                                                                    tooltip: '编辑/保存为该文件默认参数',
                                                                    onChange: setExtraParams
                                                                }}
                                                            >
                                                                {extraParams}
                                                            </Paragraph>
                                                        </Space>
                                                    }
                                                >
                                                    <Button type={"link"} icon={<EllipsisOutlined />} onClick={() => {
                                                        setExtraParams(tabList[+activeTab]?.extraParams || "")
                                                    }} />
                                                </Popover>
                                                {executing ? (
                                                    <Button
                                                        icon={<PoweroffOutlined style={{fontSize: 15}} />}
                                                        type={"link"}
                                                        danger={true}
                                                        size={"small"}
                                                        style={{width: 30, height: 25}}
                                                        onClick={() => ipcRenderer.invoke("cancel-yak")}
                                                    />
                                                ) : (
                                                    <Button
                                                        icon={<CaretRightOutlined style={{fontSize: 15}} />}
                                                        type={"link"}
                                                        ghost={true}
                                                        size={"small"}
                                                        style={{width: 30, height: 25}}
                                                        disabled={
                                                            tabList[+activeTab] && tabList[+activeTab].suffix !== "yak"
                                                        }
                                                        onClick={() => {
                                                            setErrors([])
                                                            setExecuting(true)
                                                            ipcRenderer.invoke("exec-yak", {
                                                                Script: tabList[+activeTab].code,
                                                                Params: [],
                                                                RunnerParamRaw: extraParams
                                                            })
                                                        }}
                                                    />
                                                )}
                                            </Space>
                                        )
                                    }
                                >
                                    {tabList.map((item, index) => {
                                        return (
                                            <TabPane tab={item.tab} key={`${index}`}>
                                                <div style={{height: "100%"}}>
                                                    <AutoSpin spinning={executing}>
                                                        <div style={{height: "100%"}}>
                                                            <YakEditor
                                                                type={item.suffix}
                                                                value={item.code}
                                                                setValue={(value) => {
                                                                    modifyCode(value, index)
                                                                }}
                                                            />
                                                        </div>
                                                    </AutoSpin>
                                                </div>
                                            </TabPane>
                                        )
                                    })}
                                </Tabs>
                            }
                            firstRatio='70%'
                            secondNode={
                                <div
                                    ref={xtermAsideRef}
                                    style={{
                                        width: "100%",
                                        height: "100%",
                                        overflow: "hidden",
                                        borderTop: "1px solid #dfdfdf"
                                    }}
                                >
                                    <Tabs
                                        style={{height: "100%"}}
                                        className={"right-xterm"}
                                        size={"small"}
                                        tabBarExtraContent={
                                            <Space>
                                                <SelectOne
                                                    formItemStyle={{marginBottom: 0}}
                                                    value={outputEncoding}
                                                    setValue={setOutputEncoding}
                                                    size={"small"}
                                                    data={[
                                                        {text: "GBxxx编码", value: "latin1"},
                                                        {text: "UTF-8编码", value: "utf8"}
                                                    ]}
                                                />
                                                <Button
                                                    size={"small"}
                                                    icon={<DeleteOutlined />}
                                                    type={"link"}
                                                    onClick={(e) => {
                                                        xtermClear(xtermRef)
                                                    }}
                                                />
                                            </Space>
                                        }
                                    >
                                        <TabPane
                                            tab={<div style={{width: 50, textAlign: "center"}}>输出</div>}
                                            key={"output"}
                                        >
                                            <div style={{width: "100%", height: "100%"}}>
                                                <CVXterm
                                                    ref={xtermRef}
                                                    options={{
                                                        convertEol: true,
                                                        theme: {
                                                            foreground: "#536870",
                                                            background: "#E8E9E8",
                                                            cursor: "#536870",

                                                            black: "#002831",
                                                            brightBlack: "#001e27",

                                                            red: "#d11c24",
                                                            brightRed: "#bd3613",

                                                            green: "#738a05",
                                                            brightGreen: "#475b62",

                                                            yellow: "#a57706",
                                                            brightYellow: "#536870",

                                                            blue: "#2176c7",
                                                            brightBlue: "#708284",

                                                            magenta: "#c61c6f",
                                                            brightMagenta: "#5956ba",

                                                            cyan: "#259286",
                                                            brightCyan: "#819090",

                                                            white: "#eae3cb",
                                                            brightWhite: "#fcf4dc"
                                                        }
                                                    }}
                                                />
                                            </div>
                                        </TabPane>
                                        <TabPane
                                            tab={
                                                <div style={{width: 50, textAlign: "center"}} key={"terminal"}>
                                                    终端(监修中)
                                                </div>
                                            }
                                            disabled
                                        >
                                            <Terminal />
                                        </TabPane>
                                    </Tabs>
                                </div>
                            }
                            secondRatio='30%'
                        />
                    )}
                    {tabList.length === 0 && (
                        <Empty className='right-empty' description={<p>请点击左侧打开或新建文件</p>}></Empty>
                    )}
                </div>

                <Modal
                    visible={hintShow}
                    onCancel={() => setHintShow(false)}
                    footer={[
                        <Button key='link' onClick={() => setHintShow(false)}>
                            取消
                        </Button>,
                        <Button key='submit' onClick={() => ownCloseCode()}>
                            不保存
                        </Button>,
                        <Button key='back' type='primary' onClick={() => saveCode(tabList[hintIndex], hintIndex)}>
                            保存
                        </Button>
                    ]}
                >
                    <div style={{height: 40}}>
                        <ExclamationCircleOutlined style={{fontSize: 22, color: "#faad14"}} />
                        <span style={{fontSize: 18, marginLeft: 15}}>文件未保存</span>
                    </div>
                    <p style={{fontSize: 15, marginLeft: 37}}>{`是否要保存${hintFile}里面的内容吗?`}</p>
                </Modal>

                <Modal
                    visible={renameHint}
                    onCancel={() => setHintShow(false)}
                    footer={[
                        <Button key='link' onClick={() => setRenameHint(false)}>
                            取消
                        </Button>,
                        <Button
                            key='back'
                            type='primary'
                            onClick={() => {
                                const oldRoute = tabList[renameIndex].route
                                if (!oldRoute) return
                                const flagStr = oldRoute?.indexOf("/") > -1 ? "/" : "\\"
                                const routes = oldRoute?.split(flagStr)
                                routes?.pop()
                                const newRoute = routes?.concat([renameCache]).join(flagStr)
                                if (!oldRoute || !newRoute) return
                                renameFile(renameIndex, renameCache, oldRoute, newRoute, () => {
                                    setRenameHint(false)
                                })
                            }}
                        >
                            确定
                        </Button>
                    ]}
                >
                    <div style={{height: 40}}>
                        <ExclamationCircleOutlined style={{fontSize: 22, color: "#faad14"}} />
                        <span style={{fontSize: 18, marginLeft: 15}}>文件已存在</span>
                    </div>
                    <p style={{fontSize: 15, marginLeft: 37}}>{`是否要覆盖已存在的文件吗?`}</p>
                </Modal>
            </div>
        </AutoCard>
    )
}
Example #25
Source File: DeletePostModal.tsx    From foodie with MIT License 4 votes vote down vote up
DeletePostModal: React.FC<IProps> = (props) => {
    const [isDeleting, setIsDeleting] = useState(false);
    const [error, setError] = useState<IError | null>(null);
    const didMount = useDidMount();
    const dispatch = useDispatch();
    const { targetPost, isOpen } = useSelector((state: IRootReducer) => ({
        targetPost: state.helper.targetPost,
        isOpen: state.modal.isOpenDeletePost
    }))

    const handleDeletePost = async () => {
        try {
            setIsDeleting(true);
            await deletePost(targetPost?.id as string);

            didMount && setIsDeleting(false);

            closeModal();
            props.deleteSuccessCallback(targetPost?.id as string);
            toast.dark('Post successfully deleted.', {
                progressStyle: { backgroundColor: '#4caf50' }
            });
        } catch (e) {
            if (didMount) {
                setIsDeleting(false);
                setError(e);
            }
        }
    };

    const closeModal = () => {
        if (isOpen && !isDeleting) {
            dispatch(setTargetPost(null));
            dispatch(hideModal(EModalType.DELETE_POST));
        }
    }

    return (
        <Modal
            isOpen={isOpen}
            onAfterOpen={props.onAfterOpen}
            onRequestClose={closeModal}
            contentLabel="Example Modal"
            className="modal"
            shouldCloseOnOverlayClick={!isDeleting}
            overlayClassName="modal-overlay"
        >
            <div className="relative">
                <div
                    className="absolute right-2 top-2 p-1 rounded-full flex items-center justify-center cursor-pointer hover:bg-gray-200 dark:hover:bg-indigo-1100"
                    onClick={closeModal}
                >
                    <CloseOutlined className="p-2  outline-none text-gray-500 dark:text-white" />
                </div>
                {error && (
                    <span className="p-4 bg-red-100 text-red-500 block">
                        {error?.error?.message || 'Unable to process your request.'}
                    </span>
                )}
                <div className="p-4 laptop:px-8">
                    <h2 className="dark:text-white">
                        <ExclamationCircleOutlined className="text-red-500 mr-2 pt-2" />
                        Delete Post
                    </h2>
                    <p className="text-gray-600 my-4 dark:text-gray-400">Are you sure you want to delete this post?</p>
                    <div className="flex justify-between">
                        <button
                            className="button--muted !rounded-full dark:bg-indigo-1100 dark:text-white dark:hover:bg-indigo-1100"
                            onClick={closeModal}
                            disabled={isDeleting}
                        >
                            Cancel
                        </button>
                        <button
                            className="button--danger"
                            disabled={isDeleting}
                            onClick={handleDeletePost}
                        >
                            Delete
                        </button>
                    </div>
                </div>
            </div>

        </Modal>
    );
}
Example #26
Source File: QueueListSharedComponents.tsx    From office-hours with GNU General Public License v3.0 4 votes vote down vote up
export function QueueInfoColumn({
  queueId,
  isStaff,
  buttons,
}: QueueInfoColumnProps): ReactElement {
  const { queue, mutateQueue } = useQueue(queueId);

  const disableQueue = async () => {
    await API.queues.disable(queueId);
    await mutateQueue();
    message.success("Successfully disabled queue: " + queue.room);
    await Router.push("/");
  };

  const clearQueue = async () => {
    await API.queues.clean(queueId);
    await mutateQueue();
    message.success("Successfully cleaned queue: " + queue.room);
  };

  const confirmDisable = () => {
    confirm({
      title: `Please Confirm!`,
      icon: <ExclamationCircleOutlined />,
      style: { whiteSpace: "pre-wrap" },
      content: `Please confirm that you want to disable the queue: ${queue.room}.\n
      This queue will no longer appear in the app, and any students currently in the queue will be removed.`,
      onOk() {
        disableQueue();
      },
    });
  };

  return (
    <InfoColumnContainer>
      <QueueInfo>
        <QueueRoomGroup>
          <QueueTitle data-cy="room-title">
            {queue?.room} {queue?.isDisabled && <b>(disabled)</b>}
          </QueueTitle>
          {!queue.allowQuestions && (
            <Tooltip title="This queue is no longer accepting questions">
              <StopOutlined
                data-cy="stopQuestions"
                style={{ color: "red", fontSize: "24px", marginLeft: "8px" }}
              />
            </Tooltip>
          )}
        </QueueRoomGroup>

        {queue?.isProfessorQueue && (
          <QueuePropertyRow>
            <Tag color="blue">Professor Queue</Tag>
          </QueuePropertyRow>
        )}
      </QueueInfo>
      {queue?.notes && (
        <QueuePropertyRow>
          <NotificationOutlined />
          <QueueText>
            <Linkify
              componentDecorator={(decoratedHref, decoratedText, key) => (
                <a
                  target="_blank"
                  rel="noopener noreferrer"
                  href={decoratedHref}
                  key={key}
                >
                  {decoratedText}
                </a>
              )}
            >
              <QueuePropertyText>{queue.notes}</QueuePropertyText>
            </Linkify>
          </QueueText>
        </QueuePropertyRow>
      )}
      <QueueUpToDateInfo queueId={queueId} />
      {buttons}
      <StaffH2>Staff</StaffH2>
      <TAStatuses queueId={queueId} />
      {isStaff && (
        <QueueManagementBox>
          <Popconfirm
            title={
              "Are you sure you want to clear all students from the queue?"
            }
            okText="Yes"
            cancelText="No"
            placement="top"
            arrowPointAtCenter={true}
            onConfirm={clearQueue}
          >
            <ClearQueueButton>Clear Queue</ClearQueueButton>
          </Popconfirm>
          <DisableQueueButton
            onClick={confirmDisable}
            data-cy="queue-disable-button"
            disabled={queue?.isDisabled}
          >
            {queue?.isDisabled ? `Queue deleted` : `Delete Queue`}
          </DisableQueueButton>
        </QueueManagementBox>
      )}
    </InfoColumnContainer>
  );
}
Example #27
Source File: index.tsx    From scorpio-h5-design with MIT License 4 votes vote down vote up
Component = function() {
  const { queryAllWithComponent } = Model.useContainer();
  const [componentDraw, setComponentDraw] = useState({
    category: {},
    component: {},
    visible: false,
    data: {},
  });
  const addComponentReq = useRequest(service.addComponent, {
    manual: true,
  });
  const editComponentReq = useRequest(service.editComponent, {
    manual: true,
  });
  const deleteComponentReq = useRequest(service.deleteComponent, {
    manual: true,
  });

  const edit = function({ _id }: any) {
    history.push(`/manage/component/detail?componentId=${_id}`);
  };

  const onCancel = function() {
    setComponentDraw({
      category: {},
      component: {},
      visible: false,
      data: {},
    });
  };
  const onSubmit = async function(values: any) {
    // @ts-expect-error
    if(!componentDraw.component._id){
      await addComponentReq.run({
        // @ts-expect-error
        categoryId: componentDraw.category._id,
        ...values,
      });
    }else{
      await editComponentReq.run({
        // @ts-expect-error
        _id: componentDraw.component._id,
        ...values,
      });
    }
    setComponentDraw({
      category: {},
      component: {},
      visible: false,
      data: {},
    });
    await queryAllWithComponent.refresh();
  };
  const onAddComponent = function(category: any) {
    setComponentDraw({
      category,
      component: {},
      visible: true,
      data: {},
    });
  };

  const copyId = function(component: any) {
    //
  };

  const dev = async function(component: any) {
    // @ts-expect-error
    if(!h5Lib[component._id]){
      return message.error('组件不存在,请检查本地h5lib目录');
    }
    history.push(`/manage/component/detail?componentId=${component._id}`);
  };

  const onEditComponent = function(component: any, category: any){
    setComponentDraw({
      category,
      visible: true,
      data: {...component},
      component,
    });
  };

  const onDeleteComponent = function(component: any) {
    confirm({
      title: '确认删除此组件?',
      icon: <ExclamationCircleOutlined />,
      content: '请确保组件未被其他页面引用',
      okText: '确认',
      okType: 'danger',
      cancelText: '取消',
      onOk: async()=> {
        await deleteComponentReq.run({
          _id: component._id,
        });
        await queryAllWithComponent.refresh();
      },
    });
  };

  return (
    <PageHeader
      className="manage-component"
      ghost={false}
      onBack={() => null}
      title="组件开发"
      subTitle="开发流程:申请组件ID->h5Lib目录下新增组件->组件开发页面编辑、测试"
    >
      <Spin spinning={queryAllWithComponent.loading}>
        <Tabs tabPosition='left'>
          {
            queryAllWithComponent.data.map((category: any) => (
              <TabPane tab={<>{category.name}<Badge count={category.components.length} /></>} key={category._id}>
                <Button
                  type="dashed"
                  block
                  className="manage-component-add-btn"
                  icon={<PlusOutlined />}
                  onClick={() => onAddComponent(category)}
                >新增</Button>
                {
                  category.components.map((component: any) => (
                    <Card
                      key={component._id}
                      className="manage-component-card"
                      // cover={
                      //   component.cover ? <img
                      //     alt="example"
                      //     src={component.cover}
                      //   /> : <Empty description={false} />
                      // }
                      actions={[
                        <Tooltip title={`ID:${component._id}`} key="id">
                          <Typography.Link onClick={() => copyId(component)}>ID</Typography.Link>
                        </Tooltip>,
                        <Typography.Link key="edit" onClick={() => dev(component)}>组件开发</Typography.Link>,
                        <Dropdown
                          key="more"
                          trigger={['click']}
                          overlay={
                            <Menu>
                              <Menu.Item key="0" onClick={() => { onEditComponent(component, category); }}>
                                编辑
                              </Menu.Item>
                              <Menu.Item key="1" onClick={() => { onDeleteComponent(component); }}>删除</Menu.Item>
                            </Menu>
                          }
                        >
                          <a className="ant-dropdown-link" onClick={(e) => e.preventDefault()}>
                            更多 <DownOutlined />
                          </a>
                        </Dropdown>,
                      ]}
                    >
                      <div className="manage-component-card-img">
                        <img src={component.cover} />
                      </div>
                      <Paragraph ellipsis={{ rows: 2, expandable: true, symbol: 'more' }}>
                        {component.name}
                      </Paragraph>
                    </Card>
                  ))
                }
              </TabPane>
            ))
          }
        </Tabs>
      </Spin>
      <FormRenderDrawer
        type="add"
        visible={componentDraw.visible}
        onCancel={onCancel}
        formSchema={formSchema}
        onSubmit={onSubmit}
        loading={addComponentReq.loading}
        formData={componentDraw.data}
      />
    </PageHeader>
  );
}
Example #28
Source File: CoursePreferenceSettings.tsx    From office-hours with GNU General Public License v3.0 4 votes vote down vote up
export default function CoursePreferenceSettings(): ReactElement {
  const { data: profile, mutate } = useSWR(`api/v1/profile`, async () =>
    API.profile.index()
  );

  const formattedRoles = {
    student: "Student",
    ta: "TA",
    professor: "Professor",
  };

  async function withdraw(course: UserCourse) {
    await API.course.withdrawCourse(course.course.id);
    message.success("Successfully withdrew from " + course.course.name);
    await mutate();
    await Router.push("/");
  }

  function showConfirm(courseId) {
    const course = profile?.courses.find((c) => c.course.id === courseId);

    confirm({
      title: `Please Confirm!`,
      icon: <ExclamationCircleOutlined />,
      content: `Please confirm that you want to unenroll from ${
        course.course.name
      } as a ${
        formattedRoles[course.role]
      }.  The only way to get back is by contacting a professor!`,
      onOk() {
        withdraw(course);
      },
    });
  }

  const InstructorCell = ({ courseId }: { courseId: number }) => {
    const course = useCourse(courseId);

    return <>{course.course?.coordinator_email}</>;
  };

  const columns = [
    {
      title: "Course name",
      dataIndex: "name",
      key: "name",
    },
    {
      title: "Role",
      dataIndex: "role",
      key: "role",
    },
    {
      title: "Instructor",
      dataIndex: "instructor",
      key: "instructor",
      render: function createInstructorCell(courseId) {
        return <InstructorCell courseId={courseId} />;
      },
    },
    {
      title: "",
      key: "courseId",
      dataIndex: "courseId",
      render: function withdrawButton(courseId) {
        return (
          <Button
            style={{ marginLeft: "20px" }}
            type="primary"
            shape="round"
            onClick={() => {
              showConfirm(courseId);
            }}
            danger
          >
            Withdraw
          </Button>
        );
      },
    },
  ];

  function createCourseDataSource() {
    return profile?.courses.map((c) => ({
      key: c.course.id,
      name: c.course.name,
      role: formattedRoles[c.role],
      instructor: c.course.id,
      courseId: c.course.id,
    }));
  }

  return (
    profile && (
      <div>
        <HeaderTitle>
          <h1>Course Preferences</h1>
        </HeaderTitle>
        <Table columns={columns} dataSource={createCourseDataSource()} />
      </div>
    )
  );
}
Example #29
Source File: FormRender.tsx    From ant-simple-draw with MIT License 4 votes vote down vote up
FormRender: FC<FormRenderType> = memo(
  function FormRender({ editType, onSave, showEditPropsData, id }) {
    const [form] = Form.useForm();
    useEffect(() => {
      form.setFieldsValue(showEditPropsData);
      return () => {
        form.resetFields();
      };
    }, [form, id]);

    const onFinish = (values: Store) => {
      onSave && onSave(values);
    };

    const handlechange = () => {
      onFinish(form.getFieldsValue());
    };

    const colFun = (col: number | undefined) => (col ? col : 24);

    return (
      <Form form={form} name={`form_editor`} onFinish={onFinish} onValuesChange={handlechange}>
        <Row gutter={[30, 0]}>
          {editType.map((item, index) => {
            return (
              <React.Fragment key={item.key}>
                {item.type === 'Number' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer border={item.border || false} title={item.title}>
                      <Form.Item label={item.name} name={item.key} style={{ margin: '0' }}>
                        <InputNumber style={{ width: item.width }} min={0} />
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}
                {item.type === 'Background' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer title={item.title}>
                      <Form.Item label={item.name} name={item.key} style={{ margin: '0' }}>
                        <BackGround />
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}
                {item.type === 'Switch' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer title={item.title}>
                      <Form.Item
                        style={{ margin: '0' }}
                        label={item.name}
                        name={item.key}
                        valuePropName="checked"
                        labelAlign="left"
                        labelCol={{ span: 19 }}
                      >
                        <Switch />
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}
                {item.type === 'Image' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer title={item.title}>
                      <Form.Item label={null} name={item.key} style={{ margin: '0' }}>
                        <ImgComponent />
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}
                {item.type === 'Input' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer border={true} title={item.title}>
                      <Form.Item label={item.name} name={item.key} style={{ margin: '0' }}>
                        <Input />
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}
                {item.type === 'RichText' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer
                      border={false}
                      title={item.title}
                      containerStyle={{ padding: '0' }}
                    >
                      <Form.Item label={null} name={item.key} style={{ margin: '0' }}>
                        <WangEditor />
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}
                {item.type === 'Radio' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer border={true} title={item.title}>
                      <Form.Item label={item.name} name={item.key} style={{ margin: '0' }}>
                        <Radio.Group>
                          {item.options!.map((items, index) => (
                            <Radio value={items.value} key={index}>
                              {items.label}
                            </Radio>
                          ))}
                        </Radio.Group>
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}
                {item.type === 'Select' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer border={true} title={item.title}>
                      <Form.Item label={item.name} name={item.key} style={{ margin: '0' }}>
                        <Selects data={item.options!} valKey={'value'} valName={'label'} />
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}
                {item.type === 'TextArea' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer
                      border={false}
                      title={item.title}
                      containerStyle={{ padding: '0' }}
                    >
                      <Form.Item label={null} name={item.key} style={{ margin: '0' }}>
                        <TextArea autoSize={{ minRows: 2 }} showCount />
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}
                {item.type === 'Color' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer title={item.title}>
                      <Form.Item label={item.name} name={item.key} style={{ margin: '0' }}>
                        <Input type={'color'} />
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}
                {item.type === 'Border' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer title={item.title}>
                      <Form.Item label={item.name} name={item.key} style={{ margin: '0' }}>
                        <Border />
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}
                {item.type === 'Slider' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer title={item.title}>
                      <Form.Item
                        label={item.name}
                        name={item.key}
                        style={{ margin: '0' }}
                        initialValue={100}
                      >
                        <Slider tipFormatter={(val) => val + '%'} />
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}
                {item.type === 'FontStyle' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer title={item.title}>
                      <Form.Item label={null} name={item.key} style={{ margin: '0' }}>
                        <FontStyle />
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}
                {item.type === 'Padding' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer title={item.title}>
                      <Form.Item
                        label={item.name}
                        name={item.key}
                        style={{ margin: '0', display: 'flex', alignItems: 'center' }}
                      >
                        <Padding />
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}

                {item.type === 'BorderRadius' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer title={item.title}>
                      <Form.Item
                        label={
                          <div>
                            <span> {item.name}</span>
                            <Tooltip title="只有在背景,边框等样式下有效果">
                              <ExclamationCircleOutlined
                                style={{ color: '#9da3ac', paddingLeft: '3px', fontSize: '17px' }}
                              />
                            </Tooltip>
                          </div>
                        }
                        name={item.key}
                        style={{ margin: '0', display: 'flex', alignItems: 'center' }}
                      >
                        <BorderRadius />
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}

                {item.type === 'TextShadow' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer title={item.title}>
                      <Form.Item
                        label={item.name}
                        name={item.key}
                        style={{ margin: '0', display: 'flex', alignItems: 'center' }}
                      >
                        <TextShadow />
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}

                {item.type === 'BoxShadow' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer title={item.title}>
                      <Form.Item
                        label={
                          <div>
                            <span> {item.name}</span>
                            <Tooltip title="只有在背景下有效果">
                              <ExclamationCircleOutlined
                                style={{ color: '#9da3ac', paddingLeft: '3px', fontSize: '17px' }}
                              />
                            </Tooltip>
                          </div>
                        }
                        name={item.key}
                        style={{ margin: '0', display: 'flex', alignItems: 'center' }}
                      >
                        <BoxShadow />
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}
                {item.type === 'OlUl' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer title={item.title}>
                      <Form.Item style={{ margin: '0' }}>
                        <OlUl
                          keyName={item.key}
                          form={form}
                          showEditPropsData={showEditPropsData}
                        />
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}
                {item.type === 'TaskList' && (
                  <Col span={colFun(item.col)}>
                    <AttrContainer title={item.title}>
                      <Form.Item style={{ margin: '0' }}>
                        <TaskList keyName={item.key} />
                      </Form.Item>
                    </AttrContainer>
                  </Col>
                )}
              </React.Fragment>
            );
          })}
        </Row>
      </Form>
    );
  },
  (prevProps, nextProps) => {
    if (prevProps.id !== nextProps.id) {
      // 防止多次出发组件
      return false;
    } else {
      return true;
    }
  },
)