import React, { useCallback, useMemo, useState } from 'react'; import { css, useCollectionManager_deprecated, useRecord, useRequest, useToken } from '@tachybase/client'; import { connect } from '@tachybase/schema'; import { CollectionsGraph, lodash } from '@tachybase/utils/client'; import { App, Col, Input, Row, Select, Space, Spin, Table, Tag } from 'antd'; import { useTranslation } from 'react-i18next'; const excludeCollections = ['users', 'roles', 'applications']; const useCollectionsGraph = ({ removed = [] }) => { const { collections } = useCollectionManager_deprecated(); const findAddable = useCallback( (name) => { return CollectionsGraph.connectedNodes({ collections, nodes: [name], excludes: excludeCollections, }).filter((name) => removed.includes(name)); }, [removed], ); const findRemovable = useCallback( (name) => { return CollectionsGraph.connectedNodes({ collections, nodes: [name], excludes: excludeCollections, direction: 'reverse', }).filter((name) => !removed.includes(name)); }, [removed], ); return { findAddable, findRemovable, }; }; const useCollections = () => { const record = useRecord(); const [selected, setSelected] = useState([]); const res1 = useRequest( { url: `applications/${record.name}/collectionBlacklist:list`, params: { paginate: false, params: { fields: ['name'], }, }, }, { onSuccess(data) { setSelected(data.data?.map((data) => data.name)); }, }, ); const res2 = useRequest<{ data: any[]; }>({ url: `collections`, params: { fields: ['name', 'title', 'hidden', 'category.name', 'category.color', 'category.sort'], sort: 'sort', paginate: false, }, }); const res3 = useRequest<{ data: any[]; }>({ url: `collectionCategories`, params: { sort: 'sort', paginate: false, }, }); return { loading: res1.loading || res2.loading || res3.loading, collections: (res2.data?.data || []).filter((item) => !item.hidden && !excludeCollections.includes(item.name)), removed: selected, setSelected, categories: (res3.data?.data || []).map((cat) => ({ label: cat.name, value: cat.name })), }; }; const includes = (text: string, s: string | string[]) => { const values = Array.isArray(s) ? s : [s]; for (const val of values) { if (text.toLowerCase().includes(val)) { return true; } } return false; }; const useRemovedDataSource = ({ collections, removed }) => { const [filter, setFilter] = useState({ name: '', category: [] }); const dataSource = useMemo(() => { return collections.filter((collection) => { const { name, title, category = [] } = collection; const results = [removed.includes(collection.name)]; if (filter.name) { results.push(includes(name, filter.name) || includes(title, filter.name)); } if (filter.category.length > 0) { results.push(category.some((item) => includes(item.name, filter.category))); } return !results.includes(false); }); }, [collections, removed, filter]); const setNameFilter = useMemo( () => lodash.debounce((name) => { setFilter({ ...filter, name, }); }, 300), [], ); return { dataSource, setNameFilter, setCategoryFilter: (category) => { setFilter({ ...filter, category, }); }, }; }; const useAddedDataSource = ({ collections, removed }) => { const [filter, setFilter] = useState({ name: '', category: [] }); const dataSource = collections.filter((collection) => { const { name, title, category = [] } = collection; const results = [!removed.includes(collection.name)]; if (filter.name) { results.push(includes(name, filter.name) || includes(title, filter.name)); } if (filter.category.length > 0) { results.push(category.some((item) => includes(item.name, filter.category))); } return !results.includes(false); }); const setNameFilter = useMemo( () => lodash.debounce((name) => { setFilter({ ...filter, name, }); }, 300), [], ); return { dataSource, setNameFilter, setCategoryFilter: (category) => { setFilter({ ...filter, category, }); }, }; }; export const TableTransfer = connect((props) => { const { onChange } = props; const { loading, collections, categories, removed, setSelected } = useCollections(); const [selectedRowKeys1, setSelectedRowKeys1] = useState([]); const [selectedRowKeys2, setSelectedRowKeys2] = useState([]); const { findAddable, findRemovable } = useCollectionsGraph({ removed }); const addedDataSource = useAddedDataSource({ collections, removed }); const removedDataSource = useRemovedDataSource({ collections, removed }); const { t } = useTranslation('multi-app-share-collection'); const { modal } = App.useApp(); const { token } = useToken(); const columns = useMemo( () => [ { title: t('Collection display name'), dataIndex: 'title', }, { title: t('Collection name'), dataIndex: 'name', }, { title: t('Collection category'), dataIndex: 'category', render: (categories) => categories.map((category) => ( {category.name} )), }, ], [], ); if (loading) { return ; } return (
tr.ant-table-row:hover > td { background: #e6f7ff; cursor: pointer; } `} >
{t('Unshared collections')} removedDataSource.setNameFilter(e.target.value)} style={{ width: '65%' }} placeholder={t('Enter name or title...')} allowClear />
!selectedRowKeys.includes(s)); setSelected(values); onChange(values); setSelectedRowKeys1([]); }, }} pagination={false} size={'small'} columns={columns} // dataSource={collections.filter((collection) => removed.includes(collection.name))} dataSource={removedDataSource.dataSource} scroll={{ y: 'calc(100vh - 260px)' }} onRow={({ name, disabled }: any) => ({ onClick: () => { if (disabled) return; const adding = findAddable(name); const change = () => { const values = removed.filter((s) => !adding.includes(s)); setSelected(values); onChange(values); }; if (adding.length === 1) { return change(); } modal.confirm({ title: t('Are you sure to add the following collections?'), width: '60%', content: (
adding.includes(collection.name))} pagination={false} scroll={{ y: '60vh' }} /> ), onOk() { change(); }, }); }, })} />
{t('Shared collections')} addedDataSource.setNameFilter(e.target.value)} style={{ width: '65%' }} placeholder={t('Enter name or title...')} allowClear />
!selected.includes(collection.name))} scroll={{ y: 'calc(100vh - 260px)' }} onRow={({ name }) => ({ onClick: () => { const removing = findRemovable(name); const change = () => { removed.push(...removing); const values = lodash.uniq([...removed]); setSelected(values); onChange(values); }; if (removing.length === 1) { return change(); } modal.confirm({ title: t('Are you sure to remove the following collections?'), width: '60%', content: (
removing.includes(collection.name))} pagination={false} scroll={{ y: '60vh' }} /> ), onOk() { change(); }, }); }, })} /> ); }); export default TableTransfer;