antd#Transfer TypeScript Examples

The following examples show how to use antd#Transfer. 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: data-task-creation.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
render() {
    const { isFetching, onCancel } = this.props;
    const { targetKeys, sourceFiles, selectedKeys } = this.state;
    return (
      <div className="data-task">
        <Spin spinning={isFetching}>
          <Transfer
            rowKey={(record) => record.title}
            showSearch
            className="data-task-transfer"
            dataSource={sourceFiles}
            targetKeys={targetKeys}
            selectedKeys={selectedKeys}
            onChange={this.handleChange}
            render={this.renderItem}
            listStyle={{ width: 352, height: 482 }}
            onSelectChange={this.onSelectChange}
          />
          <section className="footer flex justify-between items-center mt-5">
            <span>
              <CustomIcon type="jg" />
              {i18n.t('dop:can only add up to 10 files at a time')}
            </span>
            <div>
              <Button className="ml-2" onClick={onCancel}>
                {i18n.t('Cancel')}
              </Button>
              <Button className="ml-2" type="primary" onClick={this.onOk}>
                {i18n.t('OK')}
              </Button>
            </div>
          </section>
        </Spin>
      </div>
    );
  }
Example #2
Source File: GeneralTransfer.spec.tsx    From next-basics with GNU General Public License v3.0 5 votes vote down vote up
describe("GeneralTransfer", () => {
  it("should work", () => {
    const changeFn = jest.fn();
    const selectFn = jest.fn();

    const wrapper = shallow(
      <GeneralTransfer
        dataSource={[]}
        onChange={changeFn}
        onSelectedChange={selectFn}
      />
    );
    let transfer = wrapper.find(Transfer).first();
    transfer.invoke("onChange")([], "", []);
    expect(changeFn).toBeCalledWith([]);
    transfer.invoke("onSelectChange")([], []);
    expect(selectFn).toBeCalledWith([], []);
    transfer.invoke("filterOption")("x", {
      key: "host.disk.used_total",
      title: "disk used total",
    });

    wrapper.setProps({ maxSelected: 10 });
    transfer = wrapper.find(Transfer).first();
    expect(transfer.invoke("footer")(null)).toBeTruthy();
    expect(transfer.invoke("render")({ key: "x", title: "title" })).toBe(
      "title"
    );
  });
  it("should work and maxSelected", () => {
    const changeFn = jest.fn();
    const selectFn = jest.fn();
    const wrapper = shallow(
      <GeneralTransfer
        dataSource={[
          {
            key: "1",
            title: "test1",
          },
          {
            key: "2",
            title: "test2",
          },
          {
            key: "3",
            title: "test3",
          },
          {
            key: "4",
            title: "test3",
          },
        ]}
        onChange={changeFn}
        onSelectedChange={selectFn}
        maxSelected={2}
      />
    );
    const transfer = wrapper.find(Transfer).first();
    transfer.invoke("onChange")(["1", "2", "3"], "right", []);
    expect(changeFn).not.toHaveBeenCalled();
  });
});
Example #3
Source File: GeneralTransfer.tsx    From next-basics with GNU General Public License v3.0 5 votes vote down vote up
export function GeneralTransfer(
  props: GeneralTransferProps
): React.ReactElement {
  const { t } = useTranslation(NS_PRESENTATIONAL_BRICKS);

  const onChange = (
    targetKeys: string[],
    direction: "left" | "right",
    moveKeys: string[]
  ): void => {
    if (
      direction === "left" ||
      !props.maxSelected ||
      targetKeys.length <= props.maxSelected
    ) {
      props.onChange(targetKeys);
    } else {
      Modal.warning({
        title: "提示",
        content: `所选数量超过最大限制(${props.maxSelected}),请重新选择`,
        okText: "知道了",
      });
    }
  };

  const onSelectChange = (
    sourceSelectedKeys: string[],
    targetSelectedKeys: string[]
  ): void => {
    props.onSelectedChange?.(sourceSelectedKeys, targetSelectedKeys);
  };

  const filterOption = (inputValue: string, item: TransferItem): boolean => {
    const q = inputValue.trim().toLowerCase();
    return item.title.toLowerCase().includes(q);
  };

  const footer = (): React.ReactNode => {
    return (
      <div className={cssStyle.footer}>最多选择 {props.maxSelected} 个</div>
    );
  };

  return (
    <div className={cssStyle.container}>
      <Transfer
        lazy={false}
        dataSource={props.dataSource}
        render={(item) => item.title}
        targetKeys={props.targetKeys}
        onChange={onChange}
        onSelectChange={onSelectChange}
        locale={props.locale}
        listStyle={props.listStyle}
        titles={props.titles}
        operations={props.operations}
        selectedKeys={props.selectedKeys}
        disabled={props.disabled}
        filterOption={filterOption}
        showSearch={props.showSearch}
        showSelectAll={props.showSelectAll}
        footer={props.maxSelected && footer}
      />
    </div>
  );
}
Example #4
Source File: MemberForm.tsx    From datart with Apache License 2.0 5 votes vote down vote up
MemberForm = memo(
  ({ initialValues, onChange, onCancel, ...modalProps }: MemberFormProps) => {
    const [targetKeys, setTargetKeys] = useState<string[]>([]);
    const dispatch = useDispatch();
    const formRef = useRef<FormInstance>();
    const members = useSelector(selectMembers);
    const memberListLoading = useSelector(selectMemberListLoading);
    const orgId = useSelector(selectOrgId);
    const dataSource = useMemo(
      () => members.map(m => ({ ...m, key: m.id })),
      [members],
    );

    useEffect(() => {
      if (modalProps.visible) {
        dispatch(getMembers(orgId));
      }
    }, [dispatch, orgId, modalProps.visible]);

    useEffect(() => {
      setTargetKeys(initialValues.map(({ id }) => id));
    }, [initialValues]);

    const save = useCallback(() => {
      onChange(targetKeys.map(id => members.find(m => m.id === id)!));
      onCancel && onCancel(null as any);
    }, [targetKeys, members, onCancel, onChange]);

    const renderTitle = useCallback(
      ({ name, username, email }) => (
        <ItemTitle>
          {name && <span>{name}</span>}
          {username && <span>{` (${username})`}</span>}
          {email && <span className="email">{email}</span>}
        </ItemTitle>
      ),
      [],
    );

    const filterMemberListOptions = useCallback(
      (inputValue, option: User) =>
        [option.email, option.name, option.username].some(text =>
          text?.includes(inputValue.trim()),
        ),
      [],
    );

    return (
      <ModalForm
        {...modalProps}
        onSave={save}
        onCancel={onCancel}
        ref={formRef}
      >
        <TransferWrapper>
          <LoadingMask loading={memberListLoading}>
            <Transfer
              dataSource={dataSource}
              targetKeys={targetKeys}
              render={renderTitle}
              onChange={setTargetKeys}
              filterOption={filterMemberListOptions}
              showSearch
            />
          </LoadingMask>
        </TransferWrapper>
      </ModalForm>
    );
  },
)
Example #5
Source File: CreateForm.tsx    From anew-server with MIT License 5 votes vote down vote up
CreateForm: React.FC<CreateFormProps> = (props) => {
    const { actionRef, modalVisible, handleChange } = props;
    const [hostData, setHostData] = useState<TransferItem[]>([]);
    const [targetKeys, setTargetKeys] = useState<string[]>([]);

    const transferChange = (keys: string[]) => {
        setTargetKeys(keys);
    };

    useEffect(() => {
        queryHosts({ all: true }).then((res) => {
            if (Array.isArray(res.data.data)) {
                setHostData(
                    res.data.data.map((item: API.HostList) => ({
                        key: item.id,
                        title: item.host_name,
                        description: item.ip_address,
                    })),
                );
            }
        });
    }, []);

    return (
        <ModalForm
            title="新建主机组"
            visible={modalVisible}
            onVisibleChange={handleChange}
            onFinish={async (v) => {
                createHostGroup(v as API.HostGroupParams).then((res) => {
                    if (res.code === 200 && res.status === true) {
                        message.success(res.message);
                        if (actionRef.current) {
                            actionRef.current.reload();
                        }
                    }
                });
                return true;
            }}
        >
            <ProForm.Group>
                <ProFormText name="name" label="名称" width="md" rules={[{ required: true }]} />
                <ProFormText name="desc" label="说明" width="md" />
                <Form.Item label="选择主机" name="hosts">
                    <Transfer
                        dataSource={hostData}
                        showSearch
                        listStyle={{
                            width: 320,
                            height: 280,
                        }}
                        //operations={['加入', '退出']}
                        targetKeys={targetKeys}
                        onChange={transferChange}
                        render={(item) => `${item.title}(${item.description})`}
                    />
                </Form.Item>
            </ProForm.Group>
        </ModalForm>
    );
}
Example #6
Source File: UpdateForm.tsx    From anew-server with MIT License 5 votes vote down vote up
UpdateForm: React.FC<UpdateFormProps> = (props) => {
    const { actionRef, modalVisible, handleChange, values } = props;
    const [hostData, setHostData] = useState<TransferItem[]>([]);
    const [targetKeys, setTargetKeys] = useState<string[]>([]);

    const transferChange = (keys: string[]) => {
        setTargetKeys(keys);
    };

    useEffect(() => {
        console.log(hostData)
    },[hostData]);
    useEffect(() => {
        queryHosts({ all: true }).then((res) => {
            if (Array.isArray(res.data.data)) {
                setHostData(
                    res.data.data.map((item: API.HostList) => ({
                        key: item.id,
                        title: item.host_name,
                        description: item.ip_address,
                    })),
                );
            }
        });
        transferChange(values?.hosts_id as unknown as string[])
    }, []);

    return (
        <ModalForm
            title="更新主机组"
            visible={modalVisible}
            onVisibleChange={handleChange}
            onFinish={async (v) => {
                updateHostGroup(v as API.HostGroupParams, values?.id).then((res) => {
                    if (res.code === 200 && res.status === true) {
                        message.success(res.message);
                        if (actionRef.current) {
                            actionRef.current.reload();
                        }
                    }
                });
                return true;
            }}
        >
            <ProForm.Group>
                <ProFormText
                    name="name"
                    label="名称"
                    width="md"
                    initialValue={values?.name}
                    rules={[{ required: true }]}
                />
                <ProFormText name="desc" label="说明" width="md" initialValue={values?.desc} />
                <Form.Item label="选择主机" name="hosts">
                    <Transfer
                        dataSource={hostData}
                        showSearch
                        listStyle={{
                            width: 320,
                            height: 280,
                        }}
                        //operations={['加入', '退出']}
                        targetKeys={targetKeys ? targetKeys : []}
                        onChange={transferChange}
                        render={(item) => `${item.title}(${item.description})`}
                    />
                </Form.Item>
            </ProForm.Group>
        </ModalForm>
    );
}
Example #7
Source File: change-permission-form.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
ChangePermissionForm = (props: IProps) => {
  const { visible, data, onClose, handleSubmit } = props;
  const RDSDatabaseList = cloudServiceStore.useStore((s) => s.RDSDatabaseList);
  const [rdsID, query] = routeInfoStore.useStore((s) => [s.params.rdsID, s.query]);
  const { getRDSDatabaseList } = cloudServiceStore.effects;

  const [{ targetKeys, permissionChoose }, updater, update] = useUpdate({
    targetKeys: [],
    permissionChoose: {},
  });

  useMount(() => {
    getRDSDatabaseList({
      id: rdsID,
      query,
    });
  });

  useEffect(() => {
    const newKeys: string[] = [];
    const newChoose = {};
    forEach(get(data, 'databasePrivileges'), (item) => {
      newKeys.push(item.dBName);
      newChoose[item.dBName] = item.accountPrivilege;
    });
    update({
      targetKeys: newKeys,
      permissionChoose: newChoose,
    });
  }, [data, update]);

  const handleChange = (keys: string[]) => {
    update({
      targetKeys: keys,
      permissionChoose: reduce(
        keys,
        (acc, key) => {
          const newPermissionChoose = {
            ...acc,
            [key]: permissionChoose[key] || rdsAccountType[0].value,
          };
          return newPermissionChoose;
        },
        {},
      ),
    });
  };

  const renderTransferItem = (item: TransferItem) => {
    const { key } = item;
    const label = (
      <>
        {item.key}&nbsp;
        <span className="permission-select" onClick={(e: any) => e.stopPropagation()}>
          <RadioGroup
            onChange={(e: any) => {
              updater.permissionChoose({
                ...permissionChoose,
                [key]: e.target.value,
              });
            }}
            value={permissionChoose[key]}
          >
            {rdsAccountType.map((per) => (
              <Radio key={per.value} value={per.value}>
                {per.name}
              </Radio>
            ))}
          </RadioGroup>
        </span>
      </>
    );
    return {
      label,
      value: item.key,
    };
  };

  const fieldsList = [
    {
      getComp: () => {
        return (
          <span>
            {i18n.t('cmp:database account')}: {get(data, 'AccountName')}
          </span>
        );
      },
    },
    {
      label: i18n.t('cmp:authorization database'),
      getComp: () => {
        return (
          <Transfer
            dataSource={map(RDSDatabaseList, (db) => ({
              key: db.dBName,
              title: db.dBName,
            }))}
            className="permission-transfer"
            targetKeys={targetKeys}
            onChange={handleChange}
            render={renderTransferItem}
            titles={[i18n.t('cmp:unauthorized database'), i18n.t('cmp:authorized database')]}
          />
        );
      },
    },
  ];

  return (
    <FormModal
      title={i18n.t('change account permissions')}
      visible={visible}
      fieldsList={fieldsList}
      width={800}
      onCancel={onClose}
      onOk={() => handleSubmit(permissionChoose)}
    />
  );
}
Example #8
Source File: CategoryConditionConfiguration.tsx    From datart with Apache License 2.0 4 votes vote down vote up
CategoryConditionConfiguration: ForwardRefRenderFunction<
  FilterOptionForwardRef,
  {
    colName: string;
    dataView?: ChartDataView;
    condition?: ChartFilterCondition;
    onChange: (condition: ChartFilterCondition) => void;
    fetchDataByField?: (fieldId) => Promise<string[]>;
  } & I18NComponentProps
> = (
  {
    colName,
    i18nPrefix,
    condition,
    dataView,
    onChange: onConditionChange,
    fetchDataByField,
  },
  ref,
) => {
  const t = useI18NPrefix(i18nPrefix);
  const [curTab, setCurTab] = useState<FilterConditionType>(() => {
    if (
      [
        FilterConditionType.List,
        FilterConditionType.Condition,
        FilterConditionType.Customize,
      ].includes(condition?.type!)
    ) {
      return condition?.type!;
    }
    return FilterConditionType.List;
  });
  const [targetKeys, setTargetKeys] = useState<string[]>(() => {
    let values;
    if (condition?.operator === FilterSqlOperator.In) {
      values = condition?.value;
      if (Array.isArray(condition?.value)) {
        const firstValues =
          (condition?.value as [])?.filter(n => {
            if (IsKeyIn(n as RelationFilterValue, 'key')) {
              return (n as RelationFilterValue).isSelected;
            }
            return false;
          }) || [];
        values = firstValues?.map((n: RelationFilterValue) => n.key);
      }
    }
    return values || [];
  });
  const [selectedKeys, setSelectedKeys] = useState<string[]>([]);
  const [isTree, setIsTree] = useState(isTreeModel(condition?.value));
  const [treeOptions, setTreeOptions] = useState<string[]>([]);
  const [listDatas, setListDatas] = useState<RelationFilterValue[]>([]);
  const [treeDatas, setTreeDatas] = useState<RelationFilterValue[]>([]);

  useImperativeHandle(ref, () => ({
    onValidate: (args: ChartFilterCondition) => {
      if (isEmpty(args?.operator)) {
        return false;
      }
      if (args?.operator === FilterSqlOperator.In) {
        return !isEmptyArray(args?.value);
      } else if (
        [
          FilterSqlOperator.Contain,
          FilterSqlOperator.PrefixContain,
          FilterSqlOperator.SuffixContain,
          FilterSqlOperator.Equal,
          FilterSqlOperator.NotContain,
          FilterSqlOperator.NotPrefixContain,
          FilterSqlOperator.NotSuffixContain,
          FilterSqlOperator.NotEqual,
        ].includes(args?.operator as FilterSqlOperator)
      ) {
        return !isEmpty(args?.value);
      } else if (
        [FilterSqlOperator.Null, FilterSqlOperator.NotNull].includes(
          args?.operator as FilterSqlOperator,
        )
      ) {
        return true;
      }
      return false;
    },
  }));

  useMount(() => {
    if (curTab === FilterConditionType.List) {
      handleFetchData();
    }
  });

  const getDataOptionFields = () => {
    return dataView?.meta || [];
  };

  const isChecked = (selectedKeys, eventKey) =>
    selectedKeys.indexOf(eventKey) !== -1;

  const fetchNewDataset = async (viewId, colName: string) => {
    const fieldDataset = await getDistinctFields(
      viewId,
      [colName],
      undefined,
      undefined,
    );
    return fieldDataset;
  };

  const setListSelectedState = (
    list?: RelationFilterValue[],
    keys?: string[],
  ) => {
    return (list || []).map(c =>
      Object.assign(c, { isSelected: isChecked(keys, c.key) }),
    );
  };

  const setTreeCheckableState = (
    treeList?: RelationFilterValue[],
    keys?: string[],
  ) => {
    return (treeList || []).map(c => {
      c.isSelected = isChecked(keys, c.key);
      c.children = setTreeCheckableState(c.children, keys);
      return c;
    });
  };

  const handleGeneralListChange = async selectedKeys => {
    const items = setListSelectedState(listDatas, selectedKeys);
    setTargetKeys(selectedKeys);
    setListDatas(items);

    const generalTypeItems = items?.filter(i => i.isSelected);
    const filter = new ConditionBuilder(condition)
      .setOperator(FilterSqlOperator.In)
      .setValue(generalTypeItems)
      .asGeneral();
    onConditionChange(filter);
  };

  const filterGeneralListOptions = useCallback(
    (inputValue, option) => option.label?.includes(inputValue) || false,
    [],
  );

  const handleGeneralTreeChange = async treeSelectedKeys => {
    const selectedKeys = treeSelectedKeys.checked;
    const treeItems = setTreeCheckableState(treeDatas, selectedKeys);
    setTargetKeys(selectedKeys);
    setTreeDatas(treeItems);
    const filter = new ConditionBuilder(condition)
      .setOperator(FilterSqlOperator.In)
      .setValue(treeItems)
      .asTree();
    onConditionChange(filter);
  };

  const onSelectChange = (
    sourceSelectedKeys: string[],
    targetSelectedKeys: string[],
  ) => {
    const newSelectedKeys = [...sourceSelectedKeys, ...targetSelectedKeys];
    setSelectedKeys(newSelectedKeys);
  };

  const handleTreeOptionChange = (
    associateField: string,
    labelField: string,
  ) => {
    setTreeOptions([associateField, labelField]);
  };

  const handleFetchData = () => {
    fetchNewDataset?.(dataView?.id, colName).then(dataset => {
      if (isTree) {
        // setTreeDatas(convertToTree(dataset?.columns, selectedKeys));
        // setListDatas(convertToList(dataset?.columns, selectedKeys));
      } else {
        setListDatas(convertToList(dataset?.rows, selectedKeys));
      }
    });
  };

  const convertToList = (collection, selectedKeys) => {
    const items: string[] = (collection || []).flatMap(c => c);
    const uniqueKeys = Array.from(new Set(items));
    return uniqueKeys.map(item => ({
      key: item,
      label: item,
      isSelected: selectedKeys.includes(item),
    }));
  };

  const convertToTree = (collection, selectedKeys) => {
    const associateField = treeOptions?.[0];
    const labelField = treeOptions?.[1];

    if (!associateField || !labelField) {
      return [];
    }

    const associateKeys = Array.from(
      new Set(collection?.map(c => c[associateField])),
    );
    const treeNodes = associateKeys
      .map(key => {
        const associateItem = collection?.find(c => c[colName] === key);
        if (!associateItem) {
          return null;
        }
        const associateChildren = collection
          .filter(c => c[associateField] === key)
          .map(c => {
            const itemKey = c[labelField];
            return {
              key: itemKey,
              label: itemKey,
              isSelected: isChecked(selectedKeys, itemKey),
            };
          });
        const itemKey = associateItem?.[colName];
        return {
          key: itemKey,
          label: itemKey,
          isSelected: isChecked(selectedKeys, itemKey),
          children: associateChildren,
        };
      })
      .filter(i => Boolean(i)) as RelationFilterValue[];
    return treeNodes;
  };

  const handleTabChange = (activeKey: string) => {
    const conditionType = +activeKey;
    setCurTab(conditionType);
    const filter = new ConditionBuilder(condition)
      .setOperator(null!)
      .setValue(null)
      .asFilter(conditionType);
    setTreeDatas([]);
    setTargetKeys([]);
    setListDatas([]);
    onConditionChange(filter);
  };

  return (
    <StyledTabs activeKey={curTab.toString()} onChange={handleTabChange}>
      <Tabs.TabPane
        tab={t('general')}
        key={FilterConditionType.List.toString()}
      >
        <Row>
          <Space>
            <Button type="primary" onClick={handleFetchData}>
              {t('load')}
            </Button>
            {/* <Checkbox
                checked={isTree}
                disabled
                onChange={e => setIsTree(e.target.checked)}
              >
                {t('useTree')}
              </Checkbox> */}
          </Space>
        </Row>
        <Row>
          <Space>
            {isTree && (
              <>
                {t('associateField')}
                <Select
                  value={treeOptions?.[0]}
                  options={getDataOptionFields()?.map(f => ({
                    label: f.name,
                    value: f.id,
                  }))}
                  onChange={value =>
                    handleTreeOptionChange(value, treeOptions?.[1])
                  }
                />
                {t('labelField')}
                <Select
                  value={treeOptions?.[1]}
                  options={getDataOptionFields()?.map(f => ({
                    label: f.name,
                    value: f.id,
                  }))}
                  onChange={value =>
                    handleTreeOptionChange(treeOptions?.[0], value)
                  }
                />
              </>
            )}
          </Space>
        </Row>
        {isTree && (
          <Tree
            blockNode
            checkable
            checkStrictly
            defaultExpandAll
            checkedKeys={targetKeys}
            treeData={treeDatas}
            onCheck={handleGeneralTreeChange}
            onSelect={handleGeneralTreeChange}
          />
        )}
        {!isTree && (
          <Transfer
            operations={[t('moveToRight'), t('moveToLeft')]}
            dataSource={listDatas}
            titles={[`${t('sourceList')}`, `${t('targetList')}`]}
            targetKeys={targetKeys}
            selectedKeys={selectedKeys}
            onChange={handleGeneralListChange}
            onSelectChange={onSelectChange}
            render={item => item.label}
            filterOption={filterGeneralListOptions}
            showSearch
            pagination
          />
        )}
      </Tabs.TabPane>
      <Tabs.TabPane
        tab={t('customize')}
        key={FilterConditionType.Customize.toString()}
      >
        <CategoryConditionEditableTable
          dataView={dataView}
          i18nPrefix={i18nPrefix}
          condition={condition}
          onConditionChange={onConditionChange}
          fetchDataByField={fetchDataByField}
        />
      </Tabs.TabPane>
      <Tabs.TabPane
        tab={t('condition')}
        key={FilterConditionType.Condition.toString()}
      >
        <CategoryConditionRelationSelector
          condition={condition}
          onConditionChange={onConditionChange}
        />
      </Tabs.TabPane>
    </StyledTabs>
  );
}
Example #9
Source File: DomainAccess.tsx    From wildduck-ui with MIT License 4 votes vote down vote up
DomainAccess: React.FC = () => {
	const { Search } = Input;
	const { setBlockedList, setTag, setAllowedList, setAddDomainModalVisiblity, setDataSource } = useActions(
		domainAccessLogic,
	);
	const { blockedList, tag, allowedList, addDomainModalVisiblity, dataSource } = useValues(domainAccessLogic);
	const { data: allowedListData, isLoading: allowedListLoading } = useAllowedList(tag);
	const { data: blockedListData, isLoading: blockedListLoading } = useBlockedList(tag);
	const { mutate } = useDeleteDomain();
	const { mutate: addDomainToAllowList } = useCreateAllowedDomain();
	const { mutate: addDomainToBlockList } = useCreateBlockedDomain();

	const onSearch = (value: string) => {
		if (value.length > 0) {
			setTag(value);
		}
	};

	const renderFooter = () => (
		<Button size='small' style={{ float: 'right', margin: 5 }} onClick={() => setAddDomainModalVisiblity(true)}>
			Add Domain
		</Button>
	);

	const onChange = (targetKeys: string[], direction: string, moveKeys: string[]) => {
		const blockList = _.filter(dataSource, (domain) => targetKeys.includes(domain.id));
		const moveList = _.filter(dataSource, (domain) => moveKeys.includes(domain.id));

		if (direction === 'right') {
			_.forEach(moveList, (domain) => addDomainToBlockList({ tag: tag, domain: domain.domain }));
			setAllowedList(_.filter(allowedList, (domain) => !moveKeys.includes(domain.id)));
		} else {
			_.forEach(moveList, (domain) => addDomainToAllowList({ tag: tag, domain: domain.domain }));
			setAllowedList(_.concat(allowedList, moveList));
		}
		setBlockedList(blockList);
	};

	useEffect(() => {
		setAllowedList(_.uniq(allowedListData));
		setBlockedList(_.uniq(blockedListData));
		setDataSource(_.uniqBy(_.concat(allowedListData, blockedListData), 'id'));
	}, [allowedListData, blockedListData]);

	const Content = ({ item }: { item: any }) => (
		<Row style={{ display: 'flex', justifyContent: 'space-between' }}>
			<Col>{item.domain}</Col>
			<Col>
				<DeleteOutlined onClick={() => showConfirm(() => mutate(item.id))} />
			</Col>
		</Row>
	);

	const AddDomains = () => {
		const onFinish = (values: any) => {
			values.action === 'allow'
				? addDomainToAllowList({ tag: tag, domain: values.domain })
				: addDomainToBlockList({ tag: tag, domain: values.domain });
		};

		return (
			<Form {...layout} onFinish={onFinish} initialValues={{ action: 'allow' }}>
				<Form.Item
					label='Domain'
					name='domain'
					wrapperCol={{ span: '10' }}
					rules={[
						{
							required: true,
							message: 'Please input the Domain!',
						},
					]}
				>
					<Input placeholder='Domain name' />
				</Form.Item>
				<Form.Item label='Add To' name='action'>
					<Select style={{ width: 120 }}>
						<Select.Option value='allow'>Allow List</Select.Option>
						<Select.Option value='block'>Block List</Select.Option>
					</Select>
				</Form.Item>
				<Form.Item {...tailLayout}>
					<Button type='primary' htmlType='submit'>
						Add
					</Button>
				</Form.Item>
			</Form>
		);
	};

	return (
		<Page title='Domain Access' loading={allowedListLoading || blockedListLoading}>
			<Search
				size='large'
				placeholder='Enter a Tag'
				defaultValue={tag}
				allowClear
				enterButton='Search'
				onSearch={onSearch}
			/>

			{!_.isEmpty(tag) && !allowedListLoading && !blockedListLoading && (
				<Transfer
					dataSource={_.map(_.uniqBy(_.concat(allowedListData, blockedListData), 'id'), (domain) => ({
						...domain,
						key: domain.id,
					}))}
					targetKeys={_.map(blockedList, (domain) => domain.id)}
					titles={['Allowed List', 'Blocked List']}
					style={{ paddingTop: '5px' }}
					showSearch
					listStyle={{
						width: '90%',
						height: '90%',
					}}
					operations={['to right', 'to left']}
					render={(item: any) => ({ value: item.domain, label: <Content item={item} /> })}
					footer={renderFooter}
					onChange={onChange}
				/>
			)}
			<Modal
				title='Add Domain'
				visible={addDomainModalVisiblity}
				onCancel={() => setAddDomainModalVisiblity(false)}
				footer={null}
			>
				<AddDomains />
			</Modal>
		</Page>
	);
}